# Cindy 前端设计体系调研报告 > 调研对象:https://github.com/makecindy/cindy(Apache-2.0,默认分支 `main`) > 调研性质:**只读**。本地镜像位于 `_ref_cindy/`(`git clone --depth 1`,13311 个文件),未对仓库做任何修改。 > 一手依据:`_ref_cindy/docs/design-rules/DESIGN.md`(2033 行 / 365KB,权威视觉规范正本)+ 同目录设计治理与索引文档 + `apps/desktop`、`apps/mobile`、`packages/design-tokens` 实现代码。 --- ## 0. 结论速览 Cindy 是一款 Electron + React Native 双端的 AI Agent 工作台。它的前端外观不是"随手设计的 UI",而是一套**有治理合同、有机器门禁、有决策台账**的完整设计系统: - **视觉哲学**:激进极简的"纸张感"工作台——近单色画布 + 零阴影 + 1px 描边,彩色只作为语义信号出现(§1/§6/§7)。 - **字体**:全站**单一无衬线族 Inter**(显示标题与正文同一字体,层级只靠字号+字重);等宽 JetBrains Mono 仅用于代码。字重四档 400/500/600/700 且 700 受豁免域严格限制(§3)。 - **主题**:VSCode 式 ColorRegistry + theme-override 架构,三层 token(语义槽 / 别名 / 单例),内置 Light/Dark + 社区主题 + 可导入 VSCode / Obsidian 配色(§10)。 - **版块设计**:整窗三区(sidebar + content + 可选右面板),区域靠 **1px Board 分隔线**而非背景色差;8px 基数间距;圆角由 §5「两步决策树」唯一裁决(0/2/4/8/12/9999px,**没有 3/6/10px**)(§5)。三栏不是写死的 DOM,而是 `layoutTree.ts` 里一棵**可拖拽的布局树**(sidebar 在分割树外层 + content 递归分割 + 恒空的 float 层),1px 缝本身就是拖宽把手(含 7px 隐形抓握区、双击均分)(§13.4)。 - **实现层四处反直觉(只读规范文档一定会踩)**:① **主题色真身不在 `styles/globals.css`**——它只 import 一个 106 行、不含任何主题色的 `tokens.css`;549 个色值住在 `apps/desktop/src/renderer/themes/colors.ts`(3216 行)的 `GENERATED_DEFAULTS`,由 `packages/design-tokens` 的 DTCG 三层反向管理(§13.3);② **新用户默认主题族是 `cindy`**(`themes/families.ts:28 DEFAULT_FAMILY_ID='cindy'`),名字听起来更"默认"的 `default-light/dark` 其实是名为 **Classic** 的旧默认(§13.3);③ **CINDY 皮肤的 accent / CTA 是中性灰**(Light `#3C3F43` / Dark `#EEEEEE`),品牌红 `#DF0C27` 只出现在登录界面点缀、**禁止作页面背景**,所以"Cindy 是红色品牌"的直觉在编辑器 UI 里是错的(§13.3 / §16.1);④ **语义豁免 ≠ light/dark 同值**——`classify.ts:139-195` 的 11 个豁免 id 含义是"外部导入主题不覆盖它们",与明暗两态取值是否相同无关(§13.3)。另有两个字体侧的实现细节:`tailwind.config.ts` **没有 `sans` 键**(UI 字体只走 CSS 变量 + body 继承),默认代码字体是**系统等宽栈**而非 JetBrains Mono(§13.1)。 - **治理强度**:设计规则由 `design-governance.md`(治理合同)+ `design-decision-log.md`(只增不改的决策台账)+ 一系列冻结测试(`cindyThemes.test.ts`、`typographyDiscipline.test.ts`、`switchThemeContrast.test.ts` 等)共同守护——**规范里写的规则大多有测试断言**。 --- ## 1. 仓库与设计文档架构 ### 1.1 仓库边界 本仓只负责 **desktop、mobile 及其共享 packages**(服务端在独立仓库)。主线包: | 包 / 目录 | 角色 | | --- | --- | | `apps/desktop` | Electron 渲染层(主工作台,React + Tailwind) | | `apps/mobile` | React Native 移动端 | | `packages/design-tokens` | 设计 token 正本(DTCG 格式 + 生成链路) | | `packages/*`(共 35 个) | 共享能力包 | Topics:`agent`、`ai-agent`、`electron`、`ios`、`android`、`react-native`、`typescript`、`claude-code`、`codex`、`llm`、`macos`、`windows`。Homepage:`https://cindy.app`。 ### 1.2 设计文档的三层真相源 这是整套体系最值得借鉴的地方——**文档分工被显式写成"真相源边界"**: | 文档 | 体积 | 地位 | | --- | --- | --- | | `docs/design-rules/DESIGN.md` | 365KB / 2033 行 | **权威视觉规范正本**。所有视觉规则的最终解释权 | | `docs/design-rules/cindy-design-system.md` | 27KB | 设计文档索引 + 版本台账 | | `docs/design-rules/design-governance.md` | 45KB | **治理合同**:真相源边界、兼容红线、两级证据合同、PR 风险分类 | | `docs/design-rules/design-decision-log.md` | 121KB | **决策史台账(只增不改)**。记录"为什么这样定"与历史值 | | `docs/design-rules/token-decision-table.md` | 30KB | token 决策记录;**§9 是色彩阶梯权威值** | | `docs/design-rules/figma-component-spec.md` | 72KB | 登录链路逐参数权威(含 Figma nodeId 溯源) | | `docs/design-rules/design-inventory.md` | 269KB | 设计资产清单 | | `docs/design-rules/usage-history-charts.md`、`ios-native-design.md` | — | 专题补充规范 | | 根 `DESIGN.md` | 379B | 仅作历史路径兼容跳转,指向 `docs/design-rules/DESIGN.md` | 冲突裁决顺序被写死:**与 DESIGN.md 冲突时以 DESIGN.md 为准**;但色彩阶梯的"值"以 `token-decision-table.md §9` + `colors.ts` 为准;登录域逐参数以 `figma-component-spec.md` 为准。 ### 1.3 DESIGN.md 章节地图(2033 行) ``` §1 视觉主题与氛围 (7) §9 Agent Prompt Guide (736) §1.1 New-task home (25) §10 主题系统与 Token 参考 (767) §2 颜色与角色 (39) §11 Voice & Content 微文案 (944) §3 排版规则 (248) §12 (并入 §4)(986) §4 组件样式 (338) §13 Open Spec/Token Gaps (990) §5 布局原则 (560) §14 交互约定 (998) §6 深度与层级 (682) §15 CINDY 皮肤族 (1487) §7 Do's and Don'ts (694) §16 登录链路 (1762) §8 窗口与自适应 (721) ``` 编号纪律:**章节号是稳定标识符**(被代码注释、冻结测试、其他文档引用),可以增补但不能重排;例如 15.9 从未分配,编号空缺被永久保留。 --- ## 2. 视觉主题与氛围(§1) > "Cindy 的界面是应用于 AI Agent 工作台的激进极简——一片安静、近乎单色的画布,用户的输入与 Agent 的输出是唯一要求注意力的东西。……这是 Dieter Rams 式物件的数字等价物:每个像素都挣得它的位置,**装饰的缺席本身就是设计**。" 关键特征(§1 Key Characteristics): 1. **近单色默认主题**:彩色只经 §2 的许可语义集出现,且一律通过 token 消费。 2. **Inter 单一无衬线族**:同时承担显示标题与正文。 3. **紧凑圆角体系**:0 / 2(数据标记)/ 4(键盘 keycap)/ 8(内层控件)/ 12(容器)/ 9999px(pill 动作框)。 4. **基础语言零阴影**:深度只来自背景色阶与 1px 描边(窄例外经 token 门控,见 §10)。 5. **普通动作框默认 pill**;已注册形状(keycap、数据标记)与被包含内容作为独立可见层单独分配。 6. **工作中界面没有吉祥物与装饰插画**——品牌图像只出现在登记过的品牌表面(§15.7 / §16)。 7. **极致内容克制**:每个表面只讲一个清晰的想法。 `§1.1`(2026-09-06 用户决定):新任务首页**不显示**继承订阅或推广积分的通知卡;订阅详情归服务商设置、余额与到期归计费设置。移除是显式产品决定,**不得以"保留一次性通知行为"为由恢复**。 --- ## 3. 颜色体系(§2 + §15.16) ### 3.1 三层图层系统(Light/Dark 对称) | 角色 | Light | Dark | 用途 | | --- | --- | --- | --- | | **Surface** | `#f8f8f6` | `#1f1f1e` | 页面底色——每个页面从这里开始 | | **Card** | `#ffffff` | `#2c2c2a` | 抬升层:登录卡、模态、面板、需要离页浮起的一切 | | **Board** | `#d7d7d4` | `#3c3c3a` | 发丝分隔线——1px 边框、卡片轮廓、同层分隔 | **分层规则(扁平 vs 抬升)**——这是"版块设计"的根: - **全窗应用**(主工作台、聊天界面、看板):整窗用**单一 Surface 平底**,页面结构层面**没有 Card 层**;区域边界(sidebar / toolbar / 内容区)用 **1px Board 分隔线**绘制,**绝不用背景色差**。 - **居中卡片式布局**(登录、模态、空态卡):Surface 作页底 + Card 作浮起卡,靠 Surface↔Card 的色差读作"抬升",**不需要阴影**;卡片轮廓用 Board。 - **元素级 vs 页面级**(重要澄清):全窗布局的"扁平 Surface"只约束**页面整体结构**,不约束单个控件。全窗聊天界面可以同时有"扁平 Surface 页面"和"Card 色的聊天输入框"——这是两个不同作用域。 ### 3.2 交互中性色与文字色 - **Chip 与按钮中性色**(前景元素,不属于图层系统):Light 用 `#e5e5e5`(`--surface-chip`);Dark 主 chip 用 `#3c3c3a`(`--surface-chip`),变体 `--surface-chip-alt` `#2c2c2a` 收敛到 Dark Card 值(chip 直接坐在 Surface 上、只需 Card 级抬升时用)。拿不准时用 `--surface-chip`。 - **中性文字**:Stone `#737373`(次级正文、页脚链接)、Mid Gray `#525252`(加重次级)、Silver `#a3a3a3`(三级文字/深度弱化元信息)。 - **Silver 永不用作 placeholder**——太显眼、读起来像已填写;placeholder 用 `--text-placeholder` `#c4c4c4`。 - Dark 模式文字用软化中性色减轻眼疲劳:Soft Gray `#d4d4d4` 主文字、Silver `#a3a3a3` 次级、Stone `#737373` 三级(`--text-secondary` / `--text-tertiary` 在明暗间**值互换**)。 - 已废弃:`#d4d4d4` "白色按钮描边色" 与 `#404040` "白底按钮文字色"(2026-06 G1/G2)——从未被真实组件使用;白色 pill 次级按钮统一用 Board 描边 + `--text-primary` 文字。 ### 3.3 许可的语义色(近绝对的灰度规则的唯一出口) 新语义色**必须先登记入 §2 才能引入**。已登记的窄作用域彩色: | 语义 | 值 | 作用域 | | --- | --- | --- | | **Focus Blue** | `#417CDD` @50% | 键盘焦点环(`--focus-ring` / `--focus-ring-soft`),2026-07-17 定稿,替代 Tailwind 默认 `#3b82f6`;正常交互流程中永不可见 | | **Thinking / Warning Orange** | `#EA6B17` 双模同值 | 共享 warning-accent 族:ChatView 运行状态条(sparkles + 状态文字,无背景填充)、侧栏运行中呼吸图标、协作模式菜单行 ON 态、plan-approve 图标、Full Access 权限高亮、设置集成警告、workflow agent 状态条运行单元格(8×8px)、侧栏任务信息未解决 PR 角标点、Bot 群聊发言行(2026-09-27 登记) | | **状态四色** | running `#EA6B17` / awaiting `#19D2C1` / error `#D91F37` / done `#2AAE5B` | 双模同值,9 个内置主题全部跟随 | | **Toast 图标四色** | info `#417CDD` / success `#2AAE5B` / warning `#F3A115` / error `#D91F37` | **仅** Toast pill 内的 16×16 lucide 图标;pill 本体(背景/文字/描边/关闭图标)严格灰度 | | **Diff** | Add fg `#22863a`/`#7ee787`,bg `#f0fff4`/`#033a16`;Del fg `#b31d28`/`#ff7b72`,bg `#ffeef0`/`#67060c` | GitHub 标准;作用于 `+`/`-` 字形、变更行文字**与整行背景**(三处:Edit 工具 DiffView 卡、markdown ```diff 代码块、`.diff`/`.patch` 的 TextLightbox);行号槽与未变更行严格灰度 | | **文件类型徽章** | pdf `#B23A26` / doc `#2C5CA8` / sheet `#2E7D4F` / slide `#A25A12` / code `#5B49A8` | 前景统一独立白 `#FFFFFF`(对比度 5.96–7.09,10px 小字过 AA) | | **Bot 头像色相** | 9 个 tint(red/orange/amber/green/teal/blue/violet/pink/graphite) | 仅 `features/bots` 的 Bot 圆形头像填充;编码"这是哪个 Bot"的身份线索,不是状态;外部主题导入永不触碰 | | **Bot 未读徽章** | `#417CDD` + 独立白前景 | 仅队友列表未读指示(IM 未读语义),不是 CTA | | **权限选择器高亮** | Auto Approval `#417CDD` / Full Access `#EA6B17` | 仅选项文字/图标/勾与折叠触发器;选中行背景保持灰度 | | **使用历史类别色** | heatmap 蓝色强度阶 + `--usage-model-1…5` 种子色 + `--engine-badge-cc/-codex` | 用量热力图与日 token 图;类别色不继承 process/status 语义 | | **进程类别色** | 6 对(task 蓝/agent 服务紫/主进程粉/渲染器青/GPU 琥珀/工具绿) | **仅**资源用量表 14px 进程字形;编码进程类别,**不是健康/状态** | | **任务标签十二色** | 红橙黄绿蓝紫灰粉珊瑚青靛蓝白 | 仅标签色球与编辑色板,表示用户分类**不表示运行状态**;色球圆形细描边、选中时勾在球内;白色在双模都保持白,用独立深色勾保对比度 | | **ConfirmDialog Danger** | `#EF4444` | 仅 danger 变体的确认按钮背景 | | **Windows 任务栏提醒徽章** | 背景 `#D91F37` + 白字无描边 | 原生 OS overlay,非应用内主题 token;1 位数字用圆、多位用圆角方、>99 用 `99+` | ### 3.4 渐变系统(§2) - **核心 UI 零渐变**。视觉分隔只靠平面色块与 1px 描边——这是近乎哲学的设计选择(登录画布同样按裁定保持扁平,见 §16.5)。 - 唯一窄例外(Desktop 2026-08-10 批准):`BrandLoadingMark` 可用一条中性 `transparent → --text-primary → transparent` 渐变,**仅**作为会话消息树延迟时官方字标内部的 mask 裁剪高光。它是加载进度信号,不是背景/分隔/填充/可复用表面处置;只在 200ms 延迟揭示阈值后才可能出现,消息树一挂载即消失。 ### 3.5 2026-08 色彩阶梯修订(§15.16)——暖象牙 / 中性近黑 这是 CINDY 皮肤族的最新色彩模型,**值权威 = `token-decision-table.md §9`**,冻结在 `cindyDecisionData.ts`: **Light = 暖象牙** - page `#F2F2ED`;每个 surface/border 都是暖的,且 **B = R−5**(单一暖度常量)。 - 一个被认可的例外:纯白 `#FFFFFF` 保留给需要在近白卡片之上最大抬升的浮动平面(当前仅 `ask-option-list-bg`)。 - 卡片保持近白 `#FDFDF8`——卡片抬升 ≈9.4%,与原始 spec 一致。 - hover/chip 档坐在页面**之下**(白色天花板之上没有空间);侧栏是自己的平面、深一档(`#EEEEE9` 配 85% 玻璃不透明度)。 - **文字阶梯**:最深强调 `#0C0C0C` → 正文 `#1A1A1A`(两者中性、清晰)→ 三级 `#6B6B67`(暖 −4)→ 次级/元信息 `#888883`(暖 −5,≥3.0 floor)→ disabled `#9D9D98`(WCAG 豁免)。**文字越浅,暖度越高。** **Dark = 纯中性** - page `#181818`,所有层 R=G=B(零暖度),是自 2026-07 值的简单平行位移。 - 已知债务:近黑压缩只保留约 **65%** 的原始感知层分离度,记录在 issue #2559,修法是等亮度阶梯。 - Dark 文字档全部中性(三级 `#C1C1C1`,2026-08 等亮度中性化)。 **双重锚定原则**(修订后单个 token 无法同时服务两个平面): - 坐在**页面**上的交互填充锚定到页面(surface-hover 档)。 - 坐在**卡片/popover**上的填充锚定到卡片:light `#F6F6F1`,约低于卡片 6%——`settings-menu-bg-selected`、`perm-item-selected-bg`、`ask-badge-bg`、`cmd-palette-item-hover`。 - Dark 例外(仅本次修订):`settings-menu-bg-selected` 用菜单 hover 抬升 `#282828`,不是页面锚定的 `#1D1D1D` chip;其余三者暂留页面锚定值待各自后续。 - **不要把它们"统一"回去。** **孪生同步**:每个同时以 hex 与 HSL 承载的颜色(24 对)必须值一致;派生半透明内联值(如 `chat-input-border-focus` = 三级色 30%)在其源移动时必须重新派生。 **豁免族不动**:`login-*`(58 token)、`diff-*`、`error-*`、`overlay-*`、语义状态色、阴影。 --- ## 4. 排版与字体(§3) ### 4.1 字体族——全站单一无衬线 ``` Display / Body / UI : Inter fallback: system-ui, -apple-system, "Segoe UI", sans-serif Monospace : JetBrains Mono fallback: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace ``` > **桌面默认澄清**:JetBrains Mono 是**可选预设与历史排版样本**;实际发布的默认使用 `reference/foundations.json` → `app-font-code-default` 的**系统等宽字体栈** + 既有 CJK 回退(DS-8 保留了该栈与用户字体选择)。 选 Inter 的三条理由(原文):① 中性几何特征,不抢戏;② 小字号可读性极佳;③ 在 Web 与设计工具中广泛可用。**单一字体让层级保持干净——分离来自字号与字重,而不是字体的对比。** 原则四条: - **单一无衬线族**:显示标题与正文同一字体,不切换字体造层级。 - **字重克制**:日常 chrome 默认 400 / 500;600 是**限量**强调档;700 只活在登记豁免域内。没有 light、没有 black、没有中间值。 - **紧凑显示、舒适正文**:标题压到 1.0 行高,正文放松到 1.43–1.56,对比本身造层级、无需字重对比。 - **等宽只给代码**:JetBrains Mono 仅用于行内代码、终端命令、代码块,**永不用作 UI chrome**。 ### 4.2 层级表(Hierarchy) | 角色 | 字体 | 字号 | 字重 | 行高 | 备注 | | --- | --- | --- | --- | --- | --- | | Display / Hero | Inter | 48px (3rem) | 500 | 1.00 tight | 最大冲击力标题 | | Section Heading | Inter | 36px (2.25rem) | 500 | 1.11 tight | 功能区块标题 | | Sub-heading | Inter | 30px (1.88rem) | 400/500 | 1.20 tight | 卡片标题、功能名 | | Card Title | Inter | 24px (1.5rem) | 400 | 1.33 | 中等强调标题 | | Body Large | Inter | 18px (1.13rem) | 400/500 | 1.56 | Hero 描述、按钮文字 | | Body / Link | Inter | 16px (1rem) | 400/500 | 1.50 | 标准正文、导航 | | Caption | Inter | 14px (0.88rem) | 400 | 1.43 | 元信息、描述 | | Small | Inter | 12px (0.75rem) | 400 | 1.33 | 最小阅读字号 | | Micro Label | Inter | 10–13px | 400/500 | 1.20–1.40 | **仅辅助/非阅读**标签:侧栏树分组头(13px)、树行计数、frontmatter 字段名、scope chips、tag pills、状态徽章、面包屑段。**永不用于正文** | | Code Body | JetBrains Mono | 16px | 400 | 1.50 | 行内代码、命令 | | Code Caption | JetBrains Mono | 14px | 400 | 1.43 | 代码片段 | | Code Small | JetBrains Mono | 11–12px | 400/500 | 1.40–1.63 | 标签、树内路径 | **定位说明**:Display / Section Heading / Sub-heading 三行是**仅为品牌画布**(§16 登录/Splash 家族及自包含页)的概念角色目标;它们的实际值活在画布常量与 §16 表格里,不在应用代码中。日常 chrome **与**应用内空态都走 Body / Caption / Small / Micro 行;`apps/desktop` 代码的规范字号集是下方的「桌面 UI 字号白名单」——**层级表描述角色,白名单约束值。** ### 4.3 字重阶梯(2026-08 修订,issue #1505) 全站字重**只允许四个整百档**,禁止一切中间值(550/650…)与 800 及以上: | 档 | 名称 | 用途 | | --- | --- | --- | | 400 | Regular | 长文阅读正文、代码 | | 500 | Medium | **UI 默认强调、标题——日常 chrome 的主力档** | | 600 | Semibold | 限量强调(徽标、表头、选中态等局部加重)。新增使用要能说出「为什么 500 不够」 | | 700 | Bold | **仅限豁免登记表中的域**,禁止出现在普通 UI chrome | - 与移动端 `apps/mobile/src/theme/tokens.ts` 的 `fontWeight` token(regular/medium/semibold/bold)**一一对应——两端一张梯子**。 - **手机端按角色选字重**(2026-09-26 用户定稿):标题 600、列表行/按钮 500、正文与说明 400、分组小标签 600;**浅色字不配粗字重**。 - **两端 700 口径**:手机端 UI chrome 上限仍是 600;`bold`/700 在手机端只允许出现在登记域(原生 Markdown strong 与登录品牌画布)。"四档梯子"是两端共用的**档位定义**,不等于两端 chrome 都能用 700。 - **CJK 注记**(重要实践教训):桌面未设 `font-synthesis: none`,中文回退字体(PingFang)**公开档位只到 600**——UI 里用 700 会在中文上触发**伪粗体**(算法加粗、边缘发糊),且 600 与 700 在 CJK 上的渲染差异不可靠。**中文层级不得依赖 600 vs 700 区分,强调靠字号或颜色。** ### 4.4 桌面 UI 字号白名单(2026-08,issue #1505) - **UI 段:{10, 11, 12, 13, 14, 15, 16}px;标题/内容段:{18, 20, 24, 28}px。下限 10px——9px 及以下禁止(再小就不是文字是纹理)。** - **下限约束的是"开发者写死的档位",不约束运行期缩放结果**:用户可把 UI 字号设到 `appearanceSettings.uiSize` 最小值 12(默认 14),`useFontSettings` 按 `uiSize / 14` 缩放并 `Math.round`,此时 `--text-10` 与 `--text-11` 都会算成 9px(两档在该设置下失去区分)。这是用户主动选择的整体缩小,守卫只检查源码里写的档位。**若要保住 10px 视觉下限,应在字号设置侧(提高 `uiSize` 下限或改缩放曲线)解决,而不是往白名单里加更小的档位。** - **写法**:一律用 `tailwind.config.ts` 的 `text-` token 类(映射 `--text-` 变量;doc 紧凑模式与字号缩放能力都挂在这层变量上,**任意值类会静默漏掉这些机制**)。语义类 `text-xs/sm/base/lg` 只收编存量(等值 12/14/16/18);源码侧禁止 `text-xl` 及以上(守卫拦截)。**禁止新增任意值 `text-[Npx]`(含一切小数)与白名单外档位**;需要新档先改本表与权威来源。 - **单源约定(DS-8)**:DTCG 是已接管族的唯一编辑源,同一 Terrazzo 流程生成本节摘要、CSS 默认值、Tailwind / 字号 hook / class merge 的映射。生成新鲜度 + 独立白名单 + 实际缩放测试共同保护这条链,不再手动同步多份数值。 - **品牌画布域**(登录/Splash 等设计 px 坐标系表面)不映射白名单:字面量只允许进画布常量文件(`loginDesignTokens.ts` 的地位,对齐手机端 `loginSkinLayout.ts`)或登记的自包含品牌页生成器(`oauthResultPage.ts`),组件消费端照常受守卫扫描。 ### 4.5 排版豁免登记表(登记过的刻意分歧,不要当 bug"修") | 域 | 范围 | 允许 | 理由 | | --- | --- | --- | --- | | 登录 / Splash 品牌画布 | 桌面 `LoginControls.tsx`、`loginDesignTokens.ts`、`LegacyMigrationDialog.tsx`(仅字重)、`oauthResultPage.ts`;手机 `(auth)/login.tsx`、`LoginSkinControls.tsx`、`loginSkinLayout.ts` | 700 + 设计 px 字号 | §16 已登记 Bold,两端共用同源登录画布与 figma 坐标系 | | markdown 内容 | **仅 Markdown 渲染路径**:桌面 `MarkdownRenderer` 输出的 DOM ``(解析为绝对 700,嵌套 strong 仍封顶 700)、CodeMirror strong 语法节点、移动端原生 Markdown strong | 700 / `bold` | 用户内容语义,非 UI chrome。**不覆盖普通 UI chrome 里直接写的 ``**(如 `MakerExperimentalView.tsx`)——那些同样被 preflight 的相对 `bolder` 渲染成 700,但不属豁免域,是已登记缺口,待归一为 `font-medium`/`font-semibold` | | hljs 主题移植 | `globals.css` 内 hljs 规则 | `bold` | 第三方主题移植,保真优先 | | 第三方查看器 | `vendor/drawio/viewer-static.min.js` 及 `vendor/` 下上游产物 | 上游自带字重字号 | 渲染的是用户绘图内容而非本产品 chrome;**整个 `vendor/` 目录不纳入扫描**(守卫盲区已显式登记) | | CodeMirror / Markdown 内容字号 | `codemirrorGithubTheme.ts` 标题层级(`2.15em`/`1.62em` 相对值) | 相对字号(`em` 派生) | 内容标题层级必须随编辑器基础字号等比缩放,固定 token 会打断比例关系 | | 命令式第三方 API | xterm `ITerminalOptions.fontSize`、SVG `fontSize="N"` 等 | 数值字面量 | 这类 API 不消费 DOM class。**取值规则**:必须取自白名单档位或用户代码字号设置解析出的运行期值,不允许凭手感另取一个数 | | 用户可配置代码字号 | `--app-code-font-size` 及其 `calc` 派生(约 19 个文件) | 变量与派生 | `appearanceSettings.codeSize` 允许 10–24 逐整数,运行期可落在白名单外的值;**白名单约束的是开发者写死的档位,不约束用户设置** | | 外部页注入 | `browserCommentPreload.ts` | 系统字体族 | 注入他人网页,不强加 Inter | | 手机 WebView HTML 生成器 | `selectableMarkdownHtml.ts` 等 | CSS 语法字面量 | 手机守卫已自登记盲区,值仍须守本阶梯 | | 紧凑模式派生值 | `globals.css` `.chat-rail-compact` 段 | calc / -1px 派生 | 机制本体 | ### 4.6 排版 non-goals(已知且明确不治理) - **line-height 档位统一**:桌面存在 `leading-[1.45]`/`leading-[1.55]` 等微调档与无单位/px 混轨,本轮不收敛。 - **但有一条例外在 #1553 已治理完毕**:`text-` 与**固定 px 行高**(`leading-[Mpx]`)并存属于**功能缺陷**而非风格漂移——`text-` 会响应用户「外观 → UI 字号」设置,固定 px 行框不跟随,**用户放大字号时文字会裁切/重叠**。该组合一律改为无单位比例(比例 = 原 px ÷ 字号),默认字号下渲染不变而放大时正确。#1553 把当时全部 **38 处**清零(14 处为字号归一新引入、24 处先于其存在);门禁为 `typographyDiscipline.test.ts` 的 `fixed-leading` 规则,**按 `className`/`cn(...)` 整块区域跨行判定**(只比对同一行会漏掉 `cn()` 分行写法,#1553 实际漏过一处)。 - 语义类 `text-xs/sm/base/lg` → `text-` 的机械统一(等值改写零收益,收编即可)。 - letter-spacing 与 font-family 治理。 - 原生层排版(macOS agent-island helper 等 Swift/原生 UI)不在本白名单域。 ### 4.7 移动端文字规范(交叉引用) `apps/mobile/docs/mobile-design-guide.md §3`「文字规范速查」是移动端正本:**五档中性字色、浅色字不配粗字重、11 档字号、每种文字风格必须配行高**——由 `typographyTokenDiscipline.test.ts` 守护;角色是否选对靠 review。桌面/移动端排版规则的双向同步是硬要求。 --- ## 5. 主题系统与 Token 架构(§10) ### 5.1 双模式交付门槛(硬性) 每个 UI **必须同时实现 Light 与 Dark**,只做一种视为未完成;颜色一律走语义 token,禁止只适配一种模式的硬编码或条件补丁。但——**双模式"实机目检"不是硬门槛**:能目检更好,做不到时如实写明哪种模式未验证;**"复用了 themed 样式"不得当成"双模式已验证"**。 ### 5.2 架构:VSCode 式 ColorRegistry + theme-override 源码目录 `apps/desktop/src/renderer/themes/`: | 文件 | 职责 | | --- | --- | | `color-registry.ts` | `ColorRegistry` 单例 + `registerColor(id, defaults, description)` | | `colors.ts` | 注册所有 token(语义槽优先、别名与单例在后)——**是活清单** | | `theme-service.ts` | `applyTheme(theme)` 把所有 token 序列化进 `:root{}` 并注入 `