dsh-todo-float-ball 0.8.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.
@@ -0,0 +1,59 @@
1
+ # Data channels — how the ball learns about todos
2
+
3
+ This document records the (read-only) data surfaces this plugin attaches to,
4
+ so future maintainers can re-validate them against newer DSH versions.
5
+
6
+ ## Where the official todo data lives
7
+
8
+ | Layer | Package | Role |
9
+ |---|---|---|
10
+ | Tool | `@deepseek-ai/dsh-tool-todo` | Registers the `todo_write` tool; every accepted call appends a `todo/write` snapshot event to the session log; registers the `todos` unit on `sessionProjections` |
11
+ | Transport | `@deepseek-ai/dsh-client-connection` | Broadcasts projection values as control frames: `{type:"projection", sessionId, key:"todos", value: TodoItem[] \| null, seq}`; recomputes `todos` on `todo/write` and `turn/start` events |
12
+ | View | `@deepseek-ai/dsh-client-ui-conversation` | Renders the `todos` projection as the plan strip above the composer (`section[data-testid="todo-panel"]`), collapsible; expanded it renders `ul > li[data-status]` with a content span; collapsed it renders only a localized counts label |
13
+ | Tool row | `@deepseek-ai/dsh-client-ui-tool` | Renders the one-line `todo_write` conversation row (summary derived from call args) |
14
+
15
+ ## Channel A — DOM (primary)
16
+
17
+ - Selector: `[data-testid="todo-panel"]`
18
+ - Expanded: `panel.querySelectorAll("ul li[data-status]")` →
19
+ `{status: li[data-status], content: last span textContent}`
20
+ - Collapsed: the counts label (class contains `progress`) is parsed.
21
+ Formats seen: `"1 完成 · 2 进行中 · 3 待办"` (zh) and equivalent English
22
+ segments; both Arabic numerals and simple Chinese numerals (一/两/三…十) are
23
+ supported. Count-only data is used solely as a last resort when no
24
+ projection frame has been seen yet, because it cannot recover item contents.
25
+
26
+ ## Channel B — transport (fallback)
27
+
28
+ - `window.fetch` is wrapped once (`window.__dshtfbFetchPatched` guard):
29
+ responses are cloned; binary-ish content types (javascript/font/image) are
30
+ skipped; the text is handed to the extractor.
31
+ - `WebSocket.prototype.onmessage` is wrapped once (`WebSocket.__dshtfbTapped`
32
+ guard): string frames go straight to the extractor, Blob frames are read via
33
+ `.text()` when small enough.
34
+ - The extractor (`tryIngest`) pre-filters on the substring `"todos"`, then
35
+ tries whole-body JSON; on failure it scans line-by-line (tolerating SSE
36
+ `data:` prefixes). An object is accepted only if it matches one of:
37
+ 1. `{key:"todos", value:[...]}` — projection frame
38
+ 2. `{todos:[...]}` — snapshot/log frame
39
+ 3. `{data:{todos:[...]}}` — `todo/write` event frame
40
+ - The wrapper never mutates requests or responses; failures inside the tap are
41
+ swallowed so the host UI is unaffected.
42
+
43
+ ## Normalization
44
+
45
+ Both channels funnel into `setTodos`:
46
+
47
+ - items are filtered to `pending | in_progress | completed`;
48
+ - blank/whitespace contents are dropped;
49
+ - a signature (`status|content` joined) detects no-op updates;
50
+ - rendering is fully re-computed from the normalized list (no incremental DOM
51
+ state).
52
+
53
+ ## Stability notes
54
+
55
+ - The panel `data-testid` is a stable test hook, safer than CSS-module class
56
+ names which re-hash between DSH builds.
57
+ - If a future DSH renames the projection key or frame shape, channel A keeps
58
+ working as long as the panel exists; if both channels break, the ball
59
+ degrades to the idle state instead of showing wrong data.