@nonamelego/dsh-catppuccin 0.5.0 → 0.5.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -6,56 +6,112 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
- //#region src/client/CatppuccinRow.tsx
9
+ //#region src/client/controls.tsx
10
10
  /**
11
- * Catppuccin settings row — one General-section preference row listing the
12
- * four flavour themes plus a "follow system" (off) choice. Selecting a
13
- * flavour calls the injected `select`, which switches the official
14
- * ThemeRuntime and persists the choice. The row subscribes to `theme/change`
15
- * so the highlighted flavour tracks the live preference.
11
+ * Shared settings UI for the plugin's own rows — one source of truth for
12
+ * geometry, tokens and the two selection idioms.
16
13
  *
17
- * Beyond the flavour pick the row carries:
18
- * - per-flavour localized subtitles (item DD) under the button labels;
19
- * - the shiki style pick (item M, `default` / `italic-comments`);
20
- * - a collapsible power-user token-override editor (item K);
21
- * - a "?" help affordance (item J) — no framework tooltip API exists in
22
- * dsh-client-ui-slots yet, so the help text rides the native `title`
23
- * attribute plus an accessible name.
14
+ * The plugin injects three rows (flavour / glass / update) into the host's
15
+ * settings page. They had grown three private copies of the same segmented
16
+ * pick and two of the same switch, which drifted apart (padding, separators,
17
+ * on-state colour, knob size). The recipes and the two stateful controls live
18
+ * here; the rows only compose them.
19
+ *
20
+ * Selection language — exactly two idioms, deliberately:
21
+ * - enumerated pick (shiki style, update channel, glass mode): the host's
22
+ * "active tab" pair, `state-business-tertiary` fill + `state-business-primary`
23
+ * text (`--dsw` alias layer, so it follows the active flavour);
24
+ * - object pick (flavour swatch buttons): `interactive-bg-active` fill +
25
+ * `state-business-primary` hairline + `label-primary` text.
26
+ *
27
+ * The glass row keeps its own CSS module for the *material* only (blur, glow,
28
+ * translucent knob) and mirrors the numbers below — shape and state semantics
29
+ * are shared, texture belongs to the skin. Focus rings stay with the UA
30
+ * default for the inline-styled controls; the glass CSS draws its own.
24
31
  */
