dsh-notify-me 1.10.6 → 1.11.0
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 +19 -6
- package/README.en.md +18 -30
- package/README.md +19 -32
- package/lib/client.js +76 -187
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,19 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to **dsh-notify-me** are documented here.
|
|
4
4
|
|
|
5
|
-
## [1.
|
|
5
|
+
## [1.11.0] — 2026-10-21
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### 移除
|
|
8
8
|
|
|
9
|
-
-
|
|
9
|
+
- **「提醒方式」二选一整段删掉**:系统通知成了唯一通道,设置页不再有「通知类型」分段选择器。页面浮层当独立通道的用途是「窗口在哪块屏,卡片就在哪块屏」,可它只在 DSH 窗口处于前台时看得见——需要提醒的时刻恰恰是窗口在后台。选了浮层、切走之后,后台只剩提示音和标题标记,等于没提醒;1.10.6 想在后台补发系统通知,又违背了你选的通道。两个方向都不成立,所以直接去掉这个选项。
|
|
10
|
+
- `channel` / `overlay` / `toast` 三个配置键失效。旧配置里的值在下次读配置时删掉,其余设置不受影响(`overlayCorner` 保留)。
|
|
11
|
+
- `window.__dshNotifyMe.debug()` 里的 `channel`、`toast` 被去掉,`overlay` 改名为 `card`,只报告桌面端审批卡片(`enabled` / `corner` / `cards`)。
|
|
10
12
|
|
|
11
|
-
###
|
|
13
|
+
### 保留
|
|
12
14
|
|
|
13
|
-
-
|
|
15
|
+
- **桌面端审批卡片**:`dsh-app://` 注册不了 Service Worker,系统通知带不了「同意 / 拒绝」,所以审批类提醒仍在窗口角落画一张可点击的卡片(12 秒回收,同一条审批不叠第二张)。位置改由「提醒方式 → 审批通知上直接裁决」下面的「审批卡片位置」决定,与已删除的通道无关。
|
|
16
|
+
- 1.10.5 的「窗口失焦也算后台」与 1.10.7 的行为原样保留。
|
|
14
17
|
|
|
15
18
|
### 测试
|
|
16
19
|
|
|
17
|
-
-
|
|
20
|
+
- 冒烟删掉浮层通道用例(`overlayChannel`、`settingsOverlayRow`),换成 `cardLayer`(卡片只给审批、四角位置、卡片上的审批按钮能裁决)与 `settingsAlertSetup`(设置页没有通道分段选择器、审批卡片位置子行在、旧配置的 `channel` 被清掉)。
|
|
21
|
+
|
|
22
|
+
## [1.10.7] — 2026-10-21
|
|
23
|
+
|
|
24
|
+
### 撤销
|
|
25
|
+
|
|
26
|
+
- **1.10.6 的「浮层通道在后台补发系统通知」整条撤销**。它把「选浮层」变成了「浮层 + 后台时又弹系统通知」,等于绕过了用户明确选定的通道——用户的反馈是「我选的不是浮层提醒吗,为什么弹系统通知」。现在恢复 1.10.5 的行为:选了浮层就只画页面卡片,窗口被挡住时后台只有提示音和标题标记,系统通知一条都不发。想要后台可见的提醒,请把「通知类型」切到「系统通知」。
|
|
27
|
+
|
|
28
|
+
### 说明
|
|
29
|
+
|
|
30
|
+
- 1.10.5 的「窗口失焦也算后台」保留不动:它决定的是「要不要提醒」,与「用哪个通道提醒」无关。
|
|
18
31
|
|
|
19
32
|
## [1.10.5] — 2026-10-21
|
|
20
33
|
|
package/README.en.md
CHANGED
|
@@ -26,9 +26,9 @@ A notification plugin for DSH Web / Desktop, so you can keep full access off wit
|
|
|
26
26
|
- **When input is needed:** approval, plan review, and questions trigger notifications, sound, and a title marker.
|
|
27
27
|
- **When a background reply finishes:** return to the result; click the notification to open its conversation.
|
|
28
28
|
- **When you leave your computer:** optionally forward alerts to Discord, Bark, Gotify, or your own webhook.
|
|
29
|
-
- **When you do not watch the primary display:**
|
|
29
|
+
- **When you do not watch the primary display:** system notifications only ever appear on the primary display; the Desktop approval card is drawn in the page and follows the DSH window onto whichever screen it is on (see [Secondary displays and the approval card](#secondary-displays-and-the-approval-card)).
|
|
30
30
|
|
|
31
|
-
> Keep the page or desktop app running; backgrounding / minimizing is fine. System notifications require permission. Approval buttons need a Service Worker
|
|
31
|
+
> Keep the page or desktop app running; backgrounding / minimizing is fine. System notifications require permission. Approval buttons need a Service Worker: Web (`http://127.0.0.1` or `https`) has one, the Desktop `dsh-app://` pages cannot register one, so their notifications carry no buttons and approvals are decided on a card in the window corner instead.
|
|
32
32
|
|
|
33
33
|
> 🔎 Looking for it? Search `dsh-notify-me`, **message alerts**, **notification**, **desktop notification**, **reminder**, **approval alert**, **external notifications**, **Discord / Bark / Gotify push** or **可操作提醒 / 消息提醒 / 桌面通知 / 需要你操作** (npm keywords include both English and Chinese terms).
|
|
34
34
|
|
|
@@ -38,16 +38,12 @@ A notification plugin for DSH Web / Desktop, so you can keep full access off wit
|
|
|
38
38
|
|
|
39
39
|
*Plain notification example · Windows / Edge · no approval buttons.* [View full size](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/notify-toast.png)
|
|
40
40
|
|
|
41
|
-

|
|
42
|
-
|
|
43
|
-
*In-page overlay · the real plugin rendered on a demo page · for when system notifications cannot reach the secondary display; cards appear on whichever display the DSH window is on.* [View full size](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/overlay-en.png)
|
|
44
|
-
|
|
45
41
|
<details>
|
|
46
42
|
<summary>See the settings interface</summary>
|
|
47
43
|
|
|
48
44
|

|
|
49
45
|
|
|
50
|
-
Just the plugin panel (the real component,
|
|
46
|
+
Just the plugin panel (the real component, captured before 1.11.0) without the DSH navigation sidebar. Row explanations live in a hover tooltip that appears when the pointer rests on a title, which is why only the titles show here. 1.11.0 removed the "alert type" row at the top; the rest is unchanged.
|
|
51
47
|
</details>
|
|
52
48
|
|
|
53
49
|
## Installation
|
|
@@ -74,7 +70,7 @@ The Electron application manages the `desktop` profile. Do not change the Web co
|
|
|
74
70
|
## First-use check
|
|
75
71
|
|
|
76
72
|
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
|
|
73
|
+
2. Open **Settings → Notify me**. Confirm that the section and plugin version at its foot appear, and leave the master switch and sound enabled.
|
|
78
74
|
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 window is in front" toggle.
|
|
79
75
|
|
|
80
76
|
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).
|
|
@@ -121,26 +117,21 @@ Open **Settings → Notify me**. The page runs as how you get told, when to aler
|
|
|
121
117
|
### How you get told
|
|
122
118
|
|
|
123
119
|
- **Enable reminders**: master switch — off means no toasts, no sounds, and the tab title is left alone;
|
|
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
|
-
- **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. 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
120
|
- **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): 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).
|
|
121
|
+
- **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). Its corner comes from the Corner sub-row underneath — see [Secondary displays and the approval card](#secondary-displays-and-the-approval-card).
|
|
129
122
|
|
|
130
|
-
### Secondary displays and the
|
|
123
|
+
### Secondary displays and the approval card
|
|
131
124
|
|
|
132
|
-
Windows draws system notification banners on the primary display only. There is no setting in Windows and no API in the browser to aim one at another monitor, so with DSH parked on a second screen and the primary display unwatched, a toast is as good as not sent.
|
|
125
|
+
Windows draws system notification banners on the primary display only. There is no setting in Windows and no API in the browser to aim one at another monitor, so with DSH parked on a second screen and the primary display unwatched, a toast is as good as not sent. That part is out of reach — the only thing that can show up on the other screen comes from inside the page.
|
|
133
126
|
|
|
134
|
-
|
|
127
|
+
One thing does: the Desktop approval card. It is drawn in the DSH page, so it appears on whichever display the window sits on, and it carries Approve / Reject (Desktop toasts have no buttons).
|
|
135
128
|
|
|
136
|
-
- **Position** —
|
|
137
|
-
- **
|
|
138
|
-
- **
|
|
139
|
-
- **
|
|
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
|
-
- **System notification while the window is in the background** (on by default, since 1.10.6) — the card is drawn inside the page, so a window behind another app or minimized shows nothing and leaves only the sound. With this on, an alert arriving while the page is away is also raised as a system notification; back in front, the card is the only copy again. Turn it off to keep the overlay strictly in-page.
|
|
129
|
+
- **Position** — the Corner sub-row under "Decide approvals from the toast" shows 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;
|
|
130
|
+
- **Approvals only** — plain "needs you" and "reply finished" alerts never draw a card; they are system notifications only;
|
|
131
|
+
- **Lifetime** — an approval card retires after 12 s; clicking the card body opens its conversation. At most 4 cards stack at once;
|
|
132
|
+
- **No notification permission needed** — this path touches no Service Worker and needs no permission.
|
|
142
133
|
|
|
143
|
-
The overlay only
|
|
134
|
+
> 1.11.0 removed the "alert type" choice. The in-page overlay used to work as a standalone channel, but it is only visible while the DSH window is in front — and the moment an alert matters is exactly when the window is in the background. With the overlay chosen, backgrounding left only the sound and the tab marker, which is no alert at all.
|
|
144
135
|
|
|
145
136
|
### When to alert
|
|
146
137
|
|
|
@@ -201,9 +192,8 @@ The last block inside Advanced. The page protocol is detected automatically to t
|
|
|
201
192
|
|
|
202
193
|
## How it alerts
|
|
203
194
|
|
|
204
|
-
- **
|
|
205
|
-
|
|
206
|
-
- **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)
|
|
195
|
+
- **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)
|
|
196
|
+
- **Desktop approval card** — `dsh-app://` cannot put Approve / Reject on a toast, so approval alerts also drop a clickable card in the window corner; see [Secondary displays and the approval card](#secondary-displays-and-the-approval-card)
|
|
207
197
|
- **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
|
|
208
198
|
- **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
|
|
209
199
|
- **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
|
|
@@ -253,16 +243,14 @@ window.__dshNotifyMe.setConfig({
|
|
|
253
243
|
currentHiddenOnly: true, // true = waits in the conversation on screen keep only the tab marker (replayed once backgrounded)
|
|
254
244
|
doneHiddenOnly: true, // false = also alert "reply finished" while visible
|
|
255
245
|
ignoreSubagent: true, // true = never alert for background subagent sessions (the open one is exempt)
|
|
256
|
-
|
|
246
|
+
quickActions: true, // Approve / Reject on approval alerts
|
|
257
247
|
sound: true, // cue toggle (also governs the "system notification sound" mode)
|
|
258
248
|
soundDone: "synth", // "reply finished" cue source: 'synth' built-in | 'custom' picked file | 'system' OS sound
|
|
259
249
|
soundAttention: "synth", // "needs you" cue source, same values
|
|
260
250
|
volume: 0.5, // 0..1
|
|
261
251
|
autoFocus: true, // clicking a toast opens the conversation it came from
|
|
262
252
|
quickActions: true, // Approve / Reject buttons on approval toasts
|
|
263
|
-
|
|
264
|
-
overlayCorner: "bottom-right", // overlay anchor: 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left'
|
|
265
|
-
overlayAwayToast: true, // overlay channel: also raise a system notification while the page is away (the only cross-channel exception)
|
|
253
|
+
overlayCorner: "bottom-right", // Desktop approval card anchor: 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left'
|
|
266
254
|
externalEnabled: false, // external-notification forwarding master switch (default off: no endpoint, nothing is sent)
|
|
267
255
|
externalAttention: true, // forward "needs you" (exactly the same trigger rules as local alerts)
|
|
268
256
|
externalDone: true, // forward "reply finished"
|
|
@@ -324,7 +312,7 @@ There is no page-side fallback either: the shell preload only exposes `dshDeskto
|
|
|
324
312
|
- The first sound/toast of each page load needs one user click on the page first (browser autoplay + permission policy).
|
|
325
313
|
- Toasts are only produced once the notification permission is granted; declining means sound + title marker only.
|
|
326
314
|
- On the Desktop shell (DSH Desktop 0.2.1-rc.2 / Windows) a toast click does not reliably bring the window forward: sometimes it does, sometimes only the conversation switches. The plugin's `dsh://open` deep link on its own failed 3/3 in isolation (see [How the Desktop window gets raised](#how-the-desktop-window-gets-raised)), so when it does come forward it is most likely the OS/Chromium activating the app on the notification click itself ([discussions #10446](https://github.com/deepseek-ai/deepseek-harness/discussions/10446)).
|
|
327
|
-
- System notification banners are drawn on the primary display only: Windows has no setting and Chromium no API to aim one at another monitor, so on a multi-monitor setup the second screen never receives them.
|
|
315
|
+
- System notification banners are drawn on the primary display only: Windows has no setting and Chromium no API to aim one at another monitor, so on a multi-monitor setup the second screen never receives them. The one thing that does reach another display is the Desktop approval card, drawn in the page and carried by the window; for plain alerts across screens, forward them to your phone with external notifications.
|
|
328
316
|
- Quick-decision buttons need a Service Worker: they work on `http://127.0.0.1` and `https`, not on plain `http://192.168.x.x`-style LAN addresses, and some browsers or desktop hosts never render notification buttons. **The Desktop (`dsh-app://`) is tested and cannot register a Service Worker**, so buttons never render there and approval toasts fall back to their buttonless form. The `bridge` field of `window.__dshNotifyMe.debug()` says why; everything else keeps working.
|
|
329
317
|
- macOS: in the browser, alerts, sounds, the title marker and toast-click navigation carry no OS assumptions and work as on Windows. Two caveats: DSH Desktop (Electron) notifications require the app to be code-signed — unsigned builds simply emit no notification — and whether the official macOS build satisfies that is untested; and notification buttons on the macOS desktop host are likewise untested (the Windows desktop host is tested and renders none, see the line above), falling back to the buttonless form above.
|
|
330
318
|
- Config lives in browser `localStorage`: switching browsers/devices or clearing site data returns to defaults (one-click restore in the Settings page).
|
package/README.md
CHANGED
|
@@ -26,9 +26,9 @@ DSH Web / Desktop 的通知插件,让你敢不开完整访问,也不必一
|
|
|
26
26
|
- **需要你操作时**:审批、方案确认、提问,用通知、提示音和标题标记提醒。
|
|
27
27
|
- **后台回复完成时**:及时回来查看结果,点击通知可回到对应对话。
|
|
28
28
|
- **离开电脑时**:可选转发到 Discord、Bark、Gotify 或自己的 Webhook。
|
|
29
|
-
-
|
|
29
|
+
- **不看主屏时**:系统通知只会画在主屏,副屏看不到;桌面端的审批卡片画在页面里,跟着 DSH 窗口走到哪块屏(见[副屏与审批卡片](#副屏与审批卡片))。
|
|
30
30
|
|
|
31
|
-
> 页面或桌面应用需保持运行,后台 /
|
|
31
|
+
> 页面或桌面应用需保持运行,后台 / 最小化可以。系统通知需要通知权限。审批按钮靠 Service Worker 才能进通知:Web 端(`http://127.0.0.1` 或 `https`)有,桌面端 `dsh-app://` 页面注册不了,通知上没有按钮,审批改在窗口角落的卡片上裁决。
|
|
32
32
|
|
|
33
33
|
> 🔎 想找这个插件?可以搜:`dsh-notify-me`、**消息提醒**、**可操作提醒**、**桌面通知**、**需要你操作**、**回复完成提醒**、**后台完成提醒**、**外部通知**、**Discord / Bark / Gotify 推送**(npm 关键词已含以上中英文词)。
|
|
34
34
|
|
|
@@ -38,16 +38,12 @@ DSH Web / Desktop 的通知插件,让你敢不开完整访问,也不必一
|
|
|
38
38
|
|
|
39
39
|
*普通通知示例 · Windows / Edge · 不含审批按钮。* [查看原图](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/notify-toast.png)
|
|
40
40
|
|
|
41
|
-

|
|
42
|
-
|
|
43
|
-
*页面浮层提醒 · 真实插件在独立演示页里渲染 · 系统通知画不到副屏时用它,卡片落在 DSH 窗口所在的那块屏。* [查看原图](https://raw.githubusercontent.com/chromoany/dsh-notify-me/main/docs/screenshots/overlay-zh.png)
|
|
44
|
-
|
|
45
41
|
<details>
|
|
46
42
|
<summary>展开设置界面预览</summary>
|
|
47
43
|
|
|
48
44
|

|
|
49
45
|
|
|
50
|
-
|
|
46
|
+
只截插件自己的面板(真实组件,1.11.0 之前的截图),不带 DSH 左侧导航栏;每行说明在鼠标停在标题文字上时才出现,所以这里只看到标题。1.11.0 删掉了最上面的「通知类型」一行,其余行没变。
|
|
51
47
|
</details>
|
|
52
48
|
|
|
53
49
|
## 安装
|
|
@@ -74,7 +70,7 @@ dsh plugin --profile web add dsh-notify-me
|
|
|
74
70
|
## 首次验证
|
|
75
71
|
|
|
76
72
|
1. 在 DSH 页面上点击一次,按提示允许通知;若此前拒绝过,在浏览器站点设置中允许通知。操作系统也需要允许对应浏览器 / DSH Desktop 显示通知。
|
|
77
|
-
2. 打开 **设置 →
|
|
73
|
+
2. 打开 **设置 → 通知提醒**,确认出现该分区及底部插件版本号,保持「启用提醒」「提示音」开启。
|
|
78
74
|
3. 点击设置页的「测试『回复完成』」按钮:应出现一条测试通知,并按当前音效设置播放声音。这个本地测试不会转发到外部通知端点,也不受「窗口在前台时也提醒」开关限制。
|
|
79
75
|
|
|
80
76
|
Web 用户可再测试审批按钮。桌面端拿不到 Service Worker,这条测试会在窗口角落画一张带「同意 / 拒绝」的卡片(1.10.4 起;之前的版本这里只发普通通知)。
|
|
@@ -117,26 +113,21 @@ dsh-notify-me 把「监督」和「守在屏幕前」解耦:只在真的出现
|
|
|
117
113
|
### 提醒方式
|
|
118
114
|
|
|
119
115
|
- **启用提醒** 总开关:关闭后不弹通知、不响提示音,也不改标签页标题;
|
|
120
|
-
- **通知类型(二选一)**:一行分段选择器,左边「系统通知」、右边「浮层提醒」,点哪个就只走哪个;
|
|
121
|
-
- **系统通知**(默认):进 Windows / macOS 通知中心,需要通知权限;
|
|
122
|
-
- **页面浮层**:在 DSH 页面角落画提醒卡片,窗口在哪块屏它就在哪块屏——系统通知只会画在主屏,主屏不看时用它;不需要通知权限,桌面端也能在卡片上直接「同意 / 拒绝」。选中后卡片里会多出「浮层位置」子行,点开可选四个角落,见[副屏与浮层提醒](#副屏与浮层提醒);
|
|
123
116
|
- **提示音 / 音量**:开关直接切;音量滑块收在该行里,点一下标题(▸ 变成 ▾)展开。具体播什么由「高级设置 → 音效来源」决定;
|
|
124
|
-
- **审批通知上直接裁决**(默认开):审批带「同意 / 拒绝」,点一下即完成裁决,不用切回页面。Web 走带按钮的系统通知;桌面端 `dsh-app://` 注册不了 Service Worker,于是把按钮画在窗口角落的卡片上(12
|
|
117
|
+
- **审批通知上直接裁决**(默认开):审批带「同意 / 拒绝」,点一下即完成裁决,不用切回页面。Web 走带按钮的系统通知;桌面端 `dsh-app://` 注册不了 Service Worker,于是把按钮画在窗口角落的卡片上(12 秒自动收起,同一条审批不叠第二张卡)。卡片位置由它下面的子行「审批卡片位置」控制,见[副屏与审批卡片](#副屏与审批卡片)。
|
|
125
118
|
|
|
126
|
-
###
|
|
119
|
+
### 副屏与审批卡片
|
|
127
120
|
|
|
128
|
-
Windows 的系统通知横幅固定画在主屏上,系统没有设置项、浏览器也没有 API 能把它挪到别的显示器。主屏不看、DSH
|
|
121
|
+
Windows 的系统通知横幅固定画在主屏上,系统没有设置项、浏览器也没有 API 能把它挪到别的显示器。主屏不看、DSH 停在副屏时,通知就等于没发。这一条改不掉——真要在副屏看见,只能靠页面里的东西。
|
|
129
122
|
|
|
130
|
-
|
|
123
|
+
页面里还剩一种:桌面端的审批卡片。它画在 DSH 页面里,窗口在哪块屏就出现在哪块屏,卡片上能直接「同意 / 拒绝」(桌面端通知带不了按钮)。
|
|
131
124
|
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
- **与系统通知的关系**:二选一。以前两个开关可以同开,结果同一条提醒弹两遍、还互相挡住;1.10.0 起改成单选,1.10.2 起做成一行分段选择器。老配置里开着浮层的,升级后自动落到浮层。
|
|
137
|
-
- **窗口在后台时补发系统通知**(默认开,1.10.6 起):浮层画在页面里,窗口被别的应用挡住或最小化时看不见,等于只剩声音;开着这一项,页面离开前台时额外发一条系统通知,窗口回到前台还是只有卡片。不需要这份兜底就关掉它。
|
|
125
|
+
- **位置**:「审批通知上直接裁决」下面的「审批卡片位置」子行,行上直接显示当前值(默认右下);点它(▸ 变 ▾)再选右上 / 右下 / 左上 / 左下,改完立刻生效;
|
|
126
|
+
- **只给审批**:普通提醒与「回复完成」不画卡片,它们只有系统通知;
|
|
127
|
+
- **停留时间**:审批卡片 12 秒自动收起;点卡片正文切回对应对话。最多同时叠 4 张;
|
|
128
|
+
- **不用通知权限**:这条路径不碰 Service Worker,也不依赖通知权限。
|
|
138
129
|
|
|
139
|
-
|
|
130
|
+
> 1.11.0 删掉了「通知类型」二选一。页面浮层曾经能当独立通道用,但它只在 DSH 窗口处于前台时看得见——需要提醒的时刻恰恰是窗口在后台。选浮层、切走之后后台只剩提示音和标题标记,等于没提醒。
|
|
140
131
|
|
|
141
132
|
### 什么时候提醒
|
|
142
133
|
|
|
@@ -197,9 +188,8 @@ Windows 的系统通知横幅固定画在主屏上,系统没有设置项、浏
|
|
|
197
188
|
|
|
198
189
|
## 提醒渠道
|
|
199
190
|
|
|
200
|
-
-
|
|
201
|
-
|
|
202
|
-
- **页面浮层**:画在 DSH 页面角落的提醒卡片,落在窗口所在的那块屏,可带「同意 / 拒绝」按钮;见[副屏与浮层提醒](#副屏与浮层提醒)
|
|
191
|
+
- **系统通知**:经浏览器弹出的系统通知,进 Windows 通知中心或 macOS 通知中心(点击回到提醒所属的对话;支持快捷裁决的 Web 环境可点审批按钮直接裁决,桌面端没有按钮)
|
|
192
|
+
- **桌面端审批卡片**:`dsh-app://` 下系统通知带不了「同意 / 拒绝」,审批类提醒额外在窗口角落画一张可点卡片;见[副屏与审批卡片](#副屏与审批卡片)
|
|
203
193
|
- **提示音**:默认是 WebAudio 合成音(「需要你」与「完成」使用不同音型);可在设置里为两类提醒分别换成自定义音频文件,或改成让操作系统播放它自己的通知音
|
|
204
194
|
- **标签页标题标记**:有待处理事项时,标题前出现 `🔔 需要你 · …` / `🔔 Action needed · …`;后台会话**完成但还没看**时出现 `✅ 回复完成 · …` / `✅ Reply finished · …`,打开对应会话(或它再次开跑)即消失。两种标记可同时出现,「需要你」在前;宿主改写标题后标记基于新标题重建
|
|
205
195
|
- **外部通知**(可选,默认关):按设置里配的推送目标,把同一条提醒转发到 Discord Webhook / Bark / Gotify / 自定义 Webhook——临时离开电脑时,手机上的推送渠道照样收到
|
|
@@ -249,16 +239,13 @@ window.__dshNotifyMe.setConfig({
|
|
|
249
239
|
currentHiddenOnly: true, // true = 当前对话的等待只留标题标记(转后台补发)
|
|
250
240
|
doneHiddenOnly: true, // false = 页面可见时「完成」也提醒
|
|
251
241
|
ignoreSubagent: true, // true = 后台子代理(subagent)会话不提醒(当前打开的照常)
|
|
252
|
-
channel: "toast", // 通知类型:'toast' 系统通知 | 'overlay' 页面浮层,二选一
|
|
253
242
|
sound: true, // 提示音开关(含「系统通知音」模式)
|
|
254
243
|
soundDone: "synth", // 「完成」音效来源:'synth' 内置 | 'custom' 自定义文件 | 'system' 系统通知音
|
|
255
244
|
soundAttention: "synth", // 「需要你」音效来源,取值同上
|
|
256
245
|
volume: 0.5, // 音量 0~1
|
|
257
246
|
autoFocus: true, // 点通知回到提醒所属的对话
|
|
258
247
|
quickActions: true, // 审批通知上的「同意 / 拒绝」按钮
|
|
259
|
-
|
|
260
|
-
overlayCorner: "bottom-right", // 浮层位置:'top-right' | 'bottom-right' | 'top-left' | 'bottom-left'
|
|
261
|
-
overlayAwayToast: true, // 浮层通道下,页面在后台时额外发一条系统通知(唯一跨通道例外)
|
|
248
|
+
overlayCorner: "bottom-right", // 桌面端审批卡片位置:'top-right' | 'bottom-right' | 'top-left' | 'bottom-left'
|
|
262
249
|
externalEnabled: false, // 外部通知转发主开关(默认关:不填端点就什么都不发)
|
|
263
250
|
externalAttention: true, // 转发「需要你」(触发规则跟本地提醒完全一致)
|
|
264
251
|
externalDone: true, // 转发「回复完成」
|
|
@@ -318,7 +305,7 @@ DSH Desktop 是一层 Electron 壳,页面跑在 `dsh-app://app` 下:DOM 的
|
|
|
318
305
|
- 页面必须开着才会提醒(后台标签/最小化可以;关标签页即失效——浏览器层方案固有限制)。
|
|
319
306
|
- 通知经由浏览器弹出,需允许浏览器通知权限,且勿扰模式不能屏蔽它。
|
|
320
307
|
- 桌面端(DSH Desktop 0.2.1-rc.2 / Windows)点通知后窗口不一定上前台:有时会,有时只切对话。插件发的 `dsh://open` 深链单独复测 3/3 无效(详见[桌面端怎么抬窗的](#桌面端怎么抬窗的)),能抬的时候多半是系统/Chromium 在通知激活时自己把窗口带起来了([discussions #10446](https://github.com/deepseek-ai/deepseek-harness/discussions/10446))。
|
|
321
|
-
- 系统通知横幅只画在主屏:Windows 没有设置项、Chromium 没有 API
|
|
308
|
+
- 系统通知横幅只画在主屏:Windows 没有设置项、Chromium 没有 API 能把它指到别的显示器(多屏时副屏收不到系统通知)。页面里唯一还会出现在副屏的是桌面端的审批卡片,它跟着 DSH 窗口走;普通提醒要跨屏,只能靠外部推送转到手机。
|
|
322
309
|
- 每次页面加载后第一次出声/弹通知前,需在页面上点击过一次(浏览器自动播放与权限策略)。
|
|
323
310
|
- 未授权通知权限时只有提示音与标题标记。
|
|
324
311
|
- 快捷裁决按钮要 Service Worker:`http://127.0.0.1`、`https` 可用,`http://192.168.x.x` 这类局域网地址不行;**桌面端(`dsh-app://`)已实测无法注册 Service Worker**,按钮渲染不了,审批通知退回无按钮样式。浏览器或桌面宿主不渲染通知按钮时,`window.__dshNotifyMe.debug()` 的 `bridge` 字段会说明原因,其余功能不受影响。
|
|
@@ -326,7 +313,7 @@ DSH Desktop 是一层 Electron 壳,页面跑在 `dsh-app://app` 下:DOM 的
|
|
|
326
313
|
- 配置存在浏览器 `localStorage`:换浏览器/设备或清除站点数据后会回到默认值(设置页可一键恢复默认)。
|
|
327
314
|
- `≥ 0.1.6`(含 `0.2.0-rc.2`、`0.2.1-rc.2`)的「回复完成」提醒正文只有会话名,不再附带回复摘要——宿主快照已不再提供 `nodes`;提醒本身照常触发。
|
|
328
315
|
- 标题标记由宿主与插件共同写 `document.title`:宿主重算标题时标记可能被覆盖,直到下一次提醒事件重新写成。
|
|
329
|
-
-
|
|
316
|
+
- 「当前对话不弹通知」按「窗口是否在前台」判定:窗口被别的应用盖住、切到别的应用、最小化都算后台,此时当前对话的等待照常提醒;窗口回到前台再静默。
|
|
330
317
|
- 外部通知转发受浏览器跨域(CORS)限制:端点不放行时自动降级为「送出但不可确认」(no-cors),如实标为「已送出不可确认」;Discord Webhook 官方放行跨域,可直接确认送达。HTTP 错误如实记失败、不重试。转发在本地提醒决定要发之后才触发,所以被「当前对话不弹通知」等规则静音的提醒也不会外发。
|
|
331
318
|
|
|
332
319
|
- **设置左侧导航行图标靠 DOM 补丁**:外壳按分区 id 硬编码图标且 `settings.section` 没有图标字段,插件只能在面板渲染后把那一行的 `<svg>` 换成铃铛。命中依赖宿主当前的面板结构(`data-shortcut-modal="settings"` + 导航按钮里的标签 `<span>`);如果 DSH 改了这套结构,补丁会静默失效、显示回默认齿轮,其他功能不受影响。
|
|
@@ -336,7 +323,7 @@ DSH Desktop 是一层 Electron 壳,页面跑在 `dsh-app://app` 下:DOM 的
|
|
|
336
323
|
```powershell
|
|
337
324
|
node --check lib\client.js
|
|
338
325
|
node --check lib\index.js
|
|
339
|
-
node smoke\smoke-test.cjs # 离线状态机冒烟测试:三代宿主 + 主开关 + 中英语言用例 + 快捷裁决桥(Service Worker 半侧一并跑)+
|
|
326
|
+
node smoke\smoke-test.cjs # 离线状态机冒烟测试:三代宿主 + 主开关 + 中英语言用例 + 快捷裁决桥(Service Worker 半侧一并跑)+ 桌面审批卡片
|
|
340
327
|
node smoke\cordis-host-test.mjs # 真 cordis 端到端(找不到本机 DSH 时自动跳过)
|
|
341
328
|
npm pack --dry-run # 预览发布包
|
|
342
329
|
```
|
package/lib/client.js
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
// (client.focus()) and routes the conversation switch to that window alone. Where a worker cannot run (insecure context, no
|
|
60
60
|
// bundle URL) the plugin degrades to today's plain toast;
|
|
61
61
|
// - registers a "通知提醒 / Notify me" page into DSH Settings
|
|
62
|
-
// (settings.section slot) exposing an on/off master switch, per-
|
|
62
|
+
// (settings.section slot) exposing an on/off master switch, per-event
|
|
63
63
|
// toggles, volume and the notification language (follow-interface /
|
|
64
64
|
// Simplified Chinese / English);
|
|
65
65
|
// - preferences persist in localStorage (origin-scoped, like other
|
|
@@ -237,7 +237,6 @@
|
|
|
237
237
|
// keys on, plus "not the session on screen" — an open subagent session is
|
|
238
238
|
// just another conversation to alert for.
|
|
239
239
|
ignoreSubagent: true,
|
|
240
|
-
toast: true,
|
|
241
240
|
sound: true,
|
|
242
241
|
// Where each alert's cue comes from (per kind, so "needs you" and "reply
|
|
243
242
|
// finished" stay independent):
|
|
@@ -256,27 +255,15 @@
|
|
|
256
255
|
// through the Service Worker half of this same file; where that cannot
|
|
257
256
|
// run, the toast simply falls back to its plain form.
|
|
258
257
|
quickActions: true,
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
channel: "toast",
|
|
269
|
-
// Legacy mirrors of `channel`, kept in sync on every read/write so that a
|
|
270
|
-
// config written by an older build, or a console poke at one of them,
|
|
271
|
-
// still means what it used to.
|
|
272
|
-
overlay: false,
|
|
273
|
-
// The overlay draws inside the page, so a window sitting behind another
|
|
274
|
-
// app shows nothing at all. An alert that lands while the page is away
|
|
275
|
-
// therefore also raises a system notification — the only thing that
|
|
276
|
-
// reaches a user who is elsewhere. Off keeps the overlay strictly
|
|
277
|
-
// in-page, at the price of silence while the window is behind.
|
|
278
|
-
overlayAwayToast: true,
|
|
279
|
-
// Which corner of this window the overlay cards stack in. The screen
|
|
258
|
+
// Alerts reach the user through the OS notification center; there is no
|
|
259
|
+
// second channel. An in-page card would follow the DSH window to whatever
|
|
260
|
+
// display it sits on, but it is invisible the moment the window is not in
|
|
261
|
+
// front — which is exactly when a reminder is worth anything — so 1.11.0
|
|
262
|
+
// removed it as a channel. The card layer survives for the one case that
|
|
263
|
+
// needs it: the desktop shell cannot put Approve / Reject on a system
|
|
264
|
+
// notification, so an approval also drops a clickable card in the corner
|
|
265
|
+
// (see showFallbackApprovalCard).
|
|
266
|
+
// Which corner of this window that approval card stacks in. The screen
|
|
280
267
|
// follows the window — the page cannot aim it anywhere else either.
|
|
281
268
|
overlayCorner: "bottom-right", // bottom-right | bottom-left | top-right | top-left
|
|
282
269
|
// External forwarding: mirror every alert the plugin actually delivers to
|
|
@@ -295,31 +282,18 @@
|
|
|
295
282
|
var raw = localStorage.getItem(LS_CONFIG);
|
|
296
283
|
if (raw) cfg = JSON.parse(raw);
|
|
297
284
|
} catch (e) { /* corrupt storage -> defaults */ }
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
285
|
+
// The alert channel is gone: 1.11.0 removed the in-page overlay as a
|
|
286
|
+
// delivery option, so an old config's `channel` / `overlay` / `toast` keys
|
|
287
|
+
// mean nothing now. Dropped here so the next write stores a clean config.
|
|
288
|
+
delete cfg.channel;
|
|
289
|
+
delete cfg.overlay;
|
|
290
|
+
delete cfg.toast;
|
|
305
291
|
for (var k in DEFAULTS) if (!(k in cfg)) cfg[k] = DEFAULTS[k];
|
|
306
|
-
|
|
307
|
-
cfg.toast = cfg.channel === "toast";
|
|
308
|
-
cfg.overlay = cfg.channel === "overlay";
|
|
309
292
|
return cfg;
|
|
310
293
|
}
|
|
311
294
|
function writeConfig(patch) {
|
|
312
295
|
var cfg = readConfig();
|
|
313
296
|
for (var k in patch) cfg[k] = patch[k];
|
|
314
|
-
if (!("channel" in patch)) {
|
|
315
|
-
if (patch.overlay === true) cfg.channel = "overlay";
|
|
316
|
-
else if (patch.overlay === false && cfg.channel === "overlay") cfg.channel = "toast";
|
|
317
|
-
if (patch.toast === true) cfg.channel = "toast";
|
|
318
|
-
else if (patch.toast === false && cfg.channel === "toast") cfg.channel = "overlay";
|
|
319
|
-
}
|
|
320
|
-
cfg.channel = cfg.channel === "overlay" ? "overlay" : "toast";
|
|
321
|
-
cfg.toast = cfg.channel === "toast";
|
|
322
|
-
cfg.overlay = cfg.channel === "overlay";
|
|
323
297
|
try { localStorage.setItem(LS_CONFIG, JSON.stringify(cfg)); } catch (e) {}
|
|
324
298
|
return cfg;
|
|
325
299
|
}
|
|
@@ -327,18 +301,13 @@
|
|
|
327
301
|
// Persist a patch, swap the live module config and re-apply any active
|
|
328
302
|
// title marker (its prefix follows the notification language).
|
|
329
303
|
function setConfigLocal(patch) {
|
|
330
|
-
var wasOverlay = config.channel === "overlay";
|
|
331
304
|
config = writeConfig(patch);
|
|
332
305
|
applyTitle();
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// so an unrelated patch must not sweep them away.
|
|
306
|
+
// A new corner moves the approval-card layer without waiting for the next
|
|
307
|
+
// alert. Cards themselves are left alone: an unrelated patch must not
|
|
308
|
+
// sweep a pending approval off the screen.
|
|
337
309
|
try {
|
|
338
|
-
if (
|
|
339
|
-
if (wasOverlay) clearOverlays();
|
|
340
|
-
else if (overlayLayer) overlayLayer.className = "dnm-ov-layer dnm-ov-" + overlayCorner();
|
|
341
|
-
} else if (overlayLayer) overlayLayer.className = "dnm-ov-layer dnm-ov-" + overlayCorner();
|
|
310
|
+
if (overlayLayer) overlayLayer.className = "dnm-ov-layer dnm-ov-" + overlayCorner();
|
|
342
311
|
} catch (e) {}
|
|
343
312
|
return config;
|
|
344
313
|
}
|
|
@@ -474,18 +443,9 @@
|
|
|
474
443
|
} catch (e) {}
|
|
475
444
|
}
|
|
476
445
|
function canToast() {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
} catch (e) { return false; } // a policy-disabled Notification must not break delivery
|
|
481
|
-
}
|
|
482
|
-
// The overlay is the chosen channel, but a card inside a window the user is
|
|
483
|
-
// not looking at cannot be seen; the system notification is the only thing
|
|
484
|
-
// that reaches them. Permission still decides — without it the in-page card
|
|
485
|
-
// is all there is, waiting for them when they come back.
|
|
486
|
-
function awayToastAllowed() {
|
|
487
|
-
if (config.channel !== "overlay" || !config.overlayAwayToast) return false;
|
|
488
|
-
if (!away()) return false;
|
|
446
|
+
// System notifications are the only channel left (1.11.0); permission is
|
|
447
|
+
// the single gate. A denied or policy-disabled Notification must not break
|
|
448
|
+
// delivery: the sound and the title marker still fire.
|
|
489
449
|
try {
|
|
490
450
|
return "Notification" in window && Notification.permission === "granted";
|
|
491
451
|
} catch (e) { return false; }
|
|
@@ -775,8 +735,8 @@
|
|
|
775
735
|
return "dsh-notify-me-" + kind + "-" + toastSeq + "-" + Date.now();
|
|
776
736
|
}
|
|
777
737
|
|
|
778
|
-
function showToast(kind, title, body, sessionId, quick
|
|
779
|
-
if (!
|
|
738
|
+
function showToast(kind, title, body, sessionId, quick) {
|
|
739
|
+
if (!canToast()) return;
|
|
780
740
|
try {
|
|
781
741
|
var tag = quick ? quick.tag : uniqueToastTag(kind);
|
|
782
742
|
if (quick && bridgeActive()) {
|
|
@@ -939,11 +899,9 @@
|
|
|
939
899
|
}
|
|
940
900
|
}
|
|
941
901
|
function overlayCount() { return Object.keys(overlayCards).length; }
|
|
942
|
-
//
|
|
943
|
-
//
|
|
944
|
-
// apart:
|
|
945
|
-
// takes the card down), while the fallback card carries a decision the OS
|
|
946
|
-
// notification cannot — so a click must not dismiss it unanswered, and a
|
|
902
|
+
// The card builder behind the desktop approval fallback below (the overlay
|
|
903
|
+
// channel that also used it is gone since 1.11.0). `opts` keeps behaviours
|
|
904
|
+
// apart: a click must not dismiss an unanswered approval, and a
|
|
947
905
|
// "详情 / Details" toggle exposes the body that the CSS clamps to 3 lines.
|
|
948
906
|
function drawOverlayCard(kind, title, body, sessionId, quick, opts) {
|
|
949
907
|
opts = opts || {};
|
|
@@ -1047,25 +1005,18 @@
|
|
|
1047
1005
|
return true;
|
|
1048
1006
|
} catch (e) { return false; }
|
|
1049
1007
|
}
|
|
1050
|
-
// Same inputs as showToast: `quick` is the approval record built by
|
|
1051
|
-
// quickFor(), and it is what lets an overlay settle the request in place.
|
|
1052
|
-
function showOverlay(kind, title, body, sessionId, quick) {
|
|
1053
|
-
if (config.channel !== "overlay") return false;
|
|
1054
|
-
return drawOverlayCard(kind, title, body, sessionId, quick, { dismissOnClick: true });
|
|
1055
|
-
}
|
|
1056
1008
|
// The desktop shell cannot register the Service Worker that OS action
|
|
1057
1009
|
// buttons require, so an approval toast on a dsh-app:// page has no button
|
|
1058
|
-
// to click.
|
|
1059
|
-
//
|
|
1060
|
-
//
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
1063
|
-
//
|
|
1064
|
-
//
|
|
1010
|
+
// to click. An approval therefore also drops a card into the in-page layer,
|
|
1011
|
+
// in the configured corner and with the same styling: its buttons call
|
|
1012
|
+
// decidePending(), the exact path a Web toast button takes. Only states that
|
|
1013
|
+
// prove the bridge is unusable qualify — 'idle'/'registering' are
|
|
1014
|
+
// transitional, 'disabled' means the quick-decision switch is off (quickFor()
|
|
1015
|
+
// then returns null, so no card is drawn at all), and the Web path stays
|
|
1016
|
+
// untouched once the worker is up.
|
|
1065
1017
|
function showFallbackApprovalCard(kind, title, body, sessionId, quick) {
|
|
1066
1018
|
if (kind !== "attention") return false;
|
|
1067
1019
|
if (!quick || typeof quick.key !== "string") return false;
|
|
1068
|
-
if (config.channel !== "toast") return false;
|
|
1069
1020
|
if (bridgeActive()) return false;
|
|
1070
1021
|
if (swState.indexOf("unsupported:") !== 0 && swState.indexOf("failed:") !== 0) return false;
|
|
1071
1022
|
return drawOverlayCard(kind, title, body, sessionId, quick, { dismissOnClick: false, expand: true });
|
|
@@ -1441,13 +1392,7 @@
|
|
|
1441
1392
|
}
|
|
1442
1393
|
function deliver(kind, title, body, sessionId, quick) {
|
|
1443
1394
|
if (!config.enabled) return;
|
|
1444
|
-
|
|
1445
|
-
// the overlay channel owns the in-page card. The single exception is the
|
|
1446
|
-
// overlay channel while the page is away — that card is unseeable then,
|
|
1447
|
-
// so the alert also goes out as a system notification (see
|
|
1448
|
-
// awayToastAllowed).
|
|
1449
|
-
showToast(kind, title, body, sessionId, quick, awayToastAllowed());
|
|
1450
|
-
showOverlay(kind, title, body, sessionId, quick);
|
|
1395
|
+
showToast(kind, title, body, sessionId, quick);
|
|
1451
1396
|
showFallbackApprovalCard(kind, title, body, sessionId, quick);
|
|
1452
1397
|
if (kind === "attention") {
|
|
1453
1398
|
playAttentionSound();
|
|
@@ -2088,7 +2033,7 @@
|
|
|
2088
2033
|
});
|
|
2089
2034
|
} catch (e) {}
|
|
2090
2035
|
// keep in sync with package.json "version" on every release
|
|
2091
|
-
api.version = "1.
|
|
2036
|
+
api.version = "1.11.0";
|
|
2092
2037
|
api.setConfig = function (patch) { return setConfigLocal(patch); };
|
|
2093
2038
|
api.resetConfig = function () {
|
|
2094
2039
|
try {
|
|
@@ -2149,19 +2094,17 @@
|
|
|
2149
2094
|
currentHiddenOnly: config.currentHiddenOnly,
|
|
2150
2095
|
doneHiddenOnly: config.doneHiddenOnly,
|
|
2151
2096
|
ignoreSubagent: config.ignoreSubagent,
|
|
2152
|
-
channel: config.channel,
|
|
2153
|
-
toast: config.toast,
|
|
2154
2097
|
sound: config.sound,
|
|
2155
2098
|
soundDone: soundMode("done"),
|
|
2156
2099
|
soundAttention: soundMode("attention"),
|
|
2157
2100
|
customSoundDone: !!readSound("done"),
|
|
2158
2101
|
customSoundAttention: !!readSound("attention"),
|
|
2159
2102
|
quickActions: config.quickActions,
|
|
2160
|
-
// in-page
|
|
2161
|
-
//
|
|
2162
|
-
//
|
|
2163
|
-
|
|
2164
|
-
enabled:
|
|
2103
|
+
// The in-page card layer still exists for the desktop approval fallback
|
|
2104
|
+
// (the shell's toasts carry no buttons); `cards` is the live stack and
|
|
2105
|
+
// `corner` the configured anchor.
|
|
2106
|
+
card: {
|
|
2107
|
+
enabled: config.quickActions,
|
|
2165
2108
|
corner: overlayCorner(),
|
|
2166
2109
|
cards: overlayCount()
|
|
2167
2110
|
},
|
|
@@ -2234,7 +2177,6 @@
|
|
|
2234
2177
|
hostEnvMismatch: "(自动识别结果是「",
|
|
2235
2178
|
hostEnvMismatchTail: "」,手动选择会覆盖它)",
|
|
2236
2179
|
hostEnvDesktopForcedNote: "(当前页面不是 dsh-app://:点通知会尝试唤起 dsh:// 协议,浏览器可能弹「打开应用」确认框)",
|
|
2237
|
-
toast: "系统通知",
|
|
2238
2180
|
toastHint: "走系统通知中心,需要通知权限;桌面端这类通知没有「同意 / 拒绝」按钮。",
|
|
2239
2181
|
sound: "提示音",
|
|
2240
2182
|
soundHint: "提示音总开关;播什么由「高级设置 → 音效来源」决定。",
|
|
@@ -2307,21 +2249,13 @@
|
|
|
2307
2249
|
quickApprovalNote: "「同意」等价于审批卡片上的「允许一次」,只放行这一次。",
|
|
2308
2250
|
bridgeOff: "快捷裁决按钮暂不可用(",
|
|
2309
2251
|
bridgeOffTail: "):系统通知没有按钮,按钮改画在窗口角落的卡片上。",
|
|
2310
|
-
bridgeOffDesktop: "系统通知本身没有「同意 / 拒绝」按钮:桌面端页面(dsh-app://)注册不了 Service Worker(宿主少给 allowServiceWorkers
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
channelLabel: "通知类型",
|
|
2314
|
-
overlaySeg: "浮层提醒",
|
|
2315
|
-
channelHint: "系统通知只会出现在主屏,进通知中心要权限,桌面端的通知本身没有「同意 / 拒绝」按钮(审批会额外在窗口角落弹一张可点击的卡片);页面浮层画在 DSH 窗口所在的那块屏,不需要权限,卡片上能直接裁决。两个只能选一个。",
|
|
2316
|
-
overlayCorner: "浮层位置",
|
|
2317
|
-
overlayCornerHint: "贴在 DSH 窗口的哪个角;窗口在哪块屏,浮层就出现在哪块屏。",
|
|
2252
|
+
bridgeOffDesktop: "系统通知本身没有「同意 / 拒绝」按钮:桌面端页面(dsh-app://)注册不了 Service Worker(宿主少给 allowServiceWorkers 特权)。审批类提醒会同时在窗口角落弹一张卡片,按钮就在卡片上。",
|
|
2253
|
+
overlayCorner: "审批卡片位置",
|
|
2254
|
+
overlayCornerHint: "桌面端审批卡片贴在 DSH 窗口的哪个角;窗口在哪块屏,卡片就出现在哪块屏。",
|
|
2318
2255
|
overlayPosTR: "右上",
|
|
2319
2256
|
overlayPosBR: "右下",
|
|
2320
2257
|
overlayPosTL: "左上",
|
|
2321
2258
|
overlayPosBL: "左下",
|
|
2322
|
-
overlayQuickNote: "浮层上的「同意」同样等价于审批卡片上的「允许一次」,与通知按钮一致。",
|
|
2323
|
-
overlayAwayToast: "窗口在后台时补发系统通知",
|
|
2324
|
-
overlayAwayToastHint: "浮层画在页面里,窗口被别的应用挡住时看不见,等于没有提醒。打开后,窗口不在前台时仍会补发一条系统通知(需要通知权限)。",
|
|
2325
2259
|
testApprovalNoBridge: "已发测试提醒:系统通知没有按钮,快捷裁决按钮画在窗口角落的卡片上。",
|
|
2326
2260
|
testNeedsQuick: "先打开上面的「审批通知上直接裁决」才能测按钮。",
|
|
2327
2261
|
volume: "音量",
|
|
@@ -2366,7 +2300,6 @@
|
|
|
2366
2300
|
hostEnvMismatch: " (auto-detection sees \"",
|
|
2367
2301
|
hostEnvMismatchTail: "\"; the manual choice overrides it)",
|
|
2368
2302
|
hostEnvDesktopForcedNote: " (this page is not dsh-app://: a toast click will try to launch the dsh:// protocol and the browser may show an \"open app?\" prompt)",
|
|
2369
|
-
toast: "System notifications",
|
|
2370
2303
|
toastHint: "Through the system notification center; needs notification permission, and on the Desktop these carry no Approve / Reject buttons.",
|
|
2371
2304
|
sound: "Sound",
|
|
2372
2305
|
soundHint: "Master cue switch; what it plays comes from Advanced → Alert sound.",
|
|
@@ -2439,21 +2372,13 @@
|
|
|
2439
2372
|
quickApprovalNote: "\"Approve\" equals \"Allow once\" on the approval card — it allows that one call only.",
|
|
2440
2373
|
bridgeOff: "Quick-decision buttons are unavailable right now (",
|
|
2441
2374
|
bridgeOffTail: "); system notifications carry no buttons, so they are drawn on a card in the window corner instead.",
|
|
2442
|
-
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
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
channelLabel: "Alert type",
|
|
2446
|
-
overlaySeg: "Overlay",
|
|
2447
|
-
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.",
|
|
2448
|
-
overlayCorner: "Overlay position",
|
|
2449
|
-
overlayCornerHint: "Which corner of the DSH window the cards stack in; they follow the window to whatever display it is on.",
|
|
2375
|
+
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.",
|
|
2376
|
+
overlayCorner: "Approval card position",
|
|
2377
|
+
overlayCornerHint: "Which corner of the DSH window the Desktop approval card sits in; it follows the window to whatever display it is on.",
|
|
2450
2378
|
overlayPosTR: "Top right",
|
|
2451
2379
|
overlayPosBR: "Bottom right",
|
|
2452
2380
|
overlayPosTL: "Top left",
|
|
2453
2381
|
overlayPosBL: "Bottom left",
|
|
2454
|
-
overlayQuickNote: "\"Approve\" here equals \"Allow once\" on the approval card — the same decision the toast buttons send.",
|
|
2455
|
-
overlayAwayToast: "Also send a system notification while the window is in the background",
|
|
2456
|
-
overlayAwayToastHint: "The card is drawn inside the page, so a window hidden behind another app shows nothing — as good as no alert. With this on, an alert that arrives while the window is not in front is also sent as a system notification (notification permission required).",
|
|
2457
2382
|
testApprovalNoBridge: "Test alert sent: system notifications carry no buttons, so the quick-decision buttons are drawn on a card in the window corner.",
|
|
2458
2383
|
testNeedsQuick: "Turn on \"Decide approvals from the toast\" above to test the buttons.",
|
|
2459
2384
|
volume: "Volume",
|
|
@@ -2584,11 +2509,6 @@
|
|
|
2584
2509
|
".dnm-knob{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:#fff;transition:left .15s ease}" +
|
|
2585
2510
|
".dnm-switch.on .dnm-knob{left:16px}" +
|
|
2586
2511
|
".dnm-radio{flex:none;width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#3b82f6);cursor:pointer}" +
|
|
2587
|
-
".dnm-seg{display:inline-flex;flex:none;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.35));border-radius:8px;overflow:hidden}" +
|
|
2588
|
-
".dnm-segBtn{height:28px;padding:0 12px;border:0;background:transparent;color:var(--dsw-alias-label-secondary,rgba(90,90,90,.95));font:inherit;font-size:12px;line-height:28px;cursor:pointer;white-space:nowrap}" +
|
|
2589
|
-
".dnm-segBtn+.dnm-segBtn{border-left:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.35))}" +
|
|
2590
|
-
".dnm-segBtn:hover{color:var(--dsw-alias-label-primary,rgba(20,20,20,.9))}" +
|
|
2591
|
-
".dnm-segBtn.on{background:var(--dsw-alias-state-business-primary,#3b82f6);color:#fff}" +
|
|
2592
2512
|
".dnm-range{width:150px;accent-color:var(--dsw-alias-state-business-primary,#3b82f6)}" +
|
|
2593
2513
|
".dnm-select{border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.4));background:var(--dsw-alias-bg-layer-1,rgba(255,255,255,.03));color:var(--dsw-alias-label-primary,rgba(20,20,20,.9));border-radius:8px;padding:5px 8px;font-size:13px;font-family:inherit}" +
|
|
2594
2514
|
".dnm-btn{border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.4));background:transparent;color:var(--dsw-alias-label-primary,rgba(20,20,20,.9));border-radius:8px;padding:5px 12px;font-size:13px;cursor:pointer;font-family:inherit}" +
|
|
@@ -3051,30 +2971,6 @@
|
|
|
3051
2971
|
function secTitle(labelText) {
|
|
3052
2972
|
return h("div", { className: "dnm-secTitle" }, labelText);
|
|
3053
2973
|
}
|
|
3054
|
-
// One channel at a time. A pair of radios read as two unrelated dots, so
|
|
3055
|
-
// the choice is a segmented control: the filled half is the active option.
|
|
3056
|
-
function segButton(labelText, checked, onPick) {
|
|
3057
|
-
return h("button", {
|
|
3058
|
-
type: "button",
|
|
3059
|
-
role: "radio",
|
|
3060
|
-
"aria-checked": checked ? "true" : "false",
|
|
3061
|
-
"aria-label": labelText,
|
|
3062
|
-
className: "dnm-segBtn" + (checked ? " on" : ""),
|
|
3063
|
-
onClick: function () { if (!checked) onPick(); }
|
|
3064
|
-
}, labelText);
|
|
3065
|
-
}
|
|
3066
|
-
function rowChannel(labelText, hintText, current, onPick) {
|
|
3067
|
-
return tipRow("dnm-row", hintText, [
|
|
3068
|
-
h("div", { className: "dnm-rowText" }, [
|
|
3069
|
-
h("div", { className: "dnm-rowLabel" }, labelText),
|
|
3070
|
-
srOnly(hintText)
|
|
3071
|
-
]),
|
|
3072
|
-
h("div", { className: "dnm-seg", role: "radiogroup", "aria-label": labelText }, [
|
|
3073
|
-
segButton(t("toast"), current !== "overlay", function () { onPick("toast"); }),
|
|
3074
|
-
segButton(t("overlaySeg"), current === "overlay", function () { onPick("overlay"); })
|
|
3075
|
-
])
|
|
3076
|
-
]);
|
|
3077
|
-
}
|
|
3078
2974
|
// The permission state belongs on the System-notifications row it affects
|
|
3079
2975
|
// rather than in a footer note the user has to connect to it.
|
|
3080
2976
|
function toastHintText() {
|
|
@@ -3083,8 +2979,8 @@
|
|
|
3083
2979
|
var advState = React.useState(false);
|
|
3084
2980
|
var advancedOpen = advState[0];
|
|
3085
2981
|
var setAdvancedOpen = advState[1];
|
|
3086
|
-
// Dependent controls (
|
|
3087
|
-
//
|
|
2982
|
+
// Dependent controls (volume) stay folded away until the row they belong
|
|
2983
|
+
// to is clicked open.
|
|
3088
2984
|
var subStateHook = React.useState({});
|
|
3089
2985
|
var subState = subStateHook[0];
|
|
3090
2986
|
var setSubState = subStateHook[1];
|
|
@@ -3137,42 +3033,15 @@
|
|
|
3137
3033
|
h("p", { className: "dnm-headSub" }, t("blurb"))
|
|
3138
3034
|
])
|
|
3139
3035
|
]),
|
|
3140
|
-
// 1) How the alert reaches you.
|
|
3141
|
-
//
|
|
3036
|
+
// 1) How the alert reaches you. System notifications only — no channel
|
|
3037
|
+
// choice since 1.11.0 — with the volume slider hanging beneath the
|
|
3038
|
+
// sound switch.
|
|
3142
3039
|
secTitle(t("secWays")),
|
|
3143
3040
|
card([
|
|
3144
3041
|
rowSwitch("enabled", t("enable"), t("enableHint"), cfg.enabled, function (v) {
|
|
3145
3042
|
patch({ enabled: v });
|
|
3146
3043
|
if (!v && attentionActive > 0) { attentionActive = 0; applyTitle(); }
|
|
3147
3044
|
}),
|
|
3148
|
-
rowChannel(t("channelLabel"), t("channelHint"), cfg.channel, function (next) { patch({ channel: next }); }),
|
|
3149
|
-
cfg.channel === "overlay" ? discloseRow({
|
|
3150
|
-
key: "overlayCorner",
|
|
3151
|
-
rowClass: "dnm-row dnm-rowSub",
|
|
3152
|
-
labelText: t("overlayCorner"),
|
|
3153
|
-
hintText: t("overlayCornerHint"),
|
|
3154
|
-
valueText: t("overlayPos" + ({ "top-right": "TR", "bottom-right": "BR", "top-left": "TL", "bottom-left": "BL" })[overlayCorner()] || "BR"),
|
|
3155
|
-
sub: tipRow("dnm-row dnm-rowSub", t("overlayCornerHint"), [
|
|
3156
|
-
h("div", { className: "dnm-rowText" }, [
|
|
3157
|
-
h("div", { className: "dnm-rowLabel" }, t("overlayCorner")),
|
|
3158
|
-
srOnly(t("overlayCornerHint"))
|
|
3159
|
-
]),
|
|
3160
|
-
h("select", {
|
|
3161
|
-
className: "dnm-select",
|
|
3162
|
-
value: overlayCorner(),
|
|
3163
|
-
"aria-label": t("overlayCorner"),
|
|
3164
|
-
onChange: function (e) { patch({ overlayCorner: e.currentTarget.value }); }
|
|
3165
|
-
}, [
|
|
3166
|
-
h("option", { value: "top-right" }, t("overlayPosTR")),
|
|
3167
|
-
h("option", { value: "bottom-right" }, t("overlayPosBR")),
|
|
3168
|
-
h("option", { value: "top-left" }, t("overlayPosTL")),
|
|
3169
|
-
h("option", { value: "bottom-left" }, t("overlayPosBL"))
|
|
3170
|
-
])
|
|
3171
|
-
])
|
|
3172
|
-
}) : null,
|
|
3173
|
-
cfg.channel === "overlay" ? rowSwitch("overlayAwayToast", t("overlayAwayToast"), t("overlayAwayToastHint"), cfg.overlayAwayToast, function (v) {
|
|
3174
|
-
patch({ overlayAwayToast: v });
|
|
3175
|
-
}) : null,
|
|
3176
3045
|
cfg.sound ? discloseRow({
|
|
3177
3046
|
key: "volume",
|
|
3178
3047
|
labelText: t("sound"),
|
|
@@ -3204,7 +3073,27 @@
|
|
|
3204
3073
|
rowSwitch("quickActions", t("quickActions"), t("quickActionsHint"), cfg.quickActions, function (v) {
|
|
3205
3074
|
patch({ quickActions: v });
|
|
3206
3075
|
if (v) ensureActionBridge();
|
|
3207
|
-
})
|
|
3076
|
+
}),
|
|
3077
|
+
// The in-page card only exists to carry Approve / Reject where the OS
|
|
3078
|
+
// toast cannot (the desktop shell), so its corner hangs under the
|
|
3079
|
+
// quick-decision switch rather than a channel picker.
|
|
3080
|
+
cfg.quickActions ? tipRow("dnm-row dnm-rowSub", t("overlayCornerHint"), [
|
|
3081
|
+
h("div", { className: "dnm-rowText" }, [
|
|
3082
|
+
h("div", { className: "dnm-rowLabel" }, t("overlayCorner")),
|
|
3083
|
+
srOnly(t("overlayCornerHint"))
|
|
3084
|
+
]),
|
|
3085
|
+
h("select", {
|
|
3086
|
+
className: "dnm-select",
|
|
3087
|
+
value: overlayCorner(),
|
|
3088
|
+
"aria-label": t("overlayCorner"),
|
|
3089
|
+
onChange: function (e) { patch({ overlayCorner: e.currentTarget.value }); }
|
|
3090
|
+
}, [
|
|
3091
|
+
h("option", { value: "top-right" }, t("overlayPosTR")),
|
|
3092
|
+
h("option", { value: "bottom-right" }, t("overlayPosBR")),
|
|
3093
|
+
h("option", { value: "top-left" }, t("overlayPosTL")),
|
|
3094
|
+
h("option", { value: "bottom-left" }, t("overlayPosBL"))
|
|
3095
|
+
])
|
|
3096
|
+
]) : null
|
|
3208
3097
|
]),
|
|
3209
3098
|
// 2) Which events are worth interrupting for.
|
|
3210
3099
|
secTitle(t("secWhen")),
|
|
@@ -3222,7 +3111,7 @@
|
|
|
3222
3111
|
!cfg.quickActions ? h("div", { className: "dnm-row" }, [
|
|
3223
3112
|
h("div", { className: "dnm-rowText" }, [h("div", { className: "dnm-rowHint" }, t("testNeedsQuick"))])
|
|
3224
3113
|
]) : h("div", { className: "dnm-row" }, [
|
|
3225
|
-
h("div", { className: "dnm-rowText" }, [h("div", { className: "dnm-rowHint" },
|
|
3114
|
+
h("div", { className: "dnm-rowText" }, [h("div", { className: "dnm-rowHint" }, !bridgeActive() ? (isDesktopShell() ? t("bridgeOffDesktop") : t("bridgeOff") + swState + t("bridgeOffTail")) : t("quickApprovalNote"))])
|
|
3226
3115
|
]),
|
|
3227
3116
|
feedback ? h("div", { className: "dnm-row" }, [
|
|
3228
3117
|
h("div", { className: "dnm-rowText" }, [h("div", { className: "dnm-feedback" }, feedback)])
|
|
@@ -3453,7 +3342,7 @@
|
|
|
3453
3342
|
try { bindCurrentFace(); } catch (e) {}
|
|
3454
3343
|
try { bindUiSession(); } catch (e) {}
|
|
3455
3344
|
}, 1500);
|
|
3456
|
-
try { console.log("[dsh-notify-me] active — window.__dshNotifyMe 可配置提醒 (enabled=" + config.enabled + ", lang=" + effectiveLang() + ",
|
|
3345
|
+
try { console.log("[dsh-notify-me] active — window.__dshNotifyMe 可配置提醒 (enabled=" + config.enabled + ", lang=" + effectiveLang() + ", sound=" + config.sound + ")"); } catch (e) {}
|
|
3457
3346
|
} catch (e) {
|
|
3458
3347
|
try { console.error("[dsh-notify-me] init failed", e); } catch (e2) {}
|
|
3459
3348
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-notify-me",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "离开 DSH 页面也能收到提醒:需要你操作 /
|
|
3
|
+
"version": "1.11.0",
|
|
4
|
+
"description": "离开 DSH 页面也能收到提醒:需要你操作 / 后台回复完成时发系统通知、标题标记与提示音,支持审批快捷裁决与外部推送,Web 版与桌面版均可用。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|