Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions docs/architecture/theme-token-optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ theme preset/runtime、token contract、boundary fallback、
Mermaid、Monaco/editor、Prism syntax、terminal ANSI、language identity 和 UI exception
registry。

`547` 个唯一颜色是前端生产文件的全域审计数,不是普通 app UI 的色值预算。
`498` 个唯一颜色是前端生产文件的全域审计数,不是普通 app UI 的色值预算。
其中包含主题 preset、token contract、Mermaid、Monaco/editor、terminal、syntax、
language identity 和 UI exception 等专用 palette。真正需要继续压缩的是这些专用域
内部能被证明等价的近似色,而不是把它们直接并入普通 app semantic token。前后端职责边界
Expand All @@ -122,12 +122,12 @@ prompt snapshot 投影;CLI/TUI 颜色作为独立终端产品 surface 单独

| 指标 | 当前基线 |
| --- | ---: |
| 扫描的生产前端文件数 | 1537 |
| 扫描的生产前端文件数 | 1538 |
| 忽略的测试文件数 | 223 |
| 忽略的构建生成文件数 | 0 |
| 忽略的构建生成文件数 | 1 |
| 包含颜色字面量的文件数 | 25 |
| 颜色字面量出现次数 | 760 |
| 唯一颜色字面量数量 | 547 |
| 颜色字面量出现次数 | 722 |
| 唯一颜色字面量数量 | 498 |
| 组件或非 token 文件中的颜色出现次数 | 0 |
| 组件或非 token 唯一颜色数量 | 0 |
| App UI 颜色出现次数 | 0 |
Expand All @@ -138,8 +138,8 @@ prompt snapshot 投影;CLI/TUI 颜色作为独立终端产品 surface 单独
| token-equivalent app literal 唯一颜色数量 | 0 |
| 普通组件肉眼不可区分 near color pair | 0 |
| 普通组件需证据复核的 near color pair | 0 |
| 专用域肉眼不可区分 near color pair | 1 |
| 专用域需证据复核的 near color pair | 324 |
| 专用域肉眼不可区分 near color pair | 0 |
| 专用域需证据复核的 near color pair | 195 |

当前审计未发现 CSS 变量契约层面的硬错误:

Expand Down Expand Up @@ -188,14 +188,14 @@ prompt snapshot 投影;CLI/TUI 颜色作为独立终端产品 surface 单独

| 区域 | 当前出现次数 | 当前唯一色数 | 说明 |
| --- | ---: | ---: | --- |
| Theme presets | 223 | 200 | 主题个性与 palette 映射,不作为普通 app literal 直接合并;仅在同一主题内角色可证明等价时由 preset helper 复用 |
| Token contracts | 172 | 156 | `tokens.scss` 等静态契约根 |
| Editor | 56 | 53 | Monaco/editor 专用域,不能直接泛化到 app token;组件装饰色已迁出 raw literal |
| Mermaid | 103 | 92 | Mermaid 专用渲染域 |
| Theme runtime | 39 | 39 | `ThemeService.ts` 运行时注入;暗色 card alpha fallback 和 scrollbar fallback 已收敛到现有 overlay stop |
| Theme presets | 219 | 187 | 主题个性与 palette 映射,不作为普通 app literal 直接合并;仅在同一主题内角色可证明等价时由 preset helper 复用 |
| Token contracts | 146 | 133 | `tokens.scss` 等静态契约根 |
| Editor | 55 | 50 | Monaco/editor 专用域,不能直接泛化到 app token;组件装饰色已迁出 raw literal |
| Mermaid | 98 | 82 | Mermaid 专用渲染域 |
| Theme runtime | 37 | 36 | `ThemeService.ts` 运行时注入;暗色 card alpha fallback 和 scrollbar fallback 已收敛到现有 overlay stop |
| Language identity | 52 | 50 | 语言身份色,已集中到 identity registry |
| Terminal | 37 | 29 | terminal/ANSI 专用域;工具命令空状态已复用 `--tool-command-empty-rgb`,不再保留独立 raw 色 |
| Boundary fallback | 22 | 21 | iframe/miniapp/截图兜底值,不作为普通 app token |
| Boundary fallback | 22 | 20 | iframe/miniapp/截图兜底值,不作为普通 app token |
| Visual effects | 0 | 0 | StreamText/TextStroke raw literal 已迁出普通组件层 |
| UI exception registry | 38 | 34 | 已归档的 UI 例外色,包含 review team、agent capability、template context、insights 和 inspector 等固定身份色 |
| Generated widget | 0 | 0 | 颜色默认值已迁到 boundary fallback registry |
Expand All @@ -208,26 +208,26 @@ theme preset 或 boundary fallback 误算成普通 app UI 债务。当前队列

