dsh-notify-yimit 0.1.5 → 0.1.7

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.en.md CHANGED
@@ -6,18 +6,23 @@
6
6
  <a href="./README.en.md">English</a>
7
7
  </p>
8
8
 
9
+ <p align="center">
10
+ <a href="https://github.com/YiMlT/dsh-notify-yimit">
11
+ <img alt="GitHub repository" src="https://img.shields.io/badge/GitHub-YiMlT%2Fdsh--notify--yimit-181717?logo=github&amp;logoColor=white">
12
+ </a>
13
+ </p>
14
+
15
+ ## Overview
16
+
9
17
  A notification plugin for DeepSeek Harness: alerts you on **task completed / task failed / running (live activity) / awaiting approval / awaiting answer**.
10
- The notification title is the conversation title; both system and custom notifications support **click-to-jump to the corresponding session**.
18
+
19
+ - **Two notification styles**, switchable in Settings: **Windows system notifications** (native browser notifications) or **custom in-app notifications** (a toast at the bottom-right of the desktop, independent of the browser page); or turn everything **off** (default).
20
+ - **The notification title is the conversation title** (updated in place once the LLM generates it; the workspace directory name is never used). Toasts offer **Ignore**, and on the web also **Jump to session** (see below).
21
+ - **One codebase for desktop and web**: the host detects its platform from the runtime and hides only browser-facing entry points — notification behavior is identical.
22
+ - **Settings-page integration**: DSH Settings → "Notifications" section with a master switch, notification style, toast count/duration, and per-type background/text colors (done = green, failed = red, running = blue, approval = yellow, question = purple).
11
23
 
12
24
  ## Features
13
25
 
14
- - **Settings-page integration**: DSH Settings → "Notifications" section (native DSH styling, `--dsw-*` theme variables):
15
- - Plugin master switch (all other controls are disabled while off);
16
- - Notification style **segmented control** with three options: **Windows system notifications** / **custom in-app notifications** / **off** (default: off);
17
- - **Browser picker for jump-to-session** (shown when the style is system or custom; auto-detects installed browsers — Chrome/Edge/Firefox etc.; empty = system default browser; "Jump to session" always opens the browser);
18
- - Custom notifications: **max simultaneously visible**, **display duration**, and a **per-type background/text color** list
19
- (done = green, failed = red, running = blue, approval = yellow, question = purple; each customizable);
20
- - The custom-settings block animates open/closed; system notifications offer one-click permission request.
21
26
  - **Trigger scenarios**:
22
27
  | Scenario | Notification content |
23
28
  |---|---|
@@ -26,34 +31,39 @@ The notification title is the conversation title; both system and custom notific
26
31
  | Running | Live activity (started / thinking… / generating reply… / executing \<tool\>; toast text updates in place) |
27
32
  | Awaiting approval | The concrete approval content (tool name / reason) |
28
33
  | Awaiting answer | The concrete question from ask_user_question |
