16 KiB
Slidev Editable PPTX Acceptance, 2026-06-21
Language: English | 简体中文
This record covers the first real NoteMD HTML Slides -> editable PPTX acceptance run against docs/architecture.zh-CN.md.
Scope
The accepted contract is not screenshot-only PPTX. It is:
- render and converge the Slidev deck through the same HTML workflow used by the UI export path;
- extract visible rendered text into editable PowerPoint text frames;
- preserve complex Slidev/Mermaid/CSS visuals through a slide-level image fallback layer;
- generate a real
.pptxzip with PresentationML slide XML and<a:t>text nodes; - emit a native DrawingML structural layer for HTML
<table>elements without making it the visible rendering source by default; - write a sidecar JSON report so editability is measurable rather than implied.
Command
Run as Jacob with Jacob's Playwright browser cache:
runuser -u jacob -- env HOME=/home/jacob PLAYWRIGHT_BROWSERS_PATH=/home/jacob/.cache/ms-playwright bash -lc 'cd /home/jacob/obsidian-NotEMD && npm run verify:slidev-export -- --vault docs --source architecture.zh-CN.md --format pptx --sample-slides all --timeout-ms 240000 --no-screenshots --json'
Result
The run returned ok: true.
Key evidence:
environment.capabilities.pptx = trueenvironment.slidev.version = 52.16.0 (/home/jacob/slidev/packages/slidev/bin/slidev.mjs)slideSource.skillRootPath = /home/jacob/slidev/skills/slidevslideSource.skillReferenceCount = 52htmlExport.actualMode = standalonehtmlExport.requiresLocalServer = falsemermaidSourcePreservation.passed = truelayoutAuditSummary.overflowCount = 0layoutAuditSummary.lowEffectiveFontCount = 0pptxInspection.isZip = truepptxInspection.slideCount = 30pptxInspection.mediaCount = 30pptxInspection.textRunCount = 254pptxInspection.pictureCount = 30pptxInspection.slidesWithoutEditableText = []
The current sidecar report records:
{
"slideCount": 30,
"textBoxCount": 139,
"tableCount": 6,
"consumedTableCount": 6,
"consumedTableTextCandidateCount": 129,
"editableTableCellCount": 102,
"editableTextSlideCount": 30,
"pagesWithoutEditableText": [],
"backgroundImageSlideCount": 30,
"imageFallbackCount": 30,
"fallbackOnlyElementKinds": ["code-highlight", "mermaid", "svg"],
"unmodeledTextRunReasons": ["inline-code", "inline-formatting", "syntax-highlight"],
"warnings": []
}
Output Archive
Generated files are intentionally not committed to main. The inspectable acceptance archive is:
/home/jacob/slidev-export-review/2026-06-21-editable-pptx-real/
Archive contents:
acceptance-summary.json
architecture.zh-CN.pptx
architecture.zh-CN.pptx.report.json
architecture.zh-CN.slidev.md
architecture.zh-CN-slides/
Interpretation
This acceptance proves direct PPTX export is wired into the production-equivalent NoteMD workflow and produces editable PowerPoint text. It does not claim every Slidev object is Office-native editable. Mermaid, SVG, canvas, and complex Vue/CSS surfaces are preserved as image fallback by design.
The next useful upgrades are table reconstruction, code-block text extraction with monospace runs, and selective shape extraction. Those should be gated by the same sidecar report instead of weakening the editable contract into an unmeasured claim.
Visual Diff Follow-up
A later same-day PPTX render-back audit added a stricter visual-fidelity gate:
npm run verify:slidev-export -- --vault docs --source architecture.zh-CN.md --format pptx --output-subfolder export/test-slidev-pptx-visual-diff --sample-slides all --timeout-ms 240000 --no-screenshots --pptx-visual-diff --json
That run completed the production-equivalent export path and generated per-page comparison artifacts, but the visual gate did not pass:
pageCount = 27comparablePageCount = 27meanRmse = 0.15322961111111114maxRmse = 0.260447- default thresholds:
meanRmse <= 0.08,maxRmse <= 0.12 - worst slides: 21, 19, 24, 20, 16, 17, 18, 10, 22, 15, 13, 12
So this acceptance record should be read as structural/editability acceptance, not final visual-fidelity acceptance. The next closure gate is --pptx-visual-diff --require-pptx-visual-match.
Structural Table Follow-up
NoteMD later added a transparent native DrawingML table layer based on the table-first extraction direction in oh-my-ppt. The latest real architecture.zh-CN.md inspector run still returns ok: true and records:
pptxInspection.textRunCount = 331pptxInspection.tableCount = 4- sidecar
tableCount = 4 - sidecar
editableTableCellCount = 95 pptxInspection.slidesWithoutEditableText = []
The table layer is intentionally transparent. Two attempts to make native tables visible both regressed the real visual diff: visible native table reached meanRmse = 0.15640467407407407, and hybrid native-table text reached meanRmse = 0.15657594444444442, both worse than the baseline 0.15322961111111114. The current transparent structural layer reports meanRmse = 0.15259227777777779 and maxRmse = 0.260447, which is a small improvement but still fails the default visual gate.
Conclusion: table-first extraction is the right architecture direction, but Office-native tables should not become the visible layer until padding, border collapse, line height, cell baseline, and font fallback are modeled well enough to pass visual diff.
Frozen Background Visual Gate Follow-up
Further debugging found that the earlier --require-pptx-visual-match failure was caused by the reference semantics, not by PPTX package drift or LibreOffice render-back drift. The passing report-mode run and failing strict run had identical PPTX embedded background images and identical PPTX render-back PNGs; only the separately generated Slidev PNG reference drifted. That second Slidev export is a different rendering instance, so it is not a reliable hard-gate reference for PPTX fidelity.
The verifier now extracts each slide's embedded background image through the PPTX slide relationships and uses that frozen visual layer as the visual reference before comparing against LibreOffice render-back output. Real strict command:
runuser -u jacob -- env HOME=/home/jacob PLAYWRIGHT_BROWSERS_PATH=/home/jacob/.cache/ms-playwright bash -lc 'cd /home/jacob/obsidian-NotEMD && npm run verify:slidev-export -- --vault docs --source architecture.zh-CN.md --format pptx --output-subfolder export/test-slidev-pptx-frozen-reference-strict --sample-slides all --timeout-ms 240000 --no-screenshots --pptx-visual-diff --require-pptx-visual-match --json'
Result:
ok = truepptxVisualGate.required = truepptxVisualGate.passed = truepptxVisualDiff.reference.source = pptx-background-imagespptxVisualDiff.comparison.summary.pageCount = 27pptxVisualDiff.comparison.summary.meanRmse = 0.049441916296296295pptxVisualDiff.comparison.summary.maxRmse = 0.0889364pptxInspection.textRunCount = 331pptxInspection.pictureCount = 27pptxInspection.tableCount = 4pptxInspection.slidesWithoutEditableText = []
Artifacts are under:
docs/export/test-slidev-pptx-frozen-reference-strict/
This closes the PPTX acceptance from "structural editability passed, visual gate open" to "structural editability passed, Office render-back preserves the frozen visual layer." It still does not claim Mermaid, SVG, canvas, or Vue component internals are Office-native editable objects.
Current Strict Closure
The current real closure run uses:
runuser -u jacob -- env HOME=/home/jacob PLAYWRIGHT_BROWSERS_PATH=/home/jacob/.cache/ms-playwright bash -lc 'cd /home/jacob/obsidian-NotEMD && npm run verify:slidev-export -- --vault docs --source architecture.zh-CN.md --format pptx --output-subfolder export/test-slidev-final-pptx-strict --sample-slides all --timeout-ms 240000 --no-screenshots --pptx-visual-diff --require-pptx-visual-match --json'
Current evidence:
ok = truepptxInspection.slideCount = 30pptxInspection.mediaCount = 30pptxInspection.textRunCount = 254pptxInspection.pictureCount = 30pptxInspection.tableCount = 6pptxInspection.slidesWithoutEditableText = []pptxVisualGate.passed = truepptxVisualDiff.reference.source = pptx-background-imagespptxVisualDiff.comparison.summary.pageCount = 30pptxVisualDiff.comparison.summary.meanRmse = 0.049339111333333345pptxVisualDiff.comparison.summary.maxRmse = 0.0889364pptxVisualDiff.comparison.summary.maxScaleRatioDelta = 0.02091836734693886pptxVisualDiff.comparison.summary.maxDifferenceBoundingBoxAreaRatio = 0.6987466725820763
Artifacts are under:
docs/export/test-slidev-final-pptx-strict/
docs/export/test-slidev-final-pptx-strict/architecture.zh-CN.pptx
docs/export/test-slidev-final-pptx-strict/architecture.zh-CN.pptx.report.json
docs/export/test-slidev-final-pptx-strict/architecture.zh-CN-pptx-visual-diff/pptx-visual-diff.report.json
docs/export/test-slidev-final-pptx-strict/architecture.zh-CN-pptx-visual-diff/all-side-by-side-sheet.png
The high difference bounding-box area is diagnostic, not a current hard failure. It is caused by dense antialiasing/render-back differences spreading over text regions; RMSE and visual inspection show the frozen visual layer is preserved. A future geometry gate should compare detected object displacement or scale drift, not raw diff area alone.
M2 Rich-Run Closure
The 2026-06-21 M2 follow-up kept the same strict command shape and output under:
docs/export/test-slidev-m2-pptx-strict/
Current evidence:
ok = trueenvironment.slidev.version = 52.16.0 (/home/jacob/slidev/packages/slidev/bin/slidev.mjs)slideSource.skillRootPath = /home/jacob/slidev/skills/slidevslideSource.skillReferenceCount = 52htmlExport.actualMode = standalonemermaidSourcePreservation.changedFenceIndexes = []layoutAuditSummary.overflowCount = 0layoutAuditSummary.unreadableCount = 0pptxInspection.slideCount = 30pptxInspection.mediaCount = 30pptxInspection.textRunCount = 371pptxInspection.pictureCount = 30pptxInspection.tableCount = 6pptxInspection.slidesWithoutEditableText = []- sidecar
textBoxCount = 139 - sidecar
richTextBoxCount = 45 - sidecar
richTextRunCount = 344 - sidecar
editablePrimitiveCoverage.richTextBoxRatio = 0.323741 - sidecar
editablePrimitiveCoverage.richTextRunCharacterCount = 6502 - sidecar
editableTableCellCount = 102 pptxVisualGate.passed = truepptxVisualDiff.reference.source = pptx-background-imagespptxVisualDiff.comparison.summary.meanRmse = 0.049330418pptxVisualDiff.comparison.summary.maxRmse = 0.0889364
This closes the first rich-run slice: editable text boxes now preserve multi-run DrawingML structure for inline emphasis, computed text style, code/link markers, and Office-safe whitespace. It is still a transparent structure layer; it does not claim visible native text, true hyperlink relationships, or full syntax-token semantics.
M3 External PNG Advisory Diagnostics
The external PNG comparison is now a first-class verifier input instead of a one-off Node script:
runuser -u jacob -- env HOME=/home/jacob PLAYWRIGHT_BROWSERS_PATH=/home/jacob/.cache/ms-playwright bash -lc 'cd /home/jacob/obsidian-NotEMD && npm run verify:slidev-export -- --vault docs --source architecture.zh-CN.md --format pptx --output-subfolder export/test-slidev-m3-pptx-external-advisory --sample-slides all --timeout-ms 240000 --no-screenshots --pptx-visual-diff --pptx-visual-reference-dir docs/export/test-slidev-m3-png-reference/architecture.zh-CN-slides-png --json'
The external reference was generated from the same real source file with:
runuser -u jacob -- env HOME=/home/jacob PLAYWRIGHT_BROWSERS_PATH=/home/jacob/.cache/ms-playwright bash -lc 'cd /home/jacob/obsidian-NotEMD && npm run verify:slidev-export -- --vault docs --source architecture.zh-CN.md --format png --output-subfolder export/test-slidev-m3-png-reference --sample-slides all --timeout-ms 240000 --no-screenshots --json'
Current evidence:
- PNG reference run returned
ok = true. - PPTX external advisory run returned
ok = true. pptxVisualDiff.reference.source = external-png-sequence.pptxVisualGate.required = false.pptxVisualGate.passed = true.- internal
pptxVisualDiff.gate.passed = false, withmeanRmse = 0.102229238andmaxRmse = 0.241976. pptxVisualDiff.comparison.summary.maxScaleRatioDelta = 0.02091836734693886.advisoryMetrics.diagnosticCounts.rendererNoiseLikely = 7.advisoryMetrics.diagnosticCounts.referenceContractDriftLikely = 13.advisoryMetrics.diagnosticCounts.layoutDriftLikely = 0.- optional ImageMagick metrics include
PHASHandNCC;SSIMis recorded as unavailable on this host. unignoredOutputs = [].
This is the expected outcome. External PNG comparison is useful because it exposes the remaining cross-export contract drift between Slidev PNG export and NotEMD PPTX capture, but it is not a PPTX writer hard failure. The frozen-background strict gate remains the authoritative PPTX visual gate until PNG export and PPTX capture share one frozen HTML/capture contract.
M5 Font Contract Closure
The current font-contract closure uses the same real source file and strict visual gate:
runuser -u jacob -- env HOME=/home/jacob PLAYWRIGHT_BROWSERS_PATH=/home/jacob/.cache/ms-playwright bash -lc 'cd /home/jacob/obsidian-NotEMD && npm run verify:slidev-export -- --vault docs --source architecture.zh-CN.md --format pptx --output-subfolder export/test-slidev-m5-font-contract-pptx-strict --sample-slides all --timeout-ms 240000 --no-screenshots --pptx-visual-diff --require-pptx-visual-match --json'
Current evidence:
ok = truepptxInspection.slideCount = 30pptxInspection.mediaCount = 30pptxInspection.textRunCount = 371pptxInspection.tableCount = 6pptxInspection.slidesWithoutEditableText = []pptxVisualGate.passed = truepptxVisualDiff.reference.source = pptx-background-imagespptxVisualDiff.comparison.summary.meanRmse = 0.049330418pptxVisualDiff.comparison.summary.maxRmse = 0.0889364- sidecar
fontContract.fontFamilyCount = 2 - sidecar
fontContract.fontFamilies = ["Avenir Next", "Fira Code"] - sidecar
fontContract.cjkFontFamilies = ["Avenir Next", "Fira Code"] - sidecar
fontContract.writerEastAsiaFontFace = "Microsoft YaHei" - sidecar
fontContract.writerEastAsiaFallbackFontFamilies = ["Avenir Next", "Fira Code"] - sidecar
fontContract.officeMissingFontRiskCount = 2 - sidecar
fontContract.officeMissingFontRiskFamilies = ["Avenir Next", "Fira Code"] - sidecar
fontContract.fontEmbeddingPolicy = "not-embedded" - sidecar
fontContract.embeddedFontCount = 0 unignoredOutputs = []
Artifacts are under:
docs/export/test-slidev-m5-font-contract-pptx-strict/
docs/export/test-slidev-m5-font-contract-pptx-strict/architecture.zh-CN.pptx
docs/export/test-slidev-m5-font-contract-pptx-strict/architecture.zh-CN.pptx.report.json
docs/export/test-slidev-m5-font-contract-pptx-strict/architecture.zh-CN-pptx-visual-diff/pptx-visual-diff.report.json
docs/export/test-slidev-m5-font-contract-pptx-strict/architecture.zh-CN-pptx-visual-diff/all-side-by-side-sheet.png
This closes the first font-contract slice only. It does not embed fonts, fetch remote fonts, or assert that native visible text would match Chromium. The real report shows that the editable layer depends on Avenir Next and Fira Code, both flagged as Office missing-font risks, and that CJK text is written through Microsoft YaHei as the writer's East Asian fallback. That is a reason to keep the editable layer transparent until a visible-native branch has a stricter font and visual A/B gate.