dsh-notify-me 1.10.2 → 1.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to **dsh-notify-me** are documented here.
4
4
 
5
+ ## [1.10.4] — 2026-10-21
6
+
7
+ ### 新增
8
+
9
+ - **桌面端审批按钮落到页内卡片**:桌面壳在 `dsh-app:` 协议下拿不到 Service Worker(注册被拒),带按钮的审批通知因此只剩标题、点不出「同意 / 拒绝」。现在这种拿不到 SW 的情形会在窗口角落画一张卡片:审批标题与正文、`同意` / `拒绝` 两个按钮、正文过长时可就地展开「详情」,12 秒自动收起,同一条审批不叠第二张卡。按钮走的是和系统通知完全同一条裁决路径(`allowed-once` / `rejected`),裁完即撤卡。Web 端(SW 可用)行为不变,仍是带按钮的系统通知。
10
+
11
+ ### 修复
12
+
13
+ - 页面里写的 `api.version` 与 `package.json` 对齐(1.10.3 的发版提交只改了 `package.json`,页内版本号停在 1.10.2,冒烟断言因此一开始就是红的)。
14
+
15
+ ### 测试
16
+
17
+ - 冒烟新增 12 组断言覆盖桌面降级卡片:出现时机、两个按钮、详情展开/收起、点卡片本体不撤卡、同意、拒绝、审批从状态里消失时撤卡、同一条审批不叠卡、开关关闭时不画卡、12 秒回收、Web 路径不回归。
18
+
19
+ ## [1.10.3] — 2026-10-21
20
+
21
+ ### 修复
22
+
23
+ - **桌面端同类系统通知只弹第一条**(#15):普通通知的 `tag` 原本按类别固定(`dsh-notify-me-<kind>`),靠 `renotify: true` 让同 tag 的新通知替换并重新提示;Electron 不认这个标志,同 tag 的第二条被静默替换,横幅不再出现。现改为每条普通通知独立 tag(`dsh-notify-me-<kind>-<seq>-<ts>`,计数器防同毫秒碰撞)。审批通知的 tag 保持稳定,`closeNotificationTag()` 撤回逻辑不变。
24
+
25
+ ### 测试
26
+
27
+ - 冒烟新增 `uniqueToastTags()` 回归用例:连发三次「回复完成」断言三条 tag 互不相同、审批 tag 前缀不变;`npm test` 两套件通过。
28
+
5
29
  ## [1.10.2] — 2026-10-21
6
30
 
7
31
  ### 变更
package/README.en.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![npm total downloads](https://img.shields.io/npm/dt/dsh-notify-me?style=flat-square&label=total%20downloads&color=1F883D)](https://www.npmjs.com/package/dsh-notify-me)
14
14
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
15
15
  [![DSH Web and Desktop](https://img.shields.io/badge/DSH-Web%20%7C%20Desktop-blue?style=flat-square)](#installation)
16
- [![Client bundle size](https://img.shields.io/badge/client-%7E173%20KiB-green?style=flat-square)](lib/client.js)
16
+ [![Package size](https://img.shields.io/npm/unpacked-size/dsh-notify-me?style=flat-square&label=package%20size)](https://www.npmjs.com/package/dsh-notify-me)
17
17
  [![DSH Compatibility](https://img.shields.io/badge/DSH-tested%200.2.1--rc.2-6366f1.svg?style=flat-square)](#compatibility)
18
18
  [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
19
19
 
@@ -47,7 +47,7 @@ A notification plugin for DSH Web / Desktop, so you can keep full access off wit
47
47
 
48
48
  ![The Notify me settings page: header, how you get told, when to alert, try it, and the collapsed Advanced block](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/settings-en.png)
49
49
 
50
- The real settings component (v1.10.2 defaults) rendered in a standalone demo shell. Alert type is a segmented control, and row explanations live in a hover tooltip that appears when the pointer rests on a title, which is why only the titles show here.
50
+ Just the plugin panel (the real component, v1.10.2 defaults) without the DSH navigation sidebar. Alert type is a segmented control, and row explanations live in a hover tooltip that appears when the pointer rests on a title, which is why only the titles show here.
51
51
  </details>
52
52
 
53
53
  ## Installation
@@ -74,17 +74,17 @@ The Electron application manages the `desktop` profile. Do not change the Web co
74
74
  ## First-use check
75
75
 
76
76
  1. Click once in the DSH page and allow notifications when prompted. If you previously declined, allow them in the browser's site settings. The operating system must also allow notifications from the browser / DSH Desktop.
77
- 2. Open **Settings → Notify me**. Confirm that the section and plugin version at its foot appear, and leave the master switch, system notifications, and sound enabled.
77
+ 2. Open **Settings → Notify me**. Confirm that the section and plugin version at its foot appear, and leave the master switch, the alert type on system notifications, and sound enabled.
78
78
  3. Click **Test "reply finished"** on the settings page. A test notification should appear and sound according to your cue settings. This local test does not forward to external endpoints and ignores the "while the page is open" toggle.
79
79
 
80
- Web users can also test approval buttons. On Desktop that test sends a plain notification: **no Approve / Reject buttons is the expected result**.
80
+ Web users can also test approval buttons. On Desktop the shell cannot register a Service Worker, so this test draws an in-page card with Approve / Reject in the window corner (since 1.10.4; earlier versions sent a plain notification).
81
81
 
82
82
  **Optional console check** — in environments with DevTools, you can also run:
83
83
 
84
84
  ```js
85
85
  window.__dshNotifyMe.test("done") // "reply finished" sample
86
86
  window.__dshNotifyMe.test("attention") // "needs your input" sample
87
- window.__dshNotifyMe.test("approval") // Approve / Reject with an active Web bridge; buttonless on Desktop
87
+ window.__dshNotifyMe.test("approval") // Approve / Reject with an active Web bridge; an in-page card on Desktop
88
88
  window.__dshNotifyMe.debug() // the bridge field reports the button channel: "active" = buttons work
89
89
  ```
90
90
 
@@ -108,7 +108,7 @@ dsh-notify-me decouples **supervision** from **sitting in front of the screen**:
108
108
  | 🔔 Input needed | Alerts even in the foreground; the conversation on screen keeps only a title marker |
109
109
  | ✅ Reply finished | Alerts while the page is hidden / backgrounded |
110
110
 
111
- **Input needed** covers approval requests, plan review, and questions (`ask_user_question`). Supported Web environments show Approve / Reject buttons; Desktop does not.
111
+ **Input needed** covers approval requests, plan review, and questions (`ask_user_question`). Approvals carry Approve / Reject: on Web as a system notification with buttons, on Desktop (which cannot register a Service Worker) as a card drawn in the window corner.
112
112
 
113
113
  **Reply finished** also covers background sessions. A finished but unread session keeps the `✅ Reply finished ·` title marker until opened. Title markers are independent of the "while the page is open" toggles.
114
114
 
@@ -123,9 +123,9 @@ Open **Settings → Notify me**. The page runs as how you get told, when to aler
123
123
  - **Enable reminders**: master switch — off means no toasts, no sounds, and the tab title is left alone;
124
124
  - **Alert type (pick one)**: one segmented control — system notifications on the left, in-page overlay on the right; whichever you pick is the only channel used;
125
125
  - **System notifications** (default): native toasts landing in the Windows / macOS notification center; needs notification permission;
126
- - **In-page overlay**: cards drawn in the page corner, on whichever display the window sits on — system notifications only land on the primary display. No permission needed, and the Desktop shell can Approve / Reject right on the card. Click the row open to pick one of four corners when it is the chosen channel. See [Secondary displays and the overlay](#secondary-displays-and-the-overlay);
126
+ - **In-page overlay**: cards drawn in the page corner, on whichever display the window sits on — system notifications only land on the primary display. No permission needed, and the Desktop shell can Approve / Reject right on the card. With the overlay chosen, a Corner row appears; open it to pick one of four positions. See [Secondary displays and the overlay](#secondary-displays-and-the-overlay);
127
127
  - **Sound / Volume**: the switch is direct; the slider folds into that row — click the title (▸ turns into ▾) to reveal it. What it plays comes from Advanced → Alert sound;
128
- - **Decide approvals from the toast** (on by default): approval toasts carry Approve / Reject that settle the request in place. **Desktop notifications have no buttons** (`dsh-app://` cannot register a Service Worker) — pick the in-page overlay as the channel instead; the page states plainly which channel can carry buttons right now.
128
+ - **Decide approvals from the toast** (on by default): approvals carry Approve / Reject that settle the request in place. On Web that is a system notification with buttons; on Desktop `dsh-app://` cannot register a Service Worker, so the buttons are drawn on a card in the window corner (auto-dismissed after 12 s, one card per approval). You can also switch the channel to the in-page overlay and decide there.
129
129
 
130
130
  ### Secondary displays and the overlay
131
131
 
@@ -133,10 +133,10 @@ Windows draws system notification banners on the primary display only. There is
133
133
 
134
134
  The **in-page overlay** skips the notification center and draws the card inside the DSH page instead: wherever the window is, the card is there too.
135
135
 
136
- - **Position** — click the In-page overlay row open (▸ turns into ▾), then pick top right / bottom right / top left / bottom left; the row shows the current value. Default bottom right, changes apply immediately;
136
+ - **Position** — with the in-page overlay chosen, a Corner row appears showing the current value (bottom right by default); click it open (▸ turns into ▾), then pick top right / bottom right / top left / bottom left. Changes apply immediately;
137
137
  - **No notification permission needed** — it works when permission was never granted, and on the Desktop shell where buttonless toasts are all the OS channel can do;
138
138
  - **Approval cards carry Approve / Reject** — the same decision as Allow once / Reject on the approval card, and they work on the Desktop too (no Service Worker involved). Clicking the card body opens its conversation;
139
- - **Lifetime** — "needs you" cards retire after 12s, "reply finished" after 6s, and an approval card with buttons after 18s; a card or its close button retires one earlier. At most 4 cards stack at once;
139
+ - **Lifetime** — "needs you" and "reply finished" cards retire after 5s each, an approval card with buttons after 12s (system toasts use the same two numbers); a card or its close button retires one earlier. At most 4 cards stack at once;
140
140
  - **Relation to system notifications** — it is one or the other. Both used to be switchable at once, which meant every alert arrived twice and the pair covered each other; 1.10.0 made it a single choice, and 1.10.2 turns that into one segmented row. An old config with the overlay on migrates to it.
141
141
 
142
142
  The overlay only knows which screen the window is on, not which screen you are looking at. If the window stays on the primary display while you watch another one, it stays invisible — same as a toast.
@@ -189,7 +189,7 @@ The last block inside Advanced. The page protocol is detected automatically to t
189
189
  <summary>Alert language, Desktop window raising, reset</summary>
190
190
 
191
191
  - **Alert language**: follow the UI / Simplified Chinese / English — governs alert copy and the `🔔 …` title marker;
192
- - **How the Desktop window is raised**: `window.focus()` cannot lift a native Electron window, so since 1.5.2 the page drives the app's own `dsh://open` deep link — it works even when the window is parked in the tray;
192
+ - **How the Desktop window is raised**: `window.focus()` cannot lift a native Electron window, so since 1.5.2 the page drives the app's own `dsh://open` deep link — measured working on DSH Desktop 0.2.0-rc.2, flaky on 0.2.1-rc.2 / Windows (see the next section);
193
193
  - **Reset settings**: restores every switch and cue setting to the factory defaults (webhook targets and chosen audio files are kept).
194
194
 
195
195
  </details>
@@ -200,8 +200,9 @@ The last block inside Advanced. The page protocol is detected automatically to t
200
200
 
201
201
  ## How it alerts
202
202
 
203
- - **System notification** — a toast through the browser into the Windows action center / macOS Notification Center (clicking it opens the conversation it came from; supported Web environments offer approval buttons, Desktop does not)
204
- - **In-page overlay** — an alert card drawn in the corner of the DSH page, on whatever display the window is on, optionally with Approve / Reject buttons; see [Secondary displays and the overlay](#secondary-displays-and-the-overlay)
203
+ - **Alert channel (pick one — the two never run together)**:
204
+ - **System notification** — a toast through the browser into the Windows action center / macOS Notification Center (clicking it opens the conversation it came from; supported Web environments offer approval buttons, Desktop does not)
205
+ - **In-page overlay** — an alert card drawn in the corner of the DSH page, on whatever display the window is on, optionally with Approve / Reject buttons; see [Secondary displays and the overlay](#secondary-displays-and-the-overlay)
205
206
  - **Sound** — WebAudio beeps by default (distinct patterns for "needs you" vs "done"); each kind can instead play a picked audio file, or hand the sound over to the operating system's own notification sound
206
207
  - **Tab title marker** — while something is waiting on you, the tab title is prefixed with `🔔 Action needed · …` / `🔔 需要你 · …`; while a background session is **finished but unread**, with `✅ Reply finished · …` / `✅ 回复完成 · …`, gone once you open that session (or it starts running again). Both kinds can be up together, "needs you" first; a host title change is rebased under the markers
207
208
  - **External notifications** (optional, default off) — the same alert is also forwarded to the push targets you configure: Discord Webhook / Bark / Gotify / custom webhook — so the push channel on your phone picks it up while you step away from the machine
@@ -214,7 +215,7 @@ Per-release adaptation records live in [package.json](package.json), under `dsh.
214
215
 
215
216
  Verified to actually activate on `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2` and `0.2.0-rc.2` (each booted in its own profile: the plugin entry lands in the boot page's `__DSH_BOOT__` with its inject dependencies resolved, `/plugins/??dsh-notify-me/client.js&rev=…` serves a build containing the new code, and the Settings → Notifications section registers, i.e. the plugin's `apply` really runs), and previously on `0.1.1-rc.2`.
216
217
 
217
- `0.2.1-rc.2` (current latest) got its own isolated run: an isolated `DSH_HOME` booting `dsh web` (70 boot entries) placed the plugin entry 59th, in the `application` batch, with all three inject targets present and ordered before it; `/plugins/??dsh-notify-me/client.js&rev=…` served the 1.7.0 source build (140,153 B); loaded in a headless Chromium page, `apply` ran — `window.__dshNotifyMe.debug()` reported `uiSession: bound`, `uiSessionSource: sessionStatus`, `bridge: active`. Host-side shapes match `0.2.0-rc.2`; no plugin code change was needed.
218
+ `0.2.1-rc.2` (current latest) got its own isolated run: an isolated `DSH_HOME` booting `dsh web` (70 boot entries) placed the plugin entry 59th, in the `application` batch, with all three inject targets present and ordered before it; `/plugins/??dsh-notify-me/client.js&rev=…` served the then-installed 1.7.0 source build (140,153 B); loaded in a headless Chromium page, `apply` ran — `window.__dshNotifyMe.debug()` reported `uiSession: bound`, `uiSessionSource: sessionStatus`, `bridge: active`. Host-side shapes match `0.2.0-rc.2`; no plugin code change was needed.
218
219
 
219
220
  <details>
220
221
  <summary>Host generations and adaptation diagnostics</summary>
@@ -298,7 +299,7 @@ The reminder core stays dependency-free and self-contained: notification copy re
298
299
 
299
300
  Action buttons (`actions`) belong to persistent notifications shown through `ServiceWorkerRegistration.showNotification()`; passing `actions` to the `new Notification()` constructor throws a `TypeError`. That is a browser rule no plugin can route around, so the bundle holds two jobs in the same bytes: the page half registers the reminder factory as it always did, and the very same file is registered as a Service Worker whose only task is relaying which button was clicked back to the page. The decision itself happens in the page, through the same `PendingApproval.answer('allowed-once' | 'rejected')` the approval card uses — which is why "Approve" is exactly "Allow once" and nothing broader. The "raise a window and switch its conversation" part of a body click is scheduled on the worker half too: `client.focus()` raises exactly one window (a visible one first) and routes the conversation switch to that window alone, so several open DSH tabs never fight over focus or all switch at once.
300
301
 
301
- Every button toast is booked against its interaction key, so it can only decide the request it was raised for. Once the page answers that request or a newer one replaces it, clicking the leftover buttons decides nothing and just closes the notification. Button toasts also skip the auto-dismiss timer: an undecided approval stays in the notification center until you settle it.
302
+ Every button toast is booked against its interaction key, so it can only decide the request it was raised for. Once the page answers that request or a newer one replaces it, clicking the leftover buttons decides nothing and just closes the notification. Button toasts stay 12s (plain ones 5s) and retire on their own, so an approval left undecided there still waits for you in the page.
302
303
 
303
304
  ### How the Desktop window gets raised
304
305
 
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![npm total downloads](https://img.shields.io/npm/dt/dsh-notify-me?style=flat-square&label=total%20downloads&color=1F883D)](https://www.npmjs.com/package/dsh-notify-me)
14
14
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
15
15
  [![DSH Web and Desktop](https://img.shields.io/badge/DSH-Web%20%7C%20Desktop-blue?style=flat-square)](#安装)
16
- [![Client bundle size](https://img.shields.io/badge/client-%7E173%20KiB-green?style=flat-square)](lib/client.js)
16
+ [![Package size](https://img.shields.io/npm/unpacked-size/dsh-notify-me?style=flat-square&label=package%20size)](https://www.npmjs.com/package/dsh-notify-me)
17
17
  [![DSH Compatibility](https://img.shields.io/badge/DSH-%E5%B7%B2%E9%80%82%E9%85%8D%200.2.1--rc.2-6366f1.svg?style=flat-square)](#兼容性)
18
18
  [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
19
19
 
@@ -26,9 +26,9 @@ DSH Web / Desktop 的通知插件,让你敢不开完整访问,也不必一
26
26
  - **需要你操作时**:审批、方案确认、提问,用通知、提示音和标题标记提醒。
27
27
  - **后台回复完成时**:及时回来查看结果,点击通知可回到对应对话。
28
28
  - **离开电脑时**:可选转发到 Discord、Bark、Gotify 或自己的 Webhook。
29
- - **不看主屏时**:提醒方式可以切成页面内浮层,跟着 DSH 窗口出现在你正看着的那块屏(系统通知只会出现在主屏)。
29
+ - **不看主屏时**:把「通知类型」切成「浮层提醒」,卡片就跟着 DSH 窗口出现在你正看着的那块屏(系统通知只会出现在主屏)。
30
30
 
31
- > 页面或桌面应用需保持运行,后台 / 最小化可以。系统通知需要通知权限;审批按钮在支持 Service Worker 的 Web 环境(如 `http://127.0.0.1` 或 `https`)由通知提供,桌面端的通知没有按钮——把「提醒方式」切到页面浮层「页面浮层提醒」后可在浮层上直接裁决(见[副屏与浮层提醒](#副屏与浮层提醒))。
31
+ > 页面或桌面应用需保持运行,后台 / 最小化可以。系统通知需要通知权限;审批按钮在支持 Service Worker 的 Web 环境(如 `http://127.0.0.1` 或 `https`)由通知提供,桌面端的通知没有按钮——把「通知类型」选成「浮层提醒」后可在浮层上直接裁决(见[副屏与浮层提醒](#副屏与浮层提醒))。
32
32
 
33
33
  > 🔎 想找这个插件?可以搜:`dsh-notify-me`、**消息提醒**、**可操作提醒**、**桌面通知**、**需要你操作**、**回复完成提醒**、**后台完成提醒**、**外部通知**、**Discord / Bark / Gotify 推送**(npm 关键词已含以上中英文词)。
34
34
 
@@ -47,7 +47,7 @@ DSH Web / Desktop 的通知插件,让你敢不开完整访问,也不必一
47
47
 
48
48
  ![通知提醒设置页:页眉、提醒方式、什么时候提醒、试一试与折叠的高级设置](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/settings-zh.png)
49
49
 
50
- 真实插件设置组件(v1.10.2 默认配置)在独立演示环境里渲染;「通知类型」是分段选择器,每行的说明在鼠标停在标题文字上时才出现,所以这里只看到标题。
50
+ 只截插件自己的面板(真实组件、v1.10.2 默认配置),不带 DSH 左侧导航栏;「通知类型」是分段选择器,每行说明在鼠标停在标题文字上时才出现,所以这里只看到标题。
51
51
  </details>
52
52
 
53
53
  ## 安装
@@ -74,17 +74,17 @@ dsh plugin --profile web add dsh-notify-me
74
74
  ## 首次验证
75
75
 
76
76
  1. 在 DSH 页面上点击一次,按提示允许通知;若此前拒绝过,在浏览器站点设置中允许通知。操作系统也需要允许对应浏览器 / DSH Desktop 显示通知。
77
- 2. 打开 **设置 → 通知提醒**,确认出现该分区及底部插件版本号,保持「启用提醒」「提示音」开启,提醒方式先留「系统通知」。
77
+ 2. 打开 **设置 → 通知提醒**,确认出现该分区及底部插件版本号,保持「启用提醒」「提示音」开启,通知类型先留「系统通知」。
78
78
  3. 点击设置页的「测试『回复完成』」按钮:应出现一条测试通知,并按当前音效设置播放声音。这个本地测试不会转发到外部通知端点,也不受「页面打开时也提醒」开关限制。
79
79
 
80
- Web 用户可再测试审批按钮;桌面端该测试只发普通通知,**没有「同意 / 拒绝」按钮属于预期行为**。
80
+ Web 用户可再测试审批按钮。桌面端拿不到 Service Worker,这条测试会在窗口角落画一张带「同意 / 拒绝」的卡片(1.10.4 起;之前的版本这里只发普通通知)。
81
81
 
82
82
  **控制台验证(可选)** — 支持 DevTools 的环境也可执行:
83
83
 
84
84
  ```js
85
85
  window.__dshNotifyMe.test("done") // 「完成」示例
86
86
  window.__dshNotifyMe.test("attention") // 「需要你」示例
87
- window.__dshNotifyMe.test("approval") // Web 桥可用时带「同意 / 拒绝」按钮;桌面端为无按钮通知
87
+ window.__dshNotifyMe.test("approval") // 桥可用时带「同意 / 拒绝」按钮;桌面端画在窗口角落的卡片上
88
88
  window.__dshNotifyMe.debug() // 桥接状态看 bridge 字段,active = 按钮可用
89
89
  ```
90
90
 
@@ -104,7 +104,7 @@ dsh-notify-me 把「监督」和「守在屏幕前」解耦:只在真的出现
104
104
  | 🔔 需要你操作 | 页面可见也提醒;当前对话只留标题标记 |
105
105
  | ✅ 回复完成 | 页面隐藏 / 后台时提醒 |
106
106
 
107
- **需要你操作**包括审批请求、方案待确认(plan review)和提问(`ask_user_question`)。支持快捷裁决的 Web 环境会显示「同意 / 拒绝」按钮,桌面端无按钮。
107
+ **需要你操作**包括审批请求、方案待确认(plan review)和提问(`ask_user_question`)。审批带「同意 / 拒绝」:Web 走带按钮的系统通知,桌面端(注册不了 Service Worker)改画在窗口角落的卡片上。
108
108
 
109
109
  **回复完成**也覆盖后台会话;完成但还没看时,标题带 `✅ 回复完成 ·` 标记,打开对应会话即撤。标题标记不受「页面打开时也提醒」开关限制。
110
110
 
@@ -119,9 +119,9 @@ dsh-notify-me 把「监督」和「守在屏幕前」解耦:只在真的出现
119
119
  - **启用提醒** 总开关:关闭后不弹通知、不响提示音,也不改标签页标题;
120
120
  - **通知类型(二选一)**:一行分段选择器,左边「系统通知」、右边「浮层提醒」,点哪个就只走哪个;
121
121
  - **系统通知**(默认):进 Windows / macOS 通知中心,需要通知权限;
122
- - **页面浮层**:在 DSH 页面角落画提醒卡片,窗口在哪块屏它就在哪块屏——系统通知只会画在主屏,主屏不看时用它;不需要通知权限,桌面端也能在卡片上直接「同意 / 拒绝」。选中后可在该行点开选四个角落的位置,见[副屏与浮层提醒](#副屏与浮层提醒);
122
+ - **页面浮层**:在 DSH 页面角落画提醒卡片,窗口在哪块屏它就在哪块屏——系统通知只会画在主屏,主屏不看时用它;不需要通知权限,桌面端也能在卡片上直接「同意 / 拒绝」。选中后卡片里会多出「浮层位置」子行,点开可选四个角落,见[副屏与浮层提醒](#副屏与浮层提醒);
123
123
  - **提示音 / 音量**:开关直接切;音量滑块收在该行里,点一下标题(▸ 变成 ▾)展开。具体播什么由「高级设置 → 音效来源」决定;
124
- - **审批通知上直接裁决**(默认开):审批通知带「同意 / 拒绝」,点一下即完成裁决,不用切回页面。**桌面端的通知没有按钮**(`dsh-app://` 页面无法注册 Service Worker),需要按钮就打开「页面浮层提醒」,改在浮层上裁决;设置页会如实说明当前哪种渠道能给按钮。
124
+ - **审批通知上直接裁决**(默认开):审批带「同意 / 拒绝」,点一下即完成裁决,不用切回页面。Web 走带按钮的系统通知;桌面端 `dsh-app://` 注册不了 Service Worker,于是把按钮画在窗口角落的卡片上(12 秒自动收起,同一条审批不叠第二张卡)。也可以把「通知类型」换成「页面浮层」,改在浮层卡片上裁决。
125
125
 
126
126
  ### 副屏与浮层提醒
127
127
 
@@ -129,10 +129,10 @@ Windows 的系统通知横幅固定画在主屏上,系统没有设置项、浏
129
129
 
130
130
  「页面浮层提醒」绕开系统通知中心,直接在 DSH 页面里画一张卡片:窗口在哪块屏,卡片就出现在哪块屏。
131
131
 
132
- - **位置**:点开「页面浮层提醒」那一行(▸ 变 ▾)再选右上 / 右下 / 左上 / 左下,默认右下,行上直接显示当前值;改完立刻生效;
132
+ - **位置**:选中「浮层提醒」后,「浮层位置」子行出现,行上直接显示当前值(默认右下);点它(▸ 变 ▾)再选右上 / 右下 / 左上 / 左下,改完立刻生效;
133
133
  - **不需要通知权限**:通知权限没给、或桌面端弹不出带按钮的通知时,浮层照常工作;
134
134
  - **审批卡片带「同意 / 拒绝」**:与审批卡片的「允许一次 / 拒绝」等价,桌面端也能用(不依赖 Service Worker);点卡片正文则切回对应对话;
135
- - **停留时间**:普通提醒 5 秒自动收起,带「同意 / 拒绝」的审批卡片 12 秒,系统通知同档(普通 5 秒、带按钮 12 秒);也可以点卡片或右上角叉提前关掉。最多同时叠 4 张;
135
+ - **停留时间**:普通提醒 5 秒自动收起,带「同意 / 拒绝」的审批卡片 12 秒(系统通知用同一套数字);也可以点卡片或右上角叉提前关掉。最多同时叠 4 张;
136
136
  - **与系统通知的关系**:二选一。以前两个开关可以同开,结果同一条提醒弹两遍、还互相挡住;1.10.0 起改成单选,1.10.2 起做成一行分段选择器。老配置里开着浮层的,升级后自动落到浮层。
137
137
 
138
138
  浮层只知道窗口在哪块屏,不知道你人在看哪块屏。窗口留在主屏、人却盯着副屏时,它同样看不见。
@@ -194,12 +194,11 @@ Windows 的系统通知横幅固定画在主屏上,系统没有设置项、浏
194
194
 
195
195
  > 需要浏览器通知权限:页面上点一下 → 允许;或地址栏锁 → 站点设置 → 通知 → 允许 → 刷新。macOS 还要在 系统设置 → 通知 里允许该浏览器。
196
196
 
197
- ## 五种提醒渠道
197
+ ## 提醒渠道
198
198
 
199
- - **系统通知**:经浏览器弹出的系统通知,进 Windows 通知中心或 macOS 通知中心(点击回到提醒所属的对话;支持快捷裁决的 Web 环境可点审批按钮直接裁决,桌面端无按钮)
200
- - **页面浮层**:画在 DSH 页面角落的提醒卡片,落在窗口所在的那块屏,可带「同意 / 拒绝」按钮;见[副屏与浮层提醒](#副屏与浮层提醒)
201
-
202
- 两种渠道在设置里二选一,不会同时出现。
199
+ - **提醒渠道(二选一,不同时出现)**:
200
+ - **系统通知**:经浏览器弹出的系统通知,进 Windows 通知中心或 macOS 通知中心(点击回到提醒所属的对话;支持快捷裁决的 Web 环境可点审批按钮直接裁决,桌面端无按钮)
201
+ - **页面浮层**:画在 DSH 页面角落的提醒卡片,落在窗口所在的那块屏,可带「同意 / 拒绝」按钮;见[副屏与浮层提醒](#副屏与浮层提醒)
203
202
  - **提示音**:默认是 WebAudio 合成音(「需要你」与「完成」使用不同音型);可在设置里为两类提醒分别换成自定义音频文件,或改成让操作系统播放它自己的通知音
204
203
  - **标签页标题标记**:有待处理事项时,标题前出现 `🔔 需要你 · …` / `🔔 Action needed · …`;后台会话**完成但还没看**时出现 `✅ 回复完成 · …` / `✅ Reply finished · …`,打开对应会话(或它再次开跑)即消失。两种标记可同时出现,「需要你」在前;宿主改写标题后标记基于新标题重建
205
204
  - **外部通知**(可选,默认关):按设置里配的推送目标,把同一条提醒转发到 Discord Webhook / Bark / Gotify / 自定义 Webhook——临时离开电脑时,手机上的推送渠道照样收到
@@ -212,7 +211,7 @@ Windows 的系统通知横幅固定画在主屏上,系统没有设置项、浏
212
211
 
213
212
  实测**真正激活**(各自独立 profile 启动:插件条目进启动页 `__DSH_BOOT__` 且注入依赖解析通过、`/plugins/??dsh-notify-me/client.js&rev=…` 返回含新代码的构建,设置里出现「通知提醒」分区,即插件的 `apply` 确实执行)于 `0.1.2-rc.1`、`0.1.5-rc.1`、`0.1.5-rc.2` 与 `0.2.0-rc.2`;更早的 `0.1.1-rc.2` 亦验证过。
214
213
 
215
- `0.2.1-rc.2`(当前 latest)另跑了一轮隔离验证:隔离 `DSH_HOME` 起 `dsh web`(70 个启动条目),插件条目排在第 59、`application` 批,三个注入目标都在且都排在它前面;`/plugins/??dsh-notify-me/client.js&rev=…` 返回 1.7.0 源码构建(140,153 B);无头 Chromium 载入页面后 `apply` 正常执行,`window.__dshNotifyMe.debug()` 报 `uiSession: bound`、`uiSessionSource: sessionStatus`、`bridge: active`。宿主侧的形状与 `0.2.0-rc.2` 一致,没有需要改代码的地方。
214
+ `0.2.1-rc.2`(当前 latest)另跑了一轮隔离验证:隔离 `DSH_HOME` 起 `dsh web`(70 个启动条目),插件条目排在第 59、`application` 批,三个注入目标都在且都排在它前面;`/plugins/??dsh-notify-me/client.js&rev=…` 返回当时装的 1.7.0 源码构建(140,153 B);无头 Chromium 载入页面后 `apply` 正常执行,`window.__dshNotifyMe.debug()` 报 `uiSession: bound`、`uiSessionSource: sessionStatus`、`bridge: active`。宿主侧的形状与 `0.2.0-rc.2` 一致,没有需要改代码的地方。
216
215
 
217
216
  <details>
218
217
  <summary>宿主代际与适配排查细节</summary>
@@ -249,7 +248,7 @@ window.__dshNotifyMe.setConfig({
249
248
  currentHiddenOnly: true, // true = 当前对话的等待只留标题标记(转后台补发)
250
249
  doneHiddenOnly: true, // false = 页面可见时「完成」也提醒
251
250
  ignoreSubagent: true, // true = 后台子代理(subagent)会话不提醒(当前打开的照常)
252
- channel: "toast", // 提醒方式:'toast' 系统通知 | 'overlay' 页面浮层,二选一
251
+ channel: "toast", // 通知类型:'toast' 系统通知 | 'overlay' 页面浮层,二选一
253
252
  sound: true, // 提示音开关(含「系统通知音」模式)
254
253
  soundDone: "synth", // 「完成」音效来源:'synth' 内置 | 'custom' 自定义文件 | 'system' 系统通知音
255
254
  soundAttention: "synth", // 「需要你」音效来源,取值同上
@@ -296,7 +295,7 @@ window.__dshNotifyMe.testExternal() // 显式外发测试:向有
296
295
 
297
296
  通知按钮(`actions`)只属于 Service Worker 弹的持久通知,往 `new Notification()` 里传 `actions` 会直接抛 `TypeError`——这是绕不开的浏览器限制。所以 bundle 一份文件干两件事:页面里照常注册提醒工厂,同一份字节再注册成一个只干转信的 Service Worker,把「点了哪个按钮」(同意 / 拒绝 / 点正文)带回页面。真正的裁决发生在页面里,调的是审批卡片自己用的 `PendingApproval.answer('allowed-once' | 'rejected')`,所以「同意」就是「允许一次」,不多放行任何东西。点正文的「调窗口 + 切对话」同样归 worker 这半侧调度:它用 `client.focus()` 只调起一个窗口(优先当前可见的),并只让那个窗口切对话——多个 DSH 标签页不会互相抢焦点或一起乱切。
298
297
 
299
- 每条按钮通知按交互 key 记账,一次只认领一条等待;请求被页面里答掉、或被新请求顶替之后,残留通知上的按钮再点也不会误裁,只是安静地关掉。带按钮的通知不设自动消失——审批没裁决就一直留在通知中心里等你。
298
+ 每条按钮通知按交互 key 记账,一次只认领一条等待;请求被页面里答掉、或被新请求顶替之后,残留通知上的按钮再点也不会误裁,只是安静地关掉。带按钮的通知停留 12 秒(普通通知 5 秒),到点自动撤;没来得及点就回页面里裁决,卡片一直等着你。
300
299
 
301
300
  ### 桌面端怎么抬窗的
302
301
 
package/lib/client.js CHANGED
@@ -321,13 +321,18 @@
321
321
  // Persist a patch, swap the live module config and re-apply any active
322
322
  // title marker (its prefix follows the notification language).
323
323
  function setConfigLocal(patch) {
324
+ var wasOverlay = config.channel === "overlay";
324
325
  config = writeConfig(patch);
325
326
  applyTitle();
326
- // Overlay re-apply: switching it off takes the cards down at once and a
327
- // new corner moves the layer without waiting for the next alert.
327
+ // Overlay re-apply: leaving the overlay channel takes its cards down at
328
+ // once, and a new corner moves the layer without waiting for the next
329
+ // alert. Cards can also exist on the toast channel (the desktop fallback),
330
+ // so an unrelated patch must not sweep them away.
328
331
  try {
329
- if (config.channel !== "overlay") clearOverlays();
330
- else if (overlayLayer) overlayLayer.className = "dnm-ov-layer dnm-ov-" + overlayCorner();
332
+ if (config.channel !== "overlay") {
333
+ if (wasOverlay) clearOverlays();
334
+ else if (overlayLayer) overlayLayer.className = "dnm-ov-layer dnm-ov-" + overlayCorner();
335
+ } else if (overlayLayer) overlayLayer.className = "dnm-ov-layer dnm-ov-" + overlayCorner();
331
336
  } catch (e) {}
332
337
  return config;
333
338
  }
@@ -384,6 +389,8 @@
384
389
  approve: "同意",
385
390
  reject: "拒绝",
386
391
  dismiss: "关闭这条提醒",
392
+ details: "详情",
393
+ collapse: "收起",
387
394
  testAttention: "这是一条测试提醒:DSH 需要你的操作",
388
395
  testDone: "测试提醒 · 回复完成示例",
389
396
  testApproval: "这是一条测试审批:点「同意 / 拒绝」试试快捷裁决",
@@ -411,6 +418,8 @@
411
418
  approve: "Approve",
412
419
  reject: "Reject",
413
420
  dismiss: "Dismiss this alert",
421
+ details: "Details",
422
+ collapse: "Collapse",
414
423
  testAttention: "This is a test alert: DSH is waiting for your input",
415
424
  testDone: "Test alert · reply-finished sample",
416
425
  testApproval: "This is a test approval: try the Approve / Reject buttons to settle it",
@@ -733,10 +742,26 @@
733
742
  return q;
734
743
  }
735
744
 
745
+ // A plain toast needs a tag unique to that one notification. It used to be
746
+ // "dsh-notify-me-" + kind, which leans on renotify:true to replace the
747
+ // previous banner of the same kind; Electron ignores that flag, so the
748
+ // second toast of a kind silently overwrote the first and never re-alerted
749
+ // (issue #15). A unique tag makes every toast a fresh alert everywhere,
750
+ // while renotify stays meaningful on the web. Date.now() alone collides
751
+ // when two alerts land in the same millisecond, hence the counter; the
752
+ // >>> 0 wrap keeps it from ever going negative. Approval toasts keep their
753
+ // stable "dsh-notify-me-attention-<key>" tag: closeNotificationTag() looks
754
+ // them up by it when the request is settled.
755
+ var toastSeq = 0;
756
+ function uniqueToastTag(kind) {
757
+ toastSeq = (toastSeq + 1) >>> 0;
758
+ return "dsh-notify-me-" + kind + "-" + toastSeq + "-" + Date.now();
759
+ }
760
+
736
761
  function showToast(kind, title, body, sessionId, quick) {
737
762
  if (!canToast()) return;
738
763
  try {
739
- var tag = quick ? quick.tag : "dsh-notify-me-" + kind;
764
+ var tag = quick ? quick.tag : uniqueToastTag(kind);
740
765
  if (quick && bridgeActive()) {
741
766
  var d = texts();
742
767
  try {
@@ -817,9 +842,11 @@
817
842
  ".dnm-ov-attention .dnm-ov-dot{color:var(--dsw-alias-state-warning-primary,#f59e0b);opacity:1}" +
818
843
  ".dnm-ov-done .dnm-ov-dot{color:var(--dsw-alias-state-success-primary,#22c55e);opacity:1}" +
819
844
  ".dnm-ov-title{flex:1;min-width:0;font-weight:600;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}" +
820
- ".dnm-ov-x{flex:none;border:0;background:transparent;color:inherit;opacity:.6;font-size:15px;line-height:15px;padding:0 2px;cursor:pointer;font-family:inherit}" +
821
- ".dnm-ov-x:hover{opacity:1}" +
845
+ ".dnm-ov-x,.dnm-ov-more{flex:none;border:0;background:transparent;color:inherit;opacity:.6;font-size:15px;line-height:15px;padding:0 2px;cursor:pointer;font-family:inherit}" +
846
+ ".dnm-ov-x:hover,.dnm-ov-more:hover{opacity:1}" +
847
+ ".dnm-ov-more{font-size:11px;opacity:.8;white-space:nowrap}" +
822
848
  ".dnm-ov-body{color:var(--dsw-alias-label-secondary,rgba(220,222,226,.86));font-size:12px;line-height:18px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}" +
849
+ ".dnm-ov.dnm-ov-open .dnm-ov-body{display:block;-webkit-line-clamp:unset;max-height:40vh;overflow:auto}" +
823
850
  ".dnm-ov-actions{display:flex;gap:8px;margin-top:2px}" +
824
851
  ".dnm-ov-btn{flex:1;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.4));background:var(--dsw-alias-bg-layer-1,rgba(255,255,255,.06));color:inherit;border-radius:8px;padding:5px 10px;font-size:12px;font-family:inherit;cursor:pointer}" +
825
852
  ".dnm-ov-btn:hover{background:var(--dsw-alias-bg-layer-2,rgba(255,255,255,.12))}" +
@@ -895,17 +922,30 @@
895
922
  }
896
923
  }
897
924
  function overlayCount() { return Object.keys(overlayCards).length; }
898
- // Same inputs as showToast: `quick` is the approval record built by
899
- // quickFor(), and it is what lets an overlay settle the request in place.
900
- function showOverlay(kind, title, body, sessionId, quick) {
901
- if (config.channel !== "overlay") return false;
925
+ // One card builder for both channels: the overlay channel draws with it,
926
+ // and so does the desktop fallback below. `opts` keeps the two behaviours
927
+ // apart: the overlay card is a shortcut (a click opens the conversation and
928
+ // takes the card down), while the fallback card carries a decision the OS
929
+ // notification cannot — so a click must not dismiss it unanswered, and a
930
+ // "详情 / Details" toggle exposes the body that the CSS clamps to 3 lines.
931
+ function drawOverlayCard(kind, title, body, sessionId, quick, opts) {
932
+ opts = opts || {};
902
933
  var layer = ensureOverlayLayer();
903
934
  if (!layer) return false;
935
+ // One card per approval key: a re-delivery (the quieted copy that fires
936
+ // when the page is backgrounded) must not stack a second card.
937
+ if (quick && typeof quick.key === "string") {
938
+ var known = Object.keys(overlayCards);
939
+ for (var k = 0; k < known.length; k++) {
940
+ if (overlayCards[known[k]].key === quick.key) return false;
941
+ }
942
+ }
904
943
  var d = texts();
905
944
  try {
906
945
  var id = "ov" + (++overlaySeq);
946
+ var baseClass = "dnm-ov dnm-ov-" + (kind === "attention" ? "attention" : "done");
907
947
  var card = document.createElement("div");
908
- card.className = "dnm-ov dnm-ov-" + (kind === "attention" ? "attention" : "done");
948
+ card.className = baseClass;
909
949
  card.setAttribute("role", kind === "attention" ? "alert" : "status");
910
950
  var head = document.createElement("div");
911
951
  head.className = "dnm-ov-head";
@@ -914,6 +954,22 @@
914
954
  var label = document.createElement("span");
915
955
  label.className = "dnm-ov-title";
916
956
  label.textContent = String(title == null ? "" : title);
957
+ var more = null;
958
+ if (opts.expand && body) {
959
+ more = document.createElement("button");
960
+ more.type = "button";
961
+ more.className = "dnm-ov-more";
962
+ more.setAttribute("aria-label", d.details);
963
+ more.setAttribute("aria-expanded", "false");
964
+ more.textContent = d.details;
965
+ more.onclick = function (ev) {
966
+ if (ev && ev.stopPropagation) ev.stopPropagation();
967
+ var open = card.className.indexOf("dnm-ov-open") !== -1;
968
+ card.className = baseClass + (open ? "" : " dnm-ov-open");
969
+ more.textContent = open ? d.details : d.collapse;
970
+ more.setAttribute("aria-expanded", open ? "false" : "true");
971
+ };
972
+ }
917
973
  var close = document.createElement("button");
918
974
  close.type = "button";
919
975
  close.className = "dnm-ov-x";
@@ -925,6 +981,7 @@
925
981
  };
926
982
  head.appendChild(dot);
927
983
  head.appendChild(label);
984
+ if (more) head.appendChild(more);
928
985
  head.appendChild(close);
929
986
  card.appendChild(head);
930
987
  if (body) {
@@ -954,10 +1011,12 @@
954
1011
  }
955
1012
  // A click on the card body opens the conversation, exactly like a toast
956
1013
  // body click — and here it is an ordinary page gesture, so it reaches the
957
- // desktop's dsh:// raise path even though a worker click would not.
1014
+ // desktop's dsh:// raise path even though a worker click would not. The
1015
+ // overlay channel also takes the card down; the fallback card stays, so
1016
+ // the decision is still one click away after a look at the conversation.
958
1017
  card.onclick = function () {
959
1018
  if (config.autoFocus) focusSession(sessionId || null);
960
- dismissOverlay(id);
1019
+ if (opts.dismissOnClick !== false) dismissOverlay(id);
961
1020
  };
962
1021
  layer.appendChild(card);
963
1022
  var rec = { card: card, key: (quick && quick.key) ? quick.key : null, kind: kind, timer: null };
@@ -971,6 +1030,29 @@
971
1030
  return true;
972
1031
  } catch (e) { return false; }
973
1032
  }
1033
+ // Same inputs as showToast: `quick` is the approval record built by
1034
+ // quickFor(), and it is what lets an overlay settle the request in place.
1035
+ function showOverlay(kind, title, body, sessionId, quick) {
1036
+ if (config.channel !== "overlay") return false;
1037
+ return drawOverlayCard(kind, title, body, sessionId, quick, { dismissOnClick: true });
1038
+ }
1039
+ // The desktop shell cannot register the Service Worker that OS action
1040
+ // buttons require, so an approval toast on a dsh-app:// page has no button
1041
+ // to click. While the alert channel is system notifications, an approval
1042
+ // therefore also drops a card into the same in-page layer, in the same
1043
+ // corner and with the same styling: its buttons call decidePending(), the
1044
+ // exact path a Web toast button takes. Only states that prove the bridge is
1045
+ // unusable qualify — 'idle'/'registering' are transitional, 'disabled' means
1046
+ // the quick-decision switch is off (quickFor() then returns null, so no card
1047
+ // is drawn at all), and the Web path stays untouched once the worker is up.
1048
+ function showFallbackApprovalCard(kind, title, body, sessionId, quick) {
1049
+ if (kind !== "attention") return false;
1050
+ if (!quick || typeof quick.key !== "string") return false;
1051
+ if (config.channel !== "toast") return false;
1052
+ if (bridgeActive()) return false;
1053
+ if (swState.indexOf("unsupported:") !== 0 && swState.indexOf("failed:") !== 0) return false;
1054
+ return drawOverlayCard(kind, title, body, sessionId, quick, { dismissOnClick: false, expand: true });
1055
+ }
974
1056
  // Sound: WebAudio beeps (no asset needed). The context only runs after a
975
1057
  // user gesture; we unlock it on the first pointer/key event.
976
1058
  var audioCtx = null;
@@ -1325,6 +1407,7 @@
1325
1407
  if (!config.enabled) return;
1326
1408
  showToast(kind, title, body, sessionId, quick);
1327
1409
  showOverlay(kind, title, body, sessionId, quick);
1410
+ showFallbackApprovalCard(kind, title, body, sessionId, quick);
1328
1411
  if (kind === "attention") {
1329
1412
  playAttentionSound();
1330
1413
  } else {
@@ -1964,7 +2047,7 @@
1964
2047
  });
1965
2048
  } catch (e) {}
1966
2049
  // keep in sync with package.json "version" on every release
1967
- api.version = "1.10.2";
2050
+ api.version = "1.10.4";
1968
2051
  api.setConfig = function (patch) { return setConfigLocal(patch); };
1969
2052
  api.resetConfig = function () {
1970
2053
  try {
@@ -2177,16 +2260,16 @@
2177
2260
  autoFocus: "点击通知回到对应对话",
2178
2261
  autoFocusHint: "点通知会切到这条提醒所属的对话;关掉后点击不做任何事。",
2179
2262
  quickActions: "审批通知上直接裁决",
2180
- quickActionsHint: "审批通知上带「同意 / 拒绝」,点一下即完成裁决,不用切回页面。需要 Service Worker(http://127.0.0.1 或 https),用不了时退回普通通知。",
2263
+ quickActionsHint: "审批通知上带「同意 / 拒绝」,点一下即完成裁决,不用切回页面。需要 Service Worker(http://127.0.0.1 或 https);用不了时按钮改画在窗口角落的卡片上(桌面端就是这条路)。",
2181
2264
  quickApprovalNote: "「同意」等价于审批卡片上的「允许一次」,只放行这一次。",
2182
2265
  bridgeOff: "快捷裁决按钮暂不可用(",
2183
- bridgeOffTail: "):审批通知会退回不带按钮的普通样式。",
2184
- bridgeOffDesktop: "系统通知没有「同意 / 拒绝」按钮:桌面端页面(dsh-app://)注册不了 Service Worker(宿主少给 allowServiceWorkers 特权),按钮发不出来。想一键裁决就把「提醒方式」切到「页面浮层」。",
2266
+ bridgeOffTail: "):系统通知没有按钮,按钮改画在窗口角落的卡片上。",
2267
+ bridgeOffDesktop: "系统通知本身没有「同意 / 拒绝」按钮:桌面端页面(dsh-app://)注册不了 Service Worker(宿主少给 allowServiceWorkers 特权)。审批类提醒会同时在窗口角落弹一张卡片,按钮就在卡片上;想只留卡片、不弹系统通知,把「提醒方式」切到「页面浮层」。",
2185
2268
  overlay: "页面浮层提醒",
2186
2269
  overlayHint: "在窗口角落画提醒卡片:窗口在哪块屏,它就在哪块屏;系统通知只会出现在主屏,主屏不看时用它。不需要通知权限,卡片上能直接「同意 / 拒绝」。与系统通知二选一。",
2187
2270
  channelLabel: "通知类型",
2188
2271
  overlaySeg: "浮层提醒",
2189
- channelHint: "系统通知只会出现在主屏,进通知中心要权限,桌面端也没有「同意 / 拒绝」按钮;页面浮层画在 DSH 窗口所在的那块屏,不需要权限,卡片上能直接裁决。两个只能选一个。",
2272
+ channelHint: "系统通知只会出现在主屏,进通知中心要权限,桌面端的通知本身没有「同意 / 拒绝」按钮(审批会额外在窗口角落弹一张可点击的卡片);页面浮层画在 DSH 窗口所在的那块屏,不需要权限,卡片上能直接裁决。两个只能选一个。",
2190
2273
  overlayCorner: "浮层位置",
2191
2274
  overlayCornerHint: "贴在 DSH 窗口的哪个角;窗口在哪块屏,浮层就出现在哪块屏。",
2192
2275
  overlayPosTR: "右上",
@@ -2194,7 +2277,7 @@
2194
2277
  overlayPosTL: "左上",
2195
2278
  overlayPosBL: "左下",
2196
2279
  overlayQuickNote: "浮层上的「同意」同样等价于审批卡片上的「允许一次」,与通知按钮一致。",
2197
- testApprovalNoBridge: "已发普通测试通知(快捷裁决按钮需要 Service Worker,当前环境没有,按钮渲染不了)。",
2280
+ testApprovalNoBridge: "已发测试提醒:系统通知没有按钮,快捷裁决按钮画在窗口角落的卡片上。",
2198
2281
  testNeedsQuick: "先打开上面的「审批通知上直接裁决」才能测按钮。",
2199
2282
  volume: "音量",
2200
2283
  language: "通知语言",
@@ -2307,16 +2390,16 @@
2307
2390
  autoFocus: "Click a toast to open that conversation",
2308
2391
  autoFocusHint: "A click switches to the conversation the alert came from; off means a click does nothing.",
2309
2392
  quickActions: "Decide approvals from the toast",
2310
- quickActionsHint: "Approval notifications carry Approve / Reject; one click settles the request without switching back. Needs a Service Worker browser (http://127.0.0.1 or https), else plain toasts.",
2393
+ quickActionsHint: "Approval notifications carry Approve / Reject; one click settles the request without switching back. Needs a Service Worker browser (http://127.0.0.1 or https); when it is unavailable the buttons land on a card in the window corner (the Desktop shell's route).",
2311
2394
  quickApprovalNote: "\"Approve\" equals \"Allow once\" on the approval card — it allows that one call only.",
2312
2395
  bridgeOff: "Quick-decision buttons are unavailable right now (",
2313
- bridgeOffTail: "); approval toasts fall back to their plain form.",
2314
- bridgeOffDesktop: "System notifications carry no Approve / Reject buttons: dsh-app:// pages cannot register a Service Worker (the shell does not grant the allowServiceWorkers privilege), so the buttons cannot be attached. Switch the alert channel to the in-page overlay to settle approvals in one click.",
2396
+ bridgeOffTail: "); system notifications carry no buttons, so they are drawn on a card in the window corner instead.",
2397
+ bridgeOffDesktop: "System notifications carry no Approve / Reject buttons: dsh-app:// pages cannot register a Service Worker (the shell does not grant the allowServiceWorkers privilege). An approval therefore also drops a clickable card in the window corner, buttons and all; switch the alert channel to the in-page overlay to keep only the card and skip the system notification.",
2315
2398
  overlay: "In-page overlay",
2316
2399
  overlayHint: "Draw a card in the window corner, on whatever display the window sits on. System notifications only ever show on the primary display. No permission needed, and the card carries Approve / Reject directly. It replaces system notifications rather than adding to them.",
2317
2400
  channelLabel: "Alert type",
2318
2401
  overlaySeg: "Overlay",
2319
- channelHint: "System notifications land in the Windows notification center: permission needed, primary display only, and no Approve / Reject buttons on the Desktop shell. The in-page overlay draws on whichever display the DSH window sits on, needs no permission, and lets the Desktop shell decide right on the card. Pick one.",
2402
+ channelHint: "System notifications land in the Windows notification center: permission needed, primary display only, and the Desktop shell's notifications carry no Approve / Reject buttons (an approval also drops a clickable card in the window corner). The in-page overlay draws on whichever display the DSH window sits on, needs no permission, and lets the Desktop shell decide right on the card. Pick one.",
2320
2403
  overlayCorner: "Overlay position",
2321
2404
  overlayCornerHint: "Which corner of the DSH window the cards stack in; they follow the window to whatever display it is on.",
2322
2405
  overlayPosTR: "Top right",
@@ -2324,7 +2407,7 @@
2324
2407
  overlayPosTL: "Top left",
2325
2408
  overlayPosBL: "Bottom left",
2326
2409
  overlayQuickNote: "\"Approve\" here equals \"Allow once\" on the approval card — the same decision the toast buttons send.",
2327
- testApprovalNoBridge: "Plain test notification sent (quick-decision buttons need a Service Worker, which this environment lacks, so they cannot render).",
2410
+ testApprovalNoBridge: "Test alert sent: system notifications carry no buttons, so the quick-decision buttons are drawn on a card in the window corner.",
2328
2411
  testNeedsQuick: "Turn on \"Decide approvals from the toast\" above to test the buttons.",
2329
2412
  volume: "Volume",
2330
2413
  language: "Notification language",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-notify-me",
3
- "version": "1.10.2",
4
- "description": "离开 DSH 页面也能收到提醒:需要你操作 / 后台回复完成时发系统通知、标题标记与可选页面浮层(副屏也能看见),支持审批快捷裁决与外部推送。Brand icon and a reworked settings page in 1.9.0.",
3
+ "version": "1.10.4",
4
+ "description": "离开 DSH 页面也能收到提醒:需要你操作 / 后台回复完成时发系统通知、标题标记与可选页面浮层(副屏也能看见),支持审批快捷裁决与外部推送,Web 版与桌面版均可用。",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",