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

22 KiB
Raw Blame History

TASK · C 端次级页面向 claude.ai 产品界面对齐(2026-09-16

基线

  • 基线 commitorigin/staging = 111b4a8455b5e3d6ae7b4ea759ba35492fc806cc2026-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/staging111b4a84。符号名在括号里,行号漂移时按符号定位。

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:1560Home() 里渲染,挂在 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-compactglobals.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.tsD\d+ / Moon Chart 标题收进 grid 容器。本轮不得回退该实现,只在其上加导航。

E11 · 报告中心是卡片网格,状态信息靠底色区分

personal-report-center.tsx:218.report-center-list.report-center-cardglobals.css:3608)。状态 .report-center-status 三个变体(:3632-3635)分别用 --color-success-muted / --color-action-soft / --color-danger-muted 三块底色区分。卡片网格在报告数量超过五六份后扫读成本高于行式列表。

E12 · 套餐卡三个修饰符叠加三层描边与底色

frontend/src/components/billing-panel.tsx:135

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.tsxAGENTS.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/sessionsfrontend/src/app/api/sessions/route.ts 已存在
账户与余额 GET /api/accountfrontend/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:61personal-report-center.tsx:169),改外壳时不要连它们一起删
  • 页名在顶栏可见且窄屏下省略号截断。

R5 · 星盘 + 星历

分支 codex/cend-chart-eph-20260916,基线是 R4 合入后的 staging。

T5.1 · 星盘页

  • 五个体系 tabCHART_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-panchangadl 网格保留。
  • 「就今天问一句」提到顶栏(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-errorrole="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.tsxbilling-panel.tsxglobals.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,不要整轮压着不交。

开工前置命令

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/stagingR7、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 入口定位、下单)——无受控账号,整条留给产品负责人实测。