diff --git a/docs/architecture/theme-token-optimization.md b/docs/architecture/theme-token-optimization.md index 3a57f2aedf..82546b80e5 100644 --- a/docs/architecture/theme-token-optimization.md +++ b/docs/architecture/theme-token-optimization.md @@ -139,7 +139,7 @@ prompt snapshot 投影;CLI/TUI 颜色作为独立终端产品 surface 单独 | 普通组件肉眼不可区分 near color pair | 0 | | 普通组件需证据复核的 near color pair | 0 | | 专用域肉眼不可区分 near color pair | 0 | -| 专用域需证据复核的 near color pair | 125 | +| 专用域需证据复核的 near color pair | 119 | 当前审计未发现 CSS 变量契约层面的硬错误: @@ -158,10 +158,10 @@ prompt snapshot 投影;CLI/TUI 颜色作为独立终端产品 surface 单独 | 治理契约指标 | 当前值 | 说明 | | --- | ---: | --- | -| compatibility alias contracts | 65 | 显式列出历史别名、canonical 目标、owner、保留原因和退场条件;旧 key 只作为对外兼容定义保留 | +| compatibility alias contracts | 0 | 显式 root/runtime 兼容别名已退役;历史 generated widget 内容通过 iframe fallback 兼容,尺寸旧 family 由 family contract 单独治理 | | compatibility alias 直接使用 key | 0 | 产品代码不再通过 `var(--legacy-alias)` 读取旧 key;新增直接读取会被 baseline 拦截 | -| compatibility alias 直接使用次数 | 0 | 旧 key 定义仍存在,但内部样式读取统一使用 canonical token | -| stale compatibility alias contracts | 0 | 防止 registry 保留已经没有静态/runtime/iframe 兼容定义的旧 key | +| compatibility alias 直接使用次数 | 0 | 显式旧 key 不再在 root/runtime 定义,内部样式读取统一使用 canonical token | +| stale compatibility alias contracts | 0 | 防止 registry 重新保留没有必要的静态/runtime 兼容定义 | | compatibility alias family contracts | 2 | `--radius-* -> --size-radius-*`、`--spacing-* -> --size-gap-*` | | compatibility alias family 直接使用 key | 0 | `--radius-*`、`--spacing-*` 旧 family 不再被内部 `var()` 读取 | | compatibility alias family 直接使用次数 | 0 | generated widget payload 只暴露 canonical family,iframe shell 通过 alias fallback 兼容 legacy family | @@ -171,7 +171,7 @@ prompt snapshot 投影;CLI/TUI 颜色作为独立终端产品 surface 单独 | active surface token rename key | 0 | 防止 `--primary-color`、`--operation-color`、`--delay`、`--um-*` 等旧局部 key 回流 | | active surface token rename occurrences | 0 | 防止旧 key 在 SCSS、CSS 或 TSX inline style 中被重新定义或读取 | | surface token rename missing canonical | 0 | 防止 rename registry 指向不存在的 canonical key | -| generated widget payload key | 176 | widget iframe 对外主题变量 allowlist,作为外部边界单独预算,不计入内部 alias 读取 | +| generated widget payload key | 157 | widget iframe 对外主题变量 allowlist,作为外部边界单独预算,不计入内部 alias 读取;不再导出 tool-card 颜色转发和未使用 ghost 状态 key,历史 tool-card 颜色只在 iframe fallback 映射 | | generated widget payload compatibility alias | 0 | payload 不再直接暴露 legacy alias;历史生成内容通过 iframe 内 alias fallback 读取 canonical key | | generated widget payload compatibility family key | 0 | payload 只暴露 `--size-radius-*`、`--size-gap-*` canonical family,旧 `--radius-*`、`--spacing-*` 由 iframe alias fallback 保留 | | generated widget payload external-only compatibility key | 0 | payload 中已清空仅因外部兼容保留且内部产品代码不再读取的 legacy key | @@ -188,7 +188,7 @@ prompt snapshot 投影;CLI/TUI 颜色作为独立终端产品 surface 单独 | 区域 | 当前出现次数 | 当前唯一色数 | 说明 | | --- | ---: | ---: | --- | -| Theme presets | 206 | 174 | 主题个性与 palette 映射,不作为普通 app literal 直接合并;仅在同一主题内角色可证明等价时由 preset helper 复用 | +| Theme presets | 190 | 162 | 主题个性与 palette 映射,不作为普通 app literal 直接合并;仅在同一主题内角色可证明等价时由 preset helper 复用 | | Token contracts | 128 | 114 | `tokens.scss` 等静态契约根 | | Editor | 55 | 48 | Monaco/editor 专用域,不能直接泛化到 app token;组件装饰色已迁出 raw literal | | Mermaid | 93 | 74 | Mermaid 专用渲染域 | @@ -208,7 +208,7 @@ theme preset 或 boundary fallback 误算成普通 app UI 债务。当前队列 | 专用域 | 肉眼不可区分 pair | 需证据复核 pair | 后续处理原则 | | --- | ---: | ---: | --- | -| Theme presets | 0 | 96 | 优先处理同一 theme 内 hex/rgb 完全等价和同 alpha 阶重复;保留主题个性 | +| Theme presets | 0 | 90 | 优先处理同一 theme 内 hex/rgb 完全等价和同 alpha 阶重复;保留主题个性 | | Theme runtime | 0 | 13 | 先与静态 token 和 payload contract 对齐,避免 early render 或 system theme 回退 | | Token contracts | 0 | 16 | 优先 alias 精确等价值;状态、层级和 alpha ramp 不按数值强合并 | | Boundary fallback | 0 | 0 | first-paint 兜底已收敛到较粗 overlay stop;后续新增必须重新说明边界理由 | @@ -249,7 +249,7 @@ fallback 收敛决策表: | 里程碑 | 稳定要求 | | --- | --- | | 基线与工具 | 审计脚本必须持续区分测试文件、fallback token、dynamic family、exception domain 和 generated widget 外部兼容面。 | -| canonical token 契约 | 内部调用方只读 canonical token;compatibility alias 仅作为显式外部兼容定义保留,新增或删除都必须进入 registry。 | +| canonical token 契约 | 内部调用方只读 canonical token;root/runtime 显式 compatibility alias 保持 0。确需外部兼容时只放在对应边界,如 generated widget iframe fallback 或动态 family contract。 | | fallback 收敛 | 普通组件不得新增 `var(--token, fallback)` 色值;确需边界兜底时先证明 owner、reason 和 boundary。 | | 组件 token 抽取 | 复杂 surface 只能在 semantic token 不足以表达契约时新增 component token,并保持 surface namespace。 | | 近似色治理 | 普通组件 near pair 维持 0;专用域 near pair 只作为证据队列,不能按数值相似自动合并。 | @@ -297,7 +297,7 @@ Phase 6 防回退约束: | `nearPairs.indistinguishableTotal` | 0 | 0 | 阻止新增普通组件肉眼不可区分 pair 未被合并或记录 | | `nearPairs.nearTotal` | 0 | 0 | 阻止新增普通组件 near color 债务;新增必须合并、归类或记录理由 | | `colorDomainNearPairs.indistinguishableTotal` | 0 | 0 | 控制专用域肉眼不可区分 pair 不继续增长,后续只能逐步降低或补充证据 | -| `colorDomainNearPairs.nearTotal` | 125 | 125 | 控制 theme preset/runtime/token/editor/Mermaid 等专用域 near 队列规模 | +| `colorDomainNearPairs.nearTotal` | 119 | 119 | 控制 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 | @@ -313,7 +313,7 @@ Phase 6 防回退约束: | `surfaceTokenRenames.activeUnique` | 0 | 0 | 防止已迁移的 surface-local 旧 key 重新出现 | | `surfaceTokenRenames.activeOccurrences` | 0 | 0 | 防止旧 key 在定义和读取两侧回流 | | `surfaceTokenRenames.missingCanonicalUnique` | 0 | 0 | 防止 surface rename contract 指向不存在的 canonical key | -| `generatedWidgetPayload.varUnique` | 176 | 176 | 控制 widget 对外主题 payload allowlist 不继续膨胀 | +| `generatedWidgetPayload.varUnique` | 157 | 157 | 控制 widget 对外主题 payload allowlist 不继续膨胀 | | `generatedWidgetPayload.compatibilityAliasUnique` | 0 | 0 | 防止 payload 重新直接导出显式 legacy alias | | `generatedWidgetPayload.compatibilityAliasFamilyUnique` | 0 | 0 | 防止 payload 重新直接导出 legacy size family 具体 key | | `generatedWidgetPayload.externalOnlyCompatibilityUnique` | 0 | 0 | 防止 payload 重新保留仅因外部兼容存在的 legacy key | @@ -336,14 +336,14 @@ editor、syntax、terminal、language identity、boundary fallback 等专用域 | surface | 覆盖形态 | 重点风险 | | --- | --- | --- | -| app-shell | desktop webview、web、desktop、narrow、dark/light/system | 旧 alias 仍在 shell 邻近组件使用,system theme 不能假设桌面专有行为 | +| app-shell | desktop webview、web、desktop、narrow、dark/light/system | shell 邻近组件必须只读 canonical token,system theme 不能假设桌面专有行为 | | flow-chat | desktop webview、web、desktop、narrow、streaming/error/empty | virtualized 和历史 turn 可能隐藏 token 回归 | -| tool-cards-review | tool card、review panel、expanded/collapsed/status | danger alias 保留 destructive 语义,不能和 error 无证据合并 | +| tool-cards-review | tool card、review panel、expanded/collapsed/status | destructive/error 状态统一读取 `--color-error*`;仅 generated widget iframe fallback 保留历史 `--color-danger*` 映射 | | code-editor-diff | Monaco、diff、selection、added/deleted/conflict | editor/diff 色表达相邻状态,不能按数值相似直接合并 | | terminal | ANSI normal/bright、selection、error | ANSI 语义独立于 app semantic color | | markdown-mermaid | Markdown、Prism、Mermaid、diagram/error | Markdown accent 通过 `--markdown-primary-color` 表达;tool-card 历史 `--primary-color` 已退场,Mermaid 角色不等于 app status | | generated-widget | iframe fallback、host payload、loading/error | payload 只导出 canonical key;旧 alias 兼容集中在 iframe fallback | -| theme-settings | theme switcher、system/custom theme preview | custom theme preview 可能比普通组件更早暴露 runtime alias 缺失 | +| theme-settings | theme switcher、system/custom theme preview | custom theme preview 可能比普通组件更早暴露 runtime canonical token 缺失 | | mobile-web-shell | mobile-web、mobile/narrow、loading/error/navigation | mobile web 是独立构建目标,不能只依赖 desktop WebView 验证 | ## 现有架构地图 @@ -394,7 +394,7 @@ editor、syntax、terminal、language identity、boundary fallback 等专用域 改进方向: - fallback 只保留在明确的兼容边界。 -- 根主题层先补足兼容别名。 +- 根主题层不再新增显式 legacy alias;确有外部边界时优先放在 generated widget iframe fallback 或 family contract。 - 组件确认 canonical token 一定存在后,移除局部 fallback 字面量。 ### 3. 未定义或历史命名 token @@ -526,21 +526,21 @@ semantic token 描述产品级语义,应作为共享 UI 的默认使用层。 ### 兼容别名 -第一阶段应先保留兼容别名,避免为了清理 token 引入大面积视觉变化。 +显式 root/runtime 兼容别名已完成退役,避免旧 key 长期占用主题契约预算;历史 generated widget 内容通过 iframe fallback 兼容。 | 历史或漂移 token | 建议 canonical 目标 | 说明 | | --- | --- | --- | -| `--accent-primary` | `--color-accent-500` | `--color-primary` 也是同一 accent midpoint 的历史兼容名,新代码不应再以 primary 作为 canonical。 | -| `--text-primary` | `--color-text-primary` | 仅兼容别名。 | -| `--text-secondary` | `--color-text-secondary` | 仅兼容别名。 | -| `--text-muted` | `--color-text-muted` | 仅兼容别名。 | -| `--bg-primary` | `--color-bg-primary` | 仅兼容别名。 | -| `--bg-secondary` | `--color-bg-secondary` | 仅兼容别名。 | -| `--bg-tertiary` | `--color-bg-tertiary` | 仅兼容别名。 | -| `--border-primary` | `--border-base` | 当前 primary border 不表示更强层级,仅保留 legacy spelling。 | -| `--color-border-subtle` | `--border-subtle` | 统一到 border 命名族。 | -| `--color-danger` | `--color-error` | 当前共享 error palette,但保留 destructive action 语义;删除前需迁入 error 或 action token。 | -| `--color-bg-hover` | `--element-bg-hover` | 当前泛化 hover 已收敛到 element interaction layer。 | +| `--accent-primary` | `--color-accent-500` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射到 accent midpoint。 | +| `--text-primary` | `--color-text-primary` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--text-secondary` | `--color-text-secondary` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--text-muted` | `--color-text-muted` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--bg-primary` | `--color-bg-primary` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--bg-secondary` | `--color-bg-secondary` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--bg-tertiary` | `--color-bg-tertiary` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--border-primary` | `--border-base` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--color-border-subtle` | `--border-subtle` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射。 | +| `--color-danger` | `--color-error` | root/runtime alias 已退役;历史 generated widget 内容仅通过 iframe fallback 映射到 error palette。 | +| `--color-bg-hover` | `--element-bg-hover` | root/runtime alias 已退役;泛化 hover 收敛到 element interaction layer。 | | `--radius-*` | `--size-radius-*` | canonical family 为 `--size-radius-*`,旧 family 只作 legacy/widget iframe fallback 兼容。 | | `--spacing-*` | `--size-gap-*` | canonical family 为 `--size-gap-*`,旧 family 只作 legacy/widget iframe fallback 兼容。 | @@ -797,8 +797,8 @@ semantic token 描述产品级语义,应作为共享 UI 的默认使用层。 | intent 色被过度归一 | warning、error、success、info 或 destructive 语义混淆。 | intent token 即使色值接近,也保留独立语义。 | | git/diff 色被当作普通 success/error | added/deleted/changed/conflict 扫描效率下降。 | 使用专用 git/diff token,只有复核后才 alias 到 app intent。 | | 主题个性被抹平 | 用户选择主题的价值下降。 | theme preset 保留自己的 primitive/accent 映射。 | -| fallback 先删、alias 后补 | embedded 或 early render surface 样式丢失。 | 先加 alias,再删除 fallback。 | -| 兼容 alias 读点清零时误删定义 | 旧主题、生成式 widget iframe fallback 或外部自定义内容读取旧 key 时样式丢失。 | 只迁移内部 `var()` 读取;`tokens.scss` 和 runtime 注入继续保留兼容定义,widget payload 只导出 canonical key,legacy key 由 iframe fallback 映射。 | +| fallback 先删、边界兜底后补 | embedded 或 early render surface 样式丢失。 | 先确认 canonical export 或边界 fallback,再删除普通组件 fallback。 | +| 兼容 alias 读点清零时误删边界兼容 | 旧主题、生成式 widget iframe fallback 或外部自定义内容读取旧 key 时样式丢失。 | 只迁移内部 `var()` 读取;root/runtime 显式 alias 保持退役,widget payload 只导出 canonical key,legacy key 仅由 iframe fallback 映射。 | | 静态 token 与运行时 token 不一致 | widget、SCSS、runtime theme 注入结果不一致。 | `tokens.scss`、`ThemeService.ts`、`themePayload.ts` 同阶段对齐。 | | 动态 CSS 变量 key 被误判为未定义 | inline style 或数据驱动变量失去兜底,导致特定卡片、标签或分组颜色缺失。 | 对动态 key 建立运行时设置清单;删除 fallback 前补组件根默认值或保留边界 fallback。 | | contrast 验证不可信 | 可访问性回归可能漏掉。 | 先实现真实 contrast 检查,再声称可访问性改善。 | @@ -968,17 +968,18 @@ alpha 差异经常承担 elevation 和交互状态,不应全部压成一个值 内部调用方必须使用 canonical token;删除旧 key 前必须先完成 widget iframe fallback 兼容检查、 外部内容影响评估和视觉复核。 -- `--color-text-tertiary` 当前不是一等 text ramp,兼容映射到 - `--color-text-muted`;只有设计系统确认需要独立第三层文本强度时才转正。 -- `--color-primary` 当前是 `--color-accent-500` 的历史兼容名;新代码应使用 - accent scale 或组件 action token,只有 primary action 与 accent 明确分化时才重新建模。 -- `--color-danger` 当前映射到 `--color-error`,但保留 destructive action 语义;只有破坏性动作 - 明确选择 error token 或迁入专用 action token 后才删除。 +- `--color-text-tertiary` 当前不是一等 text ramp,root/runtime alias 已退役; + 历史 generated widget 内容仅通过 iframe fallback 映射到 `--color-text-muted`。 +- `--color-primary*`、`--accent-primary*`、`--color-accent` 和旧 semantic numeric key + 已从 root token、runtime 注入和全局 compatibility alias registry 退役;新代码应使用 + accent scale、semantic role 或组件 action token,历史 generated widget 内容仅通过 iframe alias fallback 映射。 +- `--color-danger*` 已从 root token、runtime 注入和全局 compatibility alias registry 退役; + 内部 destructive/error 状态统一读取 `--color-error*`,历史 generated widget 内容仅通过 iframe alias fallback 映射。 - generated widget payload 已停止导出低风险的 `--color-accent*` legacy、`--color-primary*`、 `--accent-primary*`、`--color-danger*`、旧 surface/bg/text 细分别名、旧 semantic scale、 旧 border 细分别名、核心 `background/bg/text` 兼容名、`--radius-*`、`--spacing-*` - 以及部分旧 font/motion 拼写;根 token 与 runtime 注入仍保留这些兼容定义,generated widget - iframe 也提供 alias fallback,避免影响 app 内部旧主题、历史 CSS 和已生成 widget 内容。 + 以及部分旧 font/motion 拼写和 tool-card 颜色转发;根 token 与 runtime 注入不再保留显式 legacy alias,generated widget + iframe 提供 alias fallback,避免影响已生成 widget 内容。 - `--color-overlay-white-03` 已从静态 root、runtime 注入和 generated widget payload 退役; generated widget iframe alias fallback 将其映射到 `--color-overlay-white-04`,避免历史内容丢失样式。 - generated widget payload 不导出宿主 FlowChat、navigation 和 z-index 内部 key;iframe 内容保留独立布局和 diff --git a/scripts/audit-theme-colors.test.mjs b/scripts/audit-theme-colors.test.mjs index 10fd617cca..11bb4a7e3b 100644 --- a/scripts/audit-theme-colors.test.mjs +++ b/scripts/audit-theme-colors.test.mjs @@ -170,20 +170,13 @@ test('repository dynamic CSS var families match the registered contract', () => assert.equal(report.surfaceTokenRenames.missingCanonicalUnique, 0); }); -test('generated widget iframe compatibility aliases match theme alias contracts', () => { +test('generated widget iframe compatibility aliases stay outside root/runtime contracts', () => { const source = readText(path.join(root, 'src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts')); const aliasEntries = Array.from(source.matchAll(/'([^']+)': '([^']+)'/g)) .map(([, key, canonical]) => [key, canonical]); const aliasKeys = new Set(aliasEntries.map(([key]) => key)); - const contractByKey = new Map(TOKEN_COMPATIBILITY_ALIAS_CONTRACTS.map(contract => [ - contract.key, - contract, - ])); - const resolveContract = (key) => { - const explicit = contractByKey.get(key); - if (explicit) { - return explicit; - } + const explicitContractKeys = new Set(TOKEN_COMPATIBILITY_ALIAS_CONTRACTS.map(contract => contract.key)); + const resolveFamilyContract = (key) => { const family = TOKEN_COMPATIBILITY_ALIAS_FAMILY_CONTRACTS.find(contract => ( key.startsWith(contract.prefix) && key.length > contract.prefix.length )); @@ -198,11 +191,74 @@ test('generated widget iframe compatibility aliases match theme alias contracts' assert.ok(aliasEntries.length > 0, 'widget iframe compatibility aliases must be explicit'); assert.equal(aliasKeys.size, aliasEntries.length, 'widget iframe compatibility aliases must be unique'); + assert.equal(explicitContractKeys.size, 0, 'explicit root/runtime compatibility alias contracts must remain retired'); for (const [key, canonical] of aliasEntries) { - const contract = resolveContract(key); - assert.ok(contract, `${key} must be registered as a compatibility alias`); - assert.equal(canonical, contract.canonical, `${key} must point to the registered canonical token`); + const familyContract = resolveFamilyContract(key); + if (familyContract) { + assert.equal(canonical, familyContract.canonical, `${key} must point to the registered family canonical token`); + } else { + assert.ok(!explicitContractKeys.has(key), `${key} must not re-enter root/runtime compatibility alias contracts`); + assert.match(canonical, /^--[a-z0-9-]+$/); + assert.notEqual(key, canonical, `${key} must point to a different canonical token`); + } + } +}); + +test('retired explicit iframe aliases do not reappear outside the compatibility boundary', () => { + const source = readText(path.join(root, 'src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts')); + const retiredExplicitAliases = Array.from(source.matchAll(/'([^']+)': '([^']+)'/g)) + .map(([, key]) => key) + .filter(key => !TOKEN_COMPATIBILITY_ALIAS_FAMILY_CONTRACTS.some(contract => ( + key.startsWith(contract.prefix) && key.length > contract.prefix.length + ))); + const allowedFiles = new Set([ + 'docs/architecture/theme-token-optimization.md', + 'src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts', + 'src/web-ui/src/tools/generative-widget/themePayload.test.ts', + ]); + const allowedExtensions = new Set(['.ts', '.tsx', '.scss', '.css', '.mjs', '.json']); + const searchRoots = ['docs', 'scripts', 'src/web-ui/src']; + const hits = []; + + const scanFile = (filePath) => { + const relativePath = path.relative(root, filePath).replace(/\\/g, '/'); + if (allowedFiles.has(relativePath) || !allowedExtensions.has(path.extname(filePath))) { + return; + } + const text = readText(filePath); + for (const key of retiredExplicitAliases) { + let index = text.indexOf(key); + while (index !== -1) { + const before = text[index - 1] || ''; + const after = text[index + key.length] || ''; + if (!/[a-zA-Z0-9_-]/.test(before) && !/[a-zA-Z0-9_-]/.test(after)) { + const line = text.slice(0, index).split(/\r?\n/).length; + hits.push(`${relativePath}:${line}: ${key}`); + } + index = text.indexOf(key, index + key.length); + } + } + }; + + const walk = (dir) => { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist' || entry.name === 'target') { + continue; + } + const entryPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + walk(entryPath); + } else { + scanFile(entryPath); + } + } + }; + + for (const searchRoot of searchRoots) { + walk(path.join(root, searchRoot)); } + + assert.deepEqual(hits, []); }); test('theme color audit reports alias family usages whose exact canonical key is missing', (t) => { @@ -322,12 +378,10 @@ test('theme color audit reports deprecated surface-local token names', (t) => { ); }); -test('theme color audit reports compatibility alias usage without treating it as raw color debt', (t) => { +test('theme color audit reports compatibility alias family usage without treating it as raw color debt', (t) => { const { dir, sourceRoot } = createFixture({ 'component-library/styles/tokens.scss': [ ':root {', - ' --color-accent-500: #60a5fa;', - ' --color-primary: var(--color-accent-500);', ' --size-radius-sm: 6px;', ' --radius-sm: var(--size-radius-sm);', '}', @@ -335,7 +389,6 @@ test('theme color audit reports compatibility alias usage without treating it as ].join('\n'), 'app/App.scss': [ '.app {', - ' color: var(--color-primary);', ' border-radius: var(--radius-sm);', '}', '', @@ -347,34 +400,29 @@ test('theme color audit reports compatibility alias usage without treating it as assert.equal(result.status, 0, result.stderr || result.stdout); const report = JSON.parse(result.stdout); - assert.equal(report.compatibilityAliases.usedUnique, 2); - assert.equal(report.compatibilityAliases.occurrences, 2); + assert.equal(report.compatibilityAliases.usedUnique, 1); + assert.equal(report.compatibilityAliases.occurrences, 1); assert.equal(report.compatibilityAliases.familyUsedUnique, 1); assert.equal(report.compatibilityAliases.familyOccurrences, 1); assert.equal(report.compatibilityAliases.missingCanonicalUnique, 0); assert.deepEqual( report.compatibilityAliases.top.map(row => [row.key, row.canonical]), - [ - ['--color-primary', '--color-accent-500'], - ['--radius-sm', '--size-radius-sm'], - ], + [['--radius-sm', '--size-radius-sm']], ); assert.equal(report.colorScopes.appUi.occurrences, 0); }); -test('theme color audit budgets generated widget payload compatibility aliases separately', (t) => { +test('theme color audit budgets generated widget payload compatibility families separately', (t) => { const { dir, sourceRoot } = createFixture({ 'component-library/styles/tokens.scss': [ ':root {', - ' --color-accent-500: #60a5fa;', - ' --color-primary: var(--color-accent-500);', ' --size-radius-sm: 6px;', ' --radius-sm: var(--size-radius-sm);', '}', '', ].join('\n'), 'tools/generative-widget/themePayload.ts': [ - "export const payloadVars = ['--color-accent-500', '--color-primary', '--size-radius-sm', '--radius-sm'];", + "export const payloadVars = ['--size-radius-sm', '--radius-sm'];", '', ].join('\n'), }); @@ -386,16 +434,16 @@ test('theme color audit budgets generated widget payload compatibility aliases s const report = JSON.parse(result.stdout); assert.equal(report.compatibilityAliases.usedUnique, 0); assert.equal(report.compatibilityAliases.familyUsedUnique, 0); - assert.equal(report.generatedWidgetPayload.varUnique, 4); - assert.equal(report.generatedWidgetPayload.compatibilityAliasUnique, 1); + assert.equal(report.generatedWidgetPayload.varUnique, 2); + assert.equal(report.generatedWidgetPayload.compatibilityAliasUnique, 0); assert.equal(report.generatedWidgetPayload.compatibilityAliasFamilyUnique, 1); - assert.equal(report.generatedWidgetPayload.externalOnlyCompatibilityUnique, 2); + assert.equal(report.generatedWidgetPayload.externalOnlyCompatibilityUnique, 1); assert.equal(report.generatedWidgetPayload.undefinedUnique, 0); assert.equal(report.generatedWidgetPayload.missingCompatibilityCanonicalUnique, 0); assert.equal(report.generatedWidgetPayload.unexportedCompatibilityCanonicalUnique, 0); assert.deepEqual( report.generatedWidgetPayload.topCompatibilityAliases.map(row => [row.key, row.canonical]), - [['--color-primary', '--color-accent-500']], + [], ); assert.deepEqual( report.generatedWidgetPayload.topCompatibilityFamilies.map(row => [row.key, row.canonical]), @@ -403,10 +451,7 @@ test('theme color audit budgets generated widget payload compatibility aliases s ); assert.deepEqual( report.generatedWidgetPayload.externalOnlyCompatibility.map(row => [row.key, row.canonical, row.familyPrefix]), - [ - ['--color-primary', '--color-accent-500', null], - ['--radius-sm', '--size-radius-sm', '--radius-'], - ], + [['--radius-sm', '--size-radius-sm', '--radius-']], ); assert.match(report.generatedWidgetPayload.externalOnlyCompatibility[0].removal, /Retire/); }); @@ -440,17 +485,17 @@ test('theme color audit reports generated widget payload compatibility aliases w ); }); -test('theme color audit reports generated widget payload aliases whose canonicals are not exported', (t) => { +test('theme color audit reports generated widget payload family aliases whose canonicals are not exported', (t) => { const { dir, sourceRoot } = createFixture({ 'component-library/styles/tokens.scss': [ ':root {', - ' --color-accent-500: #60a5fa;', - ' --color-primary: var(--color-accent-500);', + ' --size-radius-sm: 6px;', + ' --radius-sm: var(--size-radius-sm);', '}', '', ].join('\n'), 'tools/generative-widget/themePayload.ts': [ - "export const payloadVars = ['--color-primary'];", + "export const payloadVars = ['--radius-sm'];", '', ].join('\n'), }); @@ -461,11 +506,12 @@ test('theme color audit reports generated widget payload aliases whose canonical const report = JSON.parse(result.stdout); assert.equal(report.generatedWidgetPayload.undefinedUnique, 0); + assert.equal(report.generatedWidgetPayload.compatibilityAliasFamilyUnique, 1); assert.equal(report.generatedWidgetPayload.missingCompatibilityCanonicalUnique, 0); assert.equal(report.generatedWidgetPayload.unexportedCompatibilityCanonicalUnique, 1); assert.deepEqual( report.generatedWidgetPayload.unexportedCompatibilityCanonicals.map(row => [row.key, row.canonical]), - [['--color-primary', '--color-accent-500']], + [['--radius-sm', '--size-radius-sm']], ); }); diff --git a/scripts/theme-color-governance-baseline.json b/scripts/theme-color-governance-baseline.json index 3a0e4694b0..bc3b2224a0 100644 --- a/scripts/theme-color-governance-baseline.json +++ b/scripts/theme-color-governance-baseline.json @@ -15,7 +15,7 @@ "max": 0 }, "compatibilityAliases.registeredUnique": { - "max": 65 + "max": 0 }, "compatibilityAliases.usedUnique": { "max": 0 @@ -54,10 +54,10 @@ "max": 0 }, "generatedWidgetPayload.varUnique": { - "max": 176 + "max": 157 }, "generatedWidgetPayload.occurrences": { - "max": 176 + "max": 157 }, "generatedWidgetPayload.undefinedUnique": { "max": 0 @@ -105,7 +105,7 @@ "max": 0 }, "colorScopes.token.uniqueColors": { - "max": 269 + "max": 257 }, "colorScopes.exception.uniqueColors": { "max": 246 @@ -150,13 +150,13 @@ "max": 0 }, "colorDomainNearPairs.nearTotal": { - "max": 125 + "max": 119 }, "colorDomainNearPairs.themePreset.indistinguishableTotal": { "max": 0 }, "colorDomainNearPairs.themePreset.nearTotal": { - "max": 96 + "max": 90 }, "colorDomainNearPairs.themeRuntime.indistinguishableTotal": { "max": 0 @@ -237,10 +237,10 @@ "max": 0 }, "colorDomainScopes.themePreset.occurrences": { - "max": 206 + "max": 190 }, "colorDomainScopes.themePreset.uniqueColors": { - "max": 174 + "max": 162 }, "colorDomainScopes.themeRuntime.occurrences": { "max": 35 diff --git a/scripts/theme-css-var-contract.mjs b/scripts/theme-css-var-contract.mjs index d0c64d4d25..e5b9126e79 100644 --- a/scripts/theme-css-var-contract.mjs +++ b/scripts/theme-css-var-contract.mjs @@ -206,463 +206,7 @@ export const COLOR_DOMAIN_CONTRACTS = [ }, ]; -export const TOKEN_COMPATIBILITY_ALIAS_CONTRACTS = [ - { - key: '--color-bg-flowchat', - canonical: '--color-bg-scene', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Flow chat background remains a named surface alias while the scene background is the canonical root value.', - removal: 'Retire only after FlowChat, generated widget iframe fallback, and any persisted custom CSS no longer read the alias.', - }, - { - key: '--color-bg-surface', - canonical: '--color-bg-secondary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Surface background is an older shared role that currently resolves to the secondary background in every builtin theme.', - removal: 'Retire after component-level surface tokens replace generic surface reads.', - }, - { - key: '--color-bg-subtle', - canonical: '--element-bg-subtle', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Subtle background is an element-layer alias, not an independent app background palette.', - removal: 'Retire after callers migrate to element surface tokens.', - }, - { - key: '--color-bg-hover', - canonical: '--element-bg-hover', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Hover background historically lived under color-bg but now belongs to the element interaction layer.', - removal: 'Retire after hover callers move to element or component interaction tokens.', - }, - { - key: '--color-bg-elevated-hover', - canonical: '--element-bg-hover', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Elevated hover resolves to the same element hover role and does not represent a separate theme primitive today.', - removal: 'Retire after elevated surfaces expose component-specific hover tokens.', - }, - { - key: '--color-bg-base', - canonical: '--color-bg-primary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Base background is a historical alias for the primary application background.', - removal: 'Retire after legacy layout selectors stop reading base background.', - }, - { - key: '--color-surface-elevated', - canonical: '--element-bg-elevated', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Elevated surface is implemented by the element elevated layer, not a separate color family.', - removal: 'Retire after elevated component tokens cover all consumers.', - }, - { - key: '--color-surface-hover', - canonical: '--element-bg-hover', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Surface hover is a compatibility alias for the element hover layer.', - removal: 'Retire after component selectors migrate to element or component hover tokens.', - }, - { - key: '--color-hover', - canonical: '--element-bg-hover', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Generic hover is retained for old selectors that predate the element layer naming.', - removal: 'Retire after all callers use role-specific hover tokens.', - }, - { - key: '--bg-primary', - canonical: '--color-bg-primary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short background aliases are kept for historical component and external widget compatibility.', - removal: 'Retire after source and generated widget iframe fallback stop requiring short background aliases.', - }, - { - key: '--bg-secondary', - canonical: '--color-bg-secondary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short background aliases are kept for historical component and external widget compatibility.', - removal: 'Retire after source and generated widget iframe fallback stop requiring short background aliases.', - }, - { - key: '--bg-tertiary', - canonical: '--color-bg-tertiary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short background aliases are kept for historical component and external widget compatibility.', - removal: 'Retire after source and generated widget iframe fallback stop requiring short background aliases.', - }, - { - key: '--bg-elevated', - canonical: '--color-bg-elevated', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short elevated background alias preserves older selector and generated widget iframe fallback compatibility.', - removal: 'Retire after elevated component tokens replace the short alias.', - }, - { - key: '--bg-hover', - canonical: '--element-bg-hover', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short hover background alias maps to the canonical element hover layer.', - removal: 'Retire after all hover consumers use element or component tokens.', - }, - { - key: '--secondary-bg', - canonical: '--color-bg-secondary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Legacy secondary background alias remains for older CSS modules and generated widget compatibility.', - removal: 'Retire after workspace and legacy CSS callers migrate to color-bg-secondary.', - }, - { - key: '--background-primary', - canonical: '--color-bg-primary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Background primary alias preserves older naming used by app and embedded surfaces.', - removal: 'Retire after all background-* callers migrate to color-bg-* names.', - }, - { - key: '--background-secondary', - canonical: '--color-bg-secondary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Background secondary alias preserves older naming used by app and embedded surfaces.', - removal: 'Retire after all background-* callers migrate to color-bg-* names.', - }, - { - key: '--background-tertiary', - canonical: '--color-bg-tertiary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Background tertiary alias preserves older naming used by app and embedded surfaces.', - removal: 'Retire after all background-* callers migrate to color-bg-* names.', - }, - { - key: '--color-background-secondary', - canonical: '--color-bg-secondary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Color-background secondary exists only as a historical spelling variant.', - removal: 'Retire after callers use color-bg-secondary.', - }, - { - key: '--color-background-tertiary', - canonical: '--color-bg-tertiary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Color-background tertiary exists only as a historical spelling variant.', - removal: 'Retire after callers use color-bg-tertiary.', - }, - { - key: '--color-text-tertiary', - canonical: '--color-text-muted', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Tertiary text resolves to muted text in the current theme model and should not imply a fourth text ramp.', - removal: 'Retire after consumers choose either muted text or a component-specific subdued text role.', - }, - { - key: '--text-primary', - canonical: '--color-text-primary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short text aliases are retained for legacy CSS and generated widget iframe fallback compatibility.', - removal: 'Retire after all text-* consumers move to color-text-* names.', - }, - { - key: '--text-secondary', - canonical: '--color-text-secondary', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short text aliases are retained for legacy CSS and generated widget iframe fallback compatibility.', - removal: 'Retire after all text-* consumers move to color-text-* names.', - }, - { - key: '--text-tertiary', - canonical: '--color-text-muted', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short tertiary text alias maps to muted text and should not become an independent text scale.', - removal: 'Retire after consumers migrate to muted text or component-specific subdued text tokens.', - }, - { - key: '--text-muted', - canonical: '--color-text-muted', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short muted text alias is kept for legacy CSS and generated widget iframe fallback compatibility.', - removal: 'Retire after all text-* consumers move to color-text-* names.', - }, - { - key: '--text-disabled', - canonical: '--color-text-disabled', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short disabled text alias is kept for legacy CSS and generated widget iframe fallback compatibility.', - removal: 'Retire after all text-* consumers move to color-text-* names.', - }, - { - key: '--color-overlay-white-03', - canonical: '--color-overlay-white-04', - owner: 'src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts', - reason: 'The retired 03 overlay spelling is kept only as generated widget iframe fallback compatibility for historical content.', - removal: 'Retire after generated widget content no longer relies on the old 03 overlay spelling.', - }, - { - key: '--color-primary', - canonical: '--color-accent-500', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary currently means the active accent midpoint; it is kept as compatibility for older primary-button and focus selectors.', - removal: 'Retire only after primary action tokens are componentized and widget iframe fallback no longer requires this key.', - }, - { - key: '--color-primary-rgb', - canonical: '--color-accent-500-rgb', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary RGB channels are historical accent channels used for alpha composition; the canonical name follows the accent scale.', - removal: 'Retire after alpha-composition callers and generated widget iframe fallback stop requiring primary-rgb.', - }, - { - key: '--color-primary-hover', - canonical: '--color-accent-600', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary hover is the active accent hover stop in all builtin themes.', - removal: 'Retire after callers use accent hover or component action tokens.', - }, - { - key: '--color-accent', - canonical: '--color-accent-500', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Generic accent alias remains for legacy selectors that predate numeric accent scale usage.', - removal: 'Retire after callers use explicit accent scale stops or component tokens.', - }, - { - key: '--color-accent-primary', - canonical: '--color-accent-500', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Accent-primary is a historical spelling of the active accent midpoint.', - removal: 'Retire after generated widget iframe fallback and source callers stop reading it.', - }, - { - key: '--accent-primary', - canonical: '--color-accent-500', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Accent-primary short alias is kept for older CSS modules and generated widget iframe fallback compatibility.', - removal: 'Retire after callers use color-accent-500 or component action tokens.', - }, - { - key: '--accent-primary-hover', - canonical: '--color-accent-600', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Accent-primary hover short alias resolves to the canonical accent hover stop.', - removal: 'Retire after callers use color-accent-600 or component action tokens.', - }, - { - key: '--color-primary-400', - canonical: '--color-accent-400', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary scale aliases mirror accent scale stops for historical primary naming.', - removal: 'Retire after primary-* scale reads migrate to color-accent-*.', - }, - { - key: '--color-primary-500', - canonical: '--color-accent-500', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary scale aliases mirror accent scale stops for historical primary naming.', - removal: 'Retire after primary-* scale reads migrate to color-accent-*.', - }, - { - key: '--color-primary-alpha', - canonical: '--color-accent-100', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary alpha is a compatibility alias for the faint accent surface.', - removal: 'Retire after callers use color-accent-100 or component-specific accent backgrounds.', - }, - { - key: '--color-primary-bg', - canonical: '--color-accent-100', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary background is a compatibility alias for the faint accent surface.', - removal: 'Retire after callers use color-accent-100 or component-specific accent backgrounds.', - }, - { - key: '--color-primary-bg-subtle', - canonical: '--color-accent-50', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary subtle background is a compatibility alias for the faintest accent surface.', - removal: 'Retire after callers use color-accent-50 or component-specific accent backgrounds.', - }, - { - key: '--color-accent-alpha', - canonical: '--color-accent-100', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Accent alpha is a compatibility name for the faint accent surface stop.', - removal: 'Retire after callers use explicit accent scale stops.', - }, - { - key: '--color-success-100', - canonical: '--color-success-bg', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Semantic numeric aliases mirror background and foreground roles for older components.', - removal: 'Retire after callers use semantic role names instead of numeric status stops.', - }, - { - key: '--color-success-500', - canonical: '--color-success', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Semantic numeric aliases mirror background and foreground roles for older components.', - removal: 'Retire after callers use semantic role names instead of numeric status stops.', - }, - { - key: '--color-warning-100', - canonical: '--color-warning-bg', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Semantic numeric aliases mirror background and foreground roles for older components.', - removal: 'Retire after callers use semantic role names instead of numeric status stops.', - }, - { - key: '--color-warning-500', - canonical: '--color-warning', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Semantic numeric aliases mirror background and foreground roles for older components.', - removal: 'Retire after callers use semantic role names instead of numeric status stops.', - }, - { - key: '--color-warning-700', - canonical: '--color-warning', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Warning 700 currently resolves to warning foreground and is not a separate warning ramp stop.', - removal: 'Retire after warning state roles use semantic names or a real multi-stop warning palette exists.', - }, - { - key: '--color-semantic-error', - canonical: '--color-error', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Semantic error is a historical spelling of the canonical error foreground.', - removal: 'Retire after callers use color-error.', - }, - { - key: '--color-danger', - canonical: '--color-error', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Danger action color currently shares the error palette but stays named to avoid silently changing destructive-action semantics.', - removal: 'Retire only after destructive actions have a separate component action token or explicitly choose color-error.', - }, - { - key: '--color-danger-500', - canonical: '--color-error', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Danger numeric foreground currently maps to the canonical error foreground.', - removal: 'Retire after destructive action callers use role names rather than numeric danger stops.', - }, - { - key: '--color-danger-text', - canonical: '--color-error', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Danger text currently maps to error foreground while preserving destructive-action intent at call sites.', - removal: 'Retire only after destructive text call sites explicitly migrate to error or action tokens.', - }, - { - key: '--color-danger-bg', - canonical: '--color-error-bg', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Danger background currently maps to error background while preserving destructive-action intent at call sites.', - removal: 'Retire only after destructive surfaces explicitly migrate to error or action tokens.', - }, - { - key: '--color-danger-border', - canonical: '--color-error-border', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Danger border currently maps to error border while preserving destructive-action intent at call sites.', - removal: 'Retire only after destructive surfaces explicitly migrate to error or action tokens.', - }, - { - key: '--color-danger-hover', - canonical: '--color-error', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Danger hover currently maps to error foreground while preserving destructive-action intent at call sites.', - removal: 'Retire only after destructive hover states move to component action tokens.', - }, - { - key: '--border-color', - canonical: '--border-subtle', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Generic border color is retained for older selectors and maps to the subtle border role.', - removal: 'Retire after callers use explicit border-subtle or component border tokens.', - }, - { - key: '--border-hover', - canonical: '--border-medium', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Border hover maps to the medium border role in the current interaction scale.', - removal: 'Retire after hover states use component interaction border tokens.', - }, - { - key: '--border-muted', - canonical: '--border-subtle', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Muted border is a historical spelling for subtle border.', - removal: 'Retire after callers use border-subtle.', - }, - { - key: '--border-primary', - canonical: '--border-base', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Primary border means base border in the current contract and is kept for legacy selector compatibility.', - removal: 'Retire after callers use border-base or component border tokens.', - }, - { - key: '--color-border', - canonical: '--border-base', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Color-border is a legacy spelling of the canonical base border token.', - removal: 'Retire after callers use border-base.', - }, - { - key: '--color-border-primary', - canonical: '--border-base', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Color-border-primary is a legacy spelling of the canonical base border token.', - removal: 'Retire after callers use border-base.', - }, - { - key: '--color-border-subtle', - canonical: '--border-subtle', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Color-border-subtle is a legacy spelling of the canonical subtle border token.', - removal: 'Retire after callers use border-subtle.', - }, - { - key: '--element-bg', - canonical: '--element-bg-base', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Generic element background remains as compatibility for older element surface selectors.', - removal: 'Retire after callers use explicit element-bg-base or component surface tokens.', - }, - { - key: '--motion-normal', - canonical: '--motion-base', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Motion-normal is a historical alias for the base motion duration.', - removal: 'Retire after callers use motion-base.', - }, - { - key: '--font-sans', - canonical: '--font-family-sans', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short font aliases are kept for historical CSS and generated widget iframe fallback compatibility.', - removal: 'Retire after callers use font-family-* names and widget iframe fallback no longer requires short aliases.', - }, - { - key: '--font-mono', - canonical: '--font-family-mono', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Short font aliases are kept for historical CSS and generated widget iframe fallback compatibility.', - removal: 'Retire after callers use font-family-* names and widget iframe fallback no longer requires short aliases.', - }, - { - key: '--markdown-font-mono', - canonical: '--font-family-mono', - owner: 'src/web-ui/src/component-library/styles/tokens.scss; src/web-ui/src/infrastructure/theme/core/ThemeService.ts', - reason: 'Markdown monospace remains a named alias so markdown surfaces can diverge later without breaking callers.', - removal: 'Retire only if markdown and app monospace are confirmed to remain the same contract.', - }, - { - key: '--tool-compact-summary-font', - canonical: '--font-family-sans', - owner: 'src/web-ui/src/component-library/styles/tokens.scss', - reason: 'Tool compact summaries currently use the global sans font but keep a surface alias for future tool-card typography changes.', - removal: 'Retire only if tool card typography will not diverge from global sans.', - }, -]; +export const TOKEN_COMPATIBILITY_ALIAS_CONTRACTS = []; export const TOKEN_COMPATIBILITY_ALIAS_FAMILY_CONTRACTS = [ { diff --git a/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss b/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss index 3226c41aca..64c17d70a1 100644 --- a/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss +++ b/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss @@ -110,7 +110,7 @@ padding: 7px 10px; border: 1px solid var(--color-overlay-slate-10); border-radius: 10px 10px 2px 10px; - background: var(--color-overlay-white-92); + background: var(--color-overlay-white-95); color: var(--bitfun-agent-companion-bubble-fg); backdrop-filter: blur(10px); cursor: pointer; diff --git a/src/web-ui/src/component-library/components/FlowChatCards/ReadFileCard/ReadFileCard.scss b/src/web-ui/src/component-library/components/FlowChatCards/ReadFileCard/ReadFileCard.scss index b3a02f9a97..94521edf49 100644 --- a/src/web-ui/src/component-library/components/FlowChatCards/ReadFileCard/ReadFileCard.scss +++ b/src/web-ui/src/component-library/components/FlowChatCards/ReadFileCard/ReadFileCard.scss @@ -28,7 +28,7 @@ align-items: center; gap: 8px; padding: 6px 0; - border-bottom: 1px solid var(--tool-card-border-subtle); + border-bottom: 1px solid var(--border-subtle); &:last-child { border-bottom: none; @@ -38,13 +38,13 @@ &__label { font-size: 12px; font-weight: 600; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); min-width: 80px; } &__value { font-size: 13px; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); flex: 1; overflow: hidden; text-overflow: ellipsis; @@ -56,7 +56,7 @@ margin-top: 12px; padding: 12px; background: var(--color-bg-primary); - border: 1px solid var(--tool-card-border-subtle); + border: 1px solid var(--border-subtle); border-radius: 4px; } @@ -66,7 +66,7 @@ gap: 6px; font-size: 12px; font-weight: 600; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); margin-bottom: 8px; svg { @@ -76,7 +76,7 @@ &__preview-content { font-size: 13px; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); } diff --git a/src/web-ui/src/component-library/components/FlowChatCards/SnapshotCard/SnapshotCard.tsx b/src/web-ui/src/component-library/components/FlowChatCards/SnapshotCard/SnapshotCard.tsx index dcc0fd54ff..0d7fe39aa0 100644 --- a/src/web-ui/src/component-library/components/FlowChatCards/SnapshotCard/SnapshotCard.tsx +++ b/src/web-ui/src/component-library/components/FlowChatCards/SnapshotCard/SnapshotCard.tsx @@ -46,7 +46,7 @@ export const SnapshotCard: React.FC = ({ const getOperationInfo = () => { const operationMap = { - 'write': { name: t('flowChatCards.snapshotCard.writeFile'), color: 'var(--tool-card-write-color)' }, + 'write': { name: t('flowChatCards.snapshotCard.writeFile'), color: 'var(--color-success)' }, 'edit': { name: t('flowChatCards.snapshotCard.editFile'), color: 'var(--color-warning)' }, 'delete': { name: t('flowChatCards.snapshotCard.deleteFile'), color: 'var(--color-error)' }, 'multi-edit': { name: t('flowChatCards.snapshotCard.multiEdit'), color: 'var(--color-warning)' } diff --git a/src/web-ui/src/component-library/components/FlowChatCards/TaskCard/TaskCard.scss b/src/web-ui/src/component-library/components/FlowChatCards/TaskCard/TaskCard.scss index 90acfd851c..6ac610cf3a 100644 --- a/src/web-ui/src/component-library/components/FlowChatCards/TaskCard/TaskCard.scss +++ b/src/web-ui/src/component-library/components/FlowChatCards/TaskCard/TaskCard.scss @@ -47,7 +47,7 @@ &__action { font-weight: 500; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); } @@ -69,7 +69,7 @@ align-items: flex-start; gap: 8px; padding: 6px 0; - border-bottom: 1px solid var(--tool-card-border-subtle); + border-bottom: 1px solid var(--border-subtle); &:last-child { border-bottom: none; @@ -79,13 +79,13 @@ &__label { font-size: 12px; font-weight: 600; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); min-width: 80px; } &__value { font-size: 13px; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); flex: 1; } @@ -101,9 +101,9 @@ width: 100%; padding: 10px 12px; background: var(--color-bg-primary); - border: 1px solid var(--tool-card-border-subtle); + border: 1px solid var(--border-subtle); border-radius: 4px; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); font-size: 13px; font-weight: 500; cursor: pointer; @@ -124,7 +124,7 @@ } svg:last-child { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } } @@ -132,7 +132,7 @@ margin-top: 8px; padding: 12px; background: var(--color-bg-primary); - border: 1px solid var(--tool-card-border-subtle); + border: 1px solid var(--border-subtle); border-radius: 4px; max-height: 400px; overflow-y: auto; @@ -143,7 +143,7 @@ margin: 0; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 12px; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); line-height: 1.5; white-space: pre-wrap; word-break: break-word; diff --git a/src/web-ui/src/component-library/components/FlowChatCards/TodoCard/TodoCard.scss b/src/web-ui/src/component-library/components/FlowChatCards/TodoCard/TodoCard.scss index 70b7f72fe0..b9dba8e553 100644 --- a/src/web-ui/src/component-library/components/FlowChatCards/TodoCard/TodoCard.scss +++ b/src/web-ui/src/component-library/components/FlowChatCards/TodoCard/TodoCard.scss @@ -20,7 +20,7 @@ transition: all 0.2s ease; &:hover { - background: var(--tool-card-bg-hover); + background: var(--element-bg-hover); border-color: var(--border-medium); } @@ -35,12 +35,12 @@ &__action { font-weight: 500; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); } &__stats { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); font-size: 12px; margin-left: auto; } @@ -66,7 +66,7 @@ &__stat-value { font-size: 16px; font-weight: 600; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); &--in-progress { color: var(--color-warning); @@ -79,7 +79,7 @@ &__stat-label { font-size: 11px; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } @@ -127,7 +127,7 @@ margin-top: 1px; &--pending { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } &--in-progress { @@ -140,7 +140,7 @@ } &--cancelled { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } } @@ -148,7 +148,7 @@ flex: 1; min-width: 0; font-size: 12px; - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); line-height: 1.4; } @@ -157,7 +157,7 @@ margin-top: 8px; padding: 16px; text-align: center; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); font-size: 12px; background: var(--color-bg-scene); border: 1px solid var(--border-base); diff --git a/src/web-ui/src/component-library/components/FlowChatCards/WebSearchCard/WebSearchCard.scss b/src/web-ui/src/component-library/components/FlowChatCards/WebSearchCard/WebSearchCard.scss index 53c8df976d..0660053793 100644 --- a/src/web-ui/src/component-library/components/FlowChatCards/WebSearchCard/WebSearchCard.scss +++ b/src/web-ui/src/component-library/components/FlowChatCards/WebSearchCard/WebSearchCard.scss @@ -14,7 +14,7 @@ gap: 8px; padding: 8px 12px; background: var(--color-bg-primary); - border: 1px solid var(--tool-card-border-subtle); + border: 1px solid var(--border-subtle); border-radius: 6px; font-size: 13px; transition: all 0.2s ease; @@ -35,7 +35,7 @@ &__action { font-weight: 500; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); } @@ -49,7 +49,7 @@ &__count { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); font-size: 12px; margin-left: auto; } @@ -64,7 +64,7 @@ align-items: flex-start; gap: 8px; padding: 6px 0; - border-bottom: 1px solid var(--tool-card-border-subtle); + border-bottom: 1px solid var(--border-subtle); &:last-child { border-bottom: none; @@ -74,13 +74,13 @@ &__label { font-size: 12px; font-weight: 600; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); min-width: 80px; } &__value { font-size: 13px; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); flex: 1; overflow: hidden; text-overflow: ellipsis; @@ -99,9 +99,9 @@ width: 100%; padding: 10px 12px; background: var(--color-bg-primary); - border: 1px solid var(--tool-card-border-subtle); + border: 1px solid var(--border-subtle); border-radius: 4px; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); font-size: 13px; font-weight: 500; cursor: pointer; @@ -122,7 +122,7 @@ } svg:last-child { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } } @@ -130,7 +130,7 @@ margin-top: 8px; padding: 8px; background: var(--color-bg-primary); - border: 1px solid var(--tool-card-border-subtle); + border: 1px solid var(--border-subtle); border-radius: 4px; max-height: 400px; overflow-y: auto; @@ -144,7 +144,7 @@ &:not(:last-child) { margin-bottom: 8px; - border-bottom: 1px solid var(--tool-card-border-subtle); + border-bottom: 1px solid var(--border-subtle); } &:hover { @@ -182,14 +182,14 @@ &__result-snippet { font-size: 13px; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); line-height: 1.5; margin-bottom: 6px; } &__result-url { font-size: 11px; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; overflow: hidden; text-overflow: ellipsis; @@ -201,10 +201,10 @@ margin-top: 12px; padding: 24px; text-align: center; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); font-size: 13px; background: var(--color-bg-primary); - border: 1px solid var(--tool-card-border-subtle); + border: 1px solid var(--border-subtle); border-radius: 4px; } diff --git a/src/web-ui/src/component-library/components/FlowChatCards/index.ts b/src/web-ui/src/component-library/components/FlowChatCards/index.ts index 338da3b96d..70089a83dd 100644 --- a/src/web-ui/src/component-library/components/FlowChatCards/index.ts +++ b/src/web-ui/src/component-library/components/FlowChatCards/index.ts @@ -60,7 +60,7 @@ export const FLOWCHAT_CARD_CONFIGS: Record = { resultDisplayType: 'summary', description: i18nService.t('components:flowChatCards.toolConfig.write.description'), displayMode: 'standard', - primaryColor: 'var(--tool-card-write-color)' + primaryColor: 'var(--color-success)' }, 'Edit': { toolName: 'Edit', diff --git a/src/web-ui/src/component-library/components/Markdown/Markdown.scss b/src/web-ui/src/component-library/components/Markdown/Markdown.scss index d710e8d5cf..212011151e 100644 --- a/src/web-ui/src/component-library/components/Markdown/Markdown.scss +++ b/src/web-ui/src/component-library/components/Markdown/Markdown.scss @@ -32,8 +32,6 @@ --markdown-table-header-bg: var(--flowchat-md-table-th-bg); --markdown-table-row-stripe: var(--flowchat-md-table-row-stripe); --markdown-table-row-hover: var(--flowchat-md-table-hover); - --markdown-link-color: var(--flowchat-link-color); - --markdown-link-hover-color: var(--flowchat-link-hover-color); width: 100%; max-width: 100%; @@ -356,7 +354,7 @@ } .markdown-renderer .copy-button.copy-success { - color: var(--markdown-copy-success-color); + color: var(--color-success); } .markdown-renderer pre:has(.code-block-wrapper), @@ -533,7 +531,7 @@ padding: 0; border: none; background: none; - color: var(--markdown-link-color); + color: var(--flowchat-link-color); cursor: pointer; font: inherit; text-decoration: underline; @@ -545,7 +543,7 @@ .markdown-renderer .file-link:hover, .markdown-renderer .visualization-link:hover, .markdown-renderer .tab-link:hover { - color: var(--markdown-link-hover-color); + color: var(--flowchat-link-hover-color); text-decoration-thickness: 1.5px; } @@ -595,8 +593,6 @@ :root[data-theme-type="light"] .markdown-renderer, [data-theme-type="light"] .markdown-renderer, .light .markdown-renderer { - --markdown-link-color: var(--flowchat-link-color); - --markdown-link-hover-color: var(--flowchat-link-hover-color); --markdown-code-bg-elevated: var(--markdown-code-bg-light); --markdown-inline-code-bg: rgba(var(--color-static-black-rgb), 0.05); --markdown-inline-code-border: var(--color-overlay-black-12); @@ -626,11 +622,11 @@ .copy-button:hover { background: var(--color-overlay-slate-06); - color: var(--markdown-link-color); + color: var(--flowchat-link-color); } .copy-button.copy-success { - color: var(--markdown-copy-success-color-light); + color: var(--color-success); } blockquote, diff --git a/src/web-ui/src/component-library/components/Switch/Switch.scss b/src/web-ui/src/component-library/components/Switch/Switch.scss index 70336ec9c7..853f3ce20f 100644 --- a/src/web-ui/src/component-library/components/Switch/Switch.scss +++ b/src/web-ui/src/component-library/components/Switch/Switch.scss @@ -143,7 +143,7 @@ } &__input:checked + &__track &__loading { - color: var(--color-overlay-white-92); + color: var(--color-overlay-white-95); } &__input:active:not(:disabled) + &__track { diff --git a/src/web-ui/src/component-library/preview/flowchat-cards-preview.css b/src/web-ui/src/component-library/preview/flowchat-cards-preview.css index 4acc3ef5a6..5b7466d547 100644 --- a/src/web-ui/src/component-library/preview/flowchat-cards-preview.css +++ b/src/web-ui/src/component-library/preview/flowchat-cards-preview.css @@ -2,20 +2,8 @@ * FlowChat Cards preview styles */ -/* Default tool card CSS variables defined at :root */ +/* Preview-only tool card typography override. */ :root { - --tool-card-bg-primary: var(--color-overlay-white-08); - --tool-card-bg-secondary: var(--color-overlay-white-06); - --tool-card-bg-hover: var(--color-overlay-white-10); - --tool-card-bg-elevated: rgba(var(--color-static-white-rgb), 0.14); - - --tool-card-border: var(--color-overlay-white-08); - --tool-card-border-subtle: var(--color-overlay-white-04); - - --tool-card-text-primary: var(--color-text-primary); - --tool-card-text-secondary: var(--preview-text-muted); - --tool-card-text-muted: var(--git-graph-text-muted); - --tool-card-font-mono: 'SF Mono', 'Monaco', 'Cascadia Code', 'Cascadia Mono', Consolas, 'Inconsolata', 'Fira Code', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', 'Lucida Console', 'Courier New', monospace; } diff --git a/src/web-ui/src/component-library/styles/tokens.scss b/src/web-ui/src/component-library/styles/tokens.scss index b935f92efb..53219683a4 100644 --- a/src/web-ui/src/component-library/styles/tokens.scss +++ b/src/web-ui/src/component-library/styles/tokens.scss @@ -807,7 +807,6 @@ $badge-info-text: $color-info; --color-overlay-white-70: color-mix(in srgb, var(--color-static-white) 70%, transparent); --color-overlay-white-80: color-mix(in srgb, var(--color-static-white) 80%, transparent); --color-overlay-white-90: color-mix(in srgb, var(--color-static-white) 90%, transparent); - --color-overlay-white-92: color-mix(in srgb, var(--color-static-white) 92%, transparent); --color-overlay-white-95: color-mix(in srgb, var(--color-static-white) 95%, transparent); --color-overlay-black-06: #{$overlay-black-06}; --color-overlay-black-08: #{$overlay-black-08}; @@ -836,39 +835,13 @@ $badge-info-text: $color-info; --color-bg-workbench: #{$color-bg-workbench}; --bitfun-startup-bg: var(--color-bg-primary); --color-bg-scene: #{$color-bg-scene}; - --color-bg-flowchat: var(--color-bg-scene); /* alias → use --color-bg-scene */ --color-bg-tooltip: #{$color-bg-tooltip}; - --color-bg-surface: var(--color-bg-secondary); - --color-bg-subtle: var(--element-bg-subtle); - --color-bg-hover: var(--element-bg-hover); - --color-bg-elevated-hover: var(--element-bg-hover); - --color-bg-base: var(--color-bg-primary); - --color-surface-elevated: var(--element-bg-elevated); - --color-surface-hover: var(--element-bg-hover); - --color-hover: var(--element-bg-hover); - --bg-primary: var(--color-bg-primary); - --bg-secondary: var(--color-bg-secondary); - --bg-tertiary: var(--color-bg-tertiary); - --bg-elevated: var(--color-bg-elevated); - --bg-hover: var(--element-bg-hover); --panel-bg: #{$panel-bg}; - --secondary-bg: var(--color-bg-secondary); - --background-primary: var(--color-bg-primary); - --background-secondary: var(--color-bg-secondary); - --background-tertiary: var(--color-bg-tertiary); - --color-background-secondary: var(--color-bg-secondary); - --color-background-tertiary: var(--color-bg-tertiary); --color-text-primary: #{$color-text-primary}; --color-text-secondary: #{$color-text-secondary}; - --color-text-tertiary: var(--color-text-muted); --color-text-muted: #{$color-text-muted}; --color-text-disabled: #{$color-text-disabled}; - --text-primary: var(--color-text-primary); - --text-secondary: var(--color-text-secondary); - --text-tertiary: var(--color-text-muted); - --text-muted: var(--color-text-muted); - --text-disabled: var(--color-text-disabled); --element-bg-subtle: #{$element-bg-subtle}; --element-bg-soft: #{$element-bg-soft}; @@ -876,7 +849,6 @@ $badge-info-text: $color-info; --element-bg-medium: #{$element-bg-medium}; --element-bg-strong: #{$element-bg-strong}; --element-bg-elevated: #{$element-bg-elevated}; - --element-bg: var(--element-bg-base); --element-bg-hover: var(--element-bg-medium); --color-accent-50: #{$color-accent-50}; @@ -889,19 +861,6 @@ $badge-info-text: $color-info; --color-accent-600: #{$color-accent-600}; --color-accent-700: #{$color-accent-700}; --color-accent-800: #{$color-accent-800}; - --color-primary: var(--color-accent-500); - --color-primary-hover: var(--color-accent-600); - --color-accent: var(--color-accent-500); - --color-accent-primary: var(--color-accent-500); - --accent-primary: var(--color-accent-500); - --accent-primary-hover: var(--color-accent-600); - --color-primary-rgb: var(--color-accent-500-rgb); - --color-primary-400: var(--color-accent-400); - --color-primary-500: var(--color-accent-500); - --color-primary-alpha: var(--color-accent-100); - --color-primary-bg: var(--color-accent-100); - --color-primary-bg-subtle: var(--color-accent-50); - --color-accent-alpha: var(--color-accent-100); --color-purple-50: #{$color-purple-50}; --color-purple-100: #{$color-purple-100}; @@ -921,24 +880,12 @@ $badge-info-text: $color-info; --color-success: #{$color-success}; --color-success-bg: #{$color-success-bg}; --color-success-border: #{$color-success-border}; - --color-success-100: var(--color-success-bg); - --color-success-500: var(--color-success); --color-warning: #{$color-warning}; --color-warning-bg: #{$color-warning-bg}; --color-warning-border: #{$color-warning-border}; - --color-warning-100: var(--color-warning-bg); - --color-warning-500: var(--color-warning); - --color-warning-700: var(--color-warning); --color-error: #{$color-error}; --color-error-bg: #{$color-error-bg}; --color-error-border: #{$color-error-border}; - --color-semantic-error: var(--color-error); - --color-danger: var(--color-error); - --color-danger-500: var(--color-error); - --color-danger-text: var(--color-error); - --color-danger-bg: var(--color-error-bg); - --color-danger-border: var(--color-error-border); - --color-danger-hover: var(--color-error); --color-info: #{$color-info}; --color-info-bg: #{$color-info-bg}; --color-info-border: #{$color-info-border}; @@ -947,31 +894,21 @@ $badge-info-text: $color-info; --git-color-branch-bg: #{$git-color-branch-bg}; --git-color-branch-bg-hover: #{$git-color-branch-bg-hover}; --git-color-changes: #{$git-color-changes}; - --git-color-changes-bg: #{$git-color-changes-bg}; --git-color-staged: #{$git-color-staged}; --git-color-staged-bg: #{$git-color-staged-bg}; --git-color-staged-bg-hover: #{$git-color-staged-bg-hover}; --git-color-staged-border: #{$git-color-staged-border}; - --git-color-added: #{$git-color-added}; - --git-color-added-bg: #{$git-color-added-bg}; --git-color-added-bg-hover: #{$git-color-added-bg-hover}; --git-color-deleted: #{$git-color-deleted}; --git-color-deleted-bg: #{$git-color-deleted-bg}; --git-color-deleted-bg-hover: #{$git-color-deleted-bg-hover}; --border-subtle: #{$border-subtle}; - --border-color: var(--border-subtle); --border-base: #{$border-base}; --border-medium: #{$border-medium}; --border-strong: #{$border-strong}; --border-prominent: #{$border-prominent}; - --border-muted: var(--border-subtle); - --border-primary: var(--border-base); - --border-hover: #{$border-hover}; --border-focus: #{$border-focus}; - --color-border: var(--border-base); - --color-border-primary: var(--border-base); - --color-border-subtle: var(--border-subtle); --border-accent-soft: #{$border-accent-soft}; --border-accent-subtle: #{$border-accent-subtle}; --border-accent: #{$border-accent}; @@ -1010,38 +947,14 @@ $badge-info-text: $color-info; --btn-primary-active-border: transparent; --btn-primary-active-shadow: none; --btn-primary-active-transform: none; - --btn-ghost-bg: transparent; --btn-ghost-color: var(--color-text-muted); - --btn-ghost-border: transparent; - --btn-ghost-shadow: none; --btn-ghost-hover-bg: var(--color-overlay-white-06); --btn-ghost-hover-color: #b8b8b8; --btn-ghost-hover-border: transparent; - --btn-ghost-hover-shadow: none; - --btn-ghost-hover-transform: none; - --btn-ghost-active-bg: var(--color-overlay-white-08); - --btn-ghost-active-color: var(--color-text-primary); - --btn-ghost-active-border: transparent; - --btn-ghost-active-shadow: none; - --btn-ghost-active-transform: none; --window-control-close-hover-color: var(--color-error); - --tool-card-bg-primary: var(--element-bg-base); - --tool-card-bg-secondary: var(--element-bg-soft); - --tool-card-bg-hover: var(--element-bg-medium); - --tool-card-bg-elevated: var(--element-bg-elevated); - --tool-card-border: var(--border-base); - --tool-card-border-subtle: var(--border-subtle); - --tool-card-text-primary: var(--color-text-primary); - --tool-card-text-secondary: var(--color-text-secondary); - --tool-card-text-muted: var(--color-text-muted); - --tool-card-write-color: #{$git-color-staged}; --tool-card-confirmation-ring: #{$git-color-changes-bg-hover}; - --tool-card-positive-color: #{$git-color-staged}; - --tool-card-positive-bg-hover: #{$git-color-staged-bg-hover}; - --tool-card-positive-icon: color-mix(in srgb, var(--tool-card-positive-color) 90%, transparent); --tool-card-git-color: #{$color-static-orange}; --tool-card-terminal-color: #{$color-static-emerald}; - --tool-card-risk-high-color: #{$color-static-orange}; --tool-card-mcp-color: var(--color-indigo-500); --flowchat-inline-tag-blue: #{$color-accent-500}; --flowchat-inline-tag-purple: var(--color-purple-soft); @@ -1056,8 +969,6 @@ $badge-info-text: $color-info; --generative-widget-card-error-rgb: 253, 164, 175; --generative-widget-card-error-soft-rgb: 252, 165, 165; --tool-command-empty-rgb: 224, 108, 117; - --flowchat-copy-success-color: var(--color-success); - --markdown-copy-success-color: var(--color-success); --git-graph-lane-blue: #{$color-accent-500}; --git-graph-lane-green: #{$color-static-emerald}; --git-graph-lane-cyan: var(--color-cyan-500); @@ -1178,14 +1089,12 @@ $badge-info-text: $color-info; --markdown-inline-code-hover-text-light-rgb: 31, 35, 40; --markdown-code-bg-light-rgb: 246, 248, 250; --markdown-code-toolbar-bg-light-rgb: 238, 241, 246; - --markdown-copy-success-light-rgb: 26, 127, 55; --markdown-code-gutter-light-rgb: 153, 153, 153; --markdown-code-gutter-dark-rgb: 102, 102, 102; --markdown-inline-code-text-light: rgb(var(--markdown-inline-code-text-light-rgb)); --markdown-inline-code-hover-text-light: rgb(var(--markdown-inline-code-hover-text-light-rgb)); --markdown-code-bg-light: rgb(var(--markdown-code-bg-light-rgb)); --markdown-code-toolbar-bg-light: rgb(var(--markdown-code-toolbar-bg-light-rgb)); - --markdown-copy-success-color-light: rgb(var(--markdown-copy-success-light-rgb)); --preview-bg-rgb: 26, 26, 26; --preview-panel-bg-rgb: 10, 10, 10; --preview-border-rgb: 26, 26, 26; @@ -1334,7 +1243,6 @@ $badge-info-text: $color-info; --motion-instant: #{$motion-instant}; --motion-fast: #{$motion-fast}; - --motion-normal: var(--motion-base); --motion-base: #{$motion-base}; --motion-slow: #{$motion-slow}; --smooth-height-collapse-duration: var(--motion-slow); @@ -1345,22 +1253,15 @@ $badge-info-text: $color-info; --font-family-sans: #{$font-family-sans}; --font-family-mono: #{$font-family-mono}; - --font-sans: var(--font-family-sans); - --font-mono: var(--font-family-mono); - --markdown-font-mono: var(--font-family-mono); --tool-card-font-mono: 'SF Mono', 'Monaco', 'Cascadia Code', 'Cascadia Mono', Consolas, 'Inconsolata', 'Fira Code', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', 'Lucida Console', 'Courier New', monospace; - --tool-compact-summary-font: var(--font-family-sans); --input-bg: var(--element-bg-base); --input-bg-hover: var(--element-bg-medium); - --input-bg-focus: var(--element-bg-soft); - --input-bg-disabled: var(--element-bg-subtle); --input-border: var(--border-base); --input-border-hover: var(--border-medium); --input-border-focus: var(--color-accent-400); - --input-border-error: var(--color-error); --input-text: var(--color-text-primary); --font-weight-normal: #{$font-weight-normal}; --font-weight-medium: #{$font-weight-medium}; diff --git a/src/web-ui/src/flow_chat/components/CopyOutputButton.css b/src/web-ui/src/flow_chat/components/CopyOutputButton.css index c98f5a6a05..3f39985dad 100644 --- a/src/web-ui/src/flow_chat/components/CopyOutputButton.css +++ b/src/web-ui/src/flow_chat/components/CopyOutputButton.css @@ -54,18 +54,18 @@ } .copy-output-button.copied { - background: color-mix(in srgb, var(--flowchat-copy-success-color) 90%, transparent); - border-color: color-mix(in srgb, var(--flowchat-copy-success-color) 70%, transparent); + background: color-mix(in srgb, var(--color-success) 90%, transparent); + border-color: color-mix(in srgb, var(--color-success) 70%, transparent); color: var(--color-static-white); opacity: 1; pointer-events: auto; } .copy-output-button.copied:hover { - background: color-mix(in srgb, var(--flowchat-copy-success-color) 90%, transparent); - border-color: color-mix(in srgb, var(--flowchat-copy-success-color) 70%, transparent); + background: color-mix(in srgb, var(--color-success) 90%, transparent); + border-color: color-mix(in srgb, var(--color-success) 70%, transparent); transform: translateY(0); - box-shadow: 0 2px 8px color-mix(in srgb, var(--flowchat-copy-success-color) 40%, transparent); + box-shadow: 0 2px 8px color-mix(in srgb, var(--color-success) 40%, transparent); } .button-icon { diff --git a/src/web-ui/src/flow_chat/components/FlowTextBlock.scss b/src/web-ui/src/flow_chat/components/FlowTextBlock.scss index d75f73e645..dfc1c3388d 100644 --- a/src/web-ui/src/flow_chat/components/FlowTextBlock.scss +++ b/src/web-ui/src/flow_chat/components/FlowTextBlock.scss @@ -108,7 +108,7 @@ } .markdown-renderer .code-block-wrapper .copy-button.copy-success:hover { - color: var(--markdown-copy-success-color); + color: var(--color-success); transform: scale(1.08); } @@ -162,7 +162,7 @@ :root[data-theme-type='light'] .flow-text-block .markdown-renderer .code-block-wrapper .copy-button.copy-success:hover, [data-theme-type='light'] .flow-text-block .markdown-renderer .code-block-wrapper .copy-button.copy-success:hover, .light .flow-text-block .markdown-renderer .code-block-wrapper .copy-button.copy-success:hover { - color: var(--markdown-copy-success-color-light); + color: var(--color-success); } :root[data-theme='light'] .flow-text-block .markdown-renderer .inline-code, diff --git a/src/web-ui/src/flow_chat/components/FlowToolCardErrorBoundary.tsx b/src/web-ui/src/flow_chat/components/FlowToolCardErrorBoundary.tsx index dd5c08c1db..a06b0f35fd 100644 --- a/src/web-ui/src/flow_chat/components/FlowToolCardErrorBoundary.tsx +++ b/src/web-ui/src/flow_chat/components/FlowToolCardErrorBoundary.tsx @@ -86,7 +86,7 @@ function RenderFallback({ >
= { - low: 'var(--tool-card-positive-color)', + low: 'var(--color-success)', medium: 'var(--color-warning)', - high: 'var(--tool-card-risk-high-color)', + high: 'color-mix(in srgb, var(--color-warning) 55%, var(--color-error))', critical: 'var(--color-error)', }; diff --git a/src/web-ui/src/flow_chat/tool-cards/ContextCompressionDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/ContextCompressionDisplay.scss index 9270f22b58..493c21fa4c 100644 --- a/src/web-ui/src/flow_chat/tool-cards/ContextCompressionDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/ContextCompressionDisplay.scss @@ -18,7 +18,7 @@ .token-stat { font-weight: 600; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); white-space: nowrap; } @@ -31,19 +31,19 @@ font-weight: 600; flex-shrink: 0; white-space: nowrap; - color: color-mix(in srgb, var(--tool-card-positive-color) 75%, transparent); - background: color-mix(in srgb, var(--tool-card-positive-color) 8%, transparent); + color: color-mix(in srgb, var(--color-success) 75%, transparent); + background: color-mix(in srgb, var(--color-success) 8%, transparent); } .processing-text { - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); font-style: italic; } } .compression-meta { font-size: var(--flowchat-font-size-sm); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); white-space: nowrap; } @@ -59,14 +59,14 @@ } .icon-completed { - color: var(--tool-card-positive-icon); + color: color-mix(in srgb, var(--color-success) 90%, transparent); } .compression-detail-note { padding: var(--flowchat-card-expanded-pad-y) var(--size-gap-4); font-size: var(--flowchat-font-size-sm); line-height: var(--flowchat-support-line-height); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); background: rgba(var(--context-compression-note-rgb), 0.08); border-top: 1px solid rgba(var(--context-compression-note-rgb), 0.18); } diff --git a/src/web-ui/src/flow_chat/tool-cards/CreatePlanDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/CreatePlanDisplay.scss index e9820bce3c..72bb40557b 100644 --- a/src/web-ui/src/flow_chat/tool-cards/CreatePlanDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/CreatePlanDisplay.scss @@ -35,7 +35,7 @@ .create-plan-header { justify-content: flex-start; gap: var(--flowchat-inline-gap); - color: var(--tool-card-text-muted); + color: var(--color-text-muted); font-size: var(--tool-card-action-font-size); border-bottom: none; } @@ -85,7 +85,7 @@ &:hover { .file-name { - color: var(--tool-card-text-primary); + color: var(--color-text-primary); } } } @@ -116,7 +116,7 @@ .file-name { font-size: var(--flowchat-font-size-xs); font-family: var(--tool-card-font-mono); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); min-width: 0; overflow: hidden; text-overflow: ellipsis; @@ -139,7 +139,7 @@ border: 1px solid transparent; border-radius: 6px; background: transparent; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); cursor: pointer; transition: all 0.15s ease; @@ -177,14 +177,14 @@ margin: 0 0 var(--flowchat-inline-gap) 0; font-size: var(--flowchat-font-size-base); font-weight: 600; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); line-height: var(--flowchat-support-line-height); } .plan-overview { margin: 0; font-size: var(--tool-card-action-font-size); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); line-height: var(--flowchat-support-line-height); } @@ -200,7 +200,7 @@ border: 1px solid transparent; border-radius: 6px; background: transparent; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); cursor: pointer; transition: all 0.15s ease; @@ -249,7 +249,7 @@ height: 12px; &--pending { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); opacity: 0.5; } @@ -270,12 +270,12 @@ .todo-content { font-size: var(--tool-card-action-font-size); line-height: var(--flowchat-support-line-height); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); } &.status-pending { .todo-content { - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); } } @@ -283,7 +283,7 @@ background: var(--glass-blue-subtle); .todo-content { - color: var(--tool-card-text-primary); + color: var(--color-text-primary); font-weight: 500; } } @@ -291,7 +291,7 @@ &.status-completed { .todo-content { text-decoration: line-through; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); opacity: 0.6; } } @@ -301,7 +301,7 @@ .todo-content { text-decoration: line-through; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } } } @@ -310,12 +310,12 @@ /* Plan params still streaming: footer actions read as disabled / grayed. */ &--plan-generating { .create-plan-footer { - background: color-mix(in srgb, var(--color-bg-scene) 92%, var(--tool-card-text-muted)); + background: color-mix(in srgb, var(--color-bg-scene) 92%, var(--color-text-muted)); .build-btn:disabled { opacity: 1; cursor: not-allowed; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); background: color-mix(in srgb, var(--element-bg-soft) 80%, transparent); border: 1px solid var(--border-base); box-shadow: none; @@ -326,7 +326,7 @@ } svg { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } } } @@ -351,7 +351,7 @@ padding: var(--flowchat-control-pad-y) var(--flowchat-control-pad-x); font-size: var(--tool-card-action-font-size); font-weight: 500; - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); background: transparent; border: none; border-radius: 4px; @@ -359,7 +359,7 @@ transition: all 0.15s ease; &:hover:not(:disabled) { - color: var(--tool-card-text-primary); + color: var(--color-text-primary); background: var(--color-overlay-white-08); } diff --git a/src/web-ui/src/flow_chat/tool-cards/DefaultToolCard.scss b/src/web-ui/src/flow_chat/tool-cards/DefaultToolCard.scss index bd5f77b380..9fcb53f8df 100644 --- a/src/web-ui/src/flow_chat/tool-cards/DefaultToolCard.scss +++ b/src/web-ui/src/flow_chat/tool-cards/DefaultToolCard.scss @@ -166,14 +166,14 @@ } .default-tool-card__button--confirm { - background: color-mix(in srgb, var(--tool-card-positive-color) 12%, transparent); - border-color: color-mix(in srgb, var(--tool-card-positive-color) 26%, transparent); + background: color-mix(in srgb, var(--color-success) 12%, transparent); + border-color: color-mix(in srgb, var(--color-success) 26%, transparent); color: rgb(var(--default-tool-card-confirm-text-rgb)); } .default-tool-card__button--confirm:hover:not(:disabled) { - background: color-mix(in srgb, var(--tool-card-positive-color) 18%, transparent); - border-color: color-mix(in srgb, var(--tool-card-positive-color) 40%, transparent); + background: color-mix(in srgb, var(--color-success) 18%, transparent); + border-color: color-mix(in srgb, var(--color-success) 40%, transparent); } .default-tool-card__button--reject { diff --git a/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.scss b/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.scss index a114c74a35..25bef29437 100644 --- a/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.scss +++ b/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.scss @@ -161,7 +161,7 @@ color 0.15s ease; &:hover:not(.file-op-diff-pill--disabled) { - background-color: var(--tool-card-bg-hover); + background-color: var(--element-bg-hover); color: var(--color-text-primary); } @@ -220,7 +220,7 @@ } &:hover::before { - background-color: var(--tool-card-bg-hover); + background-color: var(--element-bg-hover); } &:hover { diff --git a/src/web-ui/src/flow_chat/tool-cards/GitToolDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/GitToolDisplay.scss index 3b5eea276b..22a1476d04 100644 --- a/src/web-ui/src/flow_chat/tool-cards/GitToolDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/GitToolDisplay.scss @@ -44,7 +44,7 @@ font-size: var(--flowchat-font-size-sm); line-height: var(--flowchat-support-line-height); font-weight: normal; - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); } } @@ -110,7 +110,7 @@ .git-output-summary-inline { font-size: var(--flowchat-font-size-xs); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -148,7 +148,7 @@ .output-summary { font-size: var(--flowchat-font-size-xs); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -306,7 +306,7 @@ align-items: center; gap: var(--flowchat-inline-gap); font-size: var(--flowchat-font-size-xs); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); .error-separator { color: rgba(var(--color-static-white-rgb), 0.3); @@ -321,7 +321,7 @@ } .icon-completed { - color: var(--tool-card-positive-icon); + color: color-mix(in srgb, var(--color-success) 90%, transparent); } .icon-warning { @@ -329,7 +329,7 @@ } .git-confirm-btn { - color: color-mix(in srgb, var(--tool-card-positive-color) 80%, transparent); + color: color-mix(in srgb, var(--color-success) 80%, transparent); &:hover:not(:disabled) { background: var(--git-color-staged-bg-hover); @@ -337,7 +337,7 @@ } &:active:not(:disabled) { - background: color-mix(in srgb, var(--tool-card-positive-color) 25%, transparent); + background: color-mix(in srgb, var(--color-success) 25%, transparent); } } diff --git a/src/web-ui/src/flow_chat/tool-cards/MCPToolDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/MCPToolDisplay.scss index bcf7ebfaa8..a8e4241dfa 100644 --- a/src/web-ui/src/flow_chat/tool-cards/MCPToolDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/MCPToolDisplay.scss @@ -15,7 +15,7 @@ .tool-name { font-weight: 600; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -24,7 +24,7 @@ .content-summary { font-size: var(--tool-card-action-font-size); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); white-space: nowrap; } @@ -36,11 +36,11 @@ border: none; background: transparent; cursor: pointer; - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); transition: color 0.2s ease; &:hover { - color: var(--tool-card-text-primary); + color: var(--color-text-primary); } svg { @@ -76,7 +76,7 @@ border-radius: 4px; font-size: var(--flowchat-font-size-sm); line-height: var(--flowchat-support-line-height); - color: var(--tool-card-text-primary); + color: var(--color-text-primary); white-space: pre-wrap; word-wrap: break-word; } @@ -93,7 +93,7 @@ justify-content: center; gap: var(--flowchat-control-gap); padding: var(--size-gap-6); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); font-size: var(--flowchat-font-size-sm); } @@ -106,7 +106,7 @@ min-height: 320px; border: 1px solid color-mix(in srgb, var(--color-purple-500) 20%, transparent); border-radius: 6px; - background: var(--tool-card-bg-primary); + background: var(--element-bg-base); } .resource-content { @@ -117,7 +117,7 @@ .resource-name { font-weight: 600; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); margin-bottom: var(--flowchat-inline-gap); } @@ -130,7 +130,7 @@ .resource-description { font-size: var(--flowchat-font-size-sm); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); } } } @@ -146,7 +146,7 @@ } .icon-completed { - color: var(--tool-card-positive-icon); + color: color-mix(in srgb, var(--color-success) 90%, transparent); } .mcp-action-buttons { @@ -177,15 +177,15 @@ } .mcp-confirm-btn { - color: color-mix(in srgb, var(--tool-card-positive-color) 80%, transparent); + color: color-mix(in srgb, var(--color-success) 80%, transparent); &:hover:not(:disabled) { - background: var(--tool-card-positive-bg-hover); - color: var(--tool-card-positive-color); + background: var(--color-success-bg); + color: var(--color-success); } &:active:not(:disabled) { - background: color-mix(in srgb, var(--tool-card-positive-color) 25%, transparent); + background: color-mix(in srgb, var(--color-success) 25%, transparent); } } diff --git a/src/web-ui/src/flow_chat/tool-cards/MiniAppToolDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/MiniAppToolDisplay.scss index c02af6bcc9..b0aa5cf50c 100644 --- a/src/web-ui/src/flow_chat/tool-cards/MiniAppToolDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/MiniAppToolDisplay.scss @@ -51,7 +51,7 @@ font-family: var(--tool-card-font-mono); font-size: var(--tool-card-action-font-size); font-weight: 500; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -61,7 +61,7 @@ .output-summary { font-size: var(--flowchat-font-size-xs); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -143,7 +143,7 @@ border-radius: 4px; border: 1px solid var(--border-subtle); background: transparent; - color: var(--tool-card-text-primary); + color: var(--color-text-primary); font-size: var(--flowchat-font-size-sm); font-weight: 500; cursor: pointer; @@ -175,7 +175,7 @@ align-items: center; gap: var(--flowchat-control-gap); font-size: var(--flowchat-font-size-sm); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); .error-operation { color: color-mix(in srgb, var(--color-error) 70%, transparent); diff --git a/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss index ee9c06b87d..2d122266f0 100644 --- a/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss @@ -96,7 +96,7 @@ /* Match FlowChat body text (sans); mono stack + generic monospace yields Songti-like CJK on Windows. */ font-family: var(--font-family-sans); word-break: break-word; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); padding: var(--flowchat-card-expanded-pad-y) var(--flowchat-card-expanded-pad-x); background: transparent; border: none; @@ -204,12 +204,12 @@ font-family: var(--font-family-sans); font-size: var(--flowchat-font-size-base); line-height: var(--flowchat-text-line-height); - color: var(--tool-card-text-muted); + color: var(--color-text-muted); animation: none; h1, h2, h3, h4, h5, h6, strong { - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); } p, @@ -218,20 +218,20 @@ li, blockquote, .custom-blockquote { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } em { - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } hr { - background: color-mix(in srgb, var(--tool-card-text-muted) 25%, transparent); + background: color-mix(in srgb, var(--color-text-muted) 25%, transparent); } blockquote, .custom-blockquote { - border-left: 2px solid color-mix(in srgb, var(--tool-card-text-muted) 35%, transparent); + border-left: 2px solid color-mix(in srgb, var(--color-text-muted) 35%, transparent); background: transparent; } @@ -240,13 +240,13 @@ border-radius: 0; background: transparent; border: none; - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); } .code-block-wrapper { border-radius: 6px; - border: 1px solid color-mix(in srgb, var(--tool-card-text-muted) 18%, transparent); - background: color-mix(in srgb, var(--tool-card-text-muted) 8%, transparent); + border: 1px solid color-mix(in srgb, var(--color-text-muted) 18%, transparent); + background: color-mix(in srgb, var(--color-text-muted) 8%, transparent); } .code-block-toolbar { @@ -286,7 +286,7 @@ } .code-block-wrapper .copy-button.copy-success:hover { - color: var(--markdown-copy-success-color); + color: var(--color-success); transform: scale(0.93); } @@ -296,16 +296,16 @@ .table-wrapper th, .table-wrapper td { - color: var(--tool-card-text-muted); - border-color: color-mix(in srgb, var(--tool-card-text-muted) 22%, transparent); + color: var(--color-text-muted); + border-color: color-mix(in srgb, var(--color-text-muted) 22%, transparent); } .table-wrapper th { - color: var(--tool-card-text-secondary); - background: color-mix(in srgb, var(--tool-card-text-muted) 6%, transparent); + color: var(--color-text-secondary); + background: color-mix(in srgb, var(--color-text-muted) 6%, transparent); } .table-wrapper tbody tr:nth-child(2n) { - background: color-mix(in srgb, var(--tool-card-text-muted) 5%, transparent); + background: color-mix(in srgb, var(--color-text-muted) 5%, transparent); } } diff --git a/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.scss index 790171a060..9f1358ddc0 100644 --- a/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.scss @@ -87,7 +87,7 @@ } &:hover::before { - background-color: var(--tool-card-bg-hover); + background-color: var(--element-bg-hover); } &:hover .task-header-rail__visual { @@ -331,7 +331,7 @@ display: flex; align-items: center; gap: var(--flowchat-inline-gap); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); font-size: var(--flowchat-font-size-xs); font-weight: 600; letter-spacing: 0.01em; @@ -359,7 +359,7 @@ margin-bottom: var(--flowchat-card-gap); font-size: var(--flowchat-font-size-xs); font-weight: 600; - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.5px; opacity: 0.8; @@ -398,7 +398,7 @@ align-items: center; gap: var(--flowchat-inline-gap); font-size: var(--flowchat-font-size-xs); - color: var(--tool-card-text-muted); + color: var(--color-text-muted); } .error-task, @@ -407,7 +407,7 @@ } .error-separator { - color: var(--tool-card-border); + color: var(--border-base); } .tool-actions { @@ -442,12 +442,12 @@ } .reject-button { - background: var(--tool-card-bg-secondary); - color: var(--tool-card-text-primary); - border: 1px solid var(--tool-card-border); + background: var(--element-bg-soft); + color: var(--color-text-primary); + border: 1px solid var(--border-base); &:hover:not(:disabled) { - background: var(--tool-card-bg-hover); + background: var(--element-bg-hover); border-color: var(--color-error); color: var(--color-error); } diff --git a/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.scss b/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.scss index 4782da30ce..34480f005a 100644 --- a/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.scss +++ b/src/web-ui/src/flow_chat/tool-cards/TerminalToolCard.scss @@ -100,7 +100,7 @@ font-family: var(--tool-card-font-mono); font-size: var(--flowchat-font-size-sm); font-weight: 500; - color: var(--tool-card-text-muted); + color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/src/web-ui/src/flow_chat/tool-cards/ToolTimeoutIndicator.scss b/src/web-ui/src/flow_chat/tool-cards/ToolTimeoutIndicator.scss index 8a92bc6e21..b719da4797 100644 --- a/src/web-ui/src/flow_chat/tool-cards/ToolTimeoutIndicator.scss +++ b/src/web-ui/src/flow_chat/tool-cards/ToolTimeoutIndicator.scss @@ -8,7 +8,7 @@ display: inline-flex; align-items: center; gap: var(--flowchat-inline-gap); - color: var(--tool-card-text-secondary); + color: var(--color-text-secondary); font-size: var(--flowchat-font-size-xs); font-weight: 600; letter-spacing: 0.01em; diff --git a/src/web-ui/src/flow_chat/tool-cards/toolCardMetadata.ts b/src/web-ui/src/flow_chat/tool-cards/toolCardMetadata.ts index 2ab437f36f..bc70ca1bbd 100644 --- a/src/web-ui/src/flow_chat/tool-cards/toolCardMetadata.ts +++ b/src/web-ui/src/flow_chat/tool-cards/toolCardMetadata.ts @@ -30,7 +30,7 @@ export const TOOL_CARD_CONFIGS: Record = { resultDisplayType: 'summary', description: 'Write or create a file', displayMode: 'standard', - primaryColor: 'var(--tool-card-write-color)' + primaryColor: 'var(--color-success)' }, 'Edit': { toolName: 'Edit', diff --git a/src/web-ui/src/infrastructure/theme/core/ThemeService.ts b/src/web-ui/src/infrastructure/theme/core/ThemeService.ts index cebc3d4dcb..16b109f426 100644 --- a/src/web-ui/src/infrastructure/theme/core/ThemeService.ts +++ b/src/web-ui/src/infrastructure/theme/core/ThemeService.ts @@ -552,24 +552,6 @@ export class ThemeService { root.style.setProperty('--color-bg-elevated', colors.background.elevated); root.style.setProperty('--color-bg-workbench', colors.background.workbench); root.style.setProperty('--color-bg-scene', colors.background.scene); - root.style.setProperty('--color-bg-flowchat', colors.background.scene); - root.style.setProperty('--color-bg-surface', colors.background.secondary); - root.style.setProperty('--color-bg-base', colors.background.primary); - root.style.setProperty('--color-bg-elevated-hover', colors.element.medium); - root.style.setProperty('--color-surface-elevated', colors.element.elevated); - root.style.setProperty('--color-surface-hover', colors.element.medium); - root.style.setProperty('--color-hover', colors.element.medium); - root.style.setProperty('--bg-primary', colors.background.primary); - root.style.setProperty('--bg-secondary', colors.background.secondary); - root.style.setProperty('--bg-tertiary', colors.background.tertiary); - root.style.setProperty('--bg-elevated', colors.background.elevated); - root.style.setProperty('--bg-hover', colors.element.medium); - root.style.setProperty('--secondary-bg', colors.background.secondary); - root.style.setProperty('--background-primary', colors.background.primary); - root.style.setProperty('--background-secondary', colors.background.secondary); - root.style.setProperty('--background-tertiary', colors.background.tertiary); - root.style.setProperty('--color-background-secondary', colors.background.secondary); - root.style.setProperty('--color-background-tertiary', colors.background.tertiary); if (colors.background.tooltip) { root.style.setProperty('--color-bg-tooltip', colors.background.tooltip); } @@ -579,14 +561,8 @@ export class ThemeService { root.style.setProperty('--color-text-primary', colors.text.primary); root.style.setProperty('--color-text-secondary', colors.text.secondary); - root.style.setProperty('--color-text-tertiary', colors.text.muted); root.style.setProperty('--color-text-muted', colors.text.muted); root.style.setProperty('--color-text-disabled', colors.text.disabled); - root.style.setProperty('--text-primary', colors.text.primary); - root.style.setProperty('--text-secondary', colors.text.secondary); - root.style.setProperty('--text-tertiary', colors.text.muted); - root.style.setProperty('--text-muted', colors.text.muted); - root.style.setProperty('--text-disabled', colors.text.disabled); Object.entries(colors.accent).forEach(([key, value]) => { @@ -594,19 +570,6 @@ export class ThemeService { }); const primaryAccent = colors.accent[500]; - const primaryHover = colors.accent[600]; - root.style.setProperty('--color-primary', primaryAccent); - root.style.setProperty('--color-primary-hover', primaryHover); - root.style.setProperty('--color-accent', primaryAccent); - root.style.setProperty('--color-accent-primary', primaryAccent); - root.style.setProperty('--accent-primary', primaryAccent); - root.style.setProperty('--accent-primary-hover', primaryHover); - root.style.setProperty('--color-primary-400', colors.accent[400]); - root.style.setProperty('--color-primary-500', primaryAccent); - root.style.setProperty('--color-primary-alpha', colors.accent[100]); - root.style.setProperty('--color-primary-bg', colors.accent[100]); - root.style.setProperty('--color-primary-bg-subtle', colors.accent[50]); - root.style.setProperty('--color-accent-alpha', colors.accent[100]); const flowChatLinkColors = theme.type === 'light' ? FLOW_CHAT_LINK_COLORS.light : FLOW_CHAT_LINK_COLORS.dark; @@ -615,7 +578,6 @@ export class ThemeService { const accentRgb = accentColorToRgbChannels(primaryAccent); if (accentRgb) { root.style.setProperty('--color-accent-500-rgb', accentRgb); - root.style.setProperty('--color-primary-rgb', accentRgb); } @@ -629,43 +591,22 @@ export class ThemeService { root.style.setProperty('--color-success', colors.semantic.success); root.style.setProperty('--color-success-bg', colors.semantic.successBg); root.style.setProperty('--color-success-border', colors.semantic.successBorder); - root.style.setProperty('--color-success-100', colors.semantic.successBg); - root.style.setProperty('--color-success-500', colors.semantic.success); root.style.setProperty('--color-warning', colors.semantic.warning); root.style.setProperty('--color-warning-bg', colors.semantic.warningBg); root.style.setProperty('--color-warning-border', colors.semantic.warningBorder); - root.style.setProperty('--color-warning-100', colors.semantic.warningBg); - root.style.setProperty('--color-warning-500', colors.semantic.warning); - root.style.setProperty('--color-warning-700', colors.semantic.warning); root.style.setProperty('--color-error', colors.semantic.error); root.style.setProperty('--color-error-bg', colors.semantic.errorBg); root.style.setProperty('--color-error-border', colors.semantic.errorBorder); - root.style.setProperty('--color-semantic-error', colors.semantic.error); - root.style.setProperty('--color-danger', colors.semantic.error); - root.style.setProperty('--color-danger-500', colors.semantic.error); - root.style.setProperty('--color-danger-text', colors.semantic.error); - root.style.setProperty('--color-danger-bg', colors.semantic.errorBg); - root.style.setProperty('--color-danger-border', colors.semantic.errorBorder); - root.style.setProperty('--color-danger-hover', colors.semantic.error); root.style.setProperty('--color-info', colors.semantic.info); root.style.setProperty('--color-info-bg', colors.semantic.infoBg); root.style.setProperty('--color-info-border', colors.semantic.infoBorder); - root.style.setProperty('--color-highlight', colors.semantic.highlight); - root.style.setProperty('--color-highlight-bg', colors.semantic.highlightBg); root.style.setProperty('--border-subtle', colors.border.subtle); - root.style.setProperty('--border-color', colors.border.subtle); root.style.setProperty('--border-base', colors.border.base); root.style.setProperty('--border-medium', colors.border.medium); - root.style.setProperty('--border-hover', colors.border.medium); root.style.setProperty('--border-strong', colors.border.strong); root.style.setProperty('--border-prominent', colors.border.prominent); - root.style.setProperty('--border-muted', colors.border.subtle); - root.style.setProperty('--border-primary', colors.border.base); - root.style.setProperty('--color-border', colors.border.base); - root.style.setProperty('--color-border-primary', colors.border.base); - root.style.setProperty('--color-border-subtle', colors.border.subtle); const sceneViewportBorder = theme.layout?.sceneViewportBorder ?? true; root.style.setProperty( @@ -679,18 +620,12 @@ export class ThemeService { root.style.setProperty('--element-bg-medium', colors.element.medium); root.style.setProperty('--element-bg-strong', colors.element.strong); root.style.setProperty('--element-bg-elevated', colors.element.elevated); - root.style.setProperty('--element-bg', colors.element.base); root.style.setProperty('--element-bg-hover', colors.element.medium); - root.style.setProperty('--color-bg-hover', colors.element.medium); - root.style.setProperty('--color-bg-subtle', colors.element.subtle); root.style.setProperty('--git-color-branch', colors.git.branch); root.style.setProperty('--git-color-branch-bg', colors.git.branchBg); root.style.setProperty('--git-color-changes', colors.git.changes); - root.style.setProperty('--git-color-changes-bg', colors.git.changesBg); - root.style.setProperty('--git-color-added', colors.git.added); - root.style.setProperty('--git-color-added-bg', colors.git.addedBg); root.style.setProperty('--git-color-deleted', colors.git.deleted); root.style.setProperty('--git-color-deleted-bg', colors.git.deletedBg); root.style.setProperty('--git-color-staged', colors.git.staged); @@ -773,7 +708,6 @@ export class ThemeService { Object.entries(motion.duration).forEach(([key, value]) => { root.style.setProperty(`--motion-${key}`, value); }); - root.style.setProperty('--motion-normal', motion.duration.base); } @@ -785,9 +719,8 @@ export class ThemeService { if (typography?.font) { - root.style.setProperty('--font-sans', typography.font.sans); - root.style.setProperty('--font-mono', typography.font.mono); - root.style.setProperty('--markdown-font-mono', typography.font.mono); + root.style.setProperty('--font-family-sans', typography.font.sans); + root.style.setProperty('--font-family-mono', typography.font.mono); } @@ -818,24 +751,6 @@ export class ThemeService { const buttonConfig = theme.components?.button; if (buttonConfig) { - root.style.setProperty('--btn-default-bg', buttonConfig.default.background); - root.style.setProperty('--btn-default-color', buttonConfig.default.color); - root.style.setProperty('--btn-default-border', buttonConfig.default.border); - root.style.setProperty('--btn-default-shadow', buttonConfig.default.shadow || 'none'); - - root.style.setProperty('--btn-default-hover-bg', buttonConfig.hover.background); - root.style.setProperty('--btn-default-hover-color', buttonConfig.hover.color); - root.style.setProperty('--btn-default-hover-border', buttonConfig.hover.border); - root.style.setProperty('--btn-default-hover-shadow', buttonConfig.hover.shadow || 'none'); - root.style.setProperty('--btn-default-hover-transform', buttonConfig.hover.transform || 'none'); - - root.style.setProperty('--btn-default-active-bg', buttonConfig.active.background); - root.style.setProperty('--btn-default-active-color', buttonConfig.active.color); - root.style.setProperty('--btn-default-active-border', buttonConfig.active.border); - root.style.setProperty('--btn-default-active-shadow', buttonConfig.active.shadow || 'none'); - root.style.setProperty('--btn-default-active-transform', buttonConfig.active.transform || 'none'); - - root.style.setProperty('--btn-primary-bg', buttonConfig.primary.default.background); root.style.setProperty('--btn-primary-color', buttonConfig.primary.default.color); root.style.setProperty('--btn-primary-border', buttonConfig.primary.default.border); @@ -854,34 +769,13 @@ export class ThemeService { root.style.setProperty('--btn-primary-active-transform', buttonConfig.primary.active.transform || 'none'); - root.style.setProperty('--btn-ghost-bg', buttonConfig.ghost.default.background); root.style.setProperty('--btn-ghost-color', buttonConfig.ghost.default.color); - root.style.setProperty('--btn-ghost-border', buttonConfig.ghost.default.border); - root.style.setProperty('--btn-ghost-shadow', buttonConfig.ghost.default.shadow || 'none'); root.style.setProperty('--btn-ghost-hover-bg', buttonConfig.ghost.hover.background); root.style.setProperty('--btn-ghost-hover-color', buttonConfig.ghost.hover.color); root.style.setProperty('--btn-ghost-hover-border', buttonConfig.ghost.hover.border); - root.style.setProperty('--btn-ghost-hover-shadow', buttonConfig.ghost.hover.shadow || 'none'); - root.style.setProperty('--btn-ghost-hover-transform', buttonConfig.ghost.hover.transform || 'none'); - - root.style.setProperty('--btn-ghost-active-bg', buttonConfig.ghost.active.background); - root.style.setProperty('--btn-ghost-active-color', buttonConfig.ghost.active.color); - root.style.setProperty('--btn-ghost-active-border', buttonConfig.ghost.active.border); - root.style.setProperty('--btn-ghost-active-shadow', buttonConfig.ghost.active.shadow || 'none'); - root.style.setProperty('--btn-ghost-active-transform', buttonConfig.ghost.active.transform || 'none'); } else { - root.style.setProperty('--btn-default-bg', colors.element.base); - root.style.setProperty('--btn-default-color', colors.text.secondary); - root.style.setProperty('--btn-default-border', colors.border.base); - root.style.setProperty('--btn-default-shadow', 'none'); - root.style.setProperty('--btn-default-hover-bg', colors.element.medium); - root.style.setProperty('--btn-default-hover-color', colors.text.primary); - root.style.setProperty('--btn-default-hover-border', colors.border.medium); - root.style.setProperty('--btn-default-hover-shadow', 'none'); - root.style.setProperty('--btn-default-hover-transform', 'none'); - const a = colors.accent; root.style.setProperty('--btn-primary-bg', a[200]); root.style.setProperty('--btn-primary-color', a[600]); @@ -897,105 +791,25 @@ export class ThemeService { root.style.setProperty('--btn-primary-active-border', 'transparent'); root.style.setProperty('--btn-primary-active-shadow', 'none'); root.style.setProperty('--btn-primary-active-transform', 'none'); - root.style.setProperty('--btn-ghost-bg', 'transparent'); root.style.setProperty('--btn-ghost-color', colors.text.muted); - root.style.setProperty('--btn-ghost-border', 'transparent'); - root.style.setProperty('--btn-ghost-shadow', 'none'); root.style.setProperty('--btn-ghost-hover-bg', colors.element.subtle); root.style.setProperty('--btn-ghost-hover-color', colors.text.primary); root.style.setProperty('--btn-ghost-hover-border', 'transparent'); - root.style.setProperty('--btn-ghost-hover-shadow', 'none'); - root.style.setProperty('--btn-ghost-hover-transform', 'none'); - root.style.setProperty('--btn-ghost-active-bg', colors.element.medium); - root.style.setProperty('--btn-ghost-active-color', colors.text.primary); - root.style.setProperty('--btn-ghost-active-border', 'transparent'); - root.style.setProperty('--btn-ghost-active-shadow', 'none'); - root.style.setProperty('--btn-ghost-active-transform', 'none'); } - const windowControlsConfig = theme.components?.windowControls; - if (windowControlsConfig) { - - root.style.setProperty('--window-control-minimize-dot', windowControlsConfig.minimize.dot); - root.style.setProperty('--window-control-minimize-dot-shadow', windowControlsConfig.minimize.dotShadow || 'none'); - root.style.setProperty('--window-control-minimize-hover-bg', windowControlsConfig.minimize.hoverBg); - root.style.setProperty('--window-control-minimize-hover-color', windowControlsConfig.minimize.hoverColor); - root.style.setProperty('--window-control-minimize-hover-border', windowControlsConfig.minimize.hoverBorder); - root.style.setProperty('--window-control-minimize-hover-shadow', windowControlsConfig.minimize.hoverShadow || 'none'); - - - root.style.setProperty('--window-control-maximize-dot', windowControlsConfig.maximize.dot); - root.style.setProperty('--window-control-maximize-dot-shadow', windowControlsConfig.maximize.dotShadow || 'none'); - root.style.setProperty('--window-control-maximize-hover-bg', windowControlsConfig.maximize.hoverBg); - root.style.setProperty('--window-control-maximize-hover-color', windowControlsConfig.maximize.hoverColor); - root.style.setProperty('--window-control-maximize-hover-border', windowControlsConfig.maximize.hoverBorder); - root.style.setProperty('--window-control-maximize-hover-shadow', windowControlsConfig.maximize.hoverShadow || 'none'); - - - root.style.setProperty('--window-control-close-dot', windowControlsConfig.close.dot); - root.style.setProperty('--window-control-close-dot-shadow', windowControlsConfig.close.dotShadow || 'none'); - root.style.setProperty('--window-control-close-hover-bg', windowControlsConfig.close.hoverBg); - root.style.setProperty('--window-control-close-hover-color', windowControlsConfig.close.hoverColor); - root.style.setProperty('--window-control-close-hover-border', windowControlsConfig.close.hoverBorder); - root.style.setProperty('--window-control-close-hover-shadow', windowControlsConfig.close.hoverShadow || 'none'); - - - root.style.setProperty('--window-control-default-color', windowControlsConfig.common.defaultColor); - root.style.setProperty('--window-control-default-dot', windowControlsConfig.common.defaultDot); - root.style.setProperty('--window-control-disabled-dot', windowControlsConfig.common.disabledDot); - root.style.setProperty('--window-control-flow-gradient', windowControlsConfig.common.flowGradient || 'none'); - } else { - - root.style.setProperty('--window-control-minimize-dot', colors.accent[400]); - root.style.setProperty('--window-control-minimize-dot-shadow', 'none'); - root.style.setProperty('--window-control-minimize-hover-bg', colors.accent[100]); - root.style.setProperty('--window-control-minimize-hover-color', colors.accent[500]); - root.style.setProperty('--window-control-minimize-hover-border', colors.accent[200]); - root.style.setProperty('--window-control-minimize-hover-shadow', 'none'); - - root.style.setProperty('--window-control-maximize-dot', colors.accent[400]); - root.style.setProperty('--window-control-maximize-dot-shadow', 'none'); - root.style.setProperty('--window-control-maximize-hover-bg', colors.accent[100]); - root.style.setProperty('--window-control-maximize-hover-color', colors.accent[500]); - root.style.setProperty('--window-control-maximize-hover-border', colors.accent[200]); - root.style.setProperty('--window-control-maximize-hover-shadow', 'none'); - - root.style.setProperty('--window-control-close-dot', colors.semantic.error); - root.style.setProperty('--window-control-close-dot-shadow', 'none'); - root.style.setProperty('--window-control-close-hover-bg', colors.semantic.errorBg); - root.style.setProperty('--window-control-close-hover-color', colors.semantic.error); - root.style.setProperty('--window-control-close-hover-border', colors.semantic.errorBorder); - root.style.setProperty('--window-control-close-hover-shadow', 'none'); - - root.style.setProperty('--window-control-default-color', colors.text.primary); - root.style.setProperty('--window-control-default-dot', colors.text.muted); - root.style.setProperty('--window-control-disabled-dot', colors.text.disabled); - root.style.setProperty('--window-control-flow-gradient', 'none'); - } + root.style.setProperty( + '--window-control-close-hover-color', + theme.components?.windowControls?.close.hoverColor ?? colors.semantic.error, + ); root.style.setProperty('--input-bg', colors.element.base); root.style.setProperty('--input-bg-hover', colors.element.medium); - root.style.setProperty('--input-bg-focus', colors.element.soft); - root.style.setProperty('--input-bg-disabled', colors.element.subtle); root.style.setProperty('--input-border', colors.border.base); root.style.setProperty('--input-border-hover', colors.border.medium); root.style.setProperty('--input-border-focus', colors.accent[400]); - root.style.setProperty('--input-border-error', colors.semantic.error); root.style.setProperty('--input-text', colors.text.primary); - root.style.setProperty( - '--input-placeholder', - 'color-mix(in srgb, var(--color-text-muted) 40%, var(--color-bg-primary))' - ); - - - root.style.setProperty('--card-bg', colors.element.base); - root.style.setProperty('--card-bg-hover', colors.element.medium); - root.style.setProperty('--card-bg-active', colors.element.elevated); - root.style.setProperty('--card-border', colors.border.base); - root.style.setProperty('--card-border-hover', colors.border.medium); - root.style.setProperty('--card-border-active', colors.accent[300]); if (theme.type === 'dark') { @@ -1045,17 +859,6 @@ export class ThemeService { root.style.setProperty('--tooltip-text', colors.text.primary); - root.style.setProperty('--tool-card-bg-primary', colors.element.base); - root.style.setProperty('--tool-card-bg-secondary', colors.element.soft); - root.style.setProperty('--tool-card-bg-hover', colors.element.medium); - root.style.setProperty('--tool-card-bg-elevated', colors.element.elevated); - root.style.setProperty('--tool-card-border', colors.border.base); - root.style.setProperty('--tool-card-border-subtle', colors.border.subtle); - root.style.setProperty('--tool-card-text-primary', colors.text.primary); - root.style.setProperty('--tool-card-text-secondary', colors.text.secondary); - root.style.setProperty('--tool-card-text-muted', colors.text.muted); - - root.setAttribute('data-theme', theme.id); root.setAttribute('data-theme-type', theme.type); diff --git a/src/web-ui/src/infrastructure/theme/presets/china-night-theme.ts b/src/web-ui/src/infrastructure/theme/presets/china-night-theme.ts index 7474c03561..48717d0ecb 100644 --- a/src/web-ui/src/infrastructure/theme/presets/china-night-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/china-night-theme.ts @@ -9,7 +9,6 @@ import { createStandardSpacing, createWindowControls, overlayBlack, - overlayWhite, rgbFromHex, rgbaFromHex, } from './shared'; @@ -24,7 +23,6 @@ const CHINA_NIGHT_GREEN_HOVER = '#7aab98'; const CHINA_NIGHT_SUCCESS = '#6bc072'; const CHINA_NIGHT_WARNING = '#f5b555'; const CHINA_NIGHT_ERROR = '#e85555'; -const CHINA_NIGHT_HIGHLIGHT = '#e6a84a'; const chinaNightBackground = (alpha: number | string) => rgbaFromHex(CHINA_NIGHT_BACKGROUND, alpha); const chinaNightText = (alpha: number | string) => rgbaFromHex(CHINA_NIGHT_TEXT_PRIMARY, alpha); @@ -107,8 +105,6 @@ export const bitfunChinaNightTheme: ThemeConfig = { infoBorder: chinaNightAccent(0.3), - highlight: CHINA_NIGHT_HIGHLIGHT, - highlightBg: rgbaFromHex(CHINA_NIGHT_HIGHLIGHT, 0.15), }, border: { @@ -196,53 +192,10 @@ export const bitfunChinaNightTheme: ThemeConfig = { components: { - windowControls: createWindowControls({ - standard: { - dot: chinaNightAccent(0.45), - dotShadow: `0 0 4px ${chinaNightAccent(0.2)}`, - hoverBg: chinaNightAccent(0.12), - hoverColor: CHINA_NIGHT_ACCENT, - hoverBorder: chinaNightAccent(0.2), - hoverShadow: `0 2px 8px ${chinaNightAccent(0.15)}, inset 0 1px 0 ${overlayWhite(0.1)}`, - }, - close: { - dot: chinaNightError(0.45), - dotShadow: `0 0 4px ${chinaNightError(0.2)}`, - hoverBg: chinaNightError(0.12), - hoverColor: CHINA_NIGHT_ERROR, - hoverBorder: chinaNightError(0.2), - hoverShadow: `0 2px 8px ${chinaNightError(0.15)}, inset 0 1px 0 ${overlayWhite(0.1)}`, - }, - common: { - defaultColor: overlayWhite(0.9), - defaultDot: overlayWhite(0.2), - disabledDot: overlayWhite(0.1), - flowGradient: `linear-gradient(90deg, transparent, ${overlayWhite(0.05)}, ${overlayWhite(0.08)}, ${overlayWhite(0.05)}, transparent)`, - }, - }), + windowControls: createWindowControls(CHINA_NIGHT_ERROR), button: { - default: { - background: chinaNightAccent(0.11), - color: '#9a9a9a', - border: 'transparent', - shadow: 'none', - }, - hover: { - background: chinaNightAccent(0.19), - color: CHINA_NIGHT_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: chinaNightAccent(0.15), - color: CHINA_NIGHT_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, primary: { @@ -271,24 +224,12 @@ export const bitfunChinaNightTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: '#9a9a9a', - border: 'transparent', - shadow: 'none', }, hover: { background: chinaNightAccent(0.13), color: CHINA_NIGHT_BUTTON_TEXT, border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: chinaNightAccent(0.11), - color: CHINA_NIGHT_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', }, }, }, @@ -324,4 +265,3 @@ export const bitfunChinaNightTheme: ThemeConfig = { }, }, }; - diff --git a/src/web-ui/src/infrastructure/theme/presets/china-style-theme.ts b/src/web-ui/src/infrastructure/theme/presets/china-style-theme.ts index c128e98461..8111c50d7a 100644 --- a/src/web-ui/src/infrastructure/theme/presets/china-style-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/china-style-theme.ts @@ -27,7 +27,6 @@ const CHINA_STYLE_ERROR = '#c8102e'; const CHINA_STYLE_BORDER = '#6a5c46'; const chinaStylePaper = (alpha: number | string) => rgbaFromHex(CHINA_STYLE_PAPER, alpha); -const chinaStyleInk = (alpha: number | string) => rgbaFromHex(CHINA_STYLE_INK, alpha); const chinaStyleBlue = (alpha: number | string) => rgbaFromHex(CHINA_STYLE_BLUE, alpha); const chinaStyleBlueHover = (alpha: number | string) => rgbaFromHex(CHINA_STYLE_BLUE_HOVER, alpha); const chinaStyleGreen = (alpha: number | string) => rgbaFromHex(CHINA_STYLE_GREEN, alpha); @@ -106,8 +105,6 @@ export const bitfunChinaStyleTheme: ThemeConfig = { info: CHINA_STYLE_BLUE, infoBg: chinaStyleBlue(0.08), infoBorder: chinaStyleBlue(0.25), - highlight: CHINA_STYLE_BLUE, - highlightBg: chinaStyleBlue(0.12), }, border: { @@ -195,53 +192,10 @@ export const bitfunChinaStyleTheme: ThemeConfig = { components: { - windowControls: createWindowControls({ - standard: { - dot: chinaStyleBlue(0.45), - dotShadow: `0 0 4px ${chinaStyleBlue(0.2)}`, - hoverBg: chinaStyleBlue(0.12), - hoverColor: CHINA_STYLE_BLUE, - hoverBorder: chinaStyleBlue(0.2), - hoverShadow: `0 2px 8px ${chinaStyleBlue(0.15)}, inset 0 1px 0 ${rgbaFromHex(STATIC_WHITE, 0.5)}`, - }, - close: { - dot: chinaStyleError(0.45), - dotShadow: `0 0 4px ${chinaStyleError(0.2)}`, - hoverBg: chinaStyleError(0.12), - hoverColor: CHINA_STYLE_ERROR, - hoverBorder: chinaStyleError(0.2), - hoverShadow: `0 2px 8px ${chinaStyleError(0.15)}, inset 0 1px 0 ${rgbaFromHex(STATIC_WHITE, 0.5)}`, - }, - common: { - defaultColor: chinaStyleInk(0.9), - defaultDot: chinaStyleBorder(0.2), - disabledDot: chinaStyleBorder(0.1), - flowGradient: `linear-gradient(90deg, transparent, ${chinaStyleBorder(0.05)}, ${chinaStyleBorder(0.08)}, ${chinaStyleBorder(0.05)}, transparent)`, - }, - }), + windowControls: createWindowControls(CHINA_STYLE_ERROR), button: { - default: { - background: chinaStyleBlue(0.09), - color: '#5a5a5a', - border: 'transparent', - shadow: 'none', - }, - hover: { - background: chinaStyleBlue(0.16), - color: CHINA_STYLE_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: chinaStyleBlue(0.12), - color: CHINA_STYLE_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, primary: { @@ -270,24 +224,12 @@ export const bitfunChinaStyleTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: '#5a5a5a', - border: 'transparent', - shadow: 'none', }, hover: { background: chinaStyleBlue(0.11), color: CHINA_STYLE_BUTTON_TEXT, border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: chinaStyleBlue(0.08), - color: CHINA_STYLE_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', }, }, }, diff --git a/src/web-ui/src/infrastructure/theme/presets/cyber-theme.ts b/src/web-ui/src/infrastructure/theme/presets/cyber-theme.ts index 396b73aae2..7bc0662845 100644 --- a/src/web-ui/src/infrastructure/theme/presets/cyber-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/cyber-theme.ts @@ -106,8 +106,6 @@ export const bitfunCyberTheme: ThemeConfig = { infoBorder: cyberAccent(0.35), - highlight: '#ffdd44', - highlightBg: 'rgba(255, 221, 68, 0.15)', }, border: { @@ -198,53 +196,10 @@ export const bitfunCyberTheme: ThemeConfig = { components: { - windowControls: createWindowControls({ - standard: { - dot: cyberAccent(0.5), - dotShadow: `0 0 6px ${cyberAccent(0.35)}`, - hoverBg: cyberAccent(0.15), - hoverColor: CYBER_ACCENT, - hoverBorder: cyberAccent(0.3), - hoverShadow: `0 0 12px ${cyberAccent(0.3)}, 0 2px 8px ${cyberAccent(0.2)}, inset 0 1px 0 ${cyberAccent(0.2)}`, - }, - close: { - dot: cyberError(0.5), - dotShadow: `0 0 6px ${cyberError(0.35)}`, - hoverBg: cyberError(0.15), - hoverColor: CYBER_ERROR, - hoverBorder: cyberError(0.3), - hoverShadow: `0 0 12px ${cyberError(0.3)}, 0 2px 8px ${cyberError(0.2)}, inset 0 1px 0 ${cyberError(0.2)}`, - }, - common: { - defaultColor: rgbaFromHex(CYBER_TEXT_PRIMARY, 0.9), - defaultDot: cyberAccent(0.2), - disabledDot: cyberAccent(0.1), - flowGradient: `linear-gradient(90deg, transparent, ${cyberAccent(0.08)}, ${cyberAccent(0.12)}, ${cyberAccent(0.08)}, transparent)`, - }, - }), + windowControls: createWindowControls(CYBER_ERROR), button: { - default: { - background: cyberAccent(0.08), - color: CYBER_TEXT_MUTED, - border: cyberAccent(0.15), - shadow: `0 0 8px ${cyberAccent(0.1)}`, - }, - hover: { - background: cyberAccent(0.14), - color: CYBER_TEXT_SECONDARY, - border: cyberAccent(0.3), - shadow: `0 0 16px ${cyberAccent(0.2)}, 0 2px 8px ${overlayBlack(0.3)}`, - transform: 'translateY(-1px)', - }, - active: { - background: cyberAccent(0.12), - color: CYBER_TEXT_SECONDARY, - border: cyberAccent(0.35), - shadow: `0 0 12px ${cyberAccent(0.15)}`, - transform: 'translateY(0)', - }, primary: { @@ -273,24 +228,12 @@ export const bitfunCyberTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: CYBER_TEXT_MUTED, - border: cyberAccent(0.2), - shadow: 'none', }, hover: { background: cyberAccent(0.1), color: CYBER_TEXT_SECONDARY, border: cyberAccent(0.35), - shadow: `0 0 12px ${cyberAccent(0.15)}`, - transform: 'translateY(-1px)', - }, - active: { - background: cyberAccent(0.08), - color: CYBER_TEXT_SECONDARY, - border: cyberAccent(0.3), - shadow: `0 0 8px ${cyberAccent(0.1)}`, - transform: 'translateY(0)', }, }, }, diff --git a/src/web-ui/src/infrastructure/theme/presets/dark-theme.ts b/src/web-ui/src/infrastructure/theme/presets/dark-theme.ts index 61bcd99a19..35d6943656 100644 --- a/src/web-ui/src/infrastructure/theme/presets/dark-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/dark-theme.ts @@ -109,8 +109,6 @@ export const bitfunDarkTheme: ThemeConfig = { infoBorder: overlayWhite(0.24), - highlight: '#a8a8a8', - highlightBg: overlayWhite(0.1), }, border: createDarkNeutralBorder(), @@ -187,53 +185,10 @@ export const bitfunDarkTheme: ThemeConfig = { components: { - windowControls: createWindowControls({ - standard: { - dot: overlayWhite(0.38), - dotShadow: `0 0 4px ${overlayBlack(0.35)}`, - hoverBg: overlayWhite(0.1), - hoverColor: '#e4e4e4', - hoverBorder: overlayWhite(0.16), - hoverShadow: `0 2px 8px ${overlayBlack(0.25)}, inset 0 1px 0 ${overlayWhite(0.08)}`, - }, - close: { - dot: darkError(0.45), - dotShadow: `0 0 4px ${darkError(0.2)}`, - hoverBg: darkError(0.12), - hoverColor: DARK_ERROR, - hoverBorder: darkError(0.2), - hoverShadow: `0 2px 8px ${darkError(0.15)}, inset 0 1px 0 ${overlayWhite(0.1)}`, - }, - common: { - defaultColor: rgbaFromHex(DARK_TEXT_PRIMARY, 0.9), - defaultDot: overlayWhite(0.2), - disabledDot: overlayWhite(0.1), - flowGradient: `linear-gradient(90deg, transparent, ${overlayWhite(0.05)}, ${overlayWhite(0.08)}, ${overlayWhite(0.05)}, transparent)`, - }, - }), + windowControls: createWindowControls(DARK_ERROR), button: { - default: { - background: overlayWhite(0.08), - color: '#9a9a9a', - border: 'transparent', - shadow: 'none', - }, - hover: { - background: overlayWhite(0.14), - color: DARK_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: overlayWhite(0.1), - color: DARK_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, primary: { @@ -262,24 +217,12 @@ export const bitfunDarkTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: '#9a9a9a', - border: 'transparent', - shadow: 'none', }, hover: { background: overlayWhite(0.1), color: DARK_BUTTON_TEXT, border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: overlayWhite(0.07), - color: DARK_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', }, }, }, diff --git a/src/web-ui/src/infrastructure/theme/presets/light-theme.ts b/src/web-ui/src/infrastructure/theme/presets/light-theme.ts index 52008cf142..82f1793877 100644 --- a/src/web-ui/src/infrastructure/theme/presets/light-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/light-theme.ts @@ -24,7 +24,6 @@ const LIGHT_PURPLE_HOVER = '#655680'; const LIGHT_SUCCESS = '#5b9a6f'; const LIGHT_WARNING = '#c08c42'; const LIGHT_ERROR = '#c26565'; -const LIGHT_HIGHLIGHT = '#b8863a'; const lightInk = (alpha: number | string) => rgbaFromHex(LIGHT_INK, alpha); const lightAccent = (alpha: number | string) => rgbaFromHex(LIGHT_ACCENT, alpha); @@ -113,8 +112,6 @@ export const bitfunLightTheme: ThemeConfig = { infoBorder: lightAccent(0.28), - highlight: LIGHT_HIGHLIGHT, - highlightBg: rgbaFromHex(LIGHT_HIGHLIGHT, 0.12), }, @@ -207,53 +204,10 @@ export const bitfunLightTheme: ThemeConfig = { components: { - windowControls: createWindowControls({ - standard: { - dot: lightAccent(0.5), - dotShadow: `0 0 4px ${lightInk(0.12)}`, - hoverBg: lightInk(0.08), - hoverColor: LIGHT_ACCENT_HOVER, - hoverBorder: lightAccent(0.28), - hoverShadow: `0 2px 8px ${lightInk(0.08)}, inset 0 1px 0 ${rgbaFromHex(STATIC_WHITE, 0.6)}`, - }, - close: { - dot: lightError(0.55), - dotShadow: `0 0 4px ${lightError(0.2)}`, - hoverBg: lightError(0.14), - hoverColor: '#a85555', - hoverBorder: lightError(0.25), - hoverShadow: `0 2px 8px ${lightError(0.15)}, inset 0 1px 0 ${rgbaFromHex(STATIC_WHITE, 0.6)}`, - }, - common: { - defaultColor: rgbaFromHex(LIGHT_TEXT_PRIMARY, 0.95), - defaultDot: lightAccent(0.28), - disabledDot: lightAccent(0.15), - flowGradient: `linear-gradient(90deg, transparent, ${lightAccent(0.06)}, ${lightAccent(0.1)}, ${lightAccent(0.06)}, transparent)`, - }, - }), + windowControls: createWindowControls('#a85555'), button: { - default: { - background: lightInk(0.07), - color: LIGHT_ACCENT_HOVER, - border: 'transparent', - shadow: 'none', - }, - hover: { - background: lightInk(0.11), - color: LIGHT_TEXT_STRONG, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: lightInk(0.09), - color: LIGHT_TEXT_STRONG, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, primary: { @@ -282,24 +236,12 @@ export const bitfunLightTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: LIGHT_ACCENT_HOVER, - border: 'transparent', - shadow: 'none', }, hover: { background: lightInk(0.08), color: LIGHT_TEXT_STRONG, border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: lightInk(0.055), - color: LIGHT_TEXT_STRONG, - border: 'transparent', - shadow: 'none', - transform: 'none', }, }, }, @@ -347,4 +289,3 @@ export const bitfunLightTheme: ThemeConfig = { - diff --git a/src/web-ui/src/infrastructure/theme/presets/midnight-theme.ts b/src/web-ui/src/infrastructure/theme/presets/midnight-theme.ts index 956e259e14..d928bcb9a1 100644 --- a/src/web-ui/src/infrastructure/theme/presets/midnight-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/midnight-theme.ts @@ -25,7 +25,6 @@ const MIDNIGHT_SUCCESS = '#6aab73'; const MIDNIGHT_WARNING = '#e0a055'; const MIDNIGHT_ERROR = '#cc7f7a'; const MIDNIGHT_CONTROL_ERROR = '#ef4444'; -const MIDNIGHT_HIGHLIGHT = '#d4a574'; const midnightBackground = (alpha: number | string) => rgbaFromHex(MIDNIGHT_BACKGROUND, alpha); const midnightText = (alpha: number | string) => rgbaFromHex(MIDNIGHT_TEXT_PRIMARY, alpha); @@ -36,7 +35,6 @@ const midnightPurpleHover = (alpha: number | string) => rgbaFromHex(MIDNIGHT_PUR const midnightSuccess = (alpha: number | string) => rgbaFromHex(MIDNIGHT_SUCCESS, alpha); const midnightWarning = (alpha: number | string) => rgbaFromHex(MIDNIGHT_WARNING, alpha); const midnightError = (alpha: number | string) => rgbaFromHex(MIDNIGHT_ERROR, alpha); -const midnightControlError = (alpha: number | string) => rgbaFromHex(MIDNIGHT_CONTROL_ERROR, alpha); export const bitfunMidnightTheme: ThemeConfig = { @@ -109,8 +107,6 @@ export const bitfunMidnightTheme: ThemeConfig = { infoBorder: midnightAccent(0.3), - highlight: MIDNIGHT_HIGHLIGHT, - highlightBg: rgbaFromHex(MIDNIGHT_HIGHLIGHT, 0.15), }, border: { @@ -198,53 +194,10 @@ export const bitfunMidnightTheme: ThemeConfig = { components: { - windowControls: createWindowControls({ - standard: { - dot: midnightAccent(0.45), - dotShadow: `0 0 4px ${midnightAccent(0.2)}`, - hoverBg: midnightAccent(0.12), - hoverColor: MIDNIGHT_ACCENT, - hoverBorder: midnightAccent(0.2), - hoverShadow: `0 2px 8px ${midnightAccent(0.15)}, inset 0 1px 0 ${overlayWhite(0.1)}`, - }, - close: { - dot: midnightControlError(0.45), - dotShadow: `0 0 4px ${midnightControlError(0.2)}`, - hoverBg: midnightControlError(0.12), - hoverColor: MIDNIGHT_CONTROL_ERROR, - hoverBorder: midnightControlError(0.2), - hoverShadow: `0 2px 8px ${midnightControlError(0.15)}, inset 0 1px 0 ${overlayWhite(0.1)}`, - }, - common: { - defaultColor: midnightText(0.9), - defaultDot: midnightText(0.2), - disabledDot: midnightText(0.1), - flowGradient: `linear-gradient(90deg, transparent, ${midnightText(0.05)}, ${midnightText(0.08)}, ${midnightText(0.05)}, transparent)`, - }, - }), + windowControls: createWindowControls(MIDNIGHT_CONTROL_ERROR), button: { - default: { - background: midnightText(0.11), - color: '#9a9a9a', - border: 'transparent', - shadow: 'none', - }, - hover: { - background: midnightText(0.17), - color: MIDNIGHT_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: midnightText(0.14), - color: MIDNIGHT_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, primary: { @@ -273,24 +226,12 @@ export const bitfunMidnightTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: '#9a9a9a', - border: 'transparent', - shadow: 'none', }, hover: { background: midnightText(0.13), color: MIDNIGHT_BUTTON_TEXT, border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: midnightText(0.11), - color: MIDNIGHT_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', }, }, }, @@ -324,4 +265,3 @@ export const bitfunMidnightTheme: ThemeConfig = { }, }, }; - diff --git a/src/web-ui/src/infrastructure/theme/presets/shared.ts b/src/web-ui/src/infrastructure/theme/presets/shared.ts index cae3186c33..ae3743f454 100644 --- a/src/web-ui/src/infrastructure/theme/presets/shared.ts +++ b/src/web-ui/src/infrastructure/theme/presets/shared.ts @@ -8,8 +8,6 @@ import type { WindowControlsConfig, } from '../types'; -type WindowControlState = WindowControlsConfig['minimize']; - export const STATIC_BLACK = '#000000'; export const STATIC_WHITE = '#ffffff'; @@ -47,16 +45,9 @@ export function overlayWhite(alpha: number | string): string { return rgbaFromHex(STATIC_WHITE, alpha); } -export function createWindowControls(config: { - standard: WindowControlState; - close: WindowControlState; - common: WindowControlsConfig['common']; -}): WindowControlsConfig { +export function createWindowControls(closeHoverColor: WindowControlsConfig['close']['hoverColor']): WindowControlsConfig { return { - minimize: { ...config.standard }, - maximize: { ...config.standard }, - close: { ...config.close }, - common: { ...config.common }, + close: { hoverColor: closeHoverColor }, }; } diff --git a/src/web-ui/src/infrastructure/theme/presets/slate-theme.ts b/src/web-ui/src/infrastructure/theme/presets/slate-theme.ts index f7cf5f2bfa..2ae8f37553 100644 --- a/src/web-ui/src/infrastructure/theme/presets/slate-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/slate-theme.ts @@ -117,8 +117,6 @@ export const bitfunSlateTheme: ThemeConfig = { infoBorder: overlayWhite(0.2), - highlight: '#c8cdd4', - highlightBg: overlayWhite(0.1), }, border: createDarkNeutralBorder(), @@ -195,53 +193,10 @@ export const bitfunSlateTheme: ThemeConfig = { components: { - windowControls: createWindowControls({ - standard: { - dot: 'rgba(203, 213, 225, 0.42)', - dotShadow: `0 0 4px ${overlayBlack(0.35)}`, - hoverBg: overlayWhite(0.09), - hoverColor: SLATE_ACCENT_SOFT, - hoverBorder: overlayWhite(0.14), - hoverShadow: `0 2px 8px ${overlayBlack(0.22)}, inset 0 1px 0 ${overlayWhite(0.06)}`, - }, - close: { - dot: slateError(0.5), - dotShadow: `0 0 4px ${slateError(0.25)}`, - hoverBg: slateError(0.15), - hoverColor: SLATE_ERROR, - hoverBorder: slateError(0.25), - hoverShadow: `0 2px 8px ${slateError(0.18)}, inset 0 1px 0 ${overlayWhite(0.08)}`, - }, - common: { - defaultColor: 'rgba(232, 234, 236, 0.92)', - defaultDot: 'rgba(198, 202, 208, 0.48)', - disabledDot: 'rgba(168, 171, 176, 0.2)', - flowGradient: `linear-gradient(90deg, transparent, ${overlayWhite(0.05)}, ${overlayWhite(0.08)}, ${overlayWhite(0.05)}, transparent)`, - }, - }), + windowControls: createWindowControls(SLATE_ERROR), button: { - default: { - background: overlayWhite(0.08), - color: SLATE_TEXT_MUTED, - border: 'transparent', - shadow: 'none', - }, - hover: { - background: overlayWhite(0.12), - color: SLATE_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: overlayWhite(0.1), - color: SLATE_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', - }, primary: { @@ -270,24 +225,12 @@ export const bitfunSlateTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: SLATE_TEXT_MUTED, - border: 'transparent', - shadow: 'none', }, hover: { background: overlayWhite(0.08), color: SLATE_BUTTON_TEXT, border: 'transparent', - shadow: 'none', - transform: 'none', - }, - active: { - background: overlayWhite(0.06), - color: SLATE_BUTTON_TEXT, - border: 'transparent', - shadow: 'none', - transform: 'none', }, }, }, diff --git a/src/web-ui/src/infrastructure/theme/presets/themePresetOutput.test.ts b/src/web-ui/src/infrastructure/theme/presets/themePresetOutput.test.ts index 1b386ca26d..537532d079 100644 --- a/src/web-ui/src/infrastructure/theme/presets/themePresetOutput.test.ts +++ b/src/web-ui/src/infrastructure/theme/presets/themePresetOutput.test.ts @@ -67,42 +67,42 @@ describe('builtin theme preset output', () => { }))).toMatchInlineSnapshot(` [ { - "hash": "c3b6a5647f5a098c777c5e0669578a2a6a83fe399c4ffe2b16e00b532361e0a5", + "hash": "44a6f6daeceecfb0166c667b3252f5766f98de7811599079d58d485605ba5857", "id": "bitfun-light", "type": "light", }, { - "hash": "ebbb8c7b24d44430211ca1cd36e44530dc5b2f0c7b76ed372cb25afc5ad1e90b", + "hash": "7bfb47bdfd3658b51c5c3a126aff9fafba00cf83a79d6ee148cfe3cecf9095d0", "id": "bitfun-slate", "type": "dark", }, { - "hash": "545249087b734444881c33a1e6925a5b348f9e7e150a737992177278e0b67cef", + "hash": "2033449df1da52308c856bc9fa0bb006ca9416545c7b3971ee0c1700fd3954d2", "id": "bitfun-dark", "type": "dark", }, { - "hash": "5b5429e48817fcd6d5643989f959340e6766ee5a0e3edfdff88f3984c0e343e8", + "hash": "2b5f34aa379e97bbea554fa8b376d951c8927bd2d46a25e04cef39d16742ba8d", "id": "bitfun-midnight", "type": "dark", }, { - "hash": "46ac5adf2b0dd0bc633f27665e1544893eb57617c123500b2d5b543690eca1f9", + "hash": "e1b7b1ad6ef0bd0b9cb7bc5b020da2112a43367a989837d19856179a24da529d", "id": "bitfun-china-style", "type": "light", }, { - "hash": "bc760598ce85d6e4a7f22349aace0e25ab6cb401535bdd468513a141a2069e82", + "hash": "ad731afd2ca6dd1cf75d286a14ffc82bc6cc074cdadff68e1daf382e3c445f4a", "id": "bitfun-china-night", "type": "dark", }, { - "hash": "4b4597856d6c78a81c49a8f50c91d35682d8b53c27635b7097be1cb13a2c7a22", + "hash": "b37c0cb6d539a703d1b6c0eca6c374d555ff74c0de0e2ec357bd56241a848fd4", "id": "bitfun-cyber", "type": "dark", }, { - "hash": "1c391cd9207188d5edf906dabd3f23f28e07952c10f1ee9ebf30432747fc0fa0", + "hash": "beae87b070e7b95463d50f25e715d7e8268a25aa43b8c8b6775de0ff728732c6", "id": "bitfun-tokyo-night", "type": "dark", }, diff --git a/src/web-ui/src/infrastructure/theme/presets/tokyo-night-theme.ts b/src/web-ui/src/infrastructure/theme/presets/tokyo-night-theme.ts index 7fda70aaeb..e3a6a309df 100644 --- a/src/web-ui/src/infrastructure/theme/presets/tokyo-night-theme.ts +++ b/src/web-ui/src/infrastructure/theme/presets/tokyo-night-theme.ts @@ -115,8 +115,6 @@ export const bitfunTokyoNightTheme: ThemeConfig = { infoBg: tokyoInfo(0.12), infoBorder: tokyoInfo(0.35), - highlight: TOKYO_WARNING, - highlightBg: tokyoWarning(0.15), }, border: { @@ -209,55 +207,9 @@ export const bitfunTokyoNightTheme: ThemeConfig = { typography: createExpressiveTypography(), components: { - windowControls: createWindowControls({ - standard: { - dot: tokyoAccent(0.55), - dotShadow: `0 0 6px ${tokyoAccent(0.35)}`, - hoverBg: tokyoAccent(0.14), - hoverColor: TOKYO_ACCENT, - hoverBorder: tokyoAccent(0.35), - hoverShadow: - `0 0 12px ${tokyoAccent(0.28)}, 0 2px 8px ${tokyoAccent(0.15)}, inset 0 1px 0 ${tokyoAccent(0.18)}`, - }, - close: { - dot: tokyoError(0.55), - dotShadow: `0 0 6px ${tokyoError(0.35)}`, - hoverBg: tokyoError(0.14), - hoverColor: TOKYO_ERROR, - hoverBorder: tokyoError(0.35), - hoverShadow: - `0 0 12px ${tokyoError(0.28)}, 0 2px 8px ${tokyoError(0.15)}, inset 0 1px 0 ${tokyoError(0.18)}`, - }, - common: { - defaultColor: rgbaFromHex(TOKYO_TEXT_PRIMARY, 0.92), - defaultDot: tokyoAccent(0.22), - disabledDot: tokyoAccent(0.12), - flowGradient: - `linear-gradient(90deg, transparent, ${tokyoAccent(0.08)}, ${tokyoPurple(0.1)}, ${tokyoAccent(0.08)}, transparent)`, - }, - }), + windowControls: createWindowControls(TOKYO_ERROR), button: { - default: { - background: tokyoAccent(0.1), - color: TOKYO_TEXT_SECONDARY, - border: tokyoAccent(0.22), - shadow: `0 0 8px ${tokyoAccent(0.12)}`, - }, - hover: { - background: tokyoAccent(0.16), - color: TOKYO_TEXT_PRIMARY, - border: tokyoAccent(0.38), - shadow: `0 0 16px ${tokyoAccent(0.22)}, 0 2px 8px ${overlayBlack(0.35)}`, - transform: 'translateY(-1px)', - }, - active: { - background: tokyoAccent(0.13), - color: TOKYO_TEXT_PRIMARY, - border: tokyoAccent(0.42), - shadow: `0 0 12px ${tokyoAccent(0.18)}`, - transform: 'translateY(0)', - }, primary: { default: { @@ -285,24 +237,12 @@ export const bitfunTokyoNightTheme: ThemeConfig = { ghost: { default: { - background: 'transparent', color: TOKYO_TEXT_MUTED, - border: tokyoBorder(0.55), - shadow: 'none', }, hover: { background: tokyoAccent(0.1), color: TOKYO_TEXT_PRIMARY, border: tokyoAccent(0.35), - shadow: `0 0 12px ${tokyoAccent(0.15)}`, - transform: 'translateY(-1px)', - }, - active: { - background: tokyoAccent(0.08), - color: TOKYO_TEXT_SECONDARY, - border: tokyoAccent(0.3), - shadow: `0 0 8px ${tokyoAccent(0.12)}`, - transform: 'translateY(0)', }, }, }, diff --git a/src/web-ui/src/infrastructure/theme/types/index.ts b/src/web-ui/src/infrastructure/theme/types/index.ts index 1d4d0f6a51..c52ac8d905 100644 --- a/src/web-ui/src/infrastructure/theme/types/index.ts +++ b/src/web-ui/src/infrastructure/theme/types/index.ts @@ -68,10 +68,6 @@ export interface SemanticColors { info: ColorValue; infoBg: ColorValue; infoBorder: ColorValue; - - - highlight: ColorValue; - highlightBg: ColorValue; } @@ -177,33 +173,6 @@ export interface OpacityConfig { export interface ButtonConfig { - - default: { - background: ColorValue; - color: ColorValue; - border: ColorValue; - shadow?: string; - }; - - - hover: { - background: ColorValue; - color: ColorValue; - border: ColorValue; - shadow?: string; - transform?: string; - }; - - - active: { - background: ColorValue; - color: ColorValue; - border: ColorValue; - shadow?: string; - transform?: string; - }; - - primary: { default: { background: ColorValue; @@ -230,66 +199,20 @@ export interface ButtonConfig { ghost: { default: { - background: ColorValue; color: ColorValue; - border: ColorValue; - shadow?: string; }; hover: { background: ColorValue; color: ColorValue; border: ColorValue; - shadow?: string; - transform?: string; - }; - active: { - background: ColorValue; - color: ColorValue; - border: ColorValue; - shadow?: string; - transform?: string; }; }; } export interface WindowControlsConfig { - - minimize: { - dot: ColorValue; - dotShadow?: string; - hoverBg: ColorValue; - hoverColor: ColorValue; - hoverBorder: ColorValue; - hoverShadow?: string; - }; - - - maximize: { - dot: ColorValue; - dotShadow?: string; - hoverBg: ColorValue; - hoverColor: ColorValue; - hoverBorder: ColorValue; - hoverShadow?: string; - }; - - close: { - dot: ColorValue; - dotShadow?: string; - hoverBg: ColorValue; - hoverColor: ColorValue; - hoverBorder: ColorValue; - hoverShadow?: string; - }; - - - common: { - defaultColor: ColorValue; - defaultDot: ColorValue; - disabledDot: ColorValue; - flowGradient?: string; + hoverColor: ColorValue; }; } diff --git a/src/web-ui/src/tools/generative-widget/GenerativeWidgetFrame.tsx b/src/web-ui/src/tools/generative-widget/GenerativeWidgetFrame.tsx index 72ec7d9859..f9462917b2 100644 --- a/src/web-ui/src/tools/generative-widget/GenerativeWidgetFrame.tsx +++ b/src/web-ui/src/tools/generative-widget/GenerativeWidgetFrame.tsx @@ -751,7 +751,10 @@ ${createWidgetThemeCompatibilityAliasCss()} vars['--color-text-primary'] || getComputedStyle(root).getPropertyValue('--color-text-primary') || body.style.color; - body.style.fontFamily = vars['--font-family-sans'] || vars['--font-sans'] || body.style.fontFamily; + body.style.fontFamily = + vars['--font-family-sans'] || + getComputedStyle(root).getPropertyValue('--font-family-sans') || + body.style.fontFamily; } } diff --git a/src/web-ui/src/tools/generative-widget/themePayload.test.ts b/src/web-ui/src/tools/generative-widget/themePayload.test.ts index 39adee02a0..d611b82d38 100644 --- a/src/web-ui/src/tools/generative-widget/themePayload.test.ts +++ b/src/web-ui/src/tools/generative-widget/themePayload.test.ts @@ -8,7 +8,7 @@ import { } from './themePayload'; import { createWidgetThemeCompatibilityAliasCss } from './themePayloadCompatibility'; -const WIDGET_THEME_VAR_NAMES_HASH = '067642ed0dbb41e7c2c33cf18716d6ea6ce423617a7bbe0139a55170e8241d14'; +const WIDGET_THEME_VAR_NAMES_HASH = '6fa7fa2763c01ddceff5da14094ac84c1a7eae21dd3f015559fd78b3bf34b482'; const RETIRED_WIDGET_THEME_COMPAT_KEYS = [ '--background-primary', '--background-secondary', @@ -91,8 +91,29 @@ const RETIRED_WIDGET_THEME_COMPAT_KEYS = [ '--text-primary', '--text-secondary', '--text-tertiary', + '--tool-card-bg-primary', + '--tool-card-bg-secondary', + '--tool-card-bg-hover', + '--tool-card-bg-elevated', + '--tool-card-border', + '--tool-card-border-subtle', + '--tool-card-text-primary', + '--tool-card-text-secondary', + '--tool-card-text-muted', '--tool-compact-summary-font', ] as const; +const RETIRED_WIDGET_THEME_INTERNAL_KEYS = [ + '--btn-ghost-bg', + '--btn-ghost-border', + '--btn-ghost-shadow', + '--btn-ghost-hover-shadow', + '--btn-ghost-hover-transform', + '--btn-ghost-active-bg', + '--btn-ghost-active-color', + '--btn-ghost-active-border', + '--btn-ghost-active-shadow', + '--btn-ghost-active-transform', +] as const; const STATIC_WIDGET_SHELL_THEME_VARS = new Set([ '--font-family-mono', '--font-family-sans', @@ -154,7 +175,7 @@ describe('generated widget theme payload contract', () => { first: requestedNames[0], last: requestedNames[requestedNames.length - 1], }).toEqual({ - count: 176, + count: 157, hash: WIDGET_THEME_VAR_NAMES_HASH, first: '--color-bg-primary', last: '--tool-card-action-font-weight', @@ -171,6 +192,7 @@ describe('generated widget theme payload contract', () => { const { requestedNames } = readPayloadWithHostValues(); expect(requestedNames).not.toEqual(expect.arrayContaining(RETIRED_WIDGET_THEME_COMPAT_KEYS)); + expect(requestedNames).not.toEqual(expect.arrayContaining(RETIRED_WIDGET_THEME_INTERNAL_KEYS)); expect(requestedNames).toEqual( expect.arrayContaining([ '--color-accent-50', diff --git a/src/web-ui/src/tools/generative-widget/themePayload.ts b/src/web-ui/src/tools/generative-widget/themePayload.ts index 569e5cee4a..60b0376418 100644 --- a/src/web-ui/src/tools/generative-widget/themePayload.ts +++ b/src/web-ui/src/tools/generative-widget/themePayload.ts @@ -295,31 +295,12 @@ const WIDGET_THEME_VAR_GROUPS = { '--btn-primary-active-border', '--btn-primary-active-shadow', '--btn-primary-active-transform', - '--btn-ghost-bg', '--btn-ghost-color', - '--btn-ghost-border', - '--btn-ghost-shadow', '--btn-ghost-hover-bg', '--btn-ghost-hover-color', '--btn-ghost-hover-border', - '--btn-ghost-hover-shadow', - '--btn-ghost-hover-transform', - '--btn-ghost-active-bg', - '--btn-ghost-active-color', - '--btn-ghost-active-border', - '--btn-ghost-active-shadow', - '--btn-ghost-active-transform', ], toolCard: [ - '--tool-card-bg-primary', - '--tool-card-bg-secondary', - '--tool-card-bg-hover', - '--tool-card-bg-elevated', - '--tool-card-border', - '--tool-card-border-subtle', - '--tool-card-text-primary', - '--tool-card-text-secondary', - '--tool-card-text-muted', '--tool-card-header-pad-y', '--tool-card-header-pad-x', '--tool-card-header-pad-right', diff --git a/src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts b/src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts index 823e25dbeb..accb0ccf15 100644 --- a/src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts +++ b/src/web-ui/src/tools/generative-widget/themePayloadCompatibility.ts @@ -80,6 +80,15 @@ const WIDGET_THEME_COMPAT_ALIAS_FALLBACKS = { '--spacing-12': '--size-gap-12', '--spacing-16': '--size-gap-16', '--text-tertiary': '--color-text-muted', + '--tool-card-bg-primary': '--element-bg-base', + '--tool-card-bg-secondary': '--element-bg-soft', + '--tool-card-bg-hover': '--element-bg-medium', + '--tool-card-bg-elevated': '--element-bg-elevated', + '--tool-card-border': '--border-base', + '--tool-card-border-subtle': '--border-subtle', + '--tool-card-text-primary': '--color-text-primary', + '--tool-card-text-secondary': '--color-text-secondary', + '--tool-card-text-muted': '--color-text-muted', '--tool-compact-summary-font': '--font-family-sans', } as const;