Printing/PDF export from the quarto-hub preview (issue #315)#379
Merged
Conversation
… (issue #315) Phase 1 of bd-vhdknrvl. Adds a pure, DI-based transform that turns the WASM HTML-pipeline output (standalone document, but with /.quarto/… <link>/<script> refs and relative <img> srcs) into a single self-contained document suitable for opening as a bare top-level tab: - CSS <link> → inline <style> (with url() font/image refs → data: URIs) - <script src> → executing inline <script> (reveal.js must run) - <img src> (relative + /.quarto) → base64 data: URIs VFS access is injected (SelfContainedReaders) so it is decoupled from the WASM singleton; production binding is deferred to the Phase 3 glue. Adds web-font MIME types to the shared vfsPaths.guessMimeType. Tested via 9 jsdom cases; full preview-renderer suite stays green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (issue #315) Phase 2 of bd-vhdknrvl. Adds a path-aware WASM render that produces the standalone printable form of an in-scope preview document: - coerce_format_for_print: inverse of map_format_for_preview (q2-preview→html, q2-slides→revealjs, q2-debug/q2-raw→html, else passthrough) so the HTML pipeline (and reveal deck assembler) runs. - render_single_doc_to_response gains a format_override param (existing 4 callers pass None); render_printable(path) reads the VFS file, coerces the detected format, and renders through the HTML branch with the real path so relative image srcs resolve against the doc dir. - renderPrintable wrapper + type decls in preview-runtime. The output still links /.quarto/ artifacts; the JS makeSelfContainedHtml inliner (Phase 1) embeds them before the doc opens in a top-level tab. Load-bearing render contract pinned natively in a new quarto-core integration test (the WASM crate is cdylib-only): a q2-preview-frontmatter doc renders to full HTML with its relative image src preserved, and a revealjs doc renders to a standalone deck. WASM rebuilt and verified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lockfile catch-up materialized by the WASM build; unrelated to the render_printable change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…315) Phases 3-4 of bd-vhdknrvl. Wires the printable-export end to end and exposes it in the preview UI: - forceRevealPrintMode (preview-renderer): inject view:"print" into a deck's own Reveal.initialize config, so reveal enters its paginated PDF layout when opened from a blob: URL (whose query string is not reliably exposed via location.search). - printableDocument service (hub-client): renderPrintable → makeSelfContainedHtml (readers bound to the WASM VFS) → forceRevealPrintMode for slides → Blob URL → window.open in a new top-level tab, with pop-up-block detection and delayed revoke. - Floating 🖨 button in the preview pane (Editor.tsx/.css), shown for q2-preview / q2-slides / revealjs, with a dismissible error toast. Pure logic unit-tested (revealPrintMode, isPrintableSlidesFormat, buildPrintableHtml throw path). hub-client production build (tsc -b && vite build) passes. Browser E2E still pending. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…315) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drives the real render_printable WASM export against a VFS project and runs makeSelfContainedHtml + forceRevealPrintMode over the output — the production pipeline minus window.open. Verifies a q2-preview doc renders to full HTML (image src preserved) and inlines to a self-contained document (no /.quarto refs, image as data URI), and a revealjs doc renders to a standalone deck forced into reveal print layout. Both artifacts were also opened in Chrome and visually confirmed: the doc renders themed/self-contained; the deck shows reveal's print-pdf stacked-slide layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#315) Phase 5 of bd-vhdknrvl. q2's HTML template inlines only the theme's @media print rules and omits pandoc's default print partial, so a printable document lacked heading break-avoidance, orphan/widow control, and figure/code/table break-inside protection. injectPrintStylesheet appends a conservative @media print block (before </head>) covering those basics plus @page margins and a white-background reset. Wired into buildPrintableHtml's document branch; reveal decks skip it (they ship their own precise per-slide print CSS). Injected printable-only in the JS glue, so q2 render and the live preview are untouched. The E2E wasm test now drives the full buildPrintableHtml glue and asserts data-q2-print present for docs / absent for decks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…able export (issue #315) Field bug: the printable document opened with no theme styling in both Firefox and Chrome. Root cause found by reproducing the exact browser pipeline: the compiled Bootstrap theme CSS artifact carries a leading UTF-8 BOM (U+FEFF). A <link> load strips it from the byte stream, but makeSelfContainedHtml injected the bytes verbatim into a <style>, where the BOM prefixes the first selector (`:root,[data-bs-theme=light]`) — an invalid selector in a comma list drops the WHOLE rule, so Bootstrap's `--bs-*` variables were never defined and the theme silently fell back to unstyled serif defaults. Fix: strip a leading BOM from inlined CSS and JS text. Verified end-to-end in a real browser (Chrome via CDP): stripping the BOM makes `--bs-body-font-family` resolve and the cosmo theme render correctly. Tests: 2 unit cases (BOM strip for CSS + JS) in makeSelfContainedHtml, and a themed real-WASM regression in printableDocument.wasm.test.ts that fails without the fix (asserts no inlined <style> begins with a BOM). This was missed originally because the first E2E fixture used a minimal themeless doc with no BOM-prefixed theme CSS to inline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
…es panel (issue #315) Relocates the 🖨 print affordance out of the floating preview-pane overlay into the Files sidebar header (next to New / Upload), per user request. The entire printable concern (button, in-flight + error state, handler) now lives in FileSidebar, driven by a new optional `currentFormat` prop; Editor just threads that prop through. Removes the preview-pane button, error toast, and associated CSS. Adds FileSidebar integration tests: the button is shown only for printable formats (q2-preview / q2-slides / revealjs), hidden otherwise, and clicking opens the printable document for the current file. Verified in an isolated browser mount: "🖨 Print" renders in the Files header matching the New/Upload button style. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adding the Print button made three buttons compete for the narrow sidebar header; flex-shrink squeezed New/Upload below their content width so their labels wrapped to a second line, making them taller than the single-line Print button. Give all header buttons white-space:nowrap + flex-shrink:0 and a fixed line-height (so an emoji's taller line box doesn't inflate one button), and let the header flex-wrap so whole buttons drop to a second row when the width is too small — instead of one button going two-line. All three are now the same height (28px); verified at 250px (one row) and 190px (Upload wraps as a whole button). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cscheid
added a commit
that referenced
this pull request
Jul 7, 2026
…reaking CI The workbox `maximumFileSizeToCacheInBytes` ceiling was 35MB while the `wasm_quarto_hub_client_bg.wasm` artifact had crept to ~36.7MB. When a globbed precache asset exceeds the limit, workbox emits a warning that vite-plugin-pwa's `logWorkboxResult` throws as a fatal build error, failing `vite build` (and thus the TS Test Suite's Build WASM step). The boundary was razor-thin: the local/ubuntu build landed just under 35MiB while the macOS CI build produced ~990 bytes over, so main went red only on macOS after #379 (render_printable + self-contained inliner) grew the WASM. Raise the ceiling to 64MB to restore generous headroom for continued WASM growth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #315 (bd-vhdknrvl).
Problem
Printing/PDF from the live preview is broken: the React preview formats render DOM inside a sandboxed iframe with no standalone document, so the browser's "Print Frame" gives Firefox a clipped single page and Chrome no affordance at all.
Approach — "Open printable version"
A 🖨 button in the preview renders a standalone, self-contained copy of the current document on demand and opens it in a new top-level tab, where the browser paginates natively and
@media printapplies (⌘P → clean multi-page PDF).Pipeline:
render_printable(path)— new path-aware WASM export. Coerces the preview pseudo-format to its HTML output (q2-preview→html,q2-slides→revealjs, inverse ofmap_format_for_preview) and renders through the HTML pipeline so relative imagesrcs resolve against the doc dir.makeSelfContainedHtml— inlines every VFS-backed asset: CSS/JS<link>/<script>→ inline<style>/<script>, images + CSSurl()fonts →data:URIs. Strips a leading BOM so inlined theme CSS actually applies (see below).forceRevealPrintMode— injectsview:"print"into a deck'sReveal.initializeconfig (blob URLs don't expose?print-pdfvialocation.search).injectPrintStylesheet— conservative@media printbaseline for documents (heading break-avoidance, orphans/widows, break-inside for figures/tables/code,@pagemargins).Notable bug found & fixed
The compiled Bootstrap/reveal theme CSS artifact carries a leading UTF-8 BOM. A
<link>load strips it, but inlining into a<style>prefixed the first selector (:root,[data-bs-theme=light]) — one invalid selector in a comma list drops the whole rule, so all--bs-*variables went undefined and the doc rendered unstyled. Fix: strip a leading BOM from inlined CSS/JS.Verification
printableDocument.wasm.test.ts) drives the actual export + full glue; a themed regression fails without the BOM fix.q2-previewdoc renders themed/self-contained/paginated;revealjsdeck (solarized) renders themed, self-contained, in reveal print-pdf layout.test:ci(678+76+123),cargo nextest -p quarto-core(2450), preview-renderer (480+526), WASM build, strict hub-client production build.Plan:
claude-notes/plans/2026-07-06-issue-315-preview-printing.md.🤖 Generated with Claude Code