feat(chart): 星盘改用行星符号与颜色,度数进行星表
Independent Staging Quality Gate / validate (push) Failing after 8m58s
Independent Staging Quality Gate / publish (push) Skipped

北印星盘(星盘页 / 校正右栏 / 我的报告共用 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
This commit is contained in:
Jesse_Chen
2026-09-18 00:39:13 +00:00
co-authored by Claude Fable 5.1
parent 84b293fb47
commit 5a1dcbd2a1
20 changed files with 1239 additions and 73 deletions
Binary file not shown.
+11
View File
@@ -3,3 +3,14 @@ File: InterVariable-latin.woff2
Taken from @fontsource-variable/inter@5.3.0 files/inter-latin-wght-normal.woff2
Upstream: https://github.com/rsms/inter
License: SIL Open Font License 1.1 (OFL.txt)
Noto Sans Symbols — 9-glyph planet subset
File: NotoSansSymbols-planets.woff2
Taken from https://fonts.googleapis.com/css2?family=Noto+Sans+Symbols&text=☉☽☿♀♂♃♄☊☋
(the CSS that URL returns points at one fonts.gstatic.com subset file; that file is this one)
Glyphs: U+2609 ☉ 日 · U+263D ☽ 月 · U+263F ☿ 水 · U+2640 ♀ 金 · U+2642 ♂ 火 ·
U+2643 ♃ 木 · U+2644 ♄ 土 · U+260A ☊ 罗 · U+260B ☋ 计
Upstream: https://github.com/notofonts/symbols
License: SIL Open Font License 1.1 (OFL.txt), © the Noto Project Authors
Why vendored: the publish image has no network at build time, so next/font/google
cannot be used. Same reason as Inter above.
+135 -2
View File
@@ -47,6 +47,15 @@
--color-dark-hover: var(--color-dark-hover);
--color-dark-muted: var(--color-dark-muted);
--color-dark-border: var(--color-dark-border);
--color-planet-sun: var(--color-planet-sun);
--color-planet-moon: var(--color-planet-moon);
--color-planet-mars: var(--color-planet-mars);
--color-planet-mercury: var(--color-planet-mercury);
--color-planet-jupiter: var(--color-planet-jupiter);
--color-planet-venus: var(--color-planet-venus);
--color-planet-saturn: var(--color-planet-saturn);
--color-planet-node: var(--color-planet-node);
--color-planet-ascendant: var(--color-planet-ascendant);
--radius-sm: 8px;
--radius-md: 8px;
--radius-lg: 12px;
@@ -103,6 +112,19 @@
--color-dark-hover: rgba(251, 250, 247, .1);
--color-dark-muted: #aaa8a2;
--color-dark-border: rgba(251, 250, 247, .16);
/* Planet identity colours for the North Indian chart and the chart page's
planet table. They answer "which body is this", nothing else — no benefic
or malefic reading is encoded here, which is why Rahu and Ketu share one
neutral grey. Both nodes and the ascendant are chart points, not bodies. */
--color-planet-sun: #c94f4f;
--color-planet-moon: #4c7fd1;
--color-planet-mars: #d2553c;
--color-planet-mercury: #3f9a6b;
--color-planet-jupiter: #c9902e;
--color-planet-venus: #c9689b;
--color-planet-saturn: #5b6bb0;
--color-planet-node: #6b7280;
--color-planet-ascendant: #b0413e;
--shadow-soft: 0 1px 2px rgba(29, 29, 31, .06), 0 8px 20px -18px rgba(29, 29, 31, .2);
--shadow-elevated: 0 1px 2px rgba(29, 29, 31, .07), 0 12px 28px -16px rgba(29, 29, 31, .18);
/* Display and body share one sans stack. The old value led with two Anthropic
@@ -241,6 +263,18 @@
/* Hairlines that were dark-on-light have to become light-on-dark. */
--ring-hairline: oklch(1 0 0 / .14);
--sheen: rgba(255, 255, 255, .16);
/* Planet identity colours, lifted for a dark ground. Same roles, same shared
node grey; the hues are raised rather than inverted. */
--color-planet-sun: #e07a7a;
--color-planet-moon: #7fa6e6;
--color-planet-mars: #e6866f;
--color-planet-mercury: #6cc39a;
--color-planet-jupiter: #e0b45a;
--color-planet-venus: #e08fbb;
--color-planet-saturn: #8d9be0;
--color-planet-node: #a1a7b3;
--color-planet-ascendant: #e07b78;
}
}
@@ -313,6 +347,18 @@
/* Hairlines that were dark-on-light have to become light-on-dark. */
--ring-hairline: oklch(1 0 0 / .14);
--sheen: rgba(255, 255, 255, .16);
/* Planet identity colours, lifted for a dark ground. Same roles, same shared
node grey; the hues are raised rather than inverted. */
--color-planet-sun: #e07a7a;
--color-planet-moon: #7fa6e6;
--color-planet-mars: #e6866f;
--color-planet-mercury: #6cc39a;
--color-planet-jupiter: #e0b45a;
--color-planet-venus: #e08fbb;
--color-planet-saturn: #8d9be0;
--color-planet-node: #a1a7b3;
--color-planet-ascendant: #e07b78;
}
@@ -4147,6 +4193,63 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
.personal-report-chart-svg .personal-report-chart-diamond { fill: none; stroke: var(--color-border-strong); stroke-width: 1.25; }
.personal-report-chart-svg text { fill: currentColor; }
.personal-report-chart-svg .personal-report-chart-muted { fill: var(--color-ink-muted, var(--color-ink-tertiary)); }
/* Planets are symbols, not labels. Sizes are viewBox units of the 400-unit
square: at the ~318px the chart page gives the stage, 22 units lands near
17px, which is why the old blanket `text { font-size: 16px }` override had to
become per-class — it would have shrunk the symbols instead of lifting the
sign numbers. The stack leads with the vendored 9-glyph Noto subset so the
set renders identically on Android and Linux, where no system face covers it.
U+FE0E after ♀ and ♂ is what keeps iOS from drawing them as emoji, and it is
applied at the string, not here. */
.personal-report-chart-svg .personal-report-chart-glyph,
.personal-report-chart-svg .personal-report-chart-legend-glyph {
font-family: var(--font-planet-glyphs), "Apple Symbols", "Segoe UI Symbol", "Noto Sans Symbols", serif;
font-weight: 600;
}
.personal-report-chart-svg .personal-report-chart-glyph { font-size: 22px; }
.personal-report-chart-svg .personal-report-chart-legend-glyph { font-size: 14px; }
/* Retrograde is a bar under the symbol in the symbol's own colour; the chart no
longer writes the character 逆 anywhere. */
.personal-report-chart-svg .personal-report-chart-retrograde { stroke-width: 1.9; }
/* House 1 of a North Indian chart is the ascendant by construction. */
.personal-report-chart-svg .personal-report-chart-asc {
font-size: 13px;
font-weight: 700;
letter-spacing: -.02em;
}
.personal-report-chart-svg .personal-report-chart-overflow {
font-size: 13px;
fill: var(--color-ink-tertiary);
}
/* Only reached by an occupant string that names no known body — the label is
drawn as written rather than dropped. */
.personal-report-chart-svg .personal-report-chart-occupant-text { font-size: 12px; }
.personal-report-chart-svg .personal-report-chart-legend {
font-size: 12px;
fill: var(--color-ink-tertiary);
}
/* One colour per body, carried by a modifier class rather than an inline
`style` — the personal report's markup contract forbids a style attribute
anywhere in the rendered document, and the chart is not an exception to it.
`text` takes fill; the retrograde bar takes stroke. Painting both on one
selector would outline the symbols. */
.personal-report-chart-svg text.is-sun { fill: var(--color-planet-sun); }
.personal-report-chart-svg line.is-sun { stroke: var(--color-planet-sun); }
.personal-report-chart-svg text.is-moon { fill: var(--color-planet-moon); }
.personal-report-chart-svg line.is-moon { stroke: var(--color-planet-moon); }
.personal-report-chart-svg text.is-mars { fill: var(--color-planet-mars); }
.personal-report-chart-svg line.is-mars { stroke: var(--color-planet-mars); }
.personal-report-chart-svg text.is-mercury { fill: var(--color-planet-mercury); }
.personal-report-chart-svg line.is-mercury { stroke: var(--color-planet-mercury); }
.personal-report-chart-svg text.is-jupiter { fill: var(--color-planet-jupiter); }
.personal-report-chart-svg line.is-jupiter { stroke: var(--color-planet-jupiter); }
.personal-report-chart-svg text.is-venus { fill: var(--color-planet-venus); }
.personal-report-chart-svg line.is-venus { stroke: var(--color-planet-venus); }
.personal-report-chart-svg text.is-saturn { fill: var(--color-planet-saturn); }
.personal-report-chart-svg line.is-saturn { stroke: var(--color-planet-saturn); }
.personal-report-chart-svg text.is-node { fill: var(--color-planet-node); }
.personal-report-chart-svg line.is-node { stroke: var(--color-planet-node); }
.personal-report-chart-svg text.is-ascendant { fill: var(--color-planet-ascendant); }
.personal-report-chart-figure {
min-width: 0;
max-width: 360px;
@@ -4643,7 +4746,13 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
padding: var(--space-3);
}
.chart-page-vedic-stage .personal-report-chart-svg { margin-top: 0; }
.chart-page-vedic-stage .personal-report-chart-svg text { font-size: 16px; }
/* Only the small type is lifted for the chart page's narrow stage. Raising
every `text` would also resize the planet symbols, which are already sized
for this width. */
.chart-page-vedic-stage .personal-report-chart-svg .personal-report-chart-muted { font-size: 16px; }
.chart-page-vedic-stage .personal-report-chart-svg .personal-report-chart-asc { font-size: 15px; }
.chart-page-vedic-stage .personal-report-chart-svg .personal-report-chart-legend { font-size: 14px; }
.chart-page-vedic-stage .personal-report-chart-svg .personal-report-chart-legend-glyph { font-size: 16px; }
/* One key/value table replaces the `.chart-page-center-card` overlay (two lines
on mobile, six on desktop, sat on the chart's geometric centre) and the
`.chart-page-params-below` card that repeated the same six lines for mobile.
@@ -4689,7 +4798,9 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
}
.chart-page-planet-table {
width: 100%;
min-width: 26rem;
/* Eight columns now (symbol+name, longitude, speed, house, nakshatra, its
lord, pada, dignity); 26rem was sized for four and crushed the last three. */
min-width: 34rem;
border-collapse: collapse;
background: var(--color-canvas);
font-size: var(--type-body-sm);
@@ -4713,6 +4824,28 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
font-size: var(--type-caption);
font-weight: 500;
}
.chart-page-planet-table th,
.chart-page-planet-table td { white-space: nowrap; }
/* The symbol column repeats the chart's colour so a row and a house read as the
same body; the stack matches the chart's for the same coverage reason. */
.chart-page-planet-glyph {
font-family: var(--font-planet-glyphs), "Apple Symbols", "Segoe UI Symbol", "Noto Sans Symbols", serif;
font-size: var(--type-title-sm);
font-weight: 600;
}
.chart-page-planet-abbr { color: var(--color-ink-tertiary); }
.chart-page-planet-glyph.is-sun { color: var(--color-planet-sun); }
.chart-page-planet-glyph.is-moon { color: var(--color-planet-moon); }
.chart-page-planet-glyph.is-mars { color: var(--color-planet-mars); }
.chart-page-planet-glyph.is-mercury { color: var(--color-planet-mercury); }
.chart-page-planet-glyph.is-jupiter { color: var(--color-planet-jupiter); }
.chart-page-planet-glyph.is-venus { color: var(--color-planet-venus); }
.chart-page-planet-glyph.is-saturn { color: var(--color-planet-saturn); }
.chart-page-planet-glyph.is-node { color: var(--color-planet-node); }
.chart-page-planet-glyph.is-ascendant { color: var(--color-planet-ascendant); font-weight: 700; }
.chart-page-planet-table td.chart-page-planet-mid { text-align: center; }
/* Figures line up column-wise instead of drifting with glyph width. */
.chart-page-planet-num { font-variant-numeric: tabular-nums; }
.chart-page-boundary {
margin: var(--space-4) 0 0;
color: var(--color-ink-tertiary);
+17 -1
View File
@@ -15,6 +15,22 @@ const inter = localFont({
weight: "100 900",
});
// Vendored 9-glyph Noto Sans Symbols subset (OFL) — ☉ ☽ ☿ ♀ ♂ ♃ ♄ ☊ ☋, 1.5 KB.
// The North Indian chart draws planets as these symbols, so the face has to be
// present rather than hoped for: Apple Symbols / Segoe UI Symbol cover the set
// on their own platforms, Android and most Linux browsers do not.
// `adjustFontFallback: false` on purpose: the synthetic Arial-metric fallback
// next/font/local would otherwise inject carries its own ♀ ♂ and would swallow
// the rest of the stack for the glyphs it cannot draw.
const planetGlyphs = localFont({
src: "./fonts/NotoSansSymbols-planets.woff2",
display: "swap",
variable: "--font-planet-glyphs",
weight: "400",
style: "normal",
adjustFontFallback: false,
});
export const metadata: Metadata = {
title: "Jyotisha · 印度占星",
description: "与 Mastra Agent 对话,基于星盘证据讨论事业、关系与时间窗口。",
@@ -32,7 +48,7 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
&& process.env.NEXT_PUBLIC_ENABLE_REACT_DEVTOOLS === "1";
return (
<html lang="zh-CN" className={inter.variable} suppressHydrationWarning>
<html lang="zh-CN" className={`${inter.variable} ${planetGlyphs.variable}`} suppressHydrationWarning>
<head>
{/* Synchronous on purpose: a pinned theme must be on <html> before the
first paint, or the page flashes the other theme on every load. */}