29
- - **Custom notification = desktop toast (independent of the browser)**: the host plugin spawns a **resident PowerShell + WPF host process** that pops borderless, always-on-top toasts at the **bottom-right** of the screen (multiple toasts stack upward without overlapping). Each toast has a title, body, and **Ignore** / **Jump to session** buttons (10px rounded-rect buttons), colored per type from the config. Done/failed toasts auto-dismiss after the display duration; **running/approval/question toasts stay until their state ends** (turn end / decision made / answer given). Running content is **updated in place** (400ms throttle — no re-spawn, no flicker); approval/question are stateful and **not debounced** (multiple approvals/questions within 2s are never swallowed). Whether the browser page is open or minimized does not matter. Until the session title arrives (LLM-generated asynchronously — first-turn events precede the title event), toasts show a localized placeholder ("(Unnamed session)"); when the `session/title` event lands, **the titles of already-shown toasts are updated in place** — the workspace directory name is never used.
34
+ - **Settings section**: DSH Settings → "Notifications" (native DSH styling, `--dsw-*` theme variables):
35
+ - Plugin master switch (all other controls are disabled while off);
36
+ - Notification style **segmented control** with three options: **Windows system notifications** / **custom in-app notifications** / **off** (default: off);
37
+ - **Browser picker for jump-to-session** (**web only**; shown when the style is system or custom; auto-detects installed browsers — Chrome/Edge/Firefox etc.; empty = system default browser);
38
+ - Custom notifications: **max simultaneously visible**, **display duration**, and a **per-type background/text color** list
39
+ (done = green, failed = red, running = blue, approval = yellow, question = purple; each customizable);
40
+ - The custom-settings block animates open/closed; system notifications offer one-click permission request.
41
+ - **Custom notification = desktop toast (independent of the browser)**: the host plugin spawns a **resident PowerShell + WPF host process** that pops borderless, always-on-top toasts at the **bottom-right** of the screen (multiple toasts stack upward without overlapping). Each toast has a title, body, and an **Ignore** button (**web only: plus a "Jump to session" button**; 10px rounded-rect buttons), colored per type from the config. Done/failed toasts auto-dismiss after the display duration; **running/approval/question toasts stay until their state ends** (turn end / decision made / answer given). Running content is **updated in place** (400ms throttle — no re-spawn, no flicker); approval/question are stateful and **not debounced** (multiple approvals/questions within 2s are never swallowed). Whether the browser page is open or minimized does not matter. Until the session title arrives (LLM-generated asynchronously — first-turn events precede the title event), toasts show a localized placeholder ("(Unnamed session)"); when the `session/title` event lands, **the titles of already-shown toasts are updated in place** — the workspace directory name is never used.
30
42
  - **Resident host architecture**: the plugin starts one `powershell` host process (`toast-host.ps1`) on load; WPF is loaded only once and every toast is created inside that process — toast creation latency drops from ~1s cold start to ~10ms. The host receives one JSON command per line on **stdin** (`show`/`text`/`title`/`move`/`close`/`shutdown`) and reports `pos`/`exit` on **stdout**; no per-toast process spawn and no ctl/pos file polling.
31
- - **Jump to session**: clicking a system notification or the toast's "Jump to session" button → **always opens the browser** and navigates to the session (via the URL hash convention `#dsh-notify-yimit/session=<id>`, listened to by the client; optionally with a specific browser).
43
+ - **Jump to session (web only)**: clicking a system notification or the toast's "Jump to session" button opens the browser and navigates to the session (via the URL hash convention `#dsh-notify-yimit/session=<id>`, listened to by the client; optionally with a specific browser). **The desktop app does not show that button** — opening a browser from inside the desktop app is redundant.
44
+ - **Runtime-aware UI**: the host detects its platform from the runtime (Electron runtime = desktop, plain Node = web) and adapts the UI:
45
+ - desktop: toasts omit the "Jump to session" button; the settings page hides the jump browser picker and the "system notifications come from the browser / custom toasts sit at the bottom-right" explainer;
46
+ - web: everything stays visible.
32
47
  - **System notifications**: native browser notifications; clicking one focuses the window and opens the session.
33
48
  - **Requirements**: Windows (PowerShell 5.1+, built-in); custom desktop toasts need no extra dependencies.
34
49
 
35
50
  ## Installation
36
51
 
37
- ```sh
38
- dsh plugin --profile web add dsh-notify-yimit
39
- ```
52
+ 1. **Command line** — run:
40
53
 
41
- Then **restart dsh web** (host-side plugins need a restart) and enable it in Settings → Notifications.
54
+ ```sh
55
+ dsh plugin --profile web add dsh-notify-yimit
56
+ ```
42
57
 
43
- > Manual alternative: add `"dsh-notify-yimit": "file:<path-to-this-directory>"` to the `dependencies` of
44
- > `~/.dsh/profiles/web/package.json`, run `pnpm install`, then restart.
58
+ For the desktop profile, replace `web` with `desktop`:
45
59
 
46
- ### Installing from npm (published package)
60
+ ```sh
61
+ dsh plugin --profile desktop add dsh-notify-yimit
62
+ ```
47
63
 
48
- ```sh
49
- dsh plugin --profile web add dsh-notify-yimit
50
- # or manually:
51
- # ~/.dsh/profiles/web/package.json → dependencies: "dsh-notify-yimit": "^0.1.0"
52
- # ~/.dsh/profiles/web/package.json → dsh.profile.bundles: add "dsh-notify-yimit"
53
- pnpm install # inside the profile
54
- ```
64
+ 2. **Desktop GUI** — open **Settings → Plugins** (plugin manager), type `dsh-notify-yimit` into the add-plugin field, and click **Install**.
55
65
 
