Files
Jyotisha/docs/tasks/PROGRESS-chart-glyphs-20260918.md
T
Jesse_ChenandClaude Fable 5.1 5a1dcbd2a1
Independent Staging Quality Gate / validate (push) Failing after 8m58s
Independent Staging Quality Gate / publish (push) Skipped
feat(chart): 星盘改用行星符号与颜色,度数进行星表
北印星盘(星盘页 / 校正右栏 / 我的报告共用 VedicChartSvg)宫内不再画
「水 19°」「罗逆 29°」这类显示文本——一宫三颗星就叠三行、字号压到 8。
改成九个彩色符号横排,每行最多 3 个;逆行是符号下方一道同色横线,
第 1 宫加 As 小标,盘下方两行图例。度数、星宿、顺逆下沉到星盘页的
行星表,列从四列扩到八列,首行是上升,度数精确到分。

- 符号字体:vendored 的 9 字形 Noto Sans Symbols 子集(OFL,1.5 KB),
  经 next/font/local 挂成 --font-planet-glyphs;♀ ♂ 带 U+FE0E 防 iOS
  画成表情。构建镜像不能联网,所以必须自带,与 Inter 同一条理由。
- 颜色:--color-planet-* 九个 token,:root 与两个深色块各一份。只用于
  认星不表吉凶,罗计共用中性灰。挂 is-* 修饰类而非行内 style——报告
  文档的标记合同禁止渲染结果出现 style=,星盘不做例外。
- 数据来源:toNorthIndianChart() 加结构化 occupantGlyphs(星盘页与
  markdown fence 走这条);校正与报告两条路线的模型只有引擎 / golden
  显示文本,按首字解析,解析由测试锁住。
- 星宿沿用引擎返回名:仓库无中文宿名表,印度 27 宿与中国二十八宿不是
  同一套,不自造对照。

tsc 0 错,lint 0 error,npm test 3471→3484 条、36 红与基线逐条相同,
/ 仍 Static,首屏 JS gzip +0.29%。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
2026-09-18 00:39:13 +00:00

12 KiB
Raw Blame History

PROGRESS · 星盘改用行星符号,度数进行星表(2026-09-18)

  • 分支:codex/chart-glyphs-20260918worktree .worktrees/chart-glyphs-20260918
  • 基线:origin/staging = 84b293fbfeat(consult): 对话口气改成反差与扮演象的形状)
  • 类型:产品拍板的界面改造,非 Bug,未写 docs/BUG_HISTORY.md
  • Skill 版本:不变

1. 做了什么

盘里(星盘页 / 校正右栏 / 我的报告,同一个 VedicChartSvg)不再画「水 19°」「罗逆 29°」这类显示文本,改画彩色行星符号;度数、星宿、顺逆下沉到星盘页的行星表。

落点
符号字体 frontend/src/app/fonts/NotoSansSymbols-planets.woff21,536 B,OFL,9 字形子集),来源与取法追加在 SOURCE.txt
字体挂载 layout.tsx 第二个 localFontvariable: "--font-planet-glyphs"display: "swap"adjustFontFallback: false
符号 / 名称 / 缩写 / 颜色类 / 解析 新模块 frontend/src/lib/planet-glyphs.ts
盘面渲染 frontend/src/components/personal-report/vedic-chart-svg.tsx 重写占位层
宫内锚点 north-indian-chart-geometry.ts 新增 occupantAnchor()
结构化字段 report-chart-block.tstoNorthIndianChart() 产出 occupantGlyphschart-view-contract.tsnorthIndianChartSchema 加同名可选字段
上升行 chart-view-contract.ts 新增 chartViewAscendantRowSchemachart-view-mapper.ts 新增 ascendantRow(),挂在 vedic.ascendant
行星表 chart-vedic-tab.tsx 四列 → 八列;chart-format.ts 新增 formatDegreeMinutes()
颜色与样式 globals.css9 个 --color-planet-*:root + 两个深色块 + @theme inline 各一份)、符号 / 图例 / 逆行横线 / As / 表格列样式

数据来源:结构化优先,两条路线仍按显示文本解析

