dsh-tab-status-light 1.0.0 → 1.1.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.
Files changed (2) hide show
  1. package/README.md +182 -58
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,96 +1,220 @@
1
1
  # dsh-tab-status-light
2
2
 
3
- **把 AI 的狀態畫在瀏覽器分頁圖示上** —— 一個給 DeepSeek Harness Web UI 用的外掛。
3
+ **Put the AI session state on your browser tab icon** — a plugin for the [DeepSeek Harness](https://github.com/deepseek-ai) Web UI.
4
+
5
+ [English](#english) · [繁體中文](#繁體中文)
6
+
7
+ ![Tab icon states](preview.png)
8
+
9
+ ---
10
+
11
+ ## English
12
+
13
+ While a turn runs, the tab icon blinks gray; when the turn finishes it turns green;
14
+ while the session waits for you it turns yellow; when a turn is interrupted it turns red.
15
+ You can tell the state of the agent at a glance — even from another tab — without
16
+ switching back to the app.
17
+
18
+ | State | Color | Animation | Derived from |
19
+ | --- | --- | --- | --- |
20
+ | Working | gray `#9ca3af` | blinks every 600 ms: **the plain whale** ↔ whale + gray dot (the off half has no dot at all) | `SessionStatus.running === true` |
21
+ | Done | green `#22c55e` | lit for 10 s, then back to the plain whale | a turn **seen running here** whose last `turn/end` reason is not an interruption |
22
+ | Waiting for you | yellow `#eab308` | stays lit until you answer | `SessionStatus.pendingInteraction !== undefined` (question / approval / plan review) |
23
+ | Interrupted | red `#ef4444` | lit for 20 s, then back to the plain whale | a turn **seen running here** whose last `turn/end` reason is `aborted` / `blocked` / `error` / `interrupted` |
24
+
25
+ The shipped whale is left untouched when no session is selected, when the session has
26
+ not finished a turn yet, or when you merely **switch to a session that finished long ago**
27
+ — green/red are news about something that just happened, not a replay of history.
28
+ `SETTLE_MS` (green, 10 s) and `SETTLE_MS_RED` (red, 20 s) control how long a settled
29
+ badge stays; any new finished turn lights it again.
30
+
31
+ ### Install
32
+
33
+ The plugin ships as a DSH bundle (`dsh.bundle.patch`). Install it with the harness
34
+ `plugin_manager`:
35
+
36
+ ```js
37
+ plugin_manager({ action: "install_bundle", target: "dsh-tab-status-light" })
38
+ ```
39
+
40
+ Or from the CLI:
41
+
42
+ ```sh
43
+ dsh plugin --profile web add dsh-tab-status-light
44
+ # straight from GitHub instead of npm
45
+ dsh plugin --profile web add github:kyle2241345145351324321/dsh-tab-status-light
46
+ ```
47
+
48
+ Or point at a checkout (handy while developing — edits apply on page reload):
49
+
50
+ ```js
51
+ plugin_manager({ action: "install_bundle", target: "/path/to/dsh-tab-status-light" })
52
+ ```
53
+
54
+ Reload the page (`Cmd+R` / `Ctrl+R`) after installing.
55
+
56
+ > Requires the harness Web profile with the `@deepseek-ai/dsh-client-ui-session` and
57
+ > `@deepseek-ai/dsh-api-session-controller` client packages available (a standard
58
+ > install has both).
59
+
60
+ ### How it works
61
+
62
+ - **Pure Client plugin, occupies no slot**: it reads `ctx.sessions.list` (which session
63
+ is the main view) and `ctx.uiSession.sessionStatus` (running / pending interaction),
64
+ and follows the session's event source for the newest `turn/end` reason.
65
+ - The icon is **redrawn from the live favicon**: it `fetch()`es the shipped
66
+ `favicon.svg` / `favicon-dark.svg`, splices one `<circle>` in before `</svg>`, and
67
+ swaps the `href` of `<link rel="icon">` to a data URL (Chrome repaints the tab icon).
68
+ The original `href` is restored on teardown.
69
+ - Because of that, the whale artwork is never copied into this plugin — upstream logo
70
+ changes carry over automatically.
71
+ - Badge geometry inside the shipped 50×50 viewBox: `cx=37, cy=37, r=11, stroke-width=4`;
72
+ the ring uses the tab background (dark `#35363a`, light `#ffffff`) to separate the dot
73
+ from the mark.
74
+ - Blinking is driven by a tiny Web Worker, so it **keeps blinking while the tab is in
75
+ the background** (Chrome throttles page timers in hidden tabs). It falls back to a
76
+ page timer where workers are unavailable.
77
+ - Both blink halves are precomputed data URLs, so every swap paints on the tick — no
78
+ file re-read, which is what made the pulse look uneven in earlier versions.
79
+
80
+ ### Tuning
81
+
82
+ All at the top of [`client.js`](client.js):
83
+
84
+ | Constant | Default | Meaning |
85
+ | --- | --- | --- |
86
+ | `BADGE` | `{cx:37, cy:37, radius:11, ring:4}` | dot position / radius / ring width |
87
+ | `BADGE_COLOR` | gray / green / yellow / red | state colors |
88
+ | `BLINK_MS` | `600` | blink period while working |
89
+ | `SETTLE_MS` | `10000` | how long the green badge stays |
90
+ | `SETTLE_MS_RED` | `20000` | how long the red badge stays |
91
+ | `RED_TURN_END_KINDS` | `aborted` `blocked` `error` `interrupted` | turn endings that count as interruptions |
92
+
93
+ ### Resource use
94
+
95
+ Blinking costs about **2 ms of JS per second** (≈0.2 % of one core); when idle it costs
96
+ nothing — no timers, no rescans.
97
+
98
+ ### Known limits
99
+
100
+ - The icon reflects the **main view** session only; background sessions are not shown.
101
+ - Switching to a session that finished earlier does not flash green (only a turn watched
102
+ running lights up).
103
+ - Blinking continues in a background tab, but a **frozen** tab (Chrome Memory Saver /
104
+ battery saver) stops all page JS — no web API can bypass that. To keep it alive, add the
105
+ origin to `chrome://settings/performance` → "Always keep these sites active".
106
+ `navigator.wakeLock` does **not** help: it only keeps the screen awake and is released
107
+ as soon as the page is hidden.
108
+ - When a frozen or backgrounded tab wakes up, the plugin resyncs and repaints immediately,
109
+ so the icon is always correct when you look at it.
110
+ - It swaps SVG data URLs into `<link rel="icon">`; very old browsers without SVG favicon
111
+ support will show no change.
112
+
113
+ ### Development
4
114
 
5
- > Puts the AI session state on the browser tab icon: gray blinking while it works,
6
- > green when it finishes, yellow while it waits for you, red when it is interrupted.
115
+ ```js
116
+ // this repo *is* the installable bundle — there is no build step
117
+ plugin_manager({ action: "install_bundle", target: "/absolute/path/to/dsh-tab-status-light" })
118
+ ```
119
+
120
+ Edit `client.js`, then reload the page: the Client module is served from disk.
121
+
122
+ ### License
123
+
124
+ [MIT](LICENSE)
7
125
 
8
- ![分頁圖示狀態](preview.png)
126
+ ---
127
+
128
+ ## 繁體中文
129
+
130
+ **把 AI 的狀態畫在瀏覽器分頁圖示上** —— 一個給 DeepSeek Harness Web UI 用的外掛。
131
+
132
+ AI 在做事時分頁圖示會**灰色一閃一閃**;這一輪完成變**綠**;在等你回答(提問/核准/計畫審查)
133
+ 時變**黃**;這一輪被打斷變成**紅**。切到別的分頁也看得到現在是什麼狀態,不用切回來確認。
9
134
 
10
135
  | 狀態 | 顏色 | 動畫 | 判定依據 |
11
136
  | --- | --- | --- | --- |
12
- | 進行中 | 灰 `#9ca3af` | 一閃一閃(每 600 ms 切換):**原本的鯨魚** ↔ 鯨魚+灰點,暗的那半完全沒有圓點 | `SessionStatus.running === true` |
13
- | 完成 | 綠 `#22c55e` | 亮 10 秒後自動回到原本的鯨魚 | 這一輪**被看到在跑**,且最後一個 `turn/end` reason 不是中斷類 |
14
- | 等你回答 | 黃 `#eab308` | 一直亮著(因為在等你) | `SessionStatus.pendingInteraction !== undefined`(提問 / 核准 / 計畫審查) |
15
- | 中斷 | 紅 `#ef4444` | 亮 20 秒後自動回到原本的鯨魚 | 這一輪**被看到在跑**,且最後一個 `turn/end` reason 是 `aborted` / `blocked` / `error` / `interrupted` |
16
-
17
- 沒選 session(首頁)、該 session 還沒有任何一輪結束、或只是**切換到一個早就結束的 session** 時,
18
- **不動**原本的鯨魚圖示 —— 綠/紅是「剛剛發生的事」的通知,不會從歷史重播。
19
- 「一小段時間沒事就收起來」的行為由 `SETTLE_MS`(綠 10 秒)與 `SETTLE_MS_RED`(紅 20 秒)控制;
137
+ | 進行中 | 灰 `#9ca3af` | 每 600 ms 切換:**純鯨魚** ↔ 鯨魚+灰點(暗的那半完全沒有圓點) | `SessionStatus.running === true` |
138
+ | 完成 | 綠 `#22c55e` | 亮 10 秒後回到純鯨魚 | 這一輪**被看到在跑**,且最後一個 `turn/end` 原因不是中斷類 |
139
+ | 等你回答 | 黃 `#eab308` | 一直亮著直到你回答 | `SessionStatus.pendingInteraction !== undefined`(提問/核准/計畫審查) |
140
+ | 中斷 | 紅 `#ef4444` | 亮 20 秒後回到純鯨魚 | 這一輪**被看到在跑**,且最後一個 `turn/end` 原因是 `aborted`/`blocked`/`error`/`interrupted` |
141
+
142
+ 沒選 session、該 session 還沒跑完任何一輪、或只是**切到一個早就結束的 session** 時,
143
+ 不會動原本的鯨魚圖示 —— 綠/紅是「剛剛發生的事」的通知,不會把歷史重播一次。
144
+ 綠燈停留由 `SETTLE_MS`(10 秒)控制、紅燈由 `SETTLE_MS_RED`(20 秒)控制,
20
145
  任何新的一輪結束都會讓燈重新亮起。
21
146
 
22
- ## 安裝
147
+ ### 安裝
23
148
 
24
- 外掛以 DSH bundle 的形式發佈(`dsh.bundle.patch`),用 harness 的 `plugin_manager` 安裝:
149
+ 外掛以 DSH bundle 形式發佈(`dsh.bundle.patch`),用 harness 的 `plugin_manager` 安裝:
25
150
 
26
151
  ```js
27
152
  plugin_manager({ action: "install_bundle", target: "dsh-tab-status-light" })
28
153
  ```
29
154
 
30
- 或直接指到這個資料夾(開發時這樣最快,改檔後重新整理即生效):
155
+ 或用 CLI:
156
+
157
+ ```sh
158
+ dsh plugin --profile web add dsh-tab-status-light
159
+ # 也可以直接從 GitHub 安裝(不走 npm)
160
+ dsh plugin --profile web add github:kyle2241345145351324321/dsh-tab-status-light
161
+ ```
162
+
163
+ 開發時直接指到資料夾(改檔後重新整理就生效):
31
164
 
32
165
  ```js
33
166
  plugin_manager({ action: "install_bundle", target: "/path/to/dsh-tab-status-light" })
34
167
  ```
35
168
 
36
- 安裝後重新整理頁面(`Cmd+R`)即可看到分頁圖示變化。
169
+ 安裝後重新整理頁面(`Cmd+R`/`Ctrl+R`)。
37
170
 
38
- > 需要 harness 的 Web profile,且 `@deepseek-ai/dsh-client-ui-session` 與
39
- > `@deepseek-ai/dsh-api-session-controller` 兩個 Client 套件可用(標準安裝即有)。
171
+ > 需要 harness 的 Web profile,且具備 `@deepseek-ai/dsh-client-ui-session` 與
172
+ > `@deepseek-ai/dsh-api-session-controller` 這兩個 Client 套件(標準安裝即有)。
40
173
 
41
- ## 運作方式
174
+ ### 運作方式
42
175
 
43
176
  - **純 Client 外掛、不佔用任何 slot**:讀 `ctx.sessions.list`(哪個 session 是主視窗)與
44
- `ctx.uiSession.sessionStatus`(running / pendingInteraction),並訂閱該 session 的
45
- event source 取得最新的 `turn/end` reason。
46
- - 圖示是**即時重繪**的:用 `fetch()` 讀取原本的 `favicon.svg` / `favicon-dark.svg`,
47
- 在 `</svg>` 前插入一顆 `<circle>`,轉成 data URL 後換掉 `<link rel="icon">` 的 `href`
48
- (Chrome 會跟著重畫分頁圖示)。unmount 時還原原始 href。
49
- - 因此鯨魚圖案不會被複製一份到外掛裡,上游之後換 logo 也會自動跟上。
50
- - 圓點幾何(在 50×50 viewBox 內):`cx=37, cy=37, r=11, stroke-width=4`;
51
- 外圈用分頁底色(深色主題 `#35363a`、淺色主題 `#ffffff`)把圓點跟鯨魚隔開。
52
- - 閃爍由一個小型 Web Worker 計時驅動,**分頁切到背景時照樣閃**(繞開 Chrome 對背景
53
- 分頁 `setInterval` 的節流);不支援 Worker 時自動退回頁面計時器。
54
-
55
- ## 可調參數
56
-
57
- 都在 [`client.js`](client.js) 檔頭:
58
-
59
- | 常數 | 預設 | 說明 |
60
- | --- | --- | --- |
61
- | `BADGE` | `{cx:37, cy:37, radius:11, ring:4}` | 圓點位置 / 半徑 / 外圈寬 |
62
- | `BADGE_COLOR` | 灰 / 綠 / 黃 / 紅 | 四種狀態的顏色 |
63
- | `BLINK_MS` | `600` | 灰燈閃爍間隔(每 600 ms 切換一次) |
64
- | `SETTLE_MS` | `10000` | 綠燈亮多久後收回 |
65
- | `SETTLE_MS_RED` | `20000` | 紅燈亮多久後收回 |
66
- | `RED_TURN_END_KINDS` | `aborted` `blocked` `error` `interrupted` | 哪些結束原因算中斷 |
177
+ `ctx.uiSession.sessionStatus`(running/pendingInteraction),並訂閱該 session 的
178
+ event source 取得最新的 `turn/end` 原因。
179
+ - 圖示是**即時重繪**的:`fetch()` 原本的 `favicon.svg`/`favicon-dark.svg`,在 `</svg>`
180
+ 前插入一顆 `<circle>`,把 `<link rel="icon">` 的 `href` 換成 data URL(Chrome 會跟著
181
+ 重畫分頁圖示),unmount 時還原原始 `href`。
182
+ - 因此鯨魚圖案不會被複製一份到外掛裡,上游換 logo 也會自動跟上。
183
+ - 圓點幾何(50×50 viewBox 內):`cx=37, cy=37, r=11, stroke-width=4`;
184
+ 外圈用分頁底色(深色 `#35363a`、淺色 `#ffffff`)把圓點跟鯨魚隔開。
185
+ - 閃爍由一個小型 Web Worker 計時,**分頁切到背景仍會閃**(Chrome 會節流隱藏分頁的
186
+ 頁面計時器);不支援 Worker 時自動退回頁面計時器。
187
+ - 閃爍的兩幀都是預先算好的 data URL,所以每一次切換都在節拍上完成 —— 早期版本會回去
188
+ 讀原始圖檔,那一幀就會慢半拍,看起來忽長忽短。
189
+
190
+ ### 可調參數
191
+
192
+ 都在 [`client.js`](client.js) 檔頭(`BADGE`、`BADGE_COLOR`、`BLINK_MS`、`SETTLE_MS`、
193
+ `SETTLE_MS_RED`、`RED_TURN_END_KINDS`),說明同上表。
67
194
 
68
- ## 資源用量
195
+ ### 資源用量
69
196
 
70
- 閃爍中的 JS 成本約 **2 ms/秒**(單核約 0.2%);閒置時 0(沒有計時器、沒有重新掃描)。
197
+ 閃爍中約 **2 ms JS/秒**(單核約 0.2%);閒置時 0(沒有計時器、沒有重新掃描)。
71
198
 
72
- ## 已知限制
199
+ ### 已知限制
73
200
 
74
- - 圖示由「主視窗」那個 session 決定;其他背景 session 的狀態不會顯示。
75
- - 切換到一個「早就跑完」的 session 不會亮綠燈(只亮真的看到跑完的那一輪)。
76
- - 分頁在背景時仍會閃爍;被瀏覽器**凍結**(Chrome Memory Saver / 省電模式)時則一定會停,
77
- 任何網頁都沒有繞過凍結的 API。想讓它一直閃,把這個網址加進
78
- `chrome://settings/performance` →「永不讓這些網站進入休眠 / Always keep these sites active」。
79
- - `navigator.wakeLock` 幫不上忙:它只防止螢幕休眠,且分頁一隱藏就會被釋放,
80
- 不能阻止分頁被凍結。唯一能「不被凍結」的偏方是讓分頁持續播放靜音音訊(需要一次點擊、
81
- 會出現音訊指示、也比較耗電),不值得。
82
- - 分頁解凍 / 切回前景時,外掛會立刻重新同步並重畫,所以回到分頁看到的永遠是正確狀態。
83
- - 用純 SVG data URL 換 href,不支援 SVG favicon 的瀏覽器(很舊的)會看不到變化。
201
+ - 圖示只反映**主視窗**那個 session;其他背景 session 不會顯示。
202
+ - 切換到「早就跑完」的 session 不會亮綠燈(只亮真的看到跑完的那一輪)。
203
+ - 分頁在背景仍會閃;但被**凍結**(Chrome Memory Saver/省電模式)時任何網頁的 JS 都會停,
204
+ 沒有 API 可以繞過。想讓它一直閃,把網址加進 `chrome://settings/performance` →
205
+ 「永遠讓這些網站保持運作」。`navigator.wakeLock` **沒有用**:它只防螢幕休眠,分頁一隱藏就被釋放。
206
+ - 分頁解凍/切回前景時會立刻重新同步並重畫,所以看到的時候一定是正確狀態。
207
+ - 用 SVG data URL 換 `href`,不支援 SVG favicon 的舊瀏覽器看不到變化。
84
208
 
85
- ## 開發
209
+ ### 開發
86
210
 
87
211
  ```js
88
- // 這個 repo 就是可直接安裝的 bundle 資料夾,沒有 build step
212
+ // 這個 repo 就是可直接安裝的 bundle,沒有 build step
89
213
  plugin_manager({ action: "install_bundle", target: "/absolute/path/to/dsh-tab-status-light" })
90
214
  ```
91
215
 
92
216
  改完 `client.js` 後在瀏覽器重新載入頁面即可生效(Client 模組是即時從磁碟供應的)。
93
217
 
94
- ## License
218
+ ### License
95
219
 
96
220
  [MIT](LICENSE)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-tab-status-light",
3
- "version": "1.0.0",
4
- "description": "Shows the AI session state on the browser tab icon: gray pulsing while working, green when done, yellow while it waits for an answer, red when interrupted.",
3
+ "version": "1.1.0",
4
+ "description": "Show the AI session state on the browser tab icon — gray blinking while working, green when done, yellow while it waits for your answer, red when interrupted. 把 AI 狀態畫在分頁圖示上:進行中灰燈閃爍、完成綠、等你回答黃、中斷紅。",
5
5
  "license": "MIT",
6
6
  "author": "Kyle Fu",
7
7
  "keywords": [