# PROGRESS · C 端界面对齐 R2(空状态重做) 任务书:`docs/tasks/TASK-cend-ui-claude-alignment-20260916.md` 的 R2 执行方式:**直接执行模式** 分支:`codex/cend-ui-r2-20260916` 基线:`origin/staging` = `66d9f59d`(R1 合入后) BUG 段:不占用(产品改造,非缺陷) --- ## 一处与任务书不符的现状,先说明 任务书 E5 写的是「hero 卡 + 两张入口大卡 + 『从一个主题开始』标题 + 3 列主题卡」。实际在 `66d9f59d` 上,**主题卡网格早已下线**:`starter-home.tsx` 只剩 hero 卡 + 两张入口卡 + 错误行,`globals.css` 里 `.starter-theme-*` 一条不剩。任务书写于旧观察,此处以代码为准,工作量相应变小。 「输入框被压在约 800px 内容之下」这条仍然成立(hero 卡 padding 40px + `h1` clamp 到 52px,加两张 `min-height: 228px` 的卡)。 --- ## T2.1 · 按原型重排空状态 ### 结构 拆成两个组件,分别落在两个插槽: | 组件 | 位置 | 内容 | |---|---|---| | `StarterGreeting` | 对话区内,`align-content: end` 底对齐 | 招呼语一行 + 问句 `clamp(26px, 3.2vw, 34px)` | | (不动)`ChatComposer` | `.composer-wrap`,**与会话态同一个 DOM 位置** | — | | `StarterEntries` | `.composer-wrap` 内、composer **之后** | 两枚入口 pill + 合并提示行 + 边界句 | **为什么不把 composer 搬进空态区**:那会让它在空态与会话态之间挂载到两个不同位置,首条消息落地时 React 会卸载重建——正在输入的草稿、焦点、排队卡都会受影响。现在 composer 一行没动,只是它上下的东西换了。 ### 与会话态同宽 `.welcome` 与 `.composer-wrap-starter .composer` 都改为 `min(var(--session-column-width), 100%)`。此前空态 composer 是 1040px、会话态 760px,**发出第一条消息的瞬间输入框会当场变窄一截**。这是原断言没覆盖到的问题,新断言把它锁住了。 ### 验收对照 | 任务书验收标准 | 结果 | |---|---| | 1440×900 / 1280×720 下问候与输入框同屏可见 | ⚠️ **环境缺口**,无 Chrome 无法实测。静态推算:问候块约 90px + composer 约 96px + pill 区约 84px + 内边距,合计约 330px,远小于两个视口的可用高度,但必须真机确认 | | 空态输入框宽度与会话态一致 | ✅ 两处都是 `--session-column-width`,并新增断言 | | onboarding 分步 / `productEntrypointsDisabled` / `rectificationEntrySummary` 三分支保留 | ✅ 见下 | | 首屏 gzip 相对 R1 应下降 | ✅ **41,096 → 41,044 字节(−0.13%)** | **三分支这条我第一版做错了**:我把校正入口的文案从三个分支收成了两个,漏掉了 `rectificationCardAction === "restart"`。是 `rectification-agentic-entry` 的断言打红才发现的,已补回三分支(入口标签本来就由服务端 entry summary 驱动,不读会话列表)。 ### 一处内容取舍 `dailyStarlanguage.card.action`(今日星语卡片上的「行动建议」行)在 pill 形态下没有位置,对应的 `dailyStarlanguageAction` 变量已删除。卡片降级为 pill 是任务书 D5 授权的;这条行动建议在点进去之后的正文里仍然完整。若产品认为它必须留在首页,需要另立单——现在的 pill 只能承载一个词。 --- ## T2.2 · 文案 对照 `frontend/docs/VOICE.md`: | 位置 | 文案 | 依据 | |---|---|---| | 入口 pill | 「今日星语」/「每日运势」、「生时校正」 | 原样保留,未改 | | 合并提示行 | `{今日趋势} · {校正提示}` | 原则 3「解释为什么问,一句就够」;两张卡各自的描述段合成一行 | | 校正提示(首次) | 「不确定出生时间时,用记得住的经历一步步缩小范围」 | 原值「不确定准确出生时间时,可通过已经发生的人生事件逐步缩小范围。」→ 去掉公文腔,对照 VOICE 好/坏表第 1 行 | | 校正提示(可重做) | 「上次已经校正完,可以拿最新资料再来一次」 | 原值「上一次校正已经完成,可以基于最新资料再次校正。」→ 同上 | | 校正提示(有未完成) | 「未完成的那次可以从左侧继续」 | 原值「新建一段独立校正;未完成的记录仍可从左侧历史会话继续。」→ 缩短,语义不变 | | 边界句 | 「出生时间不足以支持的判断我会说明,不会补造具体分钟。」 | 原值「不支持的个人判断会明确说明,不会补造出生时间。」→ 准确性边界语义完全保留,改为第一人称 | --- ## 收尾验收 | 红线 | 结果 | |---|---| | 1 · `tsc` 0 错 / `lint` 0 error | ✅ 118 warning,与基线同 | | 2 · 测试总数 | ✅ tests **3350**(与 R1 后持平),pass 3304,fail 31,失败清单与基线 `comm` 双向比对**零差异** | | 3 · `/` 保持 `○ Static` | ✅ `/chart` `○`、`/ephemeris` `○`、`/reports` `ƒ` 均不变 | | 4 · gzip 预期负增长 | ✅ −52 字节(−0.13%)。删掉约 108 行 starter CSS,同时新增了 pill 与问候的规则,净减不大 | | 5 · `page.tsx` 增长门禁 | ✅ 净删(删掉一个派生变量与十几行 props),`home-shell-growth-contract` 绿 | | 6 · 一律 `ChatComposer` | ✅ composer 一行未动 | | 7 · 无 spinner / 骨架 | ✅ `data-opening` 仍只换 `cursor: progress` | | 10 · 同提交更新 `DESIGN.md` | ✅ 「Product entrypoint card」整节重写为「Starter home」;顺带删掉「Suggestion card」死文档(它描述的主题卡样式正是本轮删除的 CSS) | | 11 · 不升依赖、不留新 warning | ✅ | ### 两处我自己造成的问题,记在这里 **一、CSS 规则删除器切坏了一条注释。** 我用脚本按类名批量删 starter 规则时,对逗号分隔的选择器列表做了裁剪。有一条规则前面带注释: ``` /* The card is opening a Case: static copy and a progress cursor, no spinner (...). */ .product-entrypoint-card[data-opening="true"], .product-entrypoint-card[data-opening="true"] .product-entrypoint-hitarea { cursor: progress; } ``` 注释里那个逗号被当成了选择器分隔符,裁剪时把 `*/` 一起丢掉了。结果一个未闭合注释吞掉了后面 **2762 个字符**,连带 `.session-messages-loading`、`.rectification-entry-error`、`.message-thinking` 等四条无关规则在 `class-name-definition-contract` 眼里「不存在」。已删除该残片,并复核全文注释配平(69 / 69)、扫描所有含花括号的注释(0 条)、确认 `git diff` 里被删的非 starter 选择器只有一条(是我有意改的 `.conversation.is-empty`)。 **二、第一版差点误删共享的按压动效规则。** 那条 `:active` 规则的选择器列表里同时有 `.starter-list > button`(死)和 `.new-chat` / `.button-primary` / `.composer button` 等十几个活选择器。第一版脚本按「选择器命中死类名就删整条」,会把整个按压动效干掉。已改成「逐个选择器判定,全死才删整条,否则只摘掉死的那个」,并复核该规则现在仍有 13 个活选择器。 ### 一条性能问题,顺手记下 `consultation-entrypoint` 里有一条断言写成: ```js const start = source.indexOf('