✨ feat(client): 弹窗可设自动关闭时间,完成提醒描述只显示标题
This commit is contained in:
@@ -54,6 +54,7 @@ var THROTTLE_MS = 1500;
|
||||
var SETTLE_MS = 400;
|
||||
var TOAST_DURATION_MS = 6e3;
|
||||
var TOAST_LIMIT = 3;
|
||||
var DISMISS_CHOICES = [3e3, 6e3, 1e4, 3e4, 0];
|
||||
var RAISED_LIMIT = 8;
|
||||
var REPLAY_LIMIT = 8;
|
||||
var REPLAY_TTL_MS = 30 * 60 * 1e3;
|
||||
@@ -64,7 +65,6 @@ var zh = {
|
||||
"notification.completion": "会话已完成",
|
||||
"notification.approval": "需要授权",
|
||||
"notification.question": "需要回答",
|
||||
"body.completion": "{title} 已完成这一轮回答",
|
||||
"body.approval": "{title}:工具 {tool} 正在等待你的授权",
|
||||
"body.approvalPlain": "{title}:有工具正在等待你的授权",
|
||||
"body.question": "{title}:正在等待你的回答",
|
||||
@@ -72,7 +72,6 @@ var zh = {
|
||||
"body.untitled": "未命名会话",
|
||||
"toast.view": "查看",
|
||||
"toast.dismiss": "关闭",
|
||||
"settings.title": "会话通知",
|
||||
"settings.description": "窗口不在前台时用系统通知,窗口在前台时用应用内轻弹窗;正在看的那个会话完成后不打扰。",
|
||||
"settings.permission.granted": "系统通知已开启",
|
||||
"settings.permission.default": "尚未授予系统通知权限",
|
||||
@@ -91,15 +90,22 @@ var zh = {
|
||||
"settings.health.noHooks": "未收到会话状态(通知不会触发)",
|
||||
"settings.health.error": "监听会话状态出错:{message}",
|
||||
"settings.health.starting": "正在等待会话列表",
|
||||
"settings.kinds.label": "提醒内容",
|
||||
"settings.kind.completion": "完成",
|
||||
"settings.kind.approval": "授权",
|
||||
"settings.kind.question": "提问",
|
||||
"settings.dismiss.label": "自动关闭",
|
||||
"settings.dismiss.3000": "3 秒",
|
||||
"settings.dismiss.6000": "6 秒",
|
||||
"settings.dismiss.10000": "10 秒",
|
||||
"settings.dismiss.30000": "30 秒",
|
||||
"settings.dismiss.0": "永不",
|
||||
"config.section.kinds": "提醒内容",
|
||||
"config.section.popup": "轻弹窗",
|
||||
"config.section.permission": "系统通知权限",
|
||||
"config.section.status": "运行状态",
|
||||
"config.intro": "提醒都从当前页面发出:窗口不在前台时是系统通知,窗口在前台时是这里的轻弹窗。",
|
||||
"config.saveNote": "开关保存在浏览器本地,重装插件不会丢失。",
|
||||
"config.dismiss.note": "鼠标停在上面时不会消失;选「永不」时,每条要自己点 ✕ 或按 Esc 关掉。",
|
||||
"config.testHint": "这里的按钮直接走系统通知通道,用来验证系统通知本身是否可用(窗口在前台也照发)。",
|
||||
"config.testAny": "测试提醒",
|
||||
"config.testSystem": "测试系统通知",
|
||||
@@ -125,7 +131,6 @@ var en = {
|
||||
"notification.completion": "Conversation finished",
|
||||
"notification.approval": "Approval required",
|
||||
"notification.question": "Answer needed",
|
||||
"body.completion": "{title} finished this round",
|
||||
"body.approval": "{title}: {tool} is waiting for your approval",
|
||||
"body.approvalPlain": "{title}: a tool is waiting for your approval",
|
||||
"body.question": "{title}: waiting for your answer",
|
||||
@@ -133,7 +138,6 @@ var en = {
|
||||
"body.untitled": "Untitled conversation",
|
||||
"toast.view": "View",
|
||||
"toast.dismiss": "Dismiss",
|
||||
"settings.title": "Session notifications",
|
||||
"settings.description": "A system notification while the window is in the background, a light in-app popup while it is in the foreground; the conversation on screen is never interrupted.",
|
||||
"settings.permission.granted": "System notifications are on",
|
||||
"settings.permission.default": "System notification permission has not been granted",
|
||||
@@ -152,15 +156,22 @@ var en = {
|
||||
"settings.health.noHooks": "No session status received (notifications will not fire)",
|
||||
"settings.health.error": "Watching session status failed: {message}",
|
||||
"settings.health.starting": "Waiting for the session list",
|
||||
"settings.kinds.label": "Notify about",
|
||||
"settings.kind.completion": "Finished",
|
||||
"settings.kind.approval": "Approval",
|
||||
"settings.kind.question": "Questions",
|
||||
"settings.dismiss.label": "Auto-close",
|
||||
"settings.dismiss.3000": "3s",
|
||||
"settings.dismiss.6000": "6s",
|
||||
"settings.dismiss.10000": "10s",
|
||||
"settings.dismiss.30000": "30s",
|
||||
"settings.dismiss.0": "Never",
|
||||
"config.section.kinds": "What to notify about",
|
||||
"config.section.popup": "Popup",
|
||||
"config.section.permission": "System notification permission",
|
||||
"config.section.status": "Status",
|
||||
"config.intro": "Every alert comes from this page: a system notification while the window is in the background, the light popup here while it is in the foreground.",
|
||||
"config.saveNote": "The switches are stored in this browser and survive reinstalling the plugin.",
|
||||
"config.dismiss.note": "Hovering a card pauses the countdown; with Never, close each one with ✕ or Esc.",
|
||||
"config.testHint": "The buttons here go straight to the system channel, so you can verify it on its own (they fire even while the window is in front).",
|
||||
"config.testAny": "Test alert",
|
||||
"config.testSystem": "Test system notification",
|
||||
@@ -308,24 +319,41 @@ function createStore(initial) {
|
||||
|
||||
// src/client/core/storage.js
|
||||
var STORAGE_KEY = "dsh-plugin/session-notify";
|
||||
function readStoredKinds() {
|
||||
const kinds = { completion: true, approval: true, question: true };
|
||||
function defaults() {
|
||||
return { completion: true, approval: true, question: true, dismissAfterMs: TOAST_DURATION_MS };
|
||||
}
|
||||
function readStored() {
|
||||
const preferences = defaults();
|
||||
try {
|
||||
const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
|
||||
if (typeof raw !== "string" || raw === "") return kinds;
|
||||
if (typeof raw !== "string" || raw === "") return preferences;
|
||||
const stored = JSON.parse(raw);
|
||||
for (const kind of KINDS) {
|
||||
if (typeof stored?.[kind] === "boolean") kinds[kind] = stored[kind];
|
||||
if (typeof stored?.[kind] === "boolean") preferences[kind] = stored[kind];
|
||||
}
|
||||
if (DISMISS_CHOICES.includes(stored?.dismissAfterMs)) preferences.dismissAfterMs = stored.dismissAfterMs;
|
||||
} catch {
|
||||
}
|
||||
return kinds;
|
||||
return preferences;
|
||||
}
|
||||
function writeStored(patch) {
|
||||
try {
|
||||
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify({ ...readStored(), ...patch }));
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
function readStoredKinds() {
|
||||
const { completion, approval, question } = readStored();
|
||||
return { completion, approval, question };
|
||||
}
|
||||
function writeStoredKinds(kinds) {
|
||||
try {
|
||||
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(kinds));
|
||||
} catch {
|
||||
}
|
||||
writeStored(kinds);
|
||||
}
|
||||
function readStoredDismissMs() {
|
||||
return readStored().dismissAfterMs;
|
||||
}
|
||||
function writeStoredDismissMs(ms) {
|
||||
writeStored({ dismissAfterMs: ms });
|
||||
}
|
||||
|
||||
// src/client/core/copy.js
|
||||
@@ -333,7 +361,7 @@ function createCopy(t) {
|
||||
const copyFor = (candidate) => {
|
||||
const title = candidate.title === "" ? t("body.untitled") : candidate.title;
|
||||
if (candidate.kind === "completion") {
|
||||
return { kind: "completion", title: t("notification.completion"), body: t("body.completion", { title }) };
|
||||
return { kind: "completion", title: t("notification.completion"), body: title };
|
||||
}
|
||||
if (candidate.pendingKind === "plan-review") {
|
||||
return { kind: "question", title: t("notification.question"), body: t("body.planReview", { title }) };
|
||||
@@ -365,8 +393,14 @@ function createCopy(t) {
|
||||
// src/client/core/toasts.js
|
||||
function createToastStack({ store, copy }) {
|
||||
const timers = /* @__PURE__ */ new Map();
|
||||
const held = /* @__PURE__ */ new Set();
|
||||
let seq = 0;
|
||||
const lifetime = () => {
|
||||
const configured = store.getSnapshot().dismissAfterMs;
|
||||
return typeof configured === "number" && configured >= 0 ? configured : TOAST_DURATION_MS;
|
||||
};
|
||||
const closeToast = (id) => {
|
||||
held.delete(id);
|
||||
const timer = timers.get(id);
|
||||
if (timer !== void 0) {
|
||||
clearTimeout(timer);
|
||||
@@ -377,16 +411,26 @@ function createToastStack({ store, copy }) {
|
||||
store.set({ ...snapshot, toasts: snapshot.toasts.filter((toast) => toast.id !== id) });
|
||||
};
|
||||
const armToast = (id) => {
|
||||
held.delete(id);
|
||||
const timer = timers.get(id);
|
||||
if (timer !== void 0) clearTimeout(timer);
|
||||
timers.set(id, setTimeout(() => closeToast(id), TOAST_DURATION_MS));
|
||||
timers.delete(id);
|
||||
const duration = lifetime();
|
||||
if (duration === 0) return;
|
||||
timers.set(id, setTimeout(() => closeToast(id), duration));
|
||||
};
|
||||
const holdToast = (id) => {
|
||||
held.add(id);
|
||||
const timer = timers.get(id);
|
||||
if (timer === void 0) return;
|
||||
clearTimeout(timer);
|
||||
timers.delete(id);
|
||||
};
|
||||
const rearmAll = () => {
|
||||
for (const toast of store.getSnapshot().toasts) {
|
||||
if (!held.has(toast.id)) armToast(toast.id);
|
||||
}
|
||||
};
|
||||
const showToast = (candidate) => {
|
||||
const notice = copy.copyFor(candidate);
|
||||
seq += 1;
|
||||
@@ -405,6 +449,7 @@ function createToastStack({ store, copy }) {
|
||||
clearTimeout(timer);
|
||||
timers.delete(dropped.id);
|
||||
}
|
||||
held.delete(dropped.id);
|
||||
}
|
||||
store.set({ ...snapshot, toasts: next.slice(0, TOAST_LIMIT) });
|
||||
armToast(toast.id);
|
||||
@@ -412,8 +457,9 @@ function createToastStack({ store, copy }) {
|
||||
const dispose = () => {
|
||||
for (const timer of timers.values()) clearTimeout(timer);
|
||||
timers.clear();
|
||||
held.clear();
|
||||
};
|
||||
return { showToast, closeToast, armToast, holdToast, dispose };
|
||||
return { showToast, closeToast, armToast, holdToast, rearmAll, dispose };
|
||||
}
|
||||
|
||||
// src/client/core/replay.js
|
||||
@@ -758,7 +804,13 @@ function createActions({ store, t, delivery, system, toasts, publish }) {
|
||||
writeStoredKinds(kinds);
|
||||
store.set({ ...snapshot, kinds });
|
||||
};
|
||||
return { sendTest, sendTestSystem, requestPermission, setKindEnabled };
|
||||
const setDismissAfter = (ms) => {
|
||||
writeStoredDismissMs(ms);
|
||||
const snapshot = store.getSnapshot();
|
||||
store.set({ ...snapshot, dismissAfterMs: ms });
|
||||
toasts.rearmAll();
|
||||
};
|
||||
return { sendTest, sendTestSystem, requestPermission, setKindEnabled, setDismissAfter };
|
||||
}
|
||||
|
||||
// src/client/ui/ToastLayer.js
|
||||
@@ -918,6 +970,11 @@ var CSS = `
|
||||
.dsn-row.is-action{justify-content:flex-end;padding:12px 0 0}
|
||||
.dsn-row-text{display:flex;flex-direction:column;gap:2px;min-width:0}
|
||||
.dsn-row-title{color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
||||
.dsn-chips{display:flex;align-items:center;gap:6px;flex:none}
|
||||
.dsn-chip{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;height:28px;min-width:46px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-sm);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12.5px;line-height:18px;cursor:pointer}
|
||||
.dsn-chip:hover:not([aria-checked='true']){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
||||
.dsn-chip[aria-checked='true']{border-color:transparent;background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary-foreground);cursor:default}
|
||||
.dsn-chip:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}
|
||||
.dsn-row-sub{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
||||
.dsn-state{display:inline-flex;align-items:center;gap:8px;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
||||
.dsn-dot{flex:none;width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-state-success-primary)}
|
||||
@@ -1013,12 +1070,12 @@ function ToastLayer(props) {
|
||||
}
|
||||
|
||||
// src/client/ui/ConfigSection.js
|
||||
var React5 = __toESM(require("react"), 1);
|
||||
var React6 = __toESM(require("react"), 1);
|
||||
|
||||
// src/client/ui/KindRow.js
|
||||
// src/client/ui/DismissRow.js
|
||||
var React4 = __toESM(require("react"), 1);
|
||||
var h3 = React4.createElement;
|
||||
function KindRow(props) {
|
||||
function DismissRow(props) {
|
||||
return h3(
|
||||
"div",
|
||||
{ className: "dsn-row" },
|
||||
@@ -1027,7 +1084,34 @@ function KindRow(props) {
|
||||
{ className: "dsn-row-text" },
|
||||
h3("div", { className: "dsn-row-title" }, props.label)
|
||||
),
|
||||
h3("button", {
|
||||
h3(
|
||||
"div",
|
||||
{ className: "dsn-chips", role: "radiogroup", "aria-label": props.label },
|
||||
props.options.map((option) => h3("button", {
|
||||
key: String(option.value),
|
||||
type: "button",
|
||||
role: "radio",
|
||||
className: "dsn-chip",
|
||||
"aria-checked": option.value === props.value,
|
||||
onClick: () => props.onChange(option.value)
|
||||
}, option.label))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// src/client/ui/KindRow.js
|
||||
var React5 = __toESM(require("react"), 1);
|
||||
var h4 = React5.createElement;
|
||||
function KindRow(props) {
|
||||
return h4(
|
||||
"div",
|
||||
{ className: "dsn-row" },
|
||||
h4(
|
||||
"div",
|
||||
{ className: "dsn-row-text" },
|
||||
h4("div", { className: "dsn-row-title" }, props.label)
|
||||
),
|
||||
h4("button", {
|
||||
type: "button",
|
||||
role: "switch",
|
||||
className: "dsn-switch",
|
||||
@@ -1035,16 +1119,16 @@ function KindRow(props) {
|
||||
"aria-label": props.label,
|
||||
disabled: props.disabled,
|
||||
onClick: props.onToggle
|
||||
}, h3("span", { className: "dsn-thumb" }))
|
||||
}, h4("span", { className: "dsn-thumb" }))
|
||||
);
|
||||
}
|
||||
|
||||
// src/client/ui/ConfigSection.js
|
||||
var h4 = React5.createElement;
|
||||
var h5 = React6.createElement;
|
||||
function ConfigSection(props) {
|
||||
const snapshot = useOwnStore(props.store);
|
||||
const tr = seatTranslator(props, props.fallbackT);
|
||||
if (props.view === "summary") return h4(React5.Fragment, null, tr("settings.description"));
|
||||
if (props.view === "summary") return h5(React6.Fragment, null, tr("settings.description"));
|
||||
const permission = snapshot.permission;
|
||||
const supported = permission !== "unsupported";
|
||||
const failed = permission === "denied" || snapshot.promptError !== "";
|
||||
@@ -1057,25 +1141,25 @@ function ConfigSection(props) {
|
||||
const healthText = health.state === "watching" ? tr("settings.health.watching") : health.state === "noHooks" ? tr("settings.health.noHooks") : health.state === "error" ? tr("settings.health.error", { message: health.message }) : tr("settings.health.starting");
|
||||
const healthFailed = health.state !== "watching" && health.state !== "starting";
|
||||
const delivery = props.copy.deliveryFacts(snapshot.lastDelivery, tr);
|
||||
return h4(
|
||||
React5.Fragment,
|
||||
return h5(
|
||||
React6.Fragment,
|
||||
null,
|
||||
h4("style", null, CSS),
|
||||
h4(
|
||||
h5("style", null, CSS),
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-page" },
|
||||
// The page header already shows this bundle's title and description, so this
|
||||
// lede says what the header cannot: how an alert travels, and that the
|
||||
// channel is chosen at delivery time.
|
||||
h4("p", { className: "dsn-lede" }, tr("config.intro")),
|
||||
h4(
|
||||
h5("p", { className: "dsn-lede" }, tr("config.intro")),
|
||||
h5(
|
||||
"section",
|
||||
{ className: "dsn-group" },
|
||||
h4("h3", { className: "dsn-group-title" }, tr("config.section.kinds")),
|
||||
h4(
|
||||
h5("h3", { className: "dsn-group-title" }, tr("config.section.kinds")),
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-list" },
|
||||
KINDS.map((kind) => h4(KindRow, {
|
||||
KINDS.map((kind) => h5(KindRow, {
|
||||
key: kind,
|
||||
label: tr(`settings.kind.${kind}`),
|
||||
checked: snapshot.kinds[kind] === true,
|
||||
@@ -1083,33 +1167,49 @@ function ConfigSection(props) {
|
||||
onToggle: () => props.setKind(kind, snapshot.kinds[kind] !== true)
|
||||
}))
|
||||
),
|
||||
h4("p", { className: "dsn-group-note" }, tr("config.saveNote"))
|
||||
h5("p", { className: "dsn-group-note" }, tr("config.saveNote"))
|
||||
),
|
||||
h4(
|
||||
h5(
|
||||
"section",
|
||||
{ className: "dsn-group" },
|
||||
h4("h3", { className: "dsn-group-title" }, tr("config.section.permission")),
|
||||
h4(
|
||||
h5("h3", { className: "dsn-group-title" }, tr("config.section.popup")),
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-list" },
|
||||
h4(
|
||||
h5(DismissRow, {
|
||||
label: tr("settings.dismiss.label"),
|
||||
options: DISMISS_CHOICES.map((value) => ({ value, label: tr(`settings.dismiss.${value}`) })),
|
||||
value: snapshot.dismissAfterMs,
|
||||
onChange: props.setDismiss
|
||||
})
|
||||
),
|
||||
h5("p", { className: "dsn-group-note" }, tr("config.dismiss.note"))
|
||||
),
|
||||
h5(
|
||||
"section",
|
||||
{ className: "dsn-group" },
|
||||
h5("h3", { className: "dsn-group-title" }, tr("config.section.permission")),
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-list" },
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-row" },
|
||||
h4(
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-row-text" },
|
||||
h4(
|
||||
h5(
|
||||
"span",
|
||||
{
|
||||
className: "dsn-state",
|
||||
role: failed ? "alert" : "status"
|
||||
},
|
||||
h4("span", { className: `dsn-dot${dotClass}` }),
|
||||
h5("span", { className: `dsn-dot${dotClass}` }),
|
||||
permissionText
|
||||
),
|
||||
h4("p", { className: "dsn-row-sub" }, permissionNote)
|
||||
h5("p", { className: "dsn-row-sub" }, permissionNote)
|
||||
),
|
||||
h4("button", {
|
||||
h5("button", {
|
||||
type: "button",
|
||||
className: `dsn-button${supported && permission !== "granted" ? " is-primary" : ""}`,
|
||||
disabled: !supported,
|
||||
@@ -1118,51 +1218,51 @@ function ConfigSection(props) {
|
||||
)
|
||||
)
|
||||
),
|
||||
h4(
|
||||
h5(
|
||||
"section",
|
||||
{ className: "dsn-group" },
|
||||
h4("h3", { className: "dsn-group-title" }, tr("config.section.status")),
|
||||
h4(
|
||||
h5("h3", { className: "dsn-group-title" }, tr("config.section.status")),
|
||||
h5(
|
||||
"dl",
|
||||
{ className: "dsn-facts" },
|
||||
h4("dt", { className: "dsn-fact-key" }, tr("config.diag.window")),
|
||||
h4(
|
||||
h5("dt", { className: "dsn-fact-key" }, tr("config.diag.window")),
|
||||
h5(
|
||||
"dd",
|
||||
{ className: "dsn-fact" },
|
||||
h4("p", { className: "dsn-fact-value" }, tr(windowIsAway() ? "config.diag.away" : "config.diag.inFront"))
|
||||
h5("p", { className: "dsn-fact-value" }, tr(windowIsAway() ? "config.diag.away" : "config.diag.inFront"))
|
||||
),
|
||||
h4("dt", { className: "dsn-fact-key" }, tr("config.diag.watch")),
|
||||
h4(
|
||||
h5("dt", { className: "dsn-fact-key" }, tr("config.diag.watch")),
|
||||
h5(
|
||||
"dd",
|
||||
{ className: "dsn-fact" },
|
||||
h4("p", {
|
||||
h5("p", {
|
||||
className: `dsn-fact-value${healthFailed ? " is-error" : ""}`,
|
||||
role: healthFailed ? "alert" : "status"
|
||||
}, healthText)
|
||||
),
|
||||
h4("dt", { className: "dsn-fact-key" }, tr("config.diag.last")),
|
||||
h4(
|
||||
h5("dt", { className: "dsn-fact-key" }, tr("config.diag.last")),
|
||||
h5(
|
||||
"dd",
|
||||
{ className: "dsn-fact" },
|
||||
delivery === null ? h4("p", { className: "dsn-fact-value" }, tr("config.diag.none")) : [
|
||||
h4("p", { key: "line", className: "dsn-fact-value" }, `${delivery.at} · ${delivery.outcome}`),
|
||||
delivery.title === "" ? null : h4("p", { key: "title", className: "dsn-fact-title" }, delivery.title)
|
||||
delivery === null ? h5("p", { className: "dsn-fact-value" }, tr("config.diag.none")) : [
|
||||
h5("p", { key: "line", className: "dsn-fact-value" }, `${delivery.at} · ${delivery.outcome}`),
|
||||
delivery.title === "" ? null : h5("p", { key: "title", className: "dsn-fact-title" }, delivery.title)
|
||||
]
|
||||
),
|
||||
h4("dt", { className: "dsn-fact-key" }, tr("config.diag.replay")),
|
||||
h4(
|
||||
h5("dt", { className: "dsn-fact-key" }, tr("config.diag.replay")),
|
||||
h5(
|
||||
"dd",
|
||||
{ className: "dsn-fact" },
|
||||
h4("p", { className: "dsn-fact-value" }, props.copy.replayValue(props.replayCount(), tr))
|
||||
h5("p", { className: "dsn-fact-value" }, props.copy.replayValue(props.replayCount(), tr))
|
||||
)
|
||||
),
|
||||
h4(
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-list" },
|
||||
h4(
|
||||
h5(
|
||||
"div",
|
||||
{ className: "dsn-row is-action" },
|
||||
h4("button", {
|
||||
h5("button", {
|
||||
type: "button",
|
||||
className: "dsn-button",
|
||||
onClick: props.testAny
|
||||
@@ -1175,15 +1275,15 @@ function ConfigSection(props) {
|
||||
}
|
||||
|
||||
// src/client/ui/NotifyObserver.js
|
||||
var React6 = __toESM(require("react"), 1);
|
||||
var React7 = __toESM(require("react"), 1);
|
||||
function NotifyObserver(props) {
|
||||
useWindowState();
|
||||
const useSessions = props.useSessions;
|
||||
const useSessionStatus = props.useSessionStatus;
|
||||
const select = React6.useCallback((snapshot) => snapshot, []);
|
||||
const select = React7.useCallback((snapshot) => snapshot, []);
|
||||
const list = typeof useSessions === "function" ? useSessions(select) : void 0;
|
||||
const status = typeof useSessionStatus === "function" ? useSessionStatus(select) : void 0;
|
||||
React6.useEffect(() => {
|
||||
React7.useEffect(() => {
|
||||
try {
|
||||
if (typeof props.useSessions !== "function" || typeof props.useSessionStatus !== "function") {
|
||||
props.observation.reportHealth({ state: "noHooks", message: "" });
|
||||
@@ -1210,6 +1310,7 @@ function apply(ctx) {
|
||||
const store = createStore({
|
||||
permission: notificationPermission(),
|
||||
kinds: readStoredKinds(),
|
||||
dismissAfterMs: readStoredDismissMs(),
|
||||
toasts: [],
|
||||
promptError: "",
|
||||
health: { state: "starting", message: "" },
|
||||
@@ -1312,7 +1413,8 @@ function apply(ctx) {
|
||||
ask: actions.requestPermission,
|
||||
testAny: actions.sendTest,
|
||||
testSystem: actions.sendTestSystem,
|
||||
setKind: actions.setKindEnabled
|
||||
setKind: actions.setKindEnabled,
|
||||
setDismiss: actions.setDismissAfter
|
||||
})
|
||||
}, ConfigSection));
|
||||
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
||||
|
||||
Reference in New Issue
Block a user