Files
Jyotisha/docs/tasks/PROGRESS-cend-shell-20260916.md
T
Jesse_ChenandClaude Opus 5 50ce02c837
Independent Staging Quality Gate / validate (push) Failing after 9m57s
Independent Staging Quality Gate / publish (push) Skipped
feat(ui): 星盘/星历/报告中心并入 app 外壳,侧栏不再消失
三个页面此前各是脱离外壳的独立全屏路由,各写了一套一样的
*-shell / *-topbar(只有一个「返回对话」链接)/ *-hero 骨架,
侧栏在打开它们的瞬间整个消失。

新增 AppNavRail(只读:两个 GET,零写操作)与 SecondaryShell。
会话行走 sessionHref → /?c=<uuid>,跳转复用现有侧栏的
persistLoginSessionReturn + location.assign,行为完全一致。

刻意不带重命名/收藏/归档/删除与账户菜单:那套连着 Home() 的乐观更新与
回滚层,为四个路由把它整体上提远超需要,且会撞 useState 增长门禁。

四个路由渲染标记完全不变:/ ○、/chart ○、/ephemeris ○、/reports ƒ。
CSS gzip 39,825→39,727(−0.25%);per-route JS 体积构建不输出,
已作为口径缺口记录。

两处自身问题被测试抓到并修复:usePathname() 在 app-router 上下文外
返回 null(类型说是 string)、fetch 在 jsdom 里可能不存在。
另差点随 hero 一起丢掉 BUG-717 的 eyebrow 文案(成本与速度承诺,
会印在失败页上),已放回 tab 行下方。

/reports/[reportId] 留给 R6 与目录一起做;根边界页保留「返回对话」,
它们不得 import globals.css,挂不了外壳。

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

8.5 KiB
Raw Blame History

PROGRESS · 次级页外壳 R4

任务书:docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.md 的 R4 执行方式:直接执行模式 分支:codex/cend-shell-20260916 基线:origin/staging = b3ea8c33(第一份任务书 R1–R3 合入后) BUG 段:不占用(产品改造)

R5(星盘+星历)与 R6(报告)依赖本轮。


T4.1 · 只读导航外壳

新增三个文件:

文件 职责
src/hooks/use-nav-rail.ts 两个只读 GET/api/sessions?limit=40/api/account),Promise.allSettled不含任何写操作
src/components/app-nav-rail.tsx 品牌 + 新建对话 + 星盘/星历/我的报告 + 一条「最近」平铺列表 + 身份页脚
src/components/secondary-shell.tsx SidebarProviderAppNavRailSidebarInset.chat-panel.secondary-panel + 46px 顶栏

会话行走 sessionHref("", id)/?c=<uuid>,跳转用 persistLoginSessionReturn() + window.location.assign()与现有侧栏 leaveChat() 完全一致的行为,没有另造一套。

侧栏原语(SidebarProvider / Sidebar / SidebarTrigger / SidebarRail)是通用的,抽屉与折叠态直接复用,没有重写。

验收对照

任务书验收标准 结果
AppNavRail 不 import page.tsx 的 hook,不持有会话写状态 依赖只有 ui/sidebar、chat-session-urlsession-groups、自己的 hook
四个路由都挂上外壳,桌面常驻、移动端抽屉 ⚠️ 三个/chart/ephemeris/reports/reports/[reportId](阅读页)留给 R6,见下
点「最近」行跳 /?c=<uuid> 并选中 复用 sessionHrefparseSessionUrlQuery 的既有测试未动
列表未到达时静态空态,无骨架无 spinner 三档文案:未登录 / 已 settled 无数据 / 读取中,全是静态文字
未登录时降级为品牌 + 登录入口,不报错不空白 401 → signedOut,列表位显示「登录后可以看到你的对话」,页脚变「去登录」
四个路由渲染标记逐个入记录 见下

/reports/[reportId] 本轮没挂:阅读页是纸面文档表面,外壳会改变它的滚动所有权与打印行为,而 R6 正要重做它(加目录)。同一轮里既挂外壳又改阅读结构,出问题难以定位。留给 R6,已在任务书依赖里写明。

两处我自己的健壮性问题,被测试抓到

  1. usePathname() 会返回 null 类型标注是 string,但在 app-router 上下文之外(包括直接挂载组件的单测里)返回 null,我的 pathname.startsWith(...) 直接抛 Cannot read properties of null,打红了 chart-page-viewephemeris-page 两个渲染测试共 5 条。已加 ?? ""。既有的 app-sidebar.tsx 有同样的写法但没被覆盖到,本轮不动它(红线 11)。
  2. fetch 可能不存在jsdom / 纯 node 环境)。原写法会在 load() 里同步抛出,变成未捕获的 promise 拒绝。已加 typeof fetch !== "function" 短路 + .catch() 兜底。

T4.2 · 删掉三套重复骨架

页面 删除
chart-page-view.tsx .chart-page-shell / .chart-page-topbar / .chart-page-back(返回对话)/ .chart-page-hero + <h1>星盘</h1>
ephemeris-page.tsx .ephemeris-shell / .ephemeris-topbar / .ephemeris-back / hero 的 <h1>;底部「去问」提到顶栏
personal-report-center.tsx .report-center-shell / .report-center-topbar / .report-center-back / hero 的 <h1>;「生成完整报告」提到顶栏