任务书要求「优先加结构化字段」。实际分成两条:

  • 加了结构化字段toNorthIndianChart() 在格式化字符串之前就知道星体与逆行,直接挂 occupantGlyphs: { key, retrograde, degree }[]。星盘页(chart-view-mapperbuildVargas)与 markdown 里的 jyotish-chart fence 都走这条,渲染层不解析字符串。chartViewOkSchema.parse() 会剥未声明字段,所以同步在 northIndianChartSchema 里加了可选字段,否则结构化信息会在序列化边界丢掉。

  • 仍按显示文本解析

    • 校正右栏houseTableToNorthIndianChart() 的输入是 parseRectificationHouseTable() 解出来的引擎 house_table,只有 {house, sign, occupants: string[]},且 scripts/rectification/house_table.py 给的是全名太阳 / 水星 / 罗睺),没有逆行位、没有度数。
    • 我的报告ChartV1.housespersonal-report-contract.ts 里的 z.strictObject,形状来自 Python goldenpersonal_report_contract.py),按 AGENTS §7.4 不得手造。

    这两条走 parseChartOccupant():首字 ∈ 日太月火水木金土罗计 是为 太阳),或首段拉丁字母 ∈ Sun…Ketu,逆行看是否含「逆」,并与 chart.planets[].retrograde 取或。认不出来的标签按原样画字(回归到旧行为,不吞数据)。解析本身由 tests/chart-planet-glyphs.test.tsx 的三条用例锁住(水 19° / 罗逆 29°、全名、英文名、未知名)。

两个非任务书写法的选择

  1. 颜色挂修饰类而不是行内 style 第一版按预览稿写了 style={{ fill: … }},撞到 tests/personal-report-view.test.tsassert.doesNotMatch(markup, /style\s*=/i)——报告文档的标记合同禁止渲染结果里出现 style=,这条是挡 agent 产出的内容自带 CSS 的,不该为星盘破例。改成 is-sunis-node / is-ascendant 修饰类,textfilllinestroke(合并成一条会给符号描边)。
  2. 星宿名沿用引擎返回值,不译中文。 任务书写「星宿中文」,但仓库里(前端 chart-view-labels.ts、Python scripts/)都没有星宿中文名表,chart-basics-tab.tsx 现在也是直接显示引擎返回的名字。印度 27 宿与中国二十八宿不是同一套,现造一张对照表是在下一个领域判断,超出本单授权,已写进 DESIGN.md §15。

其它细节

  • 图例两行(5 + 4 + 「横线=逆行」)。九对符号 + 汉字加那句话在 400 单位一行里会挤,按任务书让步走两行;图例落在方阵下方的 52 单位条带里,viewBox 高度由 400 变 452,不压宫位。
  • As 不占 MAX_RENDERED_OCCUPANTS 名额:它来自几何不来自数据,占名额会让第 1 宫比别的宫少显示一颗星。
  • .chart-page-vedic-stage … text { font-size: 16px } 这条通选被拆开(见下节)。
  • 打印:@media print* { print-color-adjust: exact !important } 已在,符号是普通 <text>,颜色 token 在 :root 定义、打印块不覆盖,符号照常可见且带色。

2. 测试数字(同机、同命令,各测一次)

基线 84b293fb 本轮
./node_modules/.bin/tsc --noEmit 0 错 0 错
npm run lint 0 error / 118 warning 0 error / 118 warning 未新增 warning
npm test tests 3471 3484 +13(新文件 tests/chart-planet-glyphs.test.tsx
npm test pass 3420 3433 +13
npm test fail 36 36 排序后 diff 为空
npm test skipped 15 15
next build/ ○ Static ○ Static 通过
首屏 gzip.next/static 全部 *.js 逐个 gzip 合计,68 个文件) 1,450,774 B 1,455,017 B +4,243 B+0.29%,在 ±2% 内
CSS gzip.next/static 全部 *.css4 个文件) 40,215 B 41,110 B +895 B+2.23%
字体资产 NotoSansSymbols_planets.*.woff2 1,536 B 独立 media 资产,display: swap,不计入首屏 JS

CSS +2.23% 的构成:9 个颜色 token × 3 个块 + @theme inline 一份(36 行)、17 条 is-* 上色规则(盘面 text/line 各一套)、9 条表格上色规则、符号 / 图例 / 逆行 / As / 表格列的字号与对齐。这一节的口径按 PROGRESS-starter-greeting-20260917.md 的先例是 *.js,CSS 单列出来是因为本轮变化确实落在 CSS 上,JS 与 CSS 合计 1,490,989 → 1,496,127 = +0.34%。

失败清单:基线 36 条与本轮 36 条排序后 diff 为空(database / live staging sync / ephemeris / admin 等套件)。全部是无 Docker、无数据库、无外部引擎的既有环境缺口,与本轮无关。

next build 取基线数字的方法(未用 git stashAGENTS §3.5):git diff > /tmp/…/chart-glyphs.patchgit checkout -- frontend/ → 把三个未跟踪新文件临时移出 worktree(否则 tsc 会在基线上报 occupantGlyphs 不存在,构建 exit 1、.next/static 是脏的)→ rm -rf .next && npm run build → 取数 → git apply + 移回。两侧都是干净 .next 的全量构建。

3. 被改的既有断言(原值 / 新值 / 原因)

