Skip to content

Commit 3b06447

Browse files
q2-preview: body container + title block (Plan 2D)
Phase 6.0 framework extraction: - Lift `inlinesToPlainText` / `blocksToPlainText` to `framework/plainText.ts`. - Add `framework/meta.ts` with `extractMetaString` / `extractMetaBool` / `extractMetaStringList`. - Flat barrel re-exports on `framework/index.ts` (plainText, meta, customNode bookkeeping fix). - Tighten `FormatRegistry` at `framework/types.ts` with typed optional entries for the two synthetic keys (`__fallback__`, `__title_block__`). - Migrate `ReactAstSlideRenderer.tsx`'s private `extractMetaString` to the framework helper. Deliberate behavior change for slide titles/authors containing inline markup (Emph/Strong/Code/Link now render as text instead of empty string) locked by a regression test. - Consolidate `meta.format` extraction in `ReactRenderer.tsx` and `getQ2Format.ts` onto the framework helper. - Expose framework helpers on `window.__Q2_PREVIEW_RENDERER__` for user TSX overrides. Phase 6.1+ body container in `PreviewDocument.tsx`: - Page-layout-aware `<div id="quarto-content"><main class="content">` wrapper mirrors Rust template at `template.rs:185-247`. - Imperative `document.body.className` management via useEffect with cleanup-on-unmount. - Minimal-mode title synthesis re-implements `transforms/title_block.rs:54-110`'s minimal-mode branch React-side (the transform is in `Q2_PREVIEW_TRANSFORM_EXCLUDED`). - Iframe `document.title` wiring from `meta.pagetitle ?? meta.title`, only when non-empty (preserves the static `q2-preview.html` title for untitled docs). Phase 7 title block: - New `q2-preview/custom/PreviewTitleBlock.tsx` (~110 LOC) mirrors Rust template at `template.rs:211-240` byte-for-byte. Multi-author and date-without-author Rust quirks deliberately replicated to avoid per-format CSS forks. - Registered under synthetic `__title_block__` key in `previewRegistry`; mounts inside `<main>` ahead of body blocks. `AstProps` shape parallels the `Ast` key (not `NodeArgs`). - Built-in exposed on `__Q2_PREVIEW_RENDERER__` so user TSX overrides can compose (rather than fully replace) the chrome. Tests: - 27 unit tests for framework/meta.ts + framework/plainText.ts - 18 integration tests for PreviewDocument (body container, body classes, minimal-mode title synthesis, iframe document.title) - 17 integration tests for PreviewTitleBlock (required elements, Pandoc-falsy semantics, user override full-replacement + composition) - Synthetic-key registration directly asserted in registry.test.ts (closes a pre-existing gap on `__fallback__`). Smoke-all q2-preview fixtures (11 new): - body-container-default / -full-layout / -minimal / -minimal-title - body-classes-override / -full-layout-combo - title-block-default / -full / -no-title / -multi-author / -date-no-author All 11 fixtures green in Playwright e2e; full plan checklist (19 items) ticked; cargo xtask verify clean.
1 parent 8caec72 commit 3b06447

33 files changed

Lines changed: 2110 additions & 311 deletions

claude-notes/plans/2026-05-10-q2-preview-plan-2d-body-container.md

