dsh-notify-ding 0.1.2 → 0.1.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/README.md +3 -3
- package/docs/design.md +1 -1
- package/lib/client.js +17 -21
- package/lib/index.d.ts +1 -2
- package/lib/index.js +1 -2
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh·通知叮咚插件
|
|
2
2
|
|
|
3
|
-
在 DSH
|
|
3
|
+
在 DSH 等待你回复或一轮对话跑完时,弹出系统通知。
|
|
4
4
|
|
|
5
5
|
纯浏览器端的插件,不替换任何原生界面。
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## 功能
|
|
13
13
|
|
|
14
|
-
-
|
|
14
|
+
- 某个会话出现新的等待回复交互时叮咚一次,question、plan-review、approval 都算。
|
|
15
15
|
- 某个会话从执行中落到空闲、也就是一轮对话跑完时叮咚一次。
|
|
16
16
|
- 两种触发都会弹一条浏览器系统通知,提示音由 Windows 通知系统自带。
|
|
17
17
|
- 不判断页面是否聚焦,也不区分是否为当前会话,任何会话有事都会响。
|
|
@@ -63,7 +63,7 @@ dsh plugin --profile web remove dsh-notify-ding
|
|
|
63
63
|
|
|
64
64
|
## 工作原理
|
|
65
65
|
|
|
66
|
-
浏览器半身订阅 `ctx.sessions.list` 与 `ctx.uiSession.
|
|
66
|
+
浏览器半身订阅 `ctx.sessions.list` 与 `ctx.uiSession.sessionStatus` 两个只读快照,用边沿检测判断该不该响:运行态从真落到假算一轮跑完,出现新 key 的待回答交互算需要人工介入。首帧只建基线不发声,同一待回答请求只响一次,两次响铃之间留至少 800 毫秒并把该时刻写入 localStorage 跨标签页共享。
|
|
67
67
|
|
|
68
68
|
判定通过时它弹出一条浏览器系统通知。通知按会话记录且不设自动关闭,同一会话只留最新一条;会话被选中、开始新一轮、待回答被解决或从列表移除时会关掉对应通知,点击通知则聚焦窗口、切换过去并关掉该条。完整设计见 [设计说明](docs/design.md)。
|
|
69
69
|
|
package/docs/design.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
| 信号 | 来源 | 触发条件 |
|
|
14
14
|
| --- | --- | --- |
|
|
15
|
-
|
|
|
15
|
+
| 等待回复 | `ctx.uiSession.sessionStatus` | 某会话出现新 key 的交互,question、plan-review、approval 都算 |
|
|
16
16
|
| 一轮跑完 | `ctx.sessions.list` 的 `byId[].running` | 某会话运行态从真落到假 |
|
|
17
17
|
|
|
18
18
|
两者都是可订阅的只读快照,插件不修改任何状态,只观察。
|
package/lib/client.js
CHANGED
|
@@ -15,16 +15,13 @@ window.__ModuleLoader__.load({
|
|
|
15
15
|
* 提示音由系统通知自带,插件不另外发声,全屏专注时通知音会被系统一并静音。
|
|
16
16
|
*
|
|
17
17
|
* 触发条件有两个:
|
|
18
|
-
* 1.
|
|
18
|
+
* 1. 某个会话出现了新的等待回复交互,question / plan-review / approval 都算;
|
|
19
19
|
* 2. 某个会话从「执行中」落到「空闲」,即一轮对话跑完。
|
|
20
20
|
*
|
|
21
21
|
* 按用户选择,两种都响,且不判断页面是否聚焦、不区分是否为当前会话。
|
|
22
|
-
* 重复靠两道闸门挡:同一待回答请求的 key
|
|
23
|
-
* 的最小间隔,避免多开标签页时同一件事响好几轮。
|
|
22
|
+
* 重复靠两道闸门挡:同一待回答请求的 key 只响一次,以及跨标签页共享的最小间隔,避免多开标签页时同一件事响好几轮。
|
|
24
23
|
*
|
|
25
|
-
*
|
|
26
|
-
* 会话被选中、该会话开始新一轮、待回答被解决,或会话从列表移除。每个会话
|
|
27
|
-
* 最多只保留最新一条,新通知会顶掉旧的;点击通知会聚焦窗口并切到该会话。
|
|
24
|
+
* 通知不设自动关闭时间,会一直挂在系统通知里,直到对应会话发生操作:会话被选中、该会话开始新一轮、待回答被解决,或会话从列表移除。每个会话最多只保留最新一条,新通知会顶掉旧的;点击通知会聚焦窗口并切到该会话。
|
|
28
25
|
*
|
|
29
26
|
* @module dsh-notify-ding/client
|
|
30
27
|
*/
|
|
@@ -55,8 +52,7 @@ window.__ModuleLoader__.load({
|
|
|
55
52
|
/**
|
|
56
53
|
* 读取跨标签页共享的上次响铃时刻。
|
|
57
54
|
*
|
|
58
|
-
* localStorage 可能因隐私模式或禁用存储而抛错,抛错时按 0
|
|
59
|
-
* 最坏退化成只在单个标签页内节流。
|
|
55
|
+
* localStorage 可能因隐私模式或禁用存储而抛错,抛错时按 0 处理即可,最坏退化成只在单个标签页内节流。
|
|
60
56
|
*
|
|
61
57
|
* @returns 上次响铃的 Unix 毫秒时间戳。
|
|
62
58
|
*/
|
|
@@ -117,8 +113,7 @@ window.__ModuleLoader__.load({
|
|
|
117
113
|
* 每个会话最多保留一条,新的会先关掉同会话的旧的。
|
|
118
114
|
* 点击通知会聚焦窗口并切到该会话,同时关掉这条通知。
|
|
119
115
|
*
|
|
120
|
-
*
|
|
121
|
-
* 与页面交互时再请求——权限请求必须由用户手势触发。
|
|
116
|
+
* 浏览器未授权时不强行索要权限,而是记下需要补授权,等到用户下一次与页面交互时再请求——权限请求必须由用户手势触发。
|
|
122
117
|
*
|
|
123
118
|
* @param sessionId 这条通知所属的会话。
|
|
124
119
|
* @param title 通知标题。
|
|
@@ -178,8 +173,7 @@ window.__ModuleLoader__.load({
|
|
|
178
173
|
* 发一次「叮咚」,并做节流。
|
|
179
174
|
*
|
|
180
175
|
* 节流时刻写进 localStorage,多标签页共享,避免多开时同一件事响好几轮。
|
|
181
|
-
*
|
|
182
|
-
* 这样连续两次状态变化仍然各响一次,只是间隔被拉开,不会出现漏报。
|
|
176
|
+
* 被节流压下的请求不会直接丢弃,而是挂到窗口边界的定时器上补发,这样连续两次状态变化仍然各响一次,只是间隔被拉开,不会出现漏报。
|
|
183
177
|
*
|
|
184
178
|
* @param sessionId 这条通知所属的会话。
|
|
185
179
|
* @param title 通知标题。
|
|
@@ -220,13 +214,9 @@ window.__ModuleLoader__.load({
|
|
|
220
214
|
* 用一帧会话列表快照做边沿检测:新出现的待回答交互响一次,运行态从真落到假响一次。
|
|
221
215
|
*
|
|
222
216
|
* 首帧只建基线不响,否则每次刷新页面都会把既有状态补报一轮。
|
|
223
|
-
*
|
|
224
|
-
* 运行态从假升到真说明已回到该会话开始新一轮,会话被选中,
|
|
225
|
-
* 以及会话从列表消失,都算一次操作。
|
|
217
|
+
* 同时识别「会话被操作」的信号,把对应会话还挂着的通知关掉:运行态从假升到真说明已回到该会话开始新一轮,会话被选中,以及会话从列表消失,都算一次操作。
|
|
226
218
|
*
|
|
227
|
-
*
|
|
228
|
-
* 立刻被关掉。current 短暂变空是 DSH 的 masked gap,选中会话暂时不在列表时
|
|
229
|
-
* 就会出现,不算一次操作,也不该把上一个有效选中抹掉。
|
|
219
|
+
* 选中变化先于响铃处理,否则同一帧里既跑完又刚被选中的会话,通知会建立后立刻被关掉。current 短暂变空是 DSH 的 masked gap,选中会话暂时不在列表时就会出现,不算一次操作,也不该把上一个有效选中抹掉。
|
|
230
220
|
*
|
|
231
221
|
* @param state 当前会话列表快照。
|
|
232
222
|
* @param baseline 是否首帧,首帧只建基线。
|
|
@@ -295,6 +285,12 @@ window.__ModuleLoader__.load({
|
|
|
295
285
|
for (const key of Array.from(track.pendingKeys)) if (key !== stillPending.key) track.pendingKeys.delete(key);
|
|
296
286
|
}
|
|
297
287
|
}
|
|
288
|
+
/** 从会话状态快照里投影出待回答交互,按会话 id 索引。 */
|
|
289
|
+
function readPending(statusSource) {
|
|
290
|
+
const projected = /* @__PURE__ */ new Map();
|
|
291
|
+
for (const [id, row] of statusSource.getSnapshot()) if (row.pendingInteraction) projected.set(id, row.pendingInteraction);
|
|
292
|
+
return projected;
|
|
293
|
+
}
|
|
298
294
|
/**
|
|
299
295
|
* 同时盯住会话列表与待回答交互两个快照源。
|
|
300
296
|
*
|
|
@@ -302,7 +298,7 @@ window.__ModuleLoader__.load({
|
|
|
302
298
|
*/
|
|
303
299
|
function apply(ctx) {
|
|
304
300
|
if (typeof Notification !== "undefined" && Notification.permission === "default") armPermissionGesture();
|
|
305
|
-
const
|
|
301
|
+
const statusSource = ctx.uiSession.sessionStatus;
|
|
306
302
|
const listSource = ctx.sessions.list;
|
|
307
303
|
const open = ctx.sessions.open;
|
|
308
304
|
sessionOpener = typeof open === "function" ? open.bind(ctx.sessions) : void 0;
|
|
@@ -322,13 +318,13 @@ window.__ModuleLoader__.load({
|
|
|
322
318
|
initialized = true;
|
|
323
319
|
scanSessions(state, baseline);
|
|
324
320
|
try {
|
|
325
|
-
scanPending(
|
|
321
|
+
scanPending(readPending(statusSource), state, baseline);
|
|
326
322
|
} catch (error) {
|
|
327
323
|
console.warn("[dsh-notify-ding] 读取待回答交互失败:" + String(error));
|
|
328
324
|
}
|
|
329
325
|
};
|
|
330
326
|
const unsubscribeList = listSource.subscribe(scan);
|
|
331
|
-
const unsubscribePending =
|
|
327
|
+
const unsubscribePending = statusSource.subscribe(scan);
|
|
332
328
|
scan();
|
|
333
329
|
ctx.effect(() => () => {
|
|
334
330
|
unsubscribeList();
|
package/lib/index.d.ts
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* dsh-notify-ding 的宿主入口。
|
|
4
4
|
*
|
|
5
5
|
* 提示音交给系统通知自带,插件不再自行发声,因此本入口没有运行时行为。
|
|
6
|
-
* 之所以还留一个入口文件,是因为客户端模块系统按 Loader entry 扫描
|
|
7
|
-
* dsh.client 声明:entry 指向的宿主模块必须存在,浏览器半身才会被加载。
|
|
6
|
+
* 之所以还留一个入口文件,是因为客户端模块系统按 Loader entry 扫描 dsh.client 声明:entry 指向的宿主模块必须存在,浏览器半身才会被加载。
|
|
8
7
|
*
|
|
9
8
|
* @module dsh-notify-ding
|
|
10
9
|
*/
|
package/lib/index.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* dsh-notify-ding 的宿主入口。
|
|
4
4
|
*
|
|
5
5
|
* 提示音交给系统通知自带,插件不再自行发声,因此本入口没有运行时行为。
|
|
6
|
-
* 之所以还留一个入口文件,是因为客户端模块系统按 Loader entry 扫描
|
|
7
|
-
* dsh.client 声明:entry 指向的宿主模块必须存在,浏览器半身才会被加载。
|
|
6
|
+
* 之所以还留一个入口文件,是因为客户端模块系统按 Loader entry 扫描 dsh.client 声明:entry 指向的宿主模块必须存在,浏览器半身才会被加载。
|
|
8
7
|
*
|
|
9
8
|
* @module dsh-notify-ding
|
|
10
9
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-notify-ding",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "「DSH·通知叮咚」:在 DSH 等待回复或一轮对话跑完时弹出系统通知。标准可安装的 dsh 客户端插件。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"dsh",
|
|
@@ -55,6 +55,10 @@
|
|
|
55
55
|
"build": "tsdown",
|
|
56
56
|
"typecheck": "tsc --noEmit"
|
|
57
57
|
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@deepseek-ai/dsh": "^0.2.0-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.2.0-rc.2"
|
|
61
|
+
},
|
|
58
62
|
"devDependencies": {
|
|
59
63
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
60
64
|
"@types/node": "^22.20.1",
|