56
- Then restart `dsh web`.
66
+ After installing, **restart dsh** (host-side plugins need a restart), then enable it in **Settings → Notifications**.
57
67
 
58
68
  ## Structure
59
69
 
@@ -78,12 +88,16 @@ host: session/event(turn/start|assistant/chunk|tool/call|turn/end|session/title)
78
88
  - custom (desktop toasts): one JSON command per line on stdin → resident host (show/text/move/close);
79
89
  host reports pos/exit on stdout → host-side adaptive stacking (real heights + 12px gap) and reflow;
80
90
  running: 400ms throttle + in-place text updates on activity change; at most N toasts at once;
81
- approval/question: stateful, no debounce (replace = update, nothing swallowed)
91
+ approval/question: stateful, no debounce (replace = update, nothing swallowed);
92
+ payload carries jumpEnabled (false on desktop) → the toast omits the "Jump to session" button
82
93
  - system / off: unacknowledged event queue → Typert service (client polls every 250ms)
94
+ → getState() also reports a desktop flag (true on the Electron host) so the settings page can hide
95
+ browser-only controls
83
96
  client: settings config → dispatch:
84
97
  - system → Web Notification (tag replaced per session:type, onclick jumps to session)
85
98
  - custom → only acknowledges events (no in-page overlay; desktop toasts are handled by the host)
86
99
  - session deep link: listens to #dsh-notify-yimit/session=<id> (the toast "Jump to session" channel) → ctx.sessions.open
