目标
建立一个同时覆盖 OpenAlice 前端界面、图表和 Workspace 终端的统一外观主题系统。该系统以 Base16 为最低兼容契约,支持 Base24 的终端扩展色,允许直接导入现有 scheme,并可选调用用户 PATH 中的 Matugen 或 Hellwal 从图片生成可预览、可保存、可复现的明暗主题。
本 issue 的完成态不是“终端能换 16 个颜色”,而是同一个主题来源经过明确的语义投影后驱动应用界面、第三方图表和 xterm.js;生成器差异止于输入适配层,不扩散成三套渲染逻辑。
上下文
- Repo:
TraderAlice/OpenAlice
- 目标分支:
dev
- Baseline:
TraderAlice/OpenAlice@65fd2ec0
- 当前相关表面:
ui/src/index.css:内置浅色/深色 CSS token,同时仍存在固定颜色和组件级 dark: 分支;
ui/src/theme/:只持有 light | dark | auto,没有主题 family 身份;
ui/src/components/workspace/terminalThemeProfile.ts:终端使用两套硬编码 profile;
ui/src/components/market/KlinePanel.tsx:图表文字、网格、K 线和成交量颜色直接写在 JS 中;
src/workspaces/spawn-env.ts:PTY 对外声明 TERM=xterm-256color 和 COLORTERM=truecolor;
src/core/preferences.ts、src/webui/routes/preferences.ts:文件偏好和 HTTP 边界;
src/migrations/:OPENALICE_HOME 持久化数据的迁移机制。
本设计来自以下明确需求:
“一个兼容Base16的主题系统,允许直接导入color scheme,并且程序可选检测PATH中是否存在Matugen和hellwal,如果任意一个存在则可以调用输入图片后生成主题,要可以选择浅色还是深色,如果是Matugen额外还可以方案选择,hellwal要支持offset,matugen要把md color映射到base16而不是使用wal后端,hellwal本身生成base16” — 本次设计请求
“前端的主题呢?” — 本次设计请求
“多的十六进制颜色,以及写死的颜色,到底能不能使用base16或者base16差值” — 本次设计请求
“K线颜色是否映射可以给个开关,或者重要的颜色” — 本次设计请求
“需要先有内置主题管理,要能接受常见的主题文件导入,并且能在设置选择,而这也是如何测试base16引入后正常渲染的重点” — 本次设计补充
问题
当前应用主题、终端主题和图表颜色彼此分离。应用只在 CSS 中维护两套内置配色;终端另有两套硬编码 profile;Canvas/SVG/第三方图表和若干状态组件继续直接使用十六进制、rgba()、固定 Tailwind 色阶和 dark: 分支。用户不能导入通用 scheme,也不能从同一图片生成前端和终端一致的主题。
直接把每个固定颜色替换为某个 baseXX 也不能解决问题。Base16 是基础色板,不是完整 UI token 集;背景层级、交互状态、透明强调、图表网格和安全状态必须经过确定且可测试的语义投影。K 线涨跌、盈亏、真实交易确认和破坏性操作还需要独立的辨识度策略,不能因一张图片的主色而失去含义。
预期结果
- 用户可以先在 Settings 管理内置和已导入主题;除 Base16/Base24 scheme 外,常见 ANSI16 终端主题文件也能导入并保留原色表。
- OpenAlice 检测其有效
PATH 中的 Matugen 和 Hellwal,展示绝对路径、版本和已验证能力;缺失任一工具不影响另一工具和普通导入。
- 用户选择图片、浅色/深色和生成器后可以预览结果;Matugen 额外选择 Material scheme,Hellwal 额外设置 dark/bright offset。
- Matugen 只消费 Material Dynamic Colors/tonal palettes,明确忽略其 Wal backend 产生的
base16 子树。
- Hellwal 的 ANSI 16 色 JSON 被完整保留用于终端,同时按本文规则投影出 Base16 语义色板;不得把 pywal 形状的
color0..color15 冒充原生 Base16。
- 同一个保存主题驱动应用 CSS token、运行时图表和 xterm.js。终端默认跟随应用 family,但允许独立覆盖。
- 系统/浅色/深色模式与主题 family 正交。只有同时具备 light/dark variant 的 family 才能启用“跟随系统”;单 variant 不静默借用或反转另一 variant。
- 市场方向色和普通状态色各有“高辨识度保护色/跟随主题”策略;涨跌习惯独立支持绿涨红跌和红涨绿跌。
- 已保存主题是文件状态,具备来源、生成参数和版本化映射信息;重启、开发服务器和打包 Electron 的首帧均不闪回内置配色。
主题契约
Scheme 与 family
一个主题 variant 至少包含:
- 稳定 ID、显示名称和
light | dark variant;
- 完整
base00..base0F;
- 可选
base10..base17;
- 可选精确 ANSI 0–15 override;
- 来源、工具绝对路径、工具版本、输入图片 SHA-256、生成参数和映射版本;
- 创建时间,但不把临时图片路径作为身份。
一个主题 family 包含明确的 light variant、dark variant 或两者。导入单个 scheme 时使用文件中的 variant;旧式 flat Base16 文件没有 variant 时,导入 UI 必须要求用户明确选择,禁止通过亮度猜测。生成页允许只生成当前 variant,也允许从同一图片和同一参数生成完整 light/dark family。
应用偏好至少表达:
- active family;
system | light | dark 外观模式;
- 终端是跟随应用还是使用独立 family/variant;
- 市场颜色来源、涨跌习惯、普通状态颜色来源。
导入边界
必须接受并严格区分:
- 当前 Tinted Theming YAML:
system、name、author、variant、palette;
- 旧式 flat Base16 YAML:
scheme、author、base00..base0F;
- Base24 YAML:
system: base24 和 base00..base17。
- 上述 Base16/Base24 的等价 JSON。
- iTerm2
.itermcolors plist。
- Windows Terminal scheme JSON。
- Alacritty YAML/TOML theme。
- Kitty/Ghostty theme text。
- Xresources background/foreground/cursor/
color0..color15 色表。
Base16/Base24 输入保留原始语义槽位。iTerm2、Windows Terminal、Alacritty、Kitty/Ghostty 和 Xresources 作为 ANSI16 输入,必须完整保留 foreground/background/cursor/selection 和 ANSI0–15 override,并复用本文 Hellwal ANSI16→Base16 投影;不同文件 adapter 不得各自发明映射。格式检测以内容 schema 为权威,扩展名只选择候选 parser;多个 parser 同时匹配时必须作为 ambiguous input 失败。VS Code/JetBrains 全量 UI theme、任意 CSS theme、脚本模板和 include 不在本 issue 范围内。
颜色在输入边界解析为规范的不透明 RGB 值。缺少必需槽位、重复/未知 system、非法 variant、非法颜色、错误数量或与声明 system 不符时,导入失败并指向具体字段;不得补默认值、吞掉未知结构或从文件名推断语义。导入先进入预览,用户确认后才持久化。
Base16/Base24 到终端
Base16 的 ANSI 0–15 映射固定为:
| ANSI |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
| Base16 |
00 |
08 |
0B |
0A |
0D |
0E |
0C |
05 |
03 |
08 |
0B |
0A |
0D |
0E |
0C |
07 |
xterm.js extendedAnsi 的前六项固定为:ANSI 16=base09、17=base0F、18=base01、19=base02、20=base04、21=base06。
Base24 提供独立 bright ANSI:9=base12、10=base14、11=base13、12=base16、13=base17、14=base15;base10/base11 保留为额外背景层级。若 variant 携带精确 ANSI 0–15 override,则终端渲染优先使用该 override,Base16 仍用于前端、语义消费和导出。
终端前景、背景、光标、光标反色、选择背景和选择前景全部来自当前 variant;主题变化后现有终端实例立即更新,不能要求重建 Workspace 或丢失 PTY/scrollback。
Base16 到前端
基础投影固定为:
| 前端语义 |
Base 色 |
| 页面背景 |
base00 |
| 二级表面/侧栏 |
base01 |
| 卡片/悬浮表面 |
base02 |
| 边框/分隔线 |
base03 |
| 次要文字 |
base04 |
| 正文 |
base05 |
| 强调正文 |
base06 |
| 最高对比文字 |
base07 |
| 装饰性 danger |
base08 |
| 橙色提示 |
base09 |
| warning |
base0A |
| 装饰性 success |
base0B |
| info |
base0C |
| 主 accent/focus |
base0D |
| 次 accent |
base0E |
| 特殊/弃用提示 |
base0F |
hover、active、selection、focus ring、subtle surface、透明状态背景、图表网格和 overlay 由这些基础色确定性生成。颜色混合统一在 OKLab/OKLCH 中完成;已解析主题保存实际颜色值,不把浏览器相关的 color-mix() 字符串当持久化数据。中性背景和文字优先使用现成的 base00..base07 梯度,只有连续交互状态才做差值。
前景/背景、普通文本、交互控件和非文本图形必须满足适用的 WCAG 对比度要求。on-accent 从候选前景中选择满足要求的一项;没有候选能满足时主题不能保存为可用主题,错误必须指出失败的 token 对。
所有仍在运行路径中的固定 Tailwind 色阶、十六进制、rgba() 和组件级 dark: 分支都要分类迁移为:基础 token、派生 token、受保护语义色,或有明确理由的允许项。允许项仅包括内置主题源数据、测试 fixture/期望值、媒体内容、透明/物理阴影基元以及本文定义的受保护颜色;不能用 allowlist 隐藏未迁移组件。
图表与市场颜色
Canvas、SVG 和第三方图表必须消费同一 resolved theme,并在主题切换时更新现有实例。图表背景=base00,网格来自 base01/base02/base03,轴文字=base04,轴边框=base03,交互强调=base0D。
市场颜色设置包含两个正交字段:
- 来源:
protected | theme;
- 习惯:
green-up-red-down | red-up-green-down。
protected 使用内置、分别适配 light/dark 且满足图形对比要求的方向色;theme 使用 base0B 与 base08,再按涨跌习惯决定 positive/negative 绑定。K 线实体、边框、影线、成交量、盈亏数字和市场方向提示必须共用该绑定,不得各自写颜色。背景、网格和坐标无论选择哪种来源都跟随主题。
普通运行状态另有 protected | theme 策略,控制 success/warning/danger/info。不可逆删除、权限拒绝、真实交易确认、broker 写入失败和风控阻止始终使用受保护的危险语义;主题可以调整其表面亮度和透明度,但不能改变危险含义。任何关键状态都不能只靠颜色表达。
图片生成契约
能力检测
检测 OpenAlice 实际有效 PATH 中的 matugen 和 hellwal,解析为绝对可执行路径并报告版本和能力。检测结果不是单一布尔值。设置页可手动刷新;工具不存在、版本无法识别或缺少所需 flag 时显示明确状态,不影响普通主题导入和另一生成器。
执行时使用已解析的绝对路径和 argv,不经 shell,不执行用户模板,不写 wallpaper/终端配置。图片只进入受控的本地工作目录;完成、失败或取消后清理。持久化只记录 digest 和生成参数,除非用户明确选择保留原图。
Matugen
支持 light | dark,并展示已安装版本实际支持的 scheme。当前目标集合为:
scheme-content
scheme-expressive
scheme-fidelity
scheme-fruit-salad
scheme-monochrome
scheme-neutral
scheme-rainbow
scheme-tonal-spot
scheme-vibrant
scheme-smart
调用必须使用 dry-run JSON 输出。解析 colors/palettes,忽略 Matugen JSON 的 base16 子树,不使用 --base16-backend,不消费 Wal 提取结果。
Material tonal palette 到 Base16 的 v1 映射固定如下:
| Base |
Light |
Dark |
| 00 |
neutral 98 |
neutral 10 |
| 01 |
neutral 95 |
neutral 15 |
| 02 |
neutral 90 |
neutral 20 |
| 03 |
neutral_variant 60 |
neutral_variant 60 |
| 04 |
neutral_variant 40 |
neutral_variant 70 |
| 05 |
neutral 20 |
neutral 90 |
| 06 |
neutral 10 |
neutral 95 |
| 07 |
neutral 0 |
neutral 100 |
强调色定义为 Light strong=40, alternate=30,Dark strong=80, alternate=70:
| Base |
Material tonal palette |
| 08 |
error strong |
| 09 |
tertiary alternate |
| 0A |
secondary alternate |
| 0B |
primary alternate |
| 0C |
secondary strong |
| 0D |
primary strong |
| 0E |
tertiary strong |
| 0F |
error alternate |
该映射带版本号。未来修改映射必须产生新版本,不能让已保存主题在升级后静默变色。
Hellwal
支持 --dark/--light、--dark-offset 和 --bright-offset。两个 offset 都按 Hellwal 契约严格接受 0..1;越界、NaN 或非数字输入在启动进程前失败。
调用 JSON 模式时同时使用 --no-cache 和 --skip-term-colors,确保只从 stdout 消费结果且不修改当前终端。严格解析 special.background/foreground/cursor 和 colors.color0..color15。
ANSI 0–15 原值完整保存为 terminal override。Base16 投影固定为:
base00=background/color0、base03=color8、base05=foreground/color7、base07=color15;
base01、base02 在 base00→base03 间按 OKLab 三等分;
base04 是 base03→base05 中点,base06 是 base05→base07 中点;
base08=color1、base09=color9、base0A=color3、base0B=color2、base0C=color6、base0D=color4、base0E=color5、base0F=color13。
该投影明确是 ANSI 语义到 Base16 语义的有损归一化;终端本身仍使用完整原表,因此不能因归一化丢失 bright 色。
UI 与生命周期
第一条用户可见交付必须是内置/导入主题管理,而不是图片生成器。Settings 先能列出内置和已导入 family、导入上述常见文件、选择 family/mode、应用、取消、删除并跨重启保持;该路径必须驱动真实基础 app token 和真实 xterm ANSI 测试面,作为 Base16 引入后正常渲染的主要回归入口。Matugen/Hellwal 之后接入同一个 manager,不另建生成主题仓库或选择器。
设置页的外观区域至少提供:
- family 选择、
system/light/dark、删除和来源详情;
- 导入 Base16/Base24 文件;
- 选择图片和可用生成器;
- 公共 light/dark 选择;
- Matugen scheme;
- Hellwal dark offset/bright offset;
- 应用组件、文本层级、状态、K 线和 ANSI 色表的同屏预览;
- 市场颜色来源、涨跌习惯、普通状态颜色来源;
- 保存、应用和取消。
预览是隔离状态,取消不得污染 active theme。删除 active family 必须先切换到另一个 family;不允许在后台静默回落。生成进程可取消,失败时保留用户输入和诊断,不保存半成品。
主题保存在 OPENALICE_HOME 的文件状态中;active selection 走现有 preferences 和 migration 机制。内置 OpenAlice Light/Dark 作为不可删除的完整 family 参与同一投影,不再拥有旁路渲染逻辑。
首帧主题必须在 React 渲染前可用。开发、demo、浏览器和打包 Electron 都不能先绘制内置主题再异步闪到 active family;文件状态是权威,任何首帧缓存都必须带版本/身份并在状态变化时同步失效。
约束
- 不改变 UTA、broker、账户和交易写入边界;主题是 Alice/UI 文件状态。
- 不执行 shell 字符串,不信任 scheme 文件、图片路径或生成器 stdout。
- 不允许宽松解析、未知字段猜测、缺色补默认值、单 variant 自动反色或生成失败后静默回退。
- 不允许 Matugen/Hellwal 模板、副作用后端或用户配置写入。
- 不把完整主题仅存在 localStorage;持久化 shape 变化必须使用 migration framework。
- 保持 lite/read-only 模式可用;没有任何生成器时仍可使用内置主题和导入。
- 终端继续声明
xterm-256color/truecolor;主题系统不伪装成 shell 全局主题管理器。
- 保护用户现有主题文件;升级迁移幂等,删除必须是显式用户动作。
- README 产品定位不在本 issue 范围内;若实现造成重大产品表述变化,另行确认文案。
不应残留
- 前端和终端各自维护互不关联的 active theme 身份;
- Matugen
base16/Wal backend 被当作 OpenAlice 主题输入;
- Hellwal
color0..color15 被标称为原生 Base16;
- 活跃组件中的主题相关固定 hex/rgba、固定 Tailwind 色阶和组件级
dark: 分支;
- K 线、成交量、盈亏和买卖方向各自维护颜色常量;
- 生成器通过 shell、模板或用户配置完成输出;
- 删除/加载失败后无提示地换成默认主题;
- 只在 mock/unit test 中证明工具集成,未运行真实 Matugen/Hellwal;
- 只有 scheme parser/API,没有 Settings 内置/导入管理和真实 app/xterm 渲染证明;
- 只验证浏览器开发模式,未验证打包 Electron 的 PATH 和首帧行为。
验收标准
| # |
Dimension |
Check |
Command |
Env |
Expect |
| 1 |
function |
类型、解析、映射、派生色、offset、对比度、迁移和失败路径 |
npx tsc --noEmit && pnpm test && (cd ui && npx tsc -b) |
local |
全部 exit 0;测试覆盖 Base16 common/legacy、Base24、Matugen fixture、Hellwal fixture、明暗 family、非法输入和幂等迁移 |
| 2 |
integration |
主题文件、preferences、API、UI、图表和终端共同消费一个 active family |
pnpm test:e2e |
local |
exit 0;导入/生成/预览/保存/切换/重启/删除约束均通过,图表与现有 xterm 实例无需重建即可更新 |
| 3 |
environment |
demo API 和完整外观页不依赖真实 broker/UTA |
pnpm -F open-alice-ui dev:demo |
browser demo |
/settings 可完成内置/导入主题、明暗切换、市场策略和前端/图表/终端预览;无缺失 handler |
| 4 |
environment |
Hellwal 真机能力检测和图片生成 |
hellwal --version && pnpm dev |
local,PATH 中安装 Hellwal 1.0.7 或兼容版本 |
UI 显示绝对路径/版本/能力;light/dark 和两种 offset 影响结果;保存后前端与终端一致;进程不写终端配置 |
| 5 |
environment |
Matugen 真机能力检测和 Material→Base16 |
matugen --version && pnpm dev |
local,PATH 中安装支持 dry-run JSON 的 Matugen |
UI 只展示实际支持 scheme;相同图片在 light/dark、至少两种 scheme 下产生不同且可复现结果;结果与 JSON palettes 映射一致且不读取 base16 子树 |
| 6 |
environment |
GUI/打包环境 PATH、持久化和首帧 |
CSC_IDENTITY_AUTO_DISCOVERY=false pnpm electron:smoke:workspace |
local packaged Electron |
exit 0;GUI 能检测 Homebrew/Cargo 等有效 PATH 中的工具;重启保持主题;启动无默认主题闪烁;smoke 自有包被正常清理 |
| 7 |
integration |
Base24/ANSI/extended ANSI |
pnpm test:e2e |
real xterm browser surface |
ANSI 0–15、bright 9–14 和 extended 16–21 的测试图与契约逐项一致;Hellwal 保留原始 ANSI16 |
| 8 |
integration |
市场颜色策略和地区习惯 |
pnpm test:e2e |
browser |
protected/theme 与绿涨红跌/红涨绿跌四种组合同时更新 K 线、成交量、盈亏和方向提示;背景/网格始终跟随主题;关键状态不只靠颜色表达 |
| 9 |
function |
活跃运行路径不再绕过主题 token |
`rg -n --glob '*.{ts,tsx,css}' '#[0-9a-fA-F]{3,8} |
rgba?( |
(?:text |
| 10 |
function |
完整仓库回归 |
pnpm build && pnpm test |
local |
exit 0;内置 Light/Dark 与现有默认外观等价,lite/read-only 启动不要求安装生成器 |
| 11 |
integration |
内置/常见导入主题管理是首个 Base16 渲染基准 |
pnpm test:e2e |
Settings + real app/xterm browser surface |
从 Settings 导入并选择 Base16/Base24、iTerm2、Windows Terminal、Alacritty、Kitty/Ghostty、Xresources fixtures;基础 app computed token 与 xterm ANSI/bright/extended 槽位逐项符合输入,reload 后保持且无首帧闪烁 |
依赖关系
- Depends on: 无。
- Blocks: 后续任何第三方 scheme catalog、主题分享/导出、壁纸自动同步和系统级主题监听能力。
本 issue 是完整的产品与验证契约。若实现需要拆成多个 PR,应先建立子 issue 并让每个 PR 只对应一个子 issue;子 issue 必须继承本文的映射、失败语义、安全颜色和父级验收义务。PR 合入非 default branch 时,Closes 只形成 cross-reference,必须在核对 PR merged/base 后手动关闭对应 issue;父 issue 仅在所有子 issue 完成且以上整体验收全部通过后关闭。
目标
建立一个同时覆盖 OpenAlice 前端界面、图表和 Workspace 终端的统一外观主题系统。该系统以 Base16 为最低兼容契约,支持 Base24 的终端扩展色,允许直接导入现有 scheme,并可选调用用户
PATH中的 Matugen 或 Hellwal 从图片生成可预览、可保存、可复现的明暗主题。本 issue 的完成态不是“终端能换 16 个颜色”,而是同一个主题来源经过明确的语义投影后驱动应用界面、第三方图表和 xterm.js;生成器差异止于输入适配层,不扩散成三套渲染逻辑。
上下文
TraderAlice/OpenAlicedevTraderAlice/OpenAlice@65fd2ec0ui/src/index.css:内置浅色/深色 CSS token,同时仍存在固定颜色和组件级dark:分支;ui/src/theme/:只持有light | dark | auto,没有主题 family 身份;ui/src/components/workspace/terminalThemeProfile.ts:终端使用两套硬编码 profile;ui/src/components/market/KlinePanel.tsx:图表文字、网格、K 线和成交量颜色直接写在 JS 中;src/workspaces/spawn-env.ts:PTY 对外声明TERM=xterm-256color和COLORTERM=truecolor;src/core/preferences.ts、src/webui/routes/preferences.ts:文件偏好和 HTTP 边界;src/migrations/:OPENALICE_HOME持久化数据的迁移机制。本设计来自以下明确需求:
问题
当前应用主题、终端主题和图表颜色彼此分离。应用只在 CSS 中维护两套内置配色;终端另有两套硬编码 profile;Canvas/SVG/第三方图表和若干状态组件继续直接使用十六进制、
rgba()、固定 Tailwind 色阶和dark:分支。用户不能导入通用 scheme,也不能从同一图片生成前端和终端一致的主题。直接把每个固定颜色替换为某个
baseXX也不能解决问题。Base16 是基础色板,不是完整 UI token 集;背景层级、交互状态、透明强调、图表网格和安全状态必须经过确定且可测试的语义投影。K 线涨跌、盈亏、真实交易确认和破坏性操作还需要独立的辨识度策略,不能因一张图片的主色而失去含义。预期结果
PATH中的 Matugen 和 Hellwal,展示绝对路径、版本和已验证能力;缺失任一工具不影响另一工具和普通导入。base16子树。color0..color15冒充原生 Base16。主题契约
Scheme 与 family
一个主题 variant 至少包含:
light | darkvariant;base00..base0F;base10..base17;一个主题 family 包含明确的 light variant、dark variant 或两者。导入单个 scheme 时使用文件中的
variant;旧式 flat Base16 文件没有 variant 时,导入 UI 必须要求用户明确选择,禁止通过亮度猜测。生成页允许只生成当前 variant,也允许从同一图片和同一参数生成完整 light/dark family。应用偏好至少表达:
system | light | dark外观模式;导入边界
必须接受并严格区分:
system、name、author、variant、palette;scheme、author、base00..base0F;system: base24和base00..base17。.itermcolorsplist。color0..color15色表。Base16/Base24 输入保留原始语义槽位。iTerm2、Windows Terminal、Alacritty、Kitty/Ghostty 和 Xresources 作为 ANSI16 输入,必须完整保留 foreground/background/cursor/selection 和 ANSI0–15 override,并复用本文 Hellwal ANSI16→Base16 投影;不同文件 adapter 不得各自发明映射。格式检测以内容 schema 为权威,扩展名只选择候选 parser;多个 parser 同时匹配时必须作为 ambiguous input 失败。VS Code/JetBrains 全量 UI theme、任意 CSS theme、脚本模板和 include 不在本 issue 范围内。
颜色在输入边界解析为规范的不透明 RGB 值。缺少必需槽位、重复/未知 system、非法 variant、非法颜色、错误数量或与声明 system 不符时,导入失败并指向具体字段;不得补默认值、吞掉未知结构或从文件名推断语义。导入先进入预览,用户确认后才持久化。
Base16/Base24 到终端
Base16 的 ANSI 0–15 映射固定为:
xterm.js
extendedAnsi的前六项固定为:ANSI 16=base09、17=base0F、18=base01、19=base02、20=base04、21=base06。Base24 提供独立 bright ANSI:9=
base12、10=base14、11=base13、12=base16、13=base17、14=base15;base10/base11保留为额外背景层级。若 variant 携带精确 ANSI 0–15 override,则终端渲染优先使用该 override,Base16 仍用于前端、语义消费和导出。终端前景、背景、光标、光标反色、选择背景和选择前景全部来自当前 variant;主题变化后现有终端实例立即更新,不能要求重建 Workspace 或丢失 PTY/scrollback。
Base16 到前端
基础投影固定为:
base00base01base02base03base04base05base06base07base08base09base0Abase0Bbase0Cbase0Dbase0Ebase0Fhover、active、selection、focus ring、subtle surface、透明状态背景、图表网格和 overlay 由这些基础色确定性生成。颜色混合统一在 OKLab/OKLCH 中完成;已解析主题保存实际颜色值,不把浏览器相关的
color-mix()字符串当持久化数据。中性背景和文字优先使用现成的base00..base07梯度,只有连续交互状态才做差值。前景/背景、普通文本、交互控件和非文本图形必须满足适用的 WCAG 对比度要求。
on-accent从候选前景中选择满足要求的一项;没有候选能满足时主题不能保存为可用主题,错误必须指出失败的 token 对。所有仍在运行路径中的固定 Tailwind 色阶、十六进制、
rgba()和组件级dark:分支都要分类迁移为:基础 token、派生 token、受保护语义色,或有明确理由的允许项。允许项仅包括内置主题源数据、测试 fixture/期望值、媒体内容、透明/物理阴影基元以及本文定义的受保护颜色;不能用 allowlist 隐藏未迁移组件。图表与市场颜色
Canvas、SVG 和第三方图表必须消费同一 resolved theme,并在主题切换时更新现有实例。图表背景=
base00,网格来自base01/base02/base03,轴文字=base04,轴边框=base03,交互强调=base0D。市场颜色设置包含两个正交字段:
protected | theme;green-up-red-down | red-up-green-down。protected使用内置、分别适配 light/dark 且满足图形对比要求的方向色;theme使用base0B与base08,再按涨跌习惯决定 positive/negative 绑定。K 线实体、边框、影线、成交量、盈亏数字和市场方向提示必须共用该绑定,不得各自写颜色。背景、网格和坐标无论选择哪种来源都跟随主题。普通运行状态另有
protected | theme策略,控制 success/warning/danger/info。不可逆删除、权限拒绝、真实交易确认、broker 写入失败和风控阻止始终使用受保护的危险语义;主题可以调整其表面亮度和透明度,但不能改变危险含义。任何关键状态都不能只靠颜色表达。图片生成契约
能力检测
检测 OpenAlice 实际有效
PATH中的matugen和hellwal,解析为绝对可执行路径并报告版本和能力。检测结果不是单一布尔值。设置页可手动刷新;工具不存在、版本无法识别或缺少所需 flag 时显示明确状态,不影响普通主题导入和另一生成器。执行时使用已解析的绝对路径和 argv,不经 shell,不执行用户模板,不写 wallpaper/终端配置。图片只进入受控的本地工作目录;完成、失败或取消后清理。持久化只记录 digest 和生成参数,除非用户明确选择保留原图。
Matugen
支持
light | dark,并展示已安装版本实际支持的 scheme。当前目标集合为:scheme-contentscheme-expressivescheme-fidelityscheme-fruit-saladscheme-monochromescheme-neutralscheme-rainbowscheme-tonal-spotscheme-vibrantscheme-smart调用必须使用 dry-run JSON 输出。解析
colors/palettes,忽略 Matugen JSON 的base16子树,不使用--base16-backend,不消费 Wal 提取结果。Material tonal palette 到 Base16 的 v1 映射固定如下:
强调色定义为 Light
strong=40, alternate=30,Darkstrong=80, alternate=70:该映射带版本号。未来修改映射必须产生新版本,不能让已保存主题在升级后静默变色。
Hellwal
支持
--dark/--light、--dark-offset和--bright-offset。两个 offset 都按 Hellwal 契约严格接受0..1;越界、NaN 或非数字输入在启动进程前失败。调用 JSON 模式时同时使用
--no-cache和--skip-term-colors,确保只从 stdout 消费结果且不修改当前终端。严格解析special.background/foreground/cursor和colors.color0..color15。ANSI 0–15 原值完整保存为 terminal override。Base16 投影固定为:
base00=background/color0、base03=color8、base05=foreground/color7、base07=color15;base01、base02在base00→base03间按 OKLab 三等分;base04是base03→base05中点,base06是base05→base07中点;base08=color1、base09=color9、base0A=color3、base0B=color2、base0C=color6、base0D=color4、base0E=color5、base0F=color13。该投影明确是 ANSI 语义到 Base16 语义的有损归一化;终端本身仍使用完整原表,因此不能因归一化丢失 bright 色。
UI 与生命周期
第一条用户可见交付必须是内置/导入主题管理,而不是图片生成器。Settings 先能列出内置和已导入 family、导入上述常见文件、选择 family/mode、应用、取消、删除并跨重启保持;该路径必须驱动真实基础 app token 和真实 xterm ANSI 测试面,作为 Base16 引入后正常渲染的主要回归入口。Matugen/Hellwal 之后接入同一个 manager,不另建生成主题仓库或选择器。
设置页的外观区域至少提供:
system/light/dark、删除和来源详情;预览是隔离状态,取消不得污染 active theme。删除 active family 必须先切换到另一个 family;不允许在后台静默回落。生成进程可取消,失败时保留用户输入和诊断,不保存半成品。
主题保存在
OPENALICE_HOME的文件状态中;active selection 走现有 preferences 和 migration 机制。内置 OpenAlice Light/Dark 作为不可删除的完整 family 参与同一投影,不再拥有旁路渲染逻辑。首帧主题必须在 React 渲染前可用。开发、demo、浏览器和打包 Electron 都不能先绘制内置主题再异步闪到 active family;文件状态是权威,任何首帧缓存都必须带版本/身份并在状态变化时同步失效。
约束
xterm-256color/truecolor;主题系统不伪装成 shell 全局主题管理器。不应残留
base16/Wal backend 被当作 OpenAlice 主题输入;color0..color15被标称为原生 Base16;dark:分支;验收标准
npx tsc --noEmit && pnpm test && (cd ui && npx tsc -b)pnpm test:e2epnpm -F open-alice-ui dev:demo/settings可完成内置/导入主题、明暗切换、市场策略和前端/图表/终端预览;无缺失 handlerhellwal --version && pnpm devPATH中安装 Hellwal 1.0.7 或兼容版本matugen --version && pnpm devPATH中安装支持 dry-run JSON 的 Matugenpalettes映射一致且不读取base16子树CSC_IDENTITY_AUTO_DISCOVERY=false pnpm electron:smoke:workspacepnpm test:e2epnpm test:e2epnpm build && pnpm testpnpm test:e2e依赖关系
本 issue 是完整的产品与验证契约。若实现需要拆成多个 PR,应先建立子 issue 并让每个 PR 只对应一个子 issue;子 issue 必须继承本文的映射、失败语义、安全颜色和父级验收义务。PR 合入非 default branch 时,
Closes只形成 cross-reference,必须在核对 PR merged/base 后手动关闭对应 issue;父 issue 仅在所有子 issue 完成且以上整体验收全部通过后关闭。