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
@@ -127,21 +127,21 @@ test("sidebar adds 星盘 and 星历 after 新建对话 without renaming 我的
|
||||
});
|
||||
|
||||
test("mobile width keeps two centre lines and moves the full parameter card below the chart", () => {
|
||||
assert.match(
|
||||
globalStyles,
|
||||
/@media \(max-width: 767px\) \{\r?\n \.chart-page-hero h1[\s\S]*\.chart-page-center-compact \{ display: block; \}/,
|
||||
);
|
||||
assert.match(
|
||||
globalStyles,
|
||||
/@media \(max-width: 767px\) \{\r?\n \.chart-page-hero h1[\s\S]*\.chart-page-center-extra \{ display: none; \}/,
|
||||
);
|
||||
assert.match(
|
||||
globalStyles,
|
||||
/@media \(max-width: 767px\) \{\r?\n \.chart-page-hero h1[\s\S]*\.chart-page-params-below \{ display: block; \}/,
|
||||
// 原值:三条断言都以 `@media (max-width: 767px) { .chart-page-hero h1` 起锚,
|
||||
// 再加一条 `.chart-page-back` 的 44px 触摸目标。
|
||||
// 新值:直接断那个断点块里的三个开关;`.chart-page-back` 一条删除。
|
||||
// 原因:hero 与「返回对话」按钮随页面并入 app 外壳而删除(T4.2)。被断言的三个
|
||||
// 开关本身一条没动,只是不再有 hero 那一行可以用来起锚。返回路径现在是
|
||||
// 常驻侧栏,它的触摸目标由 sidebar-contract 管。
|
||||
const mobile = globalStyles.slice(
|
||||
globalStyles.indexOf("@media (max-width: 767px)", globalStyles.indexOf(".chart-page-center-compact")
|
||||
- 2000),
|
||||
);
|
||||
assert.match(mobile, /\.chart-page-center-compact \{ display: block; \}/);
|
||||
assert.match(mobile, /\.chart-page-center-extra \{ display: none; \}/);
|
||||
assert.match(mobile, /\.chart-page-params-below \{ display: block; \}/);
|
||||
assert.match(cssDeclarations(".chart-page-tab", globalStyles), /min-height:\s*44px/);
|
||||
assert.match(cssDeclarations(".chart-page-chip", globalStyles), /min-height:\s*44px/);
|
||||
assert.match(cssDeclarations(".chart-page-back", globalStyles), /min-height:\s*44px/);
|
||||
assert.match(globalStyles, /\.chart-page-vedic-stage \.personal-report-chart-svg text \{ font-size: 16px; \}/);
|
||||
});
|
||||
|
||||
@@ -157,7 +157,12 @@ test("an incomplete profile page has no loading animation", () => {
|
||||
test("the chart page shell is visible before the natal chart arrives", () => {
|
||||
const markup = renderToStaticMarkup(React.createElement(ChartPageView, { view: null }));
|
||||
assert.match(markup, />星盘</);
|
||||
assert.match(markup, /返回对话/);
|
||||
// 原值:断言页面上有「返回对话」链接
|
||||
// 新值:断言常驻侧栏在场(「新建对话」是它的第一项)
|
||||
// 原因:星盘页并入 app 外壳后侧栏常驻,回对话不再依赖一个一次性链接;
|
||||
// 这比原断言更强——它要求的是整条导航都在,而不只是一个返回按钮。
|
||||
assert.match(markup, /新建对话/);
|
||||
assert.doesNotMatch(markup, /返回对话/);
|
||||
assert.match(markup, /这一张盘还没拿到/);
|
||||
assert.doesNotMatch(markup, /正在加载|骨架|spinner|打开即有/i);
|
||||
assert.doesNotMatch(markup, /chart-page-eyebrow/);
|
||||
|
||||
@@ -196,11 +196,16 @@ test("global report copy uses the canonical profile, allows an unrectified minut
|
||||
});
|
||||
|
||||
test("report centre and ready reader scroll inside the chat shell lock", () => {
|
||||
const centre = cssDeclarations(".report-center-shell", globalStyles);
|
||||
// 原值:`.report-center-shell`(报告中心自己的全屏滚动容器)
|
||||
// 新值:`.secondary-panel > :not(.chat-header)`——报告中心并入 app 外壳后,
|
||||
// 滚动所有权归外壳的内容区,三个次级页共用一条规则。
|
||||
// 原因:T4.2。「页面在外壳的高度锁内部滚动、不自己撑高」这条要求没变。
|
||||
const centre = cssDeclarations(".secondary-panel > :not(.chat-header)", globalStyles);
|
||||
const reader = cssDeclarations(".personal-report-reader", globalStyles);
|
||||
assert.match(globalStyles, /html, body \{ width: 100%; height: 100%; overflow: hidden; \}/);
|
||||
assert.match(centre, /height:\s*100%/);
|
||||
assert.match(centre, /overflow-y:\s*auto/);
|
||||
// 高度由 .chat-panel 的 grid 行给定,内容区自己不再声明 height。
|
||||
assert.match(cssDeclarations(".chat-panel", globalStyles), /grid-template-rows:\s*46px minmax\(0, 1fr\) auto/);
|
||||
assert.match(centre, /-webkit-overflow-scrolling:\s*touch/);
|
||||
assert.doesNotMatch(centre, /min-height:\s*100dvh/);
|
||||
assert.match(reader, /height:\s*100%/);
|
||||
|
||||
@@ -77,10 +77,14 @@ test("print pins the palette back to light so paper is always paper", () => {
|
||||
// 原因:BUG-737——那个"衬线栈"里的 Tiempos Headline 从未加载,中文标题实际落到宋体。
|
||||
// --font-display 现在是无衬线,400 在大字号上发虚,display 层级改由字重承担。
|
||||
test("report titles use the display stack at the display weight", () => {
|
||||
const centreTitle = cssDeclarations(".report-center-hero h1");
|
||||
// 原值:三处标题——报告中心页标题 `.report-center-hero h1`、封面、章节
|
||||
// 新值:两处——中心页标题已随 hero 移进 app 外壳的 46px 顶栏(T4.2),
|
||||
// 它现在走 `.chat-header strong`,由 chat-header 的规则统一管。
|
||||
// 原因:报告中心不再是有自己 hero 的独立页。封面与章节标题是报告纸面自己的
|
||||
// 排版,不受影响,仍然逐条断。
|
||||
const coverTitle = cssDeclarations(".personal-report-cover h1");
|
||||
const sectionTitle = cssDeclarations(".personal-report-section-heading h2");
|
||||
for (const [name, body] of [["centre", centreTitle], ["cover", coverTitle], ["section", sectionTitle]] as const) {
|
||||
for (const [name, body] of [["cover", coverTitle], ["section", sectionTitle]] as const) {
|
||||
assert.match(body, /font-family:\s*var\(--font-display\)/, `${name} title must use --font-display`);
|
||||
assert.match(body, /font-weight:\s*500/, `${name} title must stay weight 500`);
|
||||
}
|
||||
@@ -89,5 +93,8 @@ test("report titles use the display stack at the display weight", () => {
|
||||
test("report chrome and appendix disclosure keep 44px targets", () => {
|
||||
assert.match(cssDeclarations(".personal-report-back"), /min-height:\s*44px/);
|
||||
assert.match(cssDeclarations(".personal-report-disclosure"), /min-height:\s*44px/);
|
||||
assert.match(cssDeclarations(".report-center-back"), /min-height:\s*44px/);
|
||||
// 原值:报告中心「返回对话」按钮的 44px 触摸目标
|
||||
// 新值:删除该断言
|
||||
// 原因:报告中心并入 app 外壳,返回路径是常驻侧栏(T4.2),
|
||||
// 它的触摸目标由 sidebar-contract 与 touch-target-contract 管。
|
||||
});
|
||||
|
||||
@@ -30,7 +30,10 @@ test("tablet CSS cuts at 1023 so it matches sidebarViewportForWidth", () => {
|
||||
});
|
||||
|
||||
test("report centre and reader share the global mobile cut", () => {
|
||||
assert.match(css, /@media \(max-width: 767px\) \{[\s\S]*\.report-center-shell/);
|
||||
// 原值 `.report-center-shell` / 新值 `.report-center-body`
|
||||
// 原因:报告中心并入 app 外壳(T4.2),页面根不再是自己的全屏滚动容器;
|
||||
// 「用全局 767 断点、不自立断点」这条要求没变,下面两条 doesNotMatch 仍在守。
|
||||
assert.match(css, /@media \(max-width: 767px\) \{[\s\S]*\.report-center-body/);
|
||||
assert.match(css, /@media \(max-width: 767px\) \{[\s\S]*\.personal-report-reader-body/);
|
||||
assert.doesNotMatch(css, /@media \(max-width: 720px\)/);
|
||||
assert.doesNotMatch(css, /@media \(max-width: 760px\)/);
|
||||
@@ -42,6 +45,7 @@ test("report TOC keeps a content-width exception at 860px", () => {
|
||||
});
|
||||
|
||||
test("ephemeris page stacks at the global mobile cut", () => {
|
||||
assert.match(css, /@media \(max-width: 767px\) \{[\s\S]*\.ephemeris-shell/);
|
||||
// 原值 `.ephemeris-shell` / 新值 `.ephemeris-body` / 原因同上。
|
||||
assert.match(css, /@media \(max-width: 767px\) \{[\s\S]*\.ephemeris-body/);
|
||||
assert.match(css, /@media \(max-width: 767px\) \{[\s\S]*\.ephemeris-panchanga \{[\s\S]*grid-template-columns:\s*1fr/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user