From 5aaae4690c1b96bde18f77eba3c899163d8f3e41 Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Thu, 22 Oct 2026 16:22:54 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E6=96=87=E5=AF=BC=E5=87=BA=E7=9A=84?= =?UTF-8?q?=E5=88=86=E7=9B=98=E5=AE=AB=E4=BD=8D=E5=88=97=E8=A1=A8=E6=94=B9?= =?UTF-8?q?=E7=94=A8=E4=B8=AD=E6=96=87=E6=98=9F=E5=BA=A7=E5=90=8D=EF=BC=8C?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E5=88=97=E8=A1=A8=E4=BB=8D=E7=94=A8=E8=8B=B1?= =?UTF-8?q?=E6=96=87=E6=98=9F=E5=BA=A7=E5=90=8D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor --- docs/BUG_HISTORY.md | 16 ++++++++++++++++ frontend/DESIGN.md | 2 +- frontend/docs/VOICE.md | 1 + frontend/src/lib/report-export-blocks.ts | 11 +++++++++-- frontend/tests/report-export-blocks.test.ts | 12 +++++++++++- 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 1f53ffe4..7d3b2d0f 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -37227,3 +37227,19 @@ - 相关记录:BUG-1312、BUG-1413、BUG-1414。issue #91。 - 复发自:BUG-1312。旧的回译路径还在,短词表盖不住新句子。 - 修复版本:分支 `codex/full-data-parity-1-20261021`。 + +## BUG-1417 | 中文导出的分盘宫位列表印英文星座名 + +- 状态:resolved +- 首次发现:2026-10-21 +- 最近更新:2026-10-22 +- 影响面:个人报告导出 Markdown 里的分盘宫位文字列表。 +- 用户现象:中文导出写成「第 1 宫 · Leo:……」。英文导出的星座名本来就是英文,没有这个问题。 +- 触发条件:导出报告里勾选分盘。图盘文字列表由 `chartBlockText` 生成。 +- 根因:`report-export-blocks.ts` 的 `houseLine` 中文分支直接使用 `house.sign`。`toNorthIndianChart` 给出的是英文星座名。 +- 修复:中文列表改用 `SIGN_ZH`(白羊、金牛……,不带「座」)。英文列表仍用英文星座名。 +- 验证:`frontend/tests/report-export-blocks.test.ts`。中文列表不含英文星座名,英文列表含。 +- 防复发:该测试同时断言中文不含英文星座名、英文仍含英文星座名。 +- 相关记录:BUG-1312。issue #91。 +- 复发自:无。 +- 修复版本:分支 `codex/full-data-parity-1-20261021`。 diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md index 1f0dc4bd..b15398ac 100644 --- a/frontend/DESIGN.md +++ b/frontend/DESIGN.md @@ -664,7 +664,7 @@ Action results and errors never take page layout. They are toasts from `src/lib/ - **Buttons on the report surfaces:** the BUG-1078 report-only override (2026-09-28) was replaced the same day by the site-wide solid-button pair (BUG-1079, see Button); report buttons look as before. Outline buttons there (the header 「生成报告」 while submitting) now fade like outline buttons elsewhere instead of the report-only grey. The selected export preset is `--color-canvas-muted` + `--color-ink` + a `--color-ink-secondary` edge, not the `--color-action-soft` fill (a red-brown block in dark mode). - **导出报告 sheet (redesigned 2026-09-30, product: 「分块导出太丑」):** native modal ``, bottom sheet below 860px (up to 85dvh) and a centred `min(520px, 92vw)` panel at ≥860px, app colours only. Top to bottom: title 「导出报告」 with a × (读屏名「关闭导出」); when the report has an English edition, a two-way segment 中文 / English (starts on the edition being read, does not switch the page) with one caption 「拿去问 ChatGPT、Claude 等 AI,建议选 English,术语与原典一致。」, otherwise no caption: opening the sheet raises one info toast (id `report-export-english`) saying why; two radio cards 「整篇报告」 (subline counts 章正文 / 张分盘 / 张核对表) and 「自选章节」; only in 自选, a plain checklist grouped 正文 / 分盘 / 核对表, each group one tri-state checkbox (✓ / − / empty) plus a chevron, 正文 open and the other groups folded. Rows are borderless 44px lines, no per-block character counts. The file size appears once, on the footer button: 「导出整篇 · 约 195 KB」 / 「导出 6 项 · 约 80 KB」 / disabled 「先勾选要导出的部分」. Selected states (issue #90, 2026-10-21, overrides the 2026-09-30 neutral rule): the scope card's border and radio dot, checked / mixed boxes, and the selected language segment use `--color-action-strong` (text `--color-action`); backgrounds stay neutral; never `--color-action-soft` or `--color-action-fill` (BUG-1078: no dark red-brown block in dark mode). Escape, backdrop or × dismisses; focus returns without scrolling. No spinner, clipboard or extra fetch. - **Export selection:** 整篇 = every block of the chosen edition; 自选 starts with all 正文 chapters ticked. Switching the sheet's language resets the checklist to that edition's chapters (block ids differ per edition). Presets 常用 / 全部分盘 / 仅核对表 and 全选 were retired with the redesign. -- **Export content:** full body alone remains byte-identical to the retired ordinary Markdown export. Selected chart/table blocks append new content; selecting all groups is not byte-identical to body-only. D13 requires safe SVG in each chart, reusing the projected engine SVG or the existing hook-free Vedic drawing; offline fallback uses readable monochrome glyphs/legend and presentation attributes. No chart JSON fences or internal audit fields. Fact-table labels/precision/subtables match the reader. Full body uses the historical filename; excerpt adds `-节选`. The existing downloader appends `.md`. +- **Export content:** full body alone remains byte-identical to the retired ordinary Markdown export. Selected chart/table blocks append new content; selecting all groups is not byte-identical to body-only. D13 requires safe SVG in each chart, reusing the projected engine SVG or the existing hook-free Vedic drawing; offline fallback uses readable monochrome glyphs/legend and presentation attributes. No chart JSON fences or internal audit fields. Fact-table labels/precision/subtables match the reader. Full body uses the historical filename; excerpt adds `-节选`. The existing downloader appends `.md`. Chinese chart house lines use `SIGN_ZH`(白羊、金牛……,不带「座」). English chart house lines keep the English sign name. - **Structure:** inside the app shell, like every other secondary page — `/reports/[reportId]` was the last standalone full-screen route, and opening a report used to drop the reader out of the app entirely. 「个人报告」 sits in the 46px header; below it the reader owns its own scroll boundary, carrying the sticky screen chrome (back and the labelled 「导出」 button) and then the longform Markdown body beside a persistent TOC rail. Every phase — loading, generating, timed-out, and each error — renders inside the same shell, so the way back is always the rail. The five-chapter writer document is storage-only and is not rendered. Reports without Markdown show “旧版本报告,请重新生成”. - **TOC levels (2026-09-29):** chapters (`##`) are `--color-ink` at weight 600 with `--space-3` above each new chapter; sections (`###`) are indented `--space-5` and stay `--color-ink-secondary`. The two levels read apart without relying on the current-item pill. diff --git a/frontend/docs/VOICE.md b/frontend/docs/VOICE.md index d25ad6f1..17939e16 100644 --- a/frontend/docs/VOICE.md +++ b/frontend/docs/VOICE.md @@ -249,6 +249,7 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、 ## 报告分块导出与删除 - 列表已完成行只留「查看报告」。阅读页操作条只留「返回报告中心」、「中文 / English」切换(只在有英文版的报告上出现)与带字的「导出」按钮(读屏名「导出报告」,2026-09-29 起不再是只有图标的按钮)。不再显示「下载原始附录」或「打印」入口文案;浏览器 Ctrl+P 排版仍保留。 +- 中文导出里的分盘宫位列表写「第 1 宫 · 白羊:……」,星座名用星盘页那组(白羊、金牛、双子、巨蟹、狮子、处女、天秤、天蝎、射手、摩羯、水瓶、双鱼),不带「座」,不写 Aries 这类英文名。英文列表仍写 `House 1 · Aries: …`。 - 导出面板(2026-09-30 重设计):标题「导出报告」;语言「中文 / English」下一句「拿去问 ChatGPT、Claude 等 AI,建议选 English,术语与原典一致。」;范围「整篇报告」(副行如「7 章正文 + 22 张分盘 + 8 张核对表」)/「自选章节」(副行「只带走需要的部分」);分组名「正文 / 分盘 / 核对表」带「n/N」;按钮「导出整篇 · 约 X KB」「导出 n 项 · 约 X KB」,一项没选时「先勾选要导出的部分」。成功提示「已导出」。没有英文版时弹窗内不写字,打开时顶部浮层提示一次:「这份报告生成于英文版上线前,重新生成即可获得中英两版。」或「这份报告的英文版没有生成成功,只能导出中文版。」 - 删除确认:「确定删除这份报告?」;按钮:「删除 / 取消」;请求中:「删除中…」;失败:「报告没能删除,请稍后重试。」失败保留原行。 - 日期中文全写;深度「简版 / 标准深度 / 深度 / 研究级」;主题复用现有中文映射,未知 id 原样回退,不造含义。 diff --git a/frontend/src/lib/report-export-blocks.ts b/frontend/src/lib/report-export-blocks.ts index 12510873..ece77c12 100644 --- a/frontend/src/lib/report-export-blocks.ts +++ b/frontend/src/lib/report-export-blocks.ts @@ -6,6 +6,7 @@ import { renderReportFocusFences } from "./report-focus-block"; import type { ReportFactTable } from "./report-fact-table-schema"; import { factCellText, factStatusLabel } from "./report-fact-table-display"; import type { ReportLanguage } from "./report-language"; +import { SIGN_ZH, type SignName } from "./chart-view-labels"; import { projectOrdinaryReportMarkdown } from "./report-public-projection"; export type ReportExportBlock = Readonly<{ @@ -17,10 +18,16 @@ export type ReportExportBlock = Readonly<{ }>; export type ReportExportPreset = "common" | "charts" | "tables"; +function houseSignLabel(sign: string, language: ReportLanguage): string { + if (language === "en") return sign; + return sign in SIGN_ZH ? SIGN_ZH[sign as SignName] : sign; +} + function houseLine(house: { houseNumber: number; sign: string; occupants: readonly string[] }, language: ReportLanguage): string { + const sign = houseSignLabel(house.sign, language); return language === "en" - ? `- House ${house.houseNumber} · ${house.sign}: ${house.occupants.join(", ") || "—"}` - : `- 第 ${house.houseNumber} 宫 · ${house.sign}:${house.occupants.join("、") || "—"}`; + ? `- House ${house.houseNumber} · ${sign}: ${house.occupants.join(", ") || "—"}` + : `- 第 ${house.houseNumber} 宫 · ${sign}:${house.occupants.join("、") || "—"}`; } export function chartBlockText(block: ReportChartBlock, language: ReportLanguage = "zh"): string { diff --git a/frontend/tests/report-export-blocks.test.ts b/frontend/tests/report-export-blocks.test.ts index 54cb09db..78f7d239 100644 --- a/frontend/tests/report-export-blocks.test.ts +++ b/frontend/tests/report-export-blocks.test.ts @@ -9,6 +9,7 @@ import { ordinaryOutputLeaks, projectOrdinaryReportMarkdown } from "../src/lib/r import { assembleReportFactTables } from "../src/lib/report-fact-tables"; import { assembleLongformCharts } from "../src/lib/personal-report-generation"; import { parseReportChartBlock, toNorthIndianChart } from "../src/lib/report-chart-block"; +import { SIGN_ZH, type SignName } from "../src/lib/chart-view-labels"; const fixture = JSON.parse(readFileSync(new URL("./fixtures/report-density-fictional-reader.json", import.meta.url), "utf8")); const engine = JSON.parse(readFileSync(new URL("./fixtures/report-density-fictional-engine.json", import.meta.url), "utf8")); @@ -125,9 +126,18 @@ test("chart text matches all real golden houses and their occupants", () => { const match = /```jyotish-chart\n([\s\S]*?)```/.exec(fixture.markdown)!; const chart = parseReportChartBlock(match[1])!; const text = chartBlockText(chart); + const english = chartBlockText(chart, "en"); + const englishSign = /\b(?:Sagittarius|Capricorn|Aquarius|Aries|Taurus|Gemini|Cancer|Leo|Virgo|Libra|Scorpio|Pisces)\b/; for (const house of toNorthIndianChart(chart).houses) { - assert.ok(text.includes(`第 ${house.houseNumber} 宫 · ${house.sign}:${house.occupants.join("、") || "—"}`)); + const sign = house.sign as SignName; + // 原值:`第 ${house.houseNumber} 宫 · ${house.sign}:` + // 新值:`第 ${house.houseNumber} 宫 · ${SIGN_ZH[sign]}:` + // 原因:中文导出图盘列表用 SIGN_ZH(不带「座」);英文列表仍用英文星座名。 + assert.ok(text.includes(`第 ${house.houseNumber} 宫 · ${SIGN_ZH[sign]}:${house.occupants.join("、") || "—"}`)); + assert.ok(english.includes(`House ${house.houseNumber} · ${house.sign}:`)); } + assert.equal(englishSign.test(text), false); + assert.equal(englishSign.test(english), true); }); test("fact group exports every visible subtable with original precision and no source paths", () => { for (const table of factTables) {