dsh-plugin-session-notifier 0.1.1 → 0.1.2

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
@@ -46,10 +46,10 @@ The panel says so as well.
46
46
  **1. By package name (recommended).** Ask the agent to call:
47
47
 
48
48
  ```
49
- plugin_manager({ action: "install_bundle", target: "dsh-plugin-session-notifier@0.1.0" })
49
+ plugin_manager({ action: "install_bundle", target: "dsh-plugin-session-notifier" })
50
50
  ```
51
51
 
52
- You can also type `dsh-plugin-session-notifier` into **Plugins → Add plugin** yourself. Pick the
52
+ Omitting the version installs the latest one. You can also type `dsh-plugin-session-notifier` into **Plugins → Add plugin** yourself. Pick the
53
53
  China mainland registry mirror if you are there, it is much faster.
54
54
 
55
55
  **2. Local development:** `plugin_manager({ action: "install_bundle", target: "/absolute/path/dsh-plugin-session-notifier" })`.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # 会话结束提醒 · Session Notifier
1
+ # 会话提醒 · Session Notifier
2
2
 
3
3
  [English](README.en.md) | 中文
4
4
 
@@ -43,10 +43,10 @@ DeepSeek Harness 插件。会话跑完、出错,或者停下来等你回答(
43
43
  **一、按包名安装(推荐)**。让智能体调用:
44
44
 
45
45
  ```
46
- plugin_manager({ action: "install_bundle", target: "dsh-plugin-session-notifier@0.1.0" })
46
+ plugin_manager({ action: "install_bundle", target: "dsh-plugin-session-notifier" })
47
47
  ```
48
48
 
49
- 也可以自己在「插件 → 添加插件」里输入 `dsh-plugin-session-notifier`。
49
+ 不写版本号会装最新版。也可以自己在「插件 → 添加插件」里输入 `dsh-plugin-session-notifier`。
50
50
  安装源可选「中国大陆镜像源」,国内会快很多。
51
51
 
52
52
  **二、本地开发**:`plugin_manager({ action: "install_bundle", target: "/绝对路径/dsh-plugin-session-notifier" })`。
package/client.js CHANGED
@@ -30,11 +30,13 @@ window.__ModuleLoader__.load({
30
30
  const RECENT_LIMIT = 12;
31
31
  /** Wait before reminding about the session the user is looking at. */
32
32
  const VIEWING_MERGE_MS = 1000;
33
+ /** Two reminders landing together share one tone instead of stacking two. */
34
+ const TONE_GAP_MS = 250;
33
35
 
34
36
  //#region copy
35
37
  const zh = {
36
38
  'panel.label': '会话提醒',
37
- 'panel.title': '会话结束提醒',
39
+ 'panel.title': '会话提醒',
38
40
  'panel.desc':
39
41
  '会话跑完或停下来等你回答时,用桌面通知和提示音提醒你。切到别的窗口、全屏看视频或打游戏时也不会错过。',
40
42
  'group.master': '总开关',
@@ -104,8 +106,8 @@ window.__ModuleLoader__.load({
104
106
  };
105
107
 
106
108
  const en = {
107
- 'panel.label': 'Session alerts',
108
- 'panel.title': 'Session end notifier',
109
+ 'panel.label': 'Session Notifier',
110
+ 'panel.title': 'Session Notifier',
109
111
  'panel.desc':
110
112
  'A desktop notification and a short chime when a session finishes or stops to wait for you — so a task that ends while you are on another window is not missed.',
111
113
  'group.master': 'Master switch',
@@ -177,11 +179,11 @@ window.__ModuleLoader__.load({
177
179
 
178
180
  //#region settings
179
181
  /**
180
- * Stored-document version. Version 1 shipped with `whenFocused: false` and a 5 s
181
- * merge window; those two defaults changed, so a version-1 document is migrated
182
- * instead of silently keeping the older values.
182
+ * Stored-document version, written with every save. Nothing reads it yet: the key
183
+ * was introduced with the current defaults, so no older document can exist. It is
184
+ * here so a future default change can migrate instead of guessing.
183
185
  */
184
- const SETTINGS_VERSION = 2;
186
+ const SETTINGS_VERSION = 1;
185
187
 
186
188
  /** Merge-window choices, in milliseconds. */
187
189
  const COMBINE_CHOICES = [10000, 20000, 30000];
@@ -223,12 +225,6 @@ window.__ModuleLoader__.load({
223
225
  for (const key of Object.keys(DEFAULTS)) {
224
226
  if (typeof parsed[key] === typeof DEFAULTS[key]) next[key] = parsed[key];
225
227
  }
226
- const version = typeof parsed.v === 'number' ? parsed.v : 1;
227
- if (version < SETTINGS_VERSION) {
228
- // The two defaults that changed; other stored choices stay as the user left them.
229
- next.whenFocused = DEFAULTS.whenFocused;
230
- next.combineMs = DEFAULTS.combineMs;
231
- }
232
228
  } catch (_storageUnavailable) {
233
229
  // A blocked or corrupt store simply falls back to the defaults.
234
230
  }
@@ -287,8 +283,33 @@ window.__ModuleLoader__.load({
287
283
  /** Newest-first reminder log for the panel. */
288
284
  recent: [],
289
285
  recentListeners: new Set(),
286
+ /** Timers owned by one notification or tone, cleared with the plugin. */
287
+ timers: new Set(),
288
+ /** Wall-clock time of the last tone, so two reminders do not overlap. */
289
+ lastToneAt: 0,
290
290
  };
291
291
 
292
+ /**
293
+ * Run a callback later, keeping its timer id in the owned set.
294
+ * @param callback - work to run once.
295
+ * @param delay - delay in milliseconds.
296
+ * @returns the timer id.
297
+ */
298
+ function startTimer(callback, delay) {
299
+ const id = window.setTimeout(() => {
300
+ state.timers.delete(id);
301
+ callback();
302
+ }, delay);
303
+ state.timers.add(id);
304
+ return id;
305
+ }
306
+
307
+ /** Drop every timer this plugin started. */
308
+ function clearTimers() {
309
+ for (const id of state.timers) window.clearTimeout(id);
310
+ state.timers.clear();
311
+ }
312
+
292
313
  /** Faces assigned when they become available; every consumer tolerates absence. */
293
314
  const services = {
294
315
  sessions: null,
@@ -397,8 +418,13 @@ window.__ModuleLoader__.load({
397
418
  * Play the reminder tone for one kind of event.
398
419
  * @param kind - `done`, `awaiting`, `error`, or `test`.
399
420
  */
400
- function chime(kind) {
421
+ function chime(kind, force) {
401
422
  if (!settings.sound) return;
423
+ const at = Date.now();
424
+ if (force !== true) {
425
+ if (at - state.lastToneAt < TONE_GAP_MS) return;
426
+ state.lastToneAt = at;
427
+ }
402
428
  const context = audioContext();
403
429
  if (context === null) return;
404
430
  if (context.state === 'suspended' && typeof context.resume === 'function') {
@@ -439,7 +465,7 @@ window.__ModuleLoader__.load({
439
465
  oscillator.start(start + offset);
440
466
  oscillator.stop(start + offset + duration + 0.03);
441
467
  }
442
- window.setTimeout(() => {
468
+ startTimer(() => {
443
469
  try {
444
470
  master.disconnect();
445
471
  } catch (_disconnected) {
@@ -525,8 +551,8 @@ window.__ModuleLoader__.load({
525
551
  // Already gone.
526
552
  }
527
553
  };
528
- window.setTimeout(() => settle('unconfirmed'), 2500);
529
- window.setTimeout(() => {
554
+ startTimer(() => settle('unconfirmed'), 2500);
555
+ startTimer(() => {
530
556
  try {
531
557
  notification.close();
532
558
  } catch (_closed) {
@@ -657,7 +683,7 @@ window.__ModuleLoader__.load({
657
683
  */
658
684
  function runTestReminder() {
659
685
  primeAudio();
660
- chime('test');
686
+ chime('test', true);
661
687
  publishProbe({ status: 'pending' });
662
688
  const delivery = notifyDesktop(
663
689
  'test',
@@ -1328,6 +1354,7 @@ window.__ModuleLoader__.load({
1328
1354
  ctx.effect(
1329
1355
  () => () => {
1330
1356
  cancelAll();
1357
+ clearTimers();
1331
1358
  },
1332
1359
  'session-notifier: timers',
1333
1360
  );
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
- "title": "Session End Notifier",
4
- "description": "Raises a desktop notification and a short chime when a session finishes or waits for your answer, so you notice while you are away from the page."
3
+ "title": "Session Notifier",
4
+ "description": "A desktop notification and a short chime when a session finishes or stops to wait for you — so a task that ends while you are on another window is not missed."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
- "title": "会话结束提醒",
4
- "description": "会话跑完或停下来等你回答时,用桌面通知和提示音提醒你,切到别处也不会错过。"
3
+ "title": "会话提醒",
4
+ "description": "会话跑完或停下来等你回答时,用桌面通知和提示音提醒你。切到别的窗口、全屏看视频或打游戏时也不会错过。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-session-notifier",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Desktop notification and a short chime when a DeepSeek Harness session finishes or stops to wait for your answer.",
5
5
  "type": "module",
6
6
  "exports": {