feat(ui): copy the flow or the map as an image, for a PR comment or a README (CG-55)
"Copy image" and "Download SVG" on the Flow strip's header and in the Map's side panel. The image is the distribution loop: a flow pasted into a review, a map pasted into a README, read by somebody with no viewer open. The exporter serialises the LAYOUT OBJECT rather than scraping the DOM — no html-to-image, no foreignObject, no new dependency. buildFlowLayout and buildMapLayout already compute every rectangle, port and curve before a component renders, so the image and the screen come from one piece of arithmetic and cannot drift apart, and the whole exporter is a pure function a test runs with no browser. Output is presentation-only SVG (rect, line, path, polygon, text, tspan, clipPath) — no script, no external reference, no data: URL — which is what GitHub's sanitiser accepts in a README. Light theme is forced whatever the viewer is set to: a dark strip on GitHub's white comment background reads as a mistake, not a preference. 24px of paper around the drawing, a caption naming the path or the root at the bottom left, a CodeGraph mark at the bottom right. Fonts travel as family stacks, not bytes (spec). An SVG loaded as an image may not fetch a webfont, so a raster falls back to the platform's own monospace — every fallback in the stack advances at ~0.6em like IBM Plex Mono, so the code grid survives and only the letterforms change. Text is truncated arithmetically with an ellipsis and clipped as well, so a wider fallback cannot spill a source line out of a card. `scale` multiplies only the root width/height while the viewBox stays in CSS pixels, so the raster draws an image whose intrinsic size is already 2x instead of upscaling a 1x bitmap. The clipboard write uses the ClipboardItem promise form (Safari discards the gesture across an await) and falls back to downloading the PNG, saying which happened rather than claiming a copy it did not make. Measured on this repo: execute -> rowToFileRecord (8 hops) exports 3690x253 CSS px, 491 kB PNG at 2x / 38 kB SVG; the 16-module map reproduces the canvas exactly — 16 boxes, 52 links, 9 layer rules, both band labels, and with src/index.ts selected 15 links and 4 dimmed boxes.
This commit is contained in:
+24
-1
@@ -46,9 +46,11 @@ src/
|
||||
lib/flow-model.ts the Flow strip's card/link geometry + the end cap — a DAG (pure)
|
||||
lib/filecode-model.ts the whole-file view: fixed line height, arcs, paging (pure)
|
||||
lib/entry-model.ts the entry-points panel: rows, file groups, flow arming (pure)
|
||||
lib/export-svg.ts the Flow strip and the Map as a standalone SVG (pure)
|
||||
lib/export-image.ts rasterising that SVG to PNG, clipboard and download
|
||||
lib/live.svelte.ts /api/events: two counters every screen refreshes from
|
||||
lib/toast.svelte.ts the one transient note ("Index updated · reloaded")
|
||||
components/ TopBar, TrailBar, KindGlyph, DriftBanner, Toast, map/, flow/, symbol/, file/, entry/
|
||||
components/ TopBar, TrailBar, KindGlyph, DriftBanner, Toast, ExportButtons, map/, flow/, symbol/, file/, entry/
|
||||
views/ one component per route
|
||||
```
|
||||
|
||||
@@ -56,6 +58,27 @@ Fonts (Archivo Variable, IBM Plex Mono) are vendored through `@fontsource*` and
|
||||
emitted into `dist/viewer/assets`: a local reader must work offline and must not
|
||||
announce the project to a font CDN.
|
||||
|
||||
## Export
|
||||
|
||||
The Flow strip's header and the Map's side panel carry **Copy image** (a PNG on
|
||||
the clipboard) and **Download SVG** (a file for a README). Both render the
|
||||
**light** theme whatever the viewer is set to — an image is read on somebody
|
||||
else's screen — with 24px of paper around the drawing, a caption naming the path
|
||||
or the root, and a "CodeGraph" mark in the corner.
|
||||
|
||||
`export-svg.ts` **serialises the layout object**; it does not scrape the DOM.
|
||||
`buildFlowLayout` and `buildMapLayout` already compute every rectangle, port and
|
||||
curve before a component renders, so the image and the screen come from one
|
||||
piece of arithmetic and cannot disagree — and the exporter is a pure function
|
||||
that a test can run with no browser at all. The output is presentation-only SVG
|
||||
(no script, no `foreignObject`, no external reference), which is what GitHub
|
||||
will render in a README.
|
||||
|
||||
Fonts travel as `font-family` stacks rather than embedded bytes. An SVG loaded
|
||||
as an image may not fetch a webfont, so a raster falls back to the platform's
|
||||
own monospace; every fallback in the stack advances at ~0.6em like IBM Plex
|
||||
Mono, so the code grid survives and only the letterforms change.
|
||||
|
||||
## Routes
|
||||
|
||||
| hash | view |
|
||||
|
||||
Reference in New Issue
Block a user