Files
Jyotisha/docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.md
T
Jesse_ChenandClaude Opus 5 3fd74daf34 docs(tasks): C 端次级页面对齐任务书(R4–R8)
星盘/星历/报告三个页面各是脱离 app 外壳的独立全屏页,侧栏整个消失,
且三家各写了一套一样的 shell/topbar/hero 骨架——与上一单空状态同根因。

R4 抽只读导航外壳(前置)→ R5 星盘+星历、R6 报告
R7 生时校正、R8 设置+充值 可并行

设置单已于 111b4a84 合入,R8 阻塞解除。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
2026-09-16 04:18:37 +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` = `111b4a8455b5e3d6ae7b4ea759ba35492fc806cc`2026-09-16BUG-698 设置弹窗 vh 基线之后)
- 原型图(十套画面,本单覆盖后七套):
https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b
- **本单是 `TASK-cend-ui-claude-alignment-20260916.md` 的续篇。** 那一份改对话主链路(R1 字体与强调色、R2 空状态、R3 侧栏),本份改五个次级面。
- BUG 编号起点:**BUG-739**(当前最大 BUG-738;本单四轮均为产品改造,**默认不占 BUG 号**,只有实现中发现真实缺陷才从 739 起顺延)
### 轮次依赖
```
brief#1 R1(字体 + coral token) ← 软前置,先落地可避免两次视觉返工
├─ R4 次级页外壳 ──┬─ R5 星盘 + 星历
│ └─ R6 报告中心 + 报告阅读
├─ R7 生时校正 (文件不重叠,可与 R4/R5/R6 并行)
└─ R8 设置 + 充值 (文件不重叠,可并行;基线已解锁,见下)
```
R8 原本被 `codex/settings-dialog-size-and-nav-20260916` 挡着,该单已于 `111b4a84` 合入,**阻塞解除**。
`globals.css` 四轮都要动,但落在互不重叠的区段:R4/R5 在 `.chart-page-*` / `.ephemeris-*`R6 在 `.report-center-*` / `.personal-report-*`R7 在 26142800 的 `.rectification-workspace*`R8 在 17571960 的 `.settings-*` / `.membership-*`。**同一轮内不得越界改别轮的区段**,越界即产生冲突。
---
## 事故实证
行号按 `origin/staging``111b4a84`。符号名在括号里,行号漂移时按符号定位。
### E8 · 三个次级页各自是脱离 app 外壳的独立全屏页
| 路由 | 入口文件 | 外壳写法 |
|---|---|---|
| `/chart` | `frontend/src/app/chart/page.tsx` | `<ChartPageRoute />`,仅 `import "../site-styles"` |
| `/ephemeris` | `frontend/src/app/ephemeris/page.tsx` | `<EphemerisPage />`,同上 |
| `/reports` | `frontend/src/app/reports/page.tsx` | `<PersonalReportCenter />`,另加 `export const dynamic = "force-dynamic"` |
三个页面的根节点分别是 `chart-page-view.tsx:70``<main className="chart-page-shell">`)、`ephemeris-page.tsx:116``.ephemeris-shell`)、`personal-report-center.tsx:179``.report-center-shell`),彼此**各写了一套一模一样的骨架**:
```
*-shell
└ *-topbar → 只有一个「返回对话」链接
└ *-hero → h1 大标题 + 一行副文案
└ *-section → h2 + 内容
```
侧栏(`AppSidebar`)只在 `page.tsx:1560``Home()` 里渲染,挂在 `SidebarProvider``:1557`)下。所以用户从对话点进星盘页,**侧栏整个消失**,唯一的回路是那个「返回对话」链接。
这与 brief#1 的 E5(空状态是营销落地页)是同一个根因的三次复制:`*-hero``*-section` 就是 claude.com 营销页的 band 结构。
### E9 · 生时校正的盘面板有五个折叠块,最该看的数字埋在里面
`frontend/src/components/rectification-board.tsx``<details className="technique-audit">` 出现在 `:117``:132``:160``:182``:198` 共五处,分别装:盘面技法审计、换升时刻、次级候选分钟、口径披露、双轨一致性。
当前可信区间与代表分钟是整个流程里用户唯一持续关心的数字,现在它只以 `.rectification-board__clock``:81`)的形式出现在**右侧面板的标题行**,在窄屏(`.rectification-workspace.is-compact``globals.css:2630`)下该面板整体变成 overlay,默认不可见。
### E10 · 报告阅读页是一整篇 Markdown,没有任何导航
`frontend/src/components/personal-report/personal-report-page.tsx:421` 起:
```
<main className="personal-report-reader">
<ReportActions … />
<div className="personal-report-reader-body">
<PersonalReportMarkdownView markdown={state.markdown} />
</div>
</main>
```
一份完整个人报告是 9 节、20 余张盘。整篇直下,**没有目录、没有章节定位**,只能靠滚动。
**已修、不要重做**:BUG-61622 张盘叠在同一位置)与 BUG-617(滚动整篇重建卡顿)在 `docs/BUG_HISTORY.md` 里都是 `resolved`,修法是 `frontend/src/lib/report-chart-grid-rehype.ts``D\d+` / `Moon Chart` 标题收进 grid 容器。本轮**不得回退该实现**,只在其上加导航。
### E11 · 报告中心是卡片网格,状态信息靠底色区分
`personal-report-center.tsx:218``.report-center-list``.report-center-card``globals.css:3608`)。状态 `.report-center-status` 三个变体(`:3632-3635`)分别用 `--color-success-muted` / `--color-action-soft` / `--color-danger-muted` 三块底色区分。卡片网格在报告数量超过五六份后扫读成本高于行式列表。
### E12 · 套餐卡三个修饰符叠加三层描边与底色
`frontend/src/components/billing-panel.tsx:135`
```js
className={`membership-plan-card${recommended ? " membership-plan-card--recommended" : ""}${isCurrent ? " membership-plan-card--current" : ""}${highlighted ? " membership-plan-card--highlighted" : ""}`}
```
三个修饰符可同时命中(推荐 + 当前 + 从 URL 高亮),叠出的边框与底色组合没有设计定义。
### E13 · 设置弹窗内容区铺满,与导航宽度失衡
`globals.css:1759``.account-settings-shell`):`grid-template-columns: 176px minmax(0, 1fr)`,弹窗总宽 880px,内容区因此有约 690px。其中「个人资料」是一列表单,铺到 690px 后每行左右大量留白,字段与标签的关联被拉断。
`.settings-dialog-nav-item[aria-current="page"]` 的左侧强调条已在 BUG-698 里改成填充块,**本单不重复改**。)
---
## 根因
1. **E8/E11 的共同根因**:与 brief#1 的根因同源——`frontend/CLAUDE_DESIGN.md` 是 claude.com **营销官网**的设计系统,它自己在 `## Known Gaps` 写明 claude.ai 产品界面不在范围内。营销站的页面是「独立着陆页 + hero + 特性卡网格」,产品界面是「常驻外壳 + 主区域换内容」。三个次级页照抄了前者。
2. **E8 的直接根因**`AppSidebar` 的数据与回调(`sessions` / `charts` / `account` / `sessionControls` 共约 15 个)全部生长在 `Home()` 的 hook 里,没有一个可独立复用的导航外壳。于是新页面只能选择「不要侧栏」。
3. **E9/E10 的根因**:把"可核查的依据"与"用户要一直看着的结论"放进了同一种容器(折叠块 / 长正文流)。依据可以折叠,结论不能。
---
## 决策记录
产品负责人 2026-09-16 在会话中审阅原型图后确认「现在已经很完美了,请你开始吧」,即授权按原型图实施。逐条:
- **D8|三个次级页收进同一个外壳,侧栏常驻。** 删掉各页的「返回对话」链接与 hero 大标题;页名进顶栏。
- **D9|次级页的侧栏是只读导航,不带会话操作。** 重命名 / 删除 / 收藏 / 归档 / 分享只在 `/` 上提供。理由见 T4.1 的工作量论证;这是刻意的功能差,不是遗漏。
- **D10|生时校正的可信区间提成常驻条**(含收窄进度),盘面板五个折叠块收敛成「候选分钟」与「参数」两段。窄屏下常驻条必须仍然可见。
- **D11|报告阅读页加常驻目录。** 纸面调色板(`--report-paper` / `--report-rule` / `--report-accent`**保持不动**,延续 brief#1 决策 D3:报告是文档表面,不跟应用强调色走。
- **D12|报告中心卡片网格改行式列表**,状态用带色点的 chip。
- **D13|套餐卡的三个修饰符收敛成两种状态**:「当前套餐」与「推荐」。URL 高亮(`highlighted`)不再改变卡片外观,改为滚动定位。
- **D14|会员充值不恢复独立路由。** 线上已无 `/membership` 页面(`frontend/src/app/membership/` 不存在,`membershipHref` 只用于设置弹窗内的 pane 定位),维持现状。
---
## 硬红线
1. `./node_modules/.bin/tsc --noEmit` 0 错;`npm run lint` **0 error**
2. 测试总数不得低于开工时在 `origin/staging` 的实测值。改任何既有断言必须在进度记录里写「原值 / 新值 / 原因」三栏。
3. `next build`**`/` 保持 `○ Static`**。R4 给次级页加外壳**不得**把 `/` 变成动态渲染;`/chart``/ephemeris` 当前的渲染标记也必须保持不变(`/reports` 本来就是 `force-dynamic`,维持)。四个路由的渲染标记改前改后逐个列进进度记录。
4. 首屏 gzip`/` 的变化在 ±2% 内。次级页各自的首屏体积改前改后都要列出;R4 因为新增了侧栏,次级页体积**预期上升**,上升幅度要给出数字并说明。
5. `frontend/src/app/page.tsx``AGENTS.md` §6 现行门禁:`Home()``useState` / `useRef` 数**不得增长**`frontend/tests/home-shell-growth-contract.test.ts` 执行),行数是粗护栏(开工实测基线 + 150)。
6. 一律复用 `ChatComposer`;一律复用 `useConversationScrollAnchor` + `JumpToLatestButton`。不得手写第二套。
7. 揭幕后不得出现 spinner / 骨架 /「正在加载」(流式生成中除外)。次级页外壳在会话列表到达前显示**静态空态**,不是骨架屏。
8. **不得回退 BUG-698 的 `@supports (height: 1dvh)` 写法**`globals.css:5110` 起)。经典的 `height: 100vh; height: 100dvh;` 重复声明回退会被 Lightning CSS 折叠掉,`frontend/tests/viewport-unit-fallback-contract.test.ts` 会拦。新写的全高容器照同一模式办。
9. **不得回退 BUG-616/617 的报告盘面 grid 实现**`report-chart-grid-rehype.ts`)。
10. 改 UI 的**同一提交**内更新 `frontend/DESIGN.md`;新文案先对照 `frontend/docs/VOICE.md`
11. 不改数据库结构,不得顺带动迁移。不得顺手升级依赖、不得顺手修不在本单里的 warning。
12. 次级页的只读侧栏**不得**发起任何写操作接口。会话重命名 / 删除 / 归档的 RPC 在这些页面上必须不可达(D9)。
---
## 任务分解
### R4 · 次级页外壳(前置轮)
分支 `codex/cend-shell-20260916`。R5 与 R6 依赖本轮。
#### T4.1 · 抽出只读导航外壳
新建 `frontend/src/components/app-nav-rail.tsx`(名字可改,不要叫 `AppShell` 以免与 `Home()``SidebarProvider` 混淆)。它渲染:品牌字、三个导航项(新建对话 → `/`、我的报告 → `/reports`、星盘资料 → `/?settings=chart-library` 或等价入口)、一条「最近」列表、账户页脚。
数据只需两个现成的只读接口:
| 需要 | 来源 | 现状 |
|---|---|---|
| 会话列表 | `GET /api/sessions``frontend/src/app/api/sessions/route.ts` | 已存在 |
| 账户与余额 | `GET /api/account``frontend/src/app/api/account/route.ts` | 已存在 |
会话行用 `sessionHref(search, sessionId)``frontend/src/lib/chat-session-url.ts:32`)生成 `/?c=<uuid>` 链接,直接跳回 `/` 并选中该会话——**不要另造一套跳转**。
**为什么是只读(D9 的工作量论证)**`AppSidebar` 当前需要 `sessionControls` 这一整组回调(重命名、删除、收藏、归档、分享、菜单开合),它们连着 `Home()` 里的乐观更新、确认弹窗与错误回滚。把这套搬到四个路由共享,等于把 `Home()` 的会话管理层整体上提,**远超本单范围**且会撞红线 5。只读版本用一次 `GET` 就够,且次级页上并不需要这些操作。
**验收标准**
- `AppNavRail` 不 import `page.tsx` 的任何 hook,不持有会话写状态。
- 四个路由(`/chart``/ephemeris``/reports``/reports/[reportId]`)都挂上外壳,侧栏在桌面常驻、在移动端是抽屉。
- 点任一「最近」行跳到 `/?c=<uuid>` 并正确选中该会话(`parseSessionUrlQuery` 已有测试,补一条链接生成的断言即可)。
- 会话列表未到达时显示静态空态文案,**无骨架屏、无 spinner**(红线 7)。
- 未登录时外壳降级为只有品牌字与登录入口,不报错、不空白。
- 四个路由的渲染标记与 `/``○ Static` 逐个列进进度记录(红线 3)。
#### T4.2 · 删掉三套重复骨架
- `chart-page-view.tsx`:删 `.chart-page-topbar` 与其中的「返回对话」`<Link>``.chart-page-hero``<h1>星盘</h1>` 删除,出生行改为顶栏里的一枚 chip。
- `ephemeris-page.tsx`:同样删 `.ephemeris-topbar` / `.ephemeris-back``<h1>` 删除;底部 `.ephemeris-footer` 里的「去问」按钮提到顶栏右侧。
- `personal-report-center.tsx`:删 `.report-center-topbar` / `.report-center-back``<h1>个人报告</h1>` 删除;生成按钮提到顶栏右侧。
- `globals.css` 里对应的 `.chart-page-topbar` / `.chart-page-back` / `.ephemeris-topbar` / `.ephemeris-back` / `.report-center-topbar` / `.report-center-back` / 三个 `*-hero` 规则一并清理。
**验收标准**
- `git grep -n "返回对话" frontend/src` 无命中。
- 三个页面的错误态与空态(未登录、读取失败、无数据)仍然各自可达且文案不变——这些分支现在挂在 `*-shell *-message` 上(例如 `ephemeris-page.tsx:61``personal-report-center.tsx:169`),改外壳时**不要连它们一起删**。
- 页名在顶栏可见且窄屏下省略号截断。
---
### R5 · 星盘 + 星历
分支 `codex/cend-chart-eph-20260916`,基线是 R4 合入后的 staging。
#### T5.1 · 星盘页
- 五个体系 tab`CHART_VIEW_TABS`)改成顶栏下方一条下划线式 tab 行。
- 分盘 chips`.chart-page-varga-chips`)保留,视觉降一级。
- 参数从 `.chart-page-center-card``.chart-page-params-below` 收进一张统一的键值表;`view.vedic.boundary` 的边界句放在表下方,**文案不得改动**(它是准确性边界句)。
- 行星表加横向滚动容器。
**验收标准**
- 五个 tab 的按需 layer 拉取(`onNeedLayer("chara"/"western"/"qizheng"/"varga")`)行为不变,等待文案仍用 `CHART_VIEW_COPY.waitingLayer`,不得换成 spinner。
- 行星表在 375px 宽下横向滚动,页面本身不横向滚动。
- `chart-view-contract` 相关测试全绿。
#### T5.2 · 星历页
- 日期导航从三个并排按钮(前一天 / 今天 / 后一天)改成居中的 ` 日期 ` 形式,「今天」在非今日时以文字按钮出现在日期下方。
- 三个 section 统一成同一套卡片;`.ephemeris-panchanga``dl` 网格保留。
- 「就今天问一句」提到顶栏(T4.2 已做),底部 `.ephemeris-footer` 删除。
**验收标准**
- `isToday` 的禁用逻辑仍然正确:在今日时不出现「今天」按钮,而不是出现一个禁用按钮。
- `natalMissing` / `panchangaUnavailable` / `transitsUnavailable` / `eventsUnavailable` 四个降级分支文案与可达性不变。
---
### R6 · 报告中心 + 报告阅读
分支 `codex/cend-report-20260916`,基线是 R4 合入后的 staging。
#### T6.1 · 报告中心改行式列表(D12)
`.report-center-list` 从卡片网格改成单列行式列表,每行:状态 chip(带色点)+ 标题 + 元信息(日期 / 节数 / 盘数)+ 右侧操作。
**验收标准**
- 四种状态(`ready` / `generating` / `failed` 以及生成中的分节进度)都有对应行样式;`role="status"``generating` 行上保留。
- 失败行仍显示 `failureSummary ?? failureCode`,导出错误 `report-center-export-error``role="alert"` 保留。
- 空态与读取失败态(`personal-report-center.tsx:204-216`)不变。
#### T6.2 · 报告阅读页加目录(D11)
`.personal-report-reader` 右侧加一条常驻目录,从 Markdown 的 `##` 标题生成,滚动时高亮当前节。窄屏(< 860px)隐藏目录。
**验收标准**
- 目录项点击滚动到对应节;当前节高亮。
- **打印样式不受影响**`@page` 规则(`personal-report-page.tsx:422`)与 `@media print` 下目录必须隐藏。
- 纸面 token 一个不改(D11)。
- 不触碰 `report-chart-grid-rehype.ts`(红线 9);改后仍要实测一份 20 张盘以上的报告不重叠、滚动不卡——**这一条无真机就写成环境缺口,不得写「通过」**。
---
### R7 · 生时校正(可与 R4/R5/R6 并行)
分支 `codex/cend-rectification-20260916`。只动 `rectification-*` 文件与 `globals.css` 的 26142800 区段。
#### T7.1 · 可信区间提成常驻条(D10)
在对话区顶部加一条常驻条:当前区间、宽度、收窄进度、已答题数。数字**只能来自服务端投影**,不得前端口算(`frontend/docs/VOICE.md` 第 2 条)。
**验收标准**
- 窄屏(`.is-compact`)下常驻条仍然可见——这是本轮的核心收益,盘面板 overlay 关闭时也必须看得到区间。
- 边界语义不弱化:「代表分钟只是代表性候选,不是已确认的唯一出生分钟」这句在交付/采用轮仍完整出现一次(`AGENTS.md` Part B、VOICE.md 第 4 条)。
- 区间数字与盘面板标题行 `.rectification-board__clock` 的取值同源,不得出现两处不一致。
#### T7.2 · 盘面板折叠块收敛
五个 `<details className="technique-audit">``rectification-board.tsx:117/132/160/182/198`)收敛成两段:「候选分钟」(含次级候选与换升时刻)与「参数与口径」(含技法审计、披露、双轨一致性)。
**验收标准**
- 五块内容一条不丢,只是换了归属;`engineMeaningToDisplayCopy()` 的调用点全部保留。
- 候选分钟列表的 `is-changed` / `当前时间` 标记行为不变。
- 键盘可达性不弱于改前。
---
### R8 · 设置弹窗 + 会员充值(可并行;阻塞已解除)
分支 `codex/cend-settings-billing-20260916`。只动 `account-dialog-overlay.tsx``billing-panel.tsx``globals.css` 的 17571960 区段。
#### T8.1 · 设置内容区收窄(E13)
`.settings-dialog-content` 内的表单类面板加一个最大宽度(建议 420–460px),左对齐。列表类面板(星盘资料、订单)保持铺满。
**验收标准**
- 「个人资料」与「通用设置」的表单收窄;「星盘资料」列表与「订单」表格不受影响。
- 四个分区切换时弹窗尺寸不跳变——这是 BUG-554/BUG-698 的现象,**改完必须复验**,并在进度记录里说明复验方式。
- 不碰 `.settings-dialog-nav-item[aria-current="page"]`BUG-698 刚改过)。
#### T8.2 · 套餐卡状态收敛(D13)
`billing-panel.tsx:135` 的三修饰符拼接改成两种互斥状态:`is-current`(当前套餐)与 `is-recommended`(推荐)。`highlighted` 不再改变外观,改为挂载后滚动定位到该卡。
**验收标准**
- 三种历史组合(推荐 / 当前 / 推荐且当前)各有明确且不叠加的外观。
-`membershipHref("credits")` 一类带参数的入口进入时,对应 tab 与卡片仍被定位到(改成滚动定位后要真跑一次)。
- 支付关闭态(`paymentEnabled === false`)、套餐为空态、`packagesError` 三个分支文案与可达性不变。
---
## 让步顺序
1. **T4.1 的只读侧栏**:若 `GET /api/sessions` 在未登录或超时的情况下让次级页首屏变慢 —— 退到「侧栏先渲染品牌字与三个导航项,最近列表到达后再插入」,**但不得用骨架屏**(红线 7)。再退一步:次级页只给品牌字 + 三个导航项,不要最近列表。
2. **T4.2 的页名进顶栏**:若某页顶栏放不下页名加动作 —— 优先保页名,动作收进一个「更多」菜单。
3. **T5.1 的参数统一表**:若合并后信息密度过高 —— 退到保留 `.chart-page-center-card`,只做视觉对齐,不做结构合并。
4. **T6.2 的目录**:若从 Markdown 生成标题锚点与现有 rehype 链冲突 —— 退到只做"回到顶部",目录另立单。**不得为了做目录去改 `report-chart-grid-rehype.ts`。**
5. **T7.1 的收窄进度**:若服务端投影里没有现成的"已从 N 分钟收到 M 分钟"字段 —— 只显示当前区间与宽度,**不要前端口算进度**。缺的字段写进 `BLOCKED.md` 并另立单。
6. **T7.2 的两段收敛**:若某块内容在两段里都不自然 —— 保留三段,但不得回到五段。
7. 任何一轮若被门禁或环境卡住,**先把已完成的子任务单独提交并推 staging**,未完成项写进 `BLOCKED.md`,不要整轮压着不交。
---
## 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/cend-shell-20260916 .worktrees/cend-shell-20260916 origin/staging
cd .worktrees/cend-shell-20260916/frontend
npm ci
./node_modules/.bin/tsc --noEmit
npm run lint
npm test 2>&1 | tail -30 # 记下测试总数,红线 2 的基线
npm run build 2>&1 | tail -40 # 记下 / /chart /ephemeris /reports 四个渲染标记与各自首屏体积,红线 3、4 的基线
```
R5–R8 各自重复上述流程:R5、R6 的基线是 R4 合入后的 `origin/staging`R7、R8 可直接以 `111b4a84` 之后的 `origin/staging` 为基线。
开工时核对 `docs/BUG_HISTORY.md` 的当前最大编号(现为 BUG-738)。
---
## 环境缺口
本单诊断来自读代码与设计文档,**没有真机截图**:本会话无登录态、无 Chrome。以下必须落到 `docs/testing/` 的人工清单,不得在进度记录里写成「通过」:
- 次级页侧栏在移动端抽屉的手势与焦点行为(R4)。
- 星盘行星表在 375px 宽下的横向滚动(R5)。
- 20 张盘以上的报告在加目录后仍不重叠、滚动不卡(R6,红线 9 关联)。
- 生时校正常驻条在窄屏 `.is-compact` 下的实际可见性(R7,本轮核心收益)。
- 设置弹窗四个分区切换不跳尺寸(R8,BUG-554/698 的复验)。
- 支付流程(`membershipHref` 入口定位、下单)——无受控账号,整条留给产品负责人实测。