@nonamelego/dsh-catppuccin 0.5.1 → 0.5.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.en.md CHANGED
@@ -30,9 +30,9 @@
30
30
  - [💝 Credits](#-credits)
31
31
 
32
32
  <p align="center">
33
- <img src="assets/previews/combined.png" width="100%" alt="DeepSeek Harness under the four Catppuccin flavours"/>
33
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/combined.png" width="100%" alt="DeepSeek Harness under the four Catppuccin flavours"/>
34
34
  <br/><br/>
35
- <img src="assets/previews/glass-combined.png" width="100%" alt="Glass skin · Latte & Mocha"/>
35
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/glass-combined.png" width="100%" alt="Glass skin · Latte & Mocha"/>
36
36
  </p>
37
37
 
38
38
  ## Introduction
@@ -58,7 +58,9 @@ Catppuccin theme automatically.
58
58
  - 🧩 Registered into the official theme system, on a par with the built-in light / dark / system themes
59
59
  - 🎯 Full-UI colour coverage — not just one or two accent colours
60
60
  - ⚙️ One-click switch in Settings, persisted and restored across restarts
61
- - 🌐 Bilingual UI (Chinese / English, follows the system language)
61
+ - 🔧 **Custom token overrides**: override individual colour tokens with `--dsw-* var: value` pairs (e.g. turn comments blue); persisted alongside the selected flavour
62
+ - 🖍️ **Code-block highlight style**: default / italic-comments shiki themes
63
+ - 🌐 Seven UI languages (Chinese / English / Japanese / Korean / Spanish / French / German, follows the system language)
62
64
  - 🪟 **Glass skin**: frosted glass for the top bar / sidebar / composer / stats line /
63
65
  trajectory view / chat bubbles / new-session button, one-click toggle in Settings;
64
66
  mica & compatibility modes, adjustable blur, frost and backdrop brightness
@@ -68,7 +70,8 @@ Catppuccin theme automatically.
68
70
  content softens as it scrolls under the panes
69
71
  - 🎨 Glass colours follow the current Catppuccin theme
70
72
  - 🔄 **Update check**: one-click "Check for updates" in Settings compares the latest npm
71
- version and gives you a copyable upgrade command
73
+ version and gives you a copyable upgrade command; **auto-check is on by default** (once at
74
+ startup, then every 6 hours) and the release channel switches between stable and beta
72
75
  - 💻 **dsh-TUI terminal themes**: one command installs into dsh-TUI; the four themes sync to
73
76
  `~/.dsh-tui/themes/` automatically (see [Installation · dsh-TUI](#dsh-tui-terminal-themes))
74
77
 
@@ -78,19 +81,19 @@ Actual screenshots from a local GUI (the header image is a diagonal blend of the
78
81
 
79
82
  <details>
80
83
  <summary>🌻 Latte (light)</summary>
81
- <img src="assets/previews/latte.png"/>
84
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/latte.png"/>
82
85
  </details>
83
86
  <details>
84
87
  <summary>🪴 Frappé (dark)</summary>
85
- <img src="assets/previews/frappe.png"/>
88
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/frappe.png"/>
86
89
  </details>
87
90
  <details>
88
91
  <summary>🌺 Macchiato (dark)</summary>
89
- <img src="assets/previews/macchiato.png"/>
92
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/macchiato.png"/>
90
93
  </details>
91
94
  <details>
92
95
  <summary>🌿 Mocha (dark)</summary>
93
- <img src="assets/previews/mocha.png"/>
96
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/mocha.png"/>
94
97
  </details>
95
98
 
96
99
  ### Glass skin (Mica mode)
@@ -101,11 +104,11 @@ past the page edges; the background is the theme's solid base colour:
101
104
 
102
105
  <details>
103
106
  <summary>🌻 Latte (light glass)</summary>
104
- <img src="assets/previews/glass-latte.png"/>
107
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/glass-latte.png"/>
105
108
  </details>
106
109
  <details>
107
110
  <summary>🌿 Mocha (dark glass)</summary>
108
- <img src="assets/previews/glass-mocha.png"/>
111
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/glass-mocha.png"/>
109
112
  </details>
110
113
 
111
114
  ## Installation
@@ -146,8 +149,13 @@ The same package covers the TUI. Install it into the dsh-tui profile:
146
149
  dsh plugin --profile dsh-tui add @nonamelego/dsh-catppuccin
147
150
  ```
148
151
 
149
- Installing from the repository works the same way (use the git form while the npm release
150
- is pending). The package ships a tiny theme-sync plugin row
152
+ Installing from the repository works the same way:
153
+
154
+ ```sh
155
+ dsh plugin --profile dsh-tui add https://github.com/NoNameLeGo/dsh-catppuccin-theme
156
+ ```
157
+
158
+ The package ships a tiny theme-sync plugin row
151
159
  (`dsh-catppuccin-tui-themes`, no service dependencies): on every dsh-TUI start it syncs the
152
160
  four theme JSONs to `~/.dsh-tui/themes/`, so upgrades pick up the new palettes. After
153
161
  installing, launch `dsh --profile dsh-tui` and pick the theme with `/theme` —
@@ -178,6 +186,15 @@ installing, launch `dsh --profile dsh-tui` and pick the theme with `/theme` —
178
186
  4. Choosing **Follow system** reverts to the official theme — it restores the preference
179
187
  you had before enabling Catppuccin (light / dark / follow system) instead of forcing a reset.
180
188
 
189
+ ### Other options in the Catppuccin row
190
+
191
+ - **Code highlight style**: default / **italic comments** — affects only the shiki colours used
192
+ in code blocks and diffs.
193
+ - **Custom overrides** (collapsible; the button shows the entry count): override individual tokens
194
+ with `--dsw-* var: value` pairs, e.g. `--dsw-static-blue-500` → `#89b4fa`. The **key** commits on
195
+ blur and must start with `--` (otherwise the entry is dropped); the **value** applies as you type,
196
+ and an **empty value deletes the entry**; ✕ removes the row. Overrides persist with the flavour.
197
+
181
198
  ### Glass skin
182
199
 
183
200
  Right below the **Catppuccin theme** row in **Settings → General** you'll find the **Glass** row:
@@ -201,6 +218,8 @@ In **Settings → General**, right below the **Glass** row:
201
218
  - Clicking **Check for updates** compares the latest npm version with the installed one:
202
219
  up to date → shows the current version; newer → shows the new version plus a copyable
203
220
  upgrade command (the profile name is detected automatically, falling back to `web`).
221
+ - **Auto-check**: on by default — once after startup, then every 6 hours (switch it off on this row);
222
+ the **Channel** picks **Stable** (follows `latest` only) or **Beta** (prereleases too).
204
223
  - Locally linked / source installs (`link:` / `file:` / git) don't show an npm upgrade
205
224
  command — you'll be told to `git pull` or rebuild instead.
206
225
  - Channel policy: stable builds follow the `latest` tag; prereleases follow both `latest`
@@ -233,9 +252,10 @@ What this plugin does:
233
252
 
234
253
  ```sh
235
254
  pnpm install
236
- pnpm typecheck # tsc --noEmit type check
237
- pnpm test # vitest palette-coverage tests
238
- pnpm build # tsdown build -> lib/index.js (host) + lib/client.js (browser)
255
+ pnpm typecheck # tsc --noEmit: type check for src
256
+ pnpm typecheck:tests # tsc --noEmit: type check for the specs (vitest transpiles, it never type checks)
257
+ pnpm test # vitest palette-coverage tests
258
+ pnpm build # tsdown build -> lib/index.js (host) + lib/client.js (browser)
239
259
  ```
240
260
 
241
261
  Palettes are produced by a generator script — after editing
@@ -253,6 +273,17 @@ pnpm changelog:gen # print the draft since the last tag
253
273
  pnpm changelog:gen -- --write # write it into the [Unreleased] section
254
274
  ```
255
275
 
276
+ TypeDoc docs for the public API (`./client`, `./tui-themes` subpath exports) are generated
277
+ locally on demand into `docs/api/` (that directory is **not** committed — it's in
278
+ `.gitignore`; wire up CI Pages publishing later if an online copy is ever wanted):
279
+
280
+ ```sh
281
+ pnpm docs:api
282
+ ```
283
+
284
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guide and
285
+ [docs/state-migrations.md](docs/state-migrations.md) for the state versioning contract.
286
+
256
287
  ### Local link debugging
257
288
 
258
289
  Clone the repo, link it into a profile and add it to the bundles (use your own paths):
@@ -301,4 +332,8 @@ Then add `@nonamelego/dsh-catppuccin` to the profile's `package.json`
301
332
 
302
333
  <p align="center">
303
334
  Copyright &copy; 2021-present <a href="https://github.com/catppuccin/catppuccin" target="_blank">Catppuccin Org</a>
335
+ </p>
336
+
337
+ <p align="center">
338
+ <a href="https://github.com/catppuccin/catppuccin/blob/main/LICENSE"><img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&logoColor=d9e0ee&colorA=363a4f&colorB=b7bdf8"/></a>
304
339
  </p>
package/README.md CHANGED
@@ -28,9 +28,9 @@
28
28
  - [💝 致谢](#致谢)
29
29
 
30
30
  <p align="center">
31
- <img src="assets/previews/combined.png" width="100%" alt="Catppuccin 四主题下的 DeepSeek Harness"/>
31
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/combined.png" width="100%" alt="Catppuccin 四主题下的 DeepSeek Harness"/>
32
32
  <br/><br/>
33
- <img src="assets/previews/glass-combined.png" width="100%" alt="玻璃质感 · Latte & Mocha"/>
33
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/glass-combined.png" width="100%" alt="玻璃质感 · Latte & Mocha"/>
34
34
  </p>
35
35
 
36
36
  ## 简介
@@ -55,6 +55,8 @@ Catppuccin 主题。
55
55
  - 🧩 接入官方主题系统,与内置浅色 / 深色 / 跟随系统主题平级
56
56
  - 🎯 全界面配色覆盖,不只是一两个强调色
57
57
  - ⚙️ 设置页一行切换,选择自动保存、重启自动恢复
58
+ - 🔧 **自定义 token 覆盖**:按「`--dsw-* 变量: 值`」逐条覆盖单个配色 token(例如把注释色换成蓝色),与所选风味一起持久保存
59
+ - 🖍️ **代码块高亮风格**:默认 / 注释斜体(italic-comments)两套 shiki 风格可选
58
60
  - 🌐 中 / 英 / 日 / 韩 / 西 / 法 / 德七语文案(跟随系统语言)
59
61
  - 🪟 **玻璃质感**:顶栏 / 侧边栏 / 输入框 / 统计行 / 轨迹视图 / 聊天气泡 /
60
62
  新会话按钮磨砂玻璃效果,设置里一键开关;云母 / 兼容双模式,模糊度、磨砂度、
@@ -62,7 +64,7 @@ Catppuccin 主题。
62
64
  - 🌫️ **玻璃拟态细节**:页面上下边缘渐变模糊、折叠侧边栏悬浮玻璃、
63
65
  纯色背景跟随主题底色——内容滚入视口边缘时柔化穿过,层次更立体
64
66
  - 🎨 玻璃配色自动跟随当前 Catppuccin 主题
65
- - 🔄 **检查 Catppuccin 插件更新**:设置页一键检测本插件(dsh-catppuccin)在 npm 上的最新版本,发现新版直接给出可复制的升级命令
67
+ - 🔄 **检查 Catppuccin 插件更新**:设置页一键检测本插件(dsh-catppuccin)在 npm 上的最新版本,发现新版直接给出可复制的升级命令;**默认开启自动检查**(启动后一次 + 每 6 小时),更新渠道可选稳定版 / Beta
66
68
  - 💻 **dsh-TUI 终端主题**:一条安装命令装进 dsh-TUI,四套主题自动同步到 `~/.dsh-tui/themes/`,见[安装 · dsh-TUI](#dsh-tui-终端版主题)
67
69
 
68
70
  ## 预览
@@ -71,19 +73,19 @@ Catppuccin 主题。
71
73
 
72
74
  <details>
73
75
  <summary>🌻 Latte(浅色)</summary>
74
- <img src="assets/previews/latte.png"/>
76
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/latte.png"/>
75
77
  </details>
76
78
  <details>
77
79
  <summary>🪴 Frappé(深色)</summary>
78
- <img src="assets/previews/frappe.png"/>
80
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/frappe.png"/>
79
81
  </details>
80
82
  <details>
81
83
  <summary>🌺 Macchiato(深色)</summary>
82
- <img src="assets/previews/macchiato.png"/>
84
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/macchiato.png"/>
83
85
  </details>
84
86
  <details>
85
87
  <summary>🌿 Mocha(深色)</summary>
86
- <img src="assets/previews/mocha.png"/>
88
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/mocha.png"/>
87
89
  </details>
88
90
 
89
91
  ### 玻璃质感(Mica 云母模式)
@@ -92,16 +94,13 @@ Catppuccin 主题。
92
94
  聊天气泡、输入框与统计行都是玻璃卡片,消息滚过页面边缘时被柔化,
93
95
  背景为主题底色的纯色(截图来自本地 GUI):
94
96
 
95
- <p align="center">
96
- </p>
97
-
98
97
  <details>
99
98
  <summary>🌻 Latte(浅色玻璃)</summary>
100
- <img src="assets/previews/glass-latte.png"/>
99
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/glass-latte.png"/>
101
100
  </details>
102
101
  <details>
103
102
  <summary>🌿 Mocha(深色玻璃)</summary>
104
- <img src="assets/previews/glass-mocha.png"/>
103
+ <img src="https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/main/assets/previews/glass-mocha.png"/>
105
104
  </details>
106
105
 
107
106
  ## 安装
@@ -143,7 +142,7 @@ dsh plugin --profile web add https://github.com/NoNameLeGo/dsh-catppuccin-theme
143
142
  dsh plugin --profile dsh-tui add @nonamelego/dsh-catppuccin
144
143
  ```
145
144
 
146
- 从仓库安装同理(npm 版尚未发版时可用 git 形式,效果一致):
145
+ 从仓库安装同理(也可用 git 形式,效果一致):
147
146
 
148
147
  ```sh
149
148
  dsh plugin --profile dsh-tui add https://github.com/NoNameLeGo/dsh-catppuccin-theme
@@ -171,6 +170,13 @@ dsh plugin --profile dsh-tui add https://github.com/NoNameLeGo/dsh-catppuccin-th
171
170
  4. 选择 **跟随系统** 则回退到官方主题——会还原你启用 Catppuccin 之前
172
171
  的官方偏好(浅色 / 深色 / 跟随系统),而不是强制重置。
173
172
 
173
+ ### Catppuccin 行里的其它选项
174
+
175
+ - **代码高亮风格**:默认 / **注释斜体**(italic-comments)——只影响代码块与 diff 的 shiki 高亮配色。
176
+ - **自定义覆盖**(折叠区,按钮上显示已有条数):按「`--dsw-* 变量: 值`」逐条覆盖单个 token,
177
+ 例如 `--dsw-static-blue-500` → `#89b4fa`。**键名**失焦生效(必须以 `--` 开头,否则该条会被丢弃);
178
+ **值**输入即生效、**清空值即删除该条**;✕ 删除整行。覆盖持久保存,与所选风味一起生效。
179
+
174
180
  ### 玻璃质感
175
181
 
176
182
  在 **设置 → 常规** 的 **Catppuccin 主题** 正下方找到 **玻璃质感** 行:
@@ -196,6 +202,8 @@ dsh plugin --profile dsh-tui add https://github.com/NoNameLeGo/dsh-catppuccin-th
196
202
  - 点击 **检查更新** 即对比 npm 上的最新版与当前版本:已是最新 → 显示当前
197
203
  版本号;发现新版 → 显示新版本号并给出可复制的升级命令(命令中的 profile
198
204
  名自动探测,无需手动替换;探测失败才回退为 `web`)。
205
+ - **自动检查**:默认开启——启动后检查一次、之后每 6 小时一次(该行可关闭);
206
+ **渠道**可选 **稳定版**(只跟 `latest`)或 **Beta 版**(连预发布一起跟)。
199
207
  - 本插件为本地链接 / 源码安装(`link:` / `file:` / git)时不显示 npm 升级
200
208
  命令,会提示改用 `git pull` 或重新构建。
201
209
  - 通道策略:正式版只跟随 `latest` 标签;预发布版同时跟随 `beta`(升级命令
@@ -224,9 +232,10 @@ dsh plugin --profile dsh-tui add https://github.com/NoNameLeGo/dsh-catppuccin-th
224
232
 
225
233
  ```sh
226
234
  pnpm install
227
- pnpm typecheck # tsc --noEmit 类型检查
228
- pnpm test # vitest 跑配色表 / 契约 / e2e 覆盖测试
229
- pnpm build # tsdown 构建 -> lib/index.js(服务端)+ lib/client.js(浏览器)
235
+ pnpm typecheck # tsc --noEmit:src 的类型检查
236
+ pnpm typecheck:tests # tsc --noEmit:tests 的类型检查(vitest esbuild,不做类型检查)
237
+ pnpm test # vitest 跑配色表 / 契约 / e2e 覆盖测试
238
+ pnpm build # tsdown 构建 -> lib/index.js(服务端)+ lib/client.js(浏览器)
230
239
  ```
231
240
 
232
241
  配色表由生成器脚本产出——修改 `scripts/generate-palettes.mjs` 后重跑
@@ -237,8 +246,15 @@ pnpm build # tsdown 构建 -> lib/index.js(服务端)+ lib/client.js
237
246
  node scripts/generate-palettes.mjs [--pin <upstream-sha>]
238
247
  ```
239
248
 
240
- 对外 API(`./client`、`./tui-themes` 子路径导出)的 typedoc 文档生成到
241
- `docs/api/`(GitHub Pages 发布交给 maintainer):
249
+ CHANGELOG 草稿由 conventional 提交生成(提交正文里的 `EN:` 行会被渲染成英文摘要):
250
+
251
+ ```sh
252
+ pnpm changelog:gen # 打印上一 tag 之后的草稿
253
+ pnpm changelog:gen -- --write # 直接写入 [Unreleased] 节
254
+ ```
255
+
256
+ 对外 API(`./client`、`./tui-themes` 子路径导出)的 typedoc 文档**按需本地生成**到
257
+ `docs/api/`(该目录不入库、已进 `.gitignore`;哪天真需要在线版本再接 CI 发布):
242
258
 
243
259
  ```sh
244
260
  pnpm docs:api
package/lib/client.js CHANGED
@@ -134,8 +134,9 @@ window.__ModuleLoader__.load({
134
134
  const style = (0, react.useSyncExternalStore)(subscribePrefs, shikiStyle);
135
135
  const [overridesOpen, setOverridesOpen] = (0, react.useState)(false);
136
136
  const [draftRows, setDraftRows] = (0, react.useState)([]);
137
+ const isTokenKey = (key) => key.trim().startsWith("--");
137
138
  const commitDraft = (index, key, value) => {
138
- if (key.trim() !== "" && value.trim() !== "") {
139
+ if (isTokenKey(key) && value.trim() !== "") {
139
140
  setOverrides({
140
141
  ...overrideMap,
141
142
  [key.trim()]: value
@@ -146,11 +147,15 @@ window.__ModuleLoader__.load({
146
147
  value
147
148
  } : row));
148
149
  };
150
+ /** Rename (or, with an empty/invalid key, delete) a persisted override. The
151
+ * key input is uncontrolled and commits on blur: writing on every keystroke
152
+ * repersisted the entry under each intermediate key, and an intermediate
153
+ * key that is not a `--` token made the row delete itself mid-typing. */
149
154
  const commitPersistedKey = (oldKey, newKey) => {
150
155
  if (newKey === oldKey) return;
151
156
  const next = { ...overrideMap };
152
157
  delete next[oldKey];
153
- if (newKey.trim() !== "") next[newKey.trim()] = overrideMap[oldKey];
158
+ if (isTokenKey(newKey)) next[newKey.trim()] = overrideMap[oldKey];
154
159
  setOverrides(next);
155
160
  };
156
161
  const commitPersistedValue = (key, value) => {
@@ -334,8 +339,8 @@ window.__ModuleLoader__.load({
334
339
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
335
340
  type: "text",
336
341
  "aria-label": t("row.overridesKey"),
337
- value: key,
338
- onChange: (e) => {
342
+ defaultValue: key,
343
+ onBlur: (e) => {
339
344
  commitPersistedKey(key, e.target.value);
340
345
  },
341
346
  placeholder: "--dsw-static-blue-500",
@@ -1198,7 +1203,7 @@ window.__ModuleLoader__.load({
1198
1203
  "--dsw-static-amber-400": "color-mix(in srgb, #ef9f76 70%, #303446)",
1199
1204
  "--dsw-static-amber-500": "#ef9f76",
1200
1205
  "--dsw-static-amber-600": "#ef9f76",
1201
- "--dsw-static-amber-900": "color-mix(in srgb, #ef9f76 32%, #303446)",
1206
+ "--dsw-static-amber-900": "color-mix(in srgb, #ef9f76 18%, #232634)",
1202
1207
  "--dsw-static-blue-100": "color-mix(in srgb, #8caaee 24%, #303446)",
1203
1208
  "--dsw-static-blue-300": "color-mix(in srgb, #8caaee 50%, #303446)",
1204
1209
  "--dsw-static-blue-400": "color-mix(in srgb, #8caaee 66%, #303446)",
@@ -1225,7 +1230,7 @@ window.__ModuleLoader__.load({
1225
1230
  "--dsw-static-green-100": "color-mix(in srgb, #a6d189 18%, #303446)",
1226
1231
  "--dsw-static-green-400": "color-mix(in srgb, #a6d189 80%, #303446)",
1227
1232
  "--dsw-static-green-500": "#a6d189",
1228
- "--dsw-static-green-900": "color-mix(in srgb, #a6d189 30%, #303446)",
1233
+ "--dsw-static-green-900": "color-mix(in srgb, #a6d189 18%, #232634)",
1229
1234
  "--dsw-static-neutral-00": "#c6d0f5",
1230
1235
  "--dsw-static-neutral-1000": "#232634",
1231
1236
  "--dsw-static-neutral-100": "#a5adce",
@@ -1368,7 +1373,7 @@ window.__ModuleLoader__.load({
1368
1373
  "--dsw-static-amber-400": "color-mix(in srgb, #f5a97f 70%, #24273a)",
1369
1374
  "--dsw-static-amber-500": "#f5a97f",
1370
1375
  "--dsw-static-amber-600": "#f5a97f",
1371
- "--dsw-static-amber-900": "color-mix(in srgb, #f5a97f 32%, #24273a)",
1376
+ "--dsw-static-amber-900": "color-mix(in srgb, #f5a97f 18%, #181926)",
1372
1377
  "--dsw-static-blue-100": "color-mix(in srgb, #8aadf4 24%, #24273a)",
1373
1378
  "--dsw-static-blue-300": "color-mix(in srgb, #8aadf4 50%, #24273a)",
1374
1379
  "--dsw-static-blue-400": "color-mix(in srgb, #8aadf4 66%, #24273a)",
@@ -1395,7 +1400,7 @@ window.__ModuleLoader__.load({
1395
1400
  "--dsw-static-green-100": "color-mix(in srgb, #a6da95 18%, #24273a)",
1396
1401
  "--dsw-static-green-400": "color-mix(in srgb, #a6da95 80%, #24273a)",
1397
1402
  "--dsw-static-green-500": "#a6da95",
1398
- "--dsw-static-green-900": "color-mix(in srgb, #a6da95 30%, #24273a)",
1403
+ "--dsw-static-green-900": "color-mix(in srgb, #a6da95 18%, #181926)",
1399
1404
  "--dsw-static-neutral-00": "#cad3f5",
1400
1405
  "--dsw-static-neutral-1000": "#181926",
1401
1406
  "--dsw-static-neutral-100": "#a5adcb",
@@ -1538,7 +1543,7 @@ window.__ModuleLoader__.load({
1538
1543
  "--dsw-static-amber-400": "color-mix(in srgb, #fab387 70%, #1e1e2e)",
1539
1544
  "--dsw-static-amber-500": "#fab387",
1540
1545
  "--dsw-static-amber-600": "#fab387",
1541
- "--dsw-static-amber-900": "color-mix(in srgb, #fab387 32%, #1e1e2e)",
1546
+ "--dsw-static-amber-900": "color-mix(in srgb, #fab387 18%, #11111b)",
1542
1547
  "--dsw-static-blue-100": "color-mix(in srgb, #89b4fa 24%, #1e1e2e)",
1543
1548
  "--dsw-static-blue-300": "color-mix(in srgb, #89b4fa 50%, #1e1e2e)",
1544
1549
  "--dsw-static-blue-400": "color-mix(in srgb, #89b4fa 66%, #1e1e2e)",
@@ -1565,7 +1570,7 @@ window.__ModuleLoader__.load({
1565
1570
  "--dsw-static-green-100": "color-mix(in srgb, #a6e3a1 18%, #1e1e2e)",
1566
1571
  "--dsw-static-green-400": "color-mix(in srgb, #a6e3a1 80%, #1e1e2e)",
1567
1572
  "--dsw-static-green-500": "#a6e3a1",
1568
- "--dsw-static-green-900": "color-mix(in srgb, #a6e3a1 30%, #1e1e2e)",
1573
+ "--dsw-static-green-900": "color-mix(in srgb, #a6e3a1 18%, #11111b)",
1569
1574
  "--dsw-static-neutral-00": "#cdd6f4",
1570
1575
  "--dsw-static-neutral-1000": "#11111b",
1571
1576
  "--dsw-static-neutral-100": "#a6adc8",
@@ -1847,7 +1852,7 @@ window.__ModuleLoader__.load({
1847
1852
  },
1848
1853
  {
1849
1854
  attribute: "data-dsh-glass-settings",
1850
- selector: "[class*=\"VOzbGW_overlay\"]"
1855
+ selector: ":has(> [role=\"dialog\"][aria-modal=\"true\"])"
1851
1856
  }
1852
1857
  ];
1853
1858
  function stamp(seam) {
@@ -1879,6 +1884,7 @@ window.__ModuleLoader__.load({
1879
1884
  stampAll();
1880
1885
  let frame;
1881
1886
  let dirty = false;
1887
+ let disposed = false;
1882
1888
  const stampFrame = () => {
1883
1889
  frame = void 0;
1884
1890
  if (!dirty) return;
@@ -1886,6 +1892,7 @@ window.__ModuleLoader__.load({
1886
1892
  stampAll();
1887
1893
  };
1888
1894
  const schedule = () => {
1895
+ if (disposed) return;
1889
1896
  dirty = true;
1890
1897
  if (frame !== void 0) return;
1891
1898
  frame = requestAnimationFrame(stampFrame);
@@ -1896,6 +1903,7 @@ window.__ModuleLoader__.load({
1896
1903
  subtree: true
1897
1904
  });
1898
1905
  return () => {
1906
+ disposed = true;
1899
1907
  observer.disconnect();
1900
1908
  if (frame !== void 0) cancelAnimationFrame(frame);
1901
1909
  frame = void 0;
@@ -2415,6 +2423,7 @@ window.__ModuleLoader__.load({
2415
2423
  max,
2416
2424
  step,
2417
2425
  value,
2426
+ "aria-valuetext": `${value}${unit}`,
2418
2427
  onChange: (e) => {
2419
2428
  onChange(clamp(Number(e.target.value)));
2420
2429
  }
@@ -2792,13 +2801,23 @@ window.__ModuleLoader__.load({
2792
2801
  const [dismissedConflicts, setDismissedConflicts] = (0, react.useState)(0);
2793
2802
  const [retryRemaining, setRetryRemaining] = (0, react.useState)(null);
2794
2803
  const retryTimer = (0, react.useRef)(void 0);
2804
+ const tickerTimer = (0, react.useRef)(void 0);
2805
+ const retriesUsed = (0, react.useRef)(0);
2795
2806
  const mounted = (0, react.useRef)(true);
2796
- const runCheck = async (withChannel = channelValue) => {
2807
+ /** Drop a pending auto-retry (its timeout and its countdown ticker). */
2808
+ const cancelRetry = () => {
2797
2809
  if (retryTimer.current !== void 0) {
2798
2810
  window.clearTimeout(retryTimer.current);
2799
2811
  retryTimer.current = void 0;
2800
- setRetryRemaining(null);
2801
2812
  }
2813
+ if (tickerTimer.current !== void 0) {
2814
+ window.clearInterval(tickerTimer.current);
2815
+ tickerTimer.current = void 0;
2816
+ }
2817
+ setRetryRemaining(null);
2818
+ };
2819
+ const runCheck = async (withChannel = channelValue) => {
2820
+ cancelRetry();
2802
2821
  setPhase("checking");
2803
2822
  setCopied(false);
2804
2823
  let next;
@@ -2813,27 +2832,36 @@ window.__ModuleLoader__.load({
2813
2832
  }
2814
2833
  setPayload(next);
2815
2834
  setPhase("done");
2816
- if (!next.ok) {
2835
+ if (!next.ok && retriesUsed.current === 0) {
2836
+ retriesUsed.current = 1;
2817
2837
  const target = Date.now() + UPDATE_RETRY_AFTER_MS;
2818
2838
  setRetryRemaining(UPDATE_RETRY_AFTER_MS / 1e3);
2819
2839
  const ticker = window.setInterval(() => {
2820
2840
  const remaining = Math.max(0, Math.ceil((target - Date.now()) / 1e3));
2821
2841
  setRetryRemaining(remaining);
2822
- if (remaining <= 0) window.clearInterval(ticker);
2842
+ if (remaining <= 0) {
2843
+ window.clearInterval(ticker);
2844
+ tickerTimer.current = void 0;
2845
+ }
2823
2846
  }, 1e3);
2847
+ tickerTimer.current = ticker;
2824
2848
  retryTimer.current = window.setTimeout(() => {
2825
- window.clearInterval(ticker);
2826
- retryTimer.current = void 0;
2827
- setRetryRemaining(null);
2828
2849
  if (mounted.current) runCheck();
2850
+ else cancelRetry();
2829
2851
  }, UPDATE_RETRY_AFTER_MS);
2830
2852
  }
2831
2853
  };
2854
+ /** A user-initiated check: re-arms the one auto-retry budget (item V). */
2855
+ const runManualCheck = () => {
2856
+ retriesUsed.current = 0;
2857
+ runCheck();
2858
+ };
2832
2859
  (0, react.useEffect)(() => {
2833
2860
  mounted.current = true;
2834
2861
  return () => {
2835
2862
  mounted.current = false;
2836
2863
  if (retryTimer.current !== void 0) window.clearTimeout(retryTimer.current);
2864
+ if (tickerTimer.current !== void 0) window.clearInterval(tickerTimer.current);
2837
2865
  };
2838
2866
  }, []);
2839
2867
  const autoResult = lastAutoResult();
@@ -3035,7 +3063,7 @@ window.__ModuleLoader__.load({
3035
3063
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3036
3064
  type: "button",
3037
3065
  disabled: phase === "checking",
3038
- onClick: () => void runCheck(),
3066
+ onClick: runManualCheck,
3039
3067
  style: {
3040
3068
  ...buttonStyle,
3041
3069
  cursor: phase === "checking" ? "default" : "pointer",
@@ -3178,7 +3206,7 @@ window.__ModuleLoader__.load({
3178
3206
  }),
3179
3207
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3180
3208
  type: "button",
3181
- onClick: () => void runCheck(),
3209
+ onClick: runManualCheck,
3182
3210
  style: {
3183
3211
  ...buttonStyle,
3184
3212
  cursor: "pointer"
@@ -3432,13 +3460,19 @@ window.__ModuleLoader__.load({
3432
3460
  localStorage.setItem(UPDATE_CHANNEL_KEY, value);
3433
3461
  } catch {}
3434
3462
  }
3435
- /** Read the persisted token overrides (unparseable/absent → empty map). */
3463
+ /** Read the persisted token overrides (unparseable/absent → empty map).
3464
+ *
3465
+ * Sanitized on read (`sanitizeOverrides`, the same guard the settings document
3466
+ * goes through) so localStorage and the durable section always have the SAME
3467
+ * shape. Without it a key that is not a `--` token (e.g. `dsw-static-blue-500`
3468
+ * typed without the dashes) lives in localStorage only: the hydration compares
3469
+ * the two shapes, sees a difference, and "the document wins" overwrites the
3470
+ * entry — the user's row vanished a debounce-beat after it was typed. */
3436
3471
  function readOverrides() {
3437
3472
  try {
3438
3473
  const raw = localStorage.getItem(OVERRIDES_KEY);
3439
3474
  if (raw === null) return {};
3440
- const parsed = JSON.parse(raw);
3441
- return typeof parsed === "object" && parsed !== null ? parsed : {};
3475
+ return sanitizeOverrides(JSON.parse(raw));
3442
3476
  } catch {
3443
3477
  return {};
3444
3478
  }