Lines changed: 436 additions & 100 deletions
Large diffs are not rendered by default.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: Body classes + full layout combo
3+
format: q2-preview
4+
body-classes: custom-cls
5+
page-layout: full
6+
_quarto:
7+
tests:
8+
run:
9+
requires_js: true
10+
q2-preview:
11+
ensureHtmlElements:
12+
-
13+
- 'div#quarto-content.page-layout-full'
14+
- 'body.custom-cls'
15+
-
16+
- 'body.fullcontent'
17+
- 'div.page-layout-article'
18+
---
19+
20+
A paragraph; both knobs are set together to lock the case where a
21+
future refactor might conflate the body-classes useEffect with the
22+
page-layout className.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
title: Body classes override
3+
format: q2-preview
4+
body-classes: custom-cls
5+
_quarto:
6+
tests:
7+
run:
8+
requires_js: true
9+
q2-preview:
10+
ensureHtmlElements:
11+
-
12+
- 'body.custom-cls'
13+
-
14+
- 'body.fullcontent'
15+
---
16+
17+
A paragraph; the iframe body carries the user `body-classes` override
18+
and does NOT also carry the literal `fullcontent` default.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: Body container default
3+
format: q2-preview
4+
_quarto:
5+
tests:
6+
run:
7+
requires_js: true
8+
q2-preview:
9+
ensureHtmlElements:
10+
-
11+
- 'div#quarto-content.page-layout-article'
12+
- 'main.content#quarto-document-content'
13+
- 'body.fullcontent'
14+
- []
15+
---
16+
17+
A paragraph in the default-layout body container.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
title: Body container full layout
3+
format: q2-preview
4+
page-layout: full
5+
_quarto:
6+
tests:
7+
run:
8+
requires_js: true
9+
q2-preview:
10+
ensureHtmlElements:
11+
-
12+
- 'div#quarto-content.page-layout-full'
13+
- 'main.content'
14+
-
15+
- 'div.page-layout-article'
16+
---
17+
18+
A paragraph rendered inside the `page-layout-full` wrapper.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
title: Doc
3+
format: q2-preview
4+
minimal: true
5+
_quarto:
6+
tests:
7+
run:
8+
requires_js: true
9+
q2-preview:
10+
ensureHtmlElements:
11+
-
12+
- 'h1'
13+
- 'p'
14+
-
15+
- 'div#quarto-content'
16+
- 'main.content'
17+
- 'header#title-block-header'
18+
---
19+
20+
A paragraph; with `minimal: true` and a `title`, q2-preview synthesizes
21+
a `<h1>Doc</h1>` ahead of the body (mirrors Rust's title-block
22+
transform's minimal-mode branch). The full title-block chrome is
23+
suppressed by the minimal wrapper-skip branch.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
format: q2-preview
3+
minimal: true
4+
_quarto:
5+
tests:
6+
run:
7+
requires_js: true
8+
q2-preview:
9+
ensureHtmlElements:
10+
-
11+
- 'p'
12+
-
13+
- 'div#quarto-content'
14+
- 'main.content'
15+
- 'h1'
16+
---
17+
18+
A paragraph. The wrapper is skipped under minimal mode, and no synthetic
19+
h1 is added because no title is set in the metadata.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
title: Doc
3+
date: "2026-05-10"
4+
format: q2-preview
5+
_quarto:
6+
tests:
7+
run:
8+
requires_js: true
9+
q2-preview:
10+
ensureHtmlElements:
11+
-
12+
- 'header#title-block-header'
13+
-
14+
- 'div.quarto-title-meta'
15+
- 'div.quarto-title-meta-date'
16+
---
17+
18+
A paragraph; with `date` but no `author`, the Rust template's
19+
`$if(date)$` block is gated inside the `$if(author)$` branch so the
20+
date is suppressed. Locked here so the Rust quirk and q2-preview
21+
mirror each other.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
title: Doc
3+
format: q2-preview
4+
_quarto:
5+
tests:
6+
run:
7+
requires_js: true
8+
q2-preview:
9+
ensureHtmlElements:
10+
-
11+
- 'header#title-block-header.quarto-title-block'
12+
- 'h1.title'
13+
-
14+
- 'p.subtitle'
15+
- 'div.quarto-title-meta'
16+
- 'div.abstract'
17+
---
18+
19+
A paragraph; with only `title` set, the title-block chrome contains
20+
just `<h1 class="title">` — no subtitle, author meta, or abstract.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: Doc
3+
subtitle: Sub
4+
author: Jane Doe
5+
date: "2026-05-10"
6+
abstract: A short summary of the document.
7+
format: q2-preview
8+
_quarto:
9+
tests:
10+
run:
11+
requires_js: true
12+
q2-preview:
13+
ensureHtmlElements:
14+
-
15+
- 'header#title-block-header'
16+
- 'h1.title'
17+
- 'p.subtitle'
18+
- 'div.quarto-title-meta-author'
19+
- 'div.quarto-title-meta-date'
20+
- 'div.abstract'
21+
- 'div.abstract-title'
22+
- []
23+
---
24+
25+
A paragraph beneath the full title-block chrome.

0 commit comments

Comments
 (0)