Hephaestus plot documents (.hep): render to SVG with brand colors, and live preview in q2 preview / hub-client (bd-3qych45b) - #688
Merged
Conversation
…ych45b) Phase 1 of the hephaestus integration plan (claude-notes/plans/2026-09-17-hephaestus-hep-integration.md): an `` image in `html` / `revealjs` output is rendered at render time with hephaestus's renderer-free SVG backend, stored as a page-scoped artifact under `<stem>_files/figure-html/`, and the image is pointed at it. Preview (`q2-preview` / `q2-slides`) is deliberately left to a React component over the npm `hephaestus-svg-wasm` client (bd-sxiv2tio); the transform is native-only and excluded from the preview pipeline like `mermaid-render`. - `transforms/hephaestus.rs`: `HephaestusRenderTransform` (Finalization, after `resource-collector`, before `responsive-image`). Size from `width`/`height` attrs, else the document's hint, else 7in x 5in at 96 dpi. Content+size-addressed artifact names; per-image SVG id prefixes. Fail-soft warnings `Q-18-1` (not found), `Q-18-2` (unreadable / wrong format version), `Q-18-3` (backend degradation) — a new `image` diagnostics subsystem with docs pages and sidebar section. - `transforms/image_walk.rs`: the mutable body-image walker extracted from `responsive_image` so both transforms share one container set. - `resources/hephaestus/fonts/`: the four Roboto faces hephaestus's browser clients ship, registered once per process and mapped onto `sans-serif`, so the same `.hep` renders byte-identically on every machine and matches what the wasm client draws. OFL licence included. - quarto-core: `hephaestus = 0.4.1` (`document-read`, `svg`, `png`; no wgpu) in the native-only dependency table. On Linux, fontique's `fontconfig-dlopen` is enabled so building q2 does not require `libfontconfig1-dev`; a missing library at run time just yields an empty system font collection. - Tests: `tests/integration/hephaestus_render.rs` (11 end-to-end through `render_to_file`: artifact path, determinism, bundled font, sizing, id prefixes, revealjs, untouched non-hep images, both failure modes) plus 4 unit tests. `cargo xtask verify` green. - Docs: "Plot Documents" section in the figures guide. No snapshot files changed. Both `Cargo.lock`s gain hephaestus's text stack (parley / fontique / skrifa / kurbo / peniko / clipper2-rust / png); the WASM crate's lock records them but the crate never compiles them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…example (bd-3qych45b) A runnable project for checking the hephaestus integration by hand: one plot document referenced as a captioned figure, at an explicit size, and via a missing path (the fail-soft Q-18-1 case). Render with `cargo run --bin q2 -- render examples/plots/01-hephaestus-basic`. Render output is gitignored like the diagrams examples. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
When a document declares a brand, the light brand's `background`, `foreground` and `primary` colors become each rendered `.hep` plot's `paper`, `ink` and `accent`. hephaestus's built-in themes derive every chrome color from those anchors, so a dark-paper / light-ink brand inverts the whole plot in one step; data series keep their own color-scale colors. Only hex values cross into the plot — a named CSS color or `rgb()` function warns once per document (`Q-18-4`, new page + sidebar entry) and leaves that anchor as the document defines it. The palette is part of the artifact hash, so a brand change is a new artifact name. Pulled into phase 1 at the user's request while testing the example; phase 3 (bd-l6e3sd45) stays typography-only. Tests: three end-to-end (brand applied, non-hex slot warns and the rest still apply, no brand leaves the document's palette) plus a hex-parser unit test. `cargo xtask verify --skip-hub-build` green; the change is inside the native-only module. examples/plots/01-hephaestus-basic gains `brand.yml` (night paper, chalk ink, coral accent) and `brand: brand.yml` in the document, with the README and the figures guide describing the mapping. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
✅ 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. |
…iv2tio) Phase 2 of the hephaestus integration: `` now draws in the browser for q2-preview documents and `format: revealjs` decks, in both `q2 preview` and hub-client, through the npm `hephaestus-svg-wasm` client (0.4.1, pinned to the `hephaestus` crate version; parity tests on both sides). - preview-renderer: `HepImage` wraps the built-in `Image` as the registry's `Image` entry. For a local `.hep` target it fetches the asset-walker bytes and mounts `PlotView` (light, auto-resize, no picking); everything else delegates to `<img>`. The client loads on demand with a cached dynamic import. Sizing: `width` attr -> document hint -> 672x480, `max-width: 100%`, height via CSS `aspect-ratio`, so narrow columns and slides get a re-solved layout. Manifest miss -> "file not found" error box (no fetch); load failures -> error box. - `.hep` added to the binary-extension allowlists in `quarto-hub::resource` and `quarto-automerge-schema` (MIME `application/vnd.hephaestus.plot`), so the bytes sync into the preview VFS at all. - `optimizeDeps.exclude` for the client in hub-client and the SPA (dev pre-bundling breaks the glue's `import.meta.url` asset lookups). - Docs: preview note in the figures guide. Plan records decisions, findings, and the browser evidence for both hosts. Follow-ups filed: bd-9t5nmq81 (sandboxed frame), bd-qcltc0ll (brand colors need an upstream palette API). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflicts in the error catalog and the errors sidebar: main added an `engine` subsystem numbered 18 while this branch had numbered its `image` subsystem 18. The image codes move to `Q-19-1`..`Q-19-4` (catalog, docs pages renamed, sidebar, transform, tests, examples, preview component comments, plan). Both subsystems keep their pages. Co-Authored-By: Claude Fable 5.1 <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.
Summary
Phases 1 and 2 of hephaestus support (bd-3qych45b, plan:
claude-notes/plans/2026-09-17-hephaestus-hep-integration.md).Phase 1 —
q2 render(native)inhtml/revealjsoutput is rendered at render time with hephaestus's renderer-free SVG backend (no wgpu, no GPU), stored as a page-scoped artifact under<stem>_files/figure-html/, and the image is pointed at it. Size comes fromwidth/heightattrs, else the document's own hint, else 7in × 5in at 96 dpi; the plot re-flows at that size rather than being scaled.brand:declared, the light brand'sbackground/foreground/primarybecome the plot'spaper/ink/accent. Only hex values cross; anything else warns (Q-19-4).resources/hephaestus/fonts/, OFL) are registered once per process and mapped ontosans-serif, so the same.heprenders byte-identically on every machine and matches what hephaestus's browser client draws.imagediagnostics subsystem,Q-19-1..Q-19-4, each with a docs page and sidebar entry. Failures are warnings and leave the image as written. (Numbered 18 until the merge of main, where the newenginesubsystem had taken 18.)transforms/image_walk.rs: the mutable body-image walker extracted fromresponsive_imageso both transforms share one container set.examples/plots/01-hephaestus-basic(captioned figure, explicit size, missing-file case, dark brand).Phase 2 —
q2 previewand hub-client (bd-sxiv2tio)HepImagein@quarto/preview-rendereris the registry'sImageentry: for a local.heptarget it fetches the asset-walker bytes and mounts a livePlotViewfrom the npmhephaestus-svg-wasmclient (0.4.1, exact-pinned to the crate version; parity tests on both sides); anything else delegates to the plain<img>. Covers q2-preview documents andformat: revealjsdecks in bothq2 previewand hub-client, sinceRevealDeckrenders slide content through the same registry.widthattr → document hint → 672 px, capped at the column, height via CSSaspect-ratio, so narrow columns and slides get a re-solved layout while wide columns matchq2 render..hepadded to the binary-extension allowlists (quarto-hub::resourceand itsquarto-automerge-schemamirror) — without it the bytes never reached the preview VFS.Why SVG and not PNG
hephaestus rasterizes through wgpu, which needs a GPU adapter a headless render machine may not have, and GPU output is not byte-stable across machines. The SVG backend needs no GPU, keeps text as real
<text>, and is deterministic. PDF/Typst outputs plug into the same transform later (latex→ thepdfbackend,typst→ SVG) — bd-br9bmysi.Dependency notes
hephaestus = 0.4.1(document-read,svg,png) in the native-only table; bothCargo.locks pick up parley / fontique / skrifa / kurbo / peniko / clipper2-rust / png. All pure Rust. The main WASM bundle does not compile hephaestus.libfontconfigat build time by default. fontique'sfontconfig-dlopenfeature is enabled for Linux so building q2 does not requirelibfontconfig1-dev; a missing library at run time just yields an empty system font collection (fine — plot text uses the bundled faces).hephaestus-svg-wasm = 0.4.1(npm, MIT);optimizeDeps.excludein hub-client and the SPA keeps dev pre-bundling from breaking itsimport.meta.urlasset lookups.Test plan
crates/quarto-core/tests/integration/hephaestus_render.rs: end-to-end tests throughrender_to_file(artifact path, determinism, bundled font, sizing, id prefixes, revealjs, untouched non-hep images, missing / invalid file, brand applied / non-hex slot / no brand) + unit tests, incl. the npm-version parity test.HepImage.test.tsx(20 cases: mount, delegation, external/data targets, load-once, errors,free()on unmount, remount on new bytes, sizing rules, a11y attrs, registry layering, version pin vspackage.jsonandCargo.lock);resource.rs+fileType.test.tsfor the.hepextension.cargo xtask verifygreen (Rust + WASM + hub-client build/tests + SPA), before and after merging main.cargo run --bin q2 -- render examples/plots/01-hephaestus-basic, output inspected.q2 previewhtml document + revealjs deck; hub-client (q2 preview --ui editor)format: q2-previewdocument + revealjs deck. Plots mount as reflowed SVG; missing file shows the error box;.pngstays an<img>.🤖 Generated with Claude Code