100
+ - desktop=true: hides the jump browser picker and the notification-style explainer
87
101
  ```
88
102
 
89
103
  ## Config storage
@@ -95,3 +109,4 @@ client: settings config → dispatch:
95
109
  - System notifications require browser notification permission; `127.0.0.1` is a secure context, so it can be requested directly.
96
110
  - The plugin is off by default; enable it and pick a style to take effect.
97
111
  - Running notifications update their content in real time as activity changes; completed/failed/approval/question are one-shot (stateful ones update in place).
112
+ - Platform detection looks only at the host runtime: Electron (the Electron runtime / `DeepSeek Harness.exe`) counts as desktop, plain Node counts as web. On desktop only the browser-facing entry points are hidden — notification behavior itself is identical.
package/README.md CHANGED
@@ -5,20 +5,28 @@
5
5
  /
6
6
  <a href="./README.en.md">English</a>
7
7
  </p>
8
- DeepSeek Harness 通知插件:在 **任务完成 / 任务出错 / 运行中 / 等待审批 / 等待回答** 时提醒用户。
9
- 通知标题为对话标题;系统通知与自定义通知均支持**点击跳转到对应会话**。
8
+
9
+ <p align="center">
10
+ <a href="https://github.com/YiMlT/dsh-notify-yimit">
11
+ <img alt="GitHub 仓库" src="https://img.shields.io/badge/GitHub-YiMlT%2Fdsh--notify--yimit-181717?logo=github&amp;logoColor=white">
12
+ </a>
13
+ </p>
14
+
15
+ ## 功能概述
16
+
17
+ DeepSeek Harness 通知插件:在 **任务完成 / 任务出错 / 运行中(实时活动)/ 等待审批 / 等待回答** 时提醒用户。
18
+
19
+ - **两种通知方式**,设置页一键切换:**Windows 系统通知**(浏览器原生通知)或**插件自定义样式通知**
20
+ (桌面右下角浮窗,不依赖浏览器页面);也可整体**关闭**(默认)。
21
+ - **通知标题为对话标题**(LLM 生成后原地更新,不放工作区目录名);浮窗支持
22
+ **忽略**,web 端另有**跳转会话**(详见下文)。
23
+ - **桌面端与 web 端同一套代码**:宿主按运行时自动判定平台,只隐藏与浏览器相关的入口,
24
+ 通知能力完全一致。
25
+ - **设置页集成**:DSH 设置 → 「通知」分节,含总开关、通知方式、浮窗数量/时长、
26
+ 每种通知类型的背景与文字颜色(完成=绿、出错=红、运行中=蓝、待审批=黄、待回答=紫)。
10
27
 
11
28
  ## 功能
12
29
 
13
- - **设置页集成**:DSH 设置 → 「通知」分节(符合 DSH 原生样式,`--dsw-*` 主题变量):
14
-
15
- - 插件总开关(关闭状态下其余设置项全部禁用);
16
- - 通知方式**分段控制器**三选一:**Windows 系统通知** / **插件自定义样式通知** / **关闭**(默认关闭);
17
- - **跳转浏览器选择器**(通知方式为系统通知/自定义通知时显示;自动探测本机浏览器,
18
- 可指定 Chrome/Edge/Firefox 等;默认=系统默认浏览器;「跳转会话」始终打开浏览器);
19
- - 自定义通知:**同时最多显示数量**、**显示时长**,以及**每种通知类型的背景/文字颜色**列表
20
- (完成=绿、出错=红、运行中=蓝、待审批=黄、待回答=紫,可逐类定制);
21
- - 自定义设置区块显隐带过渡动画;系统通知可一键申请浏览器通知权限。
22
30
  - **触发场景**:
23
31
  | 场景 | 通知内容 |
24
32
  |---|---|
@@ -27,9 +35,17 @@ DeepSeek Harness 通知插件:在 **任务完成 / 任务出错 / 运行中 /
27
35
  | 运行中 | 实时活动(开始处理 / 正在思考… / 正在生成回复… / 正在执行 \<工具\>,浮窗内容实时更新) |
28
36
  | 等待审批 | 具体的审批内容(工具名 / 原因) |
29
37
  | 等待回答 | ask_user_question 的具体问题 |
38
+ - **设置页**:DSH 设置 → 「通知」分节(符合 DSH 原生样式,`--dsw-*` 主题变量):
39
+ - 插件总开关(关闭状态下其余设置项全部禁用);
40
+ - 通知方式**分段控制器**三选一:**Windows 系统通知** / **插件自定义样式通知** / **关闭**(默认关闭);
41
+ - **跳转浏览器选择器**(**仅 web 端**:通知方式为系统通知/自定义通知时显示;自动探测本机浏览器,
42
+ 可指定 Chrome/Edge/Firefox 等;默认=系统默认浏览器);
43
+ - 自定义通知:**同时最多显示数量**、**显示时长**,以及**每种通知类型的背景/文字颜色**列表
44
+ (完成=绿、出错=红、运行中=蓝、待审批=黄、待回答=紫,可逐类定制);
45
+ - 自定义设置区块显隐带过渡动画;系统通知可一键申请浏览器通知权限。
30
46
  - **自定义通知 = 桌面浮窗(不依赖浏览器)**:宿主插件通过**常驻 PowerShell + WPF 宿主进程**
31
- 在屏幕**右下角**弹出无边框置顶浮窗(多浮窗自动向上堆叠不重叠),标题、内容、**忽略**、
32
- **跳转会话**按钮(按钮为圆角矩形 10px);按通知类型使用对应配置的背景/文字色。完成/出错
47
+ 在屏幕**右下角**弹出无边框置顶浮窗(多浮窗自动向上堆叠不重叠),标题、内容、**忽略**按钮
48
+ (**web 端另有「跳转会话」按钮**,按钮为圆角矩形 10px);按通知类型使用对应配置的背景/文字色。完成/出错
33
49
  按显示时长后自动消失;**运行中/待审批/待回答在状态结束前不会消失**(任务结束/审批决定/
34
50
  回答完成后自动关闭)。运行中内容**原地更新文本**(400ms 节流,不重弹、不闪烁);
35
51
  待审批/待回答为状态性通知,**不做防抖**(2s 内多条审批/提问不会被吞)。浏览器页面是否
@@ -40,21 +56,35 @@ DeepSeek Harness 通知插件:在 **任务完成 / 任务出错 / 运行中 /
40
56
  一次,所有浮窗在该进程内创建——通知创建延迟从 ~1s 冷启动降到 ~10ms。宿主经 **stdin**
41
57
  收一行一个 JSON 命令(`show`/`text`/`title`/`move`/`close`/`shutdown`),经 **stdout** 回传
42
58
  `pos`/`exit` 报告;不再逐通知 spawn 进程,也没有 ctl/pos 文件轮询。
43
- - **跳转会话**:系统通知点击 / 自定义浮窗「跳转会话」按钮 → **始终打开浏览器**并定位到该会话
59
+ - **跳转会话(仅 web 端)**:系统通知点击 / 自定义浮窗「跳转会话」按钮 → **打开浏览器**并定位到该会话
44
60
  (通过 URL hash 约定 `#dsh-notify-yimit/session=<id>`,客户端监听后切换;可用所选浏览器打开)。