| 专用域 | 肉眼不可区分 pair | 需证据复核 pair | 后续处理原则 |
| --- | ---: | ---: | --- |
| Theme presets | 1 | 170 | 优先处理同一 theme 内 hex/rgb 完全等价和同 alpha 阶重复;保留主题个性 |
| Theme runtime | 0 | 26 | 先与静态 token 和 payload contract 对齐,避免 early render 或 system theme 回退 |
| Token contracts | 0 | 86 | 优先 alias 精确等价值;状态、层级和 alpha ramp 不按数值强合并 |
| Boundary fallback | 0 | 5 | 只在 iframe/截图 first paint 验证后收缩 |
| Mermaid | 0 | 30 | 需检查节点、边、文本、错误态和 light/dark Mermaid 输出 |
| Editor | 0 | 7 | 需检查 Monaco selection、diff、inline highlight 和 light/dark editor 可读性 |
| Theme presets | 0 | 109 | 优先处理同一 theme 内 hex/rgb 完全等价和同 alpha 阶重复;保留主题个性 |
| Theme runtime | 0 | 18 | 先与静态 token 和 payload contract 对齐,避免 early render 或 system theme 回退 |
| Token contracts | 0 | 50 | 优先 alias 精确等价值;状态、层级和 alpha ramp 不按数值强合并 |
| Boundary fallback | 0 | 4 | 只在 iframe/截图 first paint 验证后收缩 |
| Mermaid | 0 | 12 | 需检查节点、边、文本、错误态和 light/dark Mermaid 输出 |
| Editor | 0 | 2 | 需检查 Monaco selection、diff、inline highlight 和 light/dark editor 可读性 |
| Syntax / Terminal / Generated widget / Debug overlay / UI exception / Language identity / Visual effects | 0 | 0 | 当前无 near 队列;新增会被单域 baseline 拦截 |

剩余高频文件均为专用 palette 或集中 registry:

| 文件 | 颜色出现次数 | 后续处理策略 |
| --- | ---: | --- |
| `src/web-ui/src/component-library/styles/tokens.scss` | 150 | 根 token 契约;优先处理同语义 alias,避免把状态/层级 ramp 按数值强合并 |
| `src/web-ui/src/tools/mermaid-editor/theme/mermaidThemeFallbacks.ts` | 72 | Mermaid 专用渲染兜底;需以节点、边、文本、错误态截图为依据 |
| `src/web-ui/src/component-library/styles/tokens.scss` | 130 | 根 token 契约;优先处理同语义 alias,避免把状态/层级 ramp 按数值强合并 |
| `src/web-ui/src/tools/mermaid-editor/theme/mermaidThemeFallbacks.ts` | 67 | Mermaid 专用渲染兜底;需以节点、边、文本、错误态截图为依据 |
| `src/web-ui/src/shared/theme/languageIdentityAccents.ts` | 52 | 内置 language/file identity registry;调用方复用常量 |
| `src/web-ui/src/tools/editor/themes/bitfun-dark.theme.ts` | 47 | Monaco theme palette;不拆散到普通 app token |
| `src/web-ui/src/infrastructure/theme/core/ThemeService.ts` | 39 | 运行时注入;需保持 early render、system theme 和 payload 导出兼容 |
| `src/web-ui/src/tools/editor/themes/bitfun-dark.theme.ts` | 46 | Monaco theme palette;不拆散到普通 app token |
| `src/web-ui/src/infrastructure/theme/core/ThemeService.ts` | 37 | 运行时注入;需保持 early render、system theme 和 payload 导出兼容 |
| `src/web-ui/src/shared/theme/uiExceptionAccents.ts` | 38 | 固定 UI 身份/角色色 registry;新增必须说明 owner/role |
| `src/web-ui/src/tools/terminal/utils/xtermTheme.ts` | 36 | terminal ANSI palette;不与 app semantic color 合并 |
| `src/web-ui/src/infrastructure/theme/presets/slate-theme.ts` | 31 | theme preset palette;保留主题个性和 alpha ramp 边界 |
| `src/web-ui/src/infrastructure/theme/presets/slate-theme.ts` | 30 | theme preset palette;保留主题个性和 alpha ramp 边界 |

