Files
Jyotisha/docs/tasks/TASK-cend-ui-claude-alignment-20260916.md
T
Jesse_ChenandClaude Opus 5 a2c1f1fbda docs(tasks): C 端界面向 claude.ai 产品界面对齐任务书(三轮串行)
根因:CLAUDE_DESIGN.md 扒的是 claude.com 营销官网,它自己在 Known Gaps
里写明 claude.ai 产品界面不在范围内,而 DESIGN.md 把它当成了产品界面的
实现契约。于是营销页语汇(hero band、3-up feature card)进了聊天空状态,
拉丁 display face 的衬线规则套到中文界面变成了宋体。

R1 字体 + 强调色 + chrome 瘦身(BUG-737/738)
R2 空状态重做
R3 侧栏拍平 + 入口收敛 + 去头像

三轮都动 globals.css,不得并行。附原型图链接。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
2026-09-16 03:42:28 +00:00

324 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TASK · C 端界面向 claude.ai 产品界面对齐(2026-09-16
## 基线
- 基线 commit`origin/staging` = `5094fd26207e587037fa366dbe186dd313904236`2026-09-16
- 原型图(改后形态,含空状态 / 会话中 / 移动端三套画面与明暗两套):
https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b
- 本单分三轮串行:R1 → R2 → R3。三轮都动 `frontend/src/app/globals.css`**不得并行**。
- BUG 编号起点:**BUG-737**(开工时以 `docs/BUG_HISTORY.md` 实际最大号为准,当前最大为 BUG-736)
---
## 事故实证
行号按 `origin/staging``5094fd26`。符号名在括号里,行号漂移时按符号定位。
### E1 · `--font-display` 里没有一个字体是能加载的(→ 中文标题全站落到宋体)
`frontend/src/app/globals.css:100``:root``--font-display`):
```css
--font-display: "Tiempos Headline", "Songti SC", STSong, "Noto Serif CJK SC", Georgia, serif;
```
同文件 `:101``--font-body`)以 `StyreneB` 开头。
实证三条:
1. `git grep "@font-face" origin/staging -- frontend/src` 无命中。
2. `frontend/public/` 下只有 `data/``jyotish-logo.png`,没有字体文件。
3. `frontend/src/app/layout.tsx:2-13` 只通过 `next/font/local` 加载了一个 `InterVariable-latin.woff2`,变量名 `--font-inter`。注释写明「publish image cannot download fonts at build time」。
也就是说 Tiempos Headline 与 StyreneB 这两个 Anthropic 授权字体**从来没有被加载过**,它们在栈里只起占位作用。真实用户拿到的是回退结果:
| 用户环境 | `--font-display` 实际命中 |
|---|---|
| macOS / iOS | `Songti SC`(宋体) |
| Windows | `Georgia` 覆盖拉丁,中文掉到浏览器默认 serif,即 SimSun |
| Android | 多数机型无 `Noto Serif CJK SC`,同样落到系统默认 serif |
`--font-display` 的使用面(`git grep "var(--font-display)" -- frontend/src/app/globals.css` 共 20 处)覆盖:品牌字 `.brand-row:686`、顶栏会话标题 `.chat-header strong:846`、首页大标题 `.starter-hero h1`、入口卡标题、主题卡正文 `.starter-content span`、**助手回答正文里的 `h2/h3`** `.message-markdown h2, h3:1372`、所有弹窗标题 `.account-modal h2:1699`、模型选择器标题 `.model-selector-title:1604`、登录页 `.auth-story h2:1766`
`--font-body` 里的 `StyreneB` 同样从不命中,但它后面紧跟 `var(--font-inter, Inter)`,回退无害,属于同一类死配置。
### E2 · 亮色与暗色的强调色不同源
| 位置 | 亮色 | 暗色 |
|---|---|---|
| `globals.css:65``:root --color-action` | `#85432f` 深棕 | — |
| `globals.css:175``@media (prefers-color-scheme: dark)` | — | `#d78064` |
| `globals.css:246``:root[data-theme="dark"]` | — | `#d78064` |
暗色块自带注释(`:173``:244`):「`#85432f` is too dark to read as an action on」——暗色已经自己走到 coral 了,亮色还停在深棕。
**改色时必须同步的四处亮色声明**,漏一处就会出现「按钮变了、焦点环没变」:
| 行 | 声明 | 说明 |
|---|---|---|
| `globals.css:16` | `--color-ring: #85432f;` | 在 `@theme inline` 块里**硬编码**,不跟随 `:root`Tailwind 的 `ring-*` 工具类走这里 |
| `globals.css:65-69` | `--color-action` / `-soft` / `-hover` / `--color-focus` / `--color-action-on-dark` | 主声明 |
| `globals.css:133` | `--report-accent: #85432f;` | 报告纸面调色板,见决策记录 D3 |
| `globals.css:4358-4375` | 第四个 `:root` 亮色块(`--color-action` / `-hover` / `--color-focus` / `--report-accent` | 与 `:65` 同值,必须同步 |
`@theme inline` 的语义:`:7``:25``--color-action: var(--color-action)` 一类声明是 Tailwind v4 的 re-export 写法,**不是自引用 bug**,不要「修」它。但 `:16``--color-ring` 是写死的十六进制,必须手动跟改。
### E3 · 输入框下方常驻一条 44px 的横栏
`globals.css:1667``.composer-footer`):`width: min(760px, 100%); min-height: 44px;`
`frontend/src/app/page.tsx:1815-1816``.composer-footer` 里放 `ModelSelector` + `ComposerCharacterRemaining`
字数统计本身已经有 `characterRemainingVisible()` 做条件显示,但**容器高度是常驻的**,所以没到字数上限时这 44px 是空的。
### E4 · 顶栏 68px,常驻内部术语
`globals.css:852``.chat-panel`):`grid-template-rows: 68px minmax(0, 1fr) auto;`
`page.tsx:1627``<span className="chat-header-subtitle">分析对象:{...}</span>`
顶栏固定 68px + `--composer-reserve: 148px``globals.css:129`),每屏固定被吃掉 216px。「分析对象」是内部术语。
### E5 · 空状态是一张营销落地页
`frontend/src/components/starter-home.tsx:47` 起,`.starter-list.starter-workbench` 内依次是:
| 段 | 选择器 | 尺寸 |
|---|---|---|
| hero 卡 | `.starter-hero``globals.css:1910` 起) | `padding: 40px``h1` `clamp(34px, 5vw, 52px)` |
| 两张产品入口大卡 | `.product-entrypoints``:1997` 起) | 每张 `min-height: 132px` |
| 「从一个主题开始」标题 + 主题卡网格 | `.starter-themes` / `.starter-theme-card` | 3 列,每张 `min-height: 156px` |
`.welcome``:1890` 被重设为 `width: min(1040px, 100%)`。输入框在 `page.tsx:1803``composer-wrap-starter`)位于这些之后,即**首屏 800px 以上内容压在输入框之上**,且输入框宽 1040px 而上方内容也是 1040px。
对照 claude.ai 的产品空状态:一行问候 + 紧随其下的输入框(垂直居中)+ 一排小 chip,总高约 300px。主动作是「打字」,它应该是视觉焦点。
### E6 · 侧栏比 claude.ai 多一层折叠,星盘有两个入口
`frontend/src/components/app-sidebar.tsx`
| 行 | 内容 |
|---|---|
| `:266-297` | `SidebarGroup.chart-nav`「星盘列表」+ 加号,chip 列表 |
| `:299-314` | `SidebarGroup.session-nav` 内嵌 `<details>`「收藏对话」 |
| `:316-340` | `SidebarGroup.session-nav` 内嵌 `<details>`「历史对话」 |
| `:389` | 账户菜单项「星盘资料」 |
`page.tsx:1598-1601``onSelectChart` / `onAddChart` / `onOpenChartLibrary` **三个回调打开的是同一个弹窗** `openAccountDialog("chart-library")`。即侧栏「星盘列表」与账户菜单「星盘资料」是同一目的地的两个入口。
### E7 · 每条助手消息带头像
`globals.css:1074``.agent-avatar`):32px 圆形 `url("/jyotish-logo.png")``:2411` 在会话态收到 30px。claude.ai 的产品界面助手消息没有逐条头像,靠留白与行宽区分发言人。
---
## 根因
1. **E1/E2 的共同根因**`frontend/CLAUDE_DESIGN.md` 扒的是 **claude.com 营销官网**,该文件自己在 `## Known Gaps` 末尾写明「The actual Claude product surface (claude.ai chat interface) … adds many product-specific components (chat bubbles, message tools, file upload chips, conversation history sidebar) that are out of scope for this marketing-surface document」。`frontend/DESIGN.md:3` 把这份营销站文档当成了产品界面的实现契约,于是:
- 照抄了 Copernicus/Tiempos 的衬线 display 规则,但那是**拉丁 display face** 的规则,套到中文界面就变成宋体;
- 照抄了 hero band、3-up feature card、`ArrowUpRight` 链接箭头等**营销页语汇**,放进了一个聊天产品的空状态(E5)。
2. **E1 的直接根因**:字体栈里写了两个永远不会加载的授权字体名,没有任何测试或构建检查拦住「声明了一个加载不到的 family」。DESIGN.md §3「Font stacks」把它当成既定事实记录,所以后续轮次也没人质疑。
3. **E3/E4 的根因**:把「随时可调」的开发者控件(模型选择器、字数)与「用户身份上下文」(分析对象)放进了常驻 chrome,而不是放进用得到它们的那一刻。
---
## 决策记录
产品负责人 2026-09-16 在会话中的授权:
- **D1|三轮全做。** 不是只修字体就收工。R1 → R2 → R3 串行,每轮独立可验收、可单独上 staging。
- **D2|亮色强调色换成 Claude coral `#cc785c`。** 产品明确选了「换成 Claude coral」而不是「保留深棕」或「取中间值」。配套的 hover/soft/on-dark/ring/focus 由执行方按对比度要求推导,见 T1.2。
- 本条**推翻** `frontend/DESIGN.md` §2 调色板表里 `--color-action` 一行的既定值,以及 `## Where the action color appears` 一节对深棕的描述。DESIGN.md 必须在同一提交内改掉,不得留旧值。
- **D3|报告纸面的 `--report-accent` 不跟随,保持 `#85432f`。** 理由:报告是打印文档表面,coral 在米色纸上读起来发飘;DESIGN.md §2 已经允许报告走独立的窄调色板。
- 本条**推翻** DESIGN.md §2 里「Report accent … same hue as the action color」这句话,必须改写成「报告强调色与应用强调色**不同源**,刻意保留深棕」。
- **D4|中文永远不用衬线。** `--font-display` 改造后,任何 CJK 字形都不得落到 serif。拉丁字形是否保留衬线见 T1.1 的让步顺序。
- **D5|两个产品入口(今日星语 / 生时校正)降级为输入框下方的 pill,不删除。** 入口本身保留,只降权。
- **D6|星盘入口收敛到侧栏一处**,账户菜单里的「星盘资料」项删除。依据产品既有口径「多余入口宁可删除也不修」。
- **D7|助手消息头像删除。** `/jyotish-logo.png` 资源保留(登录页与 favicon 仍在用),只是不再逐条渲染。
---
## 硬红线
1. `./node_modules/.bin/tsc --noEmit` 0 错;`npm run lint` **0 error**
2. 测试总数不得低于开工时在 `origin/staging` 的实测值。改任何既有断言必须在进度记录里写「原值 / 新值 / 原因」三栏。
3. `next build``/` 保持 `○ Static`
4. 首屏 gzipR1 允许 ±2%;**R2 预期是负增长**(删掉 `.starter-hero` / `.product-entrypoints` / `.starter-theme-*` 一大片 CSS 与 JSX),若反而增长必须在进度记录里给出原因。
5. `frontend/src/app/page.tsx``AGENTS.md` §6 的**现行**门禁:`Home()``useState` / `useRef` 数**不得增长**`frontend/tests/home-shell-growth-contract.test.ts` 执行),行数是粗护栏(开工实测基线 + 150)。R2 会从 page.tsx 删掉空状态相关的 props 传递,三项都应下降;新逻辑一律进 `frontend/src/hooks/` / `frontend/src/lib/` / 组件。
6. 一律复用 `ChatComposer`,**不得手写第二个输入框**;模型选择器移进输入框是改 `ChatComposer` 的内部结构,不是新开一个 composer。
7. 揭幕后不得出现 spinner / 骨架 /「正在加载」(流式生成中除外)。
8. 改 UI 的**同一提交**内更新 `frontend/DESIGN.md`;新文案先对照 `frontend/docs/VOICE.md`
9. 不得顺手升级依赖、不得顺手修不在本单里的 warning;发现了写进 `BLOCKED.md` 或进度记录。
10. 本轮不改数据库结构,不得顺带动迁移。
11. 不得新增任何 `next/font/google` 调用。`layout.tsx:8-9` 的注释写明发布镜像在构建时下载不到字体,必须 vendor 到 `frontend/src/app/fonts/`
---
## 任务分解
### R1 · 字体、强调色、chrome 瘦身
分支 `codex/cend-ui-r1-20260916`。这一轮改动集中在 `globals.css` 的 token 区与 `ChatComposer`,风险最低,先上。
**注意**`codex/settings-dialog-size-and-nav-20260916` 的 worktree 里有未提交的 `globals.css` 改动,落在 1756–1953 与 5108 附近的弹窗区,与本轮目标行不重叠。开工前 `git fetch` 确认它是否已合入,若已合入则以新 staging 为基线重新定位行号。
#### T1.1 · 修掉字体栈(BUG-737
首选做法:vendor 一个 OFL 许可的拉丁衬线 woff2(建议 Newsreader 或 EB Garamond 的 latin subset),放 `frontend/src/app/fonts/`,按 `layout.tsx` 里 Inter 的同一套 `next/font/local` 写法加载,然后:
```css
--font-display: var(--font-<新衬线>), "PingFang SC", "Microsoft YaHei", var(--font-inter, Inter), sans-serif;
--font-body: var(--font-inter, Inter), -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
```
关键点是**拉丁衬线放第一位、CJK 无衬线紧随其后**:衬线 face 不含 CJK 字形,汉字会自动落到后面的无衬线,拉丁字母则拿到衬线。原型图 https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b 的标题就是这套栈的实际渲染结果,可直接对照。
同时从 `--font-body` 里删掉 `StyreneB`(死配置)。
**验收标准**
- `git grep -n "Tiempos\|StyreneB\|Songti\|STSong" frontend/src` 无命中(`frontend/CLAUDE_DESIGN.md` 是上游参照,不在此列)。
- 新增字体文件有 OFL/SIL 许可证文件同目录存放,体积写进进度记录。
- 新增一条 token 合同测试:断言 `--font-display``--font-body` 里出现的每一个带引号 family,要么在 `layout.tsx` 里有对应的 `next/font/local` 声明,要么在系统字体白名单里(`-apple-system` / `PingFang SC` / `Microsoft YaHei` / 通用族名)。这条是 BUG-737 的防复发措施。
- `docs/BUG_HISTORY.md` 新增 BUG-737,状态 `resolved`,记录现象、触发条件、根因、修复、验证、防复发。
- `next build``/``○ Static`;首屏 gzip 变化写进进度记录。
#### T1.2 · 亮色强调色换 coralBUG-738
把 E2 表里列的**四处亮色声明**一并改到以 `#cc785c` 为基准的一组值:
| token | 新值 | 约束 |
|---|---|---|
| `--color-action` | `#cc785c` | D2 指定 |
| `--color-action-hover` | 推导(建议 `#a9583e` | 按下态必须明显深于默认态 |
| `--color-action-soft` | 推导(建议 `#f7ece5` | 作为底色时,其上的 `--color-ink` 对比度 ≥ 7:1 |
| `--color-focus` | 与 `--color-action` 同值 | |
| `--color-action-on-dark` | 保持或按需微调 | |
| `globals.css:16` `--color-ring` | 跟改 | **易漏**,在 `@theme inline` 里硬编码 |
| `globals.css:133` / `:4375` `--report-accent` | **保持 `#85432f`** | D3 |
**验收标准**
- `git grep -n "#85432f" frontend/src/app/globals.css` 只剩 `--report-accent` 的两处(`:133` 与第四个 `:root` 块内)。
- 亮色下,coral 作为文字色放在 `--color-canvas``#fbfaf7`)上的对比度 ≥ 4.5:1;作为 `--color-surface-dark` 上的按钮底色时,其上 `--color-on-dark` 文字对比度 ≥ 4.5:1。把实测数值列进进度记录。
- 暗色两块(`:175``:246`)保持 `#d78064` 不动,两块 token 集合仍然完全一致(`class-name-definition-contract` 的同胞测试会查)。
- `frontend/DESIGN.md` §2 调色板表与 `## Where the action color appears` 一节改到新值;`## Dark theme` 一节补一句两套强调色现在同源。
- `docs/BUG_HISTORY.md` 新增 BUG-738(亮暗强调色不同源 + `--color-ring` 硬编码不跟随)。
#### T1.3 · 模型选择器与对象选择移进输入框,删掉 `.composer-footer`
- `ChatComposer``frontend/src/components/chat-composer.tsx`)内部加一条底排:左侧放模型选择器与「分析对象」选择器,右侧是发送 / 停止按钮。
- `page.tsx:1815-1816``.composer-footer` 整块删除;`globals.css:1667``.composer-footer` 规则与 `:571-572``:2436` 等相关覆盖一并清理。
- 字数提示改成只在 `characterRemainingVisible()` 为真时插入 DOM,不再占常驻高度。
- `--composer-reserve``globals.css:129``:1825`)按新高度重算。
**验收标准**
- 输入框为空且未接近字数上限时,输入框下方**没有任何常驻元素**。
- 模型选择器与对象选择器的键盘可达性与 `aria-label` 不弱于改前;`npm test` 里既有的 composer / model-selector 测试全绿。
- `ChatComposer` 仍是唯一的聊天输入框实现,校正面(自带 `value` 的那条路径)不受影响。
#### T1.4 · 顶栏瘦身
- `.chat-panel``grid-template-rows` 首行 68px → 46px`globals.css:852``:853`)。
- 删掉 `page.tsx:1627` 的「分析对象:xxx」副标题,该信息由 T1.3 的对象选择器承载。
- 顶栏只留:侧栏折叠按钮、会话标题、点数。空状态下不显示会话标题。
**验收标准**
- 会话标题在窄屏下仍然省略号截断、不换行、不溢出。
- 空状态顶栏只有折叠按钮与点数。
- 点数按钮的 `aria-label` 保持原样(含余额数字与「会员与充值」)。
---
### R2 · 空状态重做
分支 `codex/cend-ui-r2-20260916`,基线是 R1 合入后的 staging。
#### T2.1 · 按原型重排空状态
`starter-home.tsx` 重写为:问候一行 → 输入框(垂直居中)→ 两枚入口 pill(今日星语 / 生时校正,D5)→ 一排建议 chip → 一行边界说明。删掉 `.starter-hero` / `.product-entrypoints` / `.starter-themes` / `.starter-theme-card` / `.starter-workbench` 的 JSX 与 CSS。
**验收标准**
- 在 1440×900 与 1280×720 两个视口下,问候与输入框**同时**在首屏内可见,无需滚动。
- 空状态下输入框宽度与会话态一致(都走 `--session-column-width`),不再是 1040px 对 760px 两套。
- 现有的空状态相关测试(onboarding 分步、`productEntrypointsDisabled` 禁用态、`rectificationEntrySummary` 三种文案分支)全部保留并通过;文案按 VOICE.md 重写的,在进度记录里列「原值 / 新值 / 原因」。
- 首屏 gzip 相对 R1 后的基线应下降,数值写进进度记录。
#### T2.2 · 文案去内部术语
对照 `frontend/docs/VOICE.md` 复核空状态全部可见文案。已知要处理的:「分析对象」(R1 已删)、入口 pill 的说明文案、chip 文案。
**验收标准**:新文案逐条在进度记录里对照 VOICE.md 的五条原则给出依据。
---
### R3 · 侧栏拍平、入口收敛、去头像
分支 `codex/cend-ui-r3-20260916`,基线是 R2 合入后的 staging。
#### T3.1 · 侧栏拍平
`app-sidebar.tsx`
- 顶部三个平铺导航项:新建对话(coral 文字,保持 DESIGN.md 既定的「不做实心块」)、我的报告、星盘资料。
- 删掉 `:266-297` 的「星盘列表」分组与 `:299-340` 的两个 `<details>`
- 一条平铺的「最近」列表,收藏的会话用星标置顶(`session.pinned` 仍是排序依据,不再是独立分组)。
**验收标准**
- 收藏与归档功能不丢:会话行的「更多操作」菜单里收藏 / 重命名 / 分享 / 归档 / 删除五项全在。
- 归档视图(`sessionControls.showingArchived`)仍可进入。
- 侧栏折叠态(`isCollapsedDesktop`)与移动端抽屉行为不变;`viewport-breakpoint-contract.test.ts` 全绿。
- 键盘可达性不弱于改前:`handleExpandHistory` 的焦点转移逻辑要么保留、要么有等价替代。
#### T3.2 · 星盘入口收敛(D6
删掉 `app-sidebar.tsx:389` 账户菜单里的「星盘资料」项,只留侧栏一处。
**验收标准**`git grep -n "chart-library" frontend/src/app/page.tsx``openAccountDialog("chart-library")` 的调用点从三处减到一处(或两处,若加号与列表项仍分开);账户菜单项数减一。
#### T3.3 · 删掉助手消息头像(D7)
`globals.css:1074``.agent-avatar``:2408-2415` 的会话态覆盖,以及渲染它的组件节点。行间距按原型调整,靠留白区分发言人。
**验收标准**`/jyotish-logo.png` 仍被登录页与 favicon 引用(不要删文件);助手与用户消息在视觉上仍能一眼区分(用户是 coral 淡底气泡,助手是纯文本)。
---
## 让步顺序
按此顺序退让,每退一步都要在进度记录里写明退到了哪一级、为什么:
1. **T1.1 的拉丁衬线**:若 vendor 字体导致首屏 gzip 超出预算,或找不到合规的 latin subset —— 退到「拉丁也走 Inter」,即 `--font-display: var(--font-inter, Inter), "PingFang SC", …`,靠字重、字号、字距(`letter-spacing: -.02em`)做层级。**中文不用衬线这条(D4)不可退让。**
2. **T1.2 的配套色**:若推导出的 `--color-action-hover``--color-action-soft` 过不了对比度要求 —— 调这两个派生值,`--color-action` 本身的 `#cc785c` 不可退让(D2)。
3. **T1.3 的对象选择器**:若「分析对象」选择器在输入框内实现成本过高 —— 退到只把模型选择器移进去,分析对象信息改由会话标题旁的一枚静默 chip 承载;**但 `.composer-footer` 这条常驻横栏必须删掉**。
4. **T2.1 的垂直居中**:若在小高度视口(`max-height: 640px`)下居中导致内容被裁 —— 退到顶部对齐 + 上方留白,首屏可见性要求不变。
5. **T3.1 的收藏置顶**:若星标置顶与现有排序逻辑冲突 —— 退到保留一个「收藏」分组,但**必须去掉 `<details>` 折叠**,改成常驻小标题。
6. 任何一轮若被门禁或环境卡住,**先把已完成的子任务单独提交并推 staging**,未完成项写进 `BLOCKED.md`,不要整轮压着不交。
---
## 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/cend-ui-r1-20260916 .worktrees/cend-ui-r1-20260916 origin/staging
cd .worktrees/cend-ui-r1-20260916/frontend
npm ci
./node_modules/.bin/tsc --noEmit
npm run lint
npm test 2>&1 | tail -30 # 记下测试总数,这是红线 2 的基线
npm run build # 记下 / 的渲染标记与首屏 gzip,这是红线 3、4 的基线
```
开工时核对 `docs/BUG_HISTORY.md` 的当前最大编号,若已超过 BUG-736 则顺延。
R2、R3 各自重复上述流程,基线换成前一轮合入后的 `origin/staging`
---
## 环境缺口
本单的诊断全部来自读代码与设计文档,**没有真机截图**:本会话无登录态、无 Chrome。以下验收项必须落到 `docs/testing/` 的人工清单,不得在进度记录里写成「通过」:
- 中文标题在 macOS / Windows / Android 三端的实际字形(T1.1 的核心收益)。
- coral 在真实屏幕上的观感与明暗切换(T1.2)。
- 空状态在真机小屏上的首屏可见性(T2.1)。
- 侧栏抽屉在移动端的手势与焦点行为(T3.1)。