Getting started
Install
bun add @adecore/diagramThe package has no peer dependencies. It brings Zod and @adecore/drawing. A store that only validates files imports @adecore/diagram/protocol, which loads Zod and the drawing protocol, without the layout or the renderer:
| Entry point | Holds |
|---|---|
@adecore/diagram/protocol | Zod schemas, types, migrateDiagram, diagramProblemIn |
@adecore/diagram | Layout, the SVG export, painter helpers and reading order |
Write a diagram
Start from EMPTY_DIAGRAM and list the nodes and edges. Nodes need no position:
import { EMPTY_DIAGRAM, diagramProblemIn, type DiagramDocument } from '@adecore/diagram/protocol';
let diagram: DiagramDocument = {
...EMPTY_DIAGRAM,
meta: { title: 'Review', direction: 'right' },
nodes: [
{ id: 'draft', label: 'Draft' },
{ id: 'review', label: 'Review', shape: 'diamond', tone: 'orange' },
{ id: 'merged', label: 'Merged', shape: 'pill', tone: 'green' }
],
edges: [
{ from: 'draft', to: 'review' },
{ from: 'review', to: 'merged', label: 'approved' }
]
};
const problem = diagramProblemIn(diagram);
// nulldiagramProblemIn checks what the schema cannot, such as an edge to a node that does not exist.
Change it
A change is a new document with rev one higher. Add an edge back from review to draft:
diagram = {
...diagram,
rev: diagram.rev + 1,
edges: [...diagram.edges, { from: 'review', to: 'draft', label: 'changes', style: 'dashed' }]
};Check it again before you store it. An edge to an unknown id comes back as a sentence:
diagramProblemIn({ ...diagram, edges: [...diagram.edges, { from: 'review', to: 'deploy' }] });
// 'The edge from "review" to "deploy" names "deploy", which is not a node'Export it
import { layoutOf, readingOrder, toSvg } from '@adecore/diagram';
const layout = layoutOf(diagram);
const svg = toSvg(diagram, { layout, background: '#ffffff' });
const lines = readingOrder(diagram);layoutOf puts draft, review and merged in columns 0, 1 and 2. The edge back to draft closes a cycle; the layout ranks without it and still draws it. Pass the layout to toSvg so toSvg does not compute it twice. readingOrder returns:
Draft
Review
Merged
Draft -> Review
Review -> Merged: approved
Review -> Draft: changesRead it back
import { diagramProblemIn, migrateDiagram } from '@adecore/diagram/protocol';
const loaded = migrateDiagram(JSON.parse(file));
const problem = loaded && diagramProblemIn(loaded);
if (loaded === null || problem) {
throw new Error(problem ?? 'Not a diagram');
}Checklist
- Every file goes through
migrateDiagramanddiagramProblemIn, and the problem goes back to whoever wrote it. - Every saved change raises
revby one. - A cached layout belongs to one revision, and a change computes it again.
- An export outside the page gets plain colors in its palette, not CSS variables.