组件级 `var(--token, fallback)` 已收敛到 0;原先的 7 个 fallback token 不再需要
fallback contract registry 保留。
Expand Down Expand Up @@ -290,8 +290,8 @@ Phase 6 防回退约束:
| --- | ---: | ---: | --- |
| `nearPairs.indistinguishableTotal` | 0 | 0 | 阻止新增普通组件肉眼不可区分 pair 未被合并或记录 |
| `nearPairs.nearTotal` | 0 | 0 | 阻止新增普通组件 near color 债务;新增必须合并、归类或记录理由 |
| `colorDomainNearPairs.indistinguishableTotal` | 1 | 1 | 控制专用域肉眼不可区分 pair 不继续增长,后续只能逐步降低或补充证据 |
| `colorDomainNearPairs.nearTotal` | 324 | 324 | 控制 theme preset/runtime/token/editor/Mermaid 等专用域 near 队列规模 |
| `colorDomainNearPairs.indistinguishableTotal` | 0 | 0 | 控制专用域肉眼不可区分 pair 不继续增长,后续只能逐步降低或补充证据 |
| `colorDomainNearPairs.nearTotal` | 195 | 195 | 控制 theme preset/runtime/token/editor/Mermaid 等专用域 near 队列规模 |
| `colorScopes.appUi.uniqueColors` | 0 | 0 | 阻止普通组件 raw color 唯一色回涨 |
| `colorScopes.appUi.occurrences` | 0 | 0 | 阻止普通组件 raw color 出现次数回涨 |
| `tokenAliasLiterals.occurrences` | 0 | 0 | 阻止重新出现可映射到 token 的 app literal |
Expand Down
38 changes: 19 additions & 19 deletions scripts/theme-color-governance-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@
"max": 0
},
"colorScopes.token.uniqueColors": {
"max": 329
"max": 290
},
"colorScopes.exception.uniqueColors": {
"max": 269
"max": 257
},
"cssVarDefinitions.unresolvedRequiredUnique": {
"max": 0
Expand Down Expand Up @@ -147,28 +147,28 @@
"max": 0
},
"colorDomainNearPairs.indistinguishableTotal": {
"max": 1
"max": 0
},
"colorDomainNearPairs.nearTotal": {
"max": 324
"max": 195
},
"colorDomainNearPairs.themePreset.indistinguishableTotal": {
"max": 1
"max": 0
},
"colorDomainNearPairs.themePreset.nearTotal": {
"max": 170
"max": 109
},
"colorDomainNearPairs.themeRuntime.indistinguishableTotal": {
"max": 0
},
"colorDomainNearPairs.themeRuntime.nearTotal": {
"max": 26
"max": 18
},
"colorDomainNearPairs.tokenContract.indistinguishableTotal": {
"max": 0
},
"colorDomainNearPairs.tokenContract.nearTotal": {
"max": 86
"max": 50
},
"colorDomainNearPairs.generatedWidget.indistinguishableTotal": {
"max": 0
Expand All @@ -180,19 +180,19 @@
"max": 0
},
"colorDomainNearPairs.boundaryFallback.nearTotal": {
"max": 5
"max": 4
},
"colorDomainNearPairs.mermaid.indistinguishableTotal": {
"max": 0
},
"colorDomainNearPairs.mermaid.nearTotal": {
"max": 30
"max": 12
},
"colorDomainNearPairs.editor.indistinguishableTotal": {
"max": 0
},
"colorDomainNearPairs.editor.nearTotal": {
"max": 7
"max": 2
},
"colorDomainNearPairs.syntax.indistinguishableTotal": {
"max": 0
Expand Down Expand Up @@ -237,16 +237,16 @@
"max": 0
},
"colorDomainScopes.themePreset.occurrences": {
"max": 223
"max": 219
},
"colorDomainScopes.themePreset.uniqueColors": {
"max": 200
"max": 187
},
"colorDomainScopes.themeRuntime.occurrences": {
"max": 39
"max": 37
},
"colorDomainScopes.tokenContract.occurrences": {
"max": 172
"max": 146
},
"colorDomainScopes.generatedWidget.occurrences": {
"max": 0
Expand All @@ -258,10 +258,10 @@
"max": 22
},
"colorDomainScopes.boundaryFallback.uniqueColors": {
"max": 21
"max": 20
},
"colorDomainScopes.editor.occurrences": {
"max": 56
"max": 55
},
"colorDomainScopes.syntax.occurrences": {
"max": 18
Expand Down Expand Up @@ -294,10 +294,10 @@
"max": 0
},
"colorDomainScopes.mermaid.occurrences": {
"max": 103
"max": 98
},
"colorDomainScopes.mermaid.uniqueColors": {
"max": 92
"max": 82
}
}
}
6 changes: 3 additions & 3 deletions src/apps/desktop/src/generated/startup_theme_bootstrap.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"id": "bitfun-midnight",
"bgPrimary": "#2b2d30",
"bgSecondary": "#1e1f22",
"bgScene": "#27292c",
"bgScene": "#2b2d30",
"isLight": false,
"textPrimary": "#bcbec4",
"textMuted": "#6f737a",
Expand All @@ -55,9 +55,9 @@
},
{
"id": "bitfun-china-night",
"bgPrimary": "#1a1814",
"bgPrimary": "#1a1a1a",
"bgSecondary": "#212019",
"bgScene": "#1e1c17",
"bgScene": "#1a1a1a",
"isLight": false,
"textPrimary": "#e8e6e1",
"textMuted": "#928f89",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"themeType": "dark",
"bgPrimary": "#2b2d30",
"bgSecondary": "#1e1f22",
"bgScene": "#27292c",
"bgScene": "#2b2d30",
"textPrimary": "#bcbec4",
"textMuted": "#6f737a",
"accent500": "#58a6ff",
Expand Down Expand Up @@ -91,9 +91,9 @@
{
"id": "bitfun-china-night",
"themeType": "dark",
"bgPrimary": "#1a1814",
"bgPrimary": "#1a1a1a",
"bgSecondary": "#212019",
"bgScene": "#1e1c17",
"bgScene": "#1a1a1a",
"textPrimary": "#e8e6e1",
"textMuted": "#928f89",
"accent500": "#73a5cc",
Expand Down
23 changes: 10 additions & 13 deletions src/mobile-web/src/theme/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,16 @@ const themeMap: Record<ThemeId, Record<string, string>> = {
light: lightTheme,
};

const themeColors: Record<ThemeId, string> = {
dark: '#121214',
light: '#f2f2f7',
};
const BACKGROUND_KEY = '--color-bg-primary';
const TEXT_KEY = '--color-text-primary';

const textColors: Record<ThemeId, string> = {
dark: '#e8e8e8',
light: '#1c1c1e',
};
function getThemeValue(id: ThemeId, key: string): string {
return themeMap[id][key];
}

function buildThemeCSS(id: ThemeId, vars: Record<string, string>): string {
const bg = themeColors[id];
const fg = textColors[id];
const bg = getThemeValue(id, BACKGROUND_KEY);
const fg = getThemeValue(id, TEXT_KEY);
const parts: string[] = [':root {'];
for (const [key, value] of Object.entries(vars)) {
parts.push(` ${key}: ${value};`);
Expand Down Expand Up @@ -79,13 +76,13 @@ function commitThemeDOM(id: ThemeId) {
body.setAttribute('data-theme', id);

// 4. Inline style fallbacks (highest specificity)
body.style.backgroundColor = themeColors[id];
body.style.color = textColors[id];
body.style.backgroundColor = getThemeValue(id, BACKGROUND_KEY);
body.style.color = getThemeValue(id, TEXT_KEY);

// 5. Update <meta name="theme-color">
const meta = document.querySelector('meta[name="theme-color"]');
if (meta) {
meta.setAttribute('content', themeColors[id]);
meta.setAttribute('content', getThemeValue(id, BACKGROUND_KEY));
meta.removeAttribute('media');
}
}
Expand Down
Loading
Loading