From 67dd505e1d2aeeff8d799bb5d257bf5b175ef20c Mon Sep 17 00:00:00 2001 From: aliyun1121003339 Date: Mon, 6 Jul 2026 19:54:19 +0800 Subject: [PATCH] feat: honor png trns render smoke --- ...-integration-and-figure-generation-plan.md | 3 +- ...ration-and-figure-generation-plan.zh-CN.md | 5 +- .../maintainer/circuitikz-export-prototype.md | 2 +- .../circuitikz-export-prototype.zh-CN.md | 2 +- .../circuitikz-figure-generation-roadmap.md | 6 +- ...cuitikz-figure-generation-roadmap.zh-CN.md | 6 +- .../circuitikz/circuitikzRenderSmoke.ts | 77 +++++++-- src/tests/circuitikzRenderSmoke.test.ts | 148 +++++++++++++++++- website/docs/features/diagrams.mdx | 4 +- .../current/features/diagrams.mdx | 4 +- 10 files changed, 225 insertions(+), 32 deletions(-) diff --git a/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.md b/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.md index d18af809..eb047bc4 100644 --- a/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.md +++ b/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.md @@ -207,6 +207,7 @@ Phase E implementation status on 2026-07-05: `scripts/export-diagram-artifact.js - [x] Add non-interlaced 1/2/4/8-bit indexed-color PNG smoke support through packed sample decoding, PLTE palette decoding, and optional tRNS alpha. - [x] Add non-interlaced 1/2/4/8/16-bit grayscale PNG smoke support through packed/direct sample decoding and normalized gray values. - [x] Add non-interlaced 16-bit grayscale-alpha/RGB/RGBA PNG smoke support through direct sample normalization. +- [x] Add grayscale/RGB PNG `tRNS` transparent sample handling so transparent-only pixels are not counted as visible foreground. - [x] Add conservative SVG bounded-canvas and obvious text-overlap diagnostics. - [x] Add transform-aware geometry for common SVG group and element transforms before bounded-canvas/text-overlap checks. - [x] Add conservative SVG label-vs-drawing overlap diagnostics through `render-svg-label-overlap`. @@ -228,7 +229,7 @@ Phase E implementation status on 2026-07-05: `scripts/export-diagram-artifact.js - [x] Add reusable `RenderArtifact.diagnostics` summary counts and show them in the preview diagnostics panel and preview history entries. - [x] Add definition-local `transform` handling for reusable path-only SVG glyphs so dvisvgm-style scaled or mirrored label geometry is checked before `` placement. -Phase F implementation status on 2026-07-05: `src/diagram/adapters/circuitikz/circuitSpec.ts` and `src/diagram/adapters/circuitikz/circuitikzExporter.ts` now implement a constrained `CircuitSpec -> circuitikz` prototype. `scripts/export-circuitikz.js` and `npm run diagram:export-circuitikz` export deterministic LaTeX for `common-source-nmos-v1` and `cmos-inverter-v1`. `src/diagram/adapters/circuitikz/circuitikzExporter.ts` also exposes `createCircuitTopologySignature` and `assertCircuitTopologyUnchanged`, and `scripts/export-circuitikz.js` exposes that guard through `--topology-reference` so topology-preserving repair candidates can change labels/layout while being rejected before output if they add, remove, or rewire electrical topology. `src/diagram/adapters/circuitikz/circuitikzRepairBrief.ts`, `--repair-brief-output`, and `--repair-brief` now produce and consume schema `notemd.circuitikz.repair-brief.v1`, a repair handoff that carries the source `CircuitSpec`, topology signature, compile/render diagnostics, allowed changes, prohibited topology changes, next verification steps, and a candidate validation gate that can reject repaired specs before output is written. `src/diagram/adapters/circuitikz/circuitikzDiagnostics.ts` parses existing compile logs into actionable diagnostics for missing packages, unknown TikZ keys, undefined control sequences, generic LaTeX errors, emergency stops, and overfull layout warnings. `src/diagram/adapters/circuitikz/circuitikzCompileRunner.ts` can run an explicitly configured local renderer without shell parsing, feed the generated `{jobName}.log` back into diagnostics, and optionally require a concrete non-empty output artifact through `--expected-artifact`. `src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` now adds SVG-specific checks for root/dimensions/visible drawing elements, optional text tokens through `--expected-svg-text`, path-only label classification through `pathOnlyGlyphUseCount` and `render-svg-text-path-only`, conservative bounded-canvas diagnostics through `render-svg-out-of-bounds`, obvious text-overlap diagnostics through `render-svg-text-overlap`, positioned `tspan` label geometry for LaTeX-style SVG output, path-only glyph label-vs-drawing overlap diagnostics through `render-svg-path-glyph-overlap`, definition-local `transform` handling for reusable path-only glyphs before `` placement, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, SVG number grammar for leading-dot decimals and explicit plus signs, stroke-width-aware SVG bounds and label-vs-drawing overlap checks, transform-aware geometry for common group and element transforms, plus PNG screenshot checks for dimensions, non-background pixels, 1/2/4/8-bit indexed-color PLTE palettes with optional tRNS alpha, 1/2/4/8/16-bit grayscale samples, 8/16-bit grayscale-alpha/RGB/RGBA direct samples, `foregroundBounds`, `foregroundDensity`, edge-touching clipped content through `render-png-content-clipped`, and dense foreground blocks through `render-png-foreground-dense`. `docs/maintainer/fixtures/circuitikz/` now stores both golden-family `CircuitSpec` fixtures, and `scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` runs them through the same explicit shell-free renderer configuration and returns aggregate per-fixture evidence. `src/rendering/diagnostics.ts` now summarizes `RenderArtifact.diagnostics` into error/warning/info counts, and `src/ui/DiagramPreviewModal.ts` shows those counts alongside generic diagnostics, diagnostics-aware preview history entries, and source-only fallback for non-inline artifacts, so circuitikz smoke, future renderer checks, repair hints, and raw external-renderer sources have a user-visible surface without forcing TikZJax/LaTeX into the plugin runtime or faking visual rendering. `src/tests/circuitikzExporter.test.ts`, `src/tests/circuitikzRepairBrief.test.ts`, `src/tests/circuitikzCompileDiagnostics.test.ts`, `src/tests/circuitikzRenderSmoke.test.ts`, `src/tests/circuitikzCompileRunner.test.ts`, `src/tests/circuitikzExportCli.test.ts`, `src/tests/circuitikzSmokeFixturesCli.test.ts`, `src/tests/diagramPreview.test.ts`, `src/tests/diagramPreviewModal.test.ts`, and `src/tests/renderArtifactDiagnostics.test.ts` verify deterministic output, topology rejection, topology-preserving repair guard, repair brief generation, and repair candidate validation behavior, package-script exposure, UTF-8 BOM input handling, shell-free compile execution, render-smoke artifact checks, SVG smoke diagnostics, path-only SVG label classification, path-only glyph transform handling, path-only glyph overlap diagnostics, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, transform-aware SVG geometry, stroke-width-aware SVG bounds and label overlap checks, positioned `tspan` label geometry, PNG blank screenshot diagnostics, indexed-color PNG packed sample decoding and palette alpha handling, grayscale PNG packed/direct sample decoding, 16-bit direct sample normalization, PNG foreground-bound and foreground-density reporting, PNG clipped-content diagnostics, dense-foreground diagnostics, bounded SVG viewBox checks, obvious SVG text-overlap checks, maintainer fixture discovery and aggregate execution, preview diagnostics visibility, diagnostic summary counts, source-only preview fallback, diagnostics-aware history entries, diagnostics JSON output, and nonzero CLI exit for logs or smoke reports with errors. The implementation deliberately stops before OCR recognition for path-only glyph text, precise pixel-level overlap detection, full SVG path coverage, and automated visual repair execution. +Phase F implementation status on 2026-07-05: `src/diagram/adapters/circuitikz/circuitSpec.ts` and `src/diagram/adapters/circuitikz/circuitikzExporter.ts` now implement a constrained `CircuitSpec -> circuitikz` prototype. `scripts/export-circuitikz.js` and `npm run diagram:export-circuitikz` export deterministic LaTeX for `common-source-nmos-v1` and `cmos-inverter-v1`. `src/diagram/adapters/circuitikz/circuitikzExporter.ts` also exposes `createCircuitTopologySignature` and `assertCircuitTopologyUnchanged`, and `scripts/export-circuitikz.js` exposes that guard through `--topology-reference` so topology-preserving repair candidates can change labels/layout while being rejected before output if they add, remove, or rewire electrical topology. `src/diagram/adapters/circuitikz/circuitikzRepairBrief.ts`, `--repair-brief-output`, and `--repair-brief` now produce and consume schema `notemd.circuitikz.repair-brief.v1`, a repair handoff that carries the source `CircuitSpec`, topology signature, compile/render diagnostics, allowed changes, prohibited topology changes, next verification steps, and a candidate validation gate that can reject repaired specs before output is written. `src/diagram/adapters/circuitikz/circuitikzDiagnostics.ts` parses existing compile logs into actionable diagnostics for missing packages, unknown TikZ keys, undefined control sequences, generic LaTeX errors, emergency stops, and overfull layout warnings. `src/diagram/adapters/circuitikz/circuitikzCompileRunner.ts` can run an explicitly configured local renderer without shell parsing, feed the generated `{jobName}.log` back into diagnostics, and optionally require a concrete non-empty output artifact through `--expected-artifact`. `src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` now adds SVG-specific checks for root/dimensions/visible drawing elements, optional text tokens through `--expected-svg-text`, path-only label classification through `pathOnlyGlyphUseCount` and `render-svg-text-path-only`, conservative bounded-canvas diagnostics through `render-svg-out-of-bounds`, obvious text-overlap diagnostics through `render-svg-text-overlap`, positioned `tspan` label geometry for LaTeX-style SVG output, path-only glyph label-vs-drawing overlap diagnostics through `render-svg-path-glyph-overlap`, definition-local `transform` handling for reusable path-only glyphs before `` placement, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, SVG number grammar for leading-dot decimals and explicit plus signs, stroke-width-aware SVG bounds and label-vs-drawing overlap checks, transform-aware geometry for common group and element transforms, plus PNG screenshot checks for dimensions, non-background pixels, 1/2/4/8-bit indexed-color PLTE palettes with optional tRNS alpha, grayscale/RGB `tRNS` transparent sample handling, 1/2/4/8/16-bit grayscale samples, 8/16-bit grayscale-alpha/RGB/RGBA direct samples, `foregroundBounds`, `foregroundDensity`, edge-touching clipped content through `render-png-content-clipped`, and dense foreground blocks through `render-png-foreground-dense`. `docs/maintainer/fixtures/circuitikz/` now stores both golden-family `CircuitSpec` fixtures, and `scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` runs them through the same explicit shell-free renderer configuration and returns aggregate per-fixture evidence. `src/rendering/diagnostics.ts` now summarizes `RenderArtifact.diagnostics` into error/warning/info counts, and `src/ui/DiagramPreviewModal.ts` shows those counts alongside generic diagnostics, diagnostics-aware preview history entries, and source-only fallback for non-inline artifacts, so circuitikz smoke, future renderer checks, repair hints, and raw external-renderer sources have a user-visible surface without forcing TikZJax/LaTeX into the plugin runtime or faking visual rendering. `src/tests/circuitikzExporter.test.ts`, `src/tests/circuitikzRepairBrief.test.ts`, `src/tests/circuitikzCompileDiagnostics.test.ts`, `src/tests/circuitikzRenderSmoke.test.ts`, `src/tests/circuitikzCompileRunner.test.ts`, `src/tests/circuitikzExportCli.test.ts`, `src/tests/circuitikzSmokeFixturesCli.test.ts`, `src/tests/diagramPreview.test.ts`, `src/tests/diagramPreviewModal.test.ts`, and `src/tests/renderArtifactDiagnostics.test.ts` verify deterministic output, topology rejection, topology-preserving repair guard, repair brief generation, and repair candidate validation behavior, package-script exposure, UTF-8 BOM input handling, shell-free compile execution, render-smoke artifact checks, SVG smoke diagnostics, path-only SVG label classification, path-only glyph transform handling, path-only glyph overlap diagnostics, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, transform-aware SVG geometry, stroke-width-aware SVG bounds and label overlap checks, positioned `tspan` label geometry, PNG blank screenshot diagnostics, indexed-color PNG packed sample decoding and palette alpha handling, grayscale/RGB `tRNS` transparent sample handling, grayscale PNG packed/direct sample decoding, 16-bit direct sample normalization, PNG foreground-bound and foreground-density reporting, PNG clipped-content diagnostics, dense-foreground diagnostics, bounded SVG viewBox checks, obvious SVG text-overlap checks, maintainer fixture discovery and aggregate execution, preview diagnostics visibility, diagnostic summary counts, source-only preview fallback, diagnostics-aware history entries, diagnostics JSON output, and nonzero CLI exit for logs or smoke reports with errors. The implementation deliberately stops before OCR recognition for path-only glyph text, precise pixel-level overlap detection, full SVG path coverage, and automated visual repair execution. ## Current Architecture Progress Audit diff --git a/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.zh-CN.md b/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.zh-CN.md index 2fe2be5d..82ebe61b 100644 --- a/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.zh-CN.md +++ b/docs/brainstorms/2026-07-04-diagram-reference-integration-and-figure-generation-plan.zh-CN.md @@ -207,6 +207,7 @@ Phase E 于 2026-07-05 的实现状态:`scripts/export-diagram-artifact.js` - [x] 通过 packed sample decoding、PLTE palette decoding 与可选 tRNS alpha 增加非交错 1/2/4/8-bit indexed-color PNG smoke 支持。 - [x] 通过 packed/direct sample decoding 与 normalized gray values 增加非交错 1/2/4/8/16-bit grayscale PNG smoke 支持。 - [x] 通过 direct sample normalization 增加非交错 16-bit grayscale-alpha/RGB/RGBA PNG smoke 支持。 +- [x] 增加 grayscale/RGB PNG `tRNS` transparent sample handling,避免只含透明样本的像素被计为可见前景。 - [x] 增加保守的 SVG bounded-canvas 与明显 text-overlap diagnostics。 - [x] 在 bounded-canvas/text-overlap 检查前,对常见 SVG group 与 element transforms 增加 transform-aware geometry。 - [x] 通过 `render-svg-label-overlap` 增加保守的 SVG label-vs-drawing overlap diagnostics。 @@ -228,7 +229,7 @@ Phase E 于 2026-07-05 的实现状态:`scripts/export-diagram-artifact.js` - [x] 增加可复用的 `RenderArtifact.diagnostics` summary counts,并在 preview diagnostics panel 与 preview history entries 中显示。 - [x] 为可复用 path-only SVG glyph 增加 definition-local `transform` 处理,让 dvisvgm 风格的缩放或镜像 label geometry 在 `` placement 前就参与检查。 -Phase F 于 2026-07-05 的实现状态:`src/diagram/adapters/circuitikz/circuitSpec.ts` 与 `src/diagram/adapters/circuitikz/circuitikzExporter.ts` 已实现受约束的 `CircuitSpec -> circuitikz` 原型。`scripts/export-circuitikz.js` 与 `npm run diagram:export-circuitikz` 可以为 `common-source-nmos-v1` 和 `cmos-inverter-v1` 导出确定性 LaTeX。`src/diagram/adapters/circuitikz/circuitikzExporter.ts` 还暴露 `createCircuitTopologySignature` 与 `assertCircuitTopologyUnchanged`,`scripts/export-circuitikz.js` 通过 `--topology-reference` 暴露这个 guard,让 topology-preserving repair candidate 可以改 labels/layout,但如果新增、删除或重连电气拓扑,会在写出 output 前被拒绝。`src/diagram/adapters/circuitikz/circuitikzRepairBrief.ts`、`--repair-brief-output` 与 `--repair-brief` 现在会生成并消费 schema `notemd.circuitikz.repair-brief.v1`,作为 repair handoff 携带 source `CircuitSpec`、topology signature、compile/render diagnostics、允许修改项、禁止拓扑修改项、下一步验证步骤,以及能在写出 output 前拒绝 repaired spec 的 candidate validation gate。`src/diagram/adapters/circuitikz/circuitikzDiagnostics.ts` 会把已有 compile logs 解析为 actionable diagnostics,覆盖 missing packages、unknown TikZ keys、undefined control sequences、generic LaTeX errors、emergency stops 和 overfull layout warnings。`src/diagram/adapters/circuitikz/circuitikzCompileRunner.ts` 可以在不经过 shell parsing 的情况下运行显式配置的本地 renderer,把生成的 `{jobName}.log` 接回 diagnostics,并通过 `--expected-artifact` 可选要求产出具体的非空 output artifact。`src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` 现在会对 SVG 做 root、dimensions、visible drawing elements 检查,通过 `--expected-svg-text` 执行可选文本 token 检查,通过 `pathOnlyGlyphUseCount` 和 `render-svg-text-path-only` 执行 path-only label classification,通过 `render-svg-out-of-bounds` 执行保守 bounded-canvas diagnostics,通过 `render-svg-text-overlap` 执行明显 text-overlap diagnostics,并为 LaTeX-style SVG output 增加 positioned `tspan` label geometry,通过 `render-svg-path-glyph-overlap` 增加 path-only glyph label-vs-drawing overlap diagnostics,在 `` placement 前处理可复用 path-only glyph 的 definition-local `transform`,针对 A/a arc extrema 增加 exact arc bounds,针对 C/S/Q/T curve extrema 增加 exact Bezier curve bounds,按 SVG number grammar 处理 leading-dot decimals 与 explicit plus signs,增加 stroke-width-aware SVG bounds 与 label-vs-drawing overlap checks,再对常见 group 与 element transforms 执行 transform-aware geometry,同时对 PNG screenshot 做尺寸、非背景像素、1/2/4/8-bit PLTE palette 的 indexed-color packed samples 与可选 tRNS alpha、1/2/4/8/16-bit grayscale samples、8/16-bit grayscale-alpha/RGB/RGBA direct samples、`foregroundBounds`、`foregroundDensity`、通过 `render-png-content-clipped` 报告的贴边裁剪内容,以及通过 `render-png-foreground-dense` 报告的异常密集前景块检查。`docs/maintainer/fixtures/circuitikz/` 现在保存两个 golden-family `CircuitSpec` fixtures,`scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` 会用同一套显式 shell-free renderer 配置跑完这些 fixtures,并返回每个 fixture 的聚合证据。`src/rendering/diagnostics.ts` 现在会把 `RenderArtifact.diagnostics` 汇总为 error/warning/info 计数,`src/ui/DiagramPreviewModal.ts` 会在通用 diagnostics、diagnostics-aware preview history entries 与非 inline artifact 的 source-only fallback 中显示这些 summary counts,让 circuitikz smoke、未来 renderer checks、repair hints 和外部渲染器源码都有用户可见的前端入口,同时不把 TikZJax/LaTeX 强行塞进插件 runtime,也不假装已经完成视觉渲染。`src/tests/circuitikzExporter.test.ts`、`src/tests/circuitikzRepairBrief.test.ts`、`src/tests/circuitikzCompileDiagnostics.test.ts`、`src/tests/circuitikzRenderSmoke.test.ts`、`src/tests/circuitikzCompileRunner.test.ts`、`src/tests/circuitikzExportCli.test.ts`、`src/tests/circuitikzSmokeFixturesCli.test.ts`、`src/tests/diagramPreview.test.ts`、`src/tests/diagramPreviewModal.test.ts` 与 `src/tests/renderArtifactDiagnostics.test.ts` 验证确定性输出、拓扑拒绝、topology-preserving repair guard、repair brief generation 与 repair candidate validation 行为、package-script 暴露、UTF-8 BOM 输入处理、shell-free compile execution、render-smoke artifact checks、SVG smoke diagnostics、path-only SVG label classification、path-only glyph transform handling、path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、transform-aware SVG geometry、stroke-width-aware SVG bounds 与 label overlap checks、positioned `tspan` label geometry、PNG 空白截图 diagnostics、indexed-color PNG packed sample decoding 与 palette alpha handling、grayscale PNG packed/direct sample decoding、16-bit direct sample normalization、PNG 前景包围盒与前景密度报告、PNG 裁剪内容诊断、异常密集前景诊断、bounded SVG viewBox checks、明显 SVG text-overlap checks、maintainer fixture discovery 和聚合执行、preview diagnostics 可见性、diagnostic summary counts、source-only preview fallback、diagnostics-aware history entries、diagnostics JSON 输出,以及 compile log 或 smoke report 含错误时 CLI 非零退出。实现仍有意停在 path-only glyph text 的 OCR recognition、更精确 pixel-level overlap detection、完整 SVG path coverage 和 automated visual repair execution 之前。 +Phase F 于 2026-07-05 的实现状态:`src/diagram/adapters/circuitikz/circuitSpec.ts` 与 `src/diagram/adapters/circuitikz/circuitikzExporter.ts` 已实现受约束的 `CircuitSpec -> circuitikz` 原型。`scripts/export-circuitikz.js` 与 `npm run diagram:export-circuitikz` 可以为 `common-source-nmos-v1` 和 `cmos-inverter-v1` 导出确定性 LaTeX。`src/diagram/adapters/circuitikz/circuitikzExporter.ts` 还暴露 `createCircuitTopologySignature` 与 `assertCircuitTopologyUnchanged`,`scripts/export-circuitikz.js` 通过 `--topology-reference` 暴露这个 guard,让 topology-preserving repair candidate 可以改 labels/layout,但如果新增、删除或重连电气拓扑,会在写出 output 前被拒绝。`src/diagram/adapters/circuitikz/circuitikzRepairBrief.ts`、`--repair-brief-output` 与 `--repair-brief` 现在会生成并消费 schema `notemd.circuitikz.repair-brief.v1`,作为 repair handoff 携带 source `CircuitSpec`、topology signature、compile/render diagnostics、允许修改项、禁止拓扑修改项、下一步验证步骤,以及能在写出 output 前拒绝 repaired spec 的 candidate validation gate。`src/diagram/adapters/circuitikz/circuitikzDiagnostics.ts` 会把已有 compile logs 解析为 actionable diagnostics,覆盖 missing packages、unknown TikZ keys、undefined control sequences、generic LaTeX errors、emergency stops 和 overfull layout warnings。`src/diagram/adapters/circuitikz/circuitikzCompileRunner.ts` 可以在不经过 shell parsing 的情况下运行显式配置的本地 renderer,把生成的 `{jobName}.log` 接回 diagnostics,并通过 `--expected-artifact` 可选要求产出具体的非空 output artifact。`src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` 现在会对 SVG 做 root、dimensions、visible drawing elements 检查,通过 `--expected-svg-text` 执行可选文本 token 检查,通过 `pathOnlyGlyphUseCount` 和 `render-svg-text-path-only` 执行 path-only label classification,通过 `render-svg-out-of-bounds` 执行保守 bounded-canvas diagnostics,通过 `render-svg-text-overlap` 执行明显 text-overlap diagnostics,并为 LaTeX-style SVG output 增加 positioned `tspan` label geometry,通过 `render-svg-path-glyph-overlap` 增加 path-only glyph label-vs-drawing overlap diagnostics,在 `` placement 前处理可复用 path-only glyph 的 definition-local `transform`,针对 A/a arc extrema 增加 exact arc bounds,针对 C/S/Q/T curve extrema 增加 exact Bezier curve bounds,按 SVG number grammar 处理 leading-dot decimals 与 explicit plus signs,增加 stroke-width-aware SVG bounds 与 label-vs-drawing overlap checks,再对常见 group 与 element transforms 执行 transform-aware geometry,同时对 PNG screenshot 做尺寸、非背景像素、1/2/4/8-bit PLTE palette 的 indexed-color packed samples 与可选 tRNS alpha、grayscale/RGB `tRNS` transparent sample handling、1/2/4/8/16-bit grayscale samples、8/16-bit grayscale-alpha/RGB/RGBA direct samples、`foregroundBounds`、`foregroundDensity`、通过 `render-png-content-clipped` 报告的贴边裁剪内容,以及通过 `render-png-foreground-dense` 报告的异常密集前景块检查。`docs/maintainer/fixtures/circuitikz/` 现在保存两个 golden-family `CircuitSpec` fixtures,`scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` 会用同一套显式 shell-free renderer 配置跑完这些 fixtures,并返回每个 fixture 的聚合证据。`src/rendering/diagnostics.ts` 现在会把 `RenderArtifact.diagnostics` 汇总为 error/warning/info 计数,`src/ui/DiagramPreviewModal.ts` 会在通用 diagnostics、diagnostics-aware preview history entries 与非 inline artifact 的 source-only fallback 中显示这些 summary counts,让 circuitikz smoke、未来 renderer checks、repair hints 和外部渲染器源码都有用户可见的前端入口,同时不把 TikZJax/LaTeX 强行塞进插件 runtime,也不假装已经完成视觉渲染。`src/tests/circuitikzExporter.test.ts`、`src/tests/circuitikzRepairBrief.test.ts`、`src/tests/circuitikzCompileDiagnostics.test.ts`、`src/tests/circuitikzRenderSmoke.test.ts`、`src/tests/circuitikzCompileRunner.test.ts`、`src/tests/circuitikzExportCli.test.ts`、`src/tests/circuitikzSmokeFixturesCli.test.ts`、`src/tests/diagramPreview.test.ts`、`src/tests/diagramPreviewModal.test.ts` 与 `src/tests/renderArtifactDiagnostics.test.ts` 验证确定性输出、拓扑拒绝、topology-preserving repair guard、repair brief generation 与 repair candidate validation 行为、package-script 暴露、UTF-8 BOM 输入处理、shell-free compile execution、render-smoke artifact checks、SVG smoke diagnostics、path-only SVG label classification、path-only glyph transform handling、path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、transform-aware SVG geometry、stroke-width-aware SVG bounds 与 label overlap checks、positioned `tspan` label geometry、PNG 空白截图 diagnostics、indexed-color PNG packed sample decoding 与 palette alpha handling、grayscale/RGB `tRNS` transparent sample handling、grayscale PNG packed/direct sample decoding、16-bit direct sample normalization、PNG 前景包围盒与前景密度报告、PNG 裁剪内容诊断、异常密集前景诊断、bounded SVG viewBox checks、明显 SVG text-overlap checks、maintainer fixture discovery 和聚合执行、preview diagnostics 可见性、diagnostic summary counts、source-only preview fallback、diagnostics-aware history entries、diagnostics JSON 输出,以及 compile log 或 smoke report 含错误时 CLI 非零退出。实现仍有意停在 path-only glyph text 的 OCR recognition、更精确 pixel-level overlap detection、完整 SVG path coverage 和 automated visual repair execution 之前。 ## 当前架构推进审计 @@ -237,7 +238,7 @@ Phase F 于 2026-07-05 的实现状态:`src/diagram/adapters/circuitikz/circui | 保持模型输出为语义层,不回退到自由 renderer text | `DiagramSpec`、`SemanticFigureModel` 与独立 `CircuitSpec` 边界把 renderer 语法留在 adapter 内 | 当前 targets 已实现 | 在其他 target 需要同类字段前,不把 circuit-only terminal/layout 字段扩进 `DiagramSpec` | | 把编辑器集成留在 artifact boundary | Draw.io XML、Drawnix JSON、editable HTML/SVG 与 circuitikz 都通过 CLI 或 render artifact 输出,不嵌入第三方编辑器 | 已实现 | 只有 structural editability tests 存在后再扩展更丰富 primitives | | renderer 执行必须跨平台 | `circuitikzCompileRunner.ts` 使用 `shell: false`、placeholder-expanded argument arrays 和结构化 executable diagnostics | 已实现 | Windows/POSIX 行为继续放在参数数组里处理,不拼 shell command string | -| 视觉修复前必须先验证电路输出 | compile-log diagnostics、topology-preserving repair guard、SVG structural smoke、1/2/4/8-bit indexed-color、1/2/4/8/16-bit grayscale 与 8/16-bit grayscale-alpha/RGB/RGBA PNG foreground smoke 已存在 | 部分实现 | 增加 OCR-level label recognition、精确 pixel overlap gates、interlaced/16-bit indexed rejection guidance 与 topology-preserving repair prompts | +| 视觉修复前必须先验证电路输出 | compile-log diagnostics、topology-preserving repair guard、SVG structural smoke、1/2/4/8-bit indexed-color、grayscale/RGB `tRNS` transparent samples、1/2/4/8/16-bit grayscale 与 8/16-bit grayscale-alpha/RGB/RGBA PNG foreground smoke 已存在 | 部分实现 | 增加 OCR-level label recognition、精确 pixel overlap gates、interlaced/16-bit indexed rejection guidance 与 topology-preserving repair prompts | | 向用户暴露 diagnostics,但不把源码伪装成渲染结果 | `RenderArtifact.diagnostics`、diagnostic summary counts、preview history entries 与 source-only fallback 已实现 | 已实现 | 只有 renderer evidence 能区分 raw source 与 rendered output 后,再接外部 renderer artifacts | ## 权衡 diff --git a/docs/maintainer/circuitikz-export-prototype.md b/docs/maintainer/circuitikz-export-prototype.md index 68d22147..34eaf822 100644 --- a/docs/maintainer/circuitikz-export-prototype.md +++ b/docs/maintainer/circuitikz-export-prototype.md @@ -126,7 +126,7 @@ node scripts/export-circuitikz.js \ --expected-svg-text v_{out} ``` -For `.png` screenshot artifacts, the smoke check decodes non-interlaced 1/2/4/8-bit indexed-color PNG output, 1/2/4/8/16-bit grayscale PNG output, and 8/16-bit RGB, grayscale-alpha, or RGBA PNG output. Indexed-color and sub-byte grayscale images support packed samples; indexed-color images also support PLTE palette entries and optional tRNS alpha. 16-bit direct samples are normalized into the same 8-bit RGBA comparison space used by the smoke checks. It verifies positive dimensions, records the foreground pixel bounding box as `foregroundBounds`, records foreground density inside that box as `foregroundDensity`, and requires at least one pixel that differs from the top-left background color. Blank screenshots fail with `render-png-blank`; foreground content that touches the image boundary fails with `render-png-content-clipped`; foreground pixels that are unusually dense inside a non-trivial bounding box fail with `render-png-foreground-dense`; malformed or unsupported PNGs fail with `render-png-invalid` or `render-png-unsupported`. +For `.png` screenshot artifacts, the smoke check decodes non-interlaced 1/2/4/8-bit indexed-color PNG output, 1/2/4/8/16-bit grayscale PNG output, and 8/16-bit RGB, grayscale-alpha, or RGBA PNG output. Indexed-color and sub-byte grayscale images support packed samples; indexed-color images also support PLTE palette entries and optional tRNS alpha; grayscale/RGB images support tRNS transparent samples. 16-bit direct samples are normalized into the same 8-bit RGBA comparison space used by the smoke checks. It verifies positive dimensions, records the foreground pixel bounding box as `foregroundBounds`, records foreground density inside that box as `foregroundDensity`, and requires at least one pixel that differs from the top-left background color. Blank screenshots fail with `render-png-blank`; foreground content that touches the image boundary fails with `render-png-content-clipped`; foreground pixels that are unusually dense inside a non-trivial bounding box fail with `render-png-foreground-dense`; malformed or unsupported PNGs fail with `render-png-invalid` or `render-png-unsupported`. The result is recorded as `compileExecution.renderSmoke`. Missing or empty artifacts add `render-artifact-missing` or `render-artifact-empty`; SVG structure failures add diagnostics such as `render-svg-invalid`, `render-svg-dimension-missing`, `render-svg-no-visible-elements`, `render-svg-text-missing`, `render-svg-text-path-only`, `render-svg-out-of-bounds`, `render-svg-text-overlap`, `render-svg-label-overlap`, or `render-svg-path-glyph-overlap`; PNG screenshot failures add diagnostics such as `render-png-blank`, `render-png-content-clipped`, or `render-png-foreground-dense`. diff --git a/docs/maintainer/circuitikz-export-prototype.zh-CN.md b/docs/maintainer/circuitikz-export-prototype.zh-CN.md index 681fb860..dc04c3bf 100644 --- a/docs/maintainer/circuitikz-export-prototype.zh-CN.md +++ b/docs/maintainer/circuitikz-export-prototype.zh-CN.md @@ -126,7 +126,7 @@ node scripts/export-circuitikz.js \ --expected-svg-text v_{out} ``` -对于 `.png` screenshot artifact,smoke check 会解码非交错的 1/2/4/8-bit indexed-color PNG 输出、1/2/4/8/16-bit grayscale PNG 输出,以及 8/16-bit RGB、grayscale-alpha 或 RGBA PNG 输出。indexed-color 与 sub-byte grayscale 图像支持 packed samples;indexed-color 图像还支持 PLTE palette entries 与可选 tRNS alpha。16-bit direct samples 会被归一化到 smoke check 使用的同一个 8-bit RGBA 比较空间。它会检查正的尺寸,把前景像素包围盒记录为 `foregroundBounds`,把包围盒内前景密度记录为 `foregroundDensity`,并要求至少一个不同于左上角背景色的像素。空白截图会以 `render-png-blank` 失败;前景内容贴到图像边界会以 `render-png-content-clipped` 失败;非平凡包围盒内前景像素异常密集会以 `render-png-foreground-dense` 失败;格式损坏或不支持的 PNG 会以 `render-png-invalid` 或 `render-png-unsupported` 失败。 +对于 `.png` screenshot artifact,smoke check 会解码非交错的 1/2/4/8-bit indexed-color PNG 输出、1/2/4/8/16-bit grayscale PNG 输出,以及 8/16-bit RGB、grayscale-alpha 或 RGBA PNG 输出。indexed-color 与 sub-byte grayscale 图像支持 packed samples;indexed-color 图像还支持 PLTE palette entries 与可选 tRNS alpha;grayscale/RGB 图像支持 tRNS transparent samples。16-bit direct samples 会被归一化到 smoke check 使用的同一个 8-bit RGBA 比较空间。它会检查正的尺寸,把前景像素包围盒记录为 `foregroundBounds`,把包围盒内前景密度记录为 `foregroundDensity`,并要求至少一个不同于左上角背景色的像素。空白截图会以 `render-png-blank` 失败;前景内容贴到图像边界会以 `render-png-content-clipped` 失败;非平凡包围盒内前景像素异常密集会以 `render-png-foreground-dense` 失败;格式损坏或不支持的 PNG 会以 `render-png-invalid` 或 `render-png-unsupported` 失败。 检查结果会记录为 `compileExecution.renderSmoke`。缺失或空 artifact 会追加 `render-artifact-missing` 或 `render-artifact-empty`;SVG 结构失败会追加 `render-svg-invalid`、`render-svg-dimension-missing`、`render-svg-no-visible-elements`、`render-svg-text-missing`、`render-svg-text-path-only`、`render-svg-out-of-bounds`、`render-svg-text-overlap`、`render-svg-label-overlap` 或 `render-svg-path-glyph-overlap` 等 diagnostic;PNG screenshot 失败会追加 `render-png-blank`、`render-png-content-clipped` 或 `render-png-foreground-dense` 等 diagnostic。 diff --git a/docs/maintainer/circuitikz-figure-generation-roadmap.md b/docs/maintainer/circuitikz-figure-generation-roadmap.md index 9c1753e2..8d00b18c 100644 --- a/docs/maintainer/circuitikz-figure-generation-roadmap.md +++ b/docs/maintainer/circuitikz-figure-generation-roadmap.md @@ -146,12 +146,12 @@ Phase A is documented. Phase B/C now have a constrained repository prototype: - `src/diagram/adapters/circuitikz/circuitikzRepairBrief.ts`, `--repair-brief-output`, and `--repair-brief` produce and consume schema `notemd.circuitikz.repair-brief.v1`, a topology-preserving repair handoff that carries the source `CircuitSpec`, topology signature, compile/render diagnostics, allowed changes, prohibited topology changes, and next verification steps. Candidate specs can be checked against the brief signature before output is written. - `src/diagram/adapters/circuitikz/circuitikzDiagnostics.ts` parses existing LaTeX/TikZJax compile logs into actionable diagnostics without spawning a compiler or depending on shell command resolution. - `src/diagram/adapters/circuitikz/circuitikzCompileRunner.ts` can run an explicitly configured local renderer with `shell: false`, placeholder-expanded arguments, structured executable diagnostics for empty, missing, or invalid renderer commands, generated-log diagnostics, and optional `--expected-artifact` render-smoke checks. -- `src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` inspects expected render artifacts. PDF and other opaque artifacts are checked for existence/non-empty output; SVG artifacts are additionally checked for an `` root, positive dimensions or `viewBox`, visible drawing elements, optional repeated `--expected-svg-text` tokens, `render-svg-text-path-only` / `pathOnlyGlyphUseCount` classification for path-only labels, path-only glyph placement through resolved `` geometry, definition-local transforms on reusable glyph paths, path-only glyph overlap diagnostics through `render-svg-path-glyph-overlap`, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` drawing geometry, positioned `tspan` label geometry, bounded-canvas/text-overlap diagnostics, label-vs-drawing overlap diagnostics through `render-svg-label-overlap`, and transform-aware geometry for common group and element transforms; PNG screenshot artifacts are decoded for positive dimensions, non-background pixels, 1/2/4/8-bit indexed-color packed samples with PLTE/tRNS palette data, 1/2/4/8/16-bit grayscale samples, 8/16-bit grayscale-alpha/RGB/RGBA direct samples, foreground bounds, `foregroundDensity`, edge-touching clipped content through `render-png-content-clipped`, and unusually dense foreground blocks through `render-png-foreground-dense`. +- `src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` inspects expected render artifacts. PDF and other opaque artifacts are checked for existence/non-empty output; SVG artifacts are additionally checked for an `` root, positive dimensions or `viewBox`, visible drawing elements, optional repeated `--expected-svg-text` tokens, `render-svg-text-path-only` / `pathOnlyGlyphUseCount` classification for path-only labels, path-only glyph placement through resolved `` geometry, definition-local transforms on reusable glyph paths, path-only glyph overlap diagnostics through `render-svg-path-glyph-overlap`, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` drawing geometry, positioned `tspan` label geometry, bounded-canvas/text-overlap diagnostics, label-vs-drawing overlap diagnostics through `render-svg-label-overlap`, and transform-aware geometry for common group and element transforms; PNG screenshot artifacts are decoded for positive dimensions, non-background pixels, 1/2/4/8-bit indexed-color packed samples with PLTE/tRNS palette data, grayscale/RGB tRNS transparent samples, 1/2/4/8/16-bit grayscale samples, 8/16-bit grayscale-alpha/RGB/RGBA direct samples, foreground bounds, `foregroundDensity`, edge-touching clipped content through `render-png-content-clipped`, and unusually dense foreground blocks through `render-png-foreground-dense`. - `src/rendering/diagnostics.ts` summarizes `RenderArtifact.diagnostics` into error/warning/info counts, and `src/ui/DiagramPreviewModal.ts` uses that summary in both the artifact diagnostics panel and preview history entries. Any render target can attach diagnostics to `RenderArtifact.diagnostics`; the preview UI shows the summary, severity, kind, message, and advice without requiring TikZJax or LaTeX in the plugin runtime. -- `src/tests/circuitikzExporter.test.ts`, `src/tests/circuitikzRepairBrief.test.ts`, `src/tests/circuitikzCompileDiagnostics.test.ts`, `src/tests/circuitikzRenderSmoke.test.ts`, `src/tests/circuitikzCompileRunner.test.ts`, and `src/tests/circuitikzExportCli.test.ts` verify deterministic output, topology rejection, topology-preserving repair brief generation and candidate validation, package-script exposure, UTF-8 BOM handling, diagnostic parsing, shell-free compile execution, structured `compile-executable-invalid` / `compile-executable-not-found` diagnostics, diagnostics JSON output, render artifact existence/non-empty smoke checks, SVG structure checks, path-only SVG label classification, path-only glyph placement checks, definition-local glyph transform checks, path-only glyph overlap diagnostics, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` drawing geometry checks, positioned `tspan` label geometry checks, transform-aware SVG geometry checks, SVG label-vs-drawing overlap checks, PNG blank screenshot checks, indexed-color and grayscale PNG packed sample decoding, indexed-color PNG palette decoding, 16-bit direct PNG sample normalization, PNG foreground-bound and foreground-density reporting, PNG clipped-content diagnostics, dense-foreground diagnostics, and nonzero CLI exit for logs or smoke reports with errors. +- `src/tests/circuitikzExporter.test.ts`, `src/tests/circuitikzRepairBrief.test.ts`, `src/tests/circuitikzCompileDiagnostics.test.ts`, `src/tests/circuitikzRenderSmoke.test.ts`, `src/tests/circuitikzCompileRunner.test.ts`, and `src/tests/circuitikzExportCli.test.ts` verify deterministic output, topology rejection, topology-preserving repair brief generation and candidate validation, package-script exposure, UTF-8 BOM handling, diagnostic parsing, shell-free compile execution, structured `compile-executable-invalid` / `compile-executable-not-found` diagnostics, diagnostics JSON output, render artifact existence/non-empty smoke checks, SVG structure checks, path-only SVG label classification, path-only glyph placement checks, definition-local glyph transform checks, path-only glyph overlap diagnostics, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` drawing geometry checks, positioned `tspan` label geometry checks, transform-aware SVG geometry checks, SVG label-vs-drawing overlap checks, PNG blank screenshot checks, indexed-color and grayscale PNG packed sample decoding, indexed-color PNG palette decoding, grayscale/RGB PNG tRNS transparent sample handling, 16-bit direct PNG sample normalization, PNG foreground-bound and foreground-density reporting, PNG clipped-content diagnostics, dense-foreground diagnostics, and nonzero CLI exit for logs or smoke reports with errors. - `src/tests/diagramPreviewModal.test.ts` verifies that artifact diagnostics are visible in the diagram preview modal and that preview history does not collapse entries with different diagnostics. -Phase D now has log parsing, opt-in local renderer execution, artifact-level smoke checks, SVG structure smoke checks with transform-aware geometry, path-only label classification, path-only glyph placement checks, path-only glyph overlap diagnostics, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` geometry coverage, positioned `tspan` label geometry, text/text and label/drawing overlap diagnostics, first PNG screenshot nonblank, indexed-color and grayscale packed-sample decoding, 16-bit direct sample normalization, edge-clipping, and dense-foreground checks, a topology-preserving repair guard, a topology-preserving repair brief handoff with candidate validation, and a front-end diagnostics surface, but the implementation still deliberately stops before required renderer availability, OCR recognition for path-only glyph text, precise pixel-level overlap detection, full SVG path coverage, and automated Phase E repair execution. It does not bundle LaTeX, make TikZJax a plugin runtime dependency, or run a visual repair loop. +Phase D now has log parsing, opt-in local renderer execution, artifact-level smoke checks, SVG structure smoke checks with transform-aware geometry, path-only label classification, path-only glyph placement checks, path-only glyph overlap diagnostics, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` geometry coverage, positioned `tspan` label geometry, text/text and label/drawing overlap diagnostics, first PNG screenshot nonblank, indexed-color and grayscale packed-sample decoding, grayscale/RGB tRNS transparent sample handling, 16-bit direct sample normalization, edge-clipping, and dense-foreground checks, a topology-preserving repair guard, a topology-preserving repair brief handoff with candidate validation, and a front-end diagnostics surface, but the implementation still deliberately stops before required renderer availability, OCR recognition for path-only glyph text, precise pixel-level overlap detection, full SVG path coverage, and automated Phase E repair execution. It does not bundle LaTeX, make TikZJax a plugin runtime dependency, or run a visual repair loop. The SVG geometry smoke also covers SVG number grammar for leading-dot decimals and explicit plus signs. This is a small but important renderer-compatibility gate because dvisvgm can emit compact decimals that should remain fractional during bounds checks. diff --git a/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md b/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md index 555d2306..037b95e8 100644 --- a/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md +++ b/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md @@ -146,12 +146,12 @@ Phase A 已经文档化。Phase B/C 现在有了受约束的仓库内原型: - `src/diagram/adapters/circuitikz/circuitikzRepairBrief.ts`、`--repair-brief-output` 与 `--repair-brief` 会生成并消费 schema `notemd.circuitikz.repair-brief.v1`,作为 topology-preserving repair handoff,携带 source `CircuitSpec`、topology signature、compile/render diagnostics、允许修改项、禁止拓扑修改项和下一步验证步骤。candidate spec 可以在写出 output 前按 brief signature 执行校验。 - `src/diagram/adapters/circuitikz/circuitikzDiagnostics.ts` 会把已有 LaTeX/TikZJax compile logs 解析为 actionable diagnostics,不 spawn 编译器,也不依赖 shell command resolution。 - `src/diagram/adapters/circuitikz/circuitikzCompileRunner.ts` 可以用 `shell: false`、placeholder-expanded arguments、针对空、缺失或无效 renderer command 的结构化 executable diagnostics、生成 log diagnostics 和可选 `--expected-artifact` render-smoke checks 运行显式配置的本地 renderer。 -- `src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` 会检查预期 render artifact。PDF 和其他 opaque artifacts 检查存在与非空;SVG artifacts 还会检查 `` root、正的尺寸或 `viewBox`、可见绘图元素、可选重复传入的 `--expected-svg-text` tokens、用于 path-only labels 的 `render-svg-text-path-only` / `pathOnlyGlyphUseCount` 分类、通过解析 `` 几何执行的 path-only glyph placement checks、可复用 glyph path 上的 definition-local transforms、通过 `render-svg-path-glyph-overlap` 执行的 path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` drawing geometry、positioned `tspan` label geometry、bounded-canvas/text-overlap diagnostics、通过 `render-svg-label-overlap` 报告的 label-vs-drawing overlap diagnostics,以及常见 group 与 element transforms 的 transform-aware geometry;PNG screenshot artifacts 会被解码并检查正尺寸、非背景像素、1/2/4/8-bit indexed-color packed samples 与 PLTE/tRNS palette data、1/2/4/8/16-bit grayscale samples、8/16-bit grayscale-alpha/RGB/RGBA direct samples、前景包围盒、`foregroundDensity`、通过 `render-png-content-clipped` 报告的贴边裁剪内容,以及通过 `render-png-foreground-dense` 报告的异常密集前景块。 +- `src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts` 会检查预期 render artifact。PDF 和其他 opaque artifacts 检查存在与非空;SVG artifacts 还会检查 `` root、正的尺寸或 `viewBox`、可见绘图元素、可选重复传入的 `--expected-svg-text` tokens、用于 path-only labels 的 `render-svg-text-path-only` / `pathOnlyGlyphUseCount` 分类、通过解析 `` 几何执行的 path-only glyph placement checks、可复用 glyph path 上的 definition-local transforms、通过 `render-svg-path-glyph-overlap` 执行的 path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` drawing geometry、positioned `tspan` label geometry、bounded-canvas/text-overlap diagnostics、通过 `render-svg-label-overlap` 报告的 label-vs-drawing overlap diagnostics,以及常见 group 与 element transforms 的 transform-aware geometry;PNG screenshot artifacts 会被解码并检查正尺寸、非背景像素、1/2/4/8-bit indexed-color packed samples 与 PLTE/tRNS palette data、grayscale/RGB tRNS transparent samples、1/2/4/8/16-bit grayscale samples、8/16-bit grayscale-alpha/RGB/RGBA direct samples、前景包围盒、`foregroundDensity`、通过 `render-png-content-clipped` 报告的贴边裁剪内容,以及通过 `render-png-foreground-dense` 报告的异常密集前景块。 - `src/rendering/diagnostics.ts` 会把 `RenderArtifact.diagnostics` 汇总为 error/warning/info 计数,`src/ui/DiagramPreviewModal.ts` 会在 artifact diagnostics panel 和 preview history entries 中使用同一份 summary。任意 render target 都可以把 diagnostics 挂到 `RenderArtifact.diagnostics`;preview UI 会显示 summary、severity、kind、message 和 advice,而不要求插件 runtime 内置 TikZJax 或 LaTeX。 -- `src/tests/circuitikzExporter.test.ts`、`src/tests/circuitikzRepairBrief.test.ts`、`src/tests/circuitikzCompileDiagnostics.test.ts`、`src/tests/circuitikzRenderSmoke.test.ts`、`src/tests/circuitikzCompileRunner.test.ts` 与 `src/tests/circuitikzExportCli.test.ts` 验证确定性输出、拓扑拒绝、topology-preserving repair brief generation 与 candidate validation、package-script 暴露、UTF-8 BOM 处理、diagnostic parsing、shell-free compile execution、结构化 `compile-executable-invalid` / `compile-executable-not-found` diagnostics、diagnostics JSON 输出、render artifact 存在/非空 smoke checks、SVG 结构检查、path-only SVG label classification、path-only glyph placement checks、definition-local glyph transform checks、path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` drawing geometry checks、positioned `tspan` label geometry checks、transform-aware SVG geometry checks、SVG label-vs-drawing overlap checks、PNG 空白截图检查、indexed-color 与 grayscale PNG packed sample decoding、indexed-color PNG palette decoding、16-bit direct PNG sample normalization、PNG 前景包围盒与前景密度报告、PNG 裁剪内容诊断、异常密集前景诊断,以及 compile log 或 smoke report 含错误时 CLI 非零退出。 +- `src/tests/circuitikzExporter.test.ts`、`src/tests/circuitikzRepairBrief.test.ts`、`src/tests/circuitikzCompileDiagnostics.test.ts`、`src/tests/circuitikzRenderSmoke.test.ts`、`src/tests/circuitikzCompileRunner.test.ts` 与 `src/tests/circuitikzExportCli.test.ts` 验证确定性输出、拓扑拒绝、topology-preserving repair brief generation 与 candidate validation、package-script 暴露、UTF-8 BOM 处理、diagnostic parsing、shell-free compile execution、结构化 `compile-executable-invalid` / `compile-executable-not-found` diagnostics、diagnostics JSON 输出、render artifact 存在/非空 smoke checks、SVG 结构检查、path-only SVG label classification、path-only glyph placement checks、definition-local glyph transform checks、path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` drawing geometry checks、positioned `tspan` label geometry checks、transform-aware SVG geometry checks、SVG label-vs-drawing overlap checks、PNG 空白截图检查、indexed-color 与 grayscale PNG packed sample decoding、indexed-color PNG palette decoding、grayscale/RGB PNG tRNS transparent sample handling、16-bit direct PNG sample normalization、PNG 前景包围盒与前景密度报告、PNG 裁剪内容诊断、异常密集前景诊断,以及 compile log 或 smoke report 含错误时 CLI 非零退出。 - `src/tests/diagramPreviewModal.test.ts` 验证 artifact diagnostics 会显示在 diagram preview modal 中,并且 preview history 不会把 diagnostics 不同的条目错误去重。 -Phase D 现在已经具备 log parsing、opt-in local renderer execution、artifact-level smoke checks、带 transform-aware geometry、path-only label classification、path-only glyph placement checks、path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` geometry coverage、positioned `tspan` label geometry、text/text 与 label/drawing overlap diagnostics 的 SVG structure smoke checks、第一层 PNG screenshot nonblank、indexed-color 与 grayscale packed-sample decoding、16-bit direct sample normalization、edge-clipping 与 dense-foreground checks、topology-preserving repair guard、带 candidate validation 的 topology-preserving repair brief handoff,以及前端 diagnostics surface,但实现仍有意停在 required renderer availability、path-only glyph text 的 OCR recognition、精确 pixel-level overlap detection、完整 SVG path coverage 和自动化 Phase E repair execution 之前。它不会捆绑 LaTeX、不会把 TikZJax 变成插件 runtime 依赖,也不会运行视觉修复闭环。 +Phase D 现在已经具备 log parsing、opt-in local renderer execution、artifact-level smoke checks、带 transform-aware geometry、path-only label classification、path-only glyph placement checks、path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` geometry coverage、positioned `tspan` label geometry、text/text 与 label/drawing overlap diagnostics 的 SVG structure smoke checks、第一层 PNG screenshot nonblank、indexed-color 与 grayscale packed-sample decoding、grayscale/RGB tRNS transparent sample handling、16-bit direct sample normalization、edge-clipping 与 dense-foreground checks、topology-preserving repair guard、带 candidate validation 的 topology-preserving repair brief handoff,以及前端 diagnostics surface,但实现仍有意停在 required renderer availability、path-only glyph text 的 OCR recognition、精确 pixel-level overlap detection、完整 SVG path coverage 和自动化 Phase E repair execution 之前。它不会捆绑 LaTeX、不会把 TikZJax 变成插件 runtime 依赖,也不会运行视觉修复闭环。 SVG geometry smoke 也覆盖 leading-dot decimals 与 explicit plus signs 的 SVG number grammar。这是一个很小但关键的 renderer-compatibility gate,因为 dvisvgm 可能输出紧凑小数,bounds checks 中必须保留其小数语义。 diff --git a/src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts b/src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts index f8d8855d..49629273 100644 --- a/src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts +++ b/src/diagram/adapters/circuitikz/circuitikzRenderSmoke.ts @@ -72,6 +72,11 @@ interface PngPalette { alpha: number[]; } +interface PngTransparency { + grayscale?: number; + rgb?: [number, number, number]; +} + type PngPixelLayout = { kind: 'direct'; colorType: number; @@ -1491,6 +1496,37 @@ function parsePngPalette(chunks: PngChunk[], colorType: number): PngPalette | un }; } +function parsePngTransparency(chunks: PngChunk[], colorType: number): PngTransparency | undefined { + const transparency = chunks.find(chunk => chunk.type === 'tRNS'); + if (!transparency || colorType === 3) { + return undefined; + } + + if (colorType === 0) { + if (transparency.data.length !== 2) { + throw new Error('PNG grayscale transparency chunk is malformed.'); + } + return { + grayscale: transparency.data.readUInt16BE(0) + }; + } + + if (colorType === 2) { + if (transparency.data.length !== 6) { + throw new Error('PNG RGB transparency chunk is malformed.'); + } + return { + rgb: [ + transparency.data.readUInt16BE(0), + transparency.data.readUInt16BE(2), + transparency.data.readUInt16BE(4) + ] + }; + } + + return undefined; +} + function readPackedSample(decoded: Buffer, pixelIndex: number, width: number, bitDepth: number, scanlineByteLength: number): number { if (bitDepth === 8) { return decoded[pixelIndex]; @@ -1515,24 +1551,29 @@ function normalizePngSample(sample: number, bitDepth: number): number { return Math.round(sample * 255 / ((1 << bitDepth) - 1)); } -function readDirectSample(decoded: Buffer, offset: number, bitDepth: number): number { +function readRawDirectSample(decoded: Buffer, offset: number, bitDepth: number): number { if (bitDepth === 16) { - return normalizePngSample(decoded.readUInt16BE(offset), bitDepth); + return decoded.readUInt16BE(offset); } return decoded[offset]; } +function readDirectSample(decoded: Buffer, offset: number, bitDepth: number): number { + return normalizePngSample(readRawDirectSample(decoded, offset, bitDepth), bitDepth); +} + function readPixel( decoded: Buffer, pixelIndex: number, width: number, layout: PngPixelLayout, - palette?: PngPalette + palette?: PngPalette, + transparency?: PngTransparency ): [number, number, number, number] { if (layout.kind === 'grayscale') { const sample = readPackedSample(decoded, pixelIndex, width, layout.bitDepth, layout.scanlineByteLength); const gray = normalizePngSample(sample, layout.bitDepth); - return [gray, gray, gray, 255]; + return [gray, gray, gray, transparency?.grayscale === sample ? 0 : 255]; } if (layout.kind === 'indexed') { @@ -1547,16 +1588,25 @@ function readPixel( const offset = pixelIndex * layout.bytesPerPixel; switch (layout.colorType) { case 0: { - const gray = readDirectSample(decoded, offset, layout.bitDepth); - return [gray, gray, gray, 255]; + const sample = readRawDirectSample(decoded, offset, layout.bitDepth); + const gray = normalizePngSample(sample, layout.bitDepth); + return [gray, gray, gray, transparency?.grayscale === sample ? 0 : 255]; } - case 2: + case 2: { + const red = readRawDirectSample(decoded, offset, layout.bitDepth); + const green = readRawDirectSample(decoded, offset + layout.bytesPerSample, layout.bitDepth); + const blue = readRawDirectSample(decoded, offset + layout.bytesPerSample * 2, layout.bitDepth); + const transparent = transparency?.rgb + && transparency.rgb[0] === red + && transparency.rgb[1] === green + && transparency.rgb[2] === blue; return [ - readDirectSample(decoded, offset, layout.bitDepth), - readDirectSample(decoded, offset + layout.bytesPerSample, layout.bitDepth), - readDirectSample(decoded, offset + layout.bytesPerSample * 2, layout.bitDepth), - 255 + normalizePngSample(red, layout.bitDepth), + normalizePngSample(green, layout.bitDepth), + normalizePngSample(blue, layout.bitDepth), + transparent ? 0 : 255 ]; + } case 4: { const gray = readDirectSample(decoded, offset, layout.bitDepth); return [gray, gray, gray, readDirectSample(decoded, offset + layout.bytesPerSample, layout.bitDepth)]; @@ -1591,6 +1641,7 @@ function extractPngSmoke(pngBytes: Buffer): CircuitikzPngSmokeReport { throw new Error('PNG dimensions must be positive.'); } const palette = parsePngPalette(chunks, header.colorType); + const transparency = parsePngTransparency(chunks, header.colorType); const idatChunks = chunks.filter(chunk => chunk.type === 'IDAT'); if (idatChunks.length === 0) { @@ -1600,7 +1651,7 @@ function extractPngSmoke(pngBytes: Buffer): CircuitikzPngSmokeReport { const inflated = zlib.inflateSync(Buffer.concat(idatChunks.map(chunk => chunk.data))); const decoded = unfilterPngScanlines(inflated, header.height, pixelLayout.scanlineByteLength, pixelLayout.filterByteStride); const decodedPixelCount = header.width * header.height; - const background = readPixel(decoded, 0, header.width, pixelLayout, palette); + const background = readPixel(decoded, 0, header.width, pixelLayout, palette, transparency); let nonBackgroundPixelCount = 0; let minX = header.width; let minY = header.height; @@ -1608,7 +1659,7 @@ function extractPngSmoke(pngBytes: Buffer): CircuitikzPngSmokeReport { let maxY = -1; for (let pixelIndex = 0; pixelIndex < decodedPixelCount; pixelIndex += 1) { - const pixel = readPixel(decoded, pixelIndex, header.width, pixelLayout, palette); + const pixel = readPixel(decoded, pixelIndex, header.width, pixelLayout, palette, transparency); if (pixel[3] > 0 && colorDistance(pixel, background) > 8) { nonBackgroundPixelCount += 1; const x = pixelIndex % header.width; diff --git a/src/tests/circuitikzRenderSmoke.test.ts b/src/tests/circuitikzRenderSmoke.test.ts index 5fe56997..8f5c50ec 100644 --- a/src/tests/circuitikzRenderSmoke.test.ts +++ b/src/tests/circuitikzRenderSmoke.test.ts @@ -116,7 +116,8 @@ function createGrayscalePng( width: number, height: number, bitDepth: number, - samples: number[] + samples: number[], + transparentSample?: number ): Buffer { const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); const ihdr = Buffer.alloc(13); @@ -134,12 +135,62 @@ function createGrayscalePng( scanlines.push(...packPngSamples(samples.slice(row * width, row * width + width), bitDepth)); } - return Buffer.concat([ + const chunks = [ signature, - pngChunk('IHDR', ihdr), + pngChunk('IHDR', ihdr) + ]; + if (transparentSample !== undefined) { + const transparency = Buffer.alloc(2); + transparency.writeUInt16BE(transparentSample, 0); + chunks.push(pngChunk('tRNS', transparency)); + } + chunks.push( pngChunk('IDAT', zlib.deflateSync(Buffer.from(scanlines))), pngChunk('IEND', Buffer.alloc(0)) - ]); + ); + + return Buffer.concat(chunks); +} + +function createRgbPng( + width: number, + height: number, + pixels: Array<[number, number, number]>, + transparentColor?: [number, number, number] +): Buffer { + const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); + const ihdr = Buffer.alloc(13); + ihdr.writeUInt32BE(width, 0); + ihdr.writeUInt32BE(height, 4); + ihdr[8] = 8; + ihdr[9] = 2; + ihdr[10] = 0; + ihdr[11] = 0; + ihdr[12] = 0; + + const scanlines: number[] = []; + for (let row = 0; row < height; row += 1) { + scanlines.push(0); + for (let column = 0; column < width; column += 1) { + scanlines.push(...pixels[row * width + column]); + } + } + + const chunks = [ + signature, + pngChunk('IHDR', ihdr) + ]; + if (transparentColor) { + const transparency = Buffer.alloc(6); + transparentColor.forEach((sample, index) => transparency.writeUInt16BE(sample, index * 2)); + chunks.push(pngChunk('tRNS', transparency)); + } + chunks.push( + pngChunk('IDAT', zlib.deflateSync(Buffer.from(scanlines))), + pngChunk('IEND', Buffer.alloc(0)) + ); + + return Buffer.concat(chunks); } function createIndexedPng( @@ -1265,6 +1316,95 @@ describe('circuitikz render smoke inspection', () => { } }); + test('ignores grayscale PNG tRNS transparent samples when checking foreground', () => { + const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'notemd-circuitikz-png-gray-trns-smoke-')); + const pngPath = path.join(tempRoot, 'gray-trns-render.png'); + fs.writeFileSync( + pngPath, + createGrayscalePng( + 3, + 3, + 8, + [ + 255, 255, 255, + 255, 0, 255, + 255, 255, 255 + ], + 0 + ) + ); + + try { + const report = inspectCircuitikzRenderArtifact({ + expectedArtifactPath: pngPath + }); + + expect(report.artifactKind).toBe('png'); + expect(report.png).toEqual(expect.objectContaining({ + bitDepth: 8, + colorType: 0, + decodedPixelCount: 9, + nonBackgroundPixelCount: 0, + foregroundBounds: undefined + })); + expect(report.diagnostics).toEqual([ + expect.objectContaining({ + kind: 'render-png-blank', + message: expect.stringContaining('visually blank') + }) + ]); + } finally { + fs.rmSync(tempRoot, { recursive: true, force: true }); + } + }); + + test('ignores RGB PNG tRNS transparent colors when checking foreground', () => { + const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'notemd-circuitikz-png-rgb-trns-smoke-')); + const pngPath = path.join(tempRoot, 'rgb-trns-render.png'); + fs.writeFileSync( + pngPath, + createRgbPng( + 3, + 3, + [ + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 0, 0], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255] + ], + [255, 0, 0] + ) + ); + + try { + const report = inspectCircuitikzRenderArtifact({ + expectedArtifactPath: pngPath + }); + + expect(report.artifactKind).toBe('png'); + expect(report.png).toEqual(expect.objectContaining({ + bitDepth: 8, + colorType: 2, + decodedPixelCount: 9, + nonBackgroundPixelCount: 0, + foregroundBounds: undefined + })); + expect(report.diagnostics).toEqual([ + expect.objectContaining({ + kind: 'render-png-blank', + message: expect.stringContaining('visually blank') + }) + ]); + } finally { + fs.rmSync(tempRoot, { recursive: true, force: true }); + } + }); + test.each([ { label: 'grayscale', diff --git a/website/docs/features/diagrams.mdx b/website/docs/features/diagrams.mdx index 91b3d4a3..d09b8d34 100644 --- a/website/docs/features/diagrams.mdx +++ b/website/docs/features/diagrams.mdx @@ -256,7 +256,7 @@ SVG smoke verifies the `` root, positive dimensions or `viewBox`, at least The same geometry pass follows SVG number grammar for leading-dot decimals and explicit plus signs, so compact dvisvgm coordinates such as `.5`, `-.5`, or `+.5` stay fractional during bounds checks instead of becoming false out-of-bounds geometry or being skipped. -If the renderer emits `.png`, the same expected-artifact path becomes a first screenshot smoke: Notemd decodes non-interlaced 1/2/4/8-bit indexed-color PNG files, 1/2/4/8/16-bit grayscale PNG files, and 8/16-bit grayscale-alpha/RGB/RGBA PNG files. Indexed-color and sub-byte grayscale images support packed samples; indexed-color images also support PLTE and optional tRNS data. 16-bit direct samples are normalized into the same 8-bit RGBA comparison space used by the smoke checks. The smoke check verifies positive dimensions, records foreground bounds as `foregroundBounds`, records foreground density inside that box as `foregroundDensity`, fails with `render-png-blank` when every visible pixel matches the top-left background color, fails with `render-png-content-clipped` when foreground content touches the image boundary, and fails with `render-png-foreground-dense` when foreground pixels are unusually dense inside a non-trivial bounding box. This catches blank screenshots, obvious canvas clipping, and first pixel-level crowding failures without adding a platform-specific shell dependency. It is not yet OCR-level label recognition, precise text-overlap detection, or topology-preserving image repair. +If the renderer emits `.png`, the same expected-artifact path becomes a first screenshot smoke: Notemd decodes non-interlaced 1/2/4/8-bit indexed-color PNG files, 1/2/4/8/16-bit grayscale PNG files, and 8/16-bit grayscale-alpha/RGB/RGBA PNG files. Indexed-color and sub-byte grayscale images support packed samples; indexed-color images also support PLTE and optional tRNS data; grayscale/RGB images support tRNS transparent samples. 16-bit direct samples are normalized into the same 8-bit RGBA comparison space used by the smoke checks. The smoke check verifies positive dimensions, records foreground bounds as `foregroundBounds`, records foreground density inside that box as `foregroundDensity`, fails with `render-png-blank` when every visible pixel matches the top-left background color, fails with `render-png-content-clipped` when foreground content touches the image boundary, and fails with `render-png-foreground-dense` when foreground pixels are unusually dense inside a non-trivial bounding box. This catches blank screenshots, obvious canvas clipping, and first pixel-level crowding failures without adding a platform-specific shell dependency. It is not yet OCR-level label recognition, precise text-overlap detection, or topology-preserving image repair. When diagnostics show a failed compile or render-smoke run, the CLI can also write a topology-preserving repair brief: @@ -321,7 +321,7 @@ For a CMOS inverter, the prompt should request an explicit topology plus layout | General diagrams | Spec-first pipeline implemented for Mermaid, JSON Canvas, Vega-Lite, HTML | Keep expanding semantic verification coverage | | Editable figures | `editable-html-svg`, Draw.io XML, and Drawnix JSON artifact boundaries implemented | Add richer primitives only after tests prove editability | | CLI support | `npm run diagram:export-artifact` exports editable HTML/SVG, Draw.io, and Drawnix from one `DiagramSpec` | Add target-specific smoke fixtures when new targets ship | -| circuitikz | `CircuitSpec -> circuitikz` prototype exports common-source and CMOS inverter golden templates, rejects repair topology drift through `--topology-reference`, emits topology-preserving repair briefs through `--repair-brief-output` and schema `notemd.circuitikz.repair-brief.v1`, validates repair candidates through `--repair-brief`, parses compile logs, can run explicit local renderers plus `--expected-artifact`, SVG `--expected-svg-text`, `render-svg-text-path-only` / `pathOnlyGlyphUseCount` classification for path-only labels, path-only glyph placement checks for ``, path-only glyph overlap diagnostics through `render-svg-path-glyph-overlap`, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` drawing geometry checks, positioned `tspan` label geometry, transform-aware geometry for SVG bounded-canvas/text-overlap and label-vs-drawing smoke through `render-svg-label-overlap`, and PNG nonblank / clipped / dense-foreground screenshot smoke checks through `foregroundBounds`, `foregroundDensity`, `render-png-content-clipped`, and `render-png-foreground-dense` without shell parsing, includes aggregate maintainer smoke fixtures through `npm run diagram:smoke-circuitikz`, and has generic preview diagnostics, diagnostic summary counts, diagnostics-aware history entries, and source-only fallback through `RenderArtifact.diagnostics` and the preview modal | Add OCR-level label recognition, precise pixel-level overlap checks, broader SVG path coverage where needed, and automated topology-preserving repair execution | +| circuitikz | `CircuitSpec -> circuitikz` prototype exports common-source and CMOS inverter golden templates, rejects repair topology drift through `--topology-reference`, emits topology-preserving repair briefs through `--repair-brief-output` and schema `notemd.circuitikz.repair-brief.v1`, validates repair candidates through `--repair-brief`, parses compile logs, can run explicit local renderers plus `--expected-artifact`, SVG `--expected-svg-text`, `render-svg-text-path-only` / `pathOnlyGlyphUseCount` classification for path-only labels, path-only glyph placement checks for ``, path-only glyph overlap diagnostics through `render-svg-path-glyph-overlap`, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, stroke-width-aware SVG bounds and label overlap checks, `polyline` / `polygon` drawing geometry checks, positioned `tspan` label geometry, transform-aware geometry for SVG bounded-canvas/text-overlap and label-vs-drawing smoke through `render-svg-label-overlap`, and PNG nonblank / clipped / dense-foreground screenshot smoke checks, including indexed-color palette alpha and grayscale/RGB tRNS transparent samples, through `foregroundBounds`, `foregroundDensity`, `render-png-content-clipped`, and `render-png-foreground-dense` without shell parsing, includes aggregate maintainer smoke fixtures through `npm run diagram:smoke-circuitikz`, and has generic preview diagnostics, diagnostic summary counts, diagnostics-aware history entries, and source-only fallback through `RenderArtifact.diagnostics` and the preview modal | Add OCR-level label recognition, precise pixel-level overlap checks, broader SVG path coverage where needed, and automated topology-preserving repair execution | | TikZJax integration | Candidate render host for Obsidian-side display | Keep it optional; do not make TikZJax a hard plugin runtime dependency | ## Configuration diff --git a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/diagrams.mdx b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/diagrams.mdx index 629a8a0b..c0744744 100644 --- a/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/diagrams.mdx +++ b/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/diagrams.mdx @@ -256,7 +256,7 @@ SVG smoke 会检查 `` root、正的尺寸或 `viewBox`、至少一个可 同一层 geometry pass 也按 SVG number grammar 处理 leading-dot decimals 与 explicit plus signs,因此 dvisvgm 输出的 `.5`、`-.5` 或 `+.5` 这类紧凑坐标会在 bounds checks 中保留小数语义,而不会变成 false out-of-bounds geometry 或被静默跳过。 -如果 renderer 输出 `.png`,同一个 expected-artifact 路径会变成第一层 screenshot smoke:Notemd 会解码非交错的 1/2/4/8-bit indexed-color PNG 文件、1/2/4/8/16-bit grayscale PNG 文件,以及 8/16-bit grayscale-alpha/RGB/RGBA PNG 文件。indexed-color 与 sub-byte grayscale 图像支持 packed samples;indexed-color 图像还支持 PLTE 与可选 tRNS 数据。16-bit direct samples 会被归一化到 smoke check 使用的同一个 8-bit RGBA 比较空间。smoke check 会检查正尺寸,把前景包围盒记录为 `foregroundBounds`,把包围盒内前景密度记录为 `foregroundDensity`,在所有可见像素都与左上角背景色一致时以 `render-png-blank` 失败,在前景内容贴到图像边界时以 `render-png-content-clipped` 失败,并在非平凡包围盒内前景像素异常密集时以 `render-png-foreground-dense` 失败。这能在不引入平台特定 shell 依赖的情况下捕获空白截图、明显画布裁剪和第一层 pixel-level crowding failure,但它还不是 OCR-level label recognition、精确 text-overlap detection 或 topology-preserving image repair。 +如果 renderer 输出 `.png`,同一个 expected-artifact 路径会变成第一层 screenshot smoke:Notemd 会解码非交错的 1/2/4/8-bit indexed-color PNG 文件、1/2/4/8/16-bit grayscale PNG 文件,以及 8/16-bit grayscale-alpha/RGB/RGBA PNG 文件。indexed-color 与 sub-byte grayscale 图像支持 packed samples;indexed-color 图像还支持 PLTE 与可选 tRNS 数据;grayscale/RGB 图像支持 tRNS transparent samples。16-bit direct samples 会被归一化到 smoke check 使用的同一个 8-bit RGBA 比较空间。smoke check 会检查正尺寸,把前景包围盒记录为 `foregroundBounds`,把包围盒内前景密度记录为 `foregroundDensity`,在所有可见像素都与左上角背景色一致时以 `render-png-blank` 失败,在前景内容贴到图像边界时以 `render-png-content-clipped` 失败,并在非平凡包围盒内前景像素异常密集时以 `render-png-foreground-dense` 失败。这能在不引入平台特定 shell 依赖的情况下捕获空白截图、明显画布裁剪和第一层 pixel-level crowding failure,但它还不是 OCR-level label recognition、精确 text-overlap detection 或 topology-preserving image repair。 当 diagnostics 显示 compile 或 render-smoke 失败时,CLI 也可以写出 topology-preserving repair brief: @@ -321,7 +321,7 @@ runner 使用 `docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json` | General diagrams | Mermaid、JSON Canvas、Vega-Lite、HTML 的 spec-first pipeline 已实现 | 继续扩展 semantic verification coverage | | Editable figures | `editable-html-svg`、Draw.io XML、Drawnix JSON artifact boundaries 已实现 | 只有在测试证明 editability 后再加入更丰富 primitives | | CLI support | `npm run diagram:export-artifact` 可从同一个 `DiagramSpec` 导出 editable HTML/SVG、Draw.io、Drawnix | 新 target 发布时增加 target-specific smoke fixtures | -| circuitikz | `CircuitSpec -> circuitikz` 原型已经可通过 `npm run diagram:export-circuitikz` 导出 common-source 与 CMOS inverter golden templates、通过 `--topology-reference` 拒绝 repair topology drift、通过 `--repair-brief-output` 和 schema `notemd.circuitikz.repair-brief.v1` 写出 topology-preserving repair briefs、通过 `--repair-brief` 校验 repair candidates、解析 compile logs,并在不经过 shell parsing 的情况下运行显式本地 renderer、`--expected-artifact`、SVG `--expected-svg-text`、用于 path-only labels 的 `render-svg-text-path-only` / `pathOnlyGlyphUseCount` 分类、针对 `` 的 path-only glyph placement checks、通过 `render-svg-path-glyph-overlap` 执行的 path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` drawing geometry checks、positioned `tspan` label geometry、带 transform-aware geometry 的 SVG bounded-canvas / text-overlap 以及通过 `render-svg-label-overlap` 报告的 label-vs-drawing smoke,以及通过 `foregroundBounds`、`foregroundDensity`、`render-png-content-clipped` 和 `render-png-foreground-dense` 执行的 PNG nonblank / clipped / dense-foreground screenshot smoke checks;现在还通过 `npm run diagram:smoke-circuitikz` 提供聚合 maintainer smoke fixtures,并通过 `RenderArtifact.diagnostics` 和 preview modal 具备通用 preview diagnostics、diagnostic summary counts、diagnostics-aware history entries 与 source-only fallback | 增加 OCR-level label recognition、精确 pixel-level overlap checks、按需补齐更完整 SVG path coverage,并推进自动化 topology-preserving repair execution | +| circuitikz | `CircuitSpec -> circuitikz` 原型已经可通过 `npm run diagram:export-circuitikz` 导出 common-source 与 CMOS inverter golden templates、通过 `--topology-reference` 拒绝 repair topology drift、通过 `--repair-brief-output` 和 schema `notemd.circuitikz.repair-brief.v1` 写出 topology-preserving repair briefs、通过 `--repair-brief` 校验 repair candidates、解析 compile logs,并在不经过 shell parsing 的情况下运行显式本地 renderer、`--expected-artifact`、SVG `--expected-svg-text`、用于 path-only labels 的 `render-svg-text-path-only` / `pathOnlyGlyphUseCount` 分类、针对 `` 的 path-only glyph placement checks、通过 `render-svg-path-glyph-overlap` 执行的 path-only glyph overlap diagnostics、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、stroke-width-aware SVG bounds 与 label overlap checks、`polyline` / `polygon` drawing geometry checks、positioned `tspan` label geometry、带 transform-aware geometry 的 SVG bounded-canvas / text-overlap 以及通过 `render-svg-label-overlap` 报告的 label-vs-drawing smoke,以及通过 `foregroundBounds`、`foregroundDensity`、`render-png-content-clipped` 和 `render-png-foreground-dense` 执行的 PNG nonblank / clipped / dense-foreground screenshot smoke checks,包含 indexed-color palette alpha 与 grayscale/RGB tRNS transparent samples;现在还通过 `npm run diagram:smoke-circuitikz` 提供聚合 maintainer smoke fixtures,并通过 `RenderArtifact.diagnostics` 和 preview modal 具备通用 preview diagnostics、diagnostic summary counts、diagnostics-aware history entries 与 source-only fallback | 增加 OCR-level label recognition、精确 pixel-level overlap checks、按需补齐更完整 SVG path coverage,并推进自动化 topology-preserving repair execution | | TikZJax integration | Obsidian-side display 的候选 render host | 保持可选,不把 TikZJax 变成硬 runtime 依赖 | ## Configuration