25
- /** 普通按钮行内样式。 */
26
- const buttonBase = {
27
- display: "inline-flex",
28
- alignItems: "center",
29
- gap: 6,
30
- padding: "6px 12px",
31
- borderRadius: 8,
32
- border: "1px solid var(--dsw-alias-border-l1)",
33
- background: "transparent",
32
+ /** Outer row: hairline separator + the host's row rhythm (14/22 + 12/18). */
33
+ const ROW = {
34
+ borderBottom: "1px solid var(--dsw-alias-border-l2)",
35
+ display: "flex",
36
+ flexDirection: "column",
37
+ gap: 10,
38
+ padding: "16px 0"
39
+ };
40
+ /** Row title (14/22, host `groupTitle`). */
41
+ const TITLE = {
34
42
  color: "var(--dsw-alias-label-primary)",
35
- cursor: "pointer",
36
- font: "inherit"
43
+ fontSize: 14,
44
+ lineHeight: "22px",
45
+ display: "flex",
46
+ alignItems: "center"
47
+ };
48
+ /** Row description (12/18, host `groupSub`). */
49
+ const DESCRIPTION = {
50
+ color: "var(--dsw-alias-label-tertiary)",
51
+ fontSize: 12,
52
+ lineHeight: "18px"
53
+ };
54
+ /** Inline label that sits next to a control (master switch / mode pick). */
55
+ const ROW_LABEL = {
56
+ color: "var(--dsw-alias-label-secondary)",
57
+ fontSize: 12,
58
+ lineHeight: "18px"
37
59
  };
38
- /** 选中态按钮行内样式。 */
39
- const buttonActive = {
40
- ...buttonBase,
41
- border: "1px solid var(--dsw-alias-border-l3)",
60
+ /** Secondary ("ghost") action button. */
61
+ function actionButton(disabled = false) {
62
+ return {
63
+ display: "inline-flex",
64
+ alignItems: "center",
65
+ gap: 6,
66
+ padding: "6px 12px",
67
+ borderRadius: 8,
68
+ border: "1px solid var(--dsw-alias-border-l1)",
69
+ background: "transparent",
70
+ color: "var(--dsw-alias-label-primary)",
71
+ cursor: disabled ? "default" : "pointer",
72
+ opacity: disabled ? .6 : 1,
73
+ font: "inherit"
74
+ };
75
+ }
76
+ /** Object-pick base; the active variant is {@link SWATCH_ACTIVE}. */
77
+ const SWATCH_BASE = {
78
+ ...actionButton(),
79
+ border: "1px solid var(--dsw-alias-border-l1)"
80
+ };
81
+ /** Object-pick active: accent hairline + the theme's own "raised" fill. */
82
+ const SWATCH_ACTIVE = {
83
+ ...SWATCH_BASE,
84
+ border: "1px solid var(--dsw-alias-state-business-primary)",
42
85
  background: "var(--dsw-alias-interactive-bg-active)"
43
86
  };
44
- const inputBase = {
87
+ /** Text/number input — shared by the override editor and the glass knobs. */
88
+ const INPUT = {
45
89
  background: "var(--dsw-alias-bg-layer-2)",
46
- border: "1px solid var(--dsw-alias-border-l1)",
47
- borderRadius: 6,
90
+ border: "0.5px solid var(--dsw-alias-border-l4)",
91
+ borderRadius: 8,
48
92
  padding: "4px 8px",
49
93
  color: "var(--dsw-alias-label-primary)",
50
94
  fontSize: 12,
51
95
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
52
96
  };
53
- /**
54
- * A "?" help affordance: an accessible button that carries the full help
55
- * text in the native `title` tooltip. No visual styling beyond the token
56
- * palette — the affordance follows the row's label colour.
57
- */
58
- function HelpIcon({ label, help }) {
97
+ /** Card idiom taken from the host (`settings-models` editor/cards):
98
+ * module surface + 12px radius + 14/16 padding + 14px rhythm. */
99
+ const CARD = {
100
+ display: "flex",
101
+ flexDirection: "column",
102
+ gap: 14,
103
+ padding: "14px 16px",
104
+ borderRadius: 12,
105
+ background: "var(--dsw-alias-bg-module-platform)"
106
+ };
107
+ /** List-entry idiom taken from the host: hairline frame + 10px radius. */
108
+ const ENTRY = {
109
+ border: "0.5px solid var(--dsw-alias-border-l4)",
110
+ borderRadius: 10,
111
+ padding: 6
112
+ };
113
+ /** The "?" help affordance: accessible name + native title tooltip. */
114
+ function HelpBadge({ label, help }) {
59
115
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
60
116
  role: "img",
61
117
  "aria-label": label,
@@ -79,18 +135,47 @@ window.__ModuleLoader__.load({
79
135
  children: "?"
80
136
  });
81
137
  }
82
- /** The localised subtitle of one flavour button (`flavor.<id>.subtitle`);
83
- * null when the active locale carries no such key. */
84
- function subtitleText(buttonId, t) {
85
- const flavorId = buttonId.replace("catppuccin-", "");
86
- if (flavorId === buttonId) return null;
87
- const key = `flavor.${flavorId}.subtitle`;
88
- const text = t(key);
89
- return text === key ? null : text;
138
+ /**
139
+ * Index a segmented pick should focus for one key, clamped to the option
140
+ * range. Pure so the roving-focus math is testable without a DOM.
141
+ * @param current - id of the currently focused (or selected) option.
142
+ * @param direction - arrow direction, or a home/end jump.
143
+ * @param options - the pick's options, in display order.
144
+ * @returns the option index to focus.
145
+ */
146
+ function segmentedTargetIndex(current, direction, options) {
147
+ if (direction === "home") return 0;
148
+ if (direction === "end") return options.length - 1;
149
+ const found = options.findIndex((option) => option.id === current);
150
+ const index = found === -1 ? 0 : found;
151
+ return Math.min(options.length - 1, Math.max(0, index + direction));
90
152
  }
91
- /** One two-sided segmented pick using the row's token colours. */
92
- function Segmented$1(props) {
153
+ /** One enumerated pick. Roving tabindex (WAI-ARIA): only the focused cell is
154
+ * tabbable, arrows/Home/End move focus WITHOUT selecting, Enter/Space or a
155
+ * click commits. */
156
+ function Segmented(props) {
93
157
  const { label, value, options, onSelect } = props;
158
+ const [focused, setFocused] = (0, react.useState)(null);
159
+ const refs = (0, react.useRef)(/* @__PURE__ */ new Map());
160
+ const focusAt = (index) => {
161
+ const target = options[index];
162
+ if (target === void 0) return;
163
+ setFocused(target.id);
164
+ refs.current.get(target.id)?.focus();
165
+ };
166
+ const onKeyDown = (event) => {
167
+ const direction = {
168
+ ArrowLeft: -1,
169
+ ArrowUp: -1,
170
+ ArrowRight: 1,
171
+ ArrowDown: 1,
172
+ Home: "home",
173
+ End: "end"
174
+ }[event.key];
175
+ if (direction === void 0) return;
176
+ event.preventDefault();
177
+ focusAt(segmentedTargetIndex(focused ?? value, direction, options));
178
+ };
94
179
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
95
180
  style: {
96
181
  display: "inline-flex",
@@ -100,27 +185,125 @@ window.__ModuleLoader__.load({
100
185
  },
101
186
  role: "group",
102
187
  "aria-label": label,
103
- children: options.map((option, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
104
- type: "button",
105
- "aria-pressed": option.id === value,
106
- onClick: () => {
107
- onSelect(option.id);
108
- },
109
- style: {
110
- height: 26,
111
- padding: "0 12px",
112
- border: "none",
113
- borderLeft: index > 0 ? "1px solid var(--dsw-alias-border-l2)" : void 0,
114
- background: option.id === value ? "var(--dsw-alias-state-business-tertiary)" : "transparent",
115
- color: option.id === value ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-secondary)",
116
- fontSize: 12,
117
- lineHeight: "18px",
118
- cursor: "pointer"
119
- },
120
- children: option.label
121
- }, option.id))
188
+ children: options.map((option, index) => {
189
+ const active = option.id === value;
190
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
191
+ ref: (node) => {
192
+ if (node === null) refs.current.delete(option.id);
193
+ else refs.current.set(option.id, node);
194
+ },
195
+ type: "button",
196
+ "aria-pressed": active,
197
+ tabIndex: option.id === (focused ?? value) ? 0 : -1,
198
+ onFocus: () => {
199
+ setFocused(option.id);
200
+ },
201
+ onBlur: () => {
202
+ setFocused((now) => now === option.id ? null : now);
203
+ },
204
+ onKeyDown,
205
+ onClick: () => {
206
+ onSelect(option.id);
207
+ },
208
+ style: {
209
+ height: 26,
210
+ padding: "0 12px",
211
+ border: "none",
212
+ borderLeft: index > 0 ? "1px solid var(--dsw-alias-border-l2)" : void 0,
213
+ background: active ? "var(--dsw-alias-state-business-tertiary)" : "transparent",
214
+ color: active ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-secondary)",
215
+ fontSize: 12,
216
+ lineHeight: "18px",
217
+ cursor: "pointer"
218
+ },
219
+ children: option.label
220
+ }, option.id);
221
+ })
122
222
  });
123
223
  }
224
+ /** Track recipe for {@link Switch}; exported so a skin can mirror the shape. */
225
+ function switchTrack(on) {
226
+ return {
227
+ position: "relative",
228
+ display: "inline-flex",
229
+ flex: "none",
230
+ width: 44,
231
+ height: 24,
232
+ padding: 0,
233
+ border: "1px solid var(--dsw-alias-border-l2)",
234
+ borderRadius: 12,
235
+ background: on ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-bg-layer-2)",
236
+ cursor: "pointer"
237
+ };
238
+ }
239
+ /** Knob recipe for {@link Switch} (rides the track, carries the check). */
240
+ function switchKnob(on) {
241
+ return {
242
+ position: "absolute",
243
+ top: 3,
244
+ left: 3,
245
+ display: "inline-flex",
246
+ alignItems: "center",
247
+ justifyContent: "center",
248
+ width: 16,
249
+ height: 16,
250
+ borderRadius: "50%",
251
+ background: on ? "var(--dsw-alias-bg-layer-1)" : "var(--dsw-alias-label-tertiary)",
252
+ color: on ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-tertiary)",
253
+ fontSize: 10,
254
+ lineHeight: 1,
255
+ transform: on ? "translateX(22px)" : void 0,
256
+ transition: "transform 0.18s cubic-bezier(0.4, 0, 0.2, 1)"
257
+ };
258
+ }
259
+ /** Boolean switch: on = accent track + surface knob + check. */
260
+ function Switch(props) {
261
+ const { label, checked, onChange } = props;
262
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
263
+ type: "button",
264
+ role: "switch",
265
+ "aria-checked": checked,
266
+ "aria-label": label,
267
+ onClick: () => {
268
+ onChange(!checked);
269
+ },
270
+ style: switchTrack(checked),
271
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
272
+ "aria-hidden": "true",
273
+ style: switchKnob(checked),
274
+ children: checked ? "✓" : ""
275
+ })
276
+ });
277
+ }
278
+ //#endregion
279
+ //#region src/client/CatppuccinRow.tsx
280
+ /**
281
+ * Catppuccin settings row — one General-section preference row listing the
282
+ * four flavour themes plus a "follow system" (off) choice. Selecting a
283
+ * flavour calls the injected `select`, which switches the official
284
+ * ThemeRuntime and persists the choice. The row subscribes to `theme/change`
285
+ * so the highlighted flavour tracks the live preference.
286
+ *
287
+ * Beyond the flavour pick the row carries:
288
+ * - per-flavour localized subtitles (item DD) under the button labels;
289
+ * - the shiki style pick (item M, `default` / `italic-comments`);
290
+ * - a collapsible power-user token-override editor (item K);
291
+ * - a "?" help affordance (item J) — no framework tooltip API exists in
292
+ * dsh-client-ui-slots yet, so the help text rides the native `title`
293
+ * attribute plus an accessible name.
294
+ *
295
+ * Recipes, the segmented pick and the switch come from ./controls.tsx, so
296
+ * every row in this plugin shares one geometry and one selection language.
297
+ */
298
+ /** The localised subtitle of one flavour button (`flavor.<id>.subtitle`);
299
+ * null when the active locale carries no such key. */
300
+ function subtitleText(buttonId, t) {
301
+ const flavorId = buttonId.replace("catppuccin-", "");
302
+ if (flavorId === buttonId) return null;
303
+ const key = `flavor.${flavorId}.subtitle`;
304
+ const text = t(key);
305
+ return text === key ? null : text;
306
+ }
124
307
  /**
125
308
  * Render the Catppuccin row: title, one swatch button per flavour, and a
126
309
  * follow-system button. The active flavour is highlighted; clicking a flavour
@@ -132,6 +315,7 @@ window.__ModuleLoader__.load({
132
315
  const choice = (0, react.useSyncExternalStore)(subscribe, current);
133
316
  const overrideMap = (0, react.useSyncExternalStore)(subscribePrefs, overrides);
134
317
  const style = (0, react.useSyncExternalStore)(subscribePrefs, shikiStyle);
318
+ const accentHint = themes.find((flavor) => flavor.id === choice)?.accent ?? "var(--dsw-static-blue-500)";
135
319
  const [overridesOpen, setOverridesOpen] = (0, react.useState)(false);
136
320
  const [draftRows, setDraftRows] = (0, react.useState)([]);
137
321
  const commitDraft = (index, key, value) => {
@@ -165,33 +349,17 @@ window.__ModuleLoader__.load({
165
349
  setOverrides(next);
166
350
  };
167
351
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
168
- style: {
169
- borderBottom: "1px solid var(--dsw-alias-border-l2)",
170
- display: "flex",
171
- flexDirection: "column",
172
- gap: "10px",
173
- padding: "16px 0"
174
- },
352
+ style: ROW,
175
353
  children: [
176
354
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
177
- style: {
178
- color: "var(--dsw-alias-label-primary)",
179
- fontSize: 14,
180
- lineHeight: "22px",
181
- display: "flex",
182
- alignItems: "center"
183
- },
184
- children: [t("row.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HelpIcon, {
355
+ style: TITLE,
356
+ children: [t("row.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HelpBadge, {
185
357
  label: t("row.helpLabel"),
186
358
  help: t("row.help")
187
359
  })]
188
360
  }),
189
361
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
190
- style: {
191
- color: "var(--dsw-alias-label-tertiary)",
192
- fontSize: 12,
193
- lineHeight: "18px"
194
- },
362
+ style: DESCRIPTION,
195
363
  children: t("row.description")
196
364
  }),
197
365
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -208,7 +376,7 @@ window.__ModuleLoader__.load({
208
376
  "aria-pressed": selected,
209
377
  onClick: () => void select(flavor.id),
210
378
  style: {
211
- ...selected ? buttonActive : buttonBase,
379
+ ...selected ? SWATCH_ACTIVE : SWATCH_BASE,
212
380
  flexDirection: subtitle !== null ? "column" : "row",
213
381
  alignItems: subtitle !== null ? "flex-start" : "center",
214
382
  gap: subtitle !== null ? 2 : 6,
@@ -243,7 +411,7 @@ window.__ModuleLoader__.load({
243
411
  type: "button",
244
412
  "aria-pressed": choice === "off",
245
413
  onClick: () => void select("off"),
246
- style: choice === "off" ? buttonActive : buttonBase,
414
+ style: choice === "off" ? SWATCH_ACTIVE : SWATCH_BASE,
247
415
  children: t("row.off")
248
416
  }, "off")]
249
417
  }),
@@ -255,13 +423,9 @@ window.__ModuleLoader__.load({
255
423
  alignItems: "center"
256
424
  },
257
425
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
258
- style: {
259
- color: "var(--dsw-alias-label-secondary)",
260
- fontSize: 12,
261
- lineHeight: "18px"
262
- },
426
+ style: ROW_LABEL,
263
427
  children: t("row.shikiStyle")
264
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segmented$1, {
428
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segmented, {
265
429
  label: t("row.shikiStyle"),
266
430
  value: style,
267
431
  options: [{
@@ -287,7 +451,7 @@ window.__ModuleLoader__.load({
287
451
  setOverridesOpen(!overridesOpen);
288
452
  },
289
453
  style: {
290
- ...buttonBase,
454
+ ...actionButton(),
291
455
  alignSelf: "flex-start",
292
456
  fontSize: 12,
293
457
  padding: "4px 10px"
@@ -302,9 +466,8 @@ window.__ModuleLoader__.load({
302
466
  ]
303
467
  }), overridesOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
304
468
  style: {
305
- display: "flex",
306
- flexDirection: "column",
307
- gap: "6px"
469
+ ...CARD,
470
+ gap: 8
308
471
  },
309
472
  children: [
310
473
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -325,9 +488,10 @@ window.__ModuleLoader__.load({
325
488
  }),
326
489
  Object.entries(overrideMap).map(([key, value]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
327
490
  style: {
491
+ ...ENTRY,
328
492
  display: "flex",
329
493
  flexWrap: "wrap",
330
- gap: "6px",
494
+ gap: 6,
331
495
  alignItems: "center"
332
496
  },
333
497
  children: [
@@ -341,7 +505,7 @@ window.__ModuleLoader__.load({
341
505
  placeholder: "--dsw-static-blue-500",
342
506
  style: {
343
507
  flex: "1 1 220px",
344
- ...inputBase
508
+ ...INPUT
345
509
  }
346
510
  }),
347
511
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -351,10 +515,10 @@ window.__ModuleLoader__.load({
351
515
  onChange: (e) => {
352
516
  commitPersistedValue(key, e.target.value);
353
517
  },
354
- placeholder: "#89b4fa",
518
+ placeholder: accentHint,
355
519
  style: {
356
520
  flex: "1 1 140px",
357
- ...inputBase
521
+ ...INPUT
358
522
  }
359
523
  }),
360
524
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -364,7 +528,7 @@ window.__ModuleLoader__.load({
364
528
  removePersisted(key);
365
529
  },
366
530
  style: {
367
- ...buttonBase,
531
+ ...actionButton(),
368
532
  padding: "4px 8px",
369
533
  fontSize: 12
370
534
  },
@@ -374,9 +538,10 @@ window.__ModuleLoader__.load({
374
538
  }, key)),
375
539
  draftRows.map((row, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
376
540
  style: {
541
+ ...ENTRY,
377
542
  display: "flex",
378
543
  flexWrap: "wrap",
379
- gap: "6px",
544
+ gap: 6,
380
545
  alignItems: "center"
381
546
  },
382
547
  children: [
@@ -390,7 +555,7 @@ window.__ModuleLoader__.load({
390
555
  placeholder: "--dsw-static-blue-500",
391
556
  style: {
392
557
  flex: "1 1 220px",
393
- ...inputBase
558
+ ...INPUT
394
559
  }
395
560
  }),
396
561
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -400,10 +565,10 @@ window.__ModuleLoader__.load({
400
565
  onChange: (e) => {
401
566
  commitDraft(index, row.key, e.target.value);
402
567
  },
403
- placeholder: "#89b4fa",
568
+ placeholder: accentHint,
404
569
  style: {
405
570
  flex: "1 1 140px",
406
- ...inputBase
571
+ ...INPUT
407
572
  }
408
573
  }),
409
574
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -413,7 +578,7 @@ window.__ModuleLoader__.load({
413
578
  setDraftRows(draftRows.filter((_, i) => i !== index));
414
579
  },
415
580
  style: {
416
- ...buttonBase,
581
+ ...actionButton(),
417
582
  padding: "4px 8px",
418
583
  fontSize: 12
419
584
  },
@@ -430,7 +595,7 @@ window.__ModuleLoader__.load({
430
595
  }]);
431
596
  },
432
597
  style: {
433
- ...buttonBase,
598
+ ...actionButton(),
434
599
  alignSelf: "flex-start",
435
600
  fontSize: 12,
436
601
  padding: "4px 10px"
@@ -1208,9 +1373,9 @@ window.__ModuleLoader__.load({
1208
1373
  "--dsw-static-blue-50p": "color-mix(in srgb, #8caaee 14%, #303446)",
1209
1374
  "--dsw-static-blue-600": "color-mix(in srgb, #8caaee 84%, #303446)",
1210
1375
  "--dsw-static-blue-75": "color-mix(in srgb, #8caaee 18%, #303446)",
1211
- "--dsw-static-blue-800": "color-mix(in srgb, #8caaee 52%, #303446)",
1212
- "--dsw-static-blue-900": "color-mix(in srgb, #8caaee 40%, #303446)",
1213
- "--dsw-static-blue-950": "color-mix(in srgb, #8caaee 28%, #303446)",
1376
+ "--dsw-static-blue-800": "color-mix(in srgb, #8caaee 18%, #232634)",
1377
+ "--dsw-static-blue-900": "color-mix(in srgb, #8caaee 14%, #232634)",
1378
+ "--dsw-static-blue-950": "color-mix(in srgb, #8caaee 10%, #232634)",
1214
1379
  "--dsw-static-deepseek-100": "color-mix(in srgb, #8caaee 24%, #303446)",
1215
1380
  "--dsw-static-deepseek-200": "color-mix(in srgb, #8caaee 36%, #303446)",
1216
1381
  "--dsw-static-deepseek-300": "color-mix(in srgb, #8caaee 50%, #303446)",
@@ -1220,8 +1385,8 @@ window.__ModuleLoader__.load({
1220
1385
  "--dsw-static-deepseek-50": "color-mix(in srgb, #8caaee 10%, #303446)",
1221
1386
  "--dsw-static-deepseek-600": "color-mix(in srgb, #8caaee 84%, #303446)",
1222
1387
  "--dsw-static-deepseek-700-delete": "color-mix(in srgb, #8caaee 68%, #303446)",
1223
- "--dsw-static-deepseek-800": "color-mix(in srgb, #8caaee 52%, #303446)",
1224
- "--dsw-static-deepseek-900": "color-mix(in srgb, #8caaee 40%, #303446)",
1388
+ "--dsw-static-deepseek-800": "color-mix(in srgb, #8caaee 18%, #232634)",
1389
+ "--dsw-static-deepseek-900": "color-mix(in srgb, #8caaee 14%, #232634)",
1225
1390
  "--dsw-static-green-100": "color-mix(in srgb, #a6d189 18%, #303446)",
1226
1391
  "--dsw-static-green-400": "color-mix(in srgb, #a6d189 80%, #303446)",
1227
1392
  "--dsw-static-green-500": "#a6d189",
@@ -1332,7 +1497,7 @@ window.__ModuleLoader__.load({
1332
1497
  "--dsw-alias-scrollbar-bg-l2": "var(--dsw-static-neutral-600)",
1333
1498
  "--dsw-alias-scrollbar-hover-l1": "var(--dsw-static-neutral-600)",
1334
1499
  "--dsw-alias-scrollbar-hover-l2": "var(--dsw-static-neutral-550)",
1335
- "--dsw-alias-state-business-primary": "var(--dsw-static-deepseek-400)",
1500
+ "--dsw-alias-state-business-primary": "var(--dsw-static-deepseek-500)",
1336
1501
  "--dsw-alias-state-business-tertiary": "var(--dsw-static-deepseek-800)",
1337
1502
  "--dsw-alias-state-error-primary": "var(--dsw-static-red-400)",
1338
1503
  "--dsw-alias-state-error-secondary": "var(--dsw-static-red-400)",
@@ -1378,9 +1543,9 @@ window.__ModuleLoader__.load({
1378
1543
  "--dsw-static-blue-50p": "color-mix(in srgb, #8aadf4 14%, #24273a)",
1379
1544
  "--dsw-static-blue-600": "color-mix(in srgb, #8aadf4 84%, #24273a)",
1380
1545
  "--dsw-static-blue-75": "color-mix(in srgb, #8aadf4 18%, #24273a)",
1381
- "--dsw-static-blue-800": "color-mix(in srgb, #8aadf4 52%, #24273a)",
1382
- "--dsw-static-blue-900": "color-mix(in srgb, #8aadf4 40%, #24273a)",
1383
- "--dsw-static-blue-950": "color-mix(in srgb, #8aadf4 28%, #24273a)",
1546
+ "--dsw-static-blue-800": "color-mix(in srgb, #8aadf4 18%, #181926)",
1547
+ "--dsw-static-blue-900": "color-mix(in srgb, #8aadf4 14%, #181926)",
1548
+ "--dsw-static-blue-950": "color-mix(in srgb, #8aadf4 10%, #181926)",
1384
1549
  "--dsw-static-deepseek-100": "color-mix(in srgb, #8aadf4 24%, #24273a)",
1385
1550
  "--dsw-static-deepseek-200": "color-mix(in srgb, #8aadf4 36%, #24273a)",
1386
1551
  "--dsw-static-deepseek-300": "color-mix(in srgb, #8aadf4 50%, #24273a)",
@@ -1390,8 +1555,8 @@ window.__ModuleLoader__.load({
1390
1555
  "--dsw-static-deepseek-50": "color-mix(in srgb, #8aadf4 10%, #24273a)",
1391
1556
  "--dsw-static-deepseek-600": "color-mix(in srgb, #8aadf4 84%, #24273a)",
1392
1557
  "--dsw-static-deepseek-700-delete": "color-mix(in srgb, #8aadf4 68%, #24273a)",
1393
- "--dsw-static-deepseek-800": "color-mix(in srgb, #8aadf4 52%, #24273a)",
1394
- "--dsw-static-deepseek-900": "color-mix(in srgb, #8aadf4 40%, #24273a)",
1558
+ "--dsw-static-deepseek-800": "color-mix(in srgb, #8aadf4 18%, #181926)",
1559
+ "--dsw-static-deepseek-900": "color-mix(in srgb, #8aadf4 14%, #181926)",
1395
1560
  "--dsw-static-green-100": "color-mix(in srgb, #a6da95 18%, #24273a)",
1396
1561
  "--dsw-static-green-400": "color-mix(in srgb, #a6da95 80%, #24273a)",
1397
1562
  "--dsw-static-green-500": "#a6da95",
@@ -1502,7 +1667,7 @@ window.__ModuleLoader__.load({
1502
1667
  "--dsw-alias-scrollbar-bg-l2": "var(--dsw-static-neutral-600)",
1503
1668
  "--dsw-alias-scrollbar-hover-l1": "var(--dsw-static-neutral-600)",
1504
1669
  "--dsw-alias-scrollbar-hover-l2": "var(--dsw-static-neutral-550)",
1505
- "--dsw-alias-state-business-primary": "var(--dsw-static-deepseek-400)",
1670
+ "--dsw-alias-state-business-primary": "var(--dsw-static-deepseek-500)",
1506
1671
  "--dsw-alias-state-business-tertiary": "var(--dsw-static-deepseek-800)",
1507
1672
  "--dsw-alias-state-error-primary": "var(--dsw-static-red-400)",
1508
1673
  "--dsw-alias-state-error-secondary": "var(--dsw-static-red-400)",
@@ -1548,9 +1713,9 @@ window.__ModuleLoader__.load({
1548
1713
  "--dsw-static-blue-50p": "color-mix(in srgb, #89b4fa 14%, #1e1e2e)",
1549
1714
  "--dsw-static-blue-600": "color-mix(in srgb, #89b4fa 84%, #1e1e2e)",
1550
1715
  "--dsw-static-blue-75": "color-mix(in srgb, #89b4fa 18%, #1e1e2e)",
1551
- "--dsw-static-blue-800": "color-mix(in srgb, #89b4fa 52%, #1e1e2e)",
1552
- "--dsw-static-blue-900": "color-mix(in srgb, #89b4fa 40%, #1e1e2e)",
1553
- "--dsw-static-blue-950": "color-mix(in srgb, #89b4fa 28%, #1e1e2e)",
1716
+ "--dsw-static-blue-800": "color-mix(in srgb, #89b4fa 18%, #11111b)",
1717
+ "--dsw-static-blue-900": "color-mix(in srgb, #89b4fa 14%, #11111b)",
1718
+ "--dsw-static-blue-950": "color-mix(in srgb, #89b4fa 10%, #11111b)",
1554
1719
  "--dsw-static-deepseek-100": "color-mix(in srgb, #89b4fa 24%, #1e1e2e)",
1555
1720
  "--dsw-static-deepseek-200": "color-mix(in srgb, #89b4fa 36%, #1e1e2e)",
1556
1721
  "--dsw-static-deepseek-300": "color-mix(in srgb, #89b4fa 50%, #1e1e2e)",
@@ -1560,8 +1725,8 @@ window.__ModuleLoader__.load({
1560
1725
  "--dsw-static-deepseek-50": "color-mix(in srgb, #89b4fa 10%, #1e1e2e)",
1561
1726
  "--dsw-static-deepseek-600": "color-mix(in srgb, #89b4fa 84%, #1e1e2e)",
1562
1727
  "--dsw-static-deepseek-700-delete": "color-mix(in srgb, #89b4fa 68%, #1e1e2e)",
1563
- "--dsw-static-deepseek-800": "color-mix(in srgb, #89b4fa 52%, #1e1e2e)",
1564
- "--dsw-static-deepseek-900": "color-mix(in srgb, #89b4fa 40%, #1e1e2e)",
1728
+ "--dsw-static-deepseek-800": "color-mix(in srgb, #89b4fa 18%, #11111b)",
1729
+ "--dsw-static-deepseek-900": "color-mix(in srgb, #89b4fa 14%, #11111b)",
1565
1730
  "--dsw-static-green-100": "color-mix(in srgb, #a6e3a1 18%, #1e1e2e)",
1566
1731
  "--dsw-static-green-400": "color-mix(in srgb, #a6e3a1 80%, #1e1e2e)",
1567
1732
  "--dsw-static-green-500": "#a6e3a1",
@@ -1672,7 +1837,7 @@ window.__ModuleLoader__.load({
1672
1837
  "--dsw-alias-scrollbar-bg-l2": "var(--dsw-static-neutral-600)",
1673
1838
  "--dsw-alias-scrollbar-hover-l1": "var(--dsw-static-neutral-600)",
1674
1839
  "--dsw-alias-scrollbar-hover-l2": "var(--dsw-static-neutral-550)",
1675
- "--dsw-alias-state-business-primary": "var(--dsw-static-deepseek-400)",
1840
+ "--dsw-alias-state-business-primary": "var(--dsw-static-deepseek-500)",
1676
1841
  "--dsw-alias-state-business-tertiary": "var(--dsw-static-deepseek-800)",
1677
1842
  "--dsw-alias-state-error-primary": "var(--dsw-static-red-400)",
1678
1843
  "--dsw-alias-state-error-secondary": "var(--dsw-static-red-400)",
@@ -2352,7 +2517,7 @@ window.__ModuleLoader__.load({
2352
2517
  };
2353
2518
  //#endregion
2354
2519
  //#region \0dsh-css:/home/runner/work/dsh-catppuccin-theme/dsh-catppuccin-theme/src/client/glass/GlassRow.module.css.mjs
2355
- const css = ".rWmm9a_group{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;padding:16px 0;display:flex}.rWmm9a_title{color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}.rWmm9a_description{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.rWmm9a_controls{flex-direction:column;gap:10px;margin-top:6px;display:flex}.rWmm9a_row{align-items:center;gap:10px;display:flex}.rWmm9a_rowLabel{width:92px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.rWmm9a_rowHint,.rWmm9a_knobHint{color:var(--dsw-alias-label-tertiary);margin-top:-4px;margin-left:102px;font-size:12px;line-height:18px}.rWmm9a_toggle,.rWmm9a_toggleOn{border:1px solid var(--dsh-glass-rim-soft,var(--dsw-alias-border-l2));background:var(--dsh-glass-card-raised,color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent));cursor:pointer;border-radius:12px;flex:none;width:44px;height:24px;padding:0;transition:background .2s,border-color .2s,box-shadow .2s;display:inline-flex;position:relative;box-shadow:inset 0 1px #ffffff29,0 1px 2px #00000040}.rWmm9a_toggle:hover,.rWmm9a_toggleOn:hover{filter:brightness(1.12)}.rWmm9a_toggle:focus-visible,.rWmm9a_toggleOn:focus-visible{outline:2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 65%, transparent);outline-offset:2px}.rWmm9a_toggleOn{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 78%, transparent);box-shadow:inset 0 1px 0 #ffffff47, 0 0 10px color-mix(in srgb, var(--dsw-alias-state-business-primary) 38%, transparent);border-color:#0000}.rWmm9a_check{width:16px;height:16px;color:var(--dsw-alias-state-business-primary);background:linear-gradient(#fffffffa,#ffffffd1);border-radius:50%;justify-content:center;align-items:center;font-size:10px;line-height:1;transition:transform .18s cubic-bezier(.4,0,.2,1);display:inline-flex;position:absolute;top:3px;left:3px;box-shadow:0 1px 2px #00000059,inset 0 1px #ffffffe6}.rWmm9a_toggleOn .rWmm9a_check{transform:translate(22px)}.rWmm9a_knob{align-items:center;gap:10px;display:flex}.rWmm9a_knobLabel{width:92px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.rWmm9a_slider{min-width:0;accent-color:var(--dsw-alias-state-business-primary);flex:1}.rWmm9a_numberWrap{flex:none;align-items:center;gap:4px;display:inline-flex}.rWmm9a_number{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);width:56px;height:26px;color:var(--dsw-alias-label-primary);text-align:right;border-radius:8px;padding:0 6px;font-size:12px;line-height:18px}.rWmm9a_number::-webkit-outer-spin-button,.rWmm9a_number::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.rWmm9a_unit{width:18px;color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:18px}.rWmm9a_segmented{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:inline-flex;overflow:hidden}.rWmm9a_seg,.rWmm9a_segActive{height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0 12px;font-size:12px;line-height:18px}.rWmm9a_seg+.rWmm9a_seg,.rWmm9a_segActive+.rWmm9a_seg,.rWmm9a_seg+.rWmm9a_segActive{border-left:1px solid var(--dsw-alias-border-l2)}.rWmm9a_segActive{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-state-business-primary)}.rWmm9a_resetButton{border:1px solid var(--dsw-alias-border-l2);height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:8px;padding:0 12px;font-size:12px;line-height:18px}.rWmm9a_resetButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}";
2520
+ const css = ".rWmm9a_group{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding:16px 0;display:flex}.rWmm9a_title{color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}.rWmm9a_description{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.rWmm9a_controls{flex-direction:column;gap:10px;margin-top:6px;display:flex}.rWmm9a_row{align-items:center;gap:10px;display:flex}.rWmm9a_rowLabel{width:92px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.rWmm9a_rowHint,.rWmm9a_knobHint{color:var(--dsw-alias-label-tertiary);margin-top:-4px;margin-left:102px;font-size:12px;line-height:18px}.rWmm9a_toggle,.rWmm9a_toggleOn{border:1px solid var(--dsh-glass-rim-soft,var(--dsw-alias-border-l2));background:var(--dsh-glass-card-raised,color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent));cursor:pointer;border-radius:12px;flex:none;width:44px;height:24px;padding:0;transition:background .2s,border-color .2s,box-shadow .2s;display:inline-flex;position:relative;box-shadow:inset 0 1px #ffffff29,0 1px 2px #00000040}.rWmm9a_toggle:hover,.rWmm9a_toggleOn:hover{filter:brightness(1.12)}.rWmm9a_toggle:focus-visible,.rWmm9a_toggleOn:focus-visible{outline:2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 65%, transparent);outline-offset:2px}.rWmm9a_toggleOn{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 78%, transparent);box-shadow:inset 0 1px 0 #ffffff47, 0 0 10px color-mix(in srgb, var(--dsw-alias-state-business-primary) 38%, transparent);border-color:#0000}.rWmm9a_check{width:16px;height:16px;color:var(--dsw-alias-state-business-primary);background:linear-gradient(#fffffffa,#ffffffd1);border-radius:50%;justify-content:center;align-items:center;font-size:10px;line-height:1;transition:transform .18s cubic-bezier(.4,0,.2,1);display:inline-flex;position:absolute;top:3px;left:3px;box-shadow:0 1px 2px #00000059,inset 0 1px #ffffffe6}.rWmm9a_toggleOn .rWmm9a_check{transform:translate(22px)}.rWmm9a_knob{align-items:center;gap:10px;display:flex}.rWmm9a_knobLabel{width:92px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.rWmm9a_slider{min-width:0;accent-color:var(--dsw-alias-state-business-primary);flex:1}.rWmm9a_numberWrap{flex:none;align-items:center;gap:4px;display:inline-flex}.rWmm9a_number{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-2);width:56px;height:26px;color:var(--dsw-alias-label-primary);text-align:right;border-radius:8px;padding:0 6px;font-size:12px;line-height:18px}.rWmm9a_number::-webkit-outer-spin-button,.rWmm9a_number::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.rWmm9a_unit{width:18px;color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:18px}.rWmm9a_resetButton{border:1px solid var(--dsw-alias-border-l1);height:26px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:0 12px;font-size:12px;line-height:18px}.rWmm9a_resetButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}";
2356
2521
  const tagId = "@nonamelego/dsh-catppuccin/GlassRow.module.css";
2357
2522
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2358
2523
  const tag = document.createElement("style");
@@ -2375,9 +2540,6 @@ window.__ModuleLoader__.load({
2375
2540
  "row": "rWmm9a_row",
2376
2541
  "rowHint": "rWmm9a_rowHint",
2377
2542
  "rowLabel": "rWmm9a_rowLabel",
2378
- "seg": "rWmm9a_seg",
2379
- "segActive": "rWmm9a_segActive",
2380
- "segmented": "rWmm9a_segmented",
2381
2543
  "slider": "rWmm9a_slider",
2382
2544
  "title": "rWmm9a_title",
2383
2545
  "toggle": "rWmm9a_toggle",
@@ -2392,6 +2554,9 @@ window.__ModuleLoader__.load({
2392
2554
  * Holds the master on/off switch plus every glass knob: mode (mica /
2393
2555
  * compatibility), blur, frost and backdrop brightness. Every write goes
2394
2556
  * straight through to the glass layer, so the skin moves live.
2557
+ *
2558
+ * Shape, state semantics and the segmented pick come from ../controls.tsx;
2559
+ * this module only adds the glass material via GlassRow.module.css.
2395
2560
  */