61
+ **桌面端(App)不显示该按钮**——在桌面应用里再打开浏览器属于多余动作。
62
+ - **运行环境自适应**:宿主按运行时自动判定平台(Electron 运行时 = 桌面端,普通 Node = web 端),
63
+ 并据此调整界面:
64
+ - 桌面端:浮窗隐藏「跳转会话」按钮;设置页隐藏「跳转浏览器选择器」与"系统通知由浏览器弹出/
65
+ 自定义为桌面右下角浮窗"那段说明;
66
+ - web 端:全部保留。
45
67
  - **系统通知**:浏览器原生通知,点击通知即聚焦窗口并打开对应会话。
46
68
  - **运行要求**:Windows(PowerShell 5.1+,系统自带);自定义桌面浮窗无需任何额外依赖。
47
69
 
48
70
  ## 安装
49
71
 
50
- ```sh
51
- dsh plugin --profile web add dsh-notify-yimit
52
- ```
72
+ 1. **命令行**:在终端执行
73
+
74
+ ```sh
75
+ dsh plugin --profile web add dsh-notify-yimit
76
+ ```
77
+
78
+ 桌面端 profile 把 `web` 换成 `desktop` 即可:
79
+
80
+ ```sh
81
+ dsh plugin --profile desktop add dsh-notify-yimit
82
+ ```
53
83
 
54
- 然后**重启 dsh web**(host 插件生效需重启),打开 设置 → 通知 开启即可。
84
+ 2. **桌面端图形界面**:打开 **设置 → 插件**(插件管理器),在添加插件的输入框中填入
85
+ `dsh-notify-yimit`,点击**安装**。
55
86
 
56
- > 也可以手动方式:在 `~/.dsh/profiles/web/package.json` 的 dependencies 中加入
57
- > `"dsh-notify-yimit": "file:<本目录路径>"` 后 `pnpm install`,再重启。
87
+ 安装完成后**重启 dsh**(host 插件生效需重启),再到 **设置 → 通知** 开启即可。
58
88
 
59
89
  ## 结构
60
90
 
@@ -77,12 +107,15 @@ host: session/event(turn/start|assistant/chunk|tool/call|turn/end|session/title)
77
107
  - custom(桌面浮窗):stdin 一行 JSON 命令 → 常驻宿主(show/text/move/close);
78
108
  宿主 stdout 回传 pos/exit → host 自适应堆叠(真实高度 + 12px 间距)并重排;
79
109
  运行中按 400ms 节流 + 活动文本变化原地 text 更新;最多 N 个同时显示;
80
- 审批/问答状态性通知免防抖(替换即更新,不吞通知)
110
+ 审批/问答状态性通知免防抖(替换即更新,不吞通知);
111
+ 载荷带 jumpEnabled(桌面端=false)→ 浮窗不创建「跳转会话」按钮
81
112
  - system / off:未确认事件队列 → Typert 服务(客户端 250ms 轮询)
113
+ → getState() 同时返回 desktop 标志(Electron 宿主=true)→ 设置页据此隐藏仅对浏览器有意义的项
82
114
  client: 设置页「通知」配置 → 分发:
83
115
  - system → Web Notification(tag 按 会话:类型 替换,onclick 跳转会话)
84
116
  - custom → 只消费回执(不渲染页面内浮层;桌面浮窗由 host 负责)
85
117
  - 会话深链接:监听 #dsh-notify-yimit/session=<id>(桌面浮窗"跳转会话"通道)→ ctx.sessions.open
