30 KiB
Slidev Export Solution Summary
This document records the current Slidev export truth for NoteMD.
Current Export Model
NoteMD no longer treats direct slidev build as a sufficient proof for the UI export buttons.
The maintained workflow is:
- The active note is prepared as a Slidev deck when it is not already a Slidev deck.
- Existing Slidev decks are copied into an isolated prepared working workspace before export verification so the verifier can audit and patch without mutating the source note, and so sibling Slidev support entries plus explicitly referenced local assets can be mirrored into the working copy.
- The full Slidev skill directory is loaded when available, including
references/*.md. - Generated decks receive presentation guardrails before export, and large Mermaid guardrails no longer overwrite a slide that already declares
zoom. - The local Slidev fork is preferred when present.
- HTML output directories are recreated before build to avoid stale assets.
- HTML export attempts native standalone first, records the actual HTML mode, and falls back to server-script-compatible HTML only when native standalone sanity checks find real missing slide loader bindings.
- Browser rendering is verified with Playwright across the full deck by default.
- When the Playwright runtime is available,
exportSlidesCommand()and the maintainer verifier both run the sameconvergeSlidevDeckLayout()loop before finalHTML/PDF/PNG/MP4export.
The canonical maintainer workflow is documented in:
docs/maintainer/slidev-export-workflow.md
docs/maintainer/slidev-export-workflow.zh-CN.md
The current strict standalone acceptance index for the real architecture.zh-CN.md run is:
docs/maintainer/slidev-standalone-acceptance-2026-06-18.md
docs/maintainer/slidev-standalone-acceptance-2026-06-18.zh-CN.md
HTML Modes
Standalone HTML
Standalone HTML is the preferred local-inspection path when the installed Slidev CLI supports --standalone-bundle.
Expected output:
docs/export/<source-basename>-slides/index-standalone.html
This path should be openable directly from the filesystem and is the default mode used by:
npm run verify:slidev-export
When the native standalone bundle is invalid, NoteMD falls back to the server-script-compatible HTML path instead of shipping a broken index-standalone.html. Maintainer closure that specifically claims native standalone must use the strict gate, not the compatibility pass.
Server-Script HTML
Server-script mode remains available for compatibility with normal Slidev SPA builds that need HTTP serving, and it is now the automatic fallback when native standalone bundle sanity checks fail.
Expected output:
docs/export/<source-basename>-slides/index.html
docs/export/<source-basename>-slides/start-server.sh
docs/export/<source-basename>-slides/start-server.bat
docs/export/<source-basename>-slides/README.md
Use this mode when a build must be served via localhost instead of opened as a standalone file.
Strict Native Standalone Gate
Compatibility HTML verification may pass through server-script fallback. Native standalone verification is a stricter claim and must be run with:
node scripts/verify-slidev-export-workflow.cjs --json --format html --html-mode standalone --require-native-standalone --source architecture.zh-CN.md
Strict pass conditions add:
htmlExport.actualMode = "standalone"htmlExport.requiresLocalServer = falsehtmlExport.standaloneAttempt.accepted = truehtmlExport.standaloneAttempt.loaderGaps = []standaloneGate.required = truestandaloneGate.passed = true
The gate exists because a compatibility run with ok: true can still mean actualMode = "server-script-fallback".
Local Fork Resolution
Slidev command resolution prefers:
NOTEMD_SLIDEV_BINSLIDEV_CLI_PATH$HOME/slidev/packages/slidev/bin/slidev.mjsnpx -y @slidev/cli
On Jacob's workstation, the maintained verification path should report the local fork path in environment.slidev.version.
Real Verification Command
Run:
npm run verify:slidev-export
Default real source:
docs/architecture.zh-CN.md
Important pass conditions:
ok: trueslideSource.skillReferenceCount > 0ignoredOutputs: []- no stale deck text
- no missing-theme marker such as
seriph - every Playwright sample has
failed: false layoutAuditSummary.overflowCount = 0layoutAuditSummary.unreadableCount = 0layoutAuditSummary.lowEffectiveFontCount = 0- quality-margin and content-area findings are either zero or explained by a structural patch attempt
- source Mermaid fences remain one-to-one with exact fence content in the exported deck unless a human explicitly edits the source
Real maintained baseline as of 2026-06-20:
node scripts/verify-slidev-export-workflow.cjs --jsonondocs/architecture.zh-CN.mdreturnsok: true- the report confirms
/home/jacob/slidev/packages/slidev/bin/slidev.mjs - the report confirms
/home/jacob/slidev/skills/slidev - default HTML verification now audits the full prepared deck, not only representative slides
- the real
architecture.zh-CNdeck converges to29audited slides after bounded patching withoverflowCount = 0, while preserving the source Mermaid fence count (3source fences,3exported fences); current reports must also showmermaidSourcePreservation.passed = true PDFandPNGverification on the same real source also returnok: trueafter exporting from the same converged deck- the current local Slidev
52.16.0fork now passes the strict native standalone gate for the realarchitecture.zh-CNHTML export after fixing NoteMD's loader-binding detector to accept minified identifiers such as$n; the final real output isindex-standalone.html, not a fallback-onlyindex.html - existing Slidev deck fixtures now go through isolated prepared working copies, so maintainer verification no longer under-audits them as single-slide files or loses sibling
layouts/*.vue - rendered audit now includes effective font, SVG text font, table/code minimum font, quality margins, and content-area ratio
- low effective font, tight margin, and low utilization now create structural patch recommendations for table/code/prose before whole-slide zoom is reduced further; Mermaid keeps the source fence intact by default
- source preparation now injects a clean-room
SlideLayoutPlanbudget into deterministic outlines and LLM deck prompts - rendered audit now reports source-preserved Mermaid fit evidence through
layoutAudit[].mermaidFitand summary counters for Mermaid review, low zoom, and manual-review cases; the latest real strict run reportsmermaidSlideCount = 3,mermaidFitReviewCount = 3,mermaidLowZoomCount = 2, andmermaidManualReviewCount = 1 - mixed Mermaid/prose slides no longer solve fit by keeping low whole-slide zoom on prose; the patcher may move only the non-Mermaid primary content onto a readable slide while preserving every source Mermaid fence opener, metadata, body, and closer as byte-stable content
- explicit local relative assets referenced by Markdown images, HTML media/link/srcset attributes, and Slidev frontmatter keys are copied next to the prepared deck; local CSS files referenced by the deck are parsed as a local dependency graph, including
url(...)image/font dependencies and local@importstylesheet chains relative to each CSS file location, so isolated_slidev-sourcesworkspaces do not break source-relative SVG/PNG/JPEG/background/favicon/poster/font references - the local Slidev fork's standalone bundler now preserves first-slide loader bindings when stubbing Vite preload helpers; NoteMD's strict standalone gate remains fail-closed and continues to report real
loaderGaps - HTML export syncs the prepared deck's explicit local file references into the final
<source>-slides/output directory, and prepared decks default tofonts.provider: noneunless the source already declares top-levelfonts:
Dedicated standalone acceptance evidence is tracked in docs/maintainer/slidev-standalone-acceptance-2026-06-18.*. The large generated HTML and screenshot output remains archived outside the repo under /home/jacob/slidev-export-review/2026-06-18/standalone-strict/ so the main branch does not gain new one-off export artifacts.
Current Rendered Layout Model
The current workflow is now strong enough to reject broken wiring, missing skill references, stale output directories, missing local fork usage, browser-open failures, and visible layout overflow on the real docs/architecture.zh-CN.md fixture.
The current render-feedback loop is now:
prepareSlidevExportSource()still loads the full Slidev skill directory, asks the LLM to preserve source Mermaid fences, rejects LLM deck candidates that change those fences, and asks it to split dense prose, tables, or code before export.SlideLayoutPlanestimates dense Markdown blocks before generation and feeds a deterministic layout budget into outlines and LLM prompts.scripts/verify-slidev-export-workflow.cjsand the productexportSlidesCommand()now share the same convergence workflow, which renders the built HTML in Playwright, waits for visible slide content, and measures the actualslidev-pageroot plus overflow-prone elements.SlidevOverflowAuditclassifiesoverflow,unreadable-scale,render-error,low-effective-font,tight-margin, andlow-content-utilizationfrom rendered geometry, records source-preserved Mermaid fit status, and also records slot-zone owner rects, content bounds, scroll overflow, and recommended local transform scales instead of relying only on Markdown heuristics.SlidevDeckPatchnow applies overflow-derived slidezoomvalues, zone-local<Transform>scales derived from detected out-of-bounds geometry, and escalates to structural patching for tables, code, prose, or competing component slots when shrinking further would make the slide unreadable or when rendered quality findings recommend splitting.- The current structural patcher supports:
- simple heading + paragraph/list slides
- Markdown tables, including row-split and width-driven column decomposition
- pathological width-heavy and long-cell tables through deterministic record-list fallback
- non-Mermaid fenced code blocks, with TypeScript/JavaScript/Python/Rust top-level tokenizer chunking before generic semantic-block, blank-line, or line-budget fallback
- generic slot-marked layouts, including explicit
::default::, supported built-in slot layouts, and custom named slots when the slot content is structurally patchable - unique component-heavy slot zones through local
<Transform :scale="...">wrapping when structural splitting is unavailable - multiple unsafe component-heavy named slots through slot-level pagination when local Transform scale would violate the font floor
- first-slide deck headmatter content when structural splitting is possible
- mixed Mermaid/prose slides by moving prose/list content away from the diagram slide while preserving each Mermaid fence opener, metadata, body, and closer unchanged
- The HTML exporter now records structured HTML outcomes, rejects known-bad native standalone bundles, preserves rejected native attempts as
index-standalone.failed.html, and falls back to server-script-compatible HTML only for the compatibility path. - The verifier now audits the full deck by default and keeps retrying within a bounded loop until the rendered deck fits or the retry budget is exhausted.
Mermaid handling has a stricter content-preservation rule than tables, code, or prose. A user-provided Mermaid fence remains one diagram. When the preserved diagram is low-zoom, low-font, or too tight to prove presentation quality automatically, the workflow records fits, source-preserved-fit-review, or manual-review evidence instead of rewriting the source graph into several diagrams.
Operationally this is a hard invariant, not a preference: automated export must not split one source Mermaid fence, rewrite its body, change its fence metadata, or reorder it. The automatic path may only fit the rendered diagram, move non-Mermaid neighboring content, or expose review evidence; changing Mermaid content requires an explicit human source edit.
This is now enforced before the prepared deck is written, not only after export verification: both one-shot LLM deck generation and outline-continuation LLM generation compare every source Mermaid fence against the candidate deck and fall back to deterministic source-preserving preparation if the candidate changes count, order, fence metadata, or body text.
The verifier now enforces that rule with mermaidSourcePreservation: each exported Mermaid fence is compared to the corresponding source fence. A block-count-only match is not enough.
Mixed Mermaid/prose slides are different from Mermaid-only slides: low whole-slide zoom is not acceptable because it makes the prose unreadable. The supported repair is to keep the Mermaid source fence byte-stable on a diagram-focused slide and move only the prose/list content to its own readable slide. It is not a license to split one Mermaid diagram into several diagrams. If the slide layout is unsupported and that prose move cannot be done safely, the patcher blocks the low zoom instead of shrinking mixed content.
That rule is also covered by a unit regression now: even if a Mermaid slide is mistakenly routed toward a code structural patch, the patcher must refuse to treat a mermaid fence as a splittable code block.
Mixed component/prose slides now follow the same readability-first shape without changing the Mermaid rule: when a custom layout contains one complete component/Vue surface and one Markdown prose/list content block, the patcher separates them into two presentation surfaces before whole-slide zoom is considered. The next rendered audit pass can then wrap only the component block in a measured local <Transform>, while the prose page remains readable. If the mixed component slide contains a fence, table, image, directive, existing Transform, or unstable component/prose/component ordering, the patcher blocks the zoom/manual-review path instead of silently shrinking prose.
Stage 13/14 move that unsupported-boundary rule into the production fixture runner. unsupported-component-table-boundary-stress, unsupported-component-fence-boundary-stress, and unsupported-component-image-boundary-stress are expected-failure fixtures: when selected explicitly, the verifier should end with ok = false, while native standalone export, browser loading, generated-artifact visibility, the failure-review fingerprint, and Mermaid source preservation all still pass. The image fixture also proves that its local SVG asset survives in both the prepared deck and final standalone export. The default verify:slidev-layout-fixtures run remains success-only; use a concrete --fixture ... or --include-expected-failures to audit fail-transparent boundaries.
The clean-room reference from ref/infinite-canvas is still the world-rect and viewport-transform idea: nodes have { position, width, height }, the viewport has { x, y, k }, and visible bounds are derived from transform math. For NoteMD export, that becomes an export-layout camera for a fixed Slidev safe rectangle, not an interactive infinite canvas. Because the reference project is AGPL-3.0 and NoteMD is MIT, implementation must be independent.
Current landed state:
verify:slidev-exportnow resolves Jacob's local Slidev fork, Slidev skill references, and Playwright browser cache from workspace-aware paths instead of trusting the current process home blindly.- the maintainer verification chain now measures the visible
slidev-pageroot with Playwright, captures Mermaid shadow-host bounding boxes, and fails when the actual visible slide root clips content. - the patcher is no longer zoom-only: it first applies overflow-derived
zoom, then structurally splits supported Markdown table, code-fence, and simple text/list slides when rendered evidence says further shrinking would be the wrong move; Mermaid source fences are preserved by default and are not rewritten into several diagrams. - the real product export path now converges the prepared working deck before final
HTML/PDF/PNG/MP4export, instead of keeping the patch/rebuild loop verifier-only. - existing Slidev deck working copies now live under
_slidev-sources/<deck-basename>/, and common sibling support entries such aslayouts/,public/,setup/,components/,snippets/,styles/,global-top.vue, andglobal-bottom.vueare mirrored there when present. - rendered layout audit now also measures direct-text
div/section/article/aside/spanblocks, which closes the previous under-audit gap for component-heavy slides. - component-heavy slot zones now carry lightweight owner wrappers inside prepared working copies, so rendered measurements can feed slot ownership, zone-level owner geometry, local overflow scale, and slot-owned descendants that are clipped by
overflow-hiddenback into the patch loop instead of relying only on slide-global inference. - hard overflow remains rooted in the rendered slide root, while
safeRectstays the conservative fit target for measured scale recommendations; this keeps the gate tied to actual clipping while still steering shrink decisions away from edge-hugging layouts. PDFandPNGexport now passPLAYWRIGHT_BROWSERS_PATHthrough the Slidev CLI env so root-run verification can reuse Jacob's browser cache.- the real
docs/architecture.zh-CN.mdHTML workflow now passes with a full-deck Playwright audit,overflowCount = 0, and bounded retry closure. - an additional real maintainer-local structural overflow note now proves that Markdown table decomposition and code-fence chunking can converge through the same verifier path instead of only through unit tests.
- a real slot/headmatter Slidev deck now proves that native standalone loader gaps are detected and converted into a working
index.html + start-server.* + README.mdfallback instead of being treated as successful standalone output. - real maintainer-local decks now also prove:
- explicit
::default::slot handling - existing Slidev deck working-copy verification
- pathological table fallback into record-list slides, including long-cell tables where row/column splitting would preserve cramped prose
- slot-marked custom layouts backed by a real custom
layouts/*.vuefile - component-heavy custom slot layouts converging through zone-level owner-geometry-based local
<Transform>wrapping, including same-slide multi-zone cases where more than one overflowing transformable slot can be wrapped in the same pass while slot signals and rendered text hints stay as bounded fallback for attribution ties - a dense two-zone custom layout where slot-owned descendants originally clipped under
overflow-hiddenare now measured correctly and converge took: truewithout forcing whole-slide zoom - local CSS
transform/ independentscale/ CSSzoomawareness in effective font measurement, so locally transformed content is judged by rendered size
- the real
docs/architecture.zh-CN.mdstrict native standalone run now closes withactualMode = "standalone",requiresLocalServer = false,loaderGaps = [],standaloneGate.passed = true,29audited slides, zero hard overflow / unreadable scale / low effective font / quality margin warning / low utilization findings, and bounded retry closure. - Mermaid fit review is now explicit report data. It is intentionally separate from hard overflow: a
manual-reviewMermaid status is evidence that source preservation and automatic readability proof are in tension, not permission to auto-split the diagram. - the expanded full-deck fixture suite archives native standalone fixtures under
/home/jacob/slidev-export-review/2026-06-20-expanded-layout-fixtures/: source-layout stress with 52 Slidev skill references, slot/component Transform stress, mixed Mermaid/prose non-diagram content separation, media/nested-slot/ultra-wide-table stress, and frontmatter/cross-directory asset stress. - prepared deck export now keeps explicit frontmatter/media/CSS dependency assets available in final HTML output, including imported local CSS dependency chains, and disables remote font providers by default for prepared decks while preserving explicit user font configuration.
- the final CSS dependency verification archive is
/home/jacob/slidev-export-review/2026-06-20-css-asset-dependencies-final/: the realarchitecture.zh-CN.mdstrict standalone report isok = true, uses/home/jacob/slidev/packages/slidev/bin/slidev.mjs, loads 52 Slidev skill references, preserves all 3 source Mermaid fences byte-for-byte, and outputs native standalone HTML without local-server fallback. - out-of-scope CSS imports/URLs are not copied and are removed or neutralized in copied CSS files, so standalone output does not request rejected paths such as
outside.cssoroutside.svg. - the CSS import/media fixture archive is
/home/jacob/slidev-export-review/2026-06-20-css-import-media-fixtures/: the production fixture suite now verifies local CSS@importrecursion, imported CSS font/background dependencies, local video/audio/track/poster assets, CSS sanitizer behavior, and rejection of out-of-scope imported stylesheets in both prepared workspace and final standalone export. - source preparation now rejects both one-shot and outline-continuation LLM deck candidates that split, rewrite, reorder, or only alter metadata on source Mermaid fences, then falls back to deterministic source-preserving preparation instead of writing a mutated deck.
- historical generated
docs/export/test-slidev-*,docs/export/test-slidev.pdf,docs/export/test-slidev-video.mp4, and olddocs/export/slides/artifacts are no longer tracked inmain; reusable export guidance remains indocs/export/README.mdanddocs/export/README.zh-CN.md. - the font-safe slot/code convergence archive is
/home/jacob/slidev-export-review/2026-06-20-competing-slot-zones-final-fixtures-v2/: production fixtures cover unsafe competing slot pagination, font-floor zoom blocking, and fit-factor code splitting while keeping hard overflow and low effective font at zero. - the matching real
architecture.zh-CN.mdstrict standalone output is archived at/home/jacob/slidev-export-review/2026-06-20-font-safe-real/;architecture.zh-CN.slidev.mdis the reviewable exported deck andindex-standalone.htmlis the native standalone output. - the Stage 12 mixed component/prose fixture archive is
/home/jacob/slidev-export-review/2026-06-20-stage12-mixed-component-prose-fixtures/: the production suite covers a customdashboard-shellVue component surface plus Markdown prose separation and closes with 3 slides, 2 patch passes, no whole-slide zoom, and preserved component/prose fingerprints. - the Stage 12 real
architecture.zh-CN.mdstrict standalone output is archived at/home/jacob/slidev-export-review/2026-06-20-stage12-mixed-component-prose-real/;architecture.zh-CN.stage12.slidev.mdis the reviewable exported deck andarchitecture.zh-CN-slides/index-standalone.htmlis the native standalone output. - the Stage 13 expected-failure fixture archive is
/home/jacob/slidev-export-review/2026-06-20-stage13-unsupported-component-boundary-fixture/:unsupported-component-table-boundary-stressis correct only when verifierok = false,hardOverflowCount > 0, and the blocked reason says mixed component plus primary Markdown content cannot be repaired with whole-slidezoom, while its single source Mermaid fence remains byte-stable in the exported deck. - the Stage 13 default success fixture archive is
/home/jacob/slidev-export-review/2026-06-20-stage13-success-fixtures/: all 9 converging production fixtures pass, and expected-failure fixtures are not included in the default green path. - the Stage 13 real
architecture.zh-CN.mdstrict standalone output is archived at/home/jacob/slidev-export-review/2026-06-20-stage13-real/;architecture.zh-CN.stage13.slidev.mdis the reviewable exported deck andarchitecture.zh-CN-slides/index-standalone.htmlis the native standalone output. The report isok = true, uses the local Slidev fork and 52 skill references, preserves all 3 Mermaid fences withchangedFenceIndexes = [], and closes withhardOverflowCount = 0andlowEffectiveFontCount = 0. - the Stage 14 component/fence expected-failure fixture archive is
/home/jacob/slidev-export-review/2026-06-20-stage14-unsupported-component-fence-boundary-fixture/:unsupported-component-fence-boundary-stressis correct only when verifierok = false, the blocked reason says mixed component plus primary Markdown content cannot be repaired with whole-slidezoom, no whole-slide zoom is introduced, and its single source Mermaid fence remains byte-stable. - the Stage 14 component/image expected-failure fixture archive is
/home/jacob/slidev-export-review/2026-06-20-stage14-unsupported-component-image-boundary-fixture/:unsupported-component-image-boundary-stressproves the same fail-transparent behavior and also verifiesassets/boundary-image.svgin the prepared deck and final standalone export. - the Stage 14 default success fixture archive is
/home/jacob/slidev-export-review/2026-06-20-stage14-success-fixtures/: all 9 converging production fixtures still pass, and the three expected-failure fixtures stay out of the default green path. - the Stage 14 real
architecture.zh-CN.mdstrict standalone output is archived at/home/jacob/slidev-export-review/2026-06-20-stage14-real/;architecture.zh-CN.stage14.slidev.mdis the reviewable exported deck andarchitecture.zh-CN-slides/index-standalone.htmlis the native standalone output. The report isok = true, uses the local Slidev fork and 52 skill references, preserves all 3 Mermaid fences withchangedFenceIndexes = [], and closes withhardOverflowCount = 0andlowEffectiveFontCount = 0.
Current gap:
- richer custom/component-heavy Slidev layouts beyond the current supported structural set still fall back to conservative/manual-review behavior, especially when there is no stable owner, the component/prose order is ambiguous, or a single non-Mermaid component surface cannot be structurally split or transformed within the font floor; component/table/directive/fence/image boundaries now have explicit blocking coverage;
- standalone export now has a strict native gate and the real architecture fixture passes it, but correctness still depends on post-build sanity detection; server-script fallback remains a compatibility lane for future bad bundles, not evidence that native standalone passed;
- full-deck Playwright verification is now more correct, but noticeably slower, so future work should improve patch convergence instead of weakening the audit back to representative sampling;
- the Obsidian CLI can dispatch
notemd:export-slides, but it does not expose an export-complete handshake, so host-command smoke is still weaker than the maintainer verifier.
Next-Level Layout Quality Direction
The current acceptance proves that export does not clip, can pass strict standalone, and follows the real UI-equivalent workflow. It does not by itself prove presentation quality. Under the source-preserving Mermaid constraint, a large source Mermaid diagram may still require low zoom; the workflow should record that tradeoff instead of rewriting the original diagram into several diagrams.
The next direction should not replace the current render-feedback pipeline. It should:
- keep
convergeSlidevDeckLayout()as the final fact gate; - add a clean-room
SlideGeometry/SlideLayoutPlanlayer before source preparation, borrowing only the world-rect, union-bounds, and viewport-fit ideas fromref/infinite-canvaswithout copying AGPL-3.0 implementation code; - keep rendered effective-font, quality-margin, content-area-ratio, low-utilization, and Mermaid fit-review metrics in the verifier report;
- preserve Mermaid source fences while pre-splitting table/code/prose content so large non-Mermaid content is not solved mainly by low
zoom; - report hard gate and quality gate separately: hard overflow still fails closed, while quality warnings drive splitting, relayout, or manual review.
Detailed progress comparison and implementation direction are tracked in:
docs/brainstorms/2026-06-20-slidev-layout-quality-and-canvas-roadmap.zh-CN.md
Output Policy
The verification command writes inspectable files under docs/export/. They are useful local evidence, but they are not meant to be committed unless a release or documentation task explicitly asks for those generated artifacts.
Before committing, check:
git status --short docs/export
git check-ignore -v docs/export/_slidev-sources/architecture.zh-CN.slidev.md docs/export/architecture.zh-CN-slides/index-standalone.html
For local verification artifacts, git check-ignore should print nothing.
Why The Workflow Was Hardened
The previous workflow missed several failure modes:
- direct CLI smoke tests did not prove that UI actions called source preparation;
- non-outline export did not have a durable proof that full Slidev references were available to deck generation;
- stale output directories allowed old chunks or old deck content to survive rebuilds;
- LLM-created decks could emit malformed per-slide frontmatter;
- LLM-created decks could select uninstalled themes;
- large Mermaid diagrams could overflow the 16:9 canvas;
- generated output could be hard to inspect if ignored or cleaned too early.
The current verification workflow makes those failure modes explicit.