CSS 侧删掉 22 条规则、裁剪 6 条选择器列表。这次用了 R2 教训后的切分规则(只在注释之后找选择器),改完复核:注释配平 73/73、含花括号的注释 0 条。

一处内容差点被我弄丢

CHART_VIEW_COPY.eyebrow(「主盘直接算 · 分盘按需 · 不消耗点数」)原本在 hero 里,随 hero 一起被删。这是 BUG-717 专门定的文案——它是成本与速度承诺,而且会印在失败页上。测试打红才发现,已放回 tab 行下方作为一行静默说明。

错误态与空态

三个页面的降级分支(未登录、读取失败、无数据、natalMissingpanchangaUnavailable 等)全部保留,只是从 *-shell *-message 的根节点改成外壳内的一个 sectionh1 降为 h2(页名已在顶栏,一页不该有两个 h1)。

「返回对话」残留说明

git grep 返回对话 src/ 还有 4 处命中,都在根边界页error.tsxnot-found.tsxglobal-error.tsxreports/[reportId]/not-found.tsx)。按 DESIGN.md,这些页在根 layout 段里、不得 import globals.css,因此不能挂外壳,那个链接是它们唯一的回家路径。本轮不动。


收尾验收

红线 结果
1 · tsc 0 错 / lint 0 error 118 warning,与基线同
2 · 测试总数 tests 3350pass 3303fail 31,与基线 diff 逐条一致
3 · 四个路由渲染标记不变 / · /chart · /ephemeris · /reports ƒ,改前改后完全相同
4 · 次级页首屏体积 ⚠️ 口径缺口CSS 侧 39,825 → 39,727 字节(−0.25%,三套骨架并成一套是净减。但当前 Turbopack 的路由表不输出 per-route JS 体积,导航栏与 sidebar 原语给三个次级页增加的 JS 我量不到,不能给数字。任务书预期的「次级页体积上升」无法证实也无法证伪
6 · 一律 ChatComposer / 一套滚动跟随 本轮未涉及
7 · 无 spinner / 骨架 列表在途是静态文案
8 · 不回退 BUG-698 的 @supports (height: 1dvh) 未触碰
9 · 不回退 BUG-616/617 的报告盘面 grid 未触碰 report-chart-grid-rehype.ts
10 · 同提交更新 DESIGN.md 新增「Secondary page shell」一节;「Personal report centre」的 Structure 与 Width 两条重写
11 · 不升依赖、不留新 warning
12 · 只读侧栏不发起写操作 use-nav-rail.ts 只有两个 GET

Python 快速门(BUG-736):本轮未改 page.tsx;新增的 use-nav-rail.ts 不是 Home 级 hookHome() 不引用它),按规则不需要登记进 OPTIONAL_HOME_HOOKS。仍跑了 pytest tests/test_supabase_user_data_contract.py8 条全绿。

按红线 3 更新的既有断言(原值 / 新值 / 原因)

文件 原值 新值 原因
chart-page-view · 移动端 三条以 @media (max-width: 767px) { .chart-page-hero h1 起锚;另断 .chart-page-back 44px 直接断该断点块里的三个开关;删 .chart-page-back 一条 hero 与返回按钮已删;被断言的三个开关一条没动
chart-page-view · 空态 assert.match(markup, /返回对话/) assert.match(markup, /新建对话/) + doesNotMatch(/返回对话/) 返回路径从一次性链接变成常驻侧栏。这条更强:要求整条导航在场
personal-report-entry .report-center-shell 的 height/overflow .secondary-panel > :not(.chat-header) 的 overflow + .chat-panel 的 grid 行 滚动所有权归外壳;「在高度锁内部滚动、不自己撑高」没变
personal-report-theme-contract ×2 .report-center-back 44px;标题三处含 .report-center-hero h1 前者删除;后者收为封面 + 章节两处 返回按钮与页标题都移进了外壳顶栏,由 chat-header 规则统管
viewport-breakpoint-contract ×2 .report-center-shell / .ephemeris-shell 在 767 块内 .report-center-body / .ephemeris-body 页面根换了;「用全局 767 断点、不自立断点」由同测试的两条 doesNotMatch 继续守

一条实际收紧(空态从「有返回链接」变成「整条导航在场 + 不得再有返回链接」),其余是形态同步。


环境缺口(不得记为通过)

  1. 次级页侧栏在移动端抽屉的手势与焦点行为(本轮新挂,优先级最高)。
  2. 未登录时三个次级页的降级表现——无登录态,只能证明代码分支存在。
  3. 从次级页点「最近」行回到对话并正确选中——需要真实会话数据。
  4. per-route JS 体积:构建不输出,需要另外的测量手段(见红线 4)。

下一轮

R5(星盘 + 星历)与 R6(报告中心 + 报告阅读,含把 /reports/[reportId] 挂进外壳)基线取本轮合入后的 origin/staging。R7、R8 与本轮文件不重叠,可并行。