feat(ui): 报告中心改行式列表,阅读页并入外壳并把目录挪到右侧常驻
报告中心原来是卡片方阵,状态只靠三块底色区分;报告一多,扫读成本
按卡片数线性涨。现在一列一行:带色点的状态 chip、标题、创建时间 ·
深度 · 主题,操作靠右。失败原因从右边一小块挪进行内,能完整读到。
chip 里原来的 StatusIcon(generating 转圈、ready 打勾、failed 警告)
换成 5px 色点——原型如此,且列表是轮询不是演出。失败行不加「重新
生成」,顶栏已经有唯一的生成入口。
行内小字只写接口真给的东西。GET /api/reports 不返回节数和盘数,所以
不写「9 节 · 22 张盘」(VOICE.md 第 2 条),原型图上那行是 mock。
/reports/[reportId] 是最后一个脱离外壳的全屏路由,九个 phase 全部并进
SecondaryShell,根节点从 <main> 改成 <div>(外壳自己就是 main)。
任务书 E10「没有目录」已过期:目录在 cfcd369d 就存在。本轮把它从左栏
挪到正文右侧并定稿视觉,位置用 grid-column 显式指定而不是靠 DOM 次序,
这样窄屏抽屉仍能排在源码最前面,不会掉到全文末尾。860px 以下常驻栏
消失、折叠抽屉保留——删掉抽屉等于窄屏彻底失去章节定位。
挂外壳带出一个真实风险:window.print() 打的是整篇,而 .chat-app /
.chat-panel 是 height:100%;overflow:hidden,会把九节报告裁成一页。阅读
页因此多挂一条 media="print" 样式,把外壳既隐藏又解锁。放组件里而不是
globals.css:它只在阅读页挂载期存在,对话页的打印不受影响,也不用
:has() 去够祖先,更不越界到并行轮次的 CSS 区段。
纸面三个 token 一个没动,--report-accent 仍是 #85432f;目录栏用应用
调色板,因为它是 chrome 不是纸。report-chart-grid-rehype.ts 一行未动,
新增断言守住它仍然接线(BUG-616/617)。
tsc 0 错;lint 0 error / 118 warning(未增);3350→3355 条,0 条既有
断言被改,31 条无 Docker 失败与基线逐条一致;四个路由渲染标记不变,
/ 首屏 gzip −0.58%。
「20 张盘以上不重叠、滚动不卡」与「挂外壳后的打印真实输出」无真机做
不了,已写成环境缺口,清单在 docs/testing/cend-report-20260916.md。
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
c3bcddb652
commit
a1a78c7ebc
@@ -0,0 +1,130 @@
|
||||
# PROGRESS · R6 报告中心 + 报告阅读(2026-09-16)
|
||||
|
||||
任务书:`docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.md` 的 **R6**(T6.1 / T6.2 + 顺带把 `/reports/[reportId]` 挂进 `SecondaryShell`)。
|
||||
|
||||
- 基线 commit:`origin/staging` = `50ce02c8`(R4 外壳已合入)
|
||||
- 分支:`codex/cend-report-20260916`
|
||||
- 执行方:Claude subagent(直接执行模式)
|
||||
- **未 push**,按任务要求只提交到本地分支
|
||||
- BUG 编号:**未占号**。本轮是产品改造,实现中没有发现新的真实缺陷;`docs/BUG_HISTORY.md` 当前最大号 BUG-738 不变
|
||||
|
||||
---
|
||||
|
||||
## 一、开工基线数字(均在本 worktree 实测)
|
||||
|
||||
| 项 | 基线值 |
|
||||
|---|---|
|
||||
| `./node_modules/.bin/tsc --noEmit` | 0 错 |
|
||||
| `npm run lint` | **0 error**,118 warning |
|
||||
| `npm test` | tests **3350** / pass 3304 / **fail 31** / skipped 15 |
|
||||
| `npm run build` 渲染标记 | `/` **○ Static**、`/chart` ○、`/ephemeris` ○、`/reports` ƒ、`/reports/[reportId]` ƒ |
|
||||
| `/` 首屏 gzip(`.next/server/app/index.html`) | 4,109 B(复测 4,110 B) |
|
||||
| globals CSS chunk gzip | 36,334 B(raw 242,608 B) |
|
||||
|
||||
31 条失败全部是**无 Docker** 的既有失败(数据库套件 / 部署套件),与任务书「环境缺口」一致。基线清单存为 `base.txt`(`npm test` 的 `^not ok` 行去掉序号后排序)。
|
||||
|
||||
---
|
||||
|
||||
## 二、逐条验收对照
|
||||
|
||||
### T6.1 · 报告中心改行式列表(D12)
|
||||
|
||||
| 验收标准 | 结论 | 证据 |
|
||||
|---|---|---|
|
||||
| `.report-center-list` 从卡片网格改成单列行式列表 | **通过** | `.report-center-list` 改为单列 `display: grid` + `gap: 1px`,靠 `--color-border` 底色透出一条发丝线(不是两条相邻边框);`.report-center-card*` 全部改名为 `.report-center-row*`,`git grep report-center-card` 在 `src/` `tests/` 与 `globals.css` 均 0 命中 |
|
||||
| 每行:状态 chip(带色点)+ 标题 + 元信息 + 右侧操作 | **通过** | `.report-center-status::before` 加 5px `currentColor` 圆点;`.report-center-row` 是 `minmax(0, 1fr) auto` 两列;元信息在 `.report-center-row-meta` |
|
||||
| 四种状态都有对应行样式 | **三种通过,第四种是接口缺口**(见 §四) | `is-ready` / `is-generating` / `is-failed` 三个 chip 变体保留并各有行内表现;「生成中的分节进度」**没有服务端投影**:`GET /api/reports`(`frontend/src/app/api/reports/route.ts` 的 `REPORT_LIST_COLUMNS`)不返回节数,只在 `status === "failed"` 时才去查 `personal_report_sections`。按 `VOICE.md` 第 2 条「数字只来自服务端投影,不口算、不编」,前端不得自造「第 4 节,共 9 节」。原型图上那行是 mock 文案 |
|
||||
| `role="status"` 在 `generating` 行上保留 | **通过** | `role={report.status === "generating" ? "status" : undefined}` 原样保留在状态 chip 上 |
|
||||
| 失败行仍显示 `failureSummary ?? failureCode` | **通过** | 从右栏移到行内正文(meta 行下方),表达式一字未改:`report.failureSummary ?? report.failureCode ?? "生成失败"` |
|
||||
| 导出错误 `report-center-export-error` 的 `role="alert"` 保留 | **通过** | 原样 |
|
||||
| 空态与读取失败态不变 | **通过** | `report-center-empty` / `report-center-alert` / 未登录 `report-center-message` 三个分支的 JSX 与文案一字未动 |
|
||||
|
||||
补充设计决定(写进 `frontend/DESIGN.md`):
|
||||
|
||||
- 状态 chip 里原来的 `StatusIcon`(generating 用 `InlineSpinner`、ready 用 `Check`、failed 用 `TriangleAlert`)**删除**,改为 CSS 色点。理由有二:原型图就是一枚色点;列表是轮询不是演出,chip 里再转一个圈与「揭幕后不得出现 spinner」的口径不合。`InlineSpinner` 仍用于首次读取态与导出按钮内(两者都是真实在等)。
|
||||
- 失败行右侧**不加**「重新生成」按钮。顶栏已经有唯一的生成入口,再加一个就是第二个入口(用户偏好:多余入口宁可删除也不修)。
|
||||
- ready 行的按钮次序改为「导出报告(.md)」在前、「查看报告」在后,让主操作(outline)落在行尾靠右——这是行式列表里视线终点的位置。测试 `ready reports expose Markdown export only in the ready branch` 按分支切片取文本,不依赖次序,未受影响。
|
||||
|
||||
### T6.2 · 报告阅读页常驻目录(D11)+ 挂进 `SecondaryShell`
|
||||
|
||||
**先说一条与任务书事故实证不一致的事实:E10 写的「整篇直下,没有目录、没有章节定位」在 `50ce02c8` 上已经不成立。** 目录在 `cfcd369d`(长文 Markdown 那一轮)就已经存在:`personal-report-markdown-view.tsx` 的 `ReportToc` 从 `buildLongformOutline` 的 `##`/`###` 标题生成,用 `IntersectionObserver` 高亮当前节,`@media print` 下已隐藏。任务书是按更早的状态写的。所以本轮 T6.2 的实际工作是**把它做成原型图那条右侧常驻栏,并把阅读页并进外壳**,不是从零加目录。
|
||||
|
||||
| 验收标准 | 结论 | 证据 |
|
||||
|---|---|---|
|
||||
| 在 `.personal-report-reader` 右侧加一条常驻目录 | **通过(改为右侧 + 视觉定稿)** | `.personal-report-md-layout` 从 `minmax(11rem,15rem) minmax(0,1fr)`(目录在左)改为 `minmax(0,1fr) minmax(11rem,15rem)`;位置用 `grid-column: 1 / 2` 显式指定而不是靠 DOM 次序,这样窄屏抽屉仍然能排在源码最前面(否则抽屉会掉到全文末尾)。目录去掉外框,改成原型图的安静竖栏:当前项 2px `--color-action` 左条 + `--color-canvas-muted` 底 |
|
||||
| 从 Markdown 的 `##` 标题生成 | **通过(且保留 `###` 子项)** | `buildLongformOutline` 原有实现,未改;`###` 子项是既有能力,本轮不删弱 |
|
||||
| 滚动时高亮当前节 | **通过** | `ReportToc` 的 `IntersectionObserver`(`rootMargin: "-20% 0px -70% 0px"`)原样,`aria-current="location"` + `.is-current` |
|
||||
| 目录项点击滚动到对应节 | **通过** | `href="#{id}"` + `.personal-report-md-article h2, h3 { scroll-margin-top: 80px }`(既有) |
|
||||
| 窄屏(< 860px)隐藏目录 | **通过** | `@media (max-width: 860px)` 下 `.personal-report-toc-desktop { display: none }`,**常驻栏消失**。同一断点下 `.personal-report-toc-drawer`(折叠的「目录」`<details>`)仍然显示——那是既有的、默认收起的另一种形态,不是常驻栏;删掉它等于窄屏彻底失去章节定位,属于功能倒退,所以保留并给它补回栏消失后没人画的那个盒子(边框 + 圆角 + `--color-canvas`) |
|
||||
| `@page` 规则保留 | **通过** | `<style media="print">{"@page { size: A4; margin: 13mm 12mm 14mm; }"}</style>` 一字未改,既有断言仍然精确匹配 |
|
||||
| `@media print` 下目录隐藏 | **通过** | `globals.css` 里 `.personal-report-toc { display: none !important; }` 原样(两处,均为既有) |
|
||||
| 纸面 token 一个不改 | **通过** | `--report-paper` / `--report-rule` / `--report-accent` 三处定义(:root、两处 dark、print 覆盖)全部未动;`--report-accent` 仍是 `#85432f`。目录栏用的是 `--color-action` / `--color-canvas-muted` / `--color-ink*`——它是应用 chrome,不是纸面,新增断言 `assert.doesNotMatch(toc + current, /--report-(?:paper\|rule\|accent)/)` 守住这条 |
|
||||
| 不触碰 `report-chart-grid-rehype.ts` | **通过** | `git diff --stat` 里没有这个文件;新增断言 `assert.match(viewSource, /rehypePlugins=\{\[reportChartGrid\]\}/)` 守住它仍然接线 |
|
||||
| `/reports/[reportId]` 挂进 `SecondaryShell` | **通过** | 九个 phase(loading/generating、timed-out、unauthorized、not-found、failed、invalid\|network-error、legacy-unavailable、兜底、markdown-ready)**全部**包进 `<SecondaryShell title="个人报告">`,根节点从 `<main>` 改成 `<div>`(外壳自己就是 `<main className="chat-app">`,不能再套一层 main)。新增断言:`doesNotMatch(/<main className="personal-report-(?:state\|reader)"/)`,且 `SecondaryShell` 出现次数 == phase 根节点次数 |
|
||||
|
||||
**滚动所有权。** `.personal-report-reader` 仍然是自己的滚动容器(`height: 100%; overflow-y: auto`),现在坐在 `.chat-panel` 的 `minmax(0, 1fr)` 行里,同时被 `.secondary-panel > :not(.chat-header)` 覆盖到——两条规则给的是同一件事,既有断言 `report centre and ready reader scroll inside the chat shell lock` 无需改动即通过。目录栏的 `top: 72px` 相对的是这个滚动容器(避开报告自己那条 54px 的 sticky 操作条),`max-height` 从 `calc(100vh - 96px)` 调到 `calc(100vh - 150px)`,因为外壳那 46px 顶栏在滚动容器之外。`max-height` 不在 `viewport-unit-fallback-contract` 的管辖范围(该测试只管 `height`),也没有引入 `dvh`,红线 8 不受影响。
|
||||
|
||||
**打印行为(挂外壳带来的真实风险,已处理)。** `printPersonalReport()` 走的是 `window.print()`,打的是整个文档。挂进外壳后,侧栏和 46px 顶栏会跟着上纸;更要命的是 `.chat-app` / `.chat-panel` 是 `height: 100%; overflow: hidden`,会把九节报告**裁成一页**。处理方式:阅读页多挂一条 `<style media="print">{REPORT_SHELL_PRINT_CSS}</style>`,把 `.chat-app` / `.chat-panel` / `[data-slot='sidebar-inset']` 解成 `display: block; height: auto; overflow: visible`,并隐藏 `[data-slot='sidebar']` / `sidebar-rail` / `sidebar-scrim` / `.chat-header`。
|
||||
|
||||
放在组件里而不是 `globals.css`,有三个理由:(1) 这条样式只在阅读页挂载期间存在,对话页自己的打印行为一点不受影响;(2) 它要选的是 `.chat-app` 这类**祖先**,在 `globals.css` 里没法用 `.personal-report-*` 前缀限定,只能靠 `:has()`,那是更脆的写法;(3) 任务约束要求本轮只动 `.report-center-*` / `.personal-report-*` 两段 CSS,不得越界到 R5/R7/R8 的地盘——放组件里就完全不越界。新增断言覆盖了这条样式的存在与内容。
|
||||
|
||||
**没有做、也不打算顺手做的:** `ReportActions`(返回报告中心 / 打印 / 导出)保持原样留在阅读页正文顶部的 sticky 条里,没有提到外壳顶栏。任务书 T6.2 没有要求,原型图顶栏里也确实有一个返回入口(列表 → 详情的返回是合理的上下文导航,和 R4 删掉的「返回对话」不是一回事)。提上去会连带动 `.personal-report-actions` / `.personal-report-back` 的既有 CSS 与 `touch-target` / `theme-contract` 两条断言,超出本轮范围。建议留作后续单。
|
||||
|
||||
---
|
||||
|
||||
## 三、改了哪些既有断言
|
||||
|
||||
**一条都没有改。** 基线 3350 条全部保持原样,31 条无 Docker 失败逐条一致。
|
||||
|
||||
新增 5 条测试(不是修改):
|
||||
|
||||
| 文件 | 测试名 | 守什么 |
|
||||
|---|---|---|
|
||||
| `frontend/tests/personal-report-entry.test.ts` | `report centre is a single-column row list with a dotted status chip` | `.report-center-card` 不得复活;列表是单列;行是两列;chip 有色点;三个状态变体都在 |
|
||||
| `frontend/tests/personal-report-entry.test.ts` | `every report state keeps the affordance its row carried before` | `role="status"` / 失败原因 / `role="alert"` / 空态 / 读取失败态;行元信息不得出现「张盘」「节,共」这类前端口算 |
|
||||
| `frontend/tests/personal-report-view.test.ts` | `the reader renders inside the app shell, in every phase` | 每个 phase 恰好包一层外壳;不得有裸 `<main>` |
|
||||
| `frontend/tests/personal-report-view.test.ts` | `printing a report inside the shell drops the chrome and its height lock` | `@page` 不变;外壳打印样式既隐藏又解锁;目录不上纸 |
|
||||
| `frontend/tests/personal-report-view.test.ts` | `the report TOC is a persistent right-hand rail that leaves the paper palette alone` | 目录在右(`grid-column: 2`)、sticky;`--report-*` 三个 token 不进 chrome;`--report-accent: #85432f` 仍在;`reportChartGrid` 仍接线(BUG-616/617) |
|
||||
|
||||
测试总数 3350 → **3355**。
|
||||
|
||||
---
|
||||
|
||||
## 四、收工数字
|
||||
|
||||
| 项 | 基线 | 收工 | 判定 |
|
||||
|---|---|---|---|
|
||||
| `tsc --noEmit` | 0 错 | **0 错** | 通过 |
|
||||
| `npm run lint` | 0 error / 118 warning | **0 error / 118 warning** | 通过(warning 未增,也未顺手修范围外的) |
|
||||
| `npm test` | 3350 / 3304 / 31 / 15 | **3355 / 3309 / 31 / 15** | 通过;`diff base.txt after.txt` 输出为空(逐条一致) |
|
||||
| `/` 渲染标记 | ○ Static | **○ Static** | 通过 |
|
||||
| `/chart` / `/ephemeris` | ○ / ○ | **○ / ○** | 通过(未触碰) |
|
||||
| `/reports` / `/reports/[reportId]` | ƒ / ƒ | **ƒ / ƒ** | 通过(`force-dynamic` 维持) |
|
||||
| `/` 首屏 gzip | 4,109 B | **4,085 B** | −0.58%,在 ±2% 内 |
|
||||
| globals CSS chunk gzip | 36,334 B | **36,447 B** | +113 B(+0.31%)。来源:行式列表与目录栏的新规则,减去删掉的卡片规则 |
|
||||
|
||||
> 量 CSS chunk 时踩过一个坑,记下来:`.next/static/chunks/` 里会留上一轮构建的 css 文件,对着通配符求和会把两轮的都算进去,得到两次「完全一样」的假数字。chunk 名是内容哈希(基线 `1vtco9m60b6ps.css` / 收工 `24xydqkp8fxdk.css`),要按名字对,不要按目录求和。
|
||||
|
||||
---
|
||||
|
||||
## 五、是否触发让步顺序
|
||||
|
||||
**没有触发。** 任务书让步顺序第 4 条是「若从 Markdown 生成标题锚点与现有 rehype 链冲突 → 退到只做『回到顶部』」。实际情况是锚点与 rehype 链**本来就共存**:`buildLongformOutline` 只按行扫 `^#{2,3}` 生成 id 并交给 `markdownComponents` 的 `h2`/`h3` 渲染,`reportChartGrid` 只动 `h4`(`D\d+` / `Moon Chart`)与其后的图盘块,两者作用的标题层级不重叠。`report-chart-grid-rehype.ts` 一行未动。
|
||||
|
||||
---
|
||||
|
||||
## 六、环境缺口(不得记作通过)
|
||||
|
||||
1. **20 张盘以上的报告加目录后仍不重叠、滚动不卡** —— **做不了,缺口**。本会话无登录态、无 Chrome、无真实报告数据,`npm test` 只能证明 `reportChartGrid` 仍然接线、grid CSS 未被改动,**证明不了真机渲染**。这是任务书红线 9(BUG-616/617 不得回退)的配套实测,必须由产品负责人在真机上走一遍。已写进 `docs/testing/` 清单(见下)。
|
||||
2. **打印 / 导出 PDF 在挂外壳后的真实输出** —— **做不了,缺口**。新加的 `REPORT_SHELL_PRINT_CSS` 是本轮改动里唯一无法自动验证的部分:测试只能断言这条样式存在且内容正确,浏览器实际怎么分页得真打一次。这是本轮**风险最高**的一项。
|
||||
3. **窄屏(< 860px)目录抽屉的展开、焦点与滚动** —— 做不了,缺口。
|
||||
4. **侧栏在阅读页移动端抽屉的手势与焦点** —— 与 R4 同一条缺口,未解除。
|
||||
|
||||
### 给产品负责人的真机清单(建议并入 `docs/testing/`)
|
||||
|
||||
1. 打开一份**九节、20 张盘以上**的完整报告。逐屏滚到底:每张北印盘各占一格,**没有两张叠在同一位置**(BUG-616);连续滚动不掉帧、不卡顿(BUG-617)。
|
||||
2. 同一份报告,右侧目录栏:滚动时高亮跟着走;点任一节直接跳到该节标题,标题不被顶部操作条盖住。
|
||||
3. 同一份报告点「打印」→ 另存为 PDF:**侧栏和顶部窄栏不出现在纸上**;报告**不是只印出一页**,九节完整分页;目录栏不上纸;纸面底色与正文颜色是浅色(打印锁定浅色调色板)。
|
||||
4. 把浏览器窗口拉窄到 860px 以内:右侧目录栏消失,正文上方出现一个收起的「目录」;展开可用。
|
||||
5. 报告中心:至少同时存在一份**生成中**和一份**已完成**的报告,确认行式列表里状态色点一眼可辨,生成中那行能被读屏软件播报。
|
||||
6. 制造一次失败报告(或用历史失败记录),确认失败原因写在行内、能完整读到。
|
||||
@@ -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` | `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、R8 已实现,R5/R6/R7 进行中(subagent 并行)** | R4:抽出 `AppNavRail`(只读,两个 GET,零写操作)+ `SecondaryShell`,三个次级页并入 app 外壳并删掉各自的 shell/topbar/hero;四个路由渲染标记**完全不变**(`/` `/chart` `/ephemeris` 仍 Static);CSS gzip −0.25%。`/reports/[reportId]` 留给 R6 与目录一起做。两处自身健壮性问题被测试抓到:`usePathname()` 可为 null、`fetch` 可能不存在。差点弄丢 BUG-717 的 eyebrow 文案(已放回)。R8:表单分区收窄到 440px(列表分区不变)、套餐卡三修饰符收敛成互斥的 `is-current` / `is-recommended`,`--highlighted` 删除改为滚动定位;手机端 `order:-1` 改挂 `[data-plan-alias]`(版位不是状态)。测试 3350→3354(净增 4),失败清单与基线逐条一致;`/` 仍 Static;我的干净构建实测 CSS gzip −3 字节。**遗留待产品拍板**:`?plan=` 深链现在完全没有视觉指向,只有滚动位置 |
|
||||
| `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md`、`PROGRESS-cend-report-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、R6、R7、R8 已实现,R5 进行中** | R4:抽出 `AppNavRail`(只读,两个 GET,零写操作)+ `SecondaryShell`,三个次级页并入 app 外壳并删掉各自的 shell/topbar/hero;四个路由渲染标记**完全不变**(`/` `/chart` `/ephemeris` 仍 Static);CSS gzip −0.25%。`/reports/[reportId]` 留给 R6 与目录一起做。两处自身健壮性问题被测试抓到:`usePathname()` 可为 null、`fetch` 可能不存在。差点弄丢 BUG-717 的 eyebrow 文案(已放回)。R8:表单分区收窄到 440px(列表分区不变)、套餐卡三修饰符收敛成互斥的 `is-current` / `is-recommended`,`--highlighted` 删除改为滚动定位;手机端 `order:-1` 改挂 `[data-plan-alias]`(版位不是状态)。测试 3350→3354(净增 4),失败清单与基线逐条一致;`/` 仍 Static;我的干净构建实测 CSS gzip −3 字节。**遗留待产品拍板**:`?plan=` 深链现在完全没有视觉指向,只有滚动位置。R6:报告中心卡片网格改行式列表、阅读页并入外壳并把目录挪到右侧常驻。**任务书 E10 过期**——目录在 `cfcd369d` 就已存在,本轮是挪位置定稿而非从零加。挂外壳带出一个真实打印风险已处理:`.chat-app`/`.chat-panel` 是 `height:100%;overflow:hidden`,裸 `window.print()` 会把九节报告裁成一页,阅读页因此多挂一条只在挂载期生效的 print 样式解锁外壳。「生成中的分节进度」做不了——`REPORT_LIST_COLUMNS` 不返回节数,按 VOICE.md 不许前端编 |
|
||||
|
||||
### 待开的任务书(2026-09-03 审查决定)
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# 真人验收清单 · 报告中心行式列表 + 报告阅读常驻目录(R6)
|
||||
|
||||
- 日期:2026-09-16
|
||||
- 分支:`codex/cend-report-20260916`
|
||||
- 对应:`docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.md`(R6)、`docs/tasks/PROGRESS-cend-report-20260916.md`
|
||||
|
||||
会话侧已经做完的(**不用再做**):`tsc` 0 错、`lint` 0 error、3355 条测试(31 条无 Docker 失败与基线逐条一致)、`next build` 四个路由渲染标记不变、`/` 首屏 gzip −0.58%。纸面三个 token 未动、`report-chart-grid-rehype.ts` 未动,均有断言守着。
|
||||
|
||||
下面全是会话做不了的:需要登录态、真实报告数据、真实浏览器和打印机/PDF 导出。
|
||||
|
||||
---
|
||||
|
||||
## A. 20 张盘以上的报告仍然不重叠、滚动不卡(最高优先,关联 BUG-616 / BUG-617)
|
||||
|
||||
阅读页这一轮并进了 app 外壳,滚动容器的所有权变了,盘面 grid 必须复验。
|
||||
|
||||
1. 打开一份**九节、20 张盘以上**的完整个人报告。
|
||||
2. 从头滚到底,不要跳。
|
||||
3. 通过标准:
|
||||
- 每张北印盘各占一格,**没有任何两张叠在同一位置**;
|
||||
- 连续滚动不掉帧、不卡顿,尤其是经过密集盘面那几节时;
|
||||
- 页面**不出现横向滚动条**(宽表格在自己的框里横滚是对的)。
|
||||
|
||||
## B. 打印 / 导出 PDF(本轮风险最高的一项)
|
||||
|
||||
阅读页现在坐在外壳里,而 `window.print()` 打的是整个文档。
|
||||
|
||||
1. 在同一份报告里点「打印」,另存为 PDF。
|
||||
2. 通过标准:
|
||||
- 纸上**没有**左侧栏、**没有**顶部那条 46px 窄栏、**没有**右侧目录;
|
||||
- 报告**不是只印出一页**——九节应当完整分页;
|
||||
- 纸面是浅色(打印锁定浅色调色板),正文不是白底白字;
|
||||
- 页边距是 A4 / 13mm 12mm 14mm 的样子;
|
||||
- 宽表格在纸上不被裁掉右边。
|
||||
3. 顺带在微信内置浏览器里打开同一页点打印,应当出现「请在系统浏览器中打开本页后打印」的提示,而不是印出一份坏的。
|
||||
|
||||
## C. 右侧常驻目录
|
||||
|
||||
1. 桌面宽度(> 860px)打开报告。
|
||||
2. 通过标准:
|
||||
- 目录在**正文右侧**,滚动时一直在;
|
||||
- 往下滚,高亮项跟着当前看到的章节走;
|
||||
- 点任一节,跳到该节标题,**标题没有被顶部那条操作条盖住**;
|
||||
- 目录自己内容超长时在目录框里滚,不把页面撑长。
|
||||
|
||||
## D. 窄屏
|
||||
|
||||
1. 把窗口拉窄到 860px 以内(或用手机)。
|
||||
2. 通过标准:右侧目录栏消失;正文上方出现一个**收起的**「目录」,点开可用、能跳转。
|
||||
3. 再窄到手机宽度:侧栏变抽屉,从顶栏的按钮能拉开、能关上,关上后焦点不丢。
|
||||
|
||||
## E. 报告中心行式列表
|
||||
|
||||
1. 账号里同时存在**生成中**与**已完成**至少各一份报告时打开「我的报告」。
|
||||
2. 通过标准:
|
||||
- 列表是**一列行**,不是卡片方阵;每行左边一枚带色点的状态 chip;
|
||||
- 「已完成」「生成中」「未完成」三种色点一眼分得开(也请在深色模式下看一遍);
|
||||
- 生成中那行能被读屏软件播报(macOS VoiceOver / Windows 讲述人均可);
|
||||
- 行右侧:已完成是「导出报告(.md)」+「查看报告」,生成中是「查看进度」,失败行右侧**没有**按钮;
|
||||
- 每行的第二行小字是「创建时间 · 深度 · 主题」,**不应出现**任何「N 节」「N 张盘」之类的数字(接口没有给,前端不许编)。
|
||||
3. 把窗口拉窄到 767px 以内:行内操作换到正文下方、左对齐,不出现横向滚动。
|
||||
|
||||
## F. 失败行
|
||||
|
||||
1. 找一份失败的报告(或等一次真实失败)。
|
||||
2. 通过标准:失败原因写在**行内**(不是挤在右边一小块),能完整读到;不因为太长把行撑破。
|
||||
|
||||
## G. 导出报告(.md)的错误提示
|
||||
|
||||
1. 断网后在「已完成」那行点「导出报告(.md)」。
|
||||
2. 通过标准:该行出现一行红色错误文字,读屏软件按 alert 播报;再联网重试能成功;期间不会重复触发第二次导出。
|
||||
Reference in New Issue
Block a user