Skip to content

revealjs preview↔render parity: footer/logo, section attrs, resources: VFS sync#303

Merged
cscheid merged 3 commits into
mainfrom
feature/revealjs-preview-render-parity
Jun 17, 2026
Merged

revealjs preview↔render parity: footer/logo, section attrs, resources: VFS sync#303
cscheid merged 3 commits into
mainfrom
feature/revealjs-preview-render-parity

Conversation

@cscheid

@cscheid cscheid commented Jun 17, 2026

Copy link
Copy Markdown
Member

What

Closes three q2 previewq2 render parity gaps for format: revealjs decks, surfaced by an audit of examples/presentations/* (plan: claude-notes/plans/2026-06-17-revealjs-preview-render-parity-audit.md). Each commit is one focused fix.

commit fix strand
footer/logo DOM preview now emits the deck-level footer, logo, and has-logo outside .slides, mirroring assemble.rs bd-n2w0sxgd ✅
section Attr forward each section's id + classes onto <Slide>/<Stack> — fixes the title slide rendering uncentered (it lost center); every section regains its id + section class bd-vv8jft5n
resources: → VFS single-file preview honors a document's front-matter resources: so declared assets (e.g. logo:) sync into the preview VFS bd-k5rxujiy (Layer 1)

1. Deck footer/logo (bd-n2w0sxgd)

RevealDeck never read rendered.reveal.{footer,logo} (already populated in the preview AST by the reveal-footer-logo stage). New RevealChrome injects the markup into the .reveal element via useReveal()getRevealElement() (the HeaderIncludesEffect escape-hatch pattern, since @revealjs/react's <Deck> only renders children inside .slides) and toggles has-logo.

2. Section attributes (bd-vv8jft5n)

sectionAttrProps(div) derives {id, className} from the section Div's Attr; renderTopSection spreads it onto each <Slide>/<Stack>. The title slide now gets id="title-slide" class="section title-slide center" and reveal vertically + horizontally centers it.

  • Known follow-ups (left on the strand): @revealjs/react's <Stack> accepts only className (drops id), so a # Section-divider stack's own id can't round-trip yet; kvs (data-background-*) not forwarded; config-option parity (Deck config from format.revealjs.*) still pending.

3. resources: VFS sync (bd-k5rxujiy, Layer 1)

resolve_single_file_deps expands meta.resources (reusing the publish-path extract_resource_patterns + expand_patterns, globs included) into binary_files, riding the existing single_file_assets → with_binary_files VFS sync.

  • This is Layer 1 only (bytes into the VFS). A raw <img src="logo.svg"> still 404s until Layer 2 serves the bytes to it — either a per-asset rewrite in preview-renderer, or the bd-msp0 service-worker path-server. Decision deferred; see the plan.

Testing

  • New RevealDeck.integration.test.tsx (section-attr + chrome injection); single_file_deps_includes_declared_resources + _glob.
  • preview-renderer: 219 unit + 228 integration green. cargo xtask verify green (full for the TS legs; --skip-hub-build for the Rust-only resources: change — quarto-preview is native-only, not in the WASM path).
  • E2E (real q2 preview, Chrome, on examples/presentations/10-footer-logo): footer/logo are direct children of .reveal outside .slides, footer position: fixed, title slide centered. resources: Layer 1 verified via binary_count A/B (1 with resources:, 0 without).

Not included

  • The unrelated wasm-quarto-hub-client/Cargo.lock workspace version-sync (regenerated by the WASM build) is intentionally left out.

🤖 Generated with Claude Code

cscheid and others added 3 commits June 17, 2026 11:27
…review (bd-n2w0sxgd)

A `format: revealjs` deck with `footer:`/`logo:` rendered the footer
(`.footer.footer-default`), logo (`img.slide-logo`) and `.reveal.has-logo`
under `q2 render`, but NONE of them under `q2 preview` — a complete
feature miss, not a style nudge.

Root cause: `RevealDeck` never read the `rendered.reveal.{footer,logo}`
meta slots, even though the `reveal-footer-logo` transform
(`pipeline.rs::footer_render_stage`) runs in the q2-preview pipeline and
so populates them in the AST handed to preview.

Native contract: `crates/quarto-core/src/revealjs/assemble.rs`
(`render_revealjs_document` + `footer_logo_html`) places footer/logo as
DIRECT children of `.reveal`, OUTSIDE `.slides` (where `position: fixed`
resolves against the viewport — inside `.slides` it would resolve against
reveal's per-slide CSS `transform`), logo before footer, and sets
`has-logo` on `.reveal` iff a logo is present.

Fix: `revealChromeFromMeta` reads the two slots; a new `RevealChrome`
component injects the markup into the `.reveal` element via `useReveal()`
→ `getRevealElement()` and toggles `has-logo`, cleaning up on unmount —
the same imperative escape-hatch `HeaderIncludesEffect` uses for
`document.head`, because `@revealjs/react`'s `<Deck>` only renders
children inside `.slides`.

Tests: `RevealDeck.integration.test.tsx` (7 cases) — slot extraction,
placement outside `.slides`, logo-before-footer order, conditional
`has-logo`, empty-deck no-op, unmount cleanup. preview-renderer suite
green (219 unit + 224 integration); `cargo xtask verify` 14/14 green.

E2E (live `q2 preview examples/presentations/10-footer-logo`, Chrome):
  .reveal.has-logo > img.slide-logo  (parent === .reveal, not in .slides)
  .reveal.has-logo > div.footer.footer-default  (link href preserved)

Known follow-up: the footer/logo render `position: static` because the
preview's runtime-delivered reveal theme omits the `.reveal .footer` /
`.slide-logo` rules render's `theme-*.css` carries — a theme-content gap
tracked in bd-d0f9xoa6 (F4), separate from this DOM fix.

Plan: claude-notes/plans/2026-06-17-revealjs-preview-render-parity-audit.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l slides (bd-vv8jft5n)

`q2 preview`'s `RevealDeck` rendered every slide `<section>` with only
reveal's runtime classes (`present`/`future`/`stack`) — dropping the
`id` and the Quarto-authored classes that `q2 render` puts on each
`<section>` (`crates/quarto-core/src/revealjs/assemble.rs`). Most
visibly, the title slide lost `center`, so it rendered like a normal
slide instead of being vertically + horizontally centered (user report).

Fix: `sectionAttrProps(div)` derives `{id, className}` from the section
Div's Attr (`div.c[0]` = [id, classes, kvs]); `renderTopSection` spreads
it onto each leaf/inner `<Slide>` and the `<Stack>`. The title slide now
gets `id="title-slide" class="section title-slide center"` and reveal
centers it; every section regains its `id` + `section` class.

Tests: `RevealDeck.integration.test.tsx` — `sectionAttrProps`
(class-join, empty-Attr collapse) and `renderTopSection` (leaf carries
id+classes; stack carries className, inner slides carry their id+classes).
preview-renderer suite green (219 unit + 228 integration); `cargo xtask
verify` 14/14 green.

E2E (live `q2 preview examples/presentations/10-footer-logo`, Chrome):
  .slides > section#title-slide.section.title-slide.center.present
  getComputedStyle(section).top === 288.5px (reveal vertical-centers it)
  getComputedStyle(h1.title).textAlign === center

Known gaps (left on bd-vv8jft5n): `@revealjs/react`'s `<Stack>` accepts
only `className` (drops `id`), so a `# Section`-divider stack's own id
can't round-trip yet; kvs (`data-background-*`) not forwarded (deferred
to authoring, bd-bea550b0); config-option parity (Deck config from
`format.revealjs.*`) still pending.

Also retracts the earlier F4 theme-gap finding (bd-d0f9xoa6): it was a
stale-WASM artifact, not a real defect — footer/logo style correctly on
a clean build.

Plan: claude-notes/plans/2026-06-17-revealjs-preview-render-parity-audit.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d-k5rxujiy, Layer 1)

Single-file `q2 preview` only synced assets the AST image walker could
see (`Image` nodes + their includes). Assets referenced through metadata
or raw HTML — `logo:`, footer images, shortcode / CSS `url()` refs —
were invisible, so e.g. a reveal deck's `logo:` 404'd in preview.

This is "Layer 1" of the F5 fix: get the declared bytes into the VFS.
`resolve_single_file_deps` now also expands a document's front-matter
`resources:` (the same declaration used for publishing) right beside the
existing image collection, reusing the publish-path machinery —
`project_resources::extract_resource_patterns(meta, ["resources"])` +
`expand_patterns(canonical_root, deck_dir_anchor, …, DocumentMetadata,
Page)` (the same `expand_patterns` already used in this file for project
`resources:`) — folding the matches into `SingleFileDeps.binary_files`.
They then ride the existing `single_file_assets` → `with_binary_files`
sync into the VFS, exactly like the deck's images.

Properties: reuses publish semantics (declare once → publish + preview),
handles globs, format-agnostic, and on-policy (`resources:` is already
the sync upload trust boundary; see `resources_scoped_html_files`).

Layer 1 puts the bytes in the VFS but is NOT sufficient on its own: a raw
`<img src="logo.svg">` still fetches `/logo.svg` (SPA catch-all) and needs
its `src` rewritten to the minted blob URL to load — that's Layer 2
(per-asset rewrite in preview-renderer, or the bd-msp0 service worker).
See the plan for the full Layer 1 / Layer 2 split.

Tests (config.rs): `single_file_deps_includes_declared_resources`
(declared file lands in binary_files though nothing references it as an
image) and `single_file_deps_resources_glob` (`*.svg` pattern). 8/8
`single_file_deps` tests green; `cargo xtask verify --skip-hub-build`
green (Rust-only, native-crate change — not in the WASM path).

E2E (real `q2 preview` binary, RUST_LOG, A/B):
  deck `logo: logo.svg` + `resources: [logo.svg]`, logo.svg NOT a md image
    → "Discovered project files … binary_count=1"
  same deck, `resources:` removed
    → "Discovered project files … binary_count=0"
proving the `resources:` declaration (not image walking) syncs the asset.

Plan: claude-notes/plans/2026-06-17-revealjs-preview-render-parity-audit.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cscheid cscheid merged commit 0306429 into main Jun 17, 2026
5 checks passed
@cscheid cscheid deleted the feature/revealjs-preview-render-parity branch June 17, 2026 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant