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 a1da0b63..15c2740c 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 @@ -228,6 +228,7 @@ Phase E implementation status on 2026-07-05: `scripts/export-diagram-artifact.js - [x] Project `layoutHints.inputSide` and `layoutHints.outputSide` into deterministic circuitikz input/output port placement for supported golden templates. - [x] Add maintainer smoke fixtures and an aggregate runner for every supported circuitikz golden family. - [x] Add a constrained `cmos-nand2` / `cmos-nand2-v1` golden family with parallel PMOS pull-up validation, series NMOS pull-down validation, dual inputs `va` / `vb`, deterministic LaTeX export, layout-hint port projection, and a maintainer fixture. +- [x] Add a constrained `cmos-nor2` / `cmos-nor2-v1` golden family with series PMOS pull-up validation, parallel NMOS pull-down validation, dual inputs `va` / `vb`, deterministic LaTeX export, layout-hint port projection, and a maintainer fixture. - [x] Add explicit missing-renderer availability reports to the aggregate runner so release evidence can record `rendererAvailability.status: "missing-configuration"` without shell probing or silent fixture skips. - [x] Add a topology-preserving repair guard through `--topology-reference`, `createCircuitTopologySignature`, and `assertCircuitTopologyUnchanged`. - [x] Add a topology-preserving repair brief handoff through `--repair-brief-output` and schema `notemd.circuitikz.repair-brief.v1`. @@ -241,7 +242,9 @@ Phase E implementation status on 2026-07-05: `scripts/export-diagram-artifact.js - [x] Add SVG close-path current-point handling so relative commands after `Z/z` continue from the subpath start instead of causing false bounded-canvas failures. - [x] Add SVG hidden/transparent element handling so `display:none`, `visibility:hidden/collapse`, and overall `opacity:0` elements do not satisfy visible-artifact smoke. -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`, `cmos-inverter-v1`, and `cmos-nand2-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`, decoded accessibility metadata checks through `aria-label`, ``, and `<desc>`, 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 `<use>` 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, format-specific `render-png-unsupported` guidance for Adam7 interlaced PNGs and unsupported indexed-color bit depths, 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 every supported golden-family `CircuitSpec` fixture, including `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json`, and `scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` runs them through the same explicit shell-free renderer configuration, returns aggregate per-fixture evidence, and can now persist a missing-renderer availability report with `rendererAvailability.status: "missing-configuration"` and `compile-executable-invalid` while still exporting fixture `.tex` artifacts. `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, accessibility metadata expected-text checks, 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, format-specific unsupported PNG diagnostics, 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, aggregate execution, missing-renderer availability reports, 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 automatic renderer installation/discovery, OCR recognition for path-only visual 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`, `cmos-inverter-v1`, `cmos-nand2-v1`, and `cmos-nor2-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`, decoded accessibility metadata checks through `aria-label`, `<title>`, and `<desc>`, 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 `<use>` 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, format-specific `render-png-unsupported` guidance for Adam7 interlaced PNGs and unsupported indexed-color bit depths, 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 every supported golden-family `CircuitSpec` fixture, including `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json` and `docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json`, and `scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` runs them through the same explicit shell-free renderer configuration, returns aggregate per-fixture evidence, and can now persist a missing-renderer availability report with `rendererAvailability.status: "missing-configuration"` and `compile-executable-invalid` while still exporting fixture `.tex` artifacts. `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, accessibility metadata expected-text checks, 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, format-specific unsupported PNG diagnostics, 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, aggregate execution, missing-renderer availability reports, 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 automatic renderer installation/discovery, OCR recognition for path-only visual text, precise pixel-level overlap detection, full SVG path coverage, and automated visual repair execution. + +2026-07-07 CMOS NOR family increment: `src/diagram/adapters/circuitikz/circuitSpec.ts` and `src/diagram/adapters/circuitikz/circuitikzExporter.ts` now support `cmos-nor2` with golden reference `cmos-nor2-v1`. The validator locks the series PMOS pull-up stack, the parallel NMOS pull-down network, and the two input nets `va` / `vb`; the exporter emits deterministic circuitikz and projects `layoutHints.inputSide` / `layoutHints.outputSide` into presentation-only ports. `docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json` adds the maintainer smoke fixture, and `src/tests/circuitikzExporter.test.ts` / `src/tests/circuitikzSmokeFixturesCli.test.ts` cover validation, topology-preserving layout projection, PMOS-stack rejection, and aggregate fixture execution. 2026-07-07 CMOS NAND family increment: `src/diagram/adapters/circuitikz/circuitSpec.ts` and `src/diagram/adapters/circuitikz/circuitikzExporter.ts` now support `cmos-nand2` with golden reference `cmos-nand2-v1`. The validator locks the parallel PMOS pull-up network, the series NMOS pull-down stack, and the two input nets `va` / `vb`; the exporter emits deterministic circuitikz and projects `layoutHints.inputSide` / `layoutHints.outputSide` into presentation-only ports. `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json` adds the maintainer smoke fixture, and `src/tests/circuitikzExporter.test.ts` / `src/tests/circuitikzSmokeFixturesCli.test.ts` cover validation, topology-preserving layout projection, and aggregate fixture execution. 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 43e74754..68335dc9 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 @@ -228,6 +228,7 @@ Phase E 于 2026-07-05 的实现状态:`scripts/export-diagram-artifact.js` - [x] 将 `layoutHints.inputSide` 与 `layoutHints.outputSide` 投影为已支持 golden templates 的确定性 circuitikz input/output port placement。 - [x] 为每个已支持的 circuitikz golden family 增加 maintainer smoke fixtures 和聚合 runner。 - [x] 增加受约束的 `cmos-nand2` / `cmos-nand2-v1` golden family,覆盖并联 PMOS pull-up validation、串联 NMOS pull-down validation、双输入 `va` / `vb`、确定性 LaTeX 导出、layout-hint port projection 与 maintainer fixture。 +- [x] 增加受约束的 `cmos-nor2` / `cmos-nor2-v1` golden family,覆盖串联 PMOS pull-up validation、并联 NMOS pull-down validation、双输入 `va` / `vb`、确定性 LaTeX 导出、layout-hint port projection 与 maintainer fixture。 - [x] 为聚合 runner 增加显式 missing-renderer availability reports,让 release evidence 可以记录 `rendererAvailability.status: "missing-configuration"`,而不是探测 shell 或静默跳过 fixtures。 - [x] 通过 `--topology-reference`、`createCircuitTopologySignature` 和 `assertCircuitTopologyUnchanged` 增加 topology-preserving repair guard。 - [x] 通过 `--repair-brief-output` 与 schema `notemd.circuitikz.repair-brief.v1` 增加 topology-preserving repair brief handoff。 @@ -241,7 +242,9 @@ Phase E 于 2026-07-05 的实现状态:`scripts/export-diagram-artifact.js` - [x] 增加 SVG close-path current-point handling,让 `Z/z` 后的 relative commands 从 subpath start 继续,而不是产生 false bounded-canvas failures。 - [x] 增加 SVG hidden/transparent element handling,让 `display:none`、`visibility:hidden/collapse` 与整体 `opacity:0` 元素不能满足 visible-artifact smoke。 -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` 和 `cmos-nand2-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 检查,通过 `aria-label`、`<title>` 与 `<desc>` 执行解码后的 accessibility metadata checks,通过 `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,在 `<use>` 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、针对 Adam7 interlaced PNG 与 unsupported indexed-color bit depths 的 format-specific `render-png-unsupported` guidance、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` fixture,包括 `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json`;`scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` 会用同一套显式 shell-free renderer 配置跑完这些 fixtures,返回每个 fixture 的聚合证据,并且可以在未配置 renderer 时仍导出 fixture `.tex` artifacts,同时用 `rendererAvailability.status: "missing-configuration"` 与 `compile-executable-invalid` 持久化 missing-renderer availability report。`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、accessibility metadata expected-text checks、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、format-specific unsupported PNG diagnostics、grayscale PNG packed/direct sample decoding、16-bit direct sample normalization、PNG 前景包围盒与前景密度报告、PNG 裁剪内容诊断、异常密集前景诊断、bounded SVG viewBox checks、明显 SVG text-overlap checks、maintainer fixture discovery、聚合执行、missing-renderer availability reports、preview diagnostics 可见性、diagnostic summary counts、source-only preview fallback、diagnostics-aware history entries、diagnostics JSON 输出,以及 compile log 或 smoke report 含错误时 CLI 非零退出。实现仍有意停在 automatic renderer installation/discovery、path-only visual 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`、`cmos-nand2-v1` 和 `cmos-nor2-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 检查,通过 `aria-label`、`<title>` 与 `<desc>` 执行解码后的 accessibility metadata checks,通过 `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,在 `<use>` 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、针对 Adam7 interlaced PNG 与 unsupported indexed-color bit depths 的 format-specific `render-png-unsupported` guidance、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` fixture,包括 `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json` 与 `docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json`;`scripts/run-circuitikz-smoke-fixtures.js` / `npm run diagram:smoke-circuitikz` 会用同一套显式 shell-free renderer 配置跑完这些 fixtures,返回每个 fixture 的聚合证据,并且可以在未配置 renderer 时仍导出 fixture `.tex` artifacts,同时用 `rendererAvailability.status: "missing-configuration"` 与 `compile-executable-invalid` 持久化 missing-renderer availability report。`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、accessibility metadata expected-text checks、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、format-specific unsupported PNG diagnostics、grayscale PNG packed/direct sample decoding、16-bit direct sample normalization、PNG 前景包围盒与前景密度报告、PNG 裁剪内容诊断、异常密集前景诊断、bounded SVG viewBox checks、明显 SVG text-overlap checks、maintainer fixture discovery、聚合执行、missing-renderer availability reports、preview diagnostics 可见性、diagnostic summary counts、source-only preview fallback、diagnostics-aware history entries、diagnostics JSON 输出,以及 compile log 或 smoke report 含错误时 CLI 非零退出。实现仍有意停在 automatic renderer installation/discovery、path-only visual text 的 OCR recognition、更精确 pixel-level overlap detection、完整 SVG path coverage 和 automated visual repair execution 之前。 + +2026-07-07 CMOS NOR family increment:`src/diagram/adapters/circuitikz/circuitSpec.ts` 与 `src/diagram/adapters/circuitikz/circuitikzExporter.ts` 现在支持 `cmos-nor2`,golden reference 为 `cmos-nor2-v1`。validator 会锁定串联 PMOS pull-up stack、并联 NMOS pull-down network 和双输入 nets `va` / `vb`;exporter 会输出确定性 circuitikz,并把 `layoutHints.inputSide` / `layoutHints.outputSide` 投影为只影响展示的端口。`docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json` 增加 maintainer smoke fixture,`src/tests/circuitikzExporter.test.ts` / `src/tests/circuitikzSmokeFixturesCli.test.ts` 覆盖 validation、topology-preserving layout projection、PMOS-stack rejection 与 aggregate fixture execution。 2026-07-07 CMOS NAND family increment:`src/diagram/adapters/circuitikz/circuitSpec.ts` 与 `src/diagram/adapters/circuitikz/circuitikzExporter.ts` 现在支持 `cmos-nand2`,golden reference 为 `cmos-nand2-v1`。validator 会锁定并联 PMOS pull-up network、串联 NMOS pull-down stack 和双输入 nets `va` / `vb`;exporter 会输出确定性 circuitikz,并把 `layoutHints.inputSide` / `layoutHints.outputSide` 投影为只影响展示的端口。`docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json` 增加 maintainer smoke fixture,`src/tests/circuitikzExporter.test.ts` / `src/tests/circuitikzSmokeFixturesCli.test.ts` 覆盖 validation、topology-preserving layout projection 与 aggregate fixture execution。 diff --git a/docs/maintainer/circuitikz-export-prototype.md b/docs/maintainer/circuitikz-export-prototype.md index 94e1a218..a4f56462 100644 --- a/docs/maintainer/circuitikz-export-prototype.md +++ b/docs/maintainer/circuitikz-export-prototype.md @@ -167,6 +167,7 @@ The repository now includes maintainer fixtures for every supported golden famil | `docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json` | `common-source-amplifier` | | `docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json` | `cmos-inverter` | | `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json` | `cmos-nand2` | +| `docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json` | `cmos-nor2` | Run every fixture through the same explicit renderer configuration with: @@ -216,6 +217,7 @@ This is not a generic TikZ generator. The current prototype supports only golden | `common-source-amplifier` | `common-source-nmos-v1` | NMOS common-source amplifier with `R_D`, `VDD`, `vin`, `vout`, and grounded source | | `cmos-inverter` | `cmos-inverter-v1` | PMOS-over-NMOS CMOS inverter with shared gate input and shared drain output | | `cmos-nand2` | `cmos-nand2-v1` | Two-input CMOS NAND with parallel PMOS pull-up devices, series NMOS pull-down devices, `va`, `vb`, and `vout` | +| `cmos-nor2` | `cmos-nor2-v1` | Two-input CMOS NOR with series PMOS pull-up devices, parallel NMOS pull-down devices, `va`, `vb`, and `vout` | The adapter validates the structural invariant first, then emits a fixed layout. For example, the CMOS inverter requires: @@ -228,6 +230,8 @@ The adapter validates the structural invariant first, then emits a fixed layout. The CMOS NAND template adds a stronger digital-logic invariant: `MPA` and `MPB` must be PMOS devices in the parallel pull-up network from `VDD` to `vout`; `MNA` and `MNB` must be NMOS devices in a series pull-down stack from `vout` to `GND`; `va` must drive `MPA.G` and `MNA.G`; and `vb` must drive `MPB.G` and `MNB.G`. `layoutHints.inputSide` and `layoutHints.outputSide` only move the input/output ports and presentation routing; they do not change the topology signature. +The CMOS NOR template mirrors the digital-logic constraint in the opposite networks: `MPA` and `MPB` must be PMOS devices in a series pull-up stack from `VDD` to `vout`; `MNA` and `MNB` must be NMOS devices in the parallel pull-down network from `vout` to `GND`; `va` must drive `MPA.G` and `MNA.G`; and `vb` must drive `MPB.G` and `MNB.G`. The same layout-hint projection rule applies: port placement can move, but the topology signature cannot. + ## Why `CircuitSpec` Is Separate `DiagramSpec` is intentionally not widened for circuit diagrams yet. Circuit diagrams need topology, terminal references, layout lanes, and package conventions that generic flowcharts and data charts do not use. @@ -284,4 +288,4 @@ The tests verify: ## Non-Goals -This prototype does not bundle LaTeX, call TikZJax as an Obsidian runtime dependency, OCR path-only glyph text, run precise pixel-level overlap detection, cover unsupported SVG path geometry, or use rendered-image feedback for automatic repair. Those are later gates. It also does not accept arbitrary natural-language circuit requests. The important current claim is narrower: validated `CircuitSpec` input can produce stable, readable circuitikz for two high-value golden families, existing compile logs can be converted into actionable diagnostics, and an explicitly configured local renderer can be executed without shell-specific command parsing while optionally proving that a concrete output artifact was created and, for SVG or PNG output, structurally renderable enough for later visual inspection. SVG output now includes transform-aware geometry for common SVG transforms, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, positioned `tspan` label geometry, path-only label classification, path-only glyph overlap detection, and conservative label-vs-drawing overlap detection; PNG output exposes foreground bounds, foreground density, and format-specific unsupported-export guidance needed to reject obvious canvas clipping, first-pass pixel crowding, Adam7 interlaced PNGs, and unsupported indexed-color bit depths before a topology-preserving repair loop. +This prototype does not bundle LaTeX, call TikZJax as an Obsidian runtime dependency, OCR path-only glyph text, run precise pixel-level overlap detection, cover unsupported SVG path geometry, or use rendered-image feedback for automatic repair. Those are later gates. It also does not accept arbitrary natural-language circuit requests. The important current claim is narrower: validated `CircuitSpec` input can produce stable, readable circuitikz for four golden families, existing compile logs can be converted into actionable diagnostics, and an explicitly configured local renderer can be executed without shell-specific command parsing while optionally proving that a concrete output artifact was created and, for SVG or PNG output, structurally renderable enough for later visual inspection. SVG output now includes transform-aware geometry for common SVG transforms, exact arc bounds for A/a arc extrema, exact Bezier curve bounds for C/S/Q/T curve extrema, positioned `tspan` label geometry, path-only label classification, path-only glyph overlap detection, and conservative label-vs-drawing overlap detection; PNG output exposes foreground bounds, foreground density, and format-specific unsupported-export guidance needed to reject obvious canvas clipping, first-pass pixel crowding, Adam7 interlaced PNGs, and unsupported indexed-color bit depths before a topology-preserving repair loop. diff --git a/docs/maintainer/circuitikz-export-prototype.zh-CN.md b/docs/maintainer/circuitikz-export-prototype.zh-CN.md index 2ff0bc75..2dd34562 100644 --- a/docs/maintainer/circuitikz-export-prototype.zh-CN.md +++ b/docs/maintainer/circuitikz-export-prototype.zh-CN.md @@ -167,6 +167,7 @@ runner 位于 `src/diagram/adapters/circuitikz/circuitikzCompileRunner.ts`。它 | `docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json` | `common-source-amplifier` | | `docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json` | `cmos-inverter` | | `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json` | `cmos-nand2` | +| `docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json` | `cmos-nor2` | 使用同一套显式 renderer 配置跑完所有 fixture: @@ -216,6 +217,7 @@ npm run diagram:smoke-circuitikz -- \ | `common-source-amplifier` | `common-source-nmos-v1` | 带 `R_D`、`VDD`、`vin`、`vout` 和源极接地的 NMOS 共源放大器 | | `cmos-inverter` | `cmos-inverter-v1` | PMOS 在上、NMOS 在下,共用 gate 输入、共用 drain 输出的 CMOS 反相器 | | `cmos-nand2` | `cmos-nand2-v1` | 两输入 CMOS NAND,包含并联 PMOS 上拉、串联 NMOS 下拉、`va`、`vb` 与 `vout` | +| `cmos-nor2` | `cmos-nor2-v1` | 两输入 CMOS NOR,包含串联 PMOS 上拉、并联 NMOS 下拉、`va`、`vb` 与 `vout` | adapter 会先验证结构 invariant,再输出固定布局。例如 CMOS 反相器要求: @@ -228,6 +230,8 @@ adapter 会先验证结构 invariant,再输出固定布局。例如 CMOS 反 CMOS NAND template 进一步固定数字逻辑 invariant:`MPA` 与 `MPB` 必须是从 `VDD` 到 `vout` 的并联 PMOS pull-up network;`MNA` 与 `MNB` 必须是从 `vout` 到 `GND` 的串联 NMOS pull-down stack;`va` 必须同时驱动 `MPA.G` 与 `MNA.G`;`vb` 必须同时驱动 `MPB.G` 与 `MNB.G`。`layoutHints.inputSide` 与 `layoutHints.outputSide` 只移动输入/输出端口和展示 routing,不改变 topology signature。 +CMOS NOR template 则锁定互补的数字逻辑 invariant:`MPA` 与 `MPB` 必须是从 `VDD` 到 `vout` 的串联 PMOS pull-up stack;`MNA` 与 `MNB` 必须是从 `vout` 到 `GND` 的并联 NMOS pull-down network;`va` 必须同时驱动 `MPA.G` 与 `MNA.G`;`vb` 必须同时驱动 `MPB.G` 与 `MNB.G`。同样只允许通过 layout hints 移动端口展示位置,不能改变 topology signature。 + ## 为什么 `CircuitSpec` 独立存在 当前不会为了电路图过早扩展 `DiagramSpec`。电路图需要拓扑、terminal references、layout lanes 和 package conventions;这些字段并不属于普通 flowchart 或 data chart。 @@ -284,4 +288,4 @@ npm test -- --runInBand src/tests/circuitikzExporter.test.ts src/tests/circuitik ## 非目标 -这个原型不捆绑 LaTeX、不把 TikZJax 作为 Obsidian runtime 依赖调用、不 OCR 识别 path-only glyph text、不做精确 pixel-level overlap detection、不覆盖尚未支持的 SVG path geometry,也不使用渲染图像反馈进行自动修复。这些是后续 gate。它也不接受任意自然语言电路请求。当前重要声明更窄:经过验证的 `CircuitSpec` 输入可以为两个高价值 golden families 生成稳定、可读的 circuitikz,已有 compile logs 可以转换为 actionable diagnostics,并且显式配置的本地 renderer 可以在不走 shell-specific command parsing 的情况下执行,同时可选证明具体输出 artifact 已经创建;如果输出是 SVG 或 PNG,还能证明它具备进入后续视觉检查的基本结构。SVG 输出现在包含常见 SVG transforms 的 transform-aware geometry、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、positioned `tspan` label geometry、path-only label classification、path-only glyph overlap detection 与保守 label-vs-drawing overlap detection;PNG 输出则会暴露前景包围盒、前景密度与 format-specific unsupported-export guidance,用于在 topology-preserving repair loop 之前拒绝明显画布裁剪、第一层 pixel crowding、Adam7 interlaced PNG 和 unsupported indexed-color bit depths。 +这个原型不捆绑 LaTeX、不把 TikZJax 作为 Obsidian runtime 依赖调用、不 OCR 识别 path-only glyph text、不做精确 pixel-level overlap detection、不覆盖尚未支持的 SVG path geometry,也不使用渲染图像反馈进行自动修复。这些是后续 gate。它也不接受任意自然语言电路请求。当前重要声明更窄:经过验证的 `CircuitSpec` 输入可以为四个 golden families 生成稳定、可读的 circuitikz,已有 compile logs 可以转换为 actionable diagnostics,并且显式配置的本地 renderer 可以在不走 shell-specific command parsing 的情况下执行,同时可选证明具体输出 artifact 已经创建;如果输出是 SVG 或 PNG,还能证明它具备进入后续视觉检查的基本结构。SVG 输出现在包含常见 SVG transforms 的 transform-aware geometry、针对 A/a arc extrema 的 exact arc bounds、针对 C/S/Q/T curve extrema 的 exact Bezier curve bounds、positioned `tspan` label geometry、path-only label classification、path-only glyph overlap detection 与保守 label-vs-drawing overlap detection;PNG 输出则会暴露前景包围盒、前景密度与 format-specific unsupported-export guidance,用于在 topology-preserving repair loop 之前拒绝明显画布裁剪、第一层 pixel crowding、Adam7 interlaced PNG 和 unsupported indexed-color bit depths。 diff --git a/docs/maintainer/circuitikz-figure-generation-roadmap.md b/docs/maintainer/circuitikz-figure-generation-roadmap.md index 3df37ad6..834691b8 100644 --- a/docs/maintainer/circuitikz-figure-generation-roadmap.md +++ b/docs/maintainer/circuitikz-figure-generation-roadmap.md @@ -140,7 +140,7 @@ Screenshot feedback can be manual first. Automated screenshot checks should star Phase A is documented. Phase B/C now have a constrained repository prototype: - `src/diagram/adapters/circuitikz/circuitSpec.ts` defines the separate circuit-only spec boundary. -- `src/diagram/adapters/circuitikz/circuitikzExporter.ts` validates topology and emits deterministic `circuitikz` LaTeX for `common-source-amplifier`, `cmos-inverter`, and `cmos-nand2` / `cmos-nand2-v1`. It now projects `layoutHints.inputSide` and `layoutHints.outputSide` into constrained port placement for those golden templates, so layout-only repair candidates can move input/output ports without changing topology. The CMOS NAND template locks the parallel PMOS pull-up network, the series NMOS pull-down stack, and dual input nets `va` / `vb` before export. +- `src/diagram/adapters/circuitikz/circuitikzExporter.ts` validates topology and emits deterministic `circuitikz` LaTeX for `common-source-amplifier`, `cmos-inverter`, `cmos-nand2` / `cmos-nand2-v1`, and `cmos-nor2` / `cmos-nor2-v1`. It now projects `layoutHints.inputSide` and `layoutHints.outputSide` into constrained port placement for those golden templates, so layout-only repair candidates can move input/output ports without changing topology. The CMOS NAND template locks the parallel PMOS pull-up network, the series NMOS pull-down stack, and dual input nets `va` / `vb` before export; the CMOS NOR template locks the complementary series PMOS pull-up stack and parallel NMOS pull-down network before export. - `src/diagram/adapters/circuitikz/circuitikzExporter.ts` now also exposes `createCircuitTopologySignature` and `assertCircuitTopologyUnchanged` so topology-preserving repair can reject electrical drift while allowing label and layout changes. - `scripts/export-circuitikz.js` and `npm run diagram:export-circuitikz` provide the offline export command, including `--topology-reference` for repair candidates. - `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, next verification steps, and a structured `repairPrompt`. The prompt role is `topology-preserving-circuitikz-repair`; it includes `diagnosticFocus` items derived from diagnostics and `acceptanceCriteria` that require candidate validation plus fresh compile and render-smoke checks. Candidate specs can be checked against the brief signature before output is written, and `--repair-brief` now returns `repairAcceptance` evidence with schema `notemd.circuitikz.repair-acceptance.v1`, `readyForVisualAcceptance`, gate-level status, blocking diagnostics, and `remainingChecks`. `--repair-acceptance-output` can persist the same evidence as a JSON file for CI or release records. diff --git a/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md b/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md index 0d081385..be45a950 100644 --- a/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md +++ b/docs/maintainer/circuitikz-figure-generation-roadmap.zh-CN.md @@ -140,7 +140,7 @@ screenshot feedback 可以先保持人工执行。自动 screenshot 检查应从 Phase A 已经文档化。Phase B/C 现在有了受约束的仓库内原型: - `src/diagram/adapters/circuitikz/circuitSpec.ts` 定义独立的 circuit-only spec 边界。 -- `src/diagram/adapters/circuitikz/circuitikzExporter.ts` 会验证拓扑,并为 `common-source-amplifier`、`cmos-inverter` 和 `cmos-nand2` / `cmos-nand2-v1` 输出确定性的 `circuitikz` LaTeX。它现在会把 `layoutHints.inputSide` 与 `layoutHints.outputSide` 投影为这些 golden templates 的受约束 port placement,因此 layout-only repair candidates 可以移动输入/输出端口,而不改变拓扑。CMOS NAND template 会在导出前锁定并联 PMOS pull-up network、串联 NMOS pull-down stack 和双输入 nets `va` / `vb`。 +- `src/diagram/adapters/circuitikz/circuitikzExporter.ts` 会验证拓扑,并为 `common-source-amplifier`、`cmos-inverter`、`cmos-nand2` / `cmos-nand2-v1` 和 `cmos-nor2` / `cmos-nor2-v1` 输出确定性的 `circuitikz` LaTeX。它现在会把 `layoutHints.inputSide` 与 `layoutHints.outputSide` 投影为这些 golden templates 的受约束 port placement,因此 layout-only repair candidates 可以移动输入/输出端口,而不改变拓扑。CMOS NAND template 会在导出前锁定并联 PMOS pull-up network、串联 NMOS pull-down stack 和双输入 nets `va` / `vb`;CMOS NOR template 会在导出前锁定互补的串联 PMOS pull-up stack 与并联 NMOS pull-down network。 - `src/diagram/adapters/circuitikz/circuitikzExporter.ts` 现在还暴露 `createCircuitTopologySignature` 与 `assertCircuitTopologyUnchanged`,让 topology-preserving repair 可以拒绝电气拓扑漂移,同时允许 label 与 layout 变化。 - `scripts/export-circuitikz.js` 与 `npm run diagram:export-circuitikz` 提供离线导出命令,并通过 `--topology-reference` 支持 repair candidate。 - `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、允许修改项、禁止拓扑修改项、下一步验证步骤和结构化 `repairPrompt`。prompt role 是 `topology-preserving-circuitikz-repair`;它包含从 diagnostics 派生的 `diagnosticFocus` 项,以及要求 candidate validation、重新 compile 和 render-smoke checks 的 `acceptanceCriteria`。candidate spec 可以在写出 output 前按 brief signature 执行校验,`--repair-brief` 现在还会返回 schema `notemd.circuitikz.repair-acceptance.v1` 的 `repairAcceptance` 证据,包含 `readyForVisualAcceptance`、gate-level status、blocking diagnostics 与 `remainingChecks`。`--repair-acceptance-output` 可以把同一份 evidence 持久化为 JSON 文件,供 CI 或 release 记录使用。 diff --git a/docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json b/docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json new file mode 100644 index 00000000..096d33be --- /dev/null +++ b/docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json @@ -0,0 +1,34 @@ +{ + "circuitKind": "cmos-nor2", + "title": "Two Input CMOS NOR Gate", + "goldenReferenceId": "cmos-nor2-v1", + "style": { + "package": "circuitikz", + "voltageConvention": "american voltages" + }, + "nets": ["VDD", "GND", "va", "vb", "vout", "pull_up_mid"], + "components": [ + { "id": "MPA", "type": "pmos", "label": "$M_{PA}$", "terminals": { "S": "VDD", "G": "va", "D": "pull_up_mid" } }, + { "id": "MPB", "type": "pmos", "label": "$M_{PB}$", "terminals": { "S": "pull_up_mid", "G": "vb", "D": "vout" } }, + { "id": "MNA", "type": "nmos", "label": "$M_{NA}$", "terminals": { "D": "vout", "G": "va", "S": "GND" } }, + { "id": "MNB", "type": "nmos", "label": "$M_{NB}$", "terminals": { "D": "vout", "G": "vb", "S": "GND" } } + ], + "connections": [ + { "from": "VDD", "to": "MPA.S" }, + { "from": "MPA.D", "to": "MPB.S" }, + { "from": "MPB.D", "to": "vout" }, + { "from": "MNA.D", "to": "vout" }, + { "from": "MNB.D", "to": "vout" }, + { "from": "MNA.S", "to": "GND" }, + { "from": "MNB.S", "to": "GND" }, + { "from": "va", "to": "MPA.G" }, + { "from": "va", "to": "MNA.G" }, + { "from": "vb", "to": "MPB.G" }, + { "from": "vb", "to": "MNB.G" } + ], + "layoutHints": { + "inputSide": "left", + "outputSide": "right", + "routingStyle": "orthogonal" + } +} diff --git a/src/diagram/adapters/circuitikz/circuitSpec.ts b/src/diagram/adapters/circuitikz/circuitSpec.ts index 9b246089..b0017f0d 100644 --- a/src/diagram/adapters/circuitikz/circuitSpec.ts +++ b/src/diagram/adapters/circuitikz/circuitSpec.ts @@ -1,7 +1,8 @@ export const SUPPORTED_CIRCUIT_KINDS = [ 'common-source-amplifier', 'cmos-inverter', - 'cmos-nand2' + 'cmos-nand2', + 'cmos-nor2' ] as const; export type CircuitKind = typeof SUPPORTED_CIRCUIT_KINDS[number]; diff --git a/src/diagram/adapters/circuitikz/circuitikzExporter.ts b/src/diagram/adapters/circuitikz/circuitikzExporter.ts index 7b820a19..8709a7e7 100644 --- a/src/diagram/adapters/circuitikz/circuitikzExporter.ts +++ b/src/diagram/adapters/circuitikz/circuitikzExporter.ts @@ -46,12 +46,12 @@ function extendedPortX(side: 'left' | 'right'): string { return side === 'left' ? '0.8' : '5.2'; } -function nandInputPortX(side: 'left' | 'right'): string { +function dualInputPortX(side: 'left' | 'right'): string { return side === 'left' ? '0.8' : '5.2'; } function requiredNetsForCircuitKind(circuitKind: string): string[] { - if (circuitKind === 'cmos-nand2') { + if (circuitKind === 'cmos-nand2' || circuitKind === 'cmos-nor2') { return ['VDD', 'GND', 'va', 'vb', 'vout']; } @@ -286,6 +286,43 @@ function validateCmosNand2Template( } } +function validateCmosNor2Template( + spec: CircuitSpec, + componentById: Map<string, CircuitComponent>, + errors: string[] +): void { + if (spec.goldenReferenceId !== 'cmos-nor2-v1') { + errors.push('CMOS NOR requires goldenReferenceId "cmos-nor2-v1".'); + } + + requireComponent(componentById, 'MPA', 'pmos', errors); + requireComponent(componentById, 'MPB', 'pmos', errors); + requireComponent(componentById, 'MNA', 'nmos', errors); + requireComponent(componentById, 'MNB', 'nmos', errors); + + if (!hasConnection(spec.connections, 'VDD', 'MPA.S')) { + errors.push('CMOS NOR requires MPA.S to connect to VDD at the top of the pull-up stack.'); + } + if (!hasConnection(spec.connections, 'MPA.D', 'MPB.S')) { + errors.push('CMOS NOR requires MPA.D to connect to MPB.S for the series pull-up stack.'); + } + if (!hasConnection(spec.connections, 'MPB.D', 'vout')) { + errors.push('CMOS NOR requires MPB.D to connect to vout at the bottom of the pull-up stack.'); + } + if (!hasConnection(spec.connections, 'MNA.D', 'vout') || !hasConnection(spec.connections, 'MNB.D', 'vout')) { + errors.push('CMOS NOR requires both NMOS drains to connect to vout for the parallel pull-down network.'); + } + if (!hasConnection(spec.connections, 'MNA.S', 'GND') || !hasConnection(spec.connections, 'MNB.S', 'GND')) { + errors.push('CMOS NOR requires both NMOS sources to connect to GND for the parallel pull-down network.'); + } + if (!hasConnection(spec.connections, 'va', 'MPA.G') || !hasConnection(spec.connections, 'va', 'MNA.G')) { + errors.push('CMOS NOR requires va to drive both MPA.G and MNA.G.'); + } + if (!hasConnection(spec.connections, 'vb', 'MPB.G') || !hasConnection(spec.connections, 'vb', 'MNB.G')) { + errors.push('CMOS NOR requires vb to drive both MPB.G and MNB.G.'); + } +} + export function validateCircuitSpec(spec: CircuitSpec): CircuitSpecValidationResult { const errors: string[] = []; @@ -328,6 +365,8 @@ export function validateCircuitSpec(spec: CircuitSpec): CircuitSpecValidationRes validateCmosInverterTemplate(spec, componentById, errors); } else if (spec.circuitKind === 'cmos-nand2') { validateCmosNand2Template(spec, componentById, errors); + } else if (spec.circuitKind === 'cmos-nor2') { + validateCmosNor2Template(spec, componentById, errors); } return { @@ -434,23 +473,23 @@ function renderCmosNand2Template(spec: CircuitSpec): string { ? ` (MPA.G) to [short] (1.7,4.2) (MNA.G) to [short] (1.7,2.7) (1.7,4.2) to [short] (1.7,2.7) - to [short, -o] (${nandInputPortX(inputSide)},3.45) + to [short, -o] (${dualInputPortX(inputSide)},3.45) node[left]{$v_A$} (MPB.G) to [short] (1.3,4.2) (MNB.G) to [short] (1.3,1.8) (1.3,4.2) to [short] (1.3,1.8) - to [short, -o] (${nandInputPortX(inputSide)},2.7) + to [short, -o] (${dualInputPortX(inputSide)},2.7) node[left]{$v_B$};` : ` (MPA.G) to [short] (4.1,4.2) (MNA.G) to [short] (4.1,2.7) (4.1,4.2) to [short] (4.1,2.7) to [short] (4.5,2.7) - to [short, -o] (${nandInputPortX(inputSide)},4.15) + to [short, -o] (${dualInputPortX(inputSide)},4.15) node[right]{$v_A$} (MPB.G) to [short] (4.5,4.2) (MNB.G) to [short] (4.5,1.8) (4.5,4.2) to [short] (4.5,1.8) - to [short, -o] (${nandInputPortX(inputSide)},1.85) + to [short, -o] (${dualInputPortX(inputSide)},1.85) node[right]{$v_B$};`; return `\\usepackage{circuitikz} @@ -482,6 +521,68 @@ ${inputRoute} `; } +function renderCmosNor2Template(spec: CircuitSpec): string { + const mpa = spec.components.find(component => component.id === 'MPA'); + const mpb = spec.components.find(component => component.id === 'MPB'); + const mna = spec.components.find(component => component.id === 'MNA'); + const mnb = spec.components.find(component => component.id === 'MNB'); + const mpaLabel = normalizeLabel(mpa?.label, '$M_{PA}$'); + const mpbLabel = normalizeLabel(mpb?.label, '$M_{PB}$'); + const mnaLabel = normalizeLabel(mna?.label, '$M_{NA}$'); + const mnbLabel = normalizeLabel(mnb?.label, '$M_{NB}$'); + const inputSide = layoutSide(spec.layoutHints?.inputSide, 'left'); + const outputSide = layoutSide(spec.layoutHints?.outputSide, 'right'); + const inputRoute = inputSide === 'left' + ? ` (MPA.G) to [short] (1.7,4.35) + (MNA.G) to [short] (1.7,1.95) + (1.7,4.35) to [short] (1.7,1.95) + to [short, -o] (${dualInputPortX(inputSide)},4.35) + node[left]{$v_A$} + (MPB.G) to [short] (1.3,3.35) + (MNB.G) to [short] (1.3,1.45) + (1.3,3.35) to [short] (1.3,1.45) + to [short, -o] (${dualInputPortX(inputSide)},1.95) + node[left]{$v_B$};` + : ` (MPA.G) to [short] (4.1,4.35) + (MNA.G) to [short] (4.1,1.95) + (4.1,4.35) to [short] (4.1,1.95) + to [short] (4.5,1.95) + to [short, -o] (${dualInputPortX(inputSide)},4.35) + node[right]{$v_A$} + (MPB.G) to [short] (4.5,3.35) + (MNB.G) to [short] (4.5,1.45) + (4.5,3.35) to [short] (4.5,1.45) + to [short, -o] (${dualInputPortX(inputSide)},1.95) + node[right]{$v_B$};`; + + return `\\usepackage{circuitikz} +\\begin{document} +\\begin{circuitikz}[${spec.style.voltageConvention}] +\\draw + (3,5) node[vcc]{$V_{DD}$} + to [short] (3,4.8) + node[pmos, anchor=S] (MPA) {${mpaLabel}} + (MPA.D) to [short] (3,3.75) + node[pmos, anchor=S] (MPB) {${mpbLabel}} + (MPB.D) to [short] (3,2.75); +\\draw + (3,2.75) to [short, *-o] (${commonPortX(outputSide)},2.75) node[${outputSide}]{$v_{out}$}; +\\draw + (3,2.75) to [short] (2.5,2.2) + node[nmos, anchor=D] (MNA) {${mnaLabel}} + (MNA.S) to [short] (2.5,0.7) + (3,2.75) to [short] (3.5,2.2) + node[nmos, anchor=D] (MNB) {${mnbLabel}} + (MNB.S) to [short] (3.5,0.7) + (2.5,0.7) to [short] (3.5,0.7) + (3,0.7) node[ground]{}; +\\draw +${inputRoute} +\\end{circuitikz} +\\end{document} +`; +} + export function exportCircuitSpecToCircuitikz(spec: CircuitSpec): string { assertValidCircuitSpec(spec); @@ -492,6 +593,8 @@ export function exportCircuitSpecToCircuitikz(spec: CircuitSpec): string { return renderCmosInverterTemplate(spec); case 'cmos-nand2': return renderCmosNand2Template(spec); + case 'cmos-nor2': + return renderCmosNor2Template(spec); default: throw new ValidationError('Unsupported circuitKind.', 'INVALID_INPUT'); } diff --git a/src/tests/circuitikzExportDocsContract.test.ts b/src/tests/circuitikzExportDocsContract.test.ts index 8e3219c5..1c286a73 100644 --- a/src/tests/circuitikzExportDocsContract.test.ts +++ b/src/tests/circuitikzExportDocsContract.test.ts @@ -41,12 +41,15 @@ describe('circuitikz export docs contract', () => { expect(doc).toContain('cmos-inverter-v1'); expect(doc).toContain('cmos-nand2'); expect(doc).toContain('cmos-nand2-v1'); + expect(doc).toContain('cmos-nor2'); + expect(doc).toContain('cmos-nor2-v1'); expect(doc).toContain('layoutHints.inputSide'); expect(doc).toContain('layoutHints.outputSide'); expect(doc).toContain('port placement'); expect(doc).toContain('docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json'); expect(doc).toContain('docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json'); expect(doc).toContain('docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json'); + expect(doc).toContain('docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json'); expect(doc).toContain('UTF-8'); expect(doc).toContain('BOM'); expect(doc).toContain('--compile-log'); @@ -131,6 +134,8 @@ describe('circuitikz export docs contract', () => { expect(doc).toContain('CircuitSpec -> circuitikz'); expect(doc).toContain('cmos-nand2'); expect(doc).toContain('cmos-nand2-v1'); + expect(doc).toContain('cmos-nor2'); + expect(doc).toContain('cmos-nor2-v1'); expect(doc).toContain('layoutHints.inputSide'); expect(doc).toContain('layoutHints.outputSide'); expect(doc).toContain('port placement'); @@ -217,6 +222,8 @@ describe('circuitikz export docs contract', () => { expect(doc).toContain('circuitikzRepairBrief.ts'); expect(doc).toContain('cmos-nand2'); expect(doc).toContain('cmos-nand2-v1'); + expect(doc).toContain('cmos-nor2'); + expect(doc).toContain('cmos-nor2-v1'); expect(doc).toContain('layoutHints.inputSide'); expect(doc).toContain('layoutHints.outputSide'); expect(doc).toContain('port placement'); diff --git a/src/tests/circuitikzExporter.test.ts b/src/tests/circuitikzExporter.test.ts index 9596b2d6..fa0579b6 100644 --- a/src/tests/circuitikzExporter.test.ts +++ b/src/tests/circuitikzExporter.test.ts @@ -104,6 +104,43 @@ function createCmosNand2Spec(): CircuitSpec { }; } +function createCmosNor2Spec(): CircuitSpec { + return { + circuitKind: 'cmos-nor2' as CircuitSpec['circuitKind'], + title: 'Two Input CMOS NOR Gate', + goldenReferenceId: 'cmos-nor2-v1', + style: { + package: 'circuitikz', + voltageConvention: 'american voltages' + }, + nets: ['VDD', 'GND', 'va', 'vb', 'vout', 'pull_up_mid'], + components: [ + { id: 'MPA', type: 'pmos', label: '$M_{PA}$', terminals: { S: 'VDD', G: 'va', D: 'pull_up_mid' } }, + { id: 'MPB', type: 'pmos', label: '$M_{PB}$', terminals: { S: 'pull_up_mid', G: 'vb', D: 'vout' } }, + { id: 'MNA', type: 'nmos', label: '$M_{NA}$', terminals: { D: 'vout', G: 'va', S: 'GND' } }, + { id: 'MNB', type: 'nmos', label: '$M_{NB}$', terminals: { D: 'vout', G: 'vb', S: 'GND' } } + ], + connections: [ + { from: 'VDD', to: 'MPA.S' }, + { from: 'MPA.D', to: 'MPB.S' }, + { from: 'MPB.D', to: 'vout' }, + { from: 'MNA.D', to: 'vout' }, + { from: 'MNB.D', to: 'vout' }, + { from: 'MNA.S', to: 'GND' }, + { from: 'MNB.S', to: 'GND' }, + { from: 'va', to: 'MPA.G' }, + { from: 'va', to: 'MNA.G' }, + { from: 'vb', to: 'MPB.G' }, + { from: 'vb', to: 'MNB.G' } + ], + layoutHints: { + inputSide: 'left', + outputSide: 'right', + routingStyle: 'orthogonal' + } + }; +} + describe('circuitikz exporter', () => { test('validates and exports the common-source golden reference template deterministically', () => { const spec = createCommonSourceSpec(); @@ -209,6 +246,52 @@ describe('circuitikz exporter', () => { expect(() => exportCircuitSpecToCircuitikz(spec)).toThrow(/series pull-down stack/); }); + test('validates and exports a constrained two-input CMOS NOR template deterministically', () => { + const spec = createCmosNor2Spec(); + const output = exportCircuitSpecToCircuitikz(spec); + + expect(assertValidCircuitSpec(spec)).toBe(spec); + expect(output).toContain('\\begin{circuitikz}[american voltages]'); + expect(output).toContain('node[pmos, anchor=S] (MPA) {$M_{PA}$}'); + expect(output).toContain('node[pmos, anchor=S] (MPB) {$M_{PB}$}'); + expect(output).toContain('node[nmos, anchor=D] (MNA) {$M_{NA}$}'); + expect(output).toContain('node[nmos, anchor=D] (MNB) {$M_{NB}$}'); + expect(output).toContain('(MPA.D) to [short] (3,3.75)'); + expect(output).toContain('(MPB.D) to [short] (3,2.75)'); + expect(output).toContain('(3,2.75) to [short, *-o] (5,2.75) node[right]{$v_{out}$};'); + expect(output).toContain('node[left]{$v_A$}'); + expect(output).toContain('node[left]{$v_B$};'); + }); + + test('projects CMOS NOR layout hints into deterministic input and output ports', () => { + const reference = createCmosNor2Spec(); + const spec = createCmosNor2Spec(); + spec.layoutHints = { + inputSide: 'right', + outputSide: 'left', + routingStyle: 'orthogonal' + }; + + const output = exportCircuitSpecToCircuitikz(spec); + + expect(createCircuitTopologySignature(spec)).toBe(createCircuitTopologySignature(reference)); + expect(output).toContain('(3,2.75) to [short, *-o] (0.8,2.75) node[left]{$v_{out}$};'); + expect(output).toContain('to [short, -o] (5.2,4.35)'); + expect(output).toContain('node[right]{$v_A$}'); + expect(output).toContain('to [short, -o] (5.2,1.95)'); + expect(output).toContain('node[right]{$v_B$};'); + }); + + test('rejects CMOS NOR specs that do not keep the PMOS pull-up stack in series', () => { + const spec = createCmosNor2Spec(); + spec.connections = spec.connections.filter(connection => connection.from !== 'MPA.D' || connection.to !== 'MPB.S'); + + expect(validateCircuitSpec(spec).errors).toContain( + 'CMOS NOR requires MPA.D to connect to MPB.S for the series pull-up stack.' + ); + expect(() => exportCircuitSpecToCircuitikz(spec)).toThrow(/series pull-up stack/); + }); + test('projects common-source layout hints into deterministic input and output ports', () => { const reference = createCommonSourceSpec(); const spec = createCommonSourceSpec(); diff --git a/src/tests/circuitikzSmokeFixturesCli.test.ts b/src/tests/circuitikzSmokeFixturesCli.test.ts index 5bff8394..2938208d 100644 --- a/src/tests/circuitikzSmokeFixturesCli.test.ts +++ b/src/tests/circuitikzSmokeFixturesCli.test.ts @@ -22,6 +22,7 @@ describe('circuitikz smoke fixtures CLI', () => { expect(fixtureNames).toEqual([ 'cmos-inverter-v1.json', 'cmos-nand2-v1.json', + 'cmos-nor2-v1.json', 'common-source-nmos-v1.json' ]); @@ -58,7 +59,7 @@ describe('circuitikz smoke fixtures CLI', () => { const report = JSON.parse(result.stdout); expect(report).toEqual(expect.objectContaining({ ok: false, - fixtureCount: 3, + fixtureCount: 4, reportOutputPath: reportPath, rendererAvailability: expect.objectContaining({ ok: false, @@ -137,12 +138,13 @@ fs.writeFileSync( const report = JSON.parse(stdout); expect(report).toEqual(expect.objectContaining({ ok: true, - fixtureCount: 3, + fixtureCount: 4, outputDirectory })); expect(report.fixtures.map((fixture: any) => fixture.name).sort()).toEqual([ 'cmos-inverter-v1', 'cmos-nand2-v1', + 'cmos-nor2-v1', 'common-source-nmos-v1' ]); @@ -162,7 +164,7 @@ fs.writeFileSync( .trim() .split(/\r?\n/) .map(line => JSON.parse(line)); - expect(invocations).toHaveLength(3); + expect(invocations).toHaveLength(4); for (const invocation of invocations) { expect(invocation[0]).toBe(compilerPath); expect(invocation).toContain('--tex'); diff --git a/src/tests/websiteDocsContract.test.ts b/src/tests/websiteDocsContract.test.ts index 0748f992..620b2546 100644 --- a/src/tests/websiteDocsContract.test.ts +++ b/src/tests/websiteDocsContract.test.ts @@ -61,6 +61,8 @@ describe('website documentation contract', () => { expect(content).toContain('cmos-inverter-v1'); expect(content).toContain('cmos-nand2'); expect(content).toContain('cmos-nand2-v1'); + expect(content).toContain('cmos-nor2'); + expect(content).toContain('cmos-nor2-v1'); expect(content).toContain('layoutHints.inputSide'); expect(content).toContain('layoutHints.outputSide'); expect(content).toContain('port placement'); @@ -139,6 +141,7 @@ describe('website documentation contract', () => { expect(content).toContain('docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json'); expect(content).toContain('docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json'); expect(content).toContain('docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json'); + expect(content).toContain('docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json'); } }); diff --git a/website/docs/features/diagrams.mdx b/website/docs/features/diagrams.mdx index d1ab4368..7ea31817 100644 --- a/website/docs/features/diagrams.mdx +++ b/website/docs/features/diagrams.mdx @@ -213,13 +213,14 @@ Notemd now includes the first constrained repository prototype for this directio npm run diagram:export-circuitikz -- --input cmos-inverter.json --output cmos-inverter.tex ``` -The prototype adds a separate `CircuitSpec` boundary and deterministic exporter for three golden-reference families: +The prototype adds a separate `CircuitSpec` boundary and deterministic exporter for four golden-reference families: | Circuit kind | Golden reference | Current guarantee | |--------------|------------------|-------------------| | `common-source-amplifier` | `common-source-nmos-v1` | validates `VDD -> R_D -> M1.D`, `vin -> M1.G`, `M1.S -> GND`, and `M1.D -> vout` before writing LaTeX | | `cmos-inverter` | `cmos-inverter-v1` | validates PMOS-over-NMOS topology, shared gate input, shared drain output, `VDD -> MP.S`, and `MN.S -> GND` before writing LaTeX | | `cmos-nand2` | `cmos-nand2-v1` | validates parallel PMOS pull-up, series NMOS pull-down, dual inputs `va` / `vb`, and `vout` before writing LaTeX | +| `cmos-nor2` | `cmos-nor2-v1` | validates series PMOS pull-up, parallel NMOS pull-down, dual inputs `va` / `vb`, and `vout` before writing LaTeX | This is not a general TikZ generator yet. It does not compile LaTeX, call TikZJax, inspect screenshots, or run automated image-feedback repair. Those remain later gates. @@ -295,7 +296,7 @@ For release or maintainer evidence, run every supported golden family through th npm run diagram:smoke-circuitikz -- --output-dir docs/export/circuitikz-smoke --compile-executable pdflatex --compile-arg -interaction=nonstopmode --compile-arg -halt-on-error --compile-arg -output-directory={outputDir} --compile-arg {tex} --expected-artifact {outputDir}/{jobName}.pdf ``` -The runner uses `docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json`, `docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json`, and `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json`, calls the same shell-free exporter path for each fixture, and returns an aggregate JSON report with per-fixture `compileExecution` and `compileDiagnostics`. It is still a maintainer command, not a plugin runtime dependency. +The runner uses `docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json`, `docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json`, `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json`, and `docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json`, calls the same shell-free exporter path for each fixture, and returns an aggregate JSON report with per-fixture `compileExecution` and `compileDiagnostics`. It is still a maintainer command, not a plugin runtime dependency. When a maintainer machine has no renderer configured yet, run the same fixture command without `--compile-executable` and persist the environment gate explicitly: @@ -344,7 +345,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, CMOS inverter, and `cmos-nand2` / `cmos-nand2-v1` golden templates, projects `layoutHints.inputSide` and `layoutHints.outputSide` into deterministic input/output port placement without changing topology, rejects repair topology drift through `--topology-reference`, emits topology-preserving repair briefs through `--repair-brief-output` and schema `notemd.circuitikz.repair-brief.v1`, includes structured `repairPrompt` handoff content with `diagnosticFocus`, `acceptanceCriteria`, and role `topology-preserving-circuitikz-repair`, validates repair candidates through `--repair-brief`, returns `repairAcceptance` gate evidence through schema `notemd.circuitikz.repair-acceptance.v1` with `readyForVisualAcceptance` and `remainingChecks`, persists that evidence through `--repair-acceptance-output`, parses compile logs, can run explicit local renderers plus `--expected-artifact`, SVG `--expected-svg-text`, accessibility metadata checks through `aria-label`, `<title>`, and `<desc>`, hidden/transparent SVG element exclusion, `render-svg-text-path-only` / `pathOnlyGlyphUseCount` classification for path-only labels, path-only glyph placement checks for `<use href="#...">`, path-only glyph overlap diagnostics through `render-svg-path-glyph-overlap`, close-path current-point handling for `Z/z`, 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, `text-anchor`-aware positioned text 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, grayscale/RGB tRNS transparent samples, and format-specific `render-png-unsupported` guidance for Adam7 interlaced PNGs and indexed bit-depth failures, 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`, records missing renderer configuration through `rendererAvailability.status: "missing-configuration"` and `compile-executable-invalid`, 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 for path-only visual text, precise pixel-level overlap checks, broader SVG path coverage where needed, automatic renderer installation/discovery only if it can remain optional, and automated topology-preserving repair execution | +| circuitikz | `CircuitSpec -> circuitikz` prototype exports common-source, CMOS inverter, `cmos-nand2` / `cmos-nand2-v1`, and `cmos-nor2` / `cmos-nor2-v1` golden templates, projects `layoutHints.inputSide` and `layoutHints.outputSide` into deterministic input/output port placement without changing topology, rejects repair topology drift through `--topology-reference`, emits topology-preserving repair briefs through `--repair-brief-output` and schema `notemd.circuitikz.repair-brief.v1`, includes structured `repairPrompt` handoff content with `diagnosticFocus`, `acceptanceCriteria`, and role `topology-preserving-circuitikz-repair`, validates repair candidates through `--repair-brief`, returns `repairAcceptance` gate evidence through schema `notemd.circuitikz.repair-acceptance.v1` with `readyForVisualAcceptance` and `remainingChecks`, persists that evidence through `--repair-acceptance-output`, parses compile logs, can run explicit local renderers plus `--expected-artifact`, SVG `--expected-svg-text`, accessibility metadata checks through `aria-label`, `<title>`, and `<desc>`, hidden/transparent SVG element exclusion, `render-svg-text-path-only` / `pathOnlyGlyphUseCount` classification for path-only labels, path-only glyph placement checks for `<use href="#...">`, path-only glyph overlap diagnostics through `render-svg-path-glyph-overlap`, close-path current-point handling for `Z/z`, 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, `text-anchor`-aware positioned text 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, grayscale/RGB tRNS transparent samples, and format-specific `render-png-unsupported` guidance for Adam7 interlaced PNGs and indexed bit-depth failures, 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`, records missing renderer configuration through `rendererAvailability.status: "missing-configuration"` and `compile-executable-invalid`, 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 for path-only visual text, precise pixel-level overlap checks, broader SVG path coverage where needed, automatic renderer installation/discovery only if it can remain optional, 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 4f677bca..0967c792 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 @@ -213,13 +213,14 @@ Notemd 现在已经包含这个方向的第一个受约束仓库内原型。它 npm run diagram:export-circuitikz -- --input cmos-inverter.json --output cmos-inverter.tex ``` -这个原型新增了独立的 `CircuitSpec` 边界,并为三个 golden-reference families 提供确定性 exporter: +这个原型新增了独立的 `CircuitSpec` 边界,并为四个 golden-reference families 提供确定性 exporter: | Circuit kind | Golden reference | Current guarantee | |--------------|------------------|-------------------| | `common-source-amplifier` | `common-source-nmos-v1` | 写出 LaTeX 前验证 `VDD -> R_D -> M1.D`、`vin -> M1.G`、`M1.S -> GND` 和 `M1.D -> vout` | | `cmos-inverter` | `cmos-inverter-v1` | 写出 LaTeX 前验证 PMOS-over-NMOS 拓扑、shared gate input、shared drain output、`VDD -> MP.S` 和 `MN.S -> GND` | | `cmos-nand2` | `cmos-nand2-v1` | 写出 LaTeX 前验证并联 PMOS pull-up、串联 NMOS pull-down、双输入 `va` / `vb` 和 `vout` | +| `cmos-nor2` | `cmos-nor2-v1` | 写出 LaTeX 前验证串联 PMOS pull-up、并联 NMOS pull-down、双输入 `va` / `vb` 和 `vout` | 这还不是通用 TikZ 生成器。它不会编译 LaTeX、调用 TikZJax、检查截图或运行自动化图像反馈修复;这些仍是后续 gate。 @@ -295,7 +296,7 @@ npm run diagram:export-circuitikz -- --input repaired-cmos-inverter.json --repai npm run diagram:smoke-circuitikz -- --output-dir docs/export/circuitikz-smoke --compile-executable pdflatex --compile-arg -interaction=nonstopmode --compile-arg -halt-on-error --compile-arg -output-directory={outputDir} --compile-arg {tex} --expected-artifact {outputDir}/{jobName}.pdf ``` -runner 使用 `docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json`、`docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json` 和 `docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json`,对每个 fixture 调用同一条 shell-free exporter path,并返回包含每个 fixture 的 `compileExecution` 与 `compileDiagnostics` 的聚合 JSON report。它仍然是 maintainer command,不是插件 runtime 依赖。 +runner 使用 `docs/maintainer/fixtures/circuitikz/common-source-nmos-v1.json`、`docs/maintainer/fixtures/circuitikz/cmos-inverter-v1.json`、`docs/maintainer/fixtures/circuitikz/cmos-nand2-v1.json` 和 `docs/maintainer/fixtures/circuitikz/cmos-nor2-v1.json`,对每个 fixture 调用同一条 shell-free exporter path,并返回包含每个 fixture 的 `compileExecution` 与 `compileDiagnostics` 的聚合 JSON report。它仍然是 maintainer command,不是插件 runtime 依赖。 如果维护者机器尚未配置 renderer,可以不传 `--compile-executable`,用同一个 fixture command 把环境 gate 明确持久化: @@ -344,7 +345,7 @@ npm run diagram:smoke-circuitikz -- --output-dir docs/export/circuitikz-smoke -- | 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 和 `cmos-nand2` / `cmos-nand2-v1` golden templates、把 `layoutHints.inputSide` 与 `layoutHints.outputSide` 投影为不改变拓扑的确定性 input/output port placement、通过 `--topology-reference` 拒绝 repair topology drift、通过 `--repair-brief-output` 和 schema `notemd.circuitikz.repair-brief.v1` 写出 topology-preserving repair briefs、内含带 `diagnosticFocus`、`acceptanceCriteria` 与 role `topology-preserving-circuitikz-repair` 的结构化 `repairPrompt` handoff、通过 `--repair-brief` 校验 repair candidates、通过 schema `notemd.circuitikz.repair-acceptance.v1` 返回带 `readyForVisualAcceptance` 与 `remainingChecks` 的 `repairAcceptance` gate evidence、通过 `--repair-acceptance-output` 持久化这份 evidence、解析 compile logs,并在不经过 shell parsing 的情况下运行显式本地 renderer、`--expected-artifact`、SVG `--expected-svg-text`、通过 `aria-label`、`<title>` 与 `<desc>` 执行 accessibility metadata checks、hidden/transparent SVG element exclusion、用于 path-only labels 的 `render-svg-text-path-only` / `pathOnlyGlyphUseCount` 分类、针对 `<use href="#...">` 的 path-only glyph placement checks、通过 `render-svg-path-glyph-overlap` 执行的 path-only glyph overlap diagnostics、针对 `Z/z` 的 close-path current-point handling、针对 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、`text-anchor`-aware positioned text 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,以及针对 Adam7 interlaced PNG 与 indexed bit-depth failures 的 format-specific `render-png-unsupported` guidance;现在还通过 `npm run diagram:smoke-circuitikz` 提供聚合 maintainer smoke fixtures,通过 `rendererAvailability.status: "missing-configuration"` 与 `compile-executable-invalid` 记录缺失 renderer 配置,并通过 `RenderArtifact.diagnostics` 和 preview modal 具备通用 preview diagnostics、diagnostic summary counts、diagnostics-aware history entries 与 source-only fallback | 增加 path-only visual text 的 OCR-level label recognition、精确 pixel-level overlap checks、按需补齐更完整 SVG path coverage;只有能保持可选时才考虑 automatic renderer installation/discovery;继续推进自动化 topology-preserving repair execution | +| circuitikz | `CircuitSpec -> circuitikz` 原型已经可通过 `npm run diagram:export-circuitikz` 导出 common-source、CMOS inverter、`cmos-nand2` / `cmos-nand2-v1` 和 `cmos-nor2` / `cmos-nor2-v1` golden templates、把 `layoutHints.inputSide` 与 `layoutHints.outputSide` 投影为不改变拓扑的确定性 input/output port placement、通过 `--topology-reference` 拒绝 repair topology drift、通过 `--repair-brief-output` 和 schema `notemd.circuitikz.repair-brief.v1` 写出 topology-preserving repair briefs、内含带 `diagnosticFocus`、`acceptanceCriteria` 与 role `topology-preserving-circuitikz-repair` 的结构化 `repairPrompt` handoff、通过 `--repair-brief` 校验 repair candidates、通过 schema `notemd.circuitikz.repair-acceptance.v1` 返回带 `readyForVisualAcceptance` 与 `remainingChecks` 的 `repairAcceptance` gate evidence、通过 `--repair-acceptance-output` 持久化这份 evidence、解析 compile logs,并在不经过 shell parsing 的情况下运行显式本地 renderer、`--expected-artifact`、SVG `--expected-svg-text`、通过 `aria-label`、`<title>` 与 `<desc>` 执行 accessibility metadata checks、hidden/transparent SVG element exclusion、用于 path-only labels 的 `render-svg-text-path-only` / `pathOnlyGlyphUseCount` 分类、针对 `<use href="#...">` 的 path-only glyph placement checks、通过 `render-svg-path-glyph-overlap` 执行的 path-only glyph overlap diagnostics、针对 `Z/z` 的 close-path current-point handling、针对 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、`text-anchor`-aware positioned text 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,以及针对 Adam7 interlaced PNG 与 indexed bit-depth failures 的 format-specific `render-png-unsupported` guidance;现在还通过 `npm run diagram:smoke-circuitikz` 提供聚合 maintainer smoke fixtures,通过 `rendererAvailability.status: "missing-configuration"` 与 `compile-executable-invalid` 记录缺失 renderer 配置,并通过 `RenderArtifact.diagnostics` 和 preview modal 具备通用 preview diagnostics、diagnostic summary counts、diagnostics-aware history entries 与 source-only fallback | 增加 path-only visual text 的 OCR-level label recognition、精确 pixel-level overlap checks、按需补齐更完整 SVG path coverage;只有能保持可选时才考虑 automatic renderer installation/discovery;继续推进自动化 topology-preserving repair execution | | TikZJax integration | Obsidian-side display 的候选 render host | 保持可选,不把 TikZJax 变成硬 runtime 依赖 | ## Configuration diff --git a/website/scripts/audit-build.cjs b/website/scripts/audit-build.cjs index 37d9336b..704dd6ad 100644 --- a/website/scripts/audit-build.cjs +++ b/website/scripts/audit-build.cjs @@ -359,6 +359,7 @@ function auditDiagramDocs() { assertContains(englishDiagrams, 'CircuitSpec', 'English diagrams doc'); assertContains(englishDiagrams, 'cmos-inverter-v1', 'English diagrams doc'); assertContains(englishDiagrams, 'cmos-nand2-v1', 'English diagrams doc'); + assertContains(englishDiagrams, 'cmos-nor2-v1', 'English diagrams doc'); assertContains(englishDiagrams, '--compile-log', 'English diagrams doc'); assertContains(englishDiagrams, '--diagnostics-output', 'English diagrams doc'); assertContains(englishDiagrams, '--compile-executable', 'English diagrams doc'); @@ -382,6 +383,7 @@ function auditDiagramDocs() { assertContains(zhCnDiagrams, 'CircuitSpec', 'zh-CN diagrams doc'); assertContains(zhCnDiagrams, 'cmos-inverter-v1', 'zh-CN diagrams doc'); assertContains(zhCnDiagrams, 'cmos-nand2-v1', 'zh-CN diagrams doc'); + assertContains(zhCnDiagrams, 'cmos-nor2-v1', 'zh-CN diagrams doc'); assertContains(zhCnDiagrams, '--compile-log', 'zh-CN diagrams doc'); assertContains(zhCnDiagrams, '--diagnostics-output', 'zh-CN diagrams doc'); assertContains(zhCnDiagrams, '--compile-executable', 'zh-CN diagrams doc');