118
+ - desktop=true 时:隐藏「跳转浏览器选择器」与通知方式说明文字
86
119
  ```
87
120
 
88
121
  ## 配置存储
@@ -94,3 +127,5 @@ client: 设置页「通知」配置 → 分发:
94
127
  - 系统通知依赖浏览器通知权限;`127.0.0.1` 属安全上下文,可直接申请。
95
128
  - 插件默认关闭,不打扰;开启后选择通知方式生效。
96
129
  - 运行中通知在活动变化时实时更新内容;完成/出错/审批/问答为一次性通知。
130
+ - 平台判定只看宿主运行时:Electron(Electron 运行时/`DeepSeek Harness.exe`)判为桌面端,
131
+ 普通 Node 判为 web 端;桌面端隐藏的只是与浏览器相关的入口,通知能力本身完全一致。
package/lib/client.js CHANGED
@@ -358,6 +358,8 @@ window.__ModuleLoader__.load({
358
358
  if (v === null || typeof v !== 'object' || Array.isArray(v)) fail(path)
359
359
  return {
360
360
  config: parseConfig(v.config, path + '.config'),
361
+ // 宿主运行环境:true = 桌面端。缺省按 web 端处理(旧宿主无该字段)。
362
+ desktop: v.desktop === true,
361
363
  events: Array.isArray(v.events) ? v.events.map((e, i) => parseEvent(e, path + '.events[' +
362
364
  i + ']')) : []
363
365
  }
@@ -389,6 +391,16 @@ window.__ModuleLoader__.load({
389
391
  }
390
392
  })
391
393
 
394
+ // 0.2 的浏览器侧 typert 注册器要求 strict codec 必须带 create() 工厂
395
+ // (dsh-typert-registry/client.js 的 validateCodec),否则 register 抛错 →
396
+ // 插件 entry 被标记 failed → "web boot: 1 entry did not activate"。
397
+ const strictResult = (typeSymbol, schema) => ({
398
+ mode: 'strict',
399
+ typeSymbol,
400
+ schema,
401
+ create: () => schema
402
+ })
403
+
392
404
  const CONTRIBUTION = {
393
405
  package: 'dsh-notify-yimit',
394
406
  descriptors: [{
@@ -400,11 +412,7 @@ window.__ModuleLoader__.load({
400
412
  kind: 'direct'
401
413
  },
402
414
  parameters: [],
403
- result: {
404
- mode: 'strict',
405
- typeSymbol: 'dsh-notify-yimit#NotifyState',
406
- schema: stateCodec
407
- }
415
+ result: strictResult('dsh-notify-yimit#NotifyState', stateCodec)
408
416
  }, {
409
417
  id: 'dsh-notify-yimit#notify/updateConfig',
410
418
  service: 'notify',
@@ -417,17 +425,9 @@ window.__ModuleLoader__.load({
417
425
  name: 'patch',
418
426
  wire: 'patch',
419
427
  source: 'json',
420
- codec: {
421
- mode: 'strict',
422
- typeSymbol: 'dsh-notify-yimit#NotifyConfigPatch',
423
- schema: patchCodec
424
- }
428
+ codec: strictResult('dsh-notify-yimit#NotifyConfigPatch', patchCodec)
425
429
  }],
426
- result: {
427
- mode: 'strict',
428
- typeSymbol: 'dsh-notify-yimit#NotifyConfig',
429
- schema: configCodec
430
- }
430
+ result: strictResult('dsh-notify-yimit#NotifyConfig', configCodec)
431
431
  }, {
432
432
  id: 'dsh-notify-yimit#notify/ackEvents',
433
433
  service: 'notify',
@@ -440,18 +440,10 @@ window.__ModuleLoader__.load({
440
440
  name: 'ids',
441
441
  wire: 'ids',
442
442
  source: 'json',
443
- codec: {
444
- mode: 'strict',
445
- typeSymbol: 'dsh-notify-yimit#EventIds',
446
- schema: codecOf((v) => Array.isArray(v) ? v.map((x) => String(
447
- x)) : [])
448
- }
443
+ codec: strictResult('dsh-notify-yimit#EventIds', codecOf((v) => Array.isArray(v) ?
444
+ v.map((x) => String(x)) : []))
449
445
  }],
450
- result: {
451
- mode: 'strict',
452
- typeSymbol: 'dsh-notify-yimit#AckResult',
453
- schema: ackCodec
454
- }
446
+ result: strictResult('dsh-notify-yimit#AckResult', ackCodec)
455
447
  }, {
456
448
  id: 'dsh-notify-yimit#notify/listBrowsers',
457
449
  service: 'notify',
@@ -461,11 +453,7 @@ window.__ModuleLoader__.load({
461
453
  kind: 'direct'
462
454
  },
463
455
  parameters: [],
464
- result: {
465
- mode: 'strict',
466
- typeSymbol: 'dsh-notify-yimit#BrowserList',
467
- schema: browsersCodec
468
- }
456
+ result: strictResult('dsh-notify-yimit#BrowserList', browsersCodec)
469
457
  }, {
470
458
  id: 'dsh-notify-yimit#notify/startEditMode',
471
459
  service: 'notify',
@@ -475,11 +463,7 @@ window.__ModuleLoader__.load({
475
463
  kind: 'direct'
476
464
  },
477
465
  parameters: [],
478
- result: {
479
- mode: 'strict',
480
- typeSymbol: 'dsh-notify-yimit#AckResult',
481
- schema: ackCodec
482
- }
466
+ result: strictResult('dsh-notify-yimit#AckResult', ackCodec)
483
467
  }, {
484
468
  id: 'dsh-notify-yimit#notify/endEditMode',
485
469
  service: 'notify',
@@ -489,11 +473,7 @@ window.__ModuleLoader__.load({
489
473
  kind: 'direct'
490
474
  },
491
475
  parameters: [],
492
- result: {
493
- mode: 'strict',
494
- typeSymbol: 'dsh-notify-yimit#AckResult',
495
- schema: ackCodec
496
- }
476
+ result: strictResult('dsh-notify-yimit#AckResult', ackCodec)
497
477
  }]
498
478
  }
499
479
 
@@ -916,10 +896,14 @@ window.__ModuleLoader__.load({
916
896
  ['off', t('modeOff')]
917
897
  ]
918
898
  }),
919
- el('p', {
899
+ // 这段说明写的是"系统通知由浏览器弹出 / 自定义为桌面右下角浮窗",
900
+ // 只适用于 web 端;桌面端(本身就是桌面应用)不显示。
901
+ state?.desktop !== true ? el('p', {
920
902
  className: 'dn-note'
921
- }, t('modeNote'))),
922
- (config.mode === 'system' || config.mode === 'custom') ? el('div', {
903
+ }, t('modeNote')) : null),
904
+ // 「跳转会话浏览器」只对浏览器(web 端)有意义:桌面端浮窗不显示跳转按钮,
905
+ // 因此这里在桌面端隐藏该设置行。
906
+ (state?.desktop !== true && (config.mode === 'system' || config.mode === 'custom')) ? el('div', {
923
907
  className: 'dn-block'
924
908
  },
925
909
  el('div', {
@@ -1109,7 +1093,8 @@ window.__ModuleLoader__.load({
1109
1093
  )
1110
1094
  }
1111
1095
 
1112
- // ===== 修改点:inject 数组改为 ['remote', 'locale'] =====
1096
+ // ===== inject 用"服务名"(短名):0.2 客户端 fiber 按服务名解析注入,
1097
+ // 与官方 bundle 一致(dsh-api-remotes: ["remote"]、dsh-client-locale: [..."remote"...]) =====
1113
1098
  const inject = ['remote', 'locale']
1114
1099
 
1115
1100
  async function apply(ctx) {
@@ -1125,6 +1110,7 @@ window.__ModuleLoader__.load({
1125
1110
 
1126
1111
  const store = makeStore({
1127
1112
  config: null,
1113
+ desktop: false,
1128
1114
  events: [],
1129
1115
  browsers: []
1130
1116
  })
@@ -1138,6 +1124,7 @@ window.__ModuleLoader__.load({
1138
1124
  const state = store.getSnapshot()
1139
1125
  store.set({
1140
1126
  config: state.config,
1127
+ desktop: state.desktop,
1141
1128
  events: state.events,
1142
1129
  browsers: parsed.browsers
1143
1130
  })
@@ -1174,6 +1161,7 @@ window.__ModuleLoader__.load({
1174
1161
  const state = store.getSnapshot()
1175
1162
  store.set({
1176
1163
  config: parseConfig(result.value, 'config'),
1164
+ desktop: state.desktop,
1177
1165
  events: state.events,
1178
1166
  browsers: state.browsers
1179
1167
  })
package/lib/index.js CHANGED
@@ -1,7 +1,3 @@
1
- /**
2
- * dsh-notify-yimit-yimit 宿主插件。
3
- * 优化点:内存LRU控制、队列头指针优化、浏览器探测缓存、宿主崩溃退避、全面多语言支持。
4
- */
5
1
  import {
6
2
  z
7
3
  } from 'zod'
@@ -289,7 +285,11 @@ function clip(text) {
289
285
  }
290
286
 
291
287
  function titleOf(session) {
292
- const events = session?.events
288
+ // 宿主兼容:0.1.2-alpha.4 起 `Session.events` 被 `snapshotEvents()` 取代(旧版仍有 .events)。
289
+ // 优先用 snapshotEvents()(无参返回全量冻结事件数组),旧宿主回退 .events。
290
+ const events = typeof session?.snapshotEvents === 'function'
291
+ ? session.snapshotEvents()
292
+ : session?.events
293
293
  if (Array.isArray(events)) {
294
294
  // 全量扫描:标题事件通常在会话早期(seq 小)生成,长会话/恢复会话里它可能在
295
295
  // 最后 50 条之外——只扫尾部会漏掉,导致回退到占位/目录名(恢复会话不会重放
@@ -431,6 +431,21 @@ export function apply(ctx) {
431
431
 
432
432
  const TOAST_HOST_SCRIPT = fileURLToPath(new URL('./toast-host.ps1',
433
433
  import.meta.url))
434
+
435
+ // 运行环境判断:桌面端宿主由 Electron 以 Node 模式启动(execPath 是
436
+ // "DeepSeek Harness.exe"、process.versions.electron 有值),而 web 端是普通 Node。
437
+ // 判定为桌面端时不再显示浮窗里的「跳转会话」按钮——那个按钮是"打开浏览器",
438
+ // 在桌面端里属于多余动作。
439
+ const isDesktopRuntime = (() => {
440
+ try {
441
+ if (typeof process.versions.electron === 'string' && process.versions.electron.length > 0) return true
442
+ const exe = String(process.execPath ?? '').toLowerCase()
443
+ return exe.includes('deepseek harness') || exe.includes('electron')
444
+ } catch {
445
+ return false
446
+ }
447
+ })()
448
+
434
449
  const activeToasts = new Map()
435
450
  const toastOrder = []
436
451
  const pendingClose = new Set()
@@ -745,6 +760,8 @@ export function apply(ctx) {
745
760
  winTitle: `dsh-notify-yimit-${key}`,
746
761
  ignoreLabel: l.ignore,
747
762
  jumpLabel: l.jump,
763
+ // 桌面端隐藏「跳转会话」按钮(该按钮会打开浏览器,在桌面端内多余)
764
+ jumpEnabled: isDesktopRuntime !== true,
748
765
  unnamedLabel: l.unnamed,
749
766
  // 通知窗口样式:宽度 + 自定义位置(null = 默认右下贴边)
750
767
  width: cfg.toast?.width ?? 340,
@@ -1079,6 +1096,8 @@ export function apply(ctx) {
1079
1096
  getState() {
1080
1097
  return {
1081
1098
  config: store.get(),
1099
+ // 运行环境:桌面端(=Electron 宿主)还是 web 端。客户端据此隐藏只对浏览器有意义的设置项。
1100
+ desktop: isDesktopRuntime === true,
1082
1101
  events: queue.slice(queueHead).map((item) => ({
1083
1102
  ...item
1084
1103
  }))
@@ -1169,4 +1188,4 @@ export function apply(ctx) {
1169
1188
 
1170
1189
  ctx.provide('notify', service)
1171
1190
  console.log('[dsh-notify-yimit] Loaded (disabled by default; please enable in Settings -> Notifications)')
1172
- }
1191
+ }