@expanse-ade/diagram
v0.1.0
Published
Structured spec-diagram engine from Canvas ADE — typed DiagramSpec model + validator, ELK layered layout, token-driven static React renderer.
Readme
@expanse-ade/diagram
The structured spec-diagram engine extracted from Canvas ADE —
a typed DiagramSpec model + deep validator, ELK layered layout, and a token-driven,
pointer-inert static React renderer. Pure React + ELK: no Electron, PTY, or Mermaid surface.
What's inside
| Module | Surface |
|---|---|
| diagramSpec | DiagramSpec / SpecNode (incl. rows) / SpecEdge / SpecGroup types, closed status/kind vocabularies, structural caps (200 nodes / 400 edges / 12 rows), assertDiagramSpec + assertDiagramRevisions deep validators (injected-guard contract) |
| specLayout | deterministic spec → ELK-graph mapping, ELK-result → positioned layout, facing-edge beziers, specHitTest |
| specElk | lazy off-thread ELK singleton (host-injected worker; in-thread elk.bundled fallback) |
| useSpecLayout | async spec → layout React hook with a WeakMap identity cache |
| specCollapse | pure group-collapse spec→spec transform (chip pseudo-nodes) |
| specTheme | closed vocabularies → CSS-custom-property-driven styles (statuses, silhouettes, kind marks, presets calm / graphite / signal) |
| SpecNodeBody / DiagramSpecView | the shared node interior + the static renderer (SVG edge layer under absolutely-positioned token-styled divs) |
Subpaths:
@expanse-ade/diagram— the full React surface.@expanse-ade/diagram/spec— ONLY the pure spec module (types + validator + caps); safe for non-React / main-process consumers.@expanse-ade/diagram/styles.css— the motion/dim/ghost stylesheet (pl-spec-*classes).
Usage
import {
assertDiagramSpec,
DiagramSpecView,
useSpecLayout,
type DiagramSpec
} from '@expanse-ade/diagram'
import '@expanse-ade/diagram/styles.css'
function Card({ spec }: { spec: DiagramSpec }) {
const { layout, error } = useSpecLayout(spec)
return <DiagramSpecView spec={spec} w={600} h={380} motion layout={layout} error={error} />
}Host seams (optional)
import { configureSpecElkWorker, registerSpecIconRenderer } from '@expanse-ade/diagram'
// Off-thread layout: inject your bundler's worker build of elkjs/lib/elk-worker.
// (Vite shown; without this the engine runs in-thread via elk.bundled — correct, just on-thread.)
import ElkWorker from 'elkjs/lib/elk-worker.min.js?worker'
configureSpecElkWorker(() => new ElkWorker())
// Host icons for SpecNode.icon (names gated by the closed SPEC_ICON_NAMES vocabulary).
// Unregistered ⇒ the node's kind glyph renders instead.
registerSpecIconRenderer((name, { size, style }) => <MyIcon name={name} size={size} style={style} />)Theming
Everything reads CSS custom properties live off :root — --accent, --surface,
--surface-raised, --border, --border-strong, --text / --text-2 / --text-3 /
--text-faint, --ok / --warn / --err (+ -wash variants), --ui, --mono,
--r-inner / --r-pill / --r-board / --r-ctl. Hardcoded colours appear only as fallbacks.
Security contract
Every spec string renders as a React text node — no innerHTML, no markup interpolation.
assertDiagramSpec enforces slug ids, closed vocabularies, structural caps, and referential
integrity (dangling refs are rejected).
Peer dependencies
react ≥ 18 and elkjs ≥ 0.9. (@xyflow/react is NOT required — the focus-mode editor stayed
host-side; a /editor subpath may follow.)
License
MIT
