twskin 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/CONTRIBUTING.md +37 -0
  3. package/LICENSE +21 -0
  4. package/README.md +104 -0
  5. package/SECURITY.md +28 -0
  6. package/dist/bin/twskin.d.ts +3 -0
  7. package/dist/bin/twskin.d.ts.map +1 -0
  8. package/dist/bin/twskin.js +4 -0
  9. package/dist/bin/twskin.js.map +1 -0
  10. package/dist/commands.d.ts +41 -0
  11. package/dist/commands.d.ts.map +1 -0
  12. package/dist/commands.js +421 -0
  13. package/dist/commands.js.map +1 -0
  14. package/dist/context.d.ts +14 -0
  15. package/dist/context.d.ts.map +1 -0
  16. package/dist/context.js +51 -0
  17. package/dist/context.js.map +1 -0
  18. package/dist/errors.d.ts +9 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +20 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/lock.d.ts +3 -0
  23. package/dist/lock.d.ts.map +1 -0
  24. package/dist/lock.js +52 -0
  25. package/dist/lock.js.map +1 -0
  26. package/dist/main.d.ts +9 -0
  27. package/dist/main.d.ts.map +1 -0
  28. package/dist/main.js +92 -0
  29. package/dist/main.js.map +1 -0
  30. package/dist/output.d.ts +4 -0
  31. package/dist/output.d.ts.map +1 -0
  32. package/dist/output.js +27 -0
  33. package/dist/output.js.map +1 -0
  34. package/dist/persistent-cdp.d.ts +14 -0
  35. package/dist/persistent-cdp.d.ts.map +1 -0
  36. package/dist/persistent-cdp.js +140 -0
  37. package/dist/persistent-cdp.js.map +1 -0
  38. package/dist/system.d.ts +64 -0
  39. package/dist/system.d.ts.map +1 -0
  40. package/dist/system.js +234 -0
  41. package/dist/system.js.map +1 -0
  42. package/dist/theme-download.d.ts +9 -0
  43. package/dist/theme-download.d.ts.map +1 -0
  44. package/dist/theme-download.js +250 -0
  45. package/dist/theme-download.js.map +1 -0
  46. package/dist/themes.d.ts +9 -0
  47. package/dist/themes.d.ts.map +1 -0
  48. package/dist/themes.js +185 -0
  49. package/dist/themes.js.map +1 -0
  50. package/dist/types.d.ts +58 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +2 -0
  53. package/dist/types.js.map +1 -0
  54. package/docs/publishing.md +45 -0
  55. package/docs/theme-distribution.md +62 -0
  56. package/package.json +74 -0
  57. package/runtime/component-map.mjs +200 -0
  58. package/runtime/injector.mjs +705 -0
  59. package/runtime/manifest.json +47 -0
  60. package/runtime/restore.sh +33 -0
  61. package/runtime/skin.js +1133 -0
  62. package/runtime/start.sh +86 -0
  63. package/runtime/styles/base.css +687 -0
  64. package/runtime/styles/manager.css +346 -0
  65. package/runtime/token-map.mjs +432 -0
