星盘页(T5.1): - 五个体系 tab 从五枚描边药丸改成顶栏下方一条下划线式 tab 行, flex-wrap: nowrap + 容器自身横滚,375px 下仍是一行;底线画在滚动 容器的 border 上,不随内容滚走。眉标(BUG-717 的成本与速度承诺) 原文保留,位置从 tab 行上方移到下方,让 tab 行直接接住 46px 顶栏。 - 分盘 chips 保留但降一级:去描边去卡面、caption 字号、三级色, 当前项只留浅底。它是一个 tab 内部的筛选器,不是第二层导航。 - 参数从「中宫叠卡(手机两行/桌面六行)+ 盘下重复卡 + media query 三选一」收成一张八行键值表。同一组事实原本画三遍,改一处要改三处。 .chart-page-center-card / -compact / -extra / .chart-page-params-below / .chart-page-boundary-desktop 五个类连同 767/768 开关块一并删除。 view.vedic.boundary 那句准确性边界句原文未动,紧跟在表下方,且 DOM 里从出现两次变成一次。 - 三张行星表(印度宿度 / 西洋相位 / 七政十一曜)全部包上横向滚动 容器,wrap 补 min-width: 0 —— 少了它 grid item 按内容定宽,表格会 撑宽页面而不是在自己盒子里滚动。原先只有第一张有 wrap。 星历页(T5.2): - 日期导航从三个并排等宽按钮改成居中的 ‹ 日期 ›:日期是主语,两枚 44px 箭头是把手。箭头字形 aria-hidden,可访问名仍是「前一天」 「后一天」,读屏不丢方向。 - 「今天」只在非今日时以文字按钮出现在日期下方;在今日时整个不渲染, 而不是渲染成禁用按钮——灰掉的按钮仍要被读、仍要被 Tab 跳过。 - 三段统一成同一套卡片:框从内容上移到段落,.ephemeris-panchanga 的 dl 网格原样保留。收尾句原文不变,但从底部第四张卡降为页尾一行小字。 两条既有断言改动,测试文件内就地写了原值/新值/原因,进度记录成表: 参数断点开关的三条断言换成「参数只有一张键值表且那五个类不再有规则」 (更强,且避开了原写法 indexOf 未命中后静默锚错断点块的陷阱); 「今天」在场的单状态断言拆成今日/非今日两状态并加断不出现 disabled。 测试总数 3350 → 3352,失败清单与基线逐条一致(31 条无 Docker 缺口)。 tsc 0 错;lint 0 error / 118 warning(与基线同数);/ /chart /ephemeris 渲染标记均保持 ○ Static,/reports 仍 ƒ;主 CSS gzip 36,745 → 36,788 (+0.12%)。行星表 375px 横滚、tab 行横滚、读屏播报无真机验收,已在 进度记录里如实写成环境缺口。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
14 KiB
PROGRESS · 星盘 + 星历 R5
任务书:docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.md 的 R5
执行方式:直接执行模式(subagent)
分支:codex/cend-chart-eph-20260916
基线:origin/staging = 50ce02c8(R4 次级页外壳合入后)
BUG 段:不占用(产品改造,未发现真实缺陷)
开工基线实测
在 50ce02c8 干净检出、npm ci 之后实测:
| 项 | 基线值 |
|---|---|
tsc --noEmit |
0 错 |
npm run lint |
0 error / 118 warning |
npm test |
tests 3350 · pass 3304 · fail 31 · skipped 15 |
next build 路由标记 |
/ ○ · /chart ○ · /ephemeris ○ · /reports ƒ |
| 主 CSS chunk | 242,608 字节(gzip 36,745) |
31 条失败全部是无 Docker 的既有缺口,清单落在 /tmp 基线文件,改后逐条对比。
T5.1 · 星盘页
做了什么
| 任务书要求 | 做法 |
|---|---|
| 五个体系 tab 改顶栏下方下划线式 tab 行 | .chart-page-tabs 从「五枚描边药丸 + 可换行」改成一条 flex-wrap: nowrap + 自身横滚的下划线行,整行 1px 底线,当前项 2px 下划线。JSX 里眉标从 tab 行之上移到之下,让 tab 行直接接住 46px 顶栏 |
| 分盘 chips 保留、视觉降一级 | .chart-page-chip 从与 tab 共用的药丸规则里拆出来:去描边、去卡面、caption 字号、三级文字色,当前项只留一块浅底。触摸目标仍 44px |
| 参数收进统一键值表 | 新增 chart-param-table.tsx(由 chart-param-lines.tsx 改名而来),八行 dl 键值表。删除 .chart-page-center-card / -compact / -extra / .chart-page-params-below / .chart-page-boundary-desktop 五个类及其 767/768 开关块 |
| 边界句放表下方、文案一字不改 | {view.vedic.boundary} 直接跟在表后。改前 DOM 里它出现两次(一次给手机一次给桌面,靠 CSS 藏一个),现在只渲染一次。句子本身是服务端字符串,没有被碰过 |
| 行星表加横向滚动容器 | .chart-page-planet-table-wrap 加 min-width: 0(缺它 grid item 按内容定宽,表格撑宽页面而不是滚动),表加 min-width: 26rem。三张表全部包上:印度盘宿度、西洋相位、七政十一曜(原来只有第一张有 wrap) |
眉标 CHART_VIEW_COPY.eyebrow(「主盘直接算 · 分盘按需 · 不消耗点数」,BUG-717)保留,只换了位置。
按需 layer 拉取(onNeedLayer("chara"/"western"/"qizheng"/"varga"))、CHART_VIEW_COPY.waitingLayer 等待文案、waitingChart / waitingVarga 全部未动,没有引入 spinner 或骨架。
顺带修掉的一处错位
.chart-page-eyebrow 原本同时吃 .chart-page-body 的 padding-inline 和 767 块里给它自己的一份,手机上比 tab 行多缩进一个 --space-4。眉标移到 tab 行下方后这个错位会直接看得见,所以把 .chart-page-eyebrow 从那条共享选择器列表里摘掉了——只摘这一个选择器,同列表里 R6 的 .report-center-body / .report-center-message 一个没动。
T5.2 · 星历页
| 任务书要求 | 做法 |
|---|---|
日期导航改居中 ‹ 日期 › |
两枚 size="icon"(44px)箭头按钮夹住日期;箭头字形 aria-hidden,可访问名用 EPHEMERIS_COPY.previousDay / nextDay,读屏拿到的仍是「前一天」「后一天」 |
| 「今天」非今日时以文字按钮出现在日期下方 | isToday ? null : <Button variant="ghost">,在日期条下方 |
isToday 时不出现禁用按钮 |
整个不渲染。改前是 disabled={isToday},禁用按钮仍要被读屏读、仍要被 Tab 跳过 |
| 三个 section 统一成同一套卡片 | 框从内容上移到段落:.ephemeris-section 自己成为卡片,.ephemeris-transits / .ephemeris-events / .ephemeris-panchanga-item 交出各自的描边与卡面 |
.ephemeris-panchanga 的 dl 网格保留 |
桌面 repeat(5, ...)、手机 1fr 两条规则原样保留(viewport-breakpoint-contract 与 ephemeris-page 两个测试都在守) |
.ephemeris-footer 的处理(与任务书原文的差异,请复核):任务书 T5.2 写「底部 .ephemeris-footer 删除」。删掉的是那张卡片,EPHEMERIS_COPY.footerNote(「这一页是天象本身,不是对你的判断。」)原文保留,改挂成页尾一行 .ephemeris-outro 三级色小字。理由:该句是这一页的定性边界句(DESIGN §16「文案」条:这一页是天象本身,不得写运势结论),连句子一起删会丢掉一条口径;而既有测试 copy stays factual 也在断言它在场。若产品要的是连句子一起删,请回我,这是一行的事。
四个降级分支 natalMissing / panchangaUnavailable / transitsUnavailable / eventsUnavailable 文案与可达性未动;未登录 .ephemeris-message 分支未动。
被改的既有断言
两条,都在下面列清「原值 / 新值 / 原因」,测试文件里也就地写了同样的三行注释。没有静默弱化:两条改后都比改前严格。
1 · tests/chart-page-view.test.tsx
原测试名:mobile width keeps two centre lines and moves the full parameter card below the chart
新测试名:the parameters are one key/value table, not a per-breakpoint duplicate
| 原值 | 断 @media (max-width: 767px) 块里三个开关:.chart-page-center-compact { display: block; }、.chart-page-center-extra { display: none; }、.chart-page-params-below { display: block; } |
| 新值 | 断参数只有一张 .chart-page-param-row 网格表;并断上述五个类名(含 .chart-page-center-card、.chart-page-boundary-desktop)在整份 CSS 里都不再作为规则出现 |
| 原因 | 原断言守的正是「同一组参数按断点画三遍」,而 T5.1 要求的就是把它收成一张表,断言无法原样保留。新断言更强:不只要求当前画法对,还禁止任何按断点复制参数的写法回来 |
附带:原写法本身有个陷阱——globalStyles.indexOf(".chart-page-center-compact") 未命中时返回 -1,- 2000 之后 indexOf("@media ...", -2001) 会从文件开头重新搜,静默锚到无关的断点块上。新写法走 cssDeclarations() / 选择器集合,不做偏移量切片。
同一文件新增一条测试 the planet table scrolls inside its own box instead of widening the page(断 overflow-x: auto + min-width: 0 + 表 min-width,并断三个 tab 源文件里 <table> 数与 <div wrap> 数相等)。
2 · tests/ephemeris-page.test.tsx
| 原值 | copy stays factual 里一句 assert.match(markup, new RegExp(EPHEMERIS_COPY.today)),只在 date === today 这一种渲染下断言「今天」在场 |
| 新值 | 拆成独立测试 今天 is absent on today and present off it, never disabled:今日断「今天」不在场,非今日断在场,两个状态都断 disabled="" 不出现 |
| 原因 | T5.2 验收标准要求「在今日时不出现『今天』按钮,而不是出现一个禁用按钮」。原断言恰好只覆盖今日那一种情况,照搬会要求今日也渲染该按钮,与验收标准直接冲突。新断言覆盖两个状态并额外禁掉禁用态,严于原来 |
注:断言用 /disabled=""/ 而不是 /disabled/——Button 的 Tailwind 类串里本来就含 disabled:opacity-50,后者恒真。
测试总数 3350 → 3352(+2,均为新增,无删除)。
收尾验收对照
| 硬约束 | 结果 | 实测 |
|---|---|---|
1 · 只动 chart-page/ ephemeris/ 与 .chart-page-* / .ephemeris-* 区段 |
✅ | 见下「改了哪些文件」。.rectification-* / .settings-* / .membership-* / .report-center-* / .personal-report-* 一处未改;唯一触到的共享规则只摘掉了 .chart-page-eyebrow 一个选择器 |
| 2 · 按需 layer 行为不变、等待文案不换 spinner | ✅ | onNeedLayer 调用点与 waitingLayer / waitingChart / waitingVarga 逐字未动;a pending western tab uses inline waiting copy instead of a spinner 绿 |
| 3 · 四个降级分支文案与可达性不变 | ✅ | EPHEMERIS_COPY 未改任何字符串;四个分支渲染路径未动 |
4 · CHART_VIEW_COPY.eyebrow 保留 |
✅ | 保留,位置从 tab 行上方移到下方 |
5 · tsc 0 错 / lint 0 error,warning ≤ 118 |
✅ | tsc 0 错;lint 0 error / 118 warning,与基线同数 |
| 6 · 失败清单与基线逐条一致 | ✅ | diff base.txt new.txt → 完全相同;fail 仍 31 |
| 7 · 改断言写「原值/新值/原因」三行 | ✅ | 两条,测试文件里就地注释 + 上面成表 |
8 · /chart /ephemeris 保持 ○ Static,/ 保持 ○ |
✅ | 干净重建后 / ○ · /chart ○ · /ephemeris ○ · /reports ƒ,与基线逐个相同 |
9 · 同提交更新 DESIGN.md;新文案对照 VOICE |
✅ | §15 §16 重写,见下 |
| 10 · 不升级依赖、不修范围外 warning | ✅ | package.json / package-lock.json 未动 |
改完实测
| 项 | 基线 | 改后 | 差 |
|---|---|---|---|
tsc --noEmit |
0 错 | 0 错 | — |
npm run lint |
0 error / 118 warning | 0 error / 118 warning | 0 |
npm test |
3350 / fail 31 | 3352 / fail 31 | +2 tests,失败清单逐条一致 |
next build |
/ ○ /chart ○ /ephemeris ○ /reports ƒ |
完全相同 | — |
| 主 CSS chunk | 242,608 B(gzip 36,745) | 242,576 B(gzip 36,788) | 原始 −32 B(−0.013%),gzip +43 B(+0.12%),在 ±2% 内 |
gzip 基线是把工作树还原到 50ce02c8 后干净重建量到的,不是估算。首次改后构建复用了缓存、.next/static 里留着基线那份 CSS,两次数字曾一模一样——发现后两边都 rm -rf .next 重量,上表是重量后的值。
改了哪些文件
frontend/src/app/globals.css (.chart-page-* / .ephemeris-* 区段)
frontend/src/components/chart-page/chart-page-view.tsx
frontend/src/components/chart-page/chart-param-lines.tsx → chart-param-table.tsx(改名 + 重写)
frontend/src/components/chart-page/chart-qizheng-tab.tsx
frontend/src/components/chart-page/chart-vedic-tab.tsx
frontend/src/components/chart-page/chart-western-tab.tsx
frontend/src/components/ephemeris/ephemeris-page.tsx
frontend/tests/chart-page-view.test.tsx
frontend/tests/ephemeris-page.test.tsx
frontend/DESIGN.md
docs/tasks/PROGRESS-cend-chart-eph-20260916.md
CSS 注释配平:改前改后都是 84 / 84。含花括号的注释 1 条,是 R4 留下的 `*-shell { padding-inline }`,本轮未碰;全部删改都走整段精确字符串替换,没有按逗号切选择器。
触发的让步顺序
没有触发任何一条。 让步顺序第 3 条(T5.1 的参数统一表:若合并后信息密度过高则退回保留 .chart-page-center-card)是本轮唯一相关的一条,没有用上——八行键值表在 ≥1024 与盘面左右并排、在窄屏是一张独立卡,没有出现密度问题。其余六条属于 R4 / R6 / R7 / R8。
环境缺口(不得记作「通过」)
本会话无登录态、无 Chrome、无真实引擎返回,以下全部只有静态与单测证据,浏览器级行为未验:
- 行星表在 375px 宽下横向滚动、页面本身不横滚(任务书环境缺口原文列明的一条)。已有的是 CSS 契约断言(
overflow-x: auto+min-width: 0+ 表min-width: 26rem)与「三张表都包了 wrap」的源码断言,不是真机滚动验收。 - Tab 行在 375px 下横滚(本轮新写法):五个 Tab 不折行靠
flex-wrap: nowrap+ 容器横滚,同样只有静态证据。 ‹ 日期 ›箭头按钮的读屏可访问名:aria-label存在有断言,实际读屏播报未验。- 切 tab 时按需 layer 的真实拉取:
onNeedLayer调用点未改、单测覆盖渲染分支,但没有真实/api/varga_full、/api/western、/api/qizheng往返。 - 五要素 / 行运 / 未来九十天三张卡在真实数据下的疏密:fixture 是 golden,但卡片化后的实际观感未上真机。
以上第 1、2、3、5 条建议并进 docs/testing/ 的人工清单,与 R4 的抽屉手势、R6 的报告目录、R7 的常驻条、R8 的弹窗尺寸一起走一次真机。
事故:误 pop 了 R6 会话的 stash(已完全恢复,但请知会)
量 gzip 基线时我用了 git stash push 把改动暂存、还原到 50ce02c8 重建,再 git stash pop。stash 栈是整个仓库共享的一个 ref,不是每个 worktree 一份——在我 push 与 pop 之间,codex/cend-report-20260916(R6)那个会话也 push 了一个 stash,于是我 pop 到的是他们的 r6-wip,落进了我的工作树(personal-report-center.tsx、personal-report-page.tsx、两个 report 测试,以及他们那份 globals.css)。
处置:
- 从
git fsck --unreachable找回两个 stash commit:我的4c112c5b(r5-wip)、R6 的40ff9a82(r6-wip)。 git stash store把 R6 的40ff9a82原样放回 stash 栈(内容是 commit 对象,未被改写)。- 把误落进我工作树的 5 个文件还原成
HEAD内容,工作树回到干净基线。 git stash apply 4c112c5b取回我自己的改动,git diff HEAD --stat与 stash commit 的 stat 逐行相同(10 文件 / +336 −203)。
没有任何 R6 的改动进入本分支的提交(见上面「改了哪些文件」)。但请主会话转告 R6 会话:他们的 stash 位置变过,现在在栈上(git stash list 里的 On codex/cend-report-20260916: r6-wip),内容完整;直接 git stash pop 之前先确认自己的工作树是干净的。
教训,建议写进 AGENTS.md §3:多 worktree 共享一个 stash 栈,禁止用 git stash 做临时还原。要量基线就先提交,或另开一个一次性 worktree 构建,不要动共享的 stash。