文件 · 测试 原值 新值 原因
tests/chart-page-view.test.tsx · the parameters are one key/value table… assert.match(globalStyles, /\.chart-page-vedic-stage \.personal-report-chart-svg text \{ font-size: 16px; \}/) 改断 .personal-report-chart-muted 那一条仍是 16px,并新增 assert.doesNotMatch(globalStyles, /\.chart-page-vedic-stage \.personal-report-chart-svg text \{/) 盘内文字不再只有一种字号:符号按窄盘宽度定在 22 viewBox 单位。通选 text 规则会把符号一起压回 16,正是要避免的。DESIGN §15 的原意是「小字在 ~318px 盘宽下不低于 12px」,不是「所有字一样大」。断言主语(宫位序号仍 16)未改,新增的反向断言比原断言更强
tests/personal-report-markdown-view.test.tsx · markdown view draws the North Indian component… assert.match(markup, new RegExp(planetDisplayLabel("Saturn", 3.4, true)))(盘里必须出现「土逆 3°」) 改断盘里出现 且带 class="personal-report-chart-retrograde is-saturn"新增 doesNotMatch 那串文本、doesNotMatch(/°/)、「逆」在整份标记里只剩图例那一个 产品拍板把盘内文字换成符号,原断言守的正是本轮要去掉的东西,无法原样保留。断言主语(这颗逆行土星来自 fence 数据、画在盘里)未改;新增的三条反向断言把「不再写字」这一半也锁住。planetDisplayLabel() 本身没删——occupants 字符串仍喂 aria-label 与宫位表
tests/site-style-isolation-contract.test.ts · Inter is loaded through next/font… assert.match(layout, /className=\{inter\.variable\}/) assert.match(layout, /className=\{${inter.variable}[^]*}/)` 本轮新增第二个 next/font 变量(--font-planet-glyphs),两个都要挂在同一个 <html> 上。断言主语(Inter 以 CSS 变量形式挂在根元素、且是第一个)未改,只是不再要求 className 只有它一个

扩写但未弱化的既有断言:tests/personal-report-view.test.tseach house owns its occupant coordinates…no dangerous HTML… 两条一字未改且仍绿——前者的 +7 是靠「As 不占溢出名额」保住的,后者的 doesNotMatch(/style\s*=/i) 是本轮改用修饰类的直接原因。

4. 新增测试(tests/chart-planet-glyphs.test.tsx13 条)

  1. 含「水 19°」「罗逆 29°」的宫渲染出 ☿ 与 ☊,☊ 带 personal-report-chart-retrograde,☿ 不带;盘里不再有「°」,「逆」只剩图例那一个。
  2. 校正面的全名(太阳 / 水星 / 罗睺 / 计都)映射到同一组符号。
  3. PLANET_GLYPH.venus / .mars 逐字等于 +U+FE0E / +U+FE0E,且渲染结果里带。
  4. 第 1 宫有且只有一个 As,挂 is-ascendant
  5. 显式写出的上升占位(上升点 / Lagna)并入同一个 As,不画两次。
  6. 图例只在有星时出现(空盘 viewBox 仍是 0 0 400 400),出现时九个符号 + 九个汉字齐全。
  7. 一宫 6 颗星恰好两行(y 去重后 2 个);第 7 颗起出现 +2
  8. toNorthIndianChart() 挂上 occupantGlyphs,同时 occupants 字符串原样保留。
  9. 认不出的占位名按原样画字。
  10. parseChartOccupant() 覆盖四种在用写法 + 未知名。
  11. 9 个颜色 token 在 :rootprefers-color-scheme 深色块、[data-theme="dark"] 深色块各恰好一次,且深浅值不相同。
  12. 字体是 vendored wOF2--font-planet-glyphs 存在、没有 next/font/google、字体栈逐字正确、SOURCE.txt 记了许可证。
  13. 星盘页表头恰为「行星 黄道经度 速度 宫位 星宿 宿主 pada 状态」,上升是第一行,度数带分,符号列上色,旧表头「星座与度数」已消失。

5. 没做 / 做不了

  • 星宿中文名:见 §1.2。仓库无对照表,不自造。
  • 参照里其它页签(宫位 / 大运 / 星力 / 瑜伽):任务书明确本单不做,未做。
  • 真机走查:无登录态、无 Chrome,以下留给 docs/testing/——iOS Safari 上 ♀ ♂ 是否确为文本呈现(U+FE0E 生效)、Android / Linux 上九个符号是否都落在 vendored 子集、深浅两主题下九种颜色在盘面与表格的实际可读性、375px 下行星表在自己的盒子里横滚而页面不横滚、报告打印时符号与颜色是否随纸出。
  • height="auto"VedicChartSvg 上这个非法属性是 TASK-console-noise-20260916(BUG-906)的范围,本轮未顺手动。