dsh-web-icon-indicator 0.4.2 → 0.5.1
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 +25 -0
- package/README.md +132 -42
- package/README.zh.md +102 -36
- package/lib/client.js +856 -130
- package/lib/index.js +546 -118
- package/lib/types/client/index.d.ts +13 -7
- package/lib/types/index.d.ts +87 -9
- package/package.json +5 -4
- package/test/loader-hooks.mjs +21 -0
- package/test/stubs/dsh-settings.mjs +15 -0
- package/test/stubs/schemastery.mjs +12 -0
- package/test/verify.js +2273 -0
- package/docs/safari-favicon-research.md +0 -114
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.1](https://github.com/waknow/dsh-web-icon-indicator/compare/v0.5.0...v0.5.1) (2026-09-22)
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
* load on DSH 0.1.7 and keep the legacy settings contract ([6e8dbb5](https://github.com/waknow/dsh-web-icon-indicator/commit/6e8dbb5402b9847eecc33029d68316407f767288))
|
|
15
|
+
|
|
16
|
+
## [0.5.0](https://github.com/waknow/dsh-web-icon-indicator/compare/v0.4.2...v0.5.0) (2026-09-17)
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
* add GitHub Pages showcase site with live bilingual demos ([92ecd70](https://github.com/waknow/dsh-web-icon-indicator/commit/92ecd705217bf3e3a2f52edb86a05665c4ed5b08))
|
|
21
|
+
* add skills-lock.json to manage skill dependencies ([935ad4c](https://github.com/waknow/dsh-web-icon-indicator/commit/935ad4c90a1ba5e6c969ad21dc52c5fbff516c1e))
|
|
22
|
+
* per-instance default icon colour with a similarity warning ([a673c10](https://github.com/waknow/dsh-web-icon-indicator/commit/a673c109b622a074b4d4018caa104cbfb819f149))
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
* harden host/browser behavior and stop advertising unbakeable settings ([5dddbb8](https://github.com/waknow/dsh-web-icon-indicator/commit/5dddbb87c387c8e85785c1d298453919213492d9))
|
|
27
|
+
* repaint favicon instantly when the tab becomes visible ([cd5dfaf](https://github.com/waknow/dsh-web-icon-indicator/commit/cd5dfafdd79f330f775ea35a0a9163ad7ed838cb))
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
* pin the registration-time route keys and sync the workflow docs ([de370c5](https://github.com/waknow/dsh-web-icon-indicator/commit/de370c54d3b0777d028bd6ba871756202496c76d))
|
|
32
|
+
|
|
10
33
|
## [0.4.2](https://github.com/waknow/dsh-web-icon-indicator/compare/v0.4.1...v0.4.2) (2026-09-07)
|
|
11
34
|
|
|
12
35
|
### Fixed
|
|
@@ -23,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
23
46
|
### Changed
|
|
24
47
|
|
|
25
48
|
* document declaring/updating the DSH host requirement (engines.dsh) ([fb568b7](https://github.com/waknow/dsh-web-icon-indicator/commit/fb568b71362b4931712240087213845cfb3b76ee))
|
|
49
|
+
|
|
26
50
|
## [0.4.0](https://github.com/waknow/dsh-web-icon-indicator/compare/v0.3.1...v0.4.0) (2026-09-04)
|
|
27
51
|
|
|
28
52
|
### Added
|
|
@@ -33,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
33
57
|
|
|
34
58
|
* auto-create GitHub Release after successful npm publish ([02d98e3](https://github.com/waknow/dsh-web-icon-indicator/commit/02d98e3db9f83013a3e7667c6a0848efecb8ef37))
|
|
35
59
|
* note DSH 0.1.2 support and add a version-support banner ([cd42d2c](https://github.com/waknow/dsh-web-icon-indicator/commit/cd42d2c5b5650800e3620c5806b1b4ac2b25c84f))
|
|
60
|
+
|
|
36
61
|
## [0.3.1](https://github.com/waknow/dsh-web-icon-indicator/compare/v0.3.0...v0.3.1) (2026-09-02)
|
|
37
62
|
|
|
38
63
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,36 +1,38 @@
|
|
|
1
1
|
# dsh-web-icon-indicator
|
|
2
2
|
|
|
3
|
-
> 📖 [中文文档](README.zh.md) · [English](README.md) · 📝 [Changelog](CHANGELOG.md) · [Releases](https://github.com/waknow/dsh-web-icon-indicator/releases)
|
|
3
|
+
> 📖 [中文文档](README.zh.md) · [English](README.md) · 📝 [Changelog](CHANGELOG.md) · [Releases](https://github.com/waknow/dsh-web-icon-indicator/releases) · 🎨 [Live demo](https://waknow.github.io/dsh-web-icon-indicator/)
|
|
4
4
|
|
|
5
5
|
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
6
6
|
[](https://www.npmjs.com/package/dsh-web-icon-indicator)
|
|
7
7
|
[](https://www.npmjs.com/package/dsh-web-icon-indicator)
|
|
8
8
|
[](./LICENSE)
|
|
9
9
|
|
|
10
|
-
> **⚠️ DSH version support** — requires **DSH ≥ 0.1.2** (
|
|
10
|
+
> **⚠️ DSH version support** — requires **DSH ≥ 0.1.2-rc.1**. One bundle serves both settings generations: **modern** (≥ 0.1.7-alpha.1: exported `Config` schema + `configForms` + `plugins.row.config`) and **legacy** (≤ 0.1.6-alpha.1: `settings.installSection` + `settingsScope` + `settings.plugin.item`). Verified on **DSH 0.1.5-rc.3** and **DSH 0.1.7-alpha.1**; on any older host the favicon still works even if the settings page is not reachable.
|
|
11
11
|
|
|
12
12
|
Browser tab favicon reflects the current DSH session state — `idle` / `running` / `asking` / `done` — so you can see at a glance whether a session needs your attention, even when the tab is in the background.
|
|
13
13
|
|
|
14
|
+
> 🎨 **Live demo** — <https://waknow.github.io/dsh-web-icon-indicator/> · see the four states, the multi-agent counter and every effect rendered live in your browser, no install needed. The playground even drives the demo page's own tab favicon, exactly like the plugin does on a DSH page.
|
|
15
|
+
|
|
14
16
|
## ✨ What it does
|
|
15
17
|
|
|
16
18
|
- **Live session state on the tab favicon** — the browser-tab icon mirrors `idle` / `running` / `asking` / `done` (aggregate priority: `asking` > `running` > `done` > `idle`), so background tabs tell you at a glance what your agents are doing — including `ask_user_question` prompts and approval / sandbox-escalation waits, which pin the icon to `asking`.
|
|
17
19
|
- **One SVG, recolored & animated in the browser** — ships a single whale template ([`icons/base.svg`](./icons/base.svg)); every state, color and frame is rendered client-side as a `data:image/svg+xml` URI. No per-color icon files.
|
|
18
20
|
- **Six built-in effects** — `static`, `blink`, `breath`, `rainbow`, `heartbeat`, `bounce` — all driven by JavaScript, since favicons don't play SVG CSS animations.
|
|
19
21
|
- **Fully configurable, applied live** — every state's color, effect and cycle speed, plus the asking/done hold timings, apply to the running tab within ~1 s — no reload, no restart.
|
|
20
|
-
- **Built-in settings UI, zero YAML** —
|
|
21
|
-
- **Background-tab & restart-proof** — animated states keep a wall-clock fallback while `requestAnimationFrame` is paused in hidden tabs, and the status poll self-heals across host restarts. When the backend is stopped, the tab never loses its icon: the outage restores the shell's own favicon from an offline-safe `data:`-URI copy (or keeps the last painted frame), and the live icon returns on the first successful poll.
|
|
22
|
+
- **Built-in settings UI, zero YAML** — the *Favicon indicator* page edits the whole config with live color-swatch previews and persists it for you (modern hosts: the profile patch; legacy hosts: the profile `settings.yaml` — paths below).
|
|
23
|
+
- **Background-tab & restart-proof** — animated states keep a wall-clock fallback while `requestAnimationFrame` is paused in hidden tabs, and the status poll self-heals across host restarts. Returning to a tab repaints immediately: a `visibilitychange` listener fires an instant status fetch, so a state that flipped while the tab was hidden (e.g. the `done` hold expiring) shows at once instead of waiting for the next — possibly throttled — poll tick. When the backend is stopped, the tab never loses its icon: the outage restores the shell's own favicon from an offline-safe `data:`-URI copy (or keeps the last painted frame), and the live icon returns on the first successful poll.
|
|
22
24
|
- **Active-agent count at a glance** — while **more than one** agent is active (non-idle: `asking` / `running` / `done`), the favicon switches from the whale to a **full-frame number block** showing the live count (up to `99+`), colored and animated exactly like the whale would be in that state; back to the whale when 0–1 agents are active. (Same visual language as the *满幅数字* channel in [`demo/badge.html`](./demo/badge.html).)
|
|
23
25
|
|
|
24
26
|
### 🛠 Configuration UI — how to get there
|
|
25
27
|
|
|
26
28
|
| # | Step |
|
|
27
29
|
| --- | --- |
|
|
28
|
-
| 1 | Open the DSH Web GUI and go to **Settings /
|
|
29
|
-
| 2 |
|
|
30
|
-
| 3 |
|
|
31
|
-
| 4 |
|
|
30
|
+
| 1 | Open the DSH Web GUI and go to **Settings / 设置** → **Plugins / 插件**. |
|
|
31
|
+
| 2 | On ≥ 0.1.7: open the **dsh-web-icon-indicator** bundle and click **Configure / 配置** on its `dsh-web-icon-indicator` row. On ≤ 0.1.6-alpha.1: open **Plugin config / 插件配置** in that tab. |
|
|
32
|
+
| 3 | The **Favicon indicator / 标签页图标指示器** page opens with the full form. |
|
|
33
|
+
| 4 | Set **Default icon color / 默认图标颜色** for the idle whale (its only knob — idle paints one color and never animates), then expand a state row (`running` / `asking` / `done`) to edit **Effect / 特效**, **Colors / 颜色** (each swatch is a native color picker) and **Cycle (ms) / 周期(毫秒)** (shown only for animated states — static states have no cycle); use **Asking hold / 提问驻留** and **Done hold / 完成驻留** for the two timings. |
|
|
32
34
|
|
|
33
|
-
Changes are saved through the settings transport into the profile
|
|
35
|
+
Changes are saved through the settings transport into the profile patch and applied to the running tab within ~1 s — no reload, no restart. See [Configure](#configure) for the full key reference.
|
|
34
36
|
|
|
35
37
|
## 🎬 Default configuration, visualized
|
|
36
38
|
|
|
@@ -42,7 +44,7 @@ The four default states, exactly as they appear in the browser tab (the `asking`
|
|
|
42
44
|
|
|
43
45
|
| State | Default color | Default effect |
|
|
44
46
|
| --- | --- | --- |
|
|
45
|
-
| `idle` | `#1a1a1a` — deep whale | `static` |
|
|
47
|
+
| `idle` | `#1a1a1a` — deep whale (replaceable with `defaultColor`) | `static` |
|
|
46
48
|
| `running` | `#FACC15` — yellow | `static` |
|
|
47
49
|
| `asking` | `#E5484D` ⇄ `#FACC15` — red/yellow | `blink` (400 ms) |
|
|
48
50
|
| `done` | `#22A06B` — green | `static`, stays `doneHoldMs`, then back to `idle` |
|
|
@@ -115,15 +117,20 @@ Or drop the directory into `~/.dsh/profiles/web/node_modules/<name>/` and ship a
|
|
|
115
117
|
|
|
116
118
|
## Configure
|
|
117
119
|
|
|
118
|
-
All keys are optional; defaults shown.
|
|
120
|
+
All keys are optional; defaults shown. `statusPath` and `iconPathPrefix` are
|
|
121
|
+
**registration-time** keys: set them in the composition entry only — they are
|
|
122
|
+
baked into the route table and the injected script when the plugin mounts, so
|
|
123
|
+
they are intentionally **not** part of the settings page's live form (they are
|
|
124
|
+
`Config` fields, but not `.volatile()`).
|
|
119
125
|
|
|
120
126
|
| Key | Default | Meaning |
|
|
121
127
|
| --- | --- | --- |
|
|
122
128
|
| `iconsDir` | `<package>/icons/` | Directory holding the single `base.svg` |
|
|
123
|
-
| `statusPath` | `/dsh-web-icon-status.json` | JSON status endpoint |
|
|
124
|
-
| `iconPathPrefix` | `/dsh-web-icon-indicator` | URL prefix `base.svg` is served under |
|
|
129
|
+
| `statusPath` | `/dsh-web-icon-status.json` | JSON status endpoint — **registration-time (composition entry only)** |
|
|
130
|
+
| `iconPathPrefix` | `/dsh-web-icon-indicator` | URL prefix `base.svg` is served under — **registration-time (composition entry only)** |
|
|
125
131
|
| `askingHoldMs` | `3500` | Minimum visibility of the asking state |
|
|
126
132
|
| `doneHoldMs` | `5000` | Time the done state stays before falling back to idle |
|
|
133
|
+
| `defaultColor` | *(unset)* | Default icon color (the idle whale's primary) — tell multiple DSH instances apart. Warns when it is too close to another state's color |
|
|
127
134
|
| `states` | see below | Per-state visual config |
|
|
128
135
|
|
|
129
136
|
Each entry in `states` is one object per state: `{ effect, colors[], speed? }`:
|
|
@@ -138,9 +145,15 @@ config:
|
|
|
138
145
|
```
|
|
139
146
|
|
|
140
147
|
- **`effect`** — one of `static | blink | breath | rainbow | heartbeat | bounce`.
|
|
141
|
-
- **`colors`** — an **array** of hex colors. `colors[0]` is the primary. Multi-color effects read more entries: `blink` uses `colors[0]`⇄`colors[1]`, `breath` breathes `colors[0]`⇄`colors[1]` (each derives a darker second color if omitted), `rainbow` uses only `colors[0]` as the starting hue.
|
|
148
|
+
- **`colors`** — an **array** of hex colors (`#rgb` / `#rrggbb`; a malformed entry is ignored, and the state's built-in colour applies when none survives). `colors[0]` is the primary. Multi-color effects read more entries: `blink` uses `colors[0]`⇄`colors[1]`, `breath` breathes `colors[0]`⇄`colors[1]` (each derives a darker second color if omitted), `rainbow` uses only `colors[0]` as the starting hue.
|
|
142
149
|
- **`speed`** — optional per-state cycle length in ms (also the `blink` toggle interval). Default `1200`.
|
|
143
150
|
|
|
151
|
+
`idle` is special: its color is the `defaultColor` key, and the settings card
|
|
152
|
+
offers **no per-state entry** for it (one color, no animation, no cycle). A
|
|
153
|
+
`states.idle` entry is still honored when it arrives from the composition entry
|
|
154
|
+
or the settings document's user layer — backward compatibility only; it is
|
|
155
|
+
simply not editable from the page.
|
|
156
|
+
|
|
144
157
|
Entries are shallow-merged over the defaults, so you can override only a few states. Example:
|
|
145
158
|
|
|
146
159
|
```yaml
|
|
@@ -153,41 +166,118 @@ Entries are shallow-merged over the defaults, so you can override only a few sta
|
|
|
153
166
|
done: { effect: heartbeat, colors: ['#2ECC71'] }
|
|
154
167
|
```
|
|
155
168
|
|
|
156
|
-
###
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
169
|
+
### Tell multiple instances apart (`defaultColor`)
|
|
170
|
+
|
|
171
|
+
Running several DSH instances at once (different projects, profiles or ports)?
|
|
172
|
+
Give each one its own default icon color and the browser tabs become
|
|
173
|
+
immediately distinguishable — no need to touch the per-state palette:
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
- id: dsh-web-icon-indicator
|
|
177
|
+
name: 'dsh-web-icon-indicator'
|
|
178
|
+
config:
|
|
179
|
+
defaultColor: '#5B8DEF'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
- `defaultColor` is the **idle whale's primary color**. It is folded into
|
|
183
|
+
`states.idle.colors[0]`, so the idle state keeps its effect and any second
|
|
184
|
+
color you configured; the other states keep their signal colors. Unset (the
|
|
185
|
+
default) means "the idle state's own color", i.e. today's behavior.
|
|
186
|
+
- It is a **per-DSH-instance** setting, not per browser tab: every tab of one
|
|
187
|
+
instance shares it, while another instance (its own profile, e.g.
|
|
188
|
+
`dsh web --port 3081`) can use a different color.
|
|
189
|
+
- **Reset** removes your overrides back to the composition entry. When the
|
|
190
|
+
color comes from that entry (the `base` layer, which an `unset` cannot
|
|
191
|
+
reach), the card writes the idle state's own color instead — so "Reset to
|
|
192
|
+
defaults" really returns the icon to the plain whale color, and the entry's
|
|
193
|
+
value stays reachable through the clear-override control.
|
|
194
|
+
- **Similarity warning.** When the default color is perceptually too close to
|
|
195
|
+
another state's color you get a warning — live in the settings card, in the
|
|
196
|
+
host log, and as `warnings` on the status endpoint — but the value is still
|
|
197
|
+
applied (the warning never blocks saving). Distance is **CIE76 ΔE in
|
|
198
|
+
CIELAB**: `ΔE < 25` warns, `ΔE < 12` is reported as nearly identical
|
|
199
|
+
(`ΔE 2.3` is the just-noticeable difference). Every fill a state actually
|
|
200
|
+
paints is considered: the `asking` blink covers both of its colors,
|
|
201
|
+
`breath` its interpolation, and a `rainbow` state is flagged for any
|
|
202
|
+
chromatic default because it sweeps every hue. The shipped `running` and
|
|
203
|
+
`asking` colors deliberately share `#FACC15`, so states are never compared
|
|
204
|
+
with each other — only the default color against them. A malformed value is
|
|
205
|
+
ignored (and reported) rather than painted.
|
|
206
|
+
|
|
207
|
+
### Settings page (DSH ≥ 0.1.2-rc.1, both settings generations)
|
|
208
|
+
|
|
209
|
+
The plugin exports the whole config surface above as its Cordis `Config`
|
|
210
|
+
schema (a schemastery schema in `lib/index.js`) **and** registers the same
|
|
211
|
+
schema through the legacy settings service when the running host still exposes
|
|
212
|
+
it — the two paths are feature-detected at mount, so one bundle serves both.
|
|
213
|
+
The namespace differs by generation: **≥ 0.1.7** keys every live form by
|
|
214
|
+
**profile entry id**, so it is `dsh-web-icon-indicator` (the row id the bundle
|
|
215
|
+
patch declares); **≤ 0.1.6-alpha.1** uses the plugin-chosen
|
|
216
|
+
`web-icon-indicator`, the same string 0.5.x used, so an existing section keeps
|
|
217
|
+
resolving:
|
|
218
|
+
|
|
219
|
+
- **Web GUI:** open **设置 → 插件**, expand the **dsh-web-icon-indicator**
|
|
220
|
+
bundle and configure its row. The *Favicon indicator* page edits the same
|
|
221
|
+
keys: asking/done hold, the **default icon color** (with a
|
|
222
|
+
live palette preview and the similarity warning), and the per-state effect /
|
|
223
|
+
colors / cycle for `running` / `asking` / `done`. Each of those states is a
|
|
224
|
+
collapsible row whose header shows one **color chip** per state — split in two
|
|
225
|
+
for a multi-color state, so `asking` shows red | yellow at a glance — plus a
|
|
226
|
+
one-line summary of the effect and cycle (`Blink · 400ms`). Clicking a chip
|
|
227
|
+
opens the native color picker, which is where the hex value is shown/typed;
|
|
228
|
+
the card prints no colour codes itself, except in the similarity warning,
|
|
229
|
+
which names the offending hex to be actionable (e.g. `… Running color #FACC15
|
|
230
|
+
(ΔE 0)`). The expanded row lists one chip per
|
|
231
|
+
color the chosen effect actually uses: `blink` / `breath` get a `+` chip while
|
|
232
|
+
there is still room for a second color (it opens on the darker shade the
|
|
233
|
+
browser would derive anyway, and never appends a color the state already has),
|
|
234
|
+
every color after the first can be removed, and a single-color effect shows —
|
|
235
|
+
and saves — only one chip, so an unused second color can never linger
|
|
236
|
+
invisibly. `rainbow` carries no color list: its field and its chip show the
|
|
237
|
+
hue wheel, next to an optional chip for the **starting hue** (the stored
|
|
238
|
+
`colors[0]`). `idle` deliberately gets **no row** — it paints one
|
|
239
|
+
color and never animates, so the default-color field is its whole
|
|
240
|
+
configuration. Everything is staged and saved through the settings transport.
|
|
241
|
+
- **Persistence:** on ≥ 0.1.7 values land in the profile patch
|
|
242
|
+
(`~/.dsh/profiles/<profile>/cordis.patch.yml`) under the
|
|
243
|
+
`dsh-web-icon-indicator` row's `config:` block; on ≤ 0.1.6-alpha.1 they land
|
|
244
|
+
in `~/.dsh/settings.yaml` as a `web-icon-indicator:` section. In both cases
|
|
245
|
+
the composition entry stays the `base` layer; resolution order is schema
|
|
246
|
+
defaults → composition entry → user layer.
|
|
247
|
+
- **No server restart, no tab reload** for settings-page saves. On ≥ 0.1.7 a
|
|
248
|
+
live write is committed into the running plugin's config references
|
|
249
|
+
(`loader/volatile-update` — the loader does not restart the plugin); on the
|
|
250
|
+
legacy line the service calls the registered `onChange` hook. Either way
|
|
251
|
+
`askingHoldMs` / `doneHoldMs` apply host-side immediately and per-state visual
|
|
252
|
+
config (effect / colors / cycle) is synced into the running tab through the
|
|
253
|
+
status poll within ~1 s. Only code-level default changes in `lib/index.js`
|
|
254
|
+
need a tab reload (or a DSH web rebuild).
|
|
255
|
+
- **Route paths are not live settings.** `statusPath` / `iconPathPrefix` are
|
|
256
|
+
validated by `Config` but deliberately **not** `.volatile()`, so they are
|
|
257
|
+
absent from the live form: they are baked into the route table and the
|
|
258
|
+
injected script at registration time, and honoring an edit there would point
|
|
259
|
+
the browser at a path the server never serves. They are composition-entry
|
|
260
|
+
only (see the table above); changing them re-applies the plugin.
|
|
261
|
+
- The live surface therefore covers `askingHoldMs`, `doneHoldMs`, `iconsDir`,
|
|
262
|
+
`defaultColor` and `states`. `iconsDir` has no schema default, so it is
|
|
263
|
+
omitted from the resolved form until a user sets it.
|
|
177
264
|
- The browser half is a hand-written `lib/client.js` (ModuleLoader factory
|
|
178
|
-
format — no build step, no runtime deps beyond the shell's `react`).
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
265
|
+
format — no build step, no runtime deps beyond the shell's `react`). It
|
|
266
|
+
registers on both page slots (`plugins.row.config` — gated on the served
|
|
267
|
+
namespace — and the legacy `settings.plugin.item`) and resolves whichever
|
|
268
|
+
settings provider the host exposes (`configForms` or `settingsScope`) at
|
|
269
|
+
render time, so neither is a hard dependency of the module. The DSH client
|
|
270
|
+
scanner picks a new `dsh.client` declaration up on the next profile start.
|
|
271
|
+
- Deployments without a settings service are unaffected: the plugin keeps
|
|
272
|
+
running on the composition entry + schema defaults it was mounted with.
|
|
183
273
|
|
|
184
274
|
## How it works
|
|
185
275
|
|
|
186
|
-
- Host plugin with a small browser half: registers routes on the existing `webServer` — the status JSON endpoint, a static `/dsh-web-icon-indicator/base.svg` (the whale template), and one `tapIndex` that injects a small browser script into every served `index.html`. The config surface is
|
|
276
|
+
- Host plugin with a small browser half: registers routes on the existing `webServer` — the status JSON endpoint, a static `/dsh-web-icon-indicator/base.svg` (the whale template), and one `tapIndex` that injects a small browser script into every served `index.html`. The config surface is the plugin's exported `Config` schema, which the DSH settings service projects into a live page keyed by the `dsh-web-icon-indicator` entry id (see above).
|
|
187
277
|
- Status is aggregated across live `agents.list()` with priority `asking > running > done > idle`. The aggregation runs a `reconcile()` step on every request to detect running → idle transitions, because `agent/status`'s idle delivery is not guaranteed at turn end. The status endpoint also reports `active` — the number of non-idle agents — and while that count is **> 1** the injected script renders a full-frame count block (the *满幅数字* channel of [`demo/badge.html`](./demo/badge.html): a rounded block filled with the same per-frame state color/effect as the whale, bold white count sized 31%–52% of the icon, capped at `99+`) instead of the whale, so the tab shows how many agents are busy at once even in a pinned 16px tab.
|
|
188
278
|
- `ask_user_question` tool calls (via `tools/pre-execute` / `tools/result`) flip the session into `asking` with a configurable minimum-hold so the icon stays visible even when the user answers immediately.
|
|
189
279
|
- Permission / **sandbox-interception** waits are also surfaced as `asking`: when the agent hits a sandbox denial and escalates (`sandbox_permissions` + `justification`), or any other tool asks for approval, the approval service appends an `approval/asked` session event and blocks the agent until you decide. The plugin watches `session/event` (with an authoritative fold over the live session log as a fallback) and pins the session into the `asking` state for that whole wait, clearing it on `approval/decided`.
|
|
190
|
-
- The browser script polls `/dsh-web-icon-status.json` once a second, fetches `base.svg` once, and then on every `requestAnimationFrame` tick rebuilds the favicon as a `data:image/svg+xml,…` URI — replacing the `__COLOR__` placeholder with the state's configured color and applying the state's configured effect. The status response also echoes the current per-state visual config, so a settings save reaches the running tab on the next poll (~1 s) without a reload. Browsers don't play favicon SVG CSS animations, so all motion is JS-driven. Because browsers pause `requestAnimationFrame` in hidden tabs, the poll also repaints a wall-clock frame for animated states, so background tabs keep animating (coarsely) instead of freezing; full-speed animation resumes when the tab is visible again. The poll also survives host restarts — and a stopped backend never blanks the tab: at startup the script caches an offline-safe `data:`-URI copy of the original favicon, and on a fetch failure it restores that copy (or, if none could be captured, keeps the last painted frame) — it never writes the original server URL back, which would be unreachable exactly while the host is down. It retries every tick, and the live icon returns on the first successful poll (the SPA reconnects in place, so no manual refresh is needed).
|
|
280
|
+
- The browser script polls `/dsh-web-icon-status.json` once a second (the interval is fixed at 1000 ms in the injected script — it is not a config key), fetches `base.svg` once, and then on every `requestAnimationFrame` tick rebuilds the favicon as a `data:image/svg+xml,…` URI — replacing the `__COLOR__` placeholder with the state's configured color and applying the state's configured effect. The status response also echoes the current per-state visual config, so a settings save reaches the running tab on the next poll (~1 s) without a reload. Browsers don't play favicon SVG CSS animations, so all motion is JS-driven. Because browsers pause `requestAnimationFrame` in hidden tabs, the poll also repaints a wall-clock frame for animated states, so background tabs keep animating (coarsely) instead of freezing; full-speed animation resumes when the tab is visible again. Returning to a tab also triggers an immediate status fetch and repaint (`visibilitychange`), so a state that flipped while the tab was hidden shows at once instead of on the next — possibly throttled — poll tick. The poll also survives host restarts — and a stopped backend never blanks the tab: at startup the script caches an offline-safe `data:`-URI copy of the original favicon, and on a fetch failure it restores that copy (or, if none could be captured, keeps the last painted frame) — it never writes the original server URL back, which would be unreachable exactly while the host is down. It retries every tick, and the live icon returns on the first successful poll (the SPA reconnects in place, so no manual refresh is needed).
|
|
191
281
|
|
|
192
282
|
## Browser support & known limitations
|
|
193
283
|
|
package/README.zh.md
CHANGED
|
@@ -1,36 +1,38 @@
|
|
|
1
1
|
# dsh-web-icon-indicator
|
|
2
2
|
|
|
3
|
-
> 📖 [English](README.md) · [中文文档](README.zh.md) · 📝 [更新记录](CHANGELOG.md) · [Releases](https://github.com/waknow/dsh-web-icon-indicator/releases)
|
|
3
|
+
> 📖 [English](README.md) · [中文文档](README.zh.md) · 📝 [更新记录](CHANGELOG.md) · [Releases](https://github.com/waknow/dsh-web-icon-indicator/releases) · 🎨 [在线演示](https://waknow.github.io/dsh-web-icon-indicator/)
|
|
4
4
|
|
|
5
5
|
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
6
6
|
[](https://www.npmjs.com/package/dsh-web-icon-indicator)
|
|
7
7
|
[](https://www.npmjs.com/package/dsh-web-icon-indicator)
|
|
8
8
|
[](./LICENSE)
|
|
9
9
|
|
|
10
|
-
> **⚠️ DSH 版本支持** —— 需要 **DSH ≥ 0.1.2
|
|
10
|
+
> **⚠️ DSH 版本支持** —— 需要 **DSH ≥ 0.1.2-rc.1**。同一份插件同时兼容两代 settings 契约:**新一代**(≥ 0.1.7-alpha.1:导出 `Config` schema + `configForms` + `plugins.row.config`)与**旧一代**(≤ 0.1.6-alpha.1:`settings.installSection` + `settingsScope` + `settings.plugin.item`)。已在 **DSH 0.1.5-rc.3** 与 **DSH 0.1.7-alpha.1** 上验证;更早的宿主上 favicon 仍可用,只是配置页可能不可达。
|
|
11
11
|
|
|
12
12
|
浏览器标签页 favicon 实时反映 DSH 会话状态——`待机` / `运行中` / `提问` / `完成`——让你在标签页置于后台时也能一眼看出是否有会话需要处理。
|
|
13
13
|
|
|
14
|
+
> 🎨 **在线演示** — <https://waknow.github.io/dsh-web-icon-indicator/> · 在浏览器里直接体验四种状态、多 Agent 计数与全部特效,无需安装。试玩间还能驱动演示页自身标签页的真实 favicon——正如插件在 DSH 页面里做的那样。
|
|
15
|
+
|
|
14
16
|
## ✨ 功能特性
|
|
15
17
|
|
|
16
18
|
- **标签页 favicon 实时反映会话状态** —— 浏览器标签页图标同步 `idle` / `running` / `asking` / `done`(聚合优先级:`asking` > `running` > `done` > `idle`),后台标签页也能一眼看清 agent 们在做什么——包括 `ask_user_question` 提问,以及审批 / 沙箱提权等待(这两种情况会把图标钉在 `asking` 态)。
|
|
17
19
|
- **单个 SVG,浏览器内上色与动画** —— 只内置一个鲸鱼模板([`icons/base.svg`](./icons/base.svg));每个状态、颜色、每一帧都在客户端渲染为 `data:image/svg+xml` URI,不再有按颜色拆分的图标文件。
|
|
18
20
|
- **六种内置特效** —— `static`(静止)、`blink`(闪烁)、`breath`(呼吸)、`rainbow`(彩虹)、`heartbeat`(心跳)、`bounce`(跳动),全部由 JavaScript 驱动(favicon 不会播放 SVG CSS 动画)。
|
|
19
21
|
- **完全可配置、即时生效** —— 每个状态的颜色、特效、周期,以及提问 / 完成驻留时长,改动约 1 秒内同步到已打开的标签页——无需刷新、无需重启。
|
|
20
|
-
- **内置配置 UI,无需手写 YAML** ——
|
|
21
|
-
- **后台标签页与重启抗性** —— 隐藏标签页中 `requestAnimationFrame` 被暂停时,动画态会按墙钟时间补帧;状态轮询还能扛住 host 重启、后端停止:故障期间标签页图标绝不丢失(还原启动时缓存的原始图标 `data:`-URI
|
|
22
|
+
- **内置配置 UI,无需手写 YAML** —— *标签页图标指示器* 页面可编辑整套配置,带实时色块预览并自动持久化(新宿主:profile patch;旧宿主:profile `settings.yaml`,路径见下)。
|
|
23
|
+
- **后台标签页与重启抗性** —— 隐藏标签页中 `requestAnimationFrame` 被暂停时,动画态会按墙钟时间补帧;状态轮询还能扛住 host 重启、后端停止:故障期间标签页图标绝不丢失(还原启动时缓存的原始图标 `data:`-URI 副本,或保留最后一帧插件图标),端点恢复后自动回到实时状态。切回前台时会立刻触发一次状态拉取并重绘——后台标签页的定时器会被浏览器节流,轮询可能滞后,所以回到标签页的瞬间就刷新最新状态(比如 `done` 保持期在隐藏期间过期、图标应退回 `idle` 的情况)。
|
|
22
24
|
- **多 agent 一目了然** —— 当同时有**超过一个**活动 agent(非待机:`asking` / `running` / `done`)时,favicon 从鲸鱼切换为**占满整帧的数字块**,实时显示活动数(上限 `99+`),颜色与动画和该状态下鲸鱼完全一致;活动数回到 0–1 时恢复鲸鱼。(视觉与 [`demo/badge.html`](./demo/badge.html) 的「满幅数字」通道一致。)
|
|
23
25
|
|
|
24
26
|
### 🛠 配置界面——怎么找到它
|
|
25
27
|
|
|
26
28
|
| # | 步骤 |
|
|
27
29
|
| --- | --- |
|
|
28
|
-
| 1 | 打开 DSH Web GUI,进入
|
|
29
|
-
| 2 |
|
|
30
|
-
| 3 |
|
|
31
|
-
| 4 |
|
|
30
|
+
| 1 | 打开 DSH Web GUI,进入 **设置** → **插件**。 |
|
|
31
|
+
| 2 | ≥ 0.1.7:展开 **dsh-web-icon-indicator** bundle,在其 `dsh-web-icon-indicator` 行上点击 **配置**;≤ 0.1.6-alpha.1:在该选项卡里打开 **插件配置**。 |
|
|
32
|
+
| 3 | 打开 **标签页图标指示器(Favicon indicator)** 配置页。 |
|
|
33
|
+
| 4 | 先用 **默认图标颜色** 设置待机鲸鱼的颜色(这是待机唯一的设置项——待机只画一种颜色、不做动画),再展开状态行(`running` / `asking` / `done`)编辑 **特效**、**颜色**(每个色块即原生取色器),以及**周期(毫秒)**——仅动画状态显示,静态状态无周期;用 **提问驻留** / **完成驻留** 调整两个时长。 |
|
|
32
34
|
|
|
33
|
-
改动会通过 settings 传输层持久化到 profile
|
|
35
|
+
改动会通过 settings 传输层持久化到 profile patch,约 1 秒内应用到已打开的标签页——无需刷新、无需重启。完整键说明见 [配置](#配置)。
|
|
34
36
|
|
|
35
37
|
## 🎬 默认配置,可视化
|
|
36
38
|
|
|
@@ -42,7 +44,7 @@
|
|
|
42
44
|
|
|
43
45
|
| 状态 | 颜色(默认) | 特效(默认) |
|
|
44
46
|
| --- | --- | --- |
|
|
45
|
-
| `idle` 待机 | `#1a1a1a
|
|
47
|
+
| `idle` 待机 | `#1a1a1a`——深色鲸鱼(可用 `defaultColor` 替换) | `static` |
|
|
46
48
|
| `running` 运行中 | `#FACC15`——黄色 | `static` |
|
|
47
49
|
| `asking` 提问 | `#E5484D` ⇄ `#FACC15`——红/黄 | `blink`(400ms) |
|
|
48
50
|
| `done` 完成 | `#22A06B`——绿色 | `static`,保持 `doneHoldMs` 后回到 `idle` |
|
|
@@ -110,15 +112,18 @@ dsh plugin --profile web add <路径或tarball>
|
|
|
110
112
|
|
|
111
113
|
## 配置
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
所有键均可选,默认值如下。`statusPath` 与 `iconPathPrefix` 是**注册期**键:
|
|
116
|
+
只能在合成条目(composition entry)里设置——它们在插件挂载时就被烘进路由表与注入
|
|
117
|
+
脚本,因此刻意**不**进入配置页的实时表单(它们是 `Config` 字段,但非 `.volatile()`)。
|
|
114
118
|
|
|
115
119
|
| 键 | 默认值 | 含义 |
|
|
116
120
|
| --- | --- | --- |
|
|
117
121
|
| `iconsDir` | `<package>/icons/` | 单个 `base.svg` 所在目录 |
|
|
118
|
-
| `statusPath` | `/dsh-web-icon-status.json` | JSON 状态端点 |
|
|
119
|
-
| `iconPathPrefix` | `/dsh-web-icon-indicator` | `base.svg` 的 URL 前缀 |
|
|
122
|
+
| `statusPath` | `/dsh-web-icon-status.json` | JSON 状态端点 —— **注册期(仅合成条目)** |
|
|
123
|
+
| `iconPathPrefix` | `/dsh-web-icon-indicator` | `base.svg` 的 URL 前缀 —— **注册期(仅合成条目)** |
|
|
120
124
|
| `askingHoldMs` | `3500` | 提问状态的最小保持时长 |
|
|
121
125
|
| `doneHoldMs` | `5000` | 完成状态保持时长,随后回到 idle |
|
|
126
|
+
| `defaultColor` | *(未设置)* | 默认图标颜色(待机鲸鱼的主色)—— 用于区分多个 DSH 实例;与其它状态颜色过于接近时会告警 |
|
|
122
127
|
| `states` | 见下 | 每个状态的视觉配置 |
|
|
123
128
|
|
|
124
129
|
`states` 中每个状态是一个对象:`{ effect, colors[], speed? }`:
|
|
@@ -133,9 +138,13 @@ config:
|
|
|
133
138
|
```
|
|
134
139
|
|
|
135
140
|
- **`effect`** — 取 `static | blink | breath | rainbow | heartbeat | bounce` 之一。
|
|
136
|
-
- **`colors`** — **数组**,多个 hex
|
|
141
|
+
- **`colors`** — **数组**,多个 hex 颜色(`#rgb` / `#rrggbb`;非法项会被逐项忽略,全部无效时才回退到该状态的内置颜色)。`colors[0]` 为主色。多色特效读取更多项:`blink` 用 `colors[0]`⇄`colors[1]`,`breath` 在 `colors[0]`⇄`colors[1]` 间过渡(缺省时自动推导更深的第二色),`rainbow` 仅用 `colors[0]` 作起始色相。
|
|
137
142
|
- **`speed`** — 可选,该状态的周期(ms),也是 `blink` 的切换间隔。默认 `1200`。
|
|
138
143
|
|
|
144
|
+
`idle` 比较特殊:它的颜色就是 `defaultColor` 键,设置卡片**不为它提供状态条目**
|
|
145
|
+
(一种颜色、不做动画、也没有周期)。若 `states.idle` 来自合成条目或手写的
|
|
146
|
+
设置文档的用户层,仍然会被沿用——这属于向后兼容,只是无法在配置页里编辑。
|
|
147
|
+
|
|
139
148
|
每个状态条目会在默认值之上做浅合并,因此只需覆盖少量状态。示例:
|
|
140
149
|
|
|
141
150
|
```yaml
|
|
@@ -148,35 +157,92 @@ config:
|
|
|
148
157
|
done: { effect: heartbeat, colors: ['#2ECC71'] }
|
|
149
158
|
```
|
|
150
159
|
|
|
151
|
-
###
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
### 区分多个实例(`defaultColor`)
|
|
161
|
+
|
|
162
|
+
同时开多个 DSH 实例(不同项目 / profile / 端口)时,给每个实例设一个自己的默认图标
|
|
163
|
+
颜色,浏览器标签页就能一眼区分,不必去改整套状态配色:
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
- id: dsh-web-icon-indicator
|
|
167
|
+
name: 'dsh-web-icon-indicator'
|
|
168
|
+
config:
|
|
169
|
+
defaultColor: '#5B8DEF'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
- `defaultColor` 就是**待机鲸鱼的主色**。它会被折叠进 `states.idle.colors[0]`,
|
|
173
|
+
因此 idle 仍保留自己配置的特效与第二色,其它状态的颜色语义(黄=运行、红/黄=提问、
|
|
174
|
+
绿=完成)不受影响;不设置(默认)即等于「沿用 idle 自己的颜色」,行为与之前完全一致。
|
|
175
|
+
- 这是**按 DSH 实例**生效的设置,不是按标签页:同一实例的所有标签页共用它;另一个实例
|
|
176
|
+
(自己的 profile,例如 `dsh web --port 3081`)可以用另一种颜色。
|
|
177
|
+
- **恢复默认**会把你的覆盖清回合成条目。当颜色本来就来自合成条目(`base` 层,
|
|
178
|
+
用户层的 `unset` 触及不到)时,卡片改为写入 idle 自己的颜色——这样「恢复默认」真的
|
|
179
|
+
能让图标回到朴素的鲸鱼色,而合成条目里配置的值仍可通过「清除覆盖」一键取回。
|
|
180
|
+
- **相似度告警**:当默认颜色与其它状态颜色在感知上过于接近时,你会收到告警 —— 设置卡片里
|
|
181
|
+
实时显示(保存前即可见)、host 日志里记录、状态端点 `warnings` 中返回 —— 但该颜色
|
|
182
|
+
仍然会被应用(告警绝不阻断保存)。度量方式是 **CIELAB 中的 CIE76 ΔE**:`ΔE < 25`
|
|
183
|
+
告警,`ΔE < 12` 视为几乎相同(ΔE 2.3 是人眼恰可分辨的阈值)。比较覆盖每个状态实际
|
|
184
|
+
会画出的所有填充:`asking` 闪烁的两种颜色、`breath` 的插值中间色;而 `rainbow`
|
|
185
|
+
会扫过所有色相,因此任何有色默认色都会被标记。出厂默认中 `running` 与 `asking` 刻意
|
|
186
|
+
共用 `#FACC15`,所以**状态之间互不比较**,只把默认色与它们逐一比较。格式非法的值会被
|
|
187
|
+
忽略并上报,而不会被画到图标上。
|
|
188
|
+
|
|
189
|
+
### 设置页(DSH ≥ 0.1.2-rc.1,兼容两代 settings 契约)
|
|
190
|
+
|
|
191
|
+
插件把上面整套配置作为 Cordis `Config` schema 导出(`lib/index.js` 中的
|
|
192
|
+
schemastery schema),**并在宿主仍提供旧版服务时**用同一份 schema 走
|
|
193
|
+
`settings.installSection` 注册——两条路径在挂载时按能力探测,因此同一份 bundle
|
|
194
|
+
两代宿主都能用。命名空间随代际不同:**≥ 0.1.7** 按 **profile entry id** 组织实时
|
|
195
|
+
表单,即 `dsh-web-icon-indicator`(bundle patch 声明的行 id);**≤ 0.1.6-alpha.1**
|
|
196
|
+
使用插件自定的 `web-icon-indicator`,与 0.5.x 保持一致,老用户的 section 继续生效:
|
|
197
|
+
|
|
198
|
+
- **Web GUI:** 打开 **设置 → 插件**,展开 **dsh-web-icon-indicator** bundle 并
|
|
199
|
+
配置它的行,即出现 *标签页图标指示器* 页面,可编辑:提问/完成驻留、
|
|
200
|
+
**默认图标颜色**(带配色一览与相似度告警),以及
|
|
201
|
+
`running` / `asking` / `done` 三个状态各自的特效 / 颜色 / 周期。这三个状态
|
|
202
|
+
各占一行可折叠条目,行首是一个**色块**——多色状态(asking)会左右分格同时显示
|
|
203
|
+
红黄两色,旁边是一行「特效 · 周期」摘要(如 `Blink · 400ms`)。点击色块即打开
|
|
204
|
+
系统取色弹窗(十六进制值在那里显示/输入),卡片本身不打印颜色代码——唯一例外是相似度
|
|
205
|
+
告警,它会给出具体的 hex 以便定位(例如「与「运行中」的颜色 #FACC15 几乎相同(ΔE 0)」)。
|
|
206
|
+
展开该行后只列出当前
|
|
207
|
+
特效**实际会用到**的颜色:`blink` / `breath` 在还有空位时才多出一个 `+` 色块
|
|
208
|
+
(它默认停在浏览器本来就会推导的那个更深的第二色上,且不会重复添加已有颜色),第一个
|
|
209
|
+
之后的色块都可以删除;单色特效则只显示、也只保存一个色块,不会留下看不见的第二色。
|
|
210
|
+
`rainbow` 不携带颜色列表:字段与色块都显示彩虹色环,色环旁另有一个**可选的起始色相**
|
|
211
|
+
色块(也就是存下的 `colors[0]`)。`idle` 刻意**不占
|
|
212
|
+
一行**——它只画一种颜色、不做动画,默认图标颜色就是它的全部配置。所有修改都通过
|
|
213
|
+
settings 传输层暂存并保存。
|
|
214
|
+
- **持久化:** ≥ 0.1.7 写入 profile patch
|
|
215
|
+
(`~/.dsh/profiles/<profile>/cordis.patch.yml`)中 `dsh-web-icon-indicator`
|
|
216
|
+
行的 `config:` 段;≤ 0.1.6-alpha.1 写入 `~/.dsh/settings.yaml` 的
|
|
217
|
+
`web-icon-indicator:` 段。两者都保持合成条目为 `base` 层;解析顺序为
|
|
218
|
+
schema 默认值 → 合成条目 → 用户层。
|
|
219
|
+
- **无需重启服务器、无需刷新标签页**即可让配置页的修改生效:≥ 0.1.7 时实时写入
|
|
220
|
+
直接提交到运行中插件的配置引用(`loader/volatile-update`——loader 不会重启插件);
|
|
221
|
+
旧一代则由 settings 服务回调注册的 `onChange`。两种情况 `askingHoldMs` /
|
|
222
|
+
`doneHoldMs` 都在主机侧即时生效;各状态的视觉配置(特效 / 颜色 / 周期)会随状态
|
|
223
|
+
轮询同步进正在运行的标签页,约 1 秒内生效。只有改 `lib/index.js` 里的代码级默认值
|
|
224
|
+
才需要重载标签页(或重新构建 DSH Web)。
|
|
225
|
+
- **路由路径不是实时设置项。** `statusPath` / `iconPathPrefix` 由 `Config` 校验,但
|
|
226
|
+
刻意**不加** `.volatile()`,因此不出现在实时表单里:它们已被烘进路由表与注入脚本,
|
|
227
|
+
在那里生效只会让浏览器去请求服务器根本没有提供的路径。它们只存在于合成条目
|
|
228
|
+
(见上方表格),改动会重新挂载插件。
|
|
229
|
+
- 因此实时设置面覆盖 `askingHoldMs`、`doneHoldMs`、`iconsDir`、`defaultColor`
|
|
230
|
+
与 `states`。`iconsDir` 没有 schema 默认值,用户未设置时不会出现在解析后的表单中。
|
|
168
231
|
- 浏览器半区是手写的 `lib/client.js`(ModuleLoader factory 格式——无构建步骤、
|
|
169
|
-
无额外运行期依赖,仅用 shell 自带的 `react
|
|
170
|
-
|
|
171
|
-
|
|
232
|
+
无额外运行期依赖,仅用 shell 自带的 `react`)。它同时往两代页面插槽注册
|
|
233
|
+
(`plugins.row.config`——按「Host 是否提供该命名空间」门控;以及旧的
|
|
234
|
+
`settings.plugin.item`),并在渲染时按宿主实际提供的服务解析表单
|
|
235
|
+
(`configForms` 或 `settingsScope`),因此两者都不是模块的硬依赖。DSH 客户端
|
|
236
|
+
扫描器会在下次启动 profile 时识别新的 `dsh.client` 声明。
|
|
237
|
+
- 未组合 settings 服务的部署不受影响:插件继续使用挂载时的合成条目 + schema 默认值。
|
|
172
238
|
|
|
173
239
|
## 实现原理
|
|
174
240
|
|
|
175
|
-
- Host 插件 + 一个小型浏览器半区:在现有 `webServer` 上注册路由——状态 JSON 端点、静态 `/dsh-web-icon-indicator/base.svg`(鲸鱼模板),以及一个 `tapIndex` 向每个 `index.html`
|
|
241
|
+
- Host 插件 + 一个小型浏览器半区:在现有 `webServer` 上注册路由——状态 JSON 端点、静态 `/dsh-web-icon-indicator/base.svg`(鲸鱼模板),以及一个 `tapIndex` 向每个 `index.html` 注入小段浏览器脚本。整套配置就是插件导出的 `Config` schema,DSH settings 服务按 `dsh-web-icon-indicator` entry id 把它投射为实时配置页(见上)。
|
|
176
242
|
- 状态按 `agents.list()` 聚合,优先级 `asking > running > done > idle`。每次请求都会执行一次 `reconcile()` 检测 running → idle 的转换,因为 `agent/status` 的 idle 事件在回合结束时并不保证送达。状态端点还会上报 `active`——非待机 agent 数——当该数 **> 1** 时,注入脚本改为渲染占满整帧的数字块([`demo/badge.html`](./demo/badge.html) 的「满幅数字」通道:圆角色块,填充色与鲸鱼同源的逐帧状态色/特效,白色粗体数字约占图标高度 31%–52%,上限 `99+`),而不是鲸鱼,这样即使在 16px 的固定标签页里也能一眼看出同时有几个 agent 在忙。
|
|
177
243
|
- `ask_user_question` 工具调用(通过 `tools/pre-execute` / `tools/result`)把会话置为 `asking`,带可配置的最小保持时长,即使你立刻回答,图标也会保持可见。
|
|
178
244
|
- 权限 / **沙箱拦截**等待同样会显示为 `asking`:当 agent 命中沙箱拒绝并请求提权(`sandbox_permissions` + `justification`),或其他工具需要征得同意时,审批服务会先写入一条 `approval/asked` 会话事件并阻塞 agent,直到你做出决定。插件监听 `session/event`(并以实时会话日志的权威折叠作为兜底)在整个等待期间将会话置为 `asking` 状态,收到 `approval/decided` 后清除。
|
|
179
|
-
- 浏览器脚本每秒轮询 `/dsh-web-icon-status.json
|
|
245
|
+
- 浏览器脚本每秒轮询 `/dsh-web-icon-status.json`(轮询间隔在注入脚本里固定为 1000 ms,不是配置项),首次获取 `base.svg`,然后每个 `requestAnimationFrame` 周期把 favicon 重建为 `data:image/svg+xml,…` URI——把 `__COLOR__` 占位符替换为状态配置的颜色,并应用该状态配置的特效。状态响应还会携带当前的每状态视觉配置,因此设置保存后约 1 秒内(下一个轮询 tick)即同步到已打开的标签页,无需刷新。浏览器不会播放 SVG favicon 的 CSS 动画,所以一切动画都由 JS 驱动。由于浏览器在**隐藏(后台)标签页会暂停 `requestAnimationFrame`**,轮询还会为动画态补绘一帧按墙钟时间计算的画面——后台标签页保持粗粒度动画(约每 1 秒)而不会冻结,切回前台后恢复满速动画。回到前台时还会通过 `visibilitychange` 立即触发一次状态拉取并重绘——后台定时器会被节流,轮询可能滞后,所以切回标签页的瞬间就能看到最新状态(例如隐藏期间 `done` 保持期已过、图标应退回 `idle`)。轮询还能**扛住 host 重启 / 后端停止**:启动时会把原始 favicon 缓存为离线安全的 `data:`-URI 副本,请求失败时还原该副本(副本未取到则保留最后一帧插件图标)——绝不写回原始的服务端 URL(后端停止时它恰恰不可达,写回正是「图标丢失」的根因);每个 tick 持续重试,端点恢复后第一个成功轮询即换回实时图标(SPA 原地重连,无需手动刷新)。
|
|
180
246
|
|
|
181
247
|
## 浏览器支持与已知限制
|
|
182
248
|
|