@@ -0,0 +1,432 @@
1
+ // TRAE Work Skin — 主题角色 → 设计令牌扇出映射(纯函数,无 DOM 依赖)
2
+ // 用法:
3
+ // 页面载荷:injector 将本文件全文嵌入 skin.js(__TOKEN_MAP__ 占位符)
4
+ // 单测: 由仓库 tests/token-map.test.mjs 直接加载
5
+ // 设计原则:映射表普查驱动,命名空间清单完整内置于本文件;
6
+ // 主题作者只声明角色(tokens),组件在任何页面读任何命名空间都拿到同一组值。
7
+ (function (root, factory) {
8
+ const api = factory();
9
+ if (typeof module !== "undefined" && module.exports) module.exports = api;
10
+ root.DREAM_SKIN_TOKEN_MAP = api;
11
+ })(typeof self !== "undefined" ? self : globalThis, function () {
12
+ "use strict";
13
+
14
+ // ---------- 颜色工具 ----------
15
+ function parseColor(color) {
16
+ if (!color || typeof color !== "string") return null;
17
+ const c = color.trim();
18
+ let m = /^#([0-9a-f]{3})$/i.exec(c);
19
+ if (m) {
20
+ const [r, g, b] = m[1].split("").map((x) => parseInt(x + x, 16));
21
+ return { r, g, b, a: 1 };
22
+ }
23
+ m = /^#([0-9a-f]{6})$/i.exec(c);
24
+ if (m) {
25
+ const n = parseInt(m[1], 16);
26
+ return { r: n >> 16, g: (n >> 8) & 255, b: n & 255, a: 1 };
27
+ }
28
+ m = /^#([0-9a-f]{8})$/i.exec(c);
29
+ if (m) {
30
+ const n = parseInt(m[1], 16);
31
+ return { r: n >> 24, g: (n >> 16) & 255, b: (n >> 8) & 255, a: (n & 255) / 255 };
32
+ }
33
+ m = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:[\s,/]+([\d.]+))?\s*\)$/i.exec(c);
34
+ if (m) {
35
+ return { r: +m[1], g: +m[2], b: +m[3], a: m[4] == null ? 1 : +m[4] };
36
+ }
37
+ return null;
38
+ }
39
+
40
+ function luminance(color) {
41
+ const p = parseColor(color);
42
+ if (!p) return null;
43
+ const f = (v) => {
44
+ const s = v / 255;
45
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
46
+ };
47
+ return 0.2126 * f(p.r) + 0.7152 * f(p.g) + 0.0722 * f(p.b);
48
+ }
49
+
50
+ function contrastRatio(a, b) {
51
+ const la = luminance(a);
52
+ const lb = luminance(b);
53
+ if (la == null || lb == null) return null;
54
+ const [hi, lo] = la >= lb ? [la, lb] : [lb, la];
55
+ return (hi + 0.05) / (lo + 0.05);
56
+ }
57
+
58
+ // 从候选色里挑与 bg 对比度最高的;都不达标也返回最优的那个
59
+ function pickOnColor(bg, candidates = ["#000000", "#ffffff"]) {
60
+ let best = candidates[0];
61
+ let bestRatio = -1;
62
+ for (const c of candidates) {
63
+ const r = contrastRatio(bg, c) ?? 0;
64
+ if (r > bestRatio) { bestRatio = r; best = c; }
65
+ }
66
+ return best;
67
+ }
68
+
69
+ function withAlpha(color, alpha) {
70
+ const p = parseColor(color);
71
+ const a = Math.min(1, Math.max(0, Number(alpha)));
72
+ if (p) return `rgba(${p.r}, ${p.g}, ${p.b}, ${+a.toFixed(3)})`;
73
+ if (a === 1) return String(color);
74
+ return `color-mix(in srgb, ${color} ${a * 100}%, transparent)`;
75
+ }
76
+
77
+ const mix = (color, pct, other) => `color-mix(in srgb, ${color} ${pct}%, ${other})`;
78
+
79
+ // ---------- 命名空间普查清单(2026-07 实地盘点,见 docs/验证记录)----------
80
+ const NS_BG = ["base-default", "base-secondary", "base-tertiary", "brand", "brand-active", "brand-disabled", "brand-disabled-sub", "brand-hover", "brand-hover-sub", "brand-old", "brand-popup", "brand-popup-sub", "brand-sub", "card", "card-hover", "input", "invert", "invert-active", "invert-disabled", "invert-hover", "menu", "menu-old", "overlay-l0", "overlay-l1", "overlay-l2", "overlay-l3", "overlay-l4", "tooltip", "white"];
81
+ const NS_TEXT = ["brand", "brand-hover", "brand-hover-sub", "brand-sub", "default", "default-active", "default-hover", "disabled", "onaccent", "onbrand", "preformat-foreground", "secondary", "secondary-active", "secondary-hover", "tertiary"];
82
+ const NS_ICON = ["brand", "brand-hover", "brand-hover-sub", "brand-sub", "default", "default-active", "default-hover", "disabled", "onaccent", "onbrand", "secondary", "secondary-active", "secondary-hover", "tertiary", "white"];
83
+ const NS_BORDER = ["brand", "brand-sub", "contrast", "neutral-l1", "neutral-l2", "neutral-l3"];
84
+ // --vscode-icube-- 别名层(组件真正消费的那层)
85
+ const ICUBE_BG = ["base-default", "base-secondary", "base-tertiary", "brand", "brand-disabled", "brand-hover", "invert", "invert-active", "invert-disabled", "invert-hover", "menu", "menu-tr0", "overlay-l1", "overlay-l2", "overlay-l3", "overlay-l4", "tooltip"];
86
+ const ICUBE_TEXT = ["brand", "brand-hover", "default", "default-active", "default-hover", "disabled", "onaccent", "onbrand", "secondary", "secondary-active", "secondary-hover", "tertiary"];
87
+ const ICUBE_ICON = ["brand", "brand-hover", "default", "default-active", "default-hover", "disabled", "onaccent", "onbrand", "secondary", "secondary-active", "secondary-hover", "tertiary"];
88
+ const ICUBE_BORDER = ["brand", "contrast", "neutral-l1", "neutral-l2", "neutral-l3"];
89
+ const ICUBE_STATUS_KINDS = ["primary", "error", "success", "warning", "alert"];
90
+ const ICUBE_STATUS_PARTS = ["default", "hover", "active", "surface-l1", "surface-l2", "surface-l3"];
91
+ // --ras-* 镜像层
92
+ const RAS_BG = ["base-default", "base-secondary", "base-tertiary", "brand", "brand-design", "brand-disabled", "brand-disabled-design", "brand-disabled-sub", "brand-hover", "brand-hover-design", "brand-hover-sub", "brand-popup", "brand-popup-design", "brand-popup-sub", "brand-sub", "invert", "invert-active", "invert-disabled", "invert-hover", "menu", "overlay-l1", "overlay-l2", "overlay-l3", "overlay-l4", "tooltip"];
93
+ const RAS_TEXT = ["brand", "brand-design", "brand-hover", "brand-hover-design", "brand-hover-sub", "brand-sub", "default", "default-active", "default-hover", "disabled", "onaccent", "onbrand", "secondary", "secondary-active", "secondary-hover", "tertiary"];
94
+ const RAS_ICON = ["brand", "brand-design", "brand-hover", "brand-hover-design", "brand-hover-sub", "brand-sub", "default", "default-active", "default-hover", "disabled", "onaccent", "onbrand", "secondary", "secondary-active", "secondary-hover", "tertiary"];
95
+ const RAS_BORDER = ["brand", "brand-design", "brand-sub", "contrast", "neutral-l1", "neutral-l2", "neutral-l3"];
96
+ // VS Code 核心兼容层:TRAE 的 IM Channel 等非 Workbench 页面仍直接消费这些旧变量。
97
+ // 只桥接可由 V3 语义角色稳定表达的通用表面、文本、控件与状态色;尺寸/字体/ANSI 色保持宿主值。
98
+ const VSCODE_CORE = [
99
+ "--vscode-foreground", "--vscode-descriptionForeground", "--vscode-disabledForeground",
100
+ "--vscode-focusBorder", "--vscode-contrastBorder", "--vscode-contrastActiveBorder",
101
+ "--vscode-selection-background", "--vscode-errorForeground", "--vscode-icon-foreground",
102
+ "--vscode-sideBar-background", "--vscode-sideBar-foreground", "--vscode-sideBar-border",
103
+ "--vscode-widget-border",
104
+ "--vscode-editor-background", "--vscode-editor-foreground", "--vscode-editorCursor-foreground",
105
+ "--vscode-editorLineNumber-foreground", "--vscode-editorLineNumber-activeForeground",
106
+ "--vscode-editor-selectionBackground", "--vscode-editor-lineHighlightBorder",
107
+ "--vscode-editorWidget-background", "--vscode-editorWidget-foreground", "--vscode-editorWidget-border",
108
+ "--vscode-input-background", "--vscode-input-foreground", "--vscode-input-placeholderForeground", "--vscode-input-border",
109
+ "--vscode-checkbox-background", "--vscode-checkbox-foreground", "--vscode-checkbox-border",
110
+ "--vscode-button-background", "--vscode-button-foreground", "--vscode-button-hoverBackground",
111
+ "--vscode-button-activeBackground", "--vscode-button-border",
112
+ "--vscode-list-hoverBackground", "--vscode-list-hoverForeground",
113
+ "--vscode-list-activeSelectionBackground", "--vscode-list-activeSelectionForeground",
114
+ "--vscode-list-inactiveSelectionBackground", "--vscode-list-inactiveSelectionForeground",
115
+ "--vscode-panel-background", "--vscode-panel-border",
116
+ "--vscode-menu-background", "--vscode-menu-foreground", "--vscode-menu-border",
117
+ "--vscode-dropdown-background", "--vscode-dropdown-foreground", "--vscode-dropdown-border",
118
+ "--vscode-badge-background", "--vscode-badge-foreground",
119
+ "--vscode-terminal-background", "--vscode-terminal-foreground",
120
+ "--vscode-testing-iconPassed", "--vscode-testing-iconFailed", "--vscode-testing-iconErrored",
121
+ "--vscode-testing-iconQueued", "--vscode-testing-iconSkipped", "--vscode-testing-iconUnset",
122
+ ];
123
+ // 品牌色阶停靠点:[suffix, accent 占比%, 混向]
124
+ const RAMP_STOPS = [[50, 8, "white"], [100, 16, "white"], [200, 28, "white"], [300, 44, "white"], [400, 66, "white"], [500, 100, null], [600, 82, "black"], [700, 66, "black"], [800, 50, "black"], [900, 34, "black"], [950, 20, "black"]];
125
+
126
+ // ---------- 角色补全 ----------
127
+ // tokens 缺省时按规则推导:hover/active 用 color-mix,subtle 用透明度,onAccent 按亮度自动选黑白
128
+ function deriveRoles(tokens, opts) {
129
+ const t = tokens || {};
130
+ const appearance = (opts && opts.appearance) || t.appearance || "dark";
131
+ const accent = { ...(t.accent || {}) };
132
+ if (!accent.base) accent.base = "#7c9cff";
133
+ if (!accent.hover) accent.hover = mix(accent.base, 82, "white");
134
+ if (!accent.active) accent.active = mix(accent.base, 82, "black");
135
+ if (!accent.subtle) accent.subtle = withAlpha(accent.base, 0.18);
136
+ if (!accent.disabled) accent.disabled = withAlpha(accent.base, 0.4);
137
+ if (!accent.onAccent) accent.onAccent = pickOnColor(accent.base);
138
+
139
+ const dark = appearance === "dark";
140
+ const surface = { ...(t.surface || {}) };
141
+ if (!surface.base) surface.base = dark ? "#0b1018" : "#ffffff";
142
+ if (!surface.secondary) surface.secondary = mix(surface.base, dark ? 88 : 96, dark ? "white" : "black");
143
+ if (!surface.tertiary) surface.tertiary = mix(surface.base, dark ? 80 : 92, dark ? "white" : "black");
144
+ if (!surface.card) surface.card = surface.secondary;
145
+ if (!surface.cardHover) surface.cardHover = surface.tertiary;
146
+ if (!surface.input) surface.input = dark ? mix(surface.base, 85, "black") : mix(surface.base, 97, "black");
147
+ if (!surface.menu) surface.menu = surface.card;
148
+ if (!surface.tooltip) surface.tooltip = dark ? mix(surface.base, 80, "white") : mix(surface.base, 96, "black");
149
+ if (!surface.invert) surface.invert = dark ? "#f5f5f5" : "#171717";
150
+
151
+ const text = { ...(t.text || {}) };
152
+ if (!text.primary) text.primary = dark ? "#edf4f7" : "#171717";
153
+ if (!text.secondary) text.secondary = withAlpha(text.primary, 0.68);
154
+ if (!text.tertiary) text.tertiary = withAlpha(text.primary, 0.45);
155
+ if (!text.disabled) text.disabled = withAlpha(text.primary, 0.3);
156
+
157
+ const icons = { ...(t.icons || {}) };
158
+ if (!icons.primary) icons.primary = text.primary;
159
+ if (!icons.secondary) icons.secondary = text.secondary;
160
+ if (!icons.tertiary) icons.tertiary = text.tertiary;
161
+ if (!icons.disabled) icons.disabled = text.disabled;
162
+
163
+ const border = { ...(t.border || {}) };
164
+ if (!border.subtle) border.subtle = withAlpha(text.primary, 0.14);
165
+ if (!border.default) border.default = withAlpha(text.primary, 0.24);
166
+ if (!border.strong) border.strong = withAlpha(text.primary, 0.38);
167
+ if (!border.contrast) border.contrast = accent.base;
168
+
169
+ const state = { ...(t.state || {}) };
170
+ if (!state.info) state.info = "#57a8ff";
171
+ if (!state.success) state.success = "#57d38c";
172
+ if (!state.warning) state.warning = "#f5bf42";
173
+ if (!state.error) state.error = "#ff5263";
174
+
175
+ return { appearance, accent, surface, text, icons, border, state };
176
+ }
177
+
178
+ // ---------- 扇出 ----------
179
+ function buildVarMap(tokens, opts) {
180
+ const r = deriveRoles(tokens, opts);
181
+ const { accent, surface, text, icons, border, state } = r;
182
+ const out = {};
183
+
184
+ // bg 语义(后缀共享:--bg-bg-* / --vscode-icube--bg-bg-* / --ras-bg-bg-*)
185
+ const bgValue = (suffix) => {
186
+ switch (suffix) {
187
+ case "base-default": return surface.base;
188
+ case "base-secondary": return surface.secondary;
189
+ case "base-tertiary": return surface.tertiary;
190
+ case "card": return surface.card;
191
+ case "card-hover": return surface.cardHover;
192
+ case "input": return surface.input;
193
+ case "menu": case "menu-old": return surface.menu;
194
+ case "menu-tr0": return withAlpha(surface.menu, 0);
195
+ case "tooltip": return surface.tooltip;
196
+ case "white": return "#ffffff";
197
+ case "invert": return surface.invert;
198
+ case "invert-hover": return mix(surface.invert, 88, r.appearance === "dark" ? "black" : "white");
199
+ case "invert-active": return mix(surface.invert, 78, r.appearance === "dark" ? "black" : "white");
200
+ case "invert-disabled": return withAlpha(surface.invert, 0.4);
201
+ case "overlay-l0": return border.subtle;
202
+ case "overlay-l1": return border.subtle;
203
+ case "overlay-l2": return border.subtle;
204
+ case "overlay-l3": return border.default;
205
+ case "overlay-l4": return border.strong;
206
+ case "brand": case "brand-old": case "brand-sub": case "brand-design": return accent.base;
207
+ case "brand-hover": case "brand-hover-sub": case "brand-hover-design": return accent.hover;
208
+ case "brand-active": return accent.active;
209
+ case "brand-disabled": case "brand-disabled-sub": case "brand-disabled-design": return accent.disabled;
210
+ case "brand-popup": case "brand-popup-sub": case "brand-popup-design": return accent.subtle;
211
+ default: return undefined;
212
+ }
213
+ };
214
+ const textValue = (suffix) => {
215
+ switch (suffix) {
216
+ case "default": case "default-hover": case "default-active": return text.primary;
217
+ case "secondary": case "secondary-hover": case "secondary-active": return text.secondary;
218
+ case "tertiary": return text.tertiary;
219
+ case "disabled": return text.disabled;
220
+ case "brand": case "brand-sub": case "brand-design": return accent.base;
221
+ case "brand-hover": case "brand-hover-sub": case "brand-hover-design": return accent.hover;
222
+ case "onaccent": case "onbrand": return accent.onAccent;
223
+ case "preformat-foreground": return text.secondary;
224
+ default: return undefined;
225
+ }
226
+ };
227
+ const iconValue = (suffix) => {
228
+ switch (suffix) {
229
+ case "default": case "default-hover": case "default-active": return icons.primary;
230
+ case "secondary": case "secondary-hover": case "secondary-active": return icons.secondary;
231
+ case "tertiary": return icons.tertiary;
232
+ case "disabled": return icons.disabled;
233
+ case "brand": case "brand-sub": case "brand-design": return accent.base;
234
+ case "brand-hover": case "brand-hover-sub": case "brand-hover-design": return accent.hover;
235
+ case "onaccent": case "onbrand": return accent.onAccent;
236
+ case "white": return "#ffffff";
237
+ default: return undefined;
238
+ }
239
+ };
240
+ const borderValue = (suffix) => {
241
+ switch (suffix) {
242
+ case "neutral-l1": return border.subtle;
243
+ case "neutral-l2": return border.default;
244
+ case "neutral-l3": return border.strong;
245
+ case "brand": case "brand-design": return accent.base;
246
+ case "brand-sub": return accent.subtle;
247
+ case "contrast": return border.contrast;
248
+ default: return undefined;
249
+ }
250
+ };
251
+
252
+ const put = (name, value) => { if (value != null && value !== "") out[name] = value; };
253
+
254
+ for (const s of NS_BG) put(`--bg-bg-${s}`, bgValue(s));
255
+ for (const s of NS_TEXT) put(`--text-text-${s}`, textValue(s));
256
+ for (const s of NS_ICON) put(`--icon-icon-${s}`, iconValue(s));
257
+ for (const s of NS_BORDER) put(`--border-border-${s}`, borderValue(s));
258
+ for (const s of ICUBE_BG) put(`--vscode-icube--bg-bg-${s}`, bgValue(s));
259
+ for (const s of ICUBE_TEXT) put(`--vscode-icube--text-text-${s}`, textValue(s));
260
+ for (const s of ICUBE_ICON) put(`--vscode-icube--icon-icon-${s}`, iconValue(s));
261
+ for (const s of ICUBE_BORDER) put(`--vscode-icube--border-border-${s}`, borderValue(s));
262
+ for (const s of RAS_BG) put(`--ras-bg-bg-${s}`, bgValue(s));
263
+ for (const s of RAS_TEXT) put(`--ras-text-text-${s}`, textValue(s));
264
+ for (const s of RAS_ICON) put(`--ras-icon-icon-${s}`, iconValue(s));
265
+ for (const s of RAS_BORDER) put(`--ras-border-border-${s}`, borderValue(s));
266
+
267
+ // 旧版 VS Code 核心变量桥接。它们不属于 --vscode-icube-- 别名层,但仍被部分 TRAE 页面直接消费。
268
+ const vscodeCoreValues = {
269
+ "--vscode-foreground": text.primary,
270
+ "--vscode-descriptionForeground": text.secondary,
271
+ "--vscode-disabledForeground": text.disabled,
272
+ "--vscode-focusBorder": accent.base,
273
+ "--vscode-contrastBorder": border.contrast,
274
+ "--vscode-contrastActiveBorder": accent.base,
275
+ "--vscode-selection-background": accent.subtle,
276
+ "--vscode-errorForeground": state.error,
277
+ "--vscode-icon-foreground": icons.primary,
278
+ "--vscode-sideBar-background": surface.secondary,
279
+ "--vscode-sideBar-foreground": text.primary,
280
+ "--vscode-sideBar-border": border.default,
281
+ "--vscode-widget-border": border.subtle,
282
+ "--vscode-editor-background": surface.base,
283
+ "--vscode-editor-foreground": text.primary,
284
+ "--vscode-editorCursor-foreground": accent.base,
285
+ "--vscode-editorLineNumber-foreground": text.tertiary,
286
+ "--vscode-editorLineNumber-activeForeground": accent.base,
287
+ "--vscode-editor-selectionBackground": accent.subtle,
288
+ "--vscode-editor-lineHighlightBorder": border.subtle,
289
+ "--vscode-editorWidget-background": surface.menu,
290
+ "--vscode-editorWidget-foreground": text.primary,
291
+ "--vscode-editorWidget-border": border.default,
292
+ "--vscode-input-background": surface.input,
293
+ "--vscode-input-foreground": text.primary,
294
+ "--vscode-input-placeholderForeground": text.tertiary,
295
+ "--vscode-input-border": border.default,
296
+ "--vscode-checkbox-background": accent.base,
297
+ "--vscode-checkbox-foreground": accent.onAccent,
298
+ "--vscode-checkbox-border": border.default,
299
+ "--vscode-button-background": accent.base,
300
+ "--vscode-button-foreground": accent.onAccent,
301
+ "--vscode-button-hoverBackground": accent.hover,
302
+ "--vscode-button-activeBackground": accent.active,
303
+ "--vscode-button-border": border.default,
304
+ "--vscode-list-hoverBackground": withAlpha(state.info, 0.12),
305
+ "--vscode-list-hoverForeground": text.primary,
306
+ "--vscode-list-activeSelectionBackground": accent.subtle,
307
+ "--vscode-list-activeSelectionForeground": text.primary,
308
+ "--vscode-list-inactiveSelectionBackground": withAlpha(accent.base, 0.12),
309
+ "--vscode-list-inactiveSelectionForeground": text.primary,
310
+ "--vscode-panel-background": surface.secondary,
311
+ "--vscode-panel-border": border.default,
312
+ "--vscode-menu-background": surface.menu,
313
+ "--vscode-menu-foreground": text.primary,
314
+ "--vscode-menu-border": border.subtle,
315
+ "--vscode-dropdown-background": surface.input,
316
+ "--vscode-dropdown-foreground": text.primary,
317
+ "--vscode-dropdown-border": border.default,
318
+ "--vscode-badge-background": accent.base,
319
+ "--vscode-badge-foreground": accent.onAccent,
320
+ "--vscode-terminal-background": surface.base,
321
+ "--vscode-terminal-foreground": text.primary,
322
+ "--vscode-testing-iconPassed": state.success,
323
+ "--vscode-testing-iconFailed": state.error,
324
+ "--vscode-testing-iconErrored": state.error,
325
+ "--vscode-testing-iconQueued": state.info,
326
+ "--vscode-testing-iconSkipped": state.warning,
327
+ "--vscode-testing-iconUnset": icons.disabled,
328
+ };
329
+ for (const name of VSCODE_CORE) put(name, vscodeCoreValues[name]);
330
+
331
+ // 状态色家族(--vscode-icube--status-* 与 --ras-status-* 同步)
332
+ const statusColor = { primary: accent.base, error: state.error, success: state.success, warning: state.warning, alert: state.info };
333
+ for (const kind of ICUBE_STATUS_KINDS) {
334
+ const c = statusColor[kind];
335
+ for (const part of ICUBE_STATUS_PARTS) {
336
+ let v;
337
+ if (part === "default") v = c;
338
+ else if (part === "hover") v = mix(c, 84, "white");
339
+ else if (part === "active") v = mix(c, 84, "black");
340
+ else v = withAlpha(c, { "surface-l1": 0.12, "surface-l2": 0.2, "surface-l3": 0.3 }[part]);
341
+ put(`--vscode-icube--status-${kind}-${part}`, v);
342
+ put(`--ras-status-${kind}-${part}`, v);
343
+ }
344
+ }
345
+ // diff 提示色
346
+ put("--vscode-icube--cue-diff-add", withAlpha(state.success, 0.2));
347
+ put("--vscode-icube--cue-diff-add-inline", withAlpha(state.success, 0.35));
348
+ put("--vscode-icube--cue-diff-add-popup", withAlpha(state.success, 0.25));
349
+ put("--vscode-icube--cue-diff-remove", withAlpha(state.error, 0.2));
350
+ put("--vscode-icube--cue-diff-line", border.subtle);
351
+
352
+ // 品牌色阶:从 accent 一个值派生整条 ramp
353
+ for (const [stop, pct, toward] of RAMP_STOPS) {
354
+ put(`--brand-brand-${stop}`, toward ? mix(accent.base, pct, toward) : accent.base);
355
+ }
356
+ const neutral = text.secondary;
357
+ for (const [stop, pct, toward] of RAMP_STOPS) {
358
+ put(`--brand-brand-grey-${stop}`, toward ? mix(neutral, pct, toward) : neutral);
359
+ }
360
+
361
+ // 下拉/浮层菜单组件令牌
362
+ const pm = {
363
+ "bg": surface.menu, "text": text.primary, "border": border.subtle,
364
+ "item-bg": withAlpha(surface.menu, 0),
365
+ "item-hover-bg": accent.subtle, "item-selected-bg": accent.subtle,
366
+ "item-disabled-text": text.disabled,
367
+ "button-bg": accent.base, "button-hover-bg": accent.hover,
368
+ "checkmark": accent.base, "mark-color": accent.base,
369
+ "highlight-bg": accent.subtle, "footer-bg": surface.secondary,
370
+ "scrollbar-bg": border.default, "scrollbar-hover-bg": border.strong,
371
+ "separator": border.subtle,
372
+ "shadow": "0 8px 24px rgba(0, 0, 0, 0.36)", "shadow-inset": withAlpha("#ffffff", 0.06),
373
+ };
374
+ for (const [k, v] of Object.entries(pm)) put(`--popup-menu-${k}`, v);
375
+
376
+ return out;
377
+ }
378
+
379
+ // 通用对比度审计:调用方声明语义配对与阈值;无法解析的派生色必须显式报告,不能假装通过。
380
+ function auditPairs(pairs) {
381
+ const failures = [];
382
+ const unverifiable = [];
383
+ for (const pair of pairs || []) {
384
+ const ratio = contrastRatio(pair.fgValue, pair.bgValue);
385
+ const entry = {
386
+ fg: pair.fg,
387
+ fgValue: pair.fgValue,
388
+ bg: pair.bg,
389
+ bgValue: pair.bgValue,
390
+ minRatio: pair.minRatio ?? 4,
391
+ };
392
+ if (ratio == null) {
393
+ unverifiable.push(entry);
394
+ } else if (ratio < entry.minRatio) {
395
+ failures.push({ ...entry, ratio: +ratio.toFixed(2) });
396
+ }
397
+ }
398
+ return { failures, unverifiable };
399
+ }
400
+
401
+ // 角色对比度审计
402
+ function auditContrast(tokens, opts) {
403
+ const r = deriveRoles(tokens, opts);
404
+ const pairs = [
405
+ ["text.primary", r.text.primary, "surface.base", r.surface.base],
406
+ ["text.primary", r.text.primary, "surface.card", r.surface.card],
407
+ ["text.secondary", r.text.secondary, "surface.card", r.surface.card],
408
+ ["text.tertiary", r.text.tertiary, "surface.card", r.surface.card],
409
+ ["text.primary", r.text.primary, "surface.input", r.surface.input],
410
+ ["text.primary", r.text.primary, "surface.menu", r.surface.menu],
411
+ ["text.primary", r.text.primary, "surface.tooltip", r.surface.tooltip],
412
+ ["accent.onAccent", r.accent.onAccent, "accent.base", r.accent.base],
413
+ ["text.disabled", r.text.disabled, "surface.base", r.surface.base],
414
+ ];
415
+ const failures = [];
416
+ for (const [fn, fv, bn, bv] of pairs) {
417
+ const ratio = contrastRatio(fv, bv);
418
+ if (ratio != null && ratio < (bn === "text.disabled" || fn === "text.disabled" ? 2.5 : 4.0)) {
419
+ failures.push({ fg: fn, fgValue: fv, bg: bn, bgValue: bv, ratio: +ratio.toFixed(2) });
420
+ }
421
+ }
422
+ return failures;
423
+ }
424
+
425
+ return {
426
+ parseColor, luminance, contrastRatio, pickOnColor, withAlpha, mix,
427
+ deriveRoles, buildVarMap, auditContrast, auditPairs,
428
+ NS_BG, NS_TEXT, NS_ICON, NS_BORDER,
429
+ ICUBE_BG, ICUBE_TEXT, ICUBE_ICON, ICUBE_BORDER, ICUBE_STATUS_KINDS, ICUBE_STATUS_PARTS,
430
+ RAS_BG, RAS_TEXT, RAS_ICON, RAS_BORDER, VSCODE_CORE, RAMP_STOPS,
431
+ };
432
+ });