feat(ui): 星盘/星历/报告中心并入 app 外壳,侧栏不再消失
Independent Staging Quality Gate / validate (push) Failing after 9m57s
Independent Staging Quality Gate / publish (push) Skipped

三个页面此前各是脱离外壳的独立全屏路由,各写了一套一样的
*-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:
Jesse_Chen
2026-09-16 06:03:30 +00:00
co-authored by Claude Opus 5
parent b3ea8c336e
commit 50ce02c837
15 changed files with 680 additions and 263 deletions
+29 -2
View File
@@ -373,12 +373,39 @@ Every assistant reply moves through the same states on both chat surfaces, and e
Text release is paced, not animated: the frame buffer commits at most once per animation frame and reveals `max(2, ⌈backlog ÷ 12⌉)` characters per frame, so a burst catches up in about twelve frames and a slow model never reads as stalled. The reader's own toggle on the timeline always wins over the state default. Under `prefers-reduced-motion: reduce` the collapse and the label fade are instant; the pacing stays, because it is content arrival rather than decoration.
### Secondary page shell
`/chart`, `/ephemeris` and `/reports` share the chat shell through
`SecondaryShell`. Each used to own a full-screen layout — a `*-shell` root, a
`*-topbar` with one 「返回对话」 link, and a `*-hero` with a page-sized h1 —
three copies of one skeleton, none of them carrying the sidebar.
- **Composition:** `SidebarProvider``AppNavRail``SidebarInset.chat-panel.secondary-panel`.
The header is the same 46px row the chat page uses: sidebar trigger, page name,
an optional quiet note chip, and up to two right-aligned actions. The body is
the only thing that scrolls, and it owns the page floor (`--color-canvas-soft`).
- **The rail is read-only.** It carries the brand, 新建对话, the three page
actions, and one flat 「最近」 list whose rows navigate to `/?c=<uuid>`.
Renaming, pinning, archiving, deleting and the account menu stay on `/`: they
are backed by `Home()`'s optimistic-update and rollback layer, and lifting that
so four routes can share it is far more than these pages need. The footer shows
identity and balance as text, not as a menu trigger.
- **Loading:** the rail renders its nav immediately and fills the list when
`/api/sessions` returns. While that is in flight the list area carries static
copy — never a skeleton, per the unified-loading ruling.
- **Signed out:** the rail degrades to brand, nav, a 「登录后可以看到你的对话」
line, and a 去登录 footer. It never renders an error state of its own.
- **Page name:** lives in the header, so none of these pages carries an `h1` any
more. A page's own reassurance copy (the chart page's cost-and-speed eyebrow,
for one) stays in the body; it is content, not chrome.
### Personal report centre
- **Structure:** full-page archive on the page floor. A compact back control, serif page title, supporting copy, one generate action, then a list of report cards.
- **Structure:** inside the app shell, not a page of its own. The name 「我的报告」 sits in the 46px header with 「生成完整报告」 as the header's one action; the body is supporting copy, the overview line, then the list of report cards.
It used to be a standalone full-screen route with a `report-center-shell` root, a `report-center-topbar` holding one 「返回对话」 link, and a `report-center-hero` with a page-sized h1 — the sidebar vanished the moment you opened it, and the only way back was that link.
- **Surface:** `--color-canvas-soft` floor; cards use `--color-canvas`, a warm hairline, and `--radius-lg`. No drop shadow. Status is a caption badge, not a colored block.
- **Typography:** page title uses `--type-display-lg` / `--font-display` at weight 400 with `text-wrap: balance`. Card titles are `--type-title-md` at weight 500. Body stays `--type-body-md` or `--type-body-sm`.
- **Width:** 900px centered, matching long-form chat reading. Cards stack below 720px.
- **Width:** 900px centered, matching long-form chat reading. Cards stack at the global 767px cut.
- **Actions:** “生成完整报告” is the one filled action. A ready report keeps “查看报告” as the primary document action and “导出报告(.md)” beside it. The export downloads the same longform Markdown the detail page renders. Generating copy is one-step (“正在生成报告,大约 1030 秒”); there is no chapter-count progress. Card summary is a deterministic excerpt of the Markdown 「摘要」 section, stored on the cover document at generation time.
- **States:** loading, empty, populated, generating, ready, failed, unauthorized, list error. Export work uses the shared inline spinner inside the initiating button, reports a short row-local error, and never starts a second writing flow. A missing Markdown appendix is an old report: the detail page asks the reader to regenerate.
- **Accessibility:** back, generate, refresh, and row actions are 44px. Status text uses a live region while a report is generating.