dsh-web-icon-indicator 0.2.0 → 0.2.2
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/README.md +81 -29
- package/README.zh.md +79 -28
- package/assets/effects/blink.svg +1 -0
- package/assets/effects/bounce.svg +1 -0
- package/assets/effects/breath.svg +1 -0
- package/assets/effects/heartbeat.svg +1 -0
- package/assets/effects/rainbow.svg +1 -0
- package/assets/effects/static.svg +1 -0
- package/assets/states-default.svg +1 -0
- package/docs/safari-favicon-research.md +114 -0
- package/icons/base.svg +1 -1
- package/lib/client.js +235 -85
- package/lib/index.js +45 -6
- package/lib/types/index.d.ts +6 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -2,42 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
> 📖 [中文文档](README.zh.md) · [English](README.md)
|
|
4
4
|
|
|
5
|
+
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
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
|
-
##
|
|
9
|
+
## ✨ What it does
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
18
|
+
### 🛠 Configuration UI — how to get there
|
|
17
19
|
|
|
18
|
-
|
|
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
|
-
|
|
27
|
+
Changes are saved through the settings transport into the profile's `settings.yaml` and applied to the running tab within ~1 s — no reload, no restart. See [Configure](#configure) for the full key reference.
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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**
|
|
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:
|
|
@@ -102,15 +128,19 @@ service under the `web-icon-indicator` namespace (a schemastery schema in
|
|
|
102
128
|
|
|
103
129
|
- **Web GUI:** open **设置 → 插件 → 插件配置** — a *Favicon indicator* card
|
|
104
130
|
edits the same keys (asking/done hold, and per-state effect / colors /
|
|
105
|
-
cycle), staged and saved through the settings transport.
|
|
131
|
+
cycle), staged and saved through the settings transport. Each state is a
|
|
132
|
+
collapsible row showing a color dot and a one-line summary (`blink ·
|
|
133
|
+
#E5484D ⇄ #FACC15 · 400ms`); expanding a row reveals its three fields, and
|
|
134
|
+
the colors field previews parsed swatches live.
|
|
106
135
|
- **Persistence:** values land in the profile's `settings.yaml` (default
|
|
107
136
|
`~/.dsh/settings.yaml`) as a `web-icon-indicator:` section. The composition
|
|
108
137
|
entry stays the `base` layer; resolution order is schema defaults →
|
|
109
138
|
composition entry → settings document user layer.
|
|
110
|
-
- **No server restart** for
|
|
111
|
-
`doneHoldMs` apply live
|
|
112
|
-
|
|
113
|
-
|
|
139
|
+
- **No server restart, no tab reload** for settings-card saves: `askingHoldMs` /
|
|
140
|
+
`doneHoldMs` apply live host-side, and per-state visual config (effect /
|
|
141
|
+
colors / cycle) is synced into the running tab through the status poll within
|
|
142
|
+
~1 s. Only code-level default changes in `lib/index.js` need a tab reload (or
|
|
143
|
+
a DSH web rebuild).
|
|
114
144
|
- The browser half is a hand-written `lib/client.js` (ModuleLoader factory
|
|
115
145
|
format — no build step, no runtime deps beyond the shell's `react`). The DSH
|
|
116
146
|
client scanner picks a new `dsh.client` declaration up on the next profile
|
|
@@ -124,15 +154,37 @@ service under the `web-icon-indicator` namespace (a schemastery schema in
|
|
|
124
154
|
- Status is aggregated across live `agents.list()` with priority `asking > running > done > idle`. The aggregation runs a `reconcile()` step on every request to detect running → idle transitions, because `agent/status`'s idle delivery is not guaranteed at turn end.
|
|
125
155
|
- `ask_user_question` tool calls (via `tools/pre-execute` / `tools/result`) flip the session into `asking` with a configurable minimum-hold so the icon stays visible even when the user answers immediately.
|
|
126
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`.
|
|
127
|
-
- 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. 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).
|
|
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).
|
|
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).
|
|
128
179
|
|
|
129
180
|
## Caveats
|
|
130
181
|
|
|
131
|
-
- 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).
|
|
132
184
|
- The base template must keep its `__COLOR__` placeholder in the `#p { fill: … }` rule; the browser replaces that token to color each frame.
|
|
133
185
|
- The plugin runs in the **host** plane; it must be mounted into a profile's composition, not a session-scoped agent preset.
|
|
134
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.
|
|
135
187
|
|
|
136
188
|
## License
|
|
137
189
|
|
|
138
|
-
MIT
|
|
190
|
+
MIT
|
package/README.zh.md
CHANGED
|
@@ -2,42 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
> 📖 [English](README.md) · [中文文档](README.zh.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](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
|
|
12
|
-
| `running` 运行中 | `#FACC15
|
|
13
|
-
| `asking` 提问 | `#E5484D`
|
|
14
|
-
| `done` 完成 | `#22A06B
|
|
39
|
+
| `idle` 待机 | `#1a1a1a`——深色鲸鱼 | `static` |
|
|
40
|
+
| `running` 运行中 | `#FACC15`——黄色 | `static` |
|
|
41
|
+
| `asking` 提问 | `#E5484D` ⇄ `#FACC15`——红/黄 | `blink`(400ms) |
|
|
42
|
+
| `done` 完成 | `#22A06B`——绿色 | `static`,保持 `doneHoldMs` 后回到 `idle` |
|
|
15
43
|
|
|
16
|
-
|
|
44
|
+
## ✨ 全部特效,动画演示
|
|
17
45
|
|
|
18
|
-
|
|
46
|
+
下面每个预览都是真实的鲸鱼路径,按插件实际渲染方式做动画(预览是自包含的动画 SVG,在浏览器里直接播放):
|
|
19
47
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
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
|
-
|
|
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
|
|
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 源码安装:
|
|
@@ -101,13 +127,16 @@ config:
|
|
|
101
127
|
|
|
102
128
|
- **Web GUI:** 打开 **设置 → 插件 → 插件配置**,会出现 *标签页图标指示器*
|
|
103
129
|
卡片,可编辑同样的键(提问/完成驻留,以及每个状态的特效 / 颜色 / 周期),
|
|
104
|
-
通过 settings
|
|
130
|
+
通过 settings 传输层暂存并保存。每个状态是一行可折叠条目,带主色圆点和一行
|
|
131
|
+
摘要(如 `blink · #E5484D ⇄ #FACC15 · 400ms`);展开该行才显示它的三个字段,
|
|
132
|
+
颜色输入框旁会实时预览解析出的色块。
|
|
105
133
|
- **持久化:** 值写入 profile 的 `settings.yaml`(默认 `~/.dsh/settings.yaml`)
|
|
106
134
|
的 `web-icon-indicator:` 段。合成条目仍是 `base` 层;解析顺序为 schema 默认值
|
|
107
135
|
→ 合成条目 → 设置文档用户层。
|
|
108
|
-
-
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
- **无需重启服务器、无需刷新标签页**即可让设置卡片的修改生效:`askingHoldMs` /
|
|
137
|
+
`doneHoldMs` 在主机侧即时生效;各状态的视觉配置(特效 / 颜色 / 周期)会随状态
|
|
138
|
+
轮询同步进正在运行的标签页,约 1 秒内生效。只有改 `lib/index.js` 里的代码级
|
|
139
|
+
默认值才需要重载标签页(或重新构建 DSH Web)。
|
|
111
140
|
- 浏览器半区是手写的 `lib/client.js`(ModuleLoader factory 格式——无构建步骤、
|
|
112
141
|
无额外运行期依赖,仅用 shell 自带的 `react`)。DSH 客户端扫描器会在下次启动
|
|
113
142
|
profile 时识别新的 `dsh.client` 声明。
|
|
@@ -119,15 +148,37 @@ config:
|
|
|
119
148
|
- 状态按 `agents.list()` 聚合,优先级 `asking > running > done > idle`。每次请求都会执行一次 `reconcile()` 检测 running → idle 的转换,因为 `agent/status` 的 idle 事件在回合结束时并不保证送达。
|
|
120
149
|
- `ask_user_question` 工具调用(通过 `tools/pre-execute` / `tools/result`)把会话置为 `asking`,带可配置的最小保持时长,即使你立刻回答,图标也会保持可见。
|
|
121
150
|
- 权限 / **沙箱拦截**等待同样会显示为 `asking`:当 agent 命中沙箱拒绝并请求提权(`sandbox_permissions` + `justification`),或其他工具需要征得同意时,审批服务会先写入一条 `approval/asked` 会话事件并阻塞 agent,直到你做出决定。插件监听 `session/event`(并以实时会话日志的权威折叠作为兜底)在整个等待期间将会话置为 `asking` 状态,收到 `approval/decided` 后清除。
|
|
122
|
-
- 浏览器脚本每秒轮询 `/dsh-web-icon-status.json`,首次获取 `base.svg`,然后每个 `requestAnimationFrame` 周期把 favicon 重建为 `data:image/svg+xml,…` URI——把 `__COLOR__`
|
|
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 原地重连,无需手动刷新图标即可恢复)。
|
|
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)。
|
|
123
173
|
|
|
124
174
|
## 已知限制
|
|
125
175
|
|
|
126
|
-
- favicon 的 SVG CSS 动画在浏览器标签页 UI 中不会运行——所有特效都由 JavaScript 每帧重建 data-URI
|
|
176
|
+
- favicon 的 SVG CSS 动画在浏览器标签页 UI 中不会运行——所有特效都由 JavaScript 每帧重建 data-URI 实现,这是零依赖设计的刻意取舍。(本文档中的动画预览只是演示素材——真实 favicon 的动画始终由 JS 驱动。)
|
|
177
|
+
- favicon 行为因浏览器而异,其中 Safari 限制最多——见 [浏览器支持与已知限制](#浏览器支持与已知限制)。
|
|
127
178
|
- `base.svg` 模板必须保留 `#p { fill: … }` 规则中的 `__COLOR__` 占位符;浏览器会替换该标记为每帧上色。
|
|
128
179
|
- 插件运行在 **host** 平面,必须挂载进 profile 的组合配置,不能作为会话级 agent preset。
|
|
129
180
|
- 文件读取走 `fs` 服务,以配置的 `iconsDir` 为 `cwd`。请确保该路径在部署环境的沙箱策略下可读。
|
|
130
181
|
|
|
131
182
|
## 许可
|
|
132
183
|
|
|
133
|
-
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"];
|
|
@@ -49,13 +58,23 @@ window.__ModuleLoader__.load({
|
|
|
49
58
|
askingHoldMsHint: "Minimum visibility of the asking icon before it settles.",
|
|
50
59
|
doneHoldMs: "Done hold (ms)",
|
|
51
60
|
doneHoldMsHint: "How long the done icon stays before returning to idle.",
|
|
52
|
-
|
|
61
|
+
stateIdle: "Idle",
|
|
62
|
+
stateRunning: "Running",
|
|
63
|
+
stateAsking: "Asking",
|
|
64
|
+
stateDone: "Done",
|
|
53
65
|
effect: "Effect",
|
|
54
66
|
effectHint: "Animation for this state.",
|
|
55
67
|
colors: "Colors",
|
|
56
68
|
colorsHint: "Comma-separated hex colors; the first is the primary color.",
|
|
57
69
|
speed: "Cycle (ms)",
|
|
58
|
-
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",
|
|
59
78
|
unsaved: "Unsaved",
|
|
60
79
|
readOnly: "This deployment stores settings read-only.",
|
|
61
80
|
save: "Save",
|
|
@@ -75,13 +94,23 @@ window.__ModuleLoader__.load({
|
|
|
75
94
|
askingHoldMsHint: "提问图标的最短可见时长。",
|
|
76
95
|
doneHoldMs: "完成驻留(毫秒)",
|
|
77
96
|
doneHoldMsHint: "完成图标停留多久后回到待机。",
|
|
78
|
-
|
|
97
|
+
stateIdle: "待机",
|
|
98
|
+
stateRunning: "运行中",
|
|
99
|
+
stateAsking: "提问",
|
|
100
|
+
stateDone: "完成",
|
|
79
101
|
effect: "特效",
|
|
80
102
|
effectHint: "该状态的动画效果。",
|
|
81
103
|
colors: "颜色",
|
|
82
104
|
colorsHint: "逗号分隔的十六进制颜色;第一个为主色。",
|
|
83
105
|
speed: "周期(毫秒)",
|
|
84
|
-
speedHint: "
|
|
106
|
+
speedHint: "该状态的动画周期;留空保持当前值。静态状态无周期。",
|
|
107
|
+
effectStatic: "静态",
|
|
108
|
+
effectBlink: "闪烁",
|
|
109
|
+
effectBreath: "呼吸",
|
|
110
|
+
effectRainbow: "彩虹",
|
|
111
|
+
effectHeartbeat: "心跳",
|
|
112
|
+
effectBounce: "弹跳",
|
|
113
|
+
editColor: "编辑颜色",
|
|
85
114
|
unsaved: "未保存",
|
|
86
115
|
readOnly: "本部署的设置为只读。",
|
|
87
116
|
save: "保存",
|
|
@@ -111,8 +140,15 @@ window.__ModuleLoader__.load({
|
|
|
111
140
|
hint: { color: "var(--dsw-alias-label-tertiary)", fontSize: 12, lineHeight: "1.5", margin: 0 },
|
|
112
141
|
invalidText: { color: "var(--dsw-alias-label-error)", fontSize: 12, lineHeight: "1.5", margin: 0 },
|
|
113
142
|
select: { border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-3)", height: 34, borderRadius: 8, padding: "0 8px", fontSize: 13, fontFamily: "inherit", color: "var(--dsw-alias-label-primary)" },
|
|
114
|
-
|
|
115
|
-
|
|
143
|
+
statesBlock: { borderTop: "1px solid var(--dsw-alias-border-l2)", marginTop: 4, paddingTop: 4 },
|
|
144
|
+
dot: { display: "inline-block", width: 10, height: 10, borderRadius: 999, border: "1px solid var(--dsw-alias-border-l2)" },
|
|
145
|
+
summary: { flex: 1, minWidth: 0, marginLeft: 8, overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis", color: "var(--dsw-alias-label-tertiary)", fontSize: 12, lineHeight: "24px" },
|
|
146
|
+
statePanel: { padding: "2px 0 12px 22px" },
|
|
147
|
+
inputRow: { display: "flex", alignItems: "center", gap: 8 },
|
|
148
|
+
swatches: { display: "inline-flex", alignItems: "center", gap: 4, flex: "none" },
|
|
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 },
|
|
116
152
|
badges: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
117
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" },
|
|
118
154
|
footer: { borderTop: "1px solid var(--dsw-alias-border-l2)", display: "flex", justifyContent: "flex-end", alignItems: "center", gap: 8, padding: "12px 0 4px" },
|
|
@@ -123,12 +159,12 @@ window.__ModuleLoader__.load({
|
|
|
123
159
|
// the field row and the inner <input> flex. Primitives components carry no
|
|
124
160
|
// CSS-module hooks for us (the bundle has no build step), so one guarded
|
|
125
161
|
// style tag mirrors what the official cards' stylesheets do.
|
|
126
|
-
var FORM_CSS_TAG = "dsh-web-icon-indicator/form.css";
|
|
162
|
+
var FORM_CSS_TAG = "dsh-web-icon-indicator/form.v2.css";
|
|
127
163
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(FORM_CSS_TAG) + "]") === null) {
|
|
128
164
|
var cssTag = document.createElement("style");
|
|
129
165
|
cssTag.dataset.plugin = "dsh-web-icon-indicator";
|
|
130
166
|
cssTag.dataset.pluginCss = FORM_CSS_TAG;
|
|
131
|
-
cssTag.textContent = ".dsh-wii-input{display:flex;align-items:center;width:100%}.dsh-wii-input>input{flex:1;min-width:0}";
|
|
167
|
+
cssTag.textContent = ".dsh-wii-input{display:flex;align-items:center;width:100%}.dsh-wii-input>input{flex:1;min-width:0}.dsh-wii-state+.dsh-wii-state{border-top:1px solid var(--dsw-alias-border-l2)}";
|
|
132
168
|
document.head.appendChild(cssTag);
|
|
133
169
|
}
|
|
134
170
|
|
|
@@ -142,53 +178,40 @@ window.__ModuleLoader__.load({
|
|
|
142
178
|
}
|
|
143
179
|
|
|
144
180
|
function LabeledField(props) {
|
|
145
|
-
// props: { label, hint, invalidLabel, children }
|
|
181
|
+
// props: { label, hint, invalidLabel, htmlFor, children }
|
|
146
182
|
return createElement(
|
|
147
183
|
"div",
|
|
148
184
|
{ style: fieldStyle(props.separated) },
|
|
149
|
-
createElement("label", { style: styles.label }, props.label),
|
|
185
|
+
createElement("label", { style: styles.label, htmlFor: props.htmlFor }, props.label),
|
|
150
186
|
props.children,
|
|
151
187
|
createElement("p", { style: props.invalid ? styles.invalidText : styles.hint }, props.invalid ? props.invalidLabel : props.hint)
|
|
152
188
|
);
|
|
153
189
|
}
|
|
154
190
|
|
|
155
|
-
function
|
|
156
|
-
// props: { id,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
})
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function TextInput(props) {
|
|
191
|
+
function ValueInput(props) {
|
|
192
|
+
// props: { id, label, hint, invalidLabel, invalid, separated, disabled, value,
|
|
193
|
+
// placeholder, numeric, trailing, onChange }
|
|
194
|
+
var input = createElement(primitives.Input, {
|
|
195
|
+
id: props.id,
|
|
196
|
+
type: "text",
|
|
197
|
+
inputMode: props.numeric ? "numeric" : void 0,
|
|
198
|
+
className: "dsh-wii-input",
|
|
199
|
+
style: inputStyle(props.invalid),
|
|
200
|
+
disabled: props.disabled,
|
|
201
|
+
value: props.value,
|
|
202
|
+
placeholder: props.placeholder || "",
|
|
203
|
+
onChange: function (event) { props.onChange(event.target.value); },
|
|
204
|
+
});
|
|
174
205
|
return createElement(
|
|
175
206
|
LabeledField,
|
|
176
|
-
{ label: props.label, hint: props.hint, invalid: props.invalid, invalidLabel: props.invalidLabel, separated: props.separated },
|
|
177
|
-
createElement(
|
|
178
|
-
id: props.id,
|
|
179
|
-
type: "text",
|
|
180
|
-
className: "dsh-wii-input",
|
|
181
|
-
style: inputStyle(props.invalid),
|
|
182
|
-
disabled: props.disabled,
|
|
183
|
-
value: props.value,
|
|
184
|
-
placeholder: props.placeholder || "",
|
|
185
|
-
onChange: function (event) { props.onChange(event.target.value); },
|
|
186
|
-
})
|
|
207
|
+
{ label: props.label, hint: props.hint, invalid: props.invalid, invalidLabel: props.invalidLabel, separated: props.separated, htmlFor: props.id },
|
|
208
|
+
props.trailing ? createElement("div", { style: styles.inputRow }, input, props.trailing) : input
|
|
187
209
|
);
|
|
188
210
|
}
|
|
189
211
|
|
|
190
212
|
function SelectInput(props) {
|
|
191
213
|
// props: { id, value, options, onChange, disabled }
|
|
214
|
+
// options: array of { value, label } — value stays the identifier, label is localized.
|
|
192
215
|
return createElement(
|
|
193
216
|
"select",
|
|
194
217
|
{
|
|
@@ -199,7 +222,64 @@ window.__ModuleLoader__.load({
|
|
|
199
222
|
onChange: function (event) { props.onChange(event.target.value); },
|
|
200
223
|
},
|
|
201
224
|
props.options.map(function (option) {
|
|
202
|
-
return createElement("option", { key: option, value: option }, option);
|
|
225
|
+
return createElement("option", { key: option.value, value: option.value }, option.label);
|
|
226
|
+
})
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Filled dot previewing one state's primary color (colors[0]). */
|
|
231
|
+
function ColorDot(props) {
|
|
232
|
+
return createElement("span", { style: Object.assign({}, styles.dot, { background: props.color }) });
|
|
233
|
+
}
|
|
234
|
+
|
|
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
|
+
*/
|
|
248
|
+
function ColorSwatches(props) {
|
|
249
|
+
var colors = parseColors(props.text);
|
|
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
|
+
}
|
|
260
|
+
return createElement(
|
|
261
|
+
"span",
|
|
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
|
+
);
|
|
203
283
|
})
|
|
204
284
|
);
|
|
205
285
|
}
|
|
@@ -270,6 +350,10 @@ window.__ModuleLoader__.load({
|
|
|
270
350
|
var openState = useState(false);
|
|
271
351
|
var open = openState[0];
|
|
272
352
|
var setOpen = openState[1];
|
|
353
|
+
// Accordion: which per-state row is expanded (null = all collapsed).
|
|
354
|
+
var openNameState = useState(null);
|
|
355
|
+
var openName = openNameState[0];
|
|
356
|
+
var setOpenName = openNameState[1];
|
|
273
357
|
|
|
274
358
|
if (!ready || value === null) return null;
|
|
275
359
|
|
|
@@ -300,6 +384,36 @@ window.__ModuleLoader__.load({
|
|
|
300
384
|
return formatNumber(state.speed);
|
|
301
385
|
};
|
|
302
386
|
|
|
387
|
+
var stateTitleKey = function (name) {
|
|
388
|
+
return "state" + name.charAt(0).toUpperCase() + name.slice(1);
|
|
389
|
+
};
|
|
390
|
+
/** One-line collapsed summary: "Blink · #E5484D ⇄ #FACC15 · 400ms" (draft-aware). */
|
|
391
|
+
var stateSummary = function (name) {
|
|
392
|
+
var effect = stateFieldValue(name, "effect");
|
|
393
|
+
var parts = [t(EFFECT_LABEL_KEYS[effect] || effect)];
|
|
394
|
+
var colorsText = stateFieldValue(name, "colors").trim();
|
|
395
|
+
if (colorsText !== "") {
|
|
396
|
+
var parsed = parseColors(colorsText);
|
|
397
|
+
parts.push(parsed === null ? colorsText : parsed.join(" ⇄ "));
|
|
398
|
+
}
|
|
399
|
+
var speedText = stateFieldValue(name, "speed").trim();
|
|
400
|
+
if (speedText !== "" && effect !== "static") parts.push(speedText + "ms");
|
|
401
|
+
return parts.join(" · ");
|
|
402
|
+
};
|
|
403
|
+
/** Primary color for the row's dot; transparent while the draft is unparseable. */
|
|
404
|
+
var statePrimaryColor = function (name) {
|
|
405
|
+
var parsed = parseColors(stateFieldValue(name, "colors"));
|
|
406
|
+
return parsed === null ? "transparent" : parsed[0];
|
|
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
|
+
});
|
|
416
|
+
|
|
303
417
|
var dirty = Object.keys(drafts).length > 0;
|
|
304
418
|
|
|
305
419
|
var save = function () {
|
|
@@ -316,10 +430,16 @@ window.__ModuleLoader__.load({
|
|
|
316
430
|
if (parsed.kind === "clear") { if (userHas(key)) ops.push({ op: "unset", field: key }); }
|
|
317
431
|
else if (parsed.value !== value[key]) ops.push({ op: "set", field: key, value: parsed.value });
|
|
318
432
|
}
|
|
319
|
-
// Per-state visuals: rebuild
|
|
320
|
-
//
|
|
433
|
+
// Per-state visuals: rebuild `states` as the user layer's current
|
|
434
|
+
// entries with the drafts applied. `scope.set` REPLACES the whole
|
|
435
|
+
// field (no deep merge), so untouched entries — overrides saved
|
|
436
|
+
// earlier for other states, plus any key not in STATE_NAMES — are
|
|
437
|
+
// carried through instead of being dropped.
|
|
321
438
|
var statesDirty = false;
|
|
322
439
|
var nextStates = {};
|
|
440
|
+
if (userLayer !== void 0 && userLayer !== null && typeof userLayer.states === "object" && userLayer.states !== null) {
|
|
441
|
+
nextStates = Object.assign({}, userLayer.states);
|
|
442
|
+
}
|
|
323
443
|
for (i = 0; i < STATE_NAMES.length; i += 1) {
|
|
324
444
|
var name = STATE_NAMES[i];
|
|
325
445
|
var effKey = "states." + name + ".effect";
|
|
@@ -419,67 +539,97 @@ window.__ModuleLoader__.load({
|
|
|
419
539
|
"div",
|
|
420
540
|
{ style: styles.body },
|
|
421
541
|
!writable ? createElement("p", { style: styles.hint }, t("readOnly")) : null,
|
|
422
|
-
createElement(
|
|
542
|
+
createElement(ValueInput, {
|
|
423
543
|
id: "plugin-config-icon-asking-hold",
|
|
424
544
|
label: t("askingHoldMs"),
|
|
425
545
|
hint: t("askingHoldMsHint"),
|
|
426
546
|
invalidLabel: t("invalidNumber"),
|
|
427
547
|
invalid: failed === "invalidNumber",
|
|
428
548
|
disabled: disabled,
|
|
429
|
-
|
|
549
|
+
numeric: true,
|
|
550
|
+
separated: false,
|
|
430
551
|
value: fieldValue("askingHoldMs"),
|
|
431
552
|
onChange: function (text) { edit("askingHoldMs", text); },
|
|
432
553
|
}),
|
|
433
|
-
createElement(
|
|
554
|
+
createElement(ValueInput, {
|
|
434
555
|
id: "plugin-config-icon-done-hold",
|
|
435
556
|
label: t("doneHoldMs"),
|
|
436
557
|
hint: t("doneHoldMsHint"),
|
|
437
558
|
invalidLabel: t("invalidNumber"),
|
|
438
559
|
invalid: failed === "invalidNumber",
|
|
439
560
|
disabled: disabled,
|
|
561
|
+
numeric: true,
|
|
440
562
|
separated: true,
|
|
441
563
|
value: fieldValue("doneHoldMs"),
|
|
442
564
|
onChange: function (text) { edit("doneHoldMs", text); },
|
|
443
565
|
}),
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
createElement(
|
|
450
|
-
|
|
451
|
-
{
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
566
|
+
createElement(
|
|
567
|
+
"div",
|
|
568
|
+
{ style: styles.statesBlock },
|
|
569
|
+
STATE_NAMES.map(function (name) {
|
|
570
|
+
var isOpen = openName === name;
|
|
571
|
+
return createElement(
|
|
572
|
+
primitives.DisclosureRow,
|
|
573
|
+
{
|
|
574
|
+
key: name,
|
|
575
|
+
className: "dsh-wii-state",
|
|
576
|
+
icon: createElement(ColorDot, { color: statePrimaryColor(name) }),
|
|
577
|
+
title: t(stateTitleKey(name)),
|
|
578
|
+
open: isOpen,
|
|
579
|
+
expandable: true,
|
|
580
|
+
expandOnRowClick: true,
|
|
581
|
+
onToggle: function () { setOpenName(isOpen ? null : name); },
|
|
582
|
+
collapsedContent: createElement("span", { style: styles.summary }, stateSummary(name)),
|
|
583
|
+
},
|
|
584
|
+
createElement(
|
|
585
|
+
"div",
|
|
586
|
+
{ style: styles.statePanel },
|
|
587
|
+
createElement(
|
|
588
|
+
LabeledField,
|
|
589
|
+
{ label: t("effect"), hint: t("effectHint"), invalid: false, invalidLabel: "", separated: false, htmlFor: "plugin-config-icon-" + name + "-effect" },
|
|
590
|
+
createElement(SelectInput, {
|
|
591
|
+
id: "plugin-config-icon-" + name + "-effect",
|
|
592
|
+
options: effectOptions,
|
|
593
|
+
disabled: disabled,
|
|
594
|
+
value: stateFieldValue(name, "effect"),
|
|
595
|
+
onChange: function (text) { edit("states." + name + ".effect", text); },
|
|
596
|
+
})
|
|
597
|
+
),
|
|
598
|
+
createElement(ValueInput, {
|
|
599
|
+
id: "plugin-config-icon-" + name + "-colors",
|
|
600
|
+
label: t("colors"),
|
|
601
|
+
hint: t("colorsHint"),
|
|
602
|
+
invalidLabel: t("invalidColors"),
|
|
603
|
+
invalid: failed === "invalidColors",
|
|
604
|
+
disabled: disabled,
|
|
605
|
+
separated: true,
|
|
606
|
+
value: 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
|
+
}),
|
|
612
|
+
onChange: function (text) { edit("states." + name + ".colors", text); },
|
|
613
|
+
}),
|
|
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, {
|
|
618
|
+
id: "plugin-config-icon-" + name + "-speed",
|
|
619
|
+
label: t("speed"),
|
|
620
|
+
hint: t("speedHint"),
|
|
621
|
+
invalidLabel: t("invalidNumber"),
|
|
622
|
+
invalid: failed === "invalidNumber",
|
|
623
|
+
disabled: disabled,
|
|
624
|
+
numeric: true,
|
|
625
|
+
separated: true,
|
|
626
|
+
value: stateFieldValue(name, "speed"),
|
|
627
|
+
onChange: function (text) { edit("states." + name + ".speed", text); },
|
|
628
|
+
})
|
|
629
|
+
)
|
|
630
|
+
);
|
|
631
|
+
})
|
|
632
|
+
),
|
|
483
633
|
createElement(
|
|
484
634
|
"div",
|
|
485
635
|
{ style: styles.footer },
|
package/lib/index.js
CHANGED
|
@@ -131,6 +131,7 @@ const INJECTED_SCRIPT = `
|
|
|
131
131
|
var STATUS_PATH = "__STATUS_PATH__";
|
|
132
132
|
var BASE_PATH = "__BASE_PATH__";
|
|
133
133
|
var CFG = __CFG__; // { states: { idle:{effect,colors,speed}, running:…, asking:…, done:… } }
|
|
134
|
+
var CFG_JSON = null; // serialized states last seen; null = not synced yet
|
|
134
135
|
var BASE = null; // base.svg text; __COLOR__ replaced per frame
|
|
135
136
|
var RE = /__COLOR__/g;
|
|
136
137
|
var DEF_SPEED = 1200; // per-state cycle fallback when speed is omitted
|
|
@@ -153,6 +154,23 @@ const INJECTED_SCRIPT = `
|
|
|
153
154
|
if (!link) return;
|
|
154
155
|
try { link.setAttribute("href", uri); link.setAttribute("type", "image/svg+xml"); } catch (e) {}
|
|
155
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
|
+
}
|
|
156
174
|
|
|
157
175
|
// ---- color helpers ------------------------------------------------------
|
|
158
176
|
function hexToRgb(h) {
|
|
@@ -277,24 +295,41 @@ const INJECTED_SCRIPT = `
|
|
|
277
295
|
ANIM_START = Date.now();
|
|
278
296
|
// Paint the first frame synchronously so a state change shows even when
|
|
279
297
|
// rAF never fires (e.g. the tab is hidden); the rAF loop then takes over
|
|
280
|
-
// 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.
|
|
281
300
|
var initial = frameAt(state, 0);
|
|
282
|
-
if (initial && link.isConnected)
|
|
301
|
+
if (initial && link.isConnected) setHrefFresh(initial);
|
|
283
302
|
// rAF drives every effect. For "static" we paint one frame and stop;
|
|
284
|
-
// 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.
|
|
285
305
|
var step = function (now) {
|
|
286
306
|
if (t0 === null) t0 = now;
|
|
287
307
|
var uri = frameAt(state, now - t0);
|
|
288
|
-
|
|
308
|
+
var cur = linkEl();
|
|
309
|
+
if (uri && cur && cur.isConnected) setHref(uri);
|
|
289
310
|
if (isStatic(state)) return; // single frame, no loop
|
|
290
311
|
RAF = requestAnimationFrame(step);
|
|
291
312
|
};
|
|
292
313
|
RAF = requestAnimationFrame(step);
|
|
293
314
|
}
|
|
315
|
+
// Live config sync: the status response carries the current per-state
|
|
316
|
+
// visual config (the "states" field). When it differs from what this tab
|
|
317
|
+
// renders, swap it in and reset the animation phase so the next apply()
|
|
318
|
+
// repaints from scratch — a settings-card save takes effect within one
|
|
319
|
+
// poll tick, no tab reload needed. A response without "states" (older
|
|
320
|
+
// host) leaves the baked __CFG__ in place.
|
|
321
|
+
function syncCfg(j) {
|
|
322
|
+
if (!j || typeof j.states !== "object" || j.states === null) return;
|
|
323
|
+
var next = JSON.stringify(j.states);
|
|
324
|
+
if (next === CFG_JSON) return;
|
|
325
|
+
CFG_JSON = next;
|
|
326
|
+
CFG = { states: j.states };
|
|
327
|
+
PREV_STATE = null; // force a full repaint on the next apply
|
|
328
|
+
}
|
|
294
329
|
function poll() {
|
|
295
330
|
fetch(location.origin + STATUS_PATH, { cache: "no-store" })
|
|
296
331
|
.then(function (r) { if (!r.ok) throw new Error("bad"); return r.json(); })
|
|
297
|
-
.then(function (j) { apply(j.state || "idle"); })
|
|
332
|
+
.then(function (j) { syncCfg(j); apply(j.state || "idle"); })
|
|
298
333
|
// Transient failures (host restarting, network blip) must NOT kill the
|
|
299
334
|
// poll: restore the original icon and retry on the next tick. The SPA
|
|
300
335
|
// reconnects in place, so the tab survives a host restart — the icon
|
|
@@ -572,6 +607,10 @@ export default {
|
|
|
572
607
|
};
|
|
573
608
|
|
|
574
609
|
// -- Routes --------------------------------------------------------------
|
|
610
|
+
// The status endpoint also echoes the current per-state visual config
|
|
611
|
+
// (`states`): the injected script syncs it on every poll, so a settings
|
|
612
|
+
// save changes the favicon within ~1 s without a tab reload. `no-store`
|
|
613
|
+
// keeps every poll reading the freshest config.
|
|
575
614
|
ctx.effect(() => webServer.register({
|
|
576
615
|
kind: "exact",
|
|
577
616
|
path: cfg.statusPath,
|
|
@@ -579,7 +618,7 @@ export default {
|
|
|
579
618
|
res.statusCode = 200;
|
|
580
619
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
581
620
|
res.setHeader("Cache-Control", "no-store");
|
|
582
|
-
res.end(JSON.stringify(aggregate()));
|
|
621
|
+
res.end(JSON.stringify({ ...aggregate(), states: cfg.states }));
|
|
583
622
|
},
|
|
584
623
|
}));
|
|
585
624
|
|
package/lib/types/index.d.ts
CHANGED
|
@@ -77,6 +77,12 @@ export interface DshWebIconIndicatorAggregate {
|
|
|
77
77
|
state: DshWebIconStateName;
|
|
78
78
|
/** Epoch millis when that aggregate state was first entered. */
|
|
79
79
|
since: number;
|
|
80
|
+
/**
|
|
81
|
+
* Current per-state visual config, echoed by the status endpoint so the
|
|
82
|
+
* injected browser script can apply a settings save within ~1 s (no tab
|
|
83
|
+
* reload). Added in 0.2.x; older browser bundles ignore it.
|
|
84
|
+
*/
|
|
85
|
+
states: Partial<Record<DshWebIconStateName, DshWebIconStateConfig>>;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
/**
|
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.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -24,11 +24,12 @@
|
|
|
24
24
|
"@deepseek-ai/dsh-client-runtime",
|
|
25
25
|
"@deepseek-ai/dsh-client-locale",
|
|
26
26
|
"@deepseek-ai/dsh-client-ui-slots",
|
|
27
|
-
"@deepseek-ai/dsh-client-ui-settings"
|
|
27
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
28
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
28
29
|
]
|
|
29
30
|
}
|
|
30
31
|
},
|
|
31
|
-
"
|
|
32
|
+
"peerDependencies": {
|
|
32
33
|
"@deepseek-ai/schemastery": "^3.18.0",
|
|
33
34
|
"@deepseek-ai/dsh-settings": "^0.1.0-rc.7"
|
|
34
35
|
},
|
|
@@ -48,6 +49,8 @@
|
|
|
48
49
|
"files": [
|
|
49
50
|
"lib",
|
|
50
51
|
"icons",
|
|
52
|
+
"assets",
|
|
53
|
+
"docs",
|
|
51
54
|
"cordis.patch.yml",
|
|
52
55
|
"README.md",
|
|
53
56
|
"README.zh.md",
|