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
This commit is contained in:
co-authored by
Claude Opus 5
parent
b3ea8c336e
commit
50ce02c837
@@ -0,0 +1,112 @@
|
||||
# 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` | `SidebarProvider` → `AppNavRail` → `SidebarInset.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-url`、`session-groups`、自己的 hook |
|
||||
| 四个路由都挂上外壳,桌面常驻、移动端抽屉 | ⚠️ **三个**:`/chart`、`/ephemeris`、`/reports`。`/reports/[reportId]`(阅读页)留给 R6,见下 |
|
||||
| 点「最近」行跳 `/?c=<uuid>` 并选中 | ✅ 复用 `sessionHref`;`parseSessionUrlQuery` 的既有测试未动 |
|
||||
| 列表未到达时静态空态,无骨架无 spinner | ✅ 三档文案:未登录 / 已 settled 无数据 / 读取中,全是静态文字 |
|
||||
| 未登录时降级为品牌 + 登录入口,不报错不空白 | ✅ 401 → `signedOut`,列表位显示「登录后可以看到你的对话」,页脚变「去登录」 |
|
||||
| 四个路由渲染标记逐个入记录 | ✅ 见下 |
|
||||
|
||||
**`/reports/[reportId]` 本轮没挂**:阅读页是纸面文档表面,外壳会改变它的滚动所有权与打印行为,而 R6 正要重做它(加目录)。同一轮里既挂外壳又改阅读结构,出问题难以定位。留给 R6,已在任务书依赖里写明。
|
||||
|
||||
### 两处我自己的健壮性问题,被测试抓到
|
||||
|
||||
1. **`usePathname()` 会返回 `null`。** 类型标注是 `string`,但在 app-router 上下文之外(包括直接挂载组件的单测里)返回 null,我的 `pathname.startsWith(...)` 直接抛 `Cannot read properties of null`,打红了 `chart-page-view` 与 `ephemeris-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 行下方作为一行静默说明。
|
||||
|
||||
### 错误态与空态
|
||||
|
||||
三个页面的降级分支(未登录、读取失败、无数据、`natalMissing`、`panchangaUnavailable` 等)全部保留,只是从 `*-shell *-message` 的根节点改成外壳内的一个 `section`,`h1` 降为 `h2`(页名已在顶栏,一页不该有两个 h1)。
|
||||
|
||||
### 「返回对话」残留说明
|
||||
|
||||
`git grep 返回对话 src/` 还有 4 处命中,都在**根边界页**(`error.tsx`、`not-found.tsx`、`global-error.tsx`、`reports/[reportId]/not-found.tsx`)。按 `DESIGN.md`,这些页在根 layout 段里、**不得 import `globals.css`**,因此不能挂外壳,那个链接是它们唯一的回家路径。本轮不动。
|
||||
|
||||
---
|
||||
|
||||
## 收尾验收
|
||||
|
||||
| 红线 | 结果 |
|
||||
|---|---|
|
||||
| 1 · `tsc` 0 错 / `lint` 0 error | ✅ 118 warning,与基线同 |
|
||||
| 2 · 测试总数 | ✅ tests 3350,pass 3303,fail 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 级 hook(`Home()` 不引用它),按规则不需要登记进 `OPTIONAL_HOME_HOOKS`。仍跑了 `pytest tests/test_supabase_user_data_contract.py`,8 条全绿。
|
||||
|
||||
### 按红线 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 与本轮文件不重叠,可并行。
|
||||
@@ -157,7 +157,7 @@
|
||||
| `TASK-engine-runtime-hygiene-20260901.md` | `PROGRESS-engine-runtime-hygiene-20260901.md` | Python 引擎运行时治理 | 已验收 | `124d3990`(staging 实机三条待部署窗口) |
|
||||
| `TASK-billing-pricing-20260830.md` | `PROGRESS-billing-pricing-20260830.md` | 计费闭环与功能级定价 | 已合入 | `aaaa5fb7`~`a499c344` |
|
||||
| `TASK-cend-ui-claude-alignment-20260916.md` | `PROGRESS-cend-ui-r1/r2/r3-20260916.md` | **C 端界面向 claude.ai 产品界面对齐(三轮串行 R1→R2→R3,都动 `globals.css`,不得并行)**:根因是 `frontend/CLAUDE_DESIGN.md` 扒的是 **claude.com 营销官网**,它自己在 Known Gaps 里写明 claude.ai 产品界面不在范围内,而 `DESIGN.md:3` 把它当成了产品界面的实现契约。**R1**:`--font-display` 里 Tiempos Headline / StyreneB **从未加载**(无 `@font-face`、`public/` 无字体、`layout.tsx` 只 vendor 了 Inter),中文标题全站落到 **宋体 / SimSun**,波及 20 处含助手回答的 h2/h3(BUG-737);亮色强调色 `#85432f` 与暗色 `#d78064` 不同源,产品拍板亮色换 **Claude coral `#cc785c`**,**易漏点**是 `globals.css:16` 的 `--color-ring` 硬编码在 `@theme inline` 里不跟随 `:root`,另有第四个 `:root` 亮色块(`:4358`)必须同步(BUG-738);`.composer-footer` 常驻 44px + 顶栏 68px + `--composer-reserve` 148px,每屏固定吃掉 216px,模型选择器移进输入框内部、删掉底栏、顶栏收到 46px 并删「分析对象」副标题。**R2**:空状态是营销落地页(hero 卡 + 两张 132px 入口大卡 + 3 列 156px 主题卡),输入框被压在 **800px 以上**内容之下,重排成「问候 + 居中输入框 + 两枚入口 pill + 一排 chip」。**R3**:侧栏两个 `<details>` 拍平成一条「最近」、星盘的两个入口(侧栏分组 + 账户菜单)收敛到一处、删掉逐条助手头像。**决策记录 D3 推翻 DESIGN.md「报告强调色与应用同源」一句**(报告刻意保留深棕)。原型图 https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b(三套画面 + 明暗,页面标题就是建议字体栈的实际渲染)。环境缺口:无登录态无 Chrome,四项真机观感留 `docs/testing/`。BUG 段 737–738 | 待领取 | — |
|
||||
| `TASK-cend-surfaces-claude-alignment-20260916.md` | — | **次级页面对齐(上一单的续篇,R4→R5/R6,R7、R8 可并行)**:星盘 `/chart`、星历 `/ephemeris`、报告 `/reports` **各是脱离 app 外壳的独立全屏页**,顶部只有一个「返回对话」链接、侧栏整个消失,且三家各写了一套一模一样的 `*-shell`/`*-topbar`/`*-hero` 骨架——与上一单 E5 同根因(营销站 band 结构被套到产品界面)。**R4** 抽只读导航外壳 `AppNavRail`(只用现成的 `GET /api/sessions` + `GET /api/account`,会话行走 `sessionHref` 跳 `/?c=<uuid>`;**刻意不带**重命名/删除/收藏/归档——那套连着 `Home()` 的乐观更新与回滚,搬过来会撞 useState 增长门禁)。**R5** 星盘五 tab 下划线化 + 参数合表 + 行星表横向滚动;星历日期导航改 `‹ 日期 ›`。**R6** 报告中心卡片网格改行式列表;阅读页加常驻目录。**R7** 生时校正把可信区间从盘面板标题行提成常驻条(窄屏 `.is-compact` 下盘面板是 overlay,现在默认看不到区间),五个 `technique-audit` 折叠块收成两段。**R8** 设置内容区收窄(880px 弹窗里表单铺了 690px)、套餐卡三修饰符收敛成两态。**已解锁**:原挡路的设置单已于 `111b4a84`(BUG-698)合入。**两条不得回退**:BUG-698 的 `@supports (height: 1dvh)` 写法(重复声明回退会被 Lightning CSS 折叠)、BUG-616/617 的报告盘面 grid 实现。默认不占 BUG 号 | 执行中(Claude 直接执行) | — |
|
||||
| `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md` | **次级页面对齐(上一单的续篇,R4→R5/R6,R7、R8 可并行)**:星盘 `/chart`、星历 `/ephemeris`、报告 `/reports` **各是脱离 app 外壳的独立全屏页**,顶部只有一个「返回对话」链接、侧栏整个消失,且三家各写了一套一模一样的 `*-shell`/`*-topbar`/`*-hero` 骨架——与上一单 E5 同根因(营销站 band 结构被套到产品界面)。**R4** 抽只读导航外壳 `AppNavRail`(只用现成的 `GET /api/sessions` + `GET /api/account`,会话行走 `sessionHref` 跳 `/?c=<uuid>`;**刻意不带**重命名/删除/收藏/归档——那套连着 `Home()` 的乐观更新与回滚,搬过来会撞 useState 增长门禁)。**R5** 星盘五 tab 下划线化 + 参数合表 + 行星表横向滚动;星历日期导航改 `‹ 日期 ›`。**R6** 报告中心卡片网格改行式列表;阅读页加常驻目录。**R7** 生时校正把可信区间从盘面板标题行提成常驻条(窄屏 `.is-compact` 下盘面板是 overlay,现在默认看不到区间),五个 `technique-audit` 折叠块收成两段。**R8** 设置内容区收窄(880px 弹窗里表单铺了 690px)、套餐卡三修饰符收敛成两态。**已解锁**:原挡路的设置单已于 `111b4a84`(BUG-698)合入。**两条不得回退**:BUG-698 的 `@supports (height: 1dvh)` 写法(重复声明回退会被 Lightning CSS 折叠)、BUG-616/617 的报告盘面 grid 实现。默认不占 BUG 号 | **R4 已实现(Claude 直接执行),R5–R8 待做** | R4:抽出 `AppNavRail`(只读,两个 GET,零写操作)+ `SecondaryShell`,三个次级页并入 app 外壳并删掉各自的 shell/topbar/hero;四个路由渲染标记**完全不变**(`/` `/chart` `/ephemeris` 仍 Static);CSS gzip −0.25%。`/reports/[reportId]` 留给 R6 与目录一起做。两处自身健壮性问题被测试抓到:`usePathname()` 可为 null、`fetch` 可能不存在。差点弄丢 BUG-717 的 eyebrow 文案(已放回)|
|
||||
|
||||
### 待开的任务书(2026-09-03 审查决定)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user