diff --git a/CHANGELOG.md b/CHANGELOG.md index f3643395..03c062df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # 印度占星 Skill 更新日志 +## 2026-10-21 — 上升的状态格改成「/」 + +- 星盘页本命表、月亮盘和分盘表里,上升的状态格写「/」。悬停、聚焦或点一下会说明:上升点是命盘的起点,不是行星,不判断入旺或落陷。 +- 个人报告「行星主星与状态」表的上升行同样写「/」,表下面加同一句说明。英文版写英文说明。已经生成的旧报告要重新生成才变。 +- Skill 版本不变。 + ## 2026-10-21 — 合盘时间线写出起止,删掉的模型改用默认模型 - 关系时间线每段标题写出起止年月。第一段从本月开始时,起点写「现在」或 Now。 diff --git a/docs/tasks/PROGRESS-chart-ascendant-slash-20261021.md b/docs/tasks/PROGRESS-chart-ascendant-slash-20261021.md new file mode 100644 index 00000000..e5be2112 --- /dev/null +++ b/docs/tasks/PROGRESS-chart-ascendant-slash-20261021.md @@ -0,0 +1,27 @@ +# 进度:上升状态格改成「/」(issue #87,2026-10-21) + +## 做了什么 + +- 星盘页本命表、月亮盘、分盘表里,上升的状态格写「/」,带 class `chart-page-asc-status`。悬停、键盘聚焦、手机点一下都走现有 `ChartSymbolHelp`。说明句是「上升点是命盘的起点,不是行星,不判断入旺或落陷」。读屏读到的是这一句。 +- 报告「行星主星与状态」表的上升行,中文和英文都写「/」。表下面紧跟一行说明:中文用上面那句,英文是 “The ascendant is the starting point of the chart, not a planet, so it has no dignity.” +- `frontend/DESIGN.md` 两处口径、`frontend/docs/VOICE.md` 上升那半句、`CHANGELOG.md` 已改。旧报告要重新生成才会变成「/」。 + +## 命令与结果 + +| 检查 | 命令 | 结果 | +| --- | --- | --- | +| 类型 | `frontend` 下 `tsc --noEmit -p .` | 通过,退出码 0 | +| 代码检查 | `frontend` 下 `npm run lint` | 0 个错误,122 条原有警告 | +| 星盘页相关测试 | `tsx --test`:`chart-varga-table.test.tsx`、`chart-moon-transit.test.tsx`、`chart-page-hook.test.ts` | 40 通过,0 失败,0 取消 | +| 报告上升行 | `.venv` 的 `pytest tests/test_report_chart_blank_columns.py -q` | 16 通过,退出码 0 | + +全量 `npm test` 和 `next build` 没有跑,交给验收。 + +## 偏离 + +1. 任务书要求 #86 合入 `origin/staging` 后再开工。开工时 #86 仍是待验收,`885bd566` 不在 `origin/staging`。核对这份提交:不改 `chart-vedic-tab.tsx`;`pl9_reader_export.py` 只把另一张表的速度改成 4 位小数;`DESIGN.md` 改的是顶栏人名按钮。本轮按产品要求直接做,基线是当时的 `origin/staging` `9812f3173`。#86 若先合入,本分支需要 rebase。预期重叠只在 `DESIGN.md` 的不同段落。 +2. `VOICE.md` 仍写上升「不适用」。改了这一处,避免和本单相反。任务书点名的是 `DESIGN.md`。 + +## 环境 + +本机没有登录后点过页面。Python 用的是 agent-loop 工作树里的 `.venv`。 diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 22cdbd2c..4ec6adfa 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -512,6 +512,6 @@ | `TASK-synastry-staging-fix-20261021.md` | `PROGRESS-synastry-staging-fix-20261021.md` | #85 婚恋合盘 staging:P0 `.gte` 已在 staging(BUG-1399)。本分支补时间线起止、英文分析逻辑、删模型后旧对话改用默认模型、选人页开关文案,以及合盘创建真库测试(BUG-1400~1403) | 待验收 | `codex/synastry-staging-fix-20261021` | | `TASK-synastry-report-chat-fix-20261020.md` | `PROGRESS-synastry-report-chat-20261018.md` | 大运核对要认报告里的「小运」和英文 mahadasha / antardasha;预填文案只留一句 | 已验收 | `549e37b99` | | `TASK-other-subject-birth-time-edit-20261021.md` | — | 其他人的出生时间可以修改(去掉 `chart_profiles_guard_birth` 的填报时间不可改;推翻 people-archive P1 §验收 2 该条;active 锁不变) | 排队(等 #86 合入) | `codex/other-subject-birth-time-edit-20261021`;动表;不进循环 | -| `TASK-chart-ascendant-slash-20261021.md` | — | #87 上升状态格「不适用」改「/」+ 悬停 / 点按说明;报告上升行同改并在表下加说明(推翻 10-15 口径) | 待领取(等 #86 合入) | `codex/chart-ascendant-slash-20261021` | +| `TASK-chart-ascendant-slash-20261021.md` | `PROGRESS-chart-ascendant-slash-20261021.md` | #87 上升状态格「不适用」改「/」+ 悬停 / 点按说明;报告上升行同改并在表下加说明(推翻 10-15 口径) | 待验收 | `codex/chart-ascendant-slash-20261021` | | `TASK-report-export-dialog-polish-20261021.md` | — | #90 导出弹窗:英文不可用说明改顶部浮层(每次打开一次);选中态描边 / 圆点 / 勾选框用主题色、底色中性(推翻 DESIGN「Selected states use canvas-muted + ink」,BUG-1078 主语保留) | 待领取(可与 #89 并行) | `codex/report-export-dialog-polish-20261021` | | `TASK-report-english-han-leak-20261021.md` | `PROGRESS-report-english-han-leak-20261021.md` | #89 个人报告英文版约 4 成整份丢弃:漏译汉字源头补英文孪生字段、修吃空格;200 张固定种子回归 + 快速门 smoke(复发自 BUG-1272);中文逐字节不变 | 待领取(最先做;#91/#92 在后) | `codex/report-english-han-leak-20261021` | diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md index d6af4424..d726d93c 100644 --- a/frontend/DESIGN.md +++ b/frontend/DESIGN.md @@ -1184,7 +1184,7 @@ D40 / D45 / D60 的表格上方,出生时间不是「已校正」时多一句 | pada | 1–4,居中 | | 状态 | 尊贵 / 庙旺等,原样保留,只是挪到最后一列 | -第一行是上升。星宿 / 宿主 / pada 用引擎按上升经度算出的值,和九曜同一个算法(2026-10-03,BUG-1201;之前引擎漏算,这三格一直是「—」);旧缓存里没有这三项时仍写「—」,页面不自己推算。上升没有尊贵,状态格写「不适用」(2026-10-15,与分盘表、报告同一口径;不再写「—」)。数字列 `tabular-nums`,符号列与宿主列居中。 +第一行是上升。星宿 / 宿主 / pada 用引擎按上升经度算出的值,和九曜同一个算法(2026-10-03,BUG-1201;之前引擎漏算,这三格一直是「—」);旧缓存里没有这三项时仍写「—」,页面不自己推算。上升没有尊贵,状态格写「/」(2026-10-21,推翻 2026-10-15 的「不适用」;与分盘表、报告同一口径)。这一格是 `chart-page-asc-status`,鼠标悬停、键盘聚焦或手机点一下,用盘面同一套气泡显示「上升点是命盘的起点,不是行星,不判断入旺或落陷」。读屏读到的是这一句。数字列 `tabular-nums`,符号列与宿主列居中。 本轮不新增第二层页签:参照里「宫位 / 大运 / 星力 / 瑜伽」等页签的内容后续另立单。 @@ -1193,7 +1193,7 @@ D40 / D45 / D60 的表格上方,出生时间不是「已校正」时多一句 | 盘型 | 列 | 说明 | | --- | --- | --- | | 本命 | 上面八列 | 不变(与基线渲染逐字节相同) | -| D2–D60 | 行星 · 分盘星座(度°分′)· 宫位 · 本命星座 · 庙旺;仅 D9 再有同位 | 宫位从该分盘上升数;庙旺是引擎给的十档(入旺 / 本垣 / 入庙 / 极友 / 入友 / 中性 / 入敌 / 极敌 / 落陷 / 落陷取消),措辞与本命「状态」列一字不差(2026-10-03,BUG-1202,取代原先「入旺 / 自宫 / 落陷,其余—」的三档);旧缓存没有引擎等级时整列不显示,页面不自算;上升此格「不适用」。2026-09-29 收口:仅 D9 的 D1↔D9 同座比较显示此列,其他分盘不显示。2026-10-13:同座写「是」,不同座写「否」,表头仍是「同位」(不再用横杠,避免看成没算)。两列各由服务端能力声明控制,缺声明保守隐藏,不以是否命中推断支持性。**没有星宿 / 宿主 / 足**:那是本命经度的属性。逆行在行星格后标「逆」(分盘不改顺逆)。首行是分盘上升 | +| D2–D60 | 行星 · 分盘星座(度°分′)· 宫位 · 本命星座 · 庙旺;仅 D9 再有同位 | 宫位从该分盘上升数;庙旺是引擎给的十档(入旺 / 本垣 / 入庙 / 极友 / 入友 / 中性 / 入敌 / 极敌 / 落陷 / 落陷取消),措辞与本命「状态」列一字不差(2026-10-03,BUG-1202,取代原先「入旺 / 自宫 / 落陷,其余—」的三档);旧缓存没有引擎等级时整列不显示,页面不自算;上升此格写「/」,悬停或点按同一句「上升点是命盘的起点,不是行星,不判断入旺或落陷」(2026-10-21,推翻 2026-10-15)。2026-09-29 收口:仅 D9 的 D1↔D9 同座比较显示此列,其他分盘不显示。2026-10-13:同座写「是」,不同座写「否」,表头仍是「同位」(不再用横杠,避免看成没算)。两列各由服务端能力声明控制,缺声明保守隐藏,不以是否命中推断支持性。**没有星宿 / 宿主 / 足**:那是本命经度的属性。逆行在行星格后标「逆」(分盘不改顺逆)。首行是分盘上升 | | 月亮盘 | 本命八列 | 宫位从月亮所在星座数;表头「月亮盘 · 以月亮所在星座(X)为第 1 宫」;盘上「As」画在本命上升落的那一宫(`VedicChartSvg` 的 `ascendantHouse`,其它盘默认 1 宫不变) | | Bhava | 行星 · 黄道经度 · 星座宫 · Bhava 宫 · 换宫;另一张「宫 · 宫头 · 起点(sandhi)」12 行 | Sripati。换宫的行用 `--color-selected` 浅底。拿不到真实 MC 时这一栏走失败句,不画等宫(BUG-1077) | | 行运 | 行星 · 今天的位置 · 速度 · 落本命 · 本命同星 | 表头写计算时刻「行运 · YYYY-MM-DD 12:00(时区)」,上方一句「以本命上升为第 1 宫,放的是这一天中午的行星位置。岁差和交点与本命相同。」 | diff --git a/frontend/docs/VOICE.md b/frontend/docs/VOICE.md index be7f7c22..82613eea 100644 --- a/frontend/docs/VOICE.md +++ b/frontend/docs/VOICE.md @@ -210,7 +210,7 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、 - 图标说明只写名称和已有事实:上升、行星、逆行、星座名称/编号、宫位、ASC/MC 等。没有可靠度数就不写度数,不加吉凶、性格或个人运势解释。 - 鼠标悬停、键盘聚焦、手机轻点展示同一份说明,以标记旁的小气泡出现,不遮住被说明的标记。关闭说明不改变西洋相位选择。2026-09-29 晚起不再有默认提示句(原「停在或轻点盘内标记可查看说明。」已删,BUG-1101)。 - 「我的报告」生成卡片:标题写得到什么「完整本命报告」,说明一句「星盘、力量、大运、年运和瑜伽的完整数据,中英两版;生成后可离开,完成时下方自动出现。」,按钮写动作「生成报告」。标题与按钮不说同一件事(BUG-1103)。完整数据版超过约 20 万字符时,阅读页一次只画一章,目录和「上一章 / 下一章」换章;不另做全文搜索。导出仍是原来的「导出」按钮,下载 Markdown。 -- 分盘庙旺仅指入旺、自宫、落陷三档;未命中写「—」,上升写「不适用」。同位仅 D9 展示,其他分盘不留空列。缺能力声明不装作已支持。 +- 分盘庙旺仅指入旺、自宫、落陷三档;未命中写「—」。上升不是行星,状态格写「/」,说明是「上升点是命盘的起点,不是行星,不判断入旺或落陷」。同位仅 D9 展示,其他分盘不留空列。缺能力声明不装作已支持。 ## 星盘空状态、0 年大运、星历切日期 diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 7b1e3cce..f1a376bc 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -5846,6 +5846,26 @@ button.synastry-dd-add { border-radius: 0 0 var(--radius-md) var(--radius-md); } outline: 2px solid var(--color-focus); outline-offset: 2px; } +/* The ascendant status cell is "/" plus the same bubble. Padding moves onto the + button so the tap target fills the cell without a second chrome. */ +.chart-page-planet-table td.chart-page-asc-status { padding: 0; } +.chart-page-asc-status-mark { + display: flex; + align-items: center; + justify-content: flex-start; + width: 100%; + min-height: 44px; + margin: 0; + padding: var(--space-3); + border: 0; + background: transparent; + color: inherit; + font: inherit; + line-height: inherit; + text-align: inherit; + cursor: help; +} +.chart-page-planet-table td.chart-page-planet-mid .chart-page-asc-status-mark { justify-content: center; } /* 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. */ diff --git a/frontend/src/components/chart-page/chart-vedic-tab.tsx b/frontend/src/components/chart-page/chart-vedic-tab.tsx index 596bbdb9..feffb016 100644 --- a/frontend/src/components/chart-page/chart-vedic-tab.tsx +++ b/frontend/src/components/chart-page/chart-vedic-tab.tsx @@ -77,10 +77,29 @@ function PlanetRow({ row }: { row: ChartViewPlanetRow }) { ); } +/** The ascendant is the chart's starting point, not a planet, so it has no dignity. */ +const ASCENDANT_STATUS_HELP = "上升点是命盘的起点,不是行星,不判断入旺或落陷"; + +function AscendantStatusCell({ className }: { className?: string }) { + return ( + + + + ); +} + /** * The ascendant is a chart point: nakshatra, lord and pada come from the - * engine (BUG-1201); it has no dignity, so the status cell says 不适用. - * `house` is 1 except on the Moon chart, which counts from the Moon. + * engine (BUG-1201). It is not a planet, so the status cell is "/" and + * ASCENDANT_STATUS_HELP. `house` is 1 except on the Moon chart, which counts + * from the Moon. */ function AscendantRow({ row, house = row.house }: { row: ChartViewAscendantRow; house?: number }) { return ( @@ -96,7 +115,7 @@ function AscendantRow({ row, house = row.house }: { row: ChartViewAscendantRow; {row.nakshatraLord ? : "—"} {row.pada ?? "—"} - 不适用 + ); } @@ -112,6 +131,7 @@ function NatalTable({ planets, ascendant, ascendantHouse, caption }: { caption: string; }) { return ( +
@@ -135,6 +155,7 @@ function NatalTable({ planets, ascendant, ascendantHouse, caption }: {
{caption}
+
); } @@ -166,7 +187,7 @@ function VargaAscendantRow({ row, supportsDignity, supportsVargottama }: { row: {row.signLabel} {formatDegreeMinutes(row.degreeInSign)} 1宫 {row.natalSignLabel} - {supportsDignity && 不适用} + {supportsDignity && } {supportsVargottama && {row.vargottama ? "是" : "否"}} ); @@ -183,6 +204,7 @@ export function VargaTable({ varga }: { varga: ChartViewVarga }) { supportsVargottama: varga.supportsVargottama === true, }; return ( +
@@ -202,6 +224,7 @@ export function VargaTable({ varga }: { varga: ChartViewVarga }) {
{varga.id} · {varga.meaning}
+
); } diff --git a/frontend/tests/chart-moon-transit.test.tsx b/frontend/tests/chart-moon-transit.test.tsx index 283b9014..2bac63f6 100644 --- a/frontend/tests/chart-moon-transit.test.tsx +++ b/frontend/tests/chart-moon-transit.test.tsx @@ -92,7 +92,11 @@ test("the Moon chart is the D1 counted from the Moon's sign, projected without a assert.match(ascendantRow, /Uttara Phalguni<\/td>/); assert.match(ascendantRow, /is-sun/); assert.match(ascendantRow, />1不适用不适用 { diff --git a/frontend/tests/chart-page-hook.test.ts b/frontend/tests/chart-page-hook.test.ts index 2f967c90..803a6ca5 100644 --- a/frontend/tests/chart-page-hook.test.ts +++ b/frontend/tests/chart-page-hook.test.ts @@ -186,7 +186,10 @@ test("old capability-less module cache stays hidden through direct reads and lay const d9 = view.vedic.vargas.find((item) => item.id === "D9")!; assert.equal(d9.supportsDignity, undefined); assert.equal(d9.supportsVargottama, undefined); - assert.doesNotMatch(renderToStaticMarkup(React.createElement(VargaTable, { varga: d9 })), />庙旺<|>同位<|>不适用庙旺<|>同位<|>不适用庙旺<|>同位<|chart-page-asc-status/ + // 原因:issue #87。上升状态格不再写「不适用」。旧缓存整列仍须隐藏,保护改认这个 class。 + assert.doesNotMatch(renderToStaticMarkup(React.createElement(VargaTable, { varga: d9 })), />庙旺<|>同位<|chart-page-asc-status/); assert.doesNotMatch(renderToStaticMarkup(React.createElement(ChartParamTable, { view })), /引擎|Swiss Ephemeris/); }; await h.render(null); diff --git a/frontend/tests/chart-varga-table.test.tsx b/frontend/tests/chart-varga-table.test.tsx index cefabcf5..48d07b01 100644 --- a/frontend/tests/chart-varga-table.test.tsx +++ b/frontend/tests/chart-varga-table.test.tsx @@ -99,11 +99,12 @@ test("the D1 table is byte-for-byte the table the page drew before BUG-1076, asc // (Leo 29°23′ = Uttara Phalguni pada 1, lord Sun), where the baseline had dashes. const baseline = readFileSync(new URL("./fixtures/chart-d1-table-baseline.html", import.meta.url), "utf8"); const dashes = '————'; - // 原值:上升行状态格 — - // 新值:不适用 - // 原因:产品把「—」看成没数据。上升没有尊贵,与分盘表、报告同一口径写「不适用」。基线文件仍是改前的表。 + // 原值:不适用 + // 新值:带 chart-page-asc-status 的按钮,可见「/」,说明句在 data-chart-help 与 aria-label + // 原因:issue #87。上升不是行星,状态格改「/」并悬停说明。基线 fixture 文件不改。 const filled = 'Uttara Phalguni☉' - + '1不适用'; + + '1' + + ''; assert.equal(baseline.split(dashes).length, 2, "the baseline's ascendant row is the only all-dash row"); assert.equal(table, baseline.replace(dashes, filled)); }); @@ -117,7 +118,12 @@ test("the D1 ascendant row shows the engine's nakshatra, lord and pada", async ( assert.match(ascendant, /Uttara Phalguni<\/td>/); assert.match(ascendant, /is-sun/, "Uttara Phalguni's lord is the Sun"); assert.match(ascendant, />1不适用不适用\/)[key]; @@ -155,7 +161,11 @@ test("divisional rows carry engine dignity and vargottama, and drop nakshatra co assert.match(row(markup, "火星"), />中性\(Neutral\)入敌\(Enemy Sign\)极敌\(Great Enemy\)不适用不适用([\s\S]*?)<\/tbody>/)?.[1] ?? "", />同位<|\((?:Exalted|Own Sign|Debilitated|Neutral)\)不适用不适用 { diff --git a/scripts/pl9_reader_export.py b/scripts/pl9_reader_export.py index 2f5eb1a1..c2576d16 100644 --- a/scripts/pl9_reader_export.py +++ b/scripts/pl9_reader_export.py @@ -1049,10 +1049,9 @@ def render_pl9_parity_markdown(packet: dict) -> str: if ascendant: info = _ascendant_report_fields(ascendant) asc_label = 'Ascendant' if _lang == 'en' else '上升' - dignity = 'not applicable' if _lang == 'en' else '不适用' lines.append( f"| {_cell(asc_label)} | {_cell(_token(info['sign_lord']))} | {_cell(_token(info['nakshatra_lord']))} | " - f"- | - | {_cell(dignity)} | - |" + f"- | - | {_cell('/')} | - |" ) for planet in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu'): row = _dict(planets.get(planet)) @@ -1065,6 +1064,17 @@ def render_pl9_parity_markdown(packet: dict) -> str: ) lines.extend([ '', + *( + [ + ( + 'The ascendant is the starting point of the chart, not a planet, so it has no dignity.' + if _lang == 'en' + else '上升点是命盘的起点,不是行星,不判断入旺或落陷' + ), + '', + ] + if ascendant else [] + ), ( 'Sign lord, star lord, sub lord and sub-sub lord name the ruler of the planet sign, nakshatra and KP subdivision. Strength ratio compares Shadbala with the required strength. A ratio of 1 or more meets it.' if _lang == 'en' diff --git a/tests/test_report_chart_blank_columns.py b/tests/test_report_chart_blank_columns.py index 13199233..797e6f5f 100644 --- a/tests/test_report_chart_blank_columns.py +++ b/tests/test_report_chart_blank_columns.py @@ -189,6 +189,22 @@ def test_full_data_chart_section_draws_each_ready_chart_once(editions) -> None: assert "##### Navamsha" not in section and "##### p7 · Navamsha" not in section +def _paragraph_after_heading_table(markdown: str, heading: str) -> str: + """The first non-empty line after the heading's table.""" + lines = markdown.splitlines() + start = next(i for i, line in enumerate(lines) if line.startswith("#") and heading in line) + seen_row = False + for line in lines[start + 1:]: + if line.startswith("|"): + seen_row = True + continue + if seen_row and line.strip(): + return line.strip() + if line.startswith("#"): + break + raise AssertionError(f"no paragraph after {heading}") + + def _heading_table(markdown: str, heading: str) -> list[list[str]]: """Like _table_after, but the lord table is a #### heading, not ###.""" lines = markdown.splitlines() @@ -219,8 +235,20 @@ def test_birth_position_table_starts_with_the_ascendant(editions) -> None: assert zh_rows[1][5] == "1" lords = _heading_table(zh, "行星主星与状态") assert lords[1][0] == "上升" - assert lords[1][5] == "不适用" + # 原值:lords[1][5] == "不适用" + # 新值:"/" + # 原因:issue #87。上升不是行星,状态格写「/」,说明放到表下。 + assert lords[1][5] == "/" assert lords[1][2] in {fields["nakshatra_lord"], lord_zh.get(fields["nakshatra_lord"])} + assert _paragraph_after_heading_table(zh, "行星主星与状态") == ( + "上升点是命盘的起点,不是行星,不判断入旺或落陷" + ) + en_lords = _heading_table(en, "Planet Lords and Dignity") + assert en_lords[1][0] in {"Ascendant", "Lagna", "上升"} + assert en_lords[1][5] == "/" + assert _paragraph_after_heading_table(en, "Planet Lords and Dignity") == ( + "The ascendant is the starting point of the chart, not a planet, so it has no dignity." + ) en_rows = _heading_table(en, "Planetary Raw Positions") assert en_rows[1][0] in {"Ascendant", "Lagna", "上升"} assert fields["nakshatra"] in en_rows[1][6]