dsh-web-icon-indicator 0.2.1 → 0.2.3

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 ADDED
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ All notable changes to **dsh-web-icon-indicator** are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.3](https://github.com/waknow/dsh-web-icon-indicator/compare/v0.2.2...v0.2.3) (2026-08-24)
11
+
12
+ ### Changed
13
+
14
+ * add CHANGELOG.md with release history; link it from READMEs ([262730c](https://github.com/waknow/dsh-web-icon-indicator/commit/262730c83449b36fa60442204b3362d8a8b43e8f))
15
+
16
+ ## [0.2.2] - 2026-08-24
17
+
18
+ ### Changed
19
+
20
+ - Added the [awesome · DSH plugin](https://awesome-dsh-plugin.com/badge.svg) listing badge to `README.md` and `README.zh.md` now that the plugin is featured in [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) ([9042fb3](https://github.com/waknow/dsh-web-icon-indicator/commit/9042fb3)).
21
+ - npm publishing is now fully automated on `v*` tags via GitHub Actions (OIDC trusted publishing, no token secrets) — verified end-to-end with the `0.2.2` release.
22
+
23
+ ## [0.2.1] - 2026-08-21
24
+
25
+ ### Added
26
+
27
+ - Settings card UX polish and favicon render robustness ([e584912](https://github.com/waknow/dsh-web-icon-indicator/commit/e584912)).
28
+ - Documentation: visualize the default config and all effects in the READMEs; settings card shows a cycle field only for animated states and swatches as native color pickers; Safari favicon dynamic-color feasibility research with browser support tables ([23da09e](https://github.com/waknow/dsh-web-icon-indicator/commit/23da09e), [d99546d](https://github.com/waknow/dsh-web-icon-indicator/commit/d99546d), [04a8e58](https://github.com/waknow/dsh-web-icon-indicator/commit/04a8e58)).
29
+ - CI: publish to npm from GitHub Actions on version tags; verify the tag matches `package.json` before publishing; switch `@deepseek-ai` deps to `peerDependencies` ([b96e90e](https://github.com/waknow/dsh-web-icon-indicator/commit/b96e90e), [61fc3bc](https://github.com/waknow/dsh-web-icon-indicator/commit/61fc3bc), [23da09e](https://github.com/waknow/dsh-web-icon-indicator/commit/23da09e)).
30
+
31
+ ### Fixed
32
+
33
+ - Keep the heartbeat/bounce whale centered when the CSS animation runs ([4a379aa](https://github.com/waknow/dsh-web-icon-indicator/commit/4a379aa)).
34
+
35
+ ## [0.2.0] - 2026-08-20
36
+
37
+ ### Added
38
+
39
+ - Full per-state visual configuration: `effect` / `colors[]` / `speed` per state, plus `askingHoldMs` / `doneHoldMs` timings ([dca6de3](https://github.com/waknow/dsh-web-icon-indicator/commit/dca6de3)).
40
+ - DSH settings service integration (`web-icon-indicator` namespace) with a settings-page card — validate, persist to `settings.yaml`, and apply live to the running tab ([7cf6962](https://github.com/waknow/dsh-web-icon-indicator/commit/7cf6962)).
41
+ - Sandbox-interception and approval waits surfaced as the `asking` state ([1f8ebba](https://github.com/waknow/dsh-web-icon-indicator/commit/1f8ebba)).
42
+ - Background-tab animation fallback: wall-clock frames for animated states while `requestAnimationFrame` is paused in hidden tabs ([4abc9b2](https://github.com/waknow/dsh-web-icon-indicator/commit/4abc9b2)).
43
+ - Polling resilience: a transient fetch failure restores the original icon and retries on the next tick instead of killing the poll ([d7b4188](https://github.com/waknow/dsh-web-icon-indicator/commit/d7b4188)).
44
+
45
+ ### Changed
46
+
47
+ - Replaced the individual per-state SVG icons with a single [base.svg](icons/base.svg) whale template, recolored and animated entirely in the browser as `data:image/svg+xml` URIs ([0a76d0b](https://github.com/waknow/dsh-web-icon-indicator/commit/0a76d0b)).
48
+ - Browser half rewritten to use shell UI primitives for the settings card ([971f3b6](https://github.com/waknow/dsh-web-icon-indicator/commit/971f3b6)).
49
+ - Added live SVG previews of every state and effect to the READMEs ([a1f67ce](https://github.com/waknow/dsh-web-icon-indicator/commit/a1f67ce)).
50
+
51
+ ## [0.1.0] - 2026-08-19
52
+
53
+ ### Added
54
+
55
+ - Initial release: browser tab favicon mirrors the DSH session state (`idle` / `running` / `asking` / `done`) with a single-color whale icon ([c8b66cb](https://github.com/waknow/dsh-web-icon-indicator/commit/c8b66cb)).
56
+ - Repository metadata and install-from-source instructions ([69f550c](https://github.com/waknow/dsh-web-icon-indicator/commit/69f550c)).
package/README.md CHANGED
@@ -1,43 +1,69 @@
1
1
  # dsh-web-icon-indicator
2
2
 
3
- > 📖 [中文文档](README.zh.md) · [English](README.md)
3
+ > 📖 [中文文档](README.zh.md) · [English](README.md) · 📝 [Changelog](CHANGELOG.md) · [Releases](https://github.com/waknow/dsh-web-icon-indicator/releases)
4
+
5
+ [![awesome · DSH plugin](https://awesome-dsh-plugin.com/badge.svg)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
4
6
 
5
7
  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.
6
8
 
7
- ## States
9
+ ## ✨ What it does
8
10
 
9
- | State | Color (default) | Effect (default) |
10
- | --- | --- | --- |
11
- | `idle` | `#1a1a1a` (deep whale) | `static` |
12
- | `running` | `#FACC15` (yellow) | `static` |
13
- | `asking` | `#E5484D` + `#FACC15` | `blink` (400 ms) |
14
- | `done` | `#22A06B` (green) | `static` for `doneHoldMs`, then `idle` |
11
+ - **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`.
12
+ - **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.
13
+ - **Six built-in effects** — `static`, `blink`, `breath`, `rainbow`, `heartbeat`, `bounce` — all driven by JavaScript, since favicons don't play SVG CSS animations.
14
+ - **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.
15
+ - **Built-in settings UI, zero YAML** a *Favicon indicator* card in the DSH settings page edits the whole config with live color-swatch previews and persists it to `settings.yaml` for you (path below).
16
+ - **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.
15
17
 
16
- The plugin ships **one** base whale SVG ([`icons/base.svg`](./icons/base.svg)) and recolors / animates it in the browser — there are no per-color icon files anymore. Color and effect are fully configurable per state (see [Configure](#configure)).
18
+ ### 🛠 Configuration UI how to get there
17
19
 
18
- ### Dynamic color & animation
20
+ | # | Step |
21
+ | --- | --- |
22
+ | 1 | Open the DSH Web GUI and go to **Settings / 设置**. |
23
+ | 2 | In the **Plugins / 插件** tab, open **Plugin config / 插件配置**. |
24
+ | 3 | Find the **Favicon indicator / 标签页图标指示器** card. |
25
+ | 4 | Expand a state row (`idle` / `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. |
19
26
 
20
- All states share the same whale path; only the fill color (and optionally an animation) differ. Because a favicon is a plain image, SVG CSS animations never run inside the tab the injected browser script builds each frame as a `data:image/svg+xml,…` URI, replacing the `__COLOR__` placeholder with the configured color and, for animated effects, injecting a `<g transform>` for scale/translate on every `requestAnimationFrame` tick. The available effects are:
27
+ Changes are saved through the settings transport into the profile's `settings.yaml` and applied to the running tab within ~1 sno reload, no restart. See [Configure](#configure) for the full key reference.
21
28
 
22
- | Effect | Behavior |
23
- | --- | --- |
24
- | `static` | A single colored frame, no motion uses `colors[0]` |
25
- | `blink` | Toggles `colors[0]` ⇄ `colors[1]` (a darker second color is derived if missing) over `speed` |
26
- | `breath` | Pulsates between `colors[0]` and `colors[1]` (derived if missing) over `speed` |
27
- | `rainbow` | Uses `colors[0]` as the starting hue, then cycles the wheel over `speed` |
28
- | `heartbeat` | Scale pulses with a sharp lub-dub beat over `speed` — color is `colors[0]` |
29
- | `bounce` | The whale hops up and down over `speed` — color is `colors[0]` |
29
+ ## 🎬 Default configuration, visualized
30
+
31
+ The four default states, exactly as they appear in the browser tab (the `asking` whale really blinks):
32
+
33
+ <p align="center">
34
+ <img src="assets/states-default.svg" width="420" alt="Default states: idle dark whale, running yellow, asking red/yellow blinking, done green">
35
+ </p>
36
+
37
+ | State | Default color | Default effect |
38
+ | --- | --- | --- |
39
+ | `idle` | `#1a1a1a` — deep whale | `static` |
40
+ | `running` | `#FACC15` — yellow | `static` |
41
+ | `asking` | `#E5484D` ⇄ `#FACC15` — red/yellow | `blink` (400 ms) |
42
+ | `done` | `#22A06B` — green | `static`, stays `doneHoldMs`, then back to `idle` |
43
+
44
+ ## ✨ All effects, animated
30
45
 
31
- A self-contained demo (no build, no deps) with these effects lives in [`demo/dynamic-color.html`](./demo/dynamic-color.html) pick a state + effect and edit colors live, watching the browser-tab favicon update in real time.
46
+ Every preview below is the real whale path, animated the same way the plugin renders it (the previews are self-contained animated SVGs they play right in your browser):
47
+
48
+ | Effect | What it does | Preview |
49
+ | --- | --- | --- |
50
+ | `static` | A single colored frame, no motion — uses `colors[0]` | <img src="assets/effects/static.svg" width="56" alt="static effect preview"> |
51
+ | `blink` | Toggles `colors[0]` ⇄ `colors[1]` (a darker second color is derived if missing) over `speed` | <img src="assets/effects/blink.svg" width="56" alt="blink effect preview"> |
52
+ | `breath` | Pulsates smoothly between `colors[0]` and `colors[1]` (derived if missing) over `speed` | <img src="assets/effects/breath.svg" width="56" alt="breath effect preview"> |
53
+ | `rainbow` | Uses `colors[0]` as the starting hue, then cycles the color wheel over `speed` | <img src="assets/effects/rainbow.svg" width="56" alt="rainbow effect preview"> |
54
+ | `heartbeat` | Scale pulses with a sharp lub-dub beat over `speed` — color is `colors[0]` | <img src="assets/effects/heartbeat.svg" width="56" alt="heartbeat effect preview"> |
55
+ | `bounce` | The whale hops up and down over `speed` — color is `colors[0]` | <img src="assets/effects/bounce.svg" width="56" alt="bounce effect preview"> |
56
+
57
+ Want to tweak colors and watch the tab favicon change live? Open the self-contained demo ([`demo/dynamic-color.html`](./demo/dynamic-color.html)) — pick a state + effect, edit colors, and the favicon updates in real time (no build, no dependencies).
32
58
 
33
59
  ## Install
34
60
 
35
61
  This is a standard DSH bundle plugin. Install it into the `web` profile (the GUI/TUI profiles pick it up automatically through the cordis patch layer).
36
62
 
37
- From npm (**recommended** — published as `dsh-web-icon-indicator@0.1.0`):
63
+ From npm (**recommended**):
38
64
 
39
65
  ```bash
40
- dsh plugin --profile web add dsh-web-icon-indicator
66
+ dsh plugin --profile web add dsh-web-icon-indicator@latest
41
67
  ```
42
68
 
43
69
  From the Git source:
@@ -130,13 +156,35 @@ service under the `web-icon-indicator` namespace (a schemastery schema in
130
156
  - 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`.
131
157
  - 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: a transient fetch failure restores the original icon and retries on the next tick (the SPA reconnects in place, so the icon comes back without a manual refresh).
132
158
 
159
+ ## Browser support & known limitations
160
+
161
+ The favicon is a plain image, so browsers never run the SVG's own CSS/JS animation inside the tab UI — every frame is rendered here in JavaScript. How well a *changing* favicon is displayed differs by browser:
162
+
163
+ | Browser | SVG favicon | Live per-state color/effect | Why |
164
+ | --- | --- | --- | --- |
165
+ | Chrome / Edge | ✅ | ✅ smooth | Re-reads `<link rel=icon>` live; `data:`-URI SVGs are fine. |
166
+ | Firefox | ✅ | ✅ smooth | Renders SVG favicons well (and honors their `prefers-color-scheme`, unused here). |
167
+ | Safari (macOS) | ✅ rendered static | ⚠️ best-effort | Ignores in-SVG CSS; aggressive icon caching. |
168
+ | Safari (iOS) | ✅ rendered static | ⚠️ rarely | Unlikely to refresh without revisiting the tab. |
169
+
170
+ Known limitations (current as of Safari 26.3):
171
+
172
+ - **Favicons have their own cache.** Chrome keeps a favicon database, Firefox a `favicons.sqlite`, and **Safari a system-level icon cache** — none of which a normal *clear cache* touches, and WebKit even caches the "no icon" case. That is why a changed icon can linger for an existing tab. The plugin already mitigates this: it serves `base.svg` and the status endpoint with `Cache-Control: no-store`, bundles a freshness query (`?t=Date.now()`) on its fetches, and replaces the `<link rel=icon>` node on each state change.
173
+ - **Safari renders SVG favicons but ignores their internal CSS** — no `@media`, no `prefers-color-scheme`, no CSS animation. So all recoloring must be baked into each frame's markup (which the plugin does) rather than driven by CSS variables.
174
+ - **`data:`-URI SVG favicons are unreliable in Safari** (WebKit bug 236616, still open; reproduced on Safari 17.6). The plugin currently builds each frame as a `data:image/svg+xml` URI, so on Safari the tab icon may not render at all — the biggest known gap.
175
+ - **Dynamic JS updates in Safari are hit-or-miss;** they may require a reload, and Safari "locks onto" the first icon it sees. There is no guaranteed, spec-supported way to swap a favicon live in Safari today.
176
+ - **Pinned-tab icon (`<link rel="mask-icon">`) uses its own cache**, separate from the regular favicon, and is a single-colour silhouette tinted by the `color` attribute — macOS + pinned-tab only, read at page load, not live.
177
+
178
+ Full mechanics with sources (WebKit bugs, Stack Overflow, browser-engineering blogs) and a recommended path toward smoother Safari colour changes live in [`docs/safari-favicon-research.md`](./docs/safari-favicon-research.md).
179
+
133
180
  ## Caveats
134
181
 
135
- - Favicon SVG CSS animations do not run inside the browser's tab UI — all effects are produced in JavaScript by rebuilding the data-URI each frame. This is a deliberate, zero-dependency design.
182
+ - Favicon SVG CSS animations do not run inside the browser's tab UI — all effects are produced in JavaScript by rebuilding the data-URI each frame. This is a deliberate, zero-dependency design. (The animated previews in this README are demo assets for illustration only — the favicon itself is JS-animated.)
183
+ - Favicon behavior differs by browser, and Safari is the most limited — see [Browser support & known limitations](#browser-support--known-limitations).
136
184
  - The base template must keep its `__COLOR__` placeholder in the `#p { fill: … }` rule; the browser replaces that token to color each frame.
137
185
  - The plugin runs in the **host** plane; it must be mounted into a profile's composition, not a session-scoped agent preset.
138
186
  - File reads go through the `fs` service with the configured `iconsDir` as `cwd`. Make sure that path is readable under your deployment's sandbox policy.
139
187
 
140
188
  ## License
141
189
 
142
- MIT
190
+ MIT
package/README.zh.md CHANGED
@@ -1,43 +1,69 @@
1
1
  # dsh-web-icon-indicator
2
2
 
3
- > 📖 [English](README.md) · [中文文档](README.zh.md)
3
+ > 📖 [English](README.md) · [中文文档](README.zh.md) · 📝 [更新记录](CHANGELOG.md) · [Releases](https://github.com/waknow/dsh-web-icon-indicator/releases)
4
4
 
5
- 浏览器标签页 favicon 实时反映 DSH 会话状态——待机 / 运行中 / 提问 / 完成——让你在标签页置于后台时也能一眼看出是否有会话需要处理。
5
+ [![awesome · DSH plugin](https://awesome-dsh-plugin.com/badge.svg)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
6
6
 
7
- ## 状态
7
+ 浏览器标签页 favicon 实时反映 DSH 会话状态——`待机` / `运行中` / `提问` / `完成`——让你在标签页置于后台时也能一眼看出是否有会话需要处理。
8
+
9
+ ## ✨ 功能特性
10
+
11
+ - **标签页 favicon 实时反映会话状态** —— 浏览器标签页图标同步 `idle` / `running` / `asking` / `done`(聚合优先级:`asking` > `running` > `done` > `idle`),后台标签页也能一眼看清 agent 们在做什么——包括 `ask_user_question` 提问,以及审批 / 沙箱提权等待(这两种情况会把图标钉在 `asking` 态)。
12
+ - **单个 SVG,浏览器内上色与动画** —— 只内置一个鲸鱼模板([`icons/base.svg`](./icons/base.svg));每个状态、颜色、每一帧都在客户端渲染为 `data:image/svg+xml` URI,不再有按颜色拆分的图标文件。
13
+ - **六种内置特效** —— `static`(静止)、`blink`(闪烁)、`breath`(呼吸)、`rainbow`(彩虹)、`heartbeat`(心跳)、`bounce`(跳动),全部由 JavaScript 驱动(favicon 不会播放 SVG CSS 动画)。
14
+ - **完全可配置、即时生效** —— 每个状态的颜色、特效、周期,以及提问 / 完成驻留时长,改动约 1 秒内同步到已打开的标签页——无需刷新、无需重启。
15
+ - **内置配置 UI,无需手写 YAML** —— DSH 设置页里的 *标签页图标指示器* 卡片可编辑整套配置,带实时色块预览,保存后自动写入 `settings.yaml`(路径见下)。
16
+ - **后台标签页与重启抗性** —— 隐藏标签页中 `requestAnimationFrame` 被暂停时,动画态会按墙钟时间补帧;状态轮询还能扛住 host 重启,图标自动恢复。
17
+
18
+ ### 🛠 配置界面——怎么找到它
19
+
20
+ | # | 步骤 |
21
+ | --- | --- |
22
+ | 1 | 打开 DSH Web GUI,进入 **设置**。 |
23
+ | 2 | 在 **插件** 选项卡中,打开 **插件配置**。 |
24
+ | 3 | 找到 **标签页图标指示器(Favicon indicator)** 卡片。 |
25
+ | 4 | 展开某个状态行(`idle` / `running` / `asking` / `done`),编辑 **特效**、**颜色**(每个色块即原生取色器),以及**周期(毫秒)**——仅动画状态显示,静态状态无周期;用 **提问驻留** / **完成驻留** 调整两个时长。 |
26
+
27
+ 改动会通过 settings 传输层持久化到 profile 的 `settings.yaml`,约 1 秒内应用到已打开的标签页——无需刷新、无需重启。完整键说明见 [配置](#配置)。
28
+
29
+ ## 🎬 默认配置,可视化
30
+
31
+ 四个默认状态在浏览器标签页中的实际效果(`asking` 那条鲸鱼真的在闪烁):
32
+
33
+ <p align="center">
34
+ <img src="assets/states-default.svg" width="420" alt="默认状态:idle 深色鲸鱼、running 黄色、asking 红/黄闪烁、done 绿色">
35
+ </p>
8
36
 
9
37
  | 状态 | 颜色(默认) | 特效(默认) |
10
38
  | --- | --- | --- |
11
- | `idle` 待机 | `#1a1a1a`(深色鲸鱼) | `static` |
12
- | `running` 运行中 | `#FACC15`(黄色) | `static` |
13
- | `asking` 提问 | `#E5484D` + `#FACC15` | `blink`(400ms) |
14
- | `done` 完成 | `#22A06B`(绿色) | `doneHoldMs` `static`,随后回到 `idle` |
39
+ | `idle` 待机 | `#1a1a1a`——深色鲸鱼 | `static` |
40
+ | `running` 运行中 | `#FACC15`——黄色 | `static` |
41
+ | `asking` 提问 | `#E5484D` `#FACC15`——红/黄 | `blink`(400ms) |
42
+ | `done` 完成 | `#22A06B`——绿色 | `static`,保持 `doneHoldMs` 后回到 `idle` |
15
43
 
16
- 插件只内置**一个**基础鲸鱼 SVG([`icons/base.svg`](./icons/base.svg)),在浏览器中动态上色/加动画——不再有按颜色拆分的图标文件。每个状态的颜色和特效都可通过 [配置](#配置) 自由设置。
44
+ ## 全部特效,动画演示
17
45
 
18
- ### 动态颜色与动画
46
+ 下面每个预览都是真实的鲸鱼路径,按插件实际渲染方式做动画(预览是自包含的动画 SVG,在浏览器里直接播放):
19
47
 
20
- 所有状态共用同一条鲸鱼路径,只有填充色(以及可选的动画)不同。由于 favicon 是普通图片,SVG 内部的 CSS 动画不会在标签页执行——注入的浏览器脚本把每一帧构建成 `data:image/svg+xml,…` URI:在每个 `requestAnimationFrame` 周期替换 `__COLOR__` 占位符为配置颜色,动画类特效则在 `<g transform>` 中注入缩放/位移。可选特效:
21
-
22
- | 特效 | 效果 |
23
- | --- | --- |
24
- | `static` | 纯色单帧,无动画——使用 `colors[0]` |
25
- | `blink` | `colors[0]` `colors[1]` 之间按 `speed` 切换(缺省时自动推导更深的第二色) |
26
- | `breath` | 在 `colors[0]` `colors[1]` 之间平滑呼吸过渡(缺省时推导) |
27
- | `rainbow` | `colors[0]` 为起始色相,在 `speed` 内绕色轮循环 |
28
- | `heartbeat` | 在 `speed` 内做「lub-dub」式的尖锐缩放脉冲——颜色为 `colors[0]` |
29
- | `bounce` | 鲸鱼在 `speed` 内上下跳动——颜色为 `colors[0]` |
48
+ | 特效 | 效果 | 预览 |
49
+ | --- | --- | --- |
50
+ | `static` | 纯色单帧,无动画——使用 `colors[0]` | <img src="assets/effects/static.svg" width="56" alt="static 特效预览"> |
51
+ | `blink` | `colors[0]` ⇄ `colors[1]` 之间按 `speed` 切换(缺省时自动推导更深的第二色) | <img src="assets/effects/blink.svg" width="56" alt="blink 特效预览"> |
52
+ | `breath` | `colors[0]` 与 `colors[1]` 之间平滑呼吸过渡(缺省时推导) | <img src="assets/effects/breath.svg" width="56" alt="breath 特效预览"> |
53
+ | `rainbow` | `colors[0]` 为起始色相,在 `speed` 内绕色轮循环 | <img src="assets/effects/rainbow.svg" width="56" alt="rainbow 特效预览"> |
54
+ | `heartbeat` | 在 `speed` 内做「lub-dub」式的尖锐缩放脉冲——颜色为 `colors[0]` | <img src="assets/effects/heartbeat.svg" width="56" alt="heartbeat 特效预览"> |
55
+ | `bounce` | 鲸鱼在 `speed` 内上下跳动——颜色为 `colors[0]` | <img src="assets/effects/bounce.svg" width="56" alt="bounce 特效预览"> |
30
56
 
31
- 完整自包含 demo(无构建、无依赖)在 [`demo/dynamic-color.html`](./demo/dynamic-color.html):选择状态 + 特效并实时改色,即可看到标签页 favicon 即时变化。
57
+ 想改颜色并实时观察标签页 favicon 变化?打开自包含 demo[`demo/dynamic-color.html`](./demo/dynamic-color.html))——选择状态 + 特效并实时改色,favicon 即时更新(无构建、无依赖)。
32
58
 
33
59
  ## 安装
34
60
 
35
61
  这是一个标准 DSH bundle 插件。安装到 `web` profile(GUI/TUI profile 会自动通过 cordis patch 层加载):
36
62
 
37
- 从 npm 安装(**推荐**——已发布为 `dsh-web-icon-indicator@0.1.0`):
63
+ 从 npm 安装(**推荐**):
38
64
 
39
65
  ```bash
40
- dsh plugin --profile web add dsh-web-icon-indicator
66
+ dsh plugin --profile web add dsh-web-icon-indicator@latest
41
67
  ```
42
68
 
43
69
  从 Git 源码安装:
@@ -124,13 +150,35 @@ config:
124
150
  - 权限 / **沙箱拦截**等待同样会显示为 `asking`:当 agent 命中沙箱拒绝并请求提权(`sandbox_permissions` + `justification`),或其他工具需要征得同意时,审批服务会先写入一条 `approval/asked` 会话事件并阻塞 agent,直到你做出决定。插件监听 `session/event`(并以实时会话日志的权威折叠作为兜底)在整个等待期间将会话置为 `asking` 状态,收到 `approval/decided` 后清除。
125
151
  - 浏览器脚本每秒轮询 `/dsh-web-icon-status.json`,首次获取 `base.svg`,然后每个 `requestAnimationFrame` 周期把 favicon 重建为 `data:image/svg+xml,…` URI——把 `__COLOR__` 占位符替换为状态配置的颜色,并应用该状态配置的特效。状态响应还会携带当前的每状态视觉配置,因此设置保存后约 1 秒内(下一个轮询 tick)即同步到已打开的标签页,无需刷新。浏览器不会播放 SVG favicon 的 CSS 动画,所以一切动画都由 JS 驱动。由于浏览器在**隐藏(后台)标签页会暂停 `requestAnimationFrame`**,轮询还会为动画态补绘一帧按墙钟时间计算的画面——后台标签页保持粗粒度动画(约每 1 秒)而不会冻结,切回前台后恢复满速动画。轮询还能**扛住 host 重启**:瞬时请求失败时先还原原始图标,并在下一个 tick 重试(SPA 原地重连,无需手动刷新图标即可恢复)。
126
152
 
153
+ ## 浏览器支持与已知限制
154
+
155
+ favicon 本质是一张图片,浏览器不会在标签页 UI 里运行 SVG 自带的 CSS/JS 动画——每一帧都在本插件里由 JavaScript 生成。
156
+
157
+ | 浏览器 | SVG favicon | 逐帧换色 / 换特效 | 说明 |
158
+ | --- | --- | --- | --- |
159
+ | Chrome / Edge | ✅ | ✅ 顺滑 | 实时重读 `<link rel=icon>`;`data:` URI 的 SVG 可用。 |
160
+ | Firefox | ✅ | ✅ 顺滑 | 对 SVG favicon 支持良好(且会响应其 `prefers-color-scheme`,本插件未使用)。 |
161
+ | Safari(macOS) | ✅ 渲染为静态图 | ⚠️ 尽力而为 | 忽略 SVG 内嵌 CSS;favicon 缓存激进。 |
162
+ | Safari(iOS) | ✅ 渲染为静态图 | ⚠️ 基本不刷 | 通常需重新访问标签页才刷新。 |
163
+
164
+ 已知限制(截至 Safari 26.3):
165
+
166
+ - **favicon 有专属缓存。** Chrome 用 favicon 数据库、Firefox 用 `favicons.sqlite`、**Safari 用系统级图标缓存**——清普通缓存都清不掉,WebKit 甚至会把「无图标」这一状态也缓存起来。这就是改了图标后,已打开的标签页还可能显示旧图标的原因。本插件已通过「给 `base.svg` 与状态端点设置 `Cache-Control: no-store`、请求携带 freshness 参数(`?t=Date.now()`)、每次切换状态时重建 `<link rel=icon>` 节点」来缓解。
167
+ - **Safari 渲染 SVG favicon,但忽略其内部 CSS**——不支持 `@media`、`prefers-color-scheme`、CSS 动画。所以所有上色必须烘焙进每一帧的标记(本插件正是这么做的),而不能依赖 CSS 变量。
168
+ - **`data:` URI 的 SVG favicon 在 Safari 不可靠**(WebKit bug 236616,仍未关闭;Safari 17.6 复现)。本插件当前每帧都生成 `data:image/svg+xml` URI,因此在 Safari 上标签页图标可能完全不显示——这是最大的已知缺口。
169
+ - **Safari 的动态 JS 更新为 hit-or-miss**,可能需要刷新一次;Safari 会「锁定」它首次看到的图标。目前没有保证可靠的、符合规范的手段能在 Safari 中实时更换 favicon。
170
+ - **固定标签页图标(`<link rel="mask-icon">`)使用独立缓存**,与普通 favicon 分开;它是靠 `color` 属性着色的**单色剪影**——仅 macOS + 固定标签页、页面加载时读取一次、并非实时。
171
+
172
+ 完整机制与来源(WebKit bugs、Stack Overflow、浏览器工程博客)以及让 Safari 更顺滑变色/切换的推荐路径见 [`docs/safari-favicon-research.md`](./docs/safari-favicon-research.md)。
173
+
127
174
  ## 已知限制
128
175
 
129
- - favicon 的 SVG CSS 动画在浏览器标签页 UI 中不会运行——所有特效都由 JavaScript 每帧重建 data-URI 实现,这是零依赖设计的刻意取舍。
176
+ - favicon 的 SVG CSS 动画在浏览器标签页 UI 中不会运行——所有特效都由 JavaScript 每帧重建 data-URI 实现,这是零依赖设计的刻意取舍。(本文档中的动画预览只是演示素材——真实 favicon 的动画始终由 JS 驱动。)
177
+ - favicon 行为因浏览器而异,其中 Safari 限制最多——见 [浏览器支持与已知限制](#浏览器支持与已知限制)。
130
178
  - `base.svg` 模板必须保留 `#p { fill: … }` 规则中的 `__COLOR__` 占位符;浏览器会替换该标记为每帧上色。
131
179
  - 插件运行在 **host** 平面,必须挂载进 profile 的组合配置,不能作为会话级 agent preset。
132
180
  - 文件读取走 `fs` 服务,以配置的 `iconsDir` 为 `cwd`。请确保该路径在部署环境的沙箱策略下可读。
133
181
 
134
182
  ## 许可
135
183
 
136
- MIT
184
+ MIT
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96" role="img" aria-label="blink effect: toggles between red and yellow"><style>.chip{fill:#ffffff;stroke:#d0d7de;stroke-width:1.5}@keyframes bk{0%,49.9%{fill:#E5484D}50%,100%{fill:#FACC15}}#p{animation:bk .8s steps(1) infinite}</style><rect class="chip" x="4" y="4" width="88" height="88" rx="20"/><g transform="translate(23,23)"><path id="p" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96" role="img" aria-label="bounce effect: whale hops up and down"><style>.chip{fill:#ffffff;stroke:#d0d7de;stroke-width:1.5}@keyframes bo{0%,100%{transform:translateY(0)}50%{transform:translateY(-9px)}}#w{animation:bo 1.2s ease-in-out infinite}#p{fill:#FACC15}</style><rect class="chip" x="4" y="4" width="88" height="88" rx="20"/><g transform="translate(23,23)"><g id="w"><path id="p" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96" role="img" aria-label="breath effect: pulsates between green shades"><style>.chip{fill:#ffffff;stroke:#d0d7de;stroke-width:1.5}@keyframes br{0%,100%{fill:#22A06B}50%{fill:#157A4E}}#p{animation:br 1.2s ease-in-out infinite}</style><rect class="chip" x="4" y="4" width="88" height="88" rx="20"/><g transform="translate(23,23)"><path id="p" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96" role="img" aria-label="heartbeat effect: sharp lub-dub scale pulse"><style>.chip{fill:#ffffff;stroke:#d0d7de;stroke-width:1.5}@keyframes hb{0%,100%{transform:scale(1)}12%{transform:scale(1.18)}26%{transform:scale(1)}40%{transform:scale(1.09)}55%{transform:scale(1)}}#w{transform-box:fill-box;transform-origin:center;animation:hb 1.2s ease-in-out infinite}#p{fill:#E5484D}</style><rect class="chip" x="4" y="4" width="88" height="88" rx="20"/><g transform="translate(23,23)"><g id="w"><path id="p" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96" role="img" aria-label="rainbow effect: hue cycles around the color wheel"><style>.chip{fill:#ffffff;stroke:#d0d7de;stroke-width:1.5}@keyframes rb{from{filter:hue-rotate(0deg);-webkit-filter:hue-rotate(0deg)}to{filter:hue-rotate(360deg);-webkit-filter:hue-rotate(360deg)}}#p{fill:#E5484D;animation:rb 1.2s linear infinite}</style><rect class="chip" x="4" y="4" width="88" height="88" rx="20"/><g transform="translate(23,23)"><path id="p" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96" role="img" aria-label="static effect: single colored frame"><style>.chip{fill:#ffffff;stroke:#d0d7de;stroke-width:1.5}</style><rect class="chip" x="4" y="4" width="88" height="88" rx="20"/><g transform="translate(23,23)"><path d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z" fill="#FACC15"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="384" height="112" viewBox="0 0 384 112" role="img" aria-label="Default states: idle dark, running yellow, asking red-yellow blinking, done green"><style>.chip{fill:#ffffff;stroke:#d0d7de;stroke-width:1.5}#p1{fill:#1a1a1a}#p2{fill:#FACC15}#p4{fill:#22A06B}@keyframes bk{0%,49.9%{fill:#E5484D}50%,100%{fill:#FACC15}}#p3{animation:bk .8s steps(1) infinite}.lbl{font:600 12px system-ui,-apple-system,Segoe UI,sans-serif;fill:#59636e;text-anchor:middle}</style><rect class="chip" x="8" y="8" width="80" height="80" rx="20"/><rect class="chip" x="104" y="8" width="80" height="80" rx="20"/><rect class="chip" x="200" y="8" width="80" height="80" rx="20"/><rect class="chip" x="296" y="8" width="80" height="80" rx="20"/><g transform="translate(23,23)"><path id="p1" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g><g transform="translate(119,23)"><path id="p2" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g><g transform="translate(215,23)"><path id="p3" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g><g transform="translate(311,23)"><path id="p4" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/></g><text class="lbl" x="48" y="102">idle</text><text class="lbl" x="144" y="102">running</text><text class="lbl" x="240" y="102">asking</text><text class="lbl" x="336" y="102">done</text></svg>
@@ -0,0 +1,114 @@
1
+ # Safari favicon 机制研究:动态更换与 SVG 动态变色可行性
2
+
3
+ > **目的**:判断能否在 Safari 里为浏览器标签页 favicon 做「动态更换」,以及能否实现「SVG 动态变色」;
4
+ > 结论用于指导 [`dsh-web-icon-indicator`](../README.md) 的下一步实现。
5
+ >
6
+ > **资料截至** Safari 26.3(2026)。文末列出来源。文中涉及本插件的句子,均以 `lib/index.js` 当前实现为准。
7
+
8
+ ## 1. 结论速览
9
+
10
+ | 问题 | 结论 |
11
+ | --- | --- |
12
+ | Safari 能否渲染 SVG favicon? | ✅ 能,但**只当作静态图片**,完全忽略 SVG 内部的 CSS |
13
+ | 能否「动态更换」favicon(JS 改 link)? | ⚠️ 能,但**只算尽力而为**——实时性、确定性都不保证 |
14
+ | 能否实现「SVG 动态变色」? | 不能靠 CSS(currentColor / 变量 / media query);只能**每个状态生成一份新 SVG 再切换** |
15
+ | 本插件当前用的 `data:` URI 在 Safari? | ❌ 不可靠(WebKit bug 236616 未关闭) |
16
+ | Safari 原生的「动态颜色」机制 | `<link rel="mask-icon">`——仅 macOS 固定标签页、单色剪影、有独立缓存 |
17
+
18
+ 一句话结论:**Chrome / Firefox / Edge 能顺滑做到逐帧换色;Safari 只能「外部 SVG + cache-busting + 重建 `<link>` 节点 + no-store」尽力做到,且常常要刷新一次才生效。** 这是浏览器层面的硬限制,插件无法完全绕开。
19
+
20
+ ## 2. Safari 的 favicon 机制(关键点)
21
+
22
+ ### 2.1 favicon 有独立于 HTTP 缓存的「专属缓存」,Safari 是系统级图标缓存
23
+
24
+ - **Chrome**:独立的 favicon 数据库(清缓存、强刷、重启都可能清不掉)。
25
+ - **Firefox**:`favicons.sqlite`,绑在历史记录条目上。
26
+ - **Safari**:**系统级图标缓存(system-level icon cache)**。
27
+
28
+ 这意味着「改了 favicon 但老标签页不更新」是常态,绝非巧合。更糟的是 WebKit 会**缓存「这个网站没有 favicon」这一事实本身**,以避免每次加载都去探测——见 [WebKit bug 7069](https://www2.webkit.org/show_bug.cgi?id=7069)。
29
+
30
+ 所以正确的刷新手段不是清缓存,而是**改变 favicon 的 URL**(cache-busting),比如加上 `?v=<签名>`。
31
+
32
+ ### 2.2 Safari 能渲染 SVG favicon,但忽略其内部所有 CSS
33
+
34
+ 现代 Safari 支持一个真实的 `.svg` favicon 文件(`type="image/svg+xml"`),但把它当**普通图片光栅化**:
35
+
36
+ - 不支持里面的 `@media`、`prefers-color-scheme`、CSS 动画。
37
+ - 已确证:[WebKit bug 309949《SVG favicons don't respect media query overrides, notably for dark mode》](https://www2.webkit.org/show_bug.cgi?id=309949),**状态 NEW,作者注明「Safari 26.3 复现」**——Safari 只渲染基础(浅色)版本。
38
+ - [pawelgrzybek.com](https://pawelgrzybek.com/svg-favicons-that-respect-theme-preference/)(2026-03)实测:Chrome 会处理 favicon 里的 media query(但要刷新才生效),Firefox 表现最好,**Safari 完全忽略**。
39
+
40
+ > ⚠️ 结论:**永远不要指望用 SVG 里的 `currentColor` / CSS 变量 / media query / 内嵌动画来给 favicon 变色。** favicon 本质是图片,Safari 连它内部的 CSS 都不跑。这也印证了本插件注释里那句「favicons 不播 SVG CSS 动画,一切 motion 必须由 JS 生成」是对的。
41
+
42
+ ### 2.3 `data:` 形式的 SVG favicon 在 Safari 不可靠(很可能完全不显示)
43
+
44
+ [WebKit bug 236616《Data URI favicon doesn't seem to work》](https://www2.webkit.org/show_bug.cgi?id=236616) 至今 **NEW**,评论区明确说 **macOS 14.6.1 / Safari 17.6 仍不工作**,且用的就是 `data:` URI 的 SVG;还有「可能是 Safari 15.3 回归」的怀疑。
45
+
46
+ > ⚠️ **这直接命中本插件的软肋**:`lib/index.js` 的 `frameUri()` 每一帧都生成 `data:image/svg+xml,…`。在 Chrome/Firefox 没问题,但在 **Safari 里整个图标可能都不显示**。这是「要改」的首要问题。
47
+
48
+ ### 2.4 动态更换(JS 改 `<link rel=icon>`)的真实表现
49
+
50
+ 跨浏览器通用手法:
51
+ 1. **给 URL 加 cache-busting 参数**(`?v=<epoch>`)——改变 URL 即绕过专属 favicon 缓存,Safari 也会重新拉取。
52
+ 2. **用 JS 替换 / 重建 `<link>` 节点**(有些浏览器只在页面加载时读一次)——本插件已有 `setHrefFresh()` 做 `replaceChild`,方向正确。
53
+ 3. 给 favicon 资源加 `Cache-Control: no-store`。
54
+
55
+ **但 Safari 是最不配合的**:[SO 79637210](https://stackoverflow.com/questions/79637210) 明确说「动态 JS 更新在 Safari 里是 hit-or-miss,尤其在 reload 或已缓存的情况下」,「Safari 会锁定最初那个版本,强刷都不一定换」,并给出结论:**「目前没有一个完全可靠、符合规范的方式来动态改 Safari 的 favicon。」** iOS Safari 更糟(常常要重新导航)。
56
+
57
+ ### 2.5 Safari 原生的「动态颜色」机制:`mask-icon`(固定标签页)
58
+
59
+ `<link rel="mask-icon" href="…svg" color="#…">` 用于 macOS 的 **Pinned Tab(固定标签页)**,是 Safari 唯一允许你「不改 SVG、只改颜色」的机制——用 `color` 属性直接指定单色。但它限制很多:
60
+
61
+ - 仅 **macOS**,不支持 iOS。
62
+ - 仅 **固定 / 置顶标签页**;普通标签页不适用。
63
+ - **纯单色剪影**:浏览器把 SVG 当黑色 mask 用,不支持全彩。
64
+ - **有自己独立的缓存**([dev.to](https://dev.to/jamiepark-design/how-browsers-actually-load-favicons-and-why-yours-wont-update-172c)):它跟普通 favicon 分开缓存,改掉 URL 也要加 `?v=` 才会刷新。
65
+ - `color` 在**页面加载时**读取;JS 运行时改属性**不一定重绘**。
66
+
67
+ 所以它适合「每个页面加载设一次单色」,**不适合按帧 / 按状态实时驱动**。可作补充,不可作主方案。
68
+
69
+ ### 2.6 图标选择与 ICO 偏好
70
+
71
+ [WebKit bug 315573《Handle favicon, apple-touch-icon and web manifest icons correctly for pixel-perfect rendering》](https://www2.webkit.org/show_bug.cgi?id=315573)(**NEW,2026-05 更新**)说明:
72
+
73
+ - **Safari 的 UI 进程**才真正决定为某个用途(标签页 favicon / web clip / Web 应用)下载哪一个图标条目。
74
+ - 排序完全按**静态元数据**(`LinkIconType`、声明的 `sizes`、是否 precomposed),**不看** `devicePixelRatio` 或渲染目标。
75
+ - 存在「Favicon: prefers ICO, picks largest entry」的怪癖:一个多尺寸的 `.ico` 可能被当成 16px 的小图标,让同尺寸的 32×32 PNG 反而胜出。
76
+
77
+ ## 3. 可行性矩阵(各浏览器)
78
+
79
+ | 浏览器 | SVG favicon | 逐帧换色 / 换特效 | 动态更新可靠性 | 关键约束 |
80
+ | --- | --- | --- | --- | --- |
81
+ | Chrome / Edge | ✅ | ✅ 顺滑 | ✅ 高 | 每次改 href 都实时重绘;`data:` URI 可用 |
82
+ | Firefox | ✅ | ✅ 顺滑 | ✅ 高 | 对 SVG favicon 的 `prefers-color-scheme` 处理最好(本插件未用) |
83
+ | Safari(macOS) | ✅ 静态 | ⚠️ 尽力而为 | ⚠️ 中低 | 忽略内嵌 CSS;`data:` URI 不可靠;顽固缓存 |
84
+ | Safari(iOS) | ✅ 静态 | ❌ 基本不可靠 | ❌ 低 | 通常需重新访问 / 重新添加到主屏 |
85
+
86
+ ## 4. 对本插件的落地建议
87
+
88
+ 1. **首选改动:把每帧的 `data:image/svg+xml` 换成 Safari 更认的载体**,按可靠性排序:
89
+ - **Blob URL**:`URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }))` —— 每状态一个「外部」URL,既避开 `data:` 限制,又不用每帧打网络请求,比 `data:` 更容易被 Safari 当 favicon 处理。(公开资料**未查到 Safari 对 `blob:` favicon 的 100% 确认**,需真机验证。)
90
+ - **真 `.svg` URL + `?color=…&v=…`**:让服务端按 color 返回 SVG(需要新增路由),href 指向真实 URL 并 cache-busting。缺点:动画每帧要发请求;或退而求其次,生成**有限个预置颜色变体**(不像 60fps 那么贵)。
91
+ 2. **保留现有抗性措施**:`setHrefFresh()`(替换 `<link>` 节点)、`Cache-Control: no-store`、状态请求与 base.svg 请求都带 freshness 查询参数(`?t=`)——这些都已在 `lib/index.js` 里。
92
+ 3. **可选补充 `mask-icon`** 覆盖 macOS 固定标签页:`<link rel="mask-icon" href="/…/whale.svg?v=…" color="#FACC15">`。但接受它「macOS 限定、单色剪影、固定标签、加载时读一次颜色、独立缓存」的局限。
93
+ 4. **设好预期**:Chrome/Firefox/Edge 实时顺滑;**Safari 上「能变色、但可能要刷新一次」**。文档要把这层限制讲清楚,避免用户误以为 Safari 也能像 Chrome 一样逐帧实时。
94
+
95
+ ## 5. 浏览器支持与局限汇总(写入 README 的部分)
96
+
97
+ - **favicon 有专属缓存**:Chrome favicon 数据库、Firefox `favicons.sqlite`、**Safari 系统级图标缓存**,清普通缓存清不掉;WebKit 连「无图标」状态都缓存。插件已用「`no-store` + 每帧新鲜 query + 重建 `<link>`」缓解。
98
+ - **Safari 渲染 SVG 但不跑其 CSS**:无 `@media` / `prefers-color-scheme` / CSS 动画,所有上色必须在每帧标记里烘焙。
99
+ - **`data:` URI 的 SVG favicon 在 Safari 不可靠**(WebKit bug 236616):插件目前每帧都用 `data:` URI,Safari 上可能不显示——最大已知缺口。
100
+ - **Safari 动态 JS 更新为 hit-or-miss**,可能需要刷新;没有保证可靠的规范级手段。
101
+ - **`mask-icon`(固定标签)独立缓存**、单色剪影、macOS 限定、加载时读一次颜色、非实时。
102
+
103
+ ## 6. 参考来源
104
+
105
+ | 来源 | 要点 | 日期/状态 |
106
+ | --- | --- | --- |
107
+ | [WebKit bug 236616 — Data URI favicon doesn't seem to work](https://www2.webkit.org/show_bug.cgi?id=236616) | `data:` URI favicon(含 SVG)不可用;Safari 17.6 仍复现 | **NEW** |
108
+ | [WebKit bug 309949 — SVG favicons don't respect media query overrides](https://www2.webkit.org/show_bug.cgi?id=309949) | Safari 渲染 SVG favicon 但忽略其内嵌 CSS / media query | **NEW**,Safari 26.3 复现 |
109
+ | [WebKit bug 315573 — favicon / apple-touch-icon / manifest 选择](https://www2.webkit.org/show_bug.cgi?id=315573) | Safari UI 进程决定取哪个图标;按静态元数据排序;ICO 偏好怪癖 | **NEW**,2026-05 |
110
+ | [WebKit bug 7069 — favicon 缓存与「无图标」状态缓存](https://www2.webkit.org/show_bug.cgi?id=7069) | WebKit 缓存旧图标,甚至缓存「无图标」事实以省去探测开销 | RESOLVED/DUPLICATE |
111
+ | [dev.to — How Browsers Actually Load Favicons](https://dev.to/jamiepark-design/how-browsers-actually-load-favicons-and-why-yours-wont-update-172c) | 各浏览器专属 favicon 缓存;version-busting `?v=` 技巧;mask-icon 有独立缓存 | 2026-06-23 |
112
+ | [pawelgrzybek.com — SVG favicons that respect theme preference](https://pawelgrzybek.com/svg-favicons-that-respect-theme-preference/) | Chrome/Firefox/Safari 三款对 SVG favicon + media query 的对比 | 2026-03-14 |
113
+ | [SO 79637210 — Safari 动态切换 dark/light favicon](https://stackoverflow.com/questions/79637210) | Safari 不支持 link 上的 media query;动态 JS 更新 hit-or-miss;「锁定」首个图标 | 近期 |
114
+ | [nesin.io — Fix Safari showing old favicon](https://nesin.io/blog/fix-safari-showing-old-favicon-issue) | Safari 激进缓存 favicon;cache-busting `?v=x.x` 或改文件名 | 2023-05 |
package/icons/base.svg CHANGED
@@ -2,5 +2,5 @@
2
2
  <style>
3
3
  #p { fill: __COLOR__; }
4
4
  </style>
5
- <path id="p" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/>
5
+ <path id="p" fill="__COLOR__" d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z"/>
6
6
  </svg>
package/lib/client.js CHANGED
@@ -33,6 +33,15 @@ window.__ModuleLoader__.load({
33
33
  var STATE_NAMES = ["idle", "running", "asking", "done"];
34
34
  var EFFECT_NAMES = ["static", "blink", "breath", "rainbow", "heartbeat", "bounce"];
35
35
  var TOP_NUMBERS = ["askingHoldMs", "doneHoldMs"];
36
+ // Localized label key per effect identifier (value stays the identifier).
37
+ var EFFECT_LABEL_KEYS = {
38
+ static: "effectStatic",
39
+ blink: "effectBlink",
40
+ breath: "effectBreath",
41
+ rainbow: "effectRainbow",
42
+ heartbeat: "effectHeartbeat",
43
+ bounce: "effectBounce",
44
+ };
36
45
 
37
46
  /** Cordis fiber services this browser plugin injects. */
38
47
  var inject = ["slots", "settingsScope", "locale"];
@@ -58,7 +67,14 @@ window.__ModuleLoader__.load({
58
67
  colors: "Colors",
59
68
  colorsHint: "Comma-separated hex colors; the first is the primary color.",
60
69
  speed: "Cycle (ms)",
61
- speedHint: "Per-state cycle length; blank keeps the current value.",
70
+ speedHint: "Per-state cycle length; blank keeps the current value. Static states have no cycle.",
71
+ effectStatic: "Static",
72
+ effectBlink: "Blink",
73
+ effectBreath: "Breath",
74
+ effectRainbow: "Rainbow",
75
+ effectHeartbeat: "Heartbeat",
76
+ effectBounce: "Bounce",
77
+ editColor: "Edit color",
62
78
  unsaved: "Unsaved",
63
79
  readOnly: "This deployment stores settings read-only.",
64
80
  save: "Save",
@@ -87,7 +103,14 @@ window.__ModuleLoader__.load({
87
103
  colors: "颜色",
88
104
  colorsHint: "逗号分隔的十六进制颜色;第一个为主色。",
89
105
  speed: "周期(毫秒)",
90
- speedHint: "该状态的动画周期;留空保持当前值。",
106
+ speedHint: "该状态的动画周期;留空保持当前值。静态状态无周期。",
107
+ effectStatic: "静态",
108
+ effectBlink: "闪烁",
109
+ effectBreath: "呼吸",
110
+ effectRainbow: "彩虹",
111
+ effectHeartbeat: "心跳",
112
+ effectBounce: "弹跳",
113
+ editColor: "编辑颜色",
91
114
  unsaved: "未保存",
92
115
  readOnly: "本部署的设置为只读。",
93
116
  save: "保存",
@@ -124,6 +147,8 @@ window.__ModuleLoader__.load({
124
147
  inputRow: { display: "flex", alignItems: "center", gap: 8 },
125
148
  swatches: { display: "inline-flex", alignItems: "center", gap: 4, flex: "none" },
126
149
  swatch: { display: "inline-block", width: 14, height: 14, borderRadius: 4, border: "1px solid var(--dsw-alias-border-l2)" },
150
+ swatchLabel: { display: "inline-flex", position: "relative", width: 14, height: 14, cursor: "pointer" },
151
+ colorInput: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", opacity: 0, cursor: "pointer", border: 0, padding: 0, margin: 0 },
127
152
  badges: { display: "inline-flex", alignItems: "center", gap: 8 },
128
153
  badge: { whiteSpace: "nowrap", background: "var(--dsw-alias-bg-module-platform)", color: "var(--dsw-alias-label-secondary)", borderRadius: 999, padding: "1px 8px", fontSize: 11, fontWeight: 500, lineHeight: "17px" },
129
154
  footer: { borderTop: "1px solid var(--dsw-alias-border-l2)", display: "flex", justifyContent: "flex-end", alignItems: "center", gap: 8, padding: "12px 0 4px" },
@@ -153,11 +178,11 @@ window.__ModuleLoader__.load({
153
178
  }
154
179
 
155
180
  function LabeledField(props) {
156
- // props: { label, hint, invalidLabel, children }
181
+ // props: { label, hint, invalidLabel, htmlFor, children }
157
182
  return createElement(
158
183
  "div",
159
184
  { style: fieldStyle(props.separated) },
160
- createElement("label", { style: styles.label }, props.label),
185
+ createElement("label", { style: styles.label, htmlFor: props.htmlFor }, props.label),
161
186
  props.children,
162
187
  createElement("p", { style: props.invalid ? styles.invalidText : styles.hint }, props.invalid ? props.invalidLabel : props.hint)
163
188
  );
@@ -179,13 +204,14 @@ window.__ModuleLoader__.load({
179
204
  });
180
205
  return createElement(
181
206
  LabeledField,
182
- { label: props.label, hint: props.hint, invalid: props.invalid, invalidLabel: props.invalidLabel, separated: props.separated },
207
+ { label: props.label, hint: props.hint, invalid: props.invalid, invalidLabel: props.invalidLabel, separated: props.separated, htmlFor: props.id },
183
208
  props.trailing ? createElement("div", { style: styles.inputRow }, input, props.trailing) : input
184
209
  );
185
210
  }
186
211
 
187
212
  function SelectInput(props) {
188
213
  // props: { id, value, options, onChange, disabled }
214
+ // options: array of { value, label } — value stays the identifier, label is localized.
189
215
  return createElement(
190
216
  "select",
191
217
  {
@@ -196,7 +222,7 @@ window.__ModuleLoader__.load({
196
222
  onChange: function (event) { props.onChange(event.target.value); },
197
223
  },
198
224
  props.options.map(function (option) {
199
- return createElement("option", { key: option, value: option }, option);
225
+ return createElement("option", { key: option.value, value: option.value }, option.label);
200
226
  })
201
227
  );
202
228
  }
@@ -206,15 +232,54 @@ window.__ModuleLoader__.load({
206
232
  return createElement("span", { style: Object.assign({}, styles.dot, { background: props.color }) });
207
233
  }
208
234
 
209
- /** One swatch per parsed color; renders nothing while the text is unparseable. */
235
+ /** Expand a 3-digit hex (#abc) to 6-digit (#aabbcc) for <input type="color">. */
236
+ function to6(hex) {
237
+ var h = String(hex).replace("#", "");
238
+ if (h.length === 3) h = h.charAt(0) + h.charAt(0) + h.charAt(1) + h.charAt(1) + h.charAt(2) + h.charAt(2);
239
+ return "#" + h;
240
+ }
241
+
242
+ /**
243
+ * One swatch per parsed color. With an `onChange` it becomes a native
244
+ * color picker: each swatch is a label with a visually hidden
245
+ * <input type="color"> overlay; picking a color replaces that entry in the
246
+ * text. Without an onChange it renders a static preview (aria-hidden).
247
+ */
210
248
  function ColorSwatches(props) {
211
249
  var colors = parseColors(props.text);
212
250
  if (colors === null) return null;
251
+ if (typeof props.onChange !== "function") {
252
+ return createElement(
253
+ "span",
254
+ { style: styles.swatches, "aria-hidden": "true" },
255
+ colors.map(function (color) {
256
+ return createElement("span", { key: color, style: Object.assign({}, styles.swatch, { background: color }) });
257
+ })
258
+ );
259
+ }
213
260
  return createElement(
214
261
  "span",
215
- { style: styles.swatches, "aria-hidden": "true" },
216
- colors.map(function (color) {
217
- return createElement("span", { key: color, style: Object.assign({}, styles.swatch, { background: color }) });
262
+ { style: styles.swatches },
263
+ colors.map(function (color, index) {
264
+ return createElement(
265
+ "label",
266
+ {
267
+ key: index,
268
+ style: styles.swatchLabel,
269
+ title: props.label || "",
270
+ "aria-label": props.label || "",
271
+ },
272
+ createElement("input", {
273
+ type: "color",
274
+ value: to6(color),
275
+ onChange: function (event) {
276
+ var next = colors.slice();
277
+ next[index] = event.target.value;
278
+ props.onChange(next.join(", "));
279
+ },
280
+ }),
281
+ createElement("span", { style: Object.assign({}, styles.swatch, { background: color }) })
282
+ );
218
283
  })
219
284
  );
220
285
  }
@@ -322,16 +387,17 @@ window.__ModuleLoader__.load({
322
387
  var stateTitleKey = function (name) {
323
388
  return "state" + name.charAt(0).toUpperCase() + name.slice(1);
324
389
  };
325
- /** One-line collapsed summary: "blink · #E5484D ⇄ #FACC15 · 400ms" (draft-aware). */
390
+ /** One-line collapsed summary: "Blink · #E5484D ⇄ #FACC15 · 400ms" (draft-aware). */
326
391
  var stateSummary = function (name) {
327
- var parts = [stateFieldValue(name, "effect")];
392
+ var effect = stateFieldValue(name, "effect");
393
+ var parts = [t(EFFECT_LABEL_KEYS[effect] || effect)];
328
394
  var colorsText = stateFieldValue(name, "colors").trim();
329
395
  if (colorsText !== "") {
330
396
  var parsed = parseColors(colorsText);
331
397
  parts.push(parsed === null ? colorsText : parsed.join(" ⇄ "));
332
398
  }
333
399
  var speedText = stateFieldValue(name, "speed").trim();
334
- if (speedText !== "") parts.push(speedText + "ms");
400
+ if (speedText !== "" && effect !== "static") parts.push(speedText + "ms");
335
401
  return parts.join(" · ");
336
402
  };
337
403
  /** Primary color for the row's dot; transparent while the draft is unparseable. */
@@ -339,6 +405,14 @@ window.__ModuleLoader__.load({
339
405
  var parsed = parseColors(stateFieldValue(name, "colors"));
340
406
  return parsed === null ? "transparent" : parsed[0];
341
407
  };
408
+ /** Draft-aware: is this state currently static (thus has no cycle)? */
409
+ var isEffectStatic = function (name) {
410
+ return stateFieldValue(name, "effect") === "static";
411
+ };
412
+ /** Localized option list for the effect select (value stays the identifier). */
413
+ var effectOptions = EFFECT_NAMES.map(function (name) {
414
+ return { value: name, label: t(EFFECT_LABEL_KEYS[name]) };
415
+ });
342
416
 
343
417
  var dirty = Object.keys(drafts).length > 0;
344
418
 
@@ -473,7 +547,7 @@ window.__ModuleLoader__.load({
473
547
  invalid: failed === "invalidNumber",
474
548
  disabled: disabled,
475
549
  numeric: true,
476
- separated: true,
550
+ separated: false,
477
551
  value: fieldValue("askingHoldMs"),
478
552
  onChange: function (text) { edit("askingHoldMs", text); },
479
553
  }),
@@ -512,10 +586,10 @@ window.__ModuleLoader__.load({
512
586
  { style: styles.statePanel },
513
587
  createElement(
514
588
  LabeledField,
515
- { label: t("effect"), hint: t("effectHint"), invalid: false, invalidLabel: "", separated: false },
589
+ { label: t("effect"), hint: t("effectHint"), invalid: false, invalidLabel: "", separated: false, htmlFor: "plugin-config-icon-" + name + "-effect" },
516
590
  createElement(SelectInput, {
517
591
  id: "plugin-config-icon-" + name + "-effect",
518
- options: EFFECT_NAMES,
592
+ options: effectOptions,
519
593
  disabled: disabled,
520
594
  value: stateFieldValue(name, "effect"),
521
595
  onChange: function (text) { edit("states." + name + ".effect", text); },
@@ -530,10 +604,17 @@ window.__ModuleLoader__.load({
530
604
  disabled: disabled,
531
605
  separated: true,
532
606
  value: stateFieldValue(name, "colors"),
533
- trailing: createElement(ColorSwatches, { text: stateFieldValue(name, "colors") }),
607
+ trailing: createElement(ColorSwatches, {
608
+ text: stateFieldValue(name, "colors"),
609
+ label: t("editColor"),
610
+ onChange: function (text) { edit("states." + name + ".colors", text); },
611
+ }),
534
612
  onChange: function (text) { edit("states." + name + ".colors", text); },
535
613
  }),
536
- createElement(ValueInput, {
614
+ // `speed` is meaningless for a static effect (the browser paints
615
+ // one frame and never loops), so hide it — switching to an
616
+ // animated effect brings the field back.
617
+ isEffectStatic(name) ? null : createElement(ValueInput, {
537
618
  id: "plugin-config-icon-" + name + "-speed",
538
619
  label: t("speed"),
539
620
  hint: t("speedHint"),
package/lib/index.js CHANGED
@@ -154,6 +154,23 @@ const INJECTED_SCRIPT = `
154
154
  if (!link) return;
155
155
  try { link.setAttribute("href", uri); link.setAttribute("type", "image/svg+xml"); } catch (e) {}
156
156
  }
157
+ // Some browsers (notably Safari) do NOT repaint a dynamically-changed
158
+ // favicon — they only read the <link rel=icon> at page load. Replacing the
159
+ // element on a STATE change forces every browser to re-evaluate the favicon
160
+ // without touching the per-frame animation path (which mutates in place).
161
+ function setHrefFresh(uri) {
162
+ var link = linkEl();
163
+ if (!link) return;
164
+ try {
165
+ var parent = link.parentNode;
166
+ if (!parent) { link.setAttribute("href", uri); link.setAttribute("type", "image/svg+xml"); return; }
167
+ var fresh = document.createElement("link");
168
+ fresh.setAttribute("rel", "icon");
169
+ fresh.setAttribute("type", "image/svg+xml");
170
+ fresh.setAttribute("href", uri);
171
+ parent.replaceChild(fresh, link);
172
+ } catch (e) {}
173
+ }
157
174
 
158
175
  // ---- color helpers ------------------------------------------------------
159
176
  function hexToRgb(h) {
@@ -278,15 +295,18 @@ const INJECTED_SCRIPT = `
278
295
  ANIM_START = Date.now();
279
296
  // Paint the first frame synchronously so a state change shows even when
280
297
  // rAF never fires (e.g. the tab is hidden); the rAF loop then takes over
281
- // and drives the effect when the tab is visible.
298
+ // and drives the effect when the tab is visible. Use a fresh <link> node
299
+ // so browsers that don't repaint a mutated favicon still show the change.
282
300
  var initial = frameAt(state, 0);
283
- if (initial && link.isConnected) setHref(initial);
301
+ if (initial && link.isConnected) setHrefFresh(initial);
284
302
  // rAF drives every effect. For "static" we paint one frame and stop;
285
- // animated effects keep looping.
303
+ // animated effects keep looping. Re-query the current link each frame
304
+ // (setHrefFresh may have swapped it) and mutate it in place.
286
305
  var step = function (now) {
287
306
  if (t0 === null) t0 = now;
288
307
  var uri = frameAt(state, now - t0);
289
- if (uri && link.isConnected) setHref(uri);
308
+ var cur = linkEl();
309
+ if (uri && cur && cur.isConnected) setHref(uri);
290
310
  if (isStatic(state)) return; // single frame, no loop
291
311
  RAF = requestAnimationFrame(step);
292
312
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-web-icon-indicator",
3
3
  "description": "Browser tab favicon reflects the DSH session state: idle / running / asking / done, recolored and animated from one base SVG in the browser (fully configurable colors + effects). · 浏览器标签页 favicon 实时反映 DSH 会话状态:待机 / 运行中 / 提问 / 完成,由单个 base.svg 在浏览器中动态上色与动画(颜色与特效完全可配置)。",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -29,9 +29,9 @@
29
29
  ]
30
30
  }
31
31
  },
32
- "dependencies": {
33
- "@deepseek-ai/schemastery": "^3.18.0",
34
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.7"
32
+ "peerDependencies": {
33
+ "@deepseek-ai/dsh-settings": "^0.1.0-rc.7",
34
+ "@deepseek-ai/schemastery": "^3.18.0"
35
35
  },
36
36
  "exports": {
37
37
  ".": {
@@ -49,7 +49,10 @@
49
49
  "files": [
50
50
  "lib",
51
51
  "icons",
52
+ "assets",
53
+ "docs",
52
54
  "cordis.patch.yml",
55
+ "CHANGELOG.md",
53
56
  "README.md",
54
57
  "README.zh.md",
55
58
  "LICENSE"
@@ -62,5 +65,15 @@
62
65
  "favicon",
63
66
  "icon-indicator",
64
67
  "browser-tab"
65
- ]
66
- }
68
+ ],
69
+ "scripts": {
70
+ "release": "commit-and-tag-version",
71
+ "release:patch": "commit-and-tag-version --release-as patch",
72
+ "release:minor": "commit-and-tag-version --release-as minor",
73
+ "release:major": "commit-and-tag-version --release-as major",
74
+ "release:dry": "commit-and-tag-version --dry-run"
75
+ },
76
+ "devDependencies": {
77
+ "commit-and-tag-version": "^13.1.2"
78
+ }
79
+ }