2396
2561
  /** One slider + number box, wired to a single value. */
2397
2562
  function Knob(props) {
@@ -2435,74 +2600,6 @@ window.__ModuleLoader__.load({
2435
2600
  ]
2436
2601
  });
2437
2602
  }
2438
- /** A two-option segmented picker with roving tabindex (item AA): only the
2439
- * selected cell is tabbable, Arrow/Home/End move focus WITHOUT selecting,
2440
- * and Enter/Space (or click) commits — the WAI-ARIA roving-tabindex
2441
- * pattern for segmented controls. */
2442
- function Segmented(props) {
2443
- const { label, value, options, onSelect } = props;
2444
- const [focused, setFocused] = (0, react.useState)(null);
2445
- const refs = (0, react.useRef)(/* @__PURE__ */ new Map());
2446
- const moveFocus = (direction) => {
2447
- const current = focused ?? value;
2448
- const index = options.findIndex((option) => option.id === current);
2449
- let next;
2450
- if (direction === "home") next = 0;
2451
- else if (direction === "end") next = options.length - 1;
2452
- else next = Math.min(options.length - 1, Math.max(0, index + direction));
2453
- const target = options[next];
2454
- if (target === void 0) return;
2455
- setFocused(target.id);
2456
- refs.current.get(target.id)?.focus();
2457
- };
2458
- const onKeyDown = (event) => {
2459
- switch (event.key) {
2460
- case "ArrowLeft":
2461
- case "ArrowUp":
2462
- event.preventDefault();
2463
- moveFocus(-1);
2464
- break;
2465
- case "ArrowRight":
2466
- case "ArrowDown":
2467
- event.preventDefault();
2468
- moveFocus(1);
2469
- break;
2470
- case "Home":
2471
- event.preventDefault();
2472
- moveFocus("home");
2473
- break;
2474
- case "End":
2475
- event.preventDefault();
2476
- moveFocus("end");
2477
- }
2478
- };
2479
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2480
- className: GlassRow_module_css_default.segmented,
2481
- role: "group",
2482
- "aria-label": label,
2483
- children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2484
- ref: (node) => {
2485
- if (node === null) refs.current.delete(option.id);
2486
- else refs.current.set(option.id, node);
2487
- },
2488
- type: "button",
2489
- className: option.id === value ? GlassRow_module_css_default.segActive : GlassRow_module_css_default.seg,
2490
- "aria-pressed": option.id === value,
2491
- tabIndex: option.id === (focused ?? value) ? 0 : -1,
2492
- onFocus: () => {
2493
- setFocused(option.id);
2494
- },
2495
- onBlur: () => {
2496
- setFocused((now) => now === option.id ? null : now);
2497
- },
2498
- onKeyDown,
2499
- onClick: () => {
2500
- onSelect(option.id);
2501
- },
2502
- children: option.label
2503
- }, option.id))
2504
- });
2505
- }
2506
2603
  /** One-click material presets — blur/frost/brightness triples the row applies
2507
2604
  * through the existing setters (the debounced persist coalesces the burst
2508
2605
  * into one Host write). Brightness stays neutral (50) so every preset works
@@ -2547,26 +2644,9 @@ window.__ModuleLoader__.load({
2547
2644
  children: [
2548
2645
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2549
2646
  className: GlassRow_module_css_default.title,
2550
- children: [t("glass.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2551
- role: "img",
2552
- "aria-label": t("glass.helpLabel"),
2553
- title: t("glass.help"),
2554
- style: {
2555
- display: "inline-flex",
2556
- alignItems: "center",
2557
- justifyContent: "center",
2558
- width: 16,
2559
- height: 16,
2560
- borderRadius: "50%",
2561
- border: "1px solid var(--dsw-alias-border-l2)",
2562
- color: "var(--dsw-alias-label-tertiary)",
2563
- fontSize: 11,
2564
- lineHeight: 1,
2565
- cursor: "help",
2566
- userSelect: "none",
2567
- marginLeft: 6
2568
- },
2569
- children: "?"
2647
+ children: [t("glass.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HelpBadge, {
2648
+ label: t("glass.helpLabel"),
2649
+ help: t("glass.help")
2570
2650
  })]
2571
2651
  }),
2572
2652
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -2706,6 +2786,8 @@ window.__ModuleLoader__.load({
2706
2786
  * superseded a local change ("另一窗口已更新,本地改动未保存");
2707
2787
  * - the latest auto-check verdict (item H) surfaced on mount;
2708
2788
  * - a "?" help affordance (item J, native `title` tooltip).
2789
+ *
2790
+ * Recipes, the switch and the segmented pick come from ./controls.tsx.
2709
2791
  */
