Architecture
How YAML becomes an interactive diagram — the compile pipeline, stage by stage.
Marrow is a compiler with a browser runtime attached. Text goes in, a validated model comes out, geometry is computed and checked, and two renderers turn it into either SVG or canvas inside a self-contained page.
The pipeline
Stage by stage
schema — text to model
One validation code path. YAML is parsed with positions preserved so every
diagnostic can point at an exact line and column. Validation is strict: unknown
keys are errors, x- prefixed keys are preserved and ignored. Output is the
normalized Topology — the single in-memory shape every downstream module
consumes.
layout — model to geometry
Classify picks one of six archetypes; declared structure (zones, tiers)
always outranks inferred shape. Size measures each node at the exact pixel
size and letter-spacing the renderers use, via static glyph tables in
@marrow/metrics — measurement must be deterministic and identical across
machines, which rules out asking a browser.
Place runs the engine that suits the archetype: ELK layered for zoned, tiered, and flat; a compose pass to pack zone mosaics; polar for hub-spoke; circular for rings and meshes. Radial presets allocate arc space from actual node widths, so they're overlap-free by construction.
Route uses libavoid for orthogonal routing, polylines for radial shapes (orthogonal channels look wrong on a ring).
Tidy applies cable-tray discipline: merge same-pair parallels onto one visible stroke, pull fan-outs onto shared gutter lanes, order draws so alarms sit above healthy runs. On a real HQ zone this took 165 links down to 36 unique geometries.
scene — the contract
Boxes and polylines, plus geometry helpers. Everything downstream speaks this and nothing else. Port chips are scene geometry, placed collision-free by the layout stage; renderers draw them verbatim rather than inventing positions.
lint — the gate
The geometry linter runs before anything renders. L1 defects fail the build: node overlap on label-inclusive boxes, edge segments through foreign node interiors, endpoints not touching their node's box, port-chip overlap. L2 auto-repairs; L3 advises.
render-svg and canvas-engine — geometry to pixels
Two renderers, one visual language. SVG is the hero tier — full fidelity, crisp at any zoom, SVG export. The canvas host is the scale tier, used when the node count would make SVG unusable. Every visual change must land in both, plus the shared stylesheet.
Shared conventions worth knowing before touching either:
- Corner-anchored indicators. Status LED and badge sit centered on the chip's corner arc at 45°, offset
r·(1−1/√2). Both get a 2 px background halo ring. - Background-halo knockout. Anything an edge may cross — node labels, zone titles — uses
paint-order: stroke; stroke: var(--bg)in SVG andstrokeTexton canvas, and sits in a layer above the edges. A crossing link is knocked out cleanly instead of slicing through letters. - Measure what you draw. SVG pins labels with
textLength; canvas condenses (never stretches) using a cachedmeasureText.
site — pages and packaging
Collapse decides what's summarized. The planner decides single file versus multi-page site and which pages exist. The compiler inlines styles, the runtime, and the icon sprites actually used. Page builds are content-hashed and memoized, so a rebuild only touches pages whose inputs changed.
runtime — the client
About 20 KB: camera, inspector, spotlight, minimap, search, export, command palette. The chrome is shadcn/ui ported to vanilla CSS and TypeScript — never installed as components, because output must stay dependency-free.
server and cli
The dev server watches, rebuilds, and pushes live reloads over SSE, with a small JSON API. The CLI is the user-facing surface: validate, build, serve, snapshot, import, icons, schema.
Design invariants
These are the properties the whole design serves. Breaking one is a bug, not a trade-off:
- Determinism. Same input, byte-identical output.
- Self-containment. No network requests when a diagram opens.
- Declared meaning. Nothing is inferred from names.
- Machine-readable diagnostics. Every error has a stable code and a fix hint.
- Overlap-free geometry. Enforced by the linter, not by hope.