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.
- package/CHANGELOG.md +99 -0
- package/CONTRIBUTING.md +37 -0
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/SECURITY.md +28 -0
- package/dist/bin/twskin.d.ts +3 -0
- package/dist/bin/twskin.d.ts.map +1 -0
- package/dist/bin/twskin.js +4 -0
- package/dist/bin/twskin.js.map +1 -0
- package/dist/commands.d.ts +41 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +421 -0
- package/dist/commands.js.map +1 -0
- package/dist/context.d.ts +14 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +51 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/lock.d.ts +3 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +52 -0
- package/dist/lock.js.map +1 -0
- package/dist/main.d.ts +9 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +92 -0
- package/dist/main.js.map +1 -0
- package/dist/output.d.ts +4 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +27 -0
- package/dist/output.js.map +1 -0
- package/dist/persistent-cdp.d.ts +14 -0
- package/dist/persistent-cdp.d.ts.map +1 -0
- package/dist/persistent-cdp.js +140 -0
- package/dist/persistent-cdp.js.map +1 -0
- package/dist/system.d.ts +64 -0
- package/dist/system.d.ts.map +1 -0
- package/dist/system.js +234 -0
- package/dist/system.js.map +1 -0
- package/dist/theme-download.d.ts +9 -0
- package/dist/theme-download.d.ts.map +1 -0
- package/dist/theme-download.js +250 -0
- package/dist/theme-download.js.map +1 -0
- package/dist/themes.d.ts +9 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +185 -0
- package/dist/themes.js.map +1 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/publishing.md +45 -0
- package/docs/theme-distribution.md +62 -0
- package/package.json +74 -0
- package/runtime/component-map.mjs +200 -0
- package/runtime/injector.mjs +705 -0
- package/runtime/manifest.json +47 -0
- package/runtime/restore.sh +33 -0
- package/runtime/skin.js +1133 -0
- package/runtime/start.sh +86 -0
- package/runtime/styles/base.css +687 -0
- package/runtime/styles/manager.css +346 -0
- 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
|
+
});
|