@nolit/dsh-notify 0.0.1-rc.3 → 0.0.1-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -3
- package/README.md +34 -1
- package/lib/client.js +6 -3
- package/lib/client.src.js +6 -3
- package/market/index.json +1 -1
- package/package.json +1 -1
- package/test/manifest.test.mjs +4 -4
- package/test/peak.test.mjs +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,7 +11,7 @@ summary: Engineering history for Notify for Chrome (formerly DSH Notify Web). Se
|
|
|
11
11
|
> History lives here (owner instruction 2026-11-04). Boot cards and `AGENTS.md` are narrowed to
|
|
12
12
|
> the owner's unfinished asks; they are not a changelog. See `docs/RULES.md` §6.1 in the `dsh-plugins` workspace.
|
|
13
13
|
|
|
14
|
-
Current version: `0.0.1-rc.
|
|
14
|
+
Current version: `0.0.1-rc.4`
|
|
15
15
|
|
|
16
16
|
## Seeded history (from git, before this file existed)
|
|
17
17
|
|
|
@@ -97,6 +97,32 @@ Not changed, and why: `docs/` still contains absolute paths (`/home/minami` ×2,
|
|
|
97
97
|
of the defects being reported, so removing them would destroy the record. Runtime
|
|
98
98
|
code (`lib/`, `scripts/`, `locale/`, `market/`) has **zero** absolute paths.
|
|
99
99
|
|
|
100
|
+
### 2026-11-07 — `0.0.1-rc.4` — the chip actually renders
|
|
101
|
+
|
|
102
|
+
Owner decision (translated): *"The rename back to the original name is settled, and so is the
|
|
103
|
+
version. Stop treating the older versions as live: the current state is canonical and the path
|
|
104
|
+
changed. The only part you may change is the number after `rc`, so make it `rc.4` and publish
|
|
105
|
+
through the deploy harness."*
|
|
106
|
+
|
|
107
|
+
`0.0.1-rc.3` was published to npm and GitHub with a defect that made the peak chip dead. This
|
|
108
|
+
release is the corrected build.
|
|
109
|
+
|
|
110
|
+
| Surface | `0.0.1-rc.3` (published) | `0.0.1-rc.4` |
|
|
111
|
+
| :--- | :--- | :--- |
|
|
112
|
+
| Peak chip | slot registered, **never painted** (`ReferenceError: doc is not defined` on every render) | renders |
|
|
113
|
+
| Engine id | `0.0.1-rc.3` | `0.0.1-rc.4` |
|
|
114
|
+
|
|
115
|
+
**Root cause.** `makePeakChip` is a UI adapter outside `createNotifyCore`, so the core's `doc`
|
|
116
|
+
binding was out of scope. Registration succeeded, which is why the failure was invisible: the slot
|
|
117
|
+
simply rendered nothing. Fixed by resolving the document from the browser global inside the
|
|
118
|
+
component. Full detail is in the 2026-11-07 entry below.
|
|
119
|
+
|
|
120
|
+
**Verification.** `npm test` 70/70, including a new regression test that renders the registered
|
|
121
|
+
component and asserts it does not throw. `0.0.1-rc.3` is superseded; it is not a valid install
|
|
122
|
+
target.
|
|
123
|
+
|
|
124
|
+
Owner-stated version form: **`0.0.1.rc4`**.
|
|
125
|
+
|
|
100
126
|
### 2026-11-06 — `0.0.1-rc.3` — 7-segment peak countdown chip
|
|
101
127
|
|
|
102
128
|
Owner instruction (translated): *"If the window cannot be force-promoted, strengthen the in-app UI.
|
|
@@ -140,7 +166,7 @@ Owner decisions in the same turn:
|
|
|
140
166
|
| Destination slot | none | `conversation.session.header.utilities` (list) | `cordis_inspect_query` live occupancy |
|
|
141
167
|
| Settings keys | 8 | 11 | `DEFAULT_SETTINGS`, `cordis.patch.yml` |
|
|
142
168
|
| Locale keys | 74 per language | 99 per language | `locale/en.json`, `locale/ko.json` |
|
|
143
|
-
| Tests | 51 |
|
|
169
|
+
| Tests | 51 | 70 | `npm test` |
|
|
144
170
|
| Version | `0.0.1-rc.2` | `0.0.1-rc.3` | `package.json`, `market/index.json` |
|
|
145
171
|
|
|
146
172
|
#### Bug fix — route gate (owner report, same session)
|
|
@@ -296,9 +322,40 @@ Chrome would be worse than a documented recommendation.
|
|
|
296
322
|
| privacy list omitted the holiday cache | now lists all three `localStorage` keys |
|
|
297
323
|
| limitations implied notifications degrade transparently | now names the silent-drop cases |
|
|
298
324
|
|
|
325
|
+
#### Bug fix — the chip never rendered (`ReferenceError: doc is not defined`)
|
|
326
|
+
|
|
327
|
+
Owner report: *"why is your plugin not working?"* Measured after the DeepSeek sign-in was repaired.
|
|
328
|
+
|
|
329
|
+
The plugin was mounted correctly (`--dump-config` shows the `notify` row with every setting), the
|
|
330
|
+
build was current, and the slot registered. The chip still never appeared.
|
|
331
|
+
|
|
332
|
+
**Cause.** `makePeakChip` is a UI adapter that lives *outside* `createNotifyCore`. The `doc`
|
|
333
|
+
binding it referenced belongs to the core closure (line 456), so it was out of scope in the
|
|
334
|
+
component. Every render threw:
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
ReferenceError: doc is not defined
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Registration succeeded, so nothing looked broken from the outside — the slot simply painted
|
|
341
|
+
nothing. **Registering is not rendering**, and the post-rc.3 rename (`@nolit/dsh-notify-web` ->
|
|
342
|
+
`@nolit/dsh-notify`, commit `eabc41e`) carried the defect forward unchanged.
|
|
343
|
+
|
|
344
|
+
**Fix.** The component resolves the document from the browser global itself
|
|
345
|
+
(`typeof document === 'undefined' ? undefined : document`), which is correct for a UI adapter, and
|
|
346
|
+
passes it to `readActiveProvider` / `watchActiveProvider`. The three core-side call sites
|
|
347
|
+
(`peakAnnounce`, `syncBadge`) were already inside the closure and are unchanged.
|
|
348
|
+
|
|
349
|
+
**Regression test added** so this cannot recur silently: it runs `apply()`, takes the component out
|
|
350
|
+
of the registered slot, calls it, and asserts it does not throw. The previous suite asserted *which
|
|
351
|
+
slot registered*, never that the component survives a render — which is exactly why the bug passed
|
|
352
|
+
69 tests.
|
|
353
|
+
|
|
354
|
+
`npm test` -> **70/70 pass**.
|
|
355
|
+
|
|
299
356
|
#### Verification
|
|
300
357
|
|
|
301
|
-
`npm test` → **
|
|
358
|
+
`npm test` → **70/70 pass**. The new suite re-runs the published spec §5 table and asserts case #7
|
|
302
359
|
now expects `false` (makeup workday off-peak), plus the fixed UTC windows, the Beijing-day boundary,
|
|
303
360
|
malformed-payload rejection, the 8-day next-change scan, `HH:MM:SS` clamping, and that the rendered
|
|
304
361
|
SVG carries token colours only.
|
package/README.md
CHANGED
|
@@ -8,13 +8,39 @@ updated: 2026-11-05
|
|
|
8
8
|
summary: Operator manual and behavior contract for Notify for Chrome (Korean: 알림 for 크롬) — Chrome/Web Notification only, silent in-app toast fallback, native DSH settings rows and typography, Windows/install-mode notification branches.
|
|
9
9
|
-->
|
|
10
10
|
|
|
11
|
-
> Status: active · Updated: 2026-11-
|
|
11
|
+
> Status: active · Updated: 2026-11-07 · Package: `@nolit/dsh-notify` v`0.0.1-rc.4` · Korean display name: **알림 for 크롬** · Parent: `README.md` in the `dsh-plugins` workspace
|
|
12
12
|
|
|
13
13
|
**Notify for Chrome** turns DeepSeek Harness events into **Chrome notifications** (the Web
|
|
14
14
|
Notification API, `new Notification(...)`). It has no native app, no ports, no IPC, no file queue
|
|
15
15
|
and no system notifier. When Chrome cannot show a notification, the plugin falls back to a
|
|
16
16
|
**silent in-app toast**.
|
|
17
17
|
|
|
18
|
+
## 0. Version numbering — why `0.0.1-rc.4` is not lower than `0.2.1`
|
|
19
|
+
|
|
20
|
+
The version number went **down** against this project's own history, on purpose. Two separate
|
|
21
|
+
facts explain it, and until 2026-11-06 neither was written down anywhere.
|
|
22
|
+
|
|
23
|
+
**`0.2.x` was never this plugin's version.** The `v0.2.0` and `v0.2.1` releases belonged to the
|
|
24
|
+
**Windows PowerShell/WPF prototype** (a tray widget polling the DeepSeek billing window). The
|
|
25
|
+
browser plugin lived inside that repository under `plugin/` and happened to carry the same
|
|
26
|
+
number — by accident, not by design.
|
|
27
|
+
|
|
28
|
+
**The `@nolit` ladder was reset by owner decision on 2026-11-05.** Every `@nolit` package moved
|
|
29
|
+
to one scheme, `0.0.1-rc.N`, in which only the `rc#` moves. `0.1.8` and `0.2.1` are not
|
|
30
|
+
milestones under that scheme; the scheme replaced them.
|
|
31
|
+
|
|
32
|
+
| Surface | Before | After |
|
|
33
|
+
| :--- | :--- | :--- |
|
|
34
|
+
| Version scheme | per-track, drifting (`0.1.6 → 0.1.7 → 0.1.8 → 0.2.1`) | **`0.0.1-rc.N`** |
|
|
35
|
+
| Package | `@nolit/dsh-notify-web` @ `0.2.1`, nested in `plugin/` | **`@nolit/dsh-notify`** @ `0.0.1-rc.4`, repository root |
|
|
36
|
+
| Repository | Windows prototype at root | plugin at root, prototype preserved in `prototype-windows/` |
|
|
37
|
+
|
|
38
|
+
The `-web` suffix came from the 2026-10-24 recovery and was never the owner's choice;
|
|
39
|
+
`PROVENANCE.md` documented the package as `@nolit/dsh-notify` the entire time while the files
|
|
40
|
+
said `-web`. The rename landed in `0.0.1-rc.3`; `0.0.1-rc.4` is the corrected build.
|
|
41
|
+
|
|
42
|
+
Version numbers are the owner's decision. An agent must not choose, bump or "stabilise" one.
|
|
43
|
+
|
|
18
44
|
## 1. Behavior
|
|
19
45
|
|
|
20
46
|
| DSH event | Channel |
|
|
@@ -284,8 +310,15 @@ typography token contract, and the absence of every removed legacy path.
|
|
|
284
310
|
|
|
285
311
|
---
|
|
286
312
|
|
|
313
|
+
|
|
287
314
|
## 한국어 요약
|
|
288
315
|
|
|
316
|
+
**버전이 내려간 이유부터**: `0.0.1-rc.4`는 `0.2.1`보다 낮은 것이 아닙니다. `0.2.x`는 이
|
|
317
|
+
플러그인의 버전이 아니라 같은 저장소에 있던 **Windows 프로토타입**의 버전이었고, 2026-11-05
|
|
318
|
+
소유자 결정으로 모든 `@nolit` 패키지가 **`0.0.1-rc.N`** 단일 사다리로 재설정됐습니다.
|
|
319
|
+
`rc#`만 올라갑니다. 패키지명도 `@nolit/dsh-notify-web` → **`@nolit/dsh-notify`** 로
|
|
320
|
+
바뀌었습니다. `-web`은 2026-10-24 복구 당시 붙은 것으로 소유자가 고른 이름이 아니었습니다.
|
|
321
|
+
|
|
289
322
|
**알림 for 크롬**은 DeepSeek Harness의 턴 완료·도구 승인·질문·오류를 **크롬 알림**(Web
|
|
290
323
|
Notification API)으로 알립니다. 크롬이 알림을 막거나 표시할 수 없으면 **소리 없이 DSH 인앱
|
|
291
324
|
토스트**로 대신 알립니다. 자체 오디오 엔진·BYO 오디오·자체 CSS·네이티브 앱/포트/파일 경로는
|
package/lib/client.js
CHANGED
|
@@ -1899,13 +1899,16 @@ window.__ModuleLoader__.load({
|
|
|
1899
1899
|
// Route gate: the peak/off-peak clock only describes DeepSeek's own
|
|
1900
1900
|
// billing. Read at mount (the composer may already carry the attribute)
|
|
1901
1901
|
// and follow every selection change.
|
|
1902
|
+
// This component is a UI adapter outside createNotifyCore, so the core's
|
|
1903
|
+
// `doc` binding is not in scope here. Read the browser document directly.
|
|
1904
|
+
const docRef = typeof document === 'undefined' ? undefined : document
|
|
1902
1905
|
const [activeProvider, setActiveProvider] = React.useState(() =>
|
|
1903
|
-
readActiveProvider(
|
|
1906
|
+
readActiveProvider(docRef),
|
|
1904
1907
|
)
|
|
1905
1908
|
React.useEffect(() => {
|
|
1906
|
-
const read = () => setActiveProvider(readActiveProvider(
|
|
1909
|
+
const read = () => setActiveProvider(readActiveProvider(docRef))
|
|
1907
1910
|
read()
|
|
1908
|
-
return watchActiveProvider(
|
|
1911
|
+
return watchActiveProvider(docRef, read)
|
|
1909
1912
|
}, [])
|
|
1910
1913
|
|
|
1911
1914
|
if (!settings.showPeakChip) return null
|
package/lib/client.src.js
CHANGED
|
@@ -1690,13 +1690,16 @@ window.__ModuleLoader__.load({
|
|
|
1690
1690
|
// Route gate: the peak/off-peak clock only describes DeepSeek's own
|
|
1691
1691
|
// billing. Read at mount (the composer may already carry the attribute)
|
|
1692
1692
|
// and follow every selection change.
|
|
1693
|
+
// This component is a UI adapter outside createNotifyCore, so the core's
|
|
1694
|
+
// `doc` binding is not in scope here. Read the browser document directly.
|
|
1695
|
+
const docRef = typeof document === 'undefined' ? undefined : document
|
|
1693
1696
|
const [activeProvider, setActiveProvider] = React.useState(() =>
|
|
1694
|
-
readActiveProvider(
|
|
1697
|
+
readActiveProvider(docRef),
|
|
1695
1698
|
)
|
|
1696
1699
|
React.useEffect(() => {
|
|
1697
|
-
const read = () => setActiveProvider(readActiveProvider(
|
|
1700
|
+
const read = () => setActiveProvider(readActiveProvider(docRef))
|
|
1698
1701
|
read()
|
|
1699
|
-
return watchActiveProvider(
|
|
1702
|
+
return watchActiveProvider(docRef, read)
|
|
1700
1703
|
}, [])
|
|
1701
1704
|
|
|
1702
1705
|
if (!settings.showPeakChip) return null
|
package/market/index.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nolit/dsh-notify",
|
|
3
|
-
"version": "0.0.1-rc.
|
|
3
|
+
"version": "0.0.1-rc.4",
|
|
4
4
|
"description": "Notify for Chrome: Chrome notification alerts for DeepSeek Harness turns, approvals, questions and errors, with a silent in-app toast fallback.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "nolit",
|
package/test/manifest.test.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Owner decisions recorded here:
|
|
5
5
|
* - the npm package name stays @nolit/dsh-notify (renaming it would break
|
|
6
6
|
* the live profile's `link:` dependency); only the display identity changed;
|
|
7
|
-
* - the owner's version form 0.0.1.
|
|
7
|
+
* - the owner's version form 0.0.1.rc4 maps to the semver form 0.0.1-rc.4;
|
|
8
8
|
* - DSH 0.2.2-rc.1 installs Cordis 4.1.0 and requires Node >= 24.9.
|
|
9
9
|
*/
|
|
10
10
|
import assert from 'node:assert/strict'
|
|
@@ -26,11 +26,11 @@ test('package name stays @nolit/dsh-notify; display identity is Notify for Chrom
|
|
|
26
26
|
assert.match(pkg.description, /Notify for Chrome/)
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
-
test('version is the owner-declared 0.0.1-rc.
|
|
30
|
-
assert.equal(pkg.version, '0.0.1-rc.
|
|
29
|
+
test('version is the owner-declared 0.0.1-rc.4 (owner form 0.0.1.rc4)', () => {
|
|
30
|
+
assert.equal(pkg.version, '0.0.1-rc.4')
|
|
31
31
|
assert.match(pkg.version, /^\d+\.\d+\.\d+-rc\.\d+$/)
|
|
32
32
|
assert.equal(market.plugins[0].versions[0].version, pkg.version)
|
|
33
|
-
assert.ok(read('CHANGELOG.md').includes('0.0.1.
|
|
33
|
+
assert.ok(read('CHANGELOG.md').includes('0.0.1.rc4'), 'CHANGELOG must record the owner-stated form')
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
test('cordis peer range admits the installed Cordis 4.1.0', () => {
|
package/test/peak.test.mjs
CHANGED
|
@@ -331,3 +331,40 @@ test('the setting defaults off in the shipped defaults and the bundle patch', ()
|
|
|
331
331
|
const patch = readFileSync(new URL('../cordis.patch.yml', import.meta.url), 'utf8')
|
|
332
332
|
assert.match(patch, /^\s+peakOnAnyProvider: false$/m)
|
|
333
333
|
})
|
|
334
|
+
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// Regression: the chip must actually RENDER, not just register.
|
|
337
|
+
//
|
|
338
|
+
// The chip is a UI adapter outside createNotifyCore, so the core's `doc` binding
|
|
339
|
+
// is not in its scope. An earlier revision referenced it anyway, which threw
|
|
340
|
+
// `ReferenceError: doc is not defined` on every render and made the slot appear
|
|
341
|
+
// registered while nothing ever painted. Registering is not rendering.
|
|
342
|
+
// ---------------------------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
test('the registered chip component renders without a ReferenceError', () => {
|
|
345
|
+
const slots = {}
|
|
346
|
+
const ctx = {
|
|
347
|
+
locale: { getSnapshot: () => ({ active: 'ko' }), subscribe: () => () => {} },
|
|
348
|
+
logger: { warn() {}, log() {} },
|
|
349
|
+
remote: { $on: () => () => {} },
|
|
350
|
+
sessions: { list: { getSnapshot: () => ({ byId: {} }) }, scopeOf: () => undefined },
|
|
351
|
+
slots: {
|
|
352
|
+
inject: (key, cb) => {
|
|
353
|
+
const d = cb()
|
|
354
|
+
return typeof d === 'function' ? d : () => {}
|
|
355
|
+
},
|
|
356
|
+
register: (meta, component) => {
|
|
357
|
+
slots[meta.name] = component
|
|
358
|
+
return () => {}
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
effect: (fn) => {
|
|
362
|
+
const d = fn()
|
|
363
|
+
return typeof d === 'function' ? d : () => {}
|
|
364
|
+
},
|
|
365
|
+
}
|
|
366
|
+
mod.apply(ctx, {})
|
|
367
|
+
const Chip = slots['conversation.session.header.utilities']
|
|
368
|
+
assert.ok(Chip, 'the chip must register into the header utilities slot')
|
|
369
|
+
assert.doesNotThrow(() => Chip({}), 'rendering the chip must not throw')
|
|
370
|
+
})
|