2710
2792
  /** Auto-retry scheduled after a failed check (item V): one retry after 30s,
2711
2793
  * then it stops — the discipline lives here in the row, never in a hanging
@@ -2732,46 +2814,6 @@ window.__ModuleLoader__.load({
2732
2814
  default: return "update.failed";
2733
2815
  }
2734
2816
  }
2735
- const switchBase = {
2736
- position: "relative",
2737
- display: "inline-flex",
2738
- flex: "none",
2739
- width: 44,
2740
- height: 24,
2741
- padding: 0,
2742
- border: "1px solid var(--dsw-alias-border-l2)",
2743
- borderRadius: 12,
2744
- background: "var(--dsw-alias-bg-layer-2)",
2745
- cursor: "pointer"
2746
- };
2747
- const knobStyle = {
2748
- position: "absolute",
2749
- top: 3,
2750
- left: 3,
2751
- display: "inline-flex",
2752
- alignItems: "center",
2753
- justifyContent: "center",
2754
- width: 16,
2755
- height: 16,
2756
- borderRadius: "50%",
2757
- background: "var(--dsw-alias-label-tertiary)",
2758
- color: "var(--dsw-alias-label-tertiary)",
2759
- fontSize: 10,
2760
- lineHeight: 1,
2761
- transition: "transform 0.18s cubic-bezier(0.4, 0, 0.2, 1)"
2762
- };
2763
- const buttonStyle = {
2764
- display: "inline-flex",
2765
- alignItems: "center",
2766
- gap: 6,
2767
- padding: "6px 12px",
2768
- borderRadius: 8,
2769
- border: "1px solid var(--dsw-alias-border-l1)",
2770
- background: "transparent",
2771
- color: "var(--dsw-alias-label-primary)",
2772
- cursor: "pointer",
2773
- font: "inherit"
2774
- };
2775
2817
  /**
2776
2818
  * Render the update-check row: title, description, auto-check switch,
2777
2819
  * channel pick, a check button, and the verdict once checked.
@@ -2837,62 +2879,27 @@ window.__ModuleLoader__.load({
2837
2879
  const isDesktop = payload?.ok === true && payload.env === "desktop";
2838
2880
  const showConflict = conflicts > dismissedConflicts;
2839
2881
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2840
- style: {
2841
- borderBottom: "1px solid var(--dsw-alias-border-l2)",
2842
- display: "flex",
2843
- flexDirection: "column",
2844
- gap: "10px",
2845
- padding: "16px 0"
2846
- },
2882
+ style: ROW,
2847
2883
  children: [
2848
2884
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2849
- style: {
2850
- color: "var(--dsw-alias-label-primary)",
2851
- fontSize: 14,
2852
- lineHeight: "22px",
2853
- display: "flex",
2854
- alignItems: "center"
2855
- },
2856
- children: [t("update.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2857
- role: "img",
2858
- "aria-label": t("update.helpLabel"),
2859
- title: t("update.help"),
2860
- style: {
2861
- display: "inline-flex",
2862
- alignItems: "center",
2863
- justifyContent: "center",
2864
- width: 16,
2865
- height: 16,
2866
- borderRadius: "50%",
2867
- border: "1px solid var(--dsw-alias-border-l2)",
2868
- color: "var(--dsw-alias-label-tertiary)",
2869
- fontSize: 11,
2870
- lineHeight: 1,
2871
- cursor: "help",
2872
- userSelect: "none",
2873
- marginLeft: 6,
2874
- flex: "none"
2875
- },
2876
- children: "?"
2885
+ style: TITLE,
2886
+ children: [t("update.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HelpBadge, {
2887
+ label: t("update.helpLabel"),
2888
+ help: t("update.help")
2877
2889
  })]
2878
2890
  }),
2879
2891
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2880
- style: {
2881
- color: "var(--dsw-alias-label-tertiary)",
2882
- fontSize: 12,
2883
- lineHeight: "18px"
2884
- },
2892
+ style: DESCRIPTION,
2885
2893
  children: t("update.description")
2886
2894
  }),
2887
2895
  showConflict && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2888
2896
  style: {
2889
- display: "flex",
2897
+ ...CARD,
2898
+ flexDirection: "row",
2890
2899
  flexWrap: "wrap",
2891
- gap: "8px",
2892
2900
  alignItems: "center",
2893
- padding: "6px 10px",
2894
- borderRadius: 8,
2895
- border: "1px solid var(--dsw-alias-border-l2)",
2901
+ gap: 8,
2902
+ padding: "10px 12px",
2896
2903
  background: "var(--dsw-alias-state-business-tertiary)"
2897
2904
  },
2898
2905
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -2909,7 +2916,7 @@ window.__ModuleLoader__.load({
2909
2916
  setDismissedConflicts(conflicts);
2910
2917
  },
2911
2918
  style: {
2912
- ...buttonStyle,
2919
+ ...actionButton(),
2913
2920
  padding: "2px 8px",
2914
2921
  fontSize: 12
2915
2922
  },
@@ -2930,33 +2937,12 @@ window.__ModuleLoader__.load({
2930
2937
  gap: 10
2931
2938
  },
2932
2939
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2933
- style: {
2934
- color: "var(--dsw-alias-label-secondary)",
2935
- fontSize: 12,
2936
- lineHeight: "18px"
2937
- },
2940
+ style: ROW_LABEL,
2938
2941
  children: t("update.autoCheck")
2939
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2940
- type: "button",
2941
- role: "switch",
2942
- "aria-checked": autoCheckOn,
2943
- "aria-label": t("update.autoCheck"),
2944
- onClick: () => {
2945
- setAutoCheck(!autoCheckOn);
2946
- },
2947
- style: {
2948
- ...switchBase,
2949
- background: autoCheckOn ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-bg-layer-2)"
2950
- },
2951
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2952
- "aria-hidden": "true",
2953
- style: {
2954
- ...knobStyle,
2955
- transform: autoCheckOn ? "translateX(22px)" : void 0,
2956
- background: autoCheckOn ? "var(--dsw-alias-bg-layer-1)" : "var(--dsw-alias-label-tertiary)"
2957
- },
2958
- children: autoCheckOn ? "✓" : ""
2959
- })
2942
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
2943
+ label: t("update.autoCheck"),
2944
+ checked: autoCheckOn,
2945
+ onChange: setAutoCheck
2960
2946
  })]
2961
2947
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2962
2948
  style: {
@@ -2965,40 +2951,19 @@ window.__ModuleLoader__.load({
2965
2951
  gap: 10
2966
2952
  },
2967
2953
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2968
- style: {
2969
- color: "var(--dsw-alias-label-secondary)",
2970
- fontSize: 12,
2971
- lineHeight: "18px"
2972
- },
2954
+ style: ROW_LABEL,
2973
2955
  children: t("update.channel")
2974
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2975
- style: {
2976
- display: "inline-flex",
2977
- border: "1px solid var(--dsw-alias-border-l2)",
2978
- borderRadius: 8,
2979
- overflow: "hidden"
2980
- },
2981
- role: "group",
2982
- "aria-label": t("update.channel"),
2983
- children: ["latest", "beta"].map((option, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2984
- type: "button",
2985
- "aria-pressed": channelValue === option,
2986
- onClick: () => {
2987
- setChannel(option);
2988
- },
2989
- style: {
2990
- height: 26,
2991
- padding: "0 12px",
2992
- border: "none",
2993
- borderLeft: index > 0 ? "1px solid var(--dsw-alias-border-l2)" : void 0,
2994
- background: channelValue === option ? "var(--dsw-alias-state-business-tertiary)" : "transparent",
2995
- color: channelValue === option ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-secondary)",
2996
- fontSize: 12,
2997
- lineHeight: "18px",
2998
- cursor: "pointer"
2999
- },
3000
- children: t(option === "latest" ? "update.channelLatest" : "update.channelBeta")
3001
- }, option))
2956
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segmented, {
2957
+ label: t("update.channel"),
2958
+ value: channelValue,
2959
+ options: [{
2960
+ id: "latest",
2961
+ label: t("update.channelLatest")
2962
+ }, {
2963
+ id: "beta",
2964
+ label: t("update.channelBeta")
2965
+ }],
2966
+ onSelect: setChannel
3002
2967
  })]
3003
2968
  })]
3004
2969
  }),
@@ -3032,11 +2997,7 @@ window.__ModuleLoader__.load({
3032
2997
  type: "button",
3033
2998
  disabled: phase === "checking",
3034
2999
  onClick: () => void runCheck(),
3035
- style: {
3036
- ...buttonStyle,
3037
- cursor: phase === "checking" ? "default" : "pointer",
3038
- opacity: phase === "checking" ? .6 : 1
3039
- },
3000
+ style: actionButton(phase === "checking"),
3040
3001
  children: phase === "checking" ? t("update.checking") : t("update.check")
3041
3002
  }),
3042
3003
  payload?.ok === true && payload.current !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
@@ -3102,7 +3063,7 @@ window.__ModuleLoader__.load({
3102
3063
  flex: "1 1 260px",
3103
3064
  padding: "6px 10px",
3104
3065
  borderRadius: 8,
3105
- border: "1px solid var(--dsw-alias-border-l1)",
3066
+ border: "0.5px solid var(--dsw-alias-border-l4)",
3106
3067
  background: "var(--dsw-alias-bg-layer-1)",
3107
3068
  color: "var(--dsw-alias-label-primary)",
3108
3069
  fontSize: 12,
@@ -3118,10 +3079,7 @@ window.__ModuleLoader__.load({
3118
3079
  onClick: () => {
3119
3080
  copyCommand(payload.updateCommand ?? "").then(setCopied);
3120
3081
  },
3121
- style: {
3122
- ...buttonStyle,
3123
- cursor: "pointer"
3124
- },
3082
+ style: actionButton(),
3125
3083
  children: copied ? t("update.copied") : t("update.copy")
3126
3084
  })]
3127
3085
  }),
@@ -3175,10 +3133,7 @@ window.__ModuleLoader__.load({
3175
3133
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3176
3134
  type: "button",
3177
3135
  onClick: () => void runCheck(),
3178
- style: {
3179
- ...buttonStyle,
3180
- cursor: "pointer"
3181
- },
3136
+ style: actionButton(),
3182
3137
  children: t("update.retry")
3183
3138
  })
3184
3139
  ]