@weasel-js/diagram-formats 1.15.0 → 1.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,9 +17,19 @@ npm install @weasel-js/diagram-formats
17
17
  ## The interchange document
18
18
 
19
19
  A `DiagramDocument` is a diagram's pages and its theme. Each page has an id,
20
- an optional name, a sheet size, and a `DiagramData` whose places, boxes,
21
- waypoints, and label points run from the page's top-left corner. Ids share one
22
- namespace across the document. Edges whose ends are on different pages sit in
20
+ an optional name, a sheet size, and a `kind` saying what it holds. A `'graph'`
21
+ page holds a `DiagramData` whose places, boxes, waypoints, and label points run
22
+ from the page's top-left corner, and may name its `notation` (`'class'`,
23
+ `'state'`, …) for a format that writes graphs in several notations. A `'sequence'`
24
+ page holds a `SequenceData` (see `@weasel-js/diagram`), which carries no places:
25
+ it is laid out whenever it is drawn, and its `name` is the diagram's title. A
26
+ `'plot'` page holds one chart, a `PlotData` from `@weasel-js/plot`, which fills
27
+ the page. Ids share one namespace across the document. Every format lists the
28
+ page `kinds` it holds; a writer leaves out a page of another kind with a
29
+ warning, and `formatHolds` says whether a format can write a whole document.
30
+ `scenePageKinds(scene)` says which kinds a scene would export as from its
31
+ top-level nodes alone, and `formatTakes(format, kinds)` whether a format holds
32
+ them: what a menu asks on every render. Edges whose ends are on different pages sit in
23
33
  `crossPageEdges`, with no waypoints, since they draw as two stubs and their
24
34
  off-page tags.
25
35
 
@@ -38,17 +48,39 @@ It keeps the colors the scene's theme would not give: those the author set by
38
48
  hand (the trait's `ownStyle`) and any that differ from the theme, as `colors`.
39
49
  A route or a port that layout chose, and an avoiding connector's computed
40
50
  route, are read as `routed` and `routedPorts`, not as waypoints, so they stay
41
- layout's. `theme` is what colors are compared against when the scene names
42
- none.
51
+ layout's. An edge's end labels read back as `endLabels`, and a `field` row as
52
+ a field while its text still starts with its label. `theme` is what colors are
53
+ compared against when the scene names none. A page holding a plot root (one
54
+ `plotSpecs` made) is a plot page of the spec the root holds, edits included;
55
+ anything else on that page is left out. A graph page takes the `notation`, `layout`, and
56
+ `direction` its `core:pages` page keeps under `meta['diagram:graph']`
57
+ (`GRAPH_PAGE_META_KEY`, read with `graphPageSettingsOf`), where `documentSpecs`
58
+ writes them.
43
59
 
44
60
  `documentSpecs(doc, { layer, theme, measure })` is the way back. Pages are
45
61
  placed with core's `layoutPages`, as `core:pages` places them. A page where a
46
- node has no `at`, or a pool has no box, is laid out first with `layered`,
47
- running the way its `direction` says, with whatever it places pinned; the
62
+ node has no `at`, or a pool has no box, is laid out first with the layout its
63
+ `layout` names (`{ name, options }`, a key of `LAYOUTS`, default `layered`), running the way its `direction` says, with whatever it places pinned; the
48
64
  result is kept as places and routes, a page that placed nothing is moved to sit
49
65
  40 inside its corner, and the page grows to hold it. Then one `diagramScene`
50
66
  call with no layout draws every page and the edges between them, so no layout
51
- runs again after an import.
67
+ runs again after an import. A sequence page is drawn by `sequenceSpecs` as one
68
+ root holding its data, 40 inside its corner, and the page grows to hold it;
69
+ `documentFromScene` reads a page holding such a root back as a sequence page and
70
+ leaves out its parts. A plot page is one plot root filling the page, its
71
+ id `<page id>/plot`, its marks under it, drawn in the theme's roles: text in the
72
+ label color, axes in the connector stroke, grid lines in the container stroke,
73
+ series in the theme's `series` (`plotThemeOf`).
74
+
75
+ A graph page with no `notation` of its own takes the one its scene's diagram type writes (`diagramNotationOf`), and
76
+ `documentSpecs` sets `diagram:type` when every graph page's notation names one type, so an imported class diagram
77
+ opens with the UML stencils.
78
+
79
+ `PAGE_TEMPLATES` holds a new page of every kind: an empty graph page per diagram type, in its notation; a small
80
+ sequence diagram; and a small chart of each plot kind, each with the `group` a menu lists it under. `templateSpecs`
81
+ draws one as a new one-page document, the diagram type it names included, and `addTemplatePage(scene, template)`
82
+ adds one as another page, what it draws with it, as one undo entry. `pageSpecs` draws any one page placed where a
83
+ page list would place it.
52
84
 
53
85
  ## Formats
54
86
 
@@ -66,9 +98,9 @@ with it and rejects on a file nothing reads.
66
98
 
67
99
  | Format | Extensions | Reads | Writes |
68
100
  | --- | --- | --- | --- |
69
- | `DIAGRAM_JSON`, the document as JSON | `.diagram.json` | yes | the whole document |
101
+ | `DIAGRAM_JSON`, the document as JSON | `.diagram.json` | yes, graph and plot pages | the whole document |
70
102
  | `DRAW_XML`, draw-style XML | `.xml` | yes, plain or compressed | every page, uncompressed |
71
- | `DIAGRAM_MERMAID`, a Mermaid flowchart | `.mmd`, `.mermaid` | yes | one page |
103
+ | `DIAGRAM_MERMAID`, a Mermaid flowchart, class, state, ER, mind map, requirement, block, architecture, C4, kanban, or sequence diagram, or a chart | `.mmd`, `.mermaid` | yes | one page, a graph, a sequence, or a chart |
72
104
  | `DIAGRAM_DOT`, a Graphviz DOT graph | `.dot`, `.gv` | yes | one page |
73
105
 
74
106
  Not read, by decision (2026-10-20): packaged office-suite drawing formats,
@@ -87,13 +119,13 @@ The file is known by its root element, not its `.xml` extension.
87
119
 
88
120
  | Diagram | Draw-style XML |
89
121
  | --- | --- |
90
- | Outline | `rhombus` diamond, `ellipse`, `hexagon`, `parallelogram`, `trapezoid`; `rounded=1` with `arcSize` 50 or more is a stadium; any other shape reads as a rect, with a warning |
122
+ | Outline | `rhombus` diamond, `ellipse`, `hexagon`, `parallelogram`, `trapezoid`; `startState`, `endState`, and `line` as `start`, `end`, and `bar`; `rounded=1` with `arcSize` 50 or more is a stadium; any other shape reads as a rect, with a warning |
91
123
  | Colors and font | `fillColor` (`none` is transparent), `strokeColor`, `fontColor`, `strokeWidth`, `dashed`; `fontSize`, `fontFamily`, and bold in `fontStyle` as label rows' style |
92
124
  | Text | `value`; with `html=1`, tags stripped and `<br>` and blocks kept as line breaks. Written plain. |
93
125
  | Router | `edgeStyle=orthogonalEdgeStyle` orthogonal, `curved=1` bezier, else straight; `avoid` writes as orthogonal along its route |
94
126
  | Route | the edge geometry's points: `waypoints`, else `routed`, read back as `waypoints` |
95
127
  | Ports | `exitX`/`exitY` and `entryX`/`entryY`, read as the nearest compass port |
96
- | Edge label | the edge's `value`, or a label cell on it; its geometry `x` (-1 to 1) is where along, `y` how far off the line |
128
+ | Edge label | the edge's `value`, or a label cell on it; its geometry `x` (-1 to 1) is where along, `y` how far off the line. A label cell at `x` -1 or 1 is an end label (`endLabels`) |
97
129
  | Arrowheads | `startArrow`/`endArrow` and their `Fill` |
98
130
  | Container, pool, lane | `container=1` or a `swimlane` is a container; a swimlane whose swimlanes span its body is a pool of lanes, stacked in rows with `horizontal=0` |
99
131
  | Group | a `group` cell around its members |
@@ -130,6 +162,7 @@ cannot read.
130
162
  | parallelogram | `A[/text/]` | `shape=parallelogram` |
131
163
  | trapezoid | `A[/text\]` | `shape=trapezium` |
132
164
  | edge and label | `A -->\|text\| B`, `A -- text --> B` | `A -> B [label=…]` |
165
+ | end labels | — | `taillabel`, `headlabel` |
133
166
  | no arrowhead, dashed, thick | `---`, `-.->`, `==>` | `dir`, `arrowhead`, `style=dashed`, `penwidth` |
134
167
  | container | `subgraph` | `subgraph cluster_*` |
135
168
  | page name | front matter `title` | the graph's `label`, else its name |
@@ -137,8 +170,7 @@ cannot read.
137
170
  | colors | `classDef`, `class`, `:::`, `style`, `linkStyle` | `fillcolor` with `style=filled`, `color`, `fontcolor` |
138
171
  | ports | — | `node:compass`, `n`, `e`, `s`, and `w` only |
139
172
 
140
- A Mermaid shape with no outline here reads as a rect, with a warning; a
141
- Mermaid diagram of another kind is refused with an error naming the kind. DOT
173
+ A Mermaid shape with no outline here reads as a rect, with a warning. DOT
142
174
  reads its whole grammar: `strict`, `graph` and `digraph`, attribute
143
175
  statements and their scoping, edge chains, and subgraphs as edge ends. Its
144
176
  HTML labels read as their text, and the attributes it does not read are
@@ -154,6 +186,197 @@ ids that are keywords or not plain names are renamed, keeping their text.
154
186
  Bends, connectors to other pages, drawn outlines, and ports a format lacks
155
187
  are dropped with a warning.
156
188
 
189
+ ### Mermaid's other kinds
190
+
191
+ `DIAGRAM_MERMAID` reads a diagram by its header and sets the page's `notation`
192
+ to the kind it read; it writes a graph page in the kind its `notation` names,
193
+ and as a flowchart when it names none (or one Mermaid has not, with a
194
+ warning). Every kind reads front matter's `title` as the page name (else
195
+ `accTitle`), skips `%%` comments, and drops `%%{…}%%` directives and
196
+ `accDescr` with a warning. A Mermaid kind not read here (`zenuml`,
197
+ `treemap`, …) is refused with an error naming it. Each reader throws an error
198
+ naming the line on text it cannot read, and warns of what it drops.
199
+
200
+ `classDiagram`, notation `'class'`:
201
+
202
+ | Mermaid | Diagram |
203
+ | --- | --- |
204
+ | `class A`, `class A~T~`, `class A["Label"]` | a box: `<<annotation>>` rows, the name row (`A<T>`), a divider, attribute rows, a divider, method rows |
205
+ | members in `class A { … }`, or `A : +member` | a member with parentheses is a method |
206
+ | `<<interface>> A`, or inside the braces | an annotation row |
207
+ | `<\|--` `*--` `o--` `-->` `--` | `triangle-hollow`, `diamond`, `diamond-hollow`, `arrow`, none, at the end the head is on |
208
+ | `..>` `..\|>` `..` | dashed, with `arrow-open`, `triangle-hollow`, none |
209
+ | `A "1" --> "*" B : label` | `endLabels` `{ from: '1', to: '*' }`, and the label |
210
+ | `namespace N { … }` | a container |
211
+ | `note "text"`, `note for A "text"` | a `note` shape, joined to `A` by a dashed unmarked edge |
212
+ | `direction LR` | the page's `direction` |
213
+ | `style`, `classDef`, `cssClass`, `:::` | colors |
214
+
215
+ `stateDiagram-v2` and `stateDiagram`, notation `'state'`:
216
+
217
+ | Mermaid | Diagram |
218
+ | --- | --- |
219
+ | `A`, `state "Name" as A`, `A : description` | a `roundRect` with the name, then each description, as lines |
220
+ | `[*]` | a `start` where a transition leaves it and an `end` where one arrives, one each per scope |
221
+ | `state A <<fork>>`, `<<join>>`, `<<choice>>` | a `bar` across the way the page runs, and an empty diamond |
222
+ | `A --> B : label` | an edge and its label |
223
+ | `state X { … }` | a container; divided by `--` into concurrent regions, a pool with a lane per region |
224
+ | `note right of A : text`, `note left of A … end note` | a `note` shape joined to `A`; the side is dropped |
225
+ | `classDef`, `class`, `:::` | colors |
226
+
227
+ `erDiagram`, notation `'er'`:
228
+
229
+ | Mermaid | Diagram |
230
+ | --- | --- |
231
+ | `A`, `A["Label"]` | a box of its name row |
232
+ | `A { type name PK, FK "comment" }` | a divider, then a `field` row per attribute: the type as its label; the name, keys, and comment as its value |
233
+ | `\|o` `\|\|` `}o` `}\|` (and mirrored) | `crow-zero-one`, `crow-one`, `crow-zero-many`, `crow-one-many` at that end |
234
+ | `--`, `..` | solid, dashed |
235
+ | `A \|\|--o{ B : label` | the label |
236
+ | `style`, `classDef`, `class` | colors |
237
+
238
+ `mindmap`, notation `'mindmap'`:
239
+
240
+ | Mermaid | Diagram |
241
+ | --- | --- |
242
+ | an indented line | a node, joined to the line it is indented under by an unmarked edge |
243
+ | `text`, `id[text]`, `id(text)`, `id((text))`, `id))text((`, `id)text(`, `id{{text}}` | no outline of its own, `rect`, `roundRect`, `ellipse`, `cloud`, `cloud`, `hexagon` |
244
+ | no id | `n` and its place in the file: `n2`, `n3`, … |
245
+ | the whole map | laid out by `radial`, the root in the middle and its branches to both sides (the page's `layout`) |
246
+ | `::icon(…)`, `:::class` | dropped, with a warning |
247
+
248
+ `requirementDiagram`, notation `'requirement'`:
249
+
250
+ | Mermaid | Diagram |
251
+ | --- | --- |
252
+ | `requirement name { … }`, its five other kinds, and `element name { … }` | a box: a `<<kind>>` row, the name row, a divider, a `field` row per `key: value` |
253
+ | `a - satisfies -> b`, `b <- satisfies - a` | a dashed arrow from `a` to `b` labeled `<<satisfies>>` |
254
+ | `style`, `classDef`, `class` | colors |
255
+
256
+ `block-beta`, notation `'block'`, laid out by `grid` (the page's `layout`):
257
+
258
+ | Mermaid | Diagram |
259
+ | --- | --- |
260
+ | `a`, `a["text"]`, and the flowchart's other brackets | a node: its text (else its id) in the flowchart's outline for those brackets |
261
+ | `columns 3` | the grid's `columns`, or a group's under `groupColumns`; `columns auto` leaves it unset |
262
+ | `a:2`, `block:g:2` | `spans` |
263
+ | `space`, `space:2` | `skips` on the next block; spaces after a level's last block are dropped, with a warning |
264
+ | `block:g … end`, `block … end` | a container holding its blocks in order; an unnamed one is `block-1`, `block-2`, … |
265
+ | `a --> b`, `a --- b`, `a -.-> b`, `a ==> b`, `a <--> b`, `a -- "text" --> b` | an edge: its heads, dashed or 3 wide, and its label |
266
+ | `style`, `classDef`, `class` | colors |
267
+ | `a<["text"]>(right)` | a rect, its direction dropped with a warning |
268
+
269
+ `architecture-beta`, notation `'architecture'`, laid out running right:
270
+
271
+ | Mermaid | Diagram |
272
+ | --- | --- |
273
+ | `service a(icon)[Title] in g` | a node of its title (else its id): `cloud`, `database`, `disk`, `internet`, and `server` as the `cloud`, `cylinder`, `disk`, `globe`, and `server` outlines; no icon, or any other with a warning, as `roundRect` |
274
+ | `junction j in g` | a 12-wide `start` dot with no text |
275
+ | `group g(icon)[Title] in parent` | a container titled with its title; its icon is dropped with a warning |
276
+ | `a:R --> L:b`, `--`, `<--`, `<-->` | an edge leaving `a`'s `e` port and meeting `b`'s `w` (`L`, `R`, `T`, `B` as `w`, `e`, `n`, `s`), with its heads |
277
+ | `a{group}:R --> L:b` | an edge from `a`'s group, meeting its box where layout chooses |
278
+
279
+ `C4Context`, `C4Container`, `C4Component`, `C4Dynamic`, and `C4Deployment`, notations `'c4-context'` through `'c4-deployment'`:
280
+
281
+ | Mermaid | Diagram |
282
+ | --- | --- |
283
+ | `Person`, `System`, `Container`, `Component`, their `Db` and `Queue` kinds, and each one's `_Ext` | a node 200 wide: a `<<kind>>` row (`<<external_system_db>>`), its name in bold, a container's or component's `[technology]`, and its description wrapped; outline `person`, `cylinder`, `queue`, or `roundRect`; role `muted` when external, else `accent` |
284
+ | `Enterprise_Boundary`, `System_Boundary`, `Container_Boundary`, `Boundary`, `Deployment_Node`, `Node` | a container titled `Name [enterprise]`, `[system]`, `[container]`, or the type it gives |
285
+ | `Rel(a, b, "label", "technology")`, `BiRel`, `Rel_Back` | an edge labeled `label [technology]`; heads at both ends; a head at `a` only |
286
+ | `Rel_U`, `Rel_D`, `Rel_L`, `Rel_R` | ports: `n` to `s`, `s` to `n`, `w` to `e`, `e` to `w` |
287
+ | `UpdateElementStyle(a, $bgColor, $fontColor, $borderColor)`, `UpdateRelStyle(a, b, $textColor, $lineColor)` | colors |
288
+ | `title` | the page name |
289
+ | sprites, tags, links, a relationship's description, offsets, and layout settings | dropped, with a warning |
290
+
291
+ Written back, a box with no `<<kind>>` row is a system, a person, store, or queue by its outline, and external when its role is `muted`.
292
+
293
+ `kanban`, notation `'kanban'`:
294
+
295
+ | Mermaid | Diagram |
296
+ | --- | --- |
297
+ | the board | one pool, `board`, whose lanes run along `x`, sized to hold its longest column |
298
+ | `todo[Todo]`, a line at the shallowest indent | a lane 220 wide; one with no id is `column-` and its place |
299
+ | `id[text]`, a line indented under it | a node 180 wide in that lane: its text wrapped into rows; one with no id is `card-` and its place |
300
+ | `@{ ticket: MC-1, assigned: 'kim', priority: 'High' }` | a `field` row per pair |
301
+
302
+ Writing a kind writes what its reader reads, so text read and written reads
303
+ back as the same page. A page from elsewhere writes as near as the kind
304
+ allows, with a warning for each thing it cannot carry: an end a class diagram
305
+ has no head for is an arrow, one an ER diagram has no crow's foot for is
306
+ exactly one, an unlabeled relationship is `traces`, and a mind map keeps the
307
+ first root's tree.
308
+ ### Mermaid xychart
309
+
310
+ `xychart-beta`, or `xychart`, reads onto one 800 by 500 plot page holding an
311
+ `xychart` (`readMermaidXychart`), and a plot page holding one writes back as
312
+ `xychart-beta` (`writeMermaidXychart`). Errors name the line.
313
+
314
+ | `XyChartData` | Mermaid |
315
+ | --- | --- |
316
+ | `orientation: 'horizontal'` | `xychart-beta horizontal` |
317
+ | `title`, and the page name | `title "…"`, else the front matter's `title` |
318
+ | `x`, a band axis | `x-axis "title" [a, "b c"]` |
319
+ | `x`, a range axis | `x-axis "title" 1 --> 10` |
320
+ | `y` | `y-axis "title" 0 --> 100`; one end alone is dropped on writing, with a warning |
321
+ | a series | `bar "name" [1, 2]`, `line [1, 2]` |
322
+
323
+ Reading drops other front matter, `%%{init}%%` directives, `accTitle` and
324
+ `accDescr`, with a warning each, and warns of values past the last category.
325
+
326
+ ### Mermaid charts
327
+
328
+ Each of these reads onto one plot page holding the chart kind named, and a
329
+ plot page holding that kind writes back as it; the writer picks the Mermaid
330
+ kind from the chart's kind. Each reader names the line of an error (a score
331
+ out of range, a field that overlaps another, a checkout of a branch that does
332
+ not exist) and warns of what it drops; each writer warns of what Mermaid
333
+ cannot hold.
334
+
335
+ | Mermaid | Chart | What is read |
336
+ | --- | --- | --- |
337
+ | `gantt` | `gantt` | `title`, `dateFormat`, `axisFormat`, `excludes`, `todayMarker`, `section`; tasks `Name :tags, id, start, end` with `done`/`active`/`crit`/`milestone`, a start that is a date or `after a b`, an end that is a date, a duration (`3d`, `2w`, `5h`, `1M`, …) or `until a`, each kept as written |
338
+ | `timeline` | `timeline` | `title`, `section`, `period : event : event`, and `: event` lines continuing the period above |
339
+ | `pie` | `pie` | `pie showData title …`, `title`, `"label" : value` |
340
+ | `quadrantChart` | `quadrant` | `title`, `x-axis Low --> High`, `y-axis`, `quadrant-1` to `quadrant-4`, points `Name: [x, y]` with `:::class` and `radius`, `color`, `stroke-color`, `stroke-width`, `classDef` |
341
+ | `journey` | `journey` | `title`, `section`, `Task: score: Actor, Actor` |
342
+ | `gitGraph` | `gitgraph` | `gitGraph LR:`/`TB:`/`BT:`, `commit` (`id`, `type`, `tag`, `msg`), `branch` (`order`), `checkout`/`switch`, `merge` (`id`, `type`, `tag`), `cherry-pick` (`id`, `tag`, `parent`); the title from front matter |
343
+ | `sankey-beta` | `sankey` | CSV `source,target,value` lines, quoted as CSV quotes; the title from front matter |
344
+ | `packet-beta` | `packet` | `title`, `0-15: "label"`, `16: "label"`, `+8: "label"` |
345
+ | `radar-beta` | `radar` | `title`, `axis a["Label"], b`, `curve c["Label"]{1, 2}` and `curve c{ a: 1, b: 2 }`, `max`, `min`, `graticule`, `ticks`, `showLegend` |
346
+
347
+ Dropped on reading, with a warning: Gantt `click`, `call`, `inclusiveEndDates`,
348
+ `topAxis`, `tickInterval`, `weekday`, `displayMode`, and `vert` tasks; a
349
+ timeline's direction; configuration such as a packet's bits per row or a git
350
+ graph's main branch name. Gantt dates are read and written in UTC.
351
+
352
+ ### Mermaid sequence diagrams
353
+
354
+ A `sequenceDiagram` reads into one sequence page, and a sequence page writes
355
+ back as one. The reader throws an error naming the line on what it cannot read,
356
+ including an `end` that closes nothing and a fragment never closed.
357
+
358
+ | Sequence | Mermaid |
359
+ | --- | --- |
360
+ | participant and label | `participant A as Text`, `actor A` |
361
+ | outline | `participant A@{ "type" : "boundary" }`: `boundary`, `control`, `entity`, `database` (the cylinder), `collections`, `queue` |
362
+ | message `line`, `head`, `both` | `->`, `-->`, `->>`, `-->>`, `-x`, `--x`, `-)`, `--)`, `<<->>`, `<<-->>` |
363
+ | `activate: 'to'`, `deactivate: 'from'` | `A->>+B`, `B-->>-A` |
364
+ | activate, deactivate | `activate A`, `deactivate A` |
365
+ | note | `Note left of A`, `Note right of A`, `Note over A,B` |
366
+ | fragment | `loop`, `alt`/`else`, `opt`, `par`/`and`, `critical`/`option`, `break`; `rect <color>` is a `region` |
367
+ | box | `box <color> Label` … `end` |
368
+ | create, destroy | `create participant B`, `destroy B` |
369
+ | `autonumber` | `autonumber`, `autonumber 10` |
370
+ | page name | `title`, or the front matter's `title` |
371
+
372
+ A participant a message or note names before any declaration is added in the
373
+ order it is first named. Configuration, accessibility text, participant menus
374
+ (`link`, `links`, `properties`, `details`), and an autonumber step other than 1
375
+ are dropped with a warning. Writing, colors and roles on participants and
376
+ fragments, a region's label, and a second head on a message whose head is not
377
+ an arrow are dropped with a warning; an activation Mermaid has no shorthand for
378
+ is written as its own `activate` or `deactivate` line.
379
+
157
380
  ## Exporting a page
158
381
 
159
382
  A page is a rect in world space; a `PlacedPage` from core's `placedPagesOf` is
@@ -187,6 +410,7 @@ annotation layers are left out.
187
410
  | A shape's outline (`diagram.outline`) | A path of that outline in the shape's box, with its fill and stroke; a container's divider under its header as a second path. A container's members follow inside its group. |
188
411
  | A connector, or any node that derives its path | One path along the route, with its stroke and its `marker-start` and `marker-end`. A path made of parts writes one path per part. |
189
412
  | A drawn mark | Its unit-square path stretched over its box. |
413
+ | A plot's mark | Its path or its text, in the plot root's frame. |
190
414
  | Rows, labels, text marks, and off-page tags | Text, laid out in the lines the canvas lays it into; a tag's background is a rect behind it. |
191
415
  | Anything else | What `@weasel-js/svg`'s `svgLeafFromKit` writes for it. |
192
416
 
@@ -234,5 +458,6 @@ register, as for the PNG, and this needs WebGL2 and `OffscreenCanvas`.
234
458
 
235
459
  ## Scenes it reads
236
460
 
237
- Poses are read as world-space `RectPose`s, which is what a diagram scene
238
- holds. A scene that composes child poses into a parent's frame is not one.
461
+ Poses are `RectPose`s. A child stores its pose in its parent's frame, and both
462
+ the reader and the page exports fold it through that frame, so each sees a node
463
+ where the canvas draws it.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { DiagramData, LayoutDirection, DataEdge, DiagramTheme, DiagramSpec, MeasureRowText } from '@weasel-js/diagram';
2
- import { Scene, SerializedDoc, RectPose } from '@weasel-js/core';
1
+ import { DiagramData, LayoutDirection, LayoutOptions, SequenceData, DataEdge, DiagramTheme, MeasureRowText, DiagramSpec, DiagramType } from '@weasel-js/diagram';
2
+ import { PlotData, PlotTheme } from '@weasel-js/plot';
3
+ import { Page, Scene, SerializedDoc, PlacedPage, RectPose } from '@weasel-js/core';
3
4
  import { SvgNode, SerializeOptions } from '@weasel-js/svg';
4
5
  import { PdfSkip } from '@weasel-js/pdf';
5
6
 
@@ -10,19 +11,44 @@ import { PdfSkip } from '@weasel-js/pdf';
10
11
  * once for a chart and for a data-backed host alike.
11
12
  */
12
13
 
13
- /**
14
- * One page: its sheet size, and what is on it in coordinates from the page's
15
- * top-left corner. Ids share one namespace across the whole document.
16
- */
17
- interface DocumentPage {
14
+ /** What every page has: its sheet size. Ids share one namespace across the whole document. */
15
+ interface PageBase {
18
16
  id: string;
19
17
  name?: string;
20
18
  width: number;
21
19
  height: number;
20
+ }
21
+ /** A page of nodes and edges, in coordinates from the page's top-left corner. */
22
+ interface GraphPage extends PageBase {
23
+ kind: 'graph';
22
24
  data: DiagramData;
23
25
  /** The way layout runs when the page is laid out on import, which it is when a node has no `at`. */
24
26
  direction?: LayoutDirection;
27
+ /** The layout that runs when the page is laid out on import. Unset, `layered`. */
28
+ layout?: PageLayout;
29
+ /** What the graph models, such as `'class'` or `'state'`: a format with a notation of that name writes it in
30
+ * that notation. Unset, a plain flowchart. */
31
+ notation?: string;
32
+ }
33
+ /** A layout by its name in `@weasel-js/diagram`'s `LAYOUTS`, and the options a file can carry for it. */
34
+ interface PageLayout {
35
+ name: 'layered' | 'tree' | 'force' | 'chain' | 'radial' | 'grid';
36
+ options?: Pick<LayoutOptions, 'nodeGap' | 'rankGap' | 'order' | 'chainSide' | 'grid'>;
25
37
  }
38
+ /** A page holding one chart, which fills it. */
39
+ interface PlotPage extends PageBase {
40
+ kind: 'plot';
41
+ data: PlotData;
42
+ }
43
+ /** A sequence diagram, laid out from its data whenever it is drawn. Its `name` is the diagram's title. */
44
+ interface SequencePage extends PageBase {
45
+ kind: 'sequence';
46
+ data: SequenceData;
47
+ }
48
+ /** One page of a document; `kind` says what its `data` is. */
49
+ type DocumentPage = GraphPage | SequencePage | PlotPage;
50
+ /** The kinds of page a document can hold. */
51
+ type PageKind = DocumentPage['kind'];
26
52
  /** A diagram as every format reads and writes it. */
27
53
  interface DiagramDocument {
28
54
  pages: readonly DocumentPage[];
@@ -65,9 +91,19 @@ interface DiagramFormat {
65
91
  write?(doc: DiagramDocument, opts?: WriteOptions): ExportResult;
66
92
  /** Whether {@link write} writes one page rather than the whole document. */
67
93
  onePage?: boolean;
94
+ /** The kinds of page it reads and writes. A writer leaves out a page of any other kind, with a warning. */
95
+ kinds: readonly PageKind[];
68
96
  }
69
97
  /** The page a one-page format writes: the one `opts.page` names, else the first. `null` for a document with none. */
70
98
  declare function writtenPage(doc: DiagramDocument, opts?: WriteOptions): DocumentPage | null;
99
+ /** `page` as a graph, for a format that writes graphs only. Throws on a page of any other kind. */
100
+ declare function graphPageOf(page: DocumentPage, format: string): GraphPage;
101
+ /** The graph pages of `doc`, warning once for each page of another kind left out. */
102
+ declare function graphPagesOf(doc: DiagramDocument, format: string, warn: (message: string) => void): GraphPage[];
103
+ /** Whether `format` can write every page of `doc`. */
104
+ declare function formatHolds(format: DiagramFormat, doc: DiagramDocument): boolean;
105
+ /** Whether `format` reads and writes every kind in `kinds`: what a menu asks of `scenePageKinds`. */
106
+ declare function formatTakes(format: DiagramFormat, kinds: ReadonlySet<PageKind>): boolean;
71
107
 
72
108
  /** The formats a diagram reads from and writes to, and how a file finds its format. */
73
109
 
@@ -97,19 +133,35 @@ declare const DIAGRAM_JSON: DiagramFormat;
97
133
  */
98
134
  declare const DRAW_XML: DiagramFormat;
99
135
 
100
- /** Mermaid as a {@link DiagramFormat}. */
101
-
102
- /**
103
- * A Mermaid flowchart, one page per file, laid out on import. Any Mermaid
104
- * diagram is recognized, so reading another kind says which kind it is.
105
- */
136
+ /** Mermaid diagrams, one page per file, laid out on import. Any Mermaid diagram is recognized, so reading a kind not read says which it is. */
106
137
  declare const DIAGRAM_MERMAID: DiagramFormat;
107
138
 
139
+ /** Reads a Mermaid `xychart-beta` (or `xychart`) diagram. Throws, naming the line, on text that is not one. */
140
+ declare function readMermaidXychart(text: string): ImportResult;
141
+
142
+ /** Writes `page`'s xychart as Mermaid. Throws on a page holding another kind of chart. */
143
+ declare function writeMermaidXychart(page: PlotPage): ExportResult;
144
+
145
+ /** Plot pages to and from a scene: one plot root per page, which the plot package compiles and keeps. */
146
+
147
+ /** A diagram theme's colors by the roles a chart draws in: text in the label color, axes in the connector stroke,
148
+ * grid lines in the container stroke, series in its series, in its face. */
149
+ declare function plotThemeOf(theme: DiagramTheme): PlotTheme;
150
+
108
151
  /** Graphviz DOT as a {@link DiagramFormat}. */
109
152
 
110
153
  /** A Graphviz DOT graph, one page per file, laid out on import unless every node has a `pos`. */
111
154
  declare const DIAGRAM_DOT: DiagramFormat;
112
155
 
156
+ /** A graph page's notation, layout, and direction, kept on its scene page so they survive the scene. */
157
+
158
+ /** The key a scene page's `meta` holds a graph page's {@link GraphPageSettings} under. */
159
+ declare const GRAPH_PAGE_META_KEY = "diagram:graph";
160
+ /** What a graph page says beyond its data: how it is laid out and what it models. */
161
+ type GraphPageSettings = Pick<GraphPage, 'direction' | 'layout' | 'notation'>;
162
+ /** The graph settings scene page `page` keeps; empty when it keeps none. */
163
+ declare function graphPageSettingsOf(page: Pick<Page, 'meta'>): GraphPageSettings;
164
+
113
165
  /** A diagram scene as a {@link DiagramDocument}: what every format writes from. */
114
166
 
115
167
  /** Options for {@link documentFromScene}. */
@@ -121,16 +173,30 @@ interface DocumentFromSceneOptions {
121
173
  * Reads a diagram scene into a document: one page per page in its `core:pages`
122
174
  * (one page around everything when it has none), each holding the shapes,
123
175
  * connectors, containers and pools, and groups on it, in coordinates from the
124
- * page's corner. A shape belongs to the page its outermost container does, and
176
+ * page's corner; a page holding a plot root is a plot page of its spec, and
177
+ * whatever else is on it is left out. A shape belongs to the page its outermost container does, and
125
178
  * that is the one its box overlaps most, or the nearest one when it overlaps
126
179
  * none. A connector the scene splits off-page, or whose ends export to
127
180
  * different pages, goes in `crossPageEdges`: without waypoints when split,
128
181
  * with them in world coordinates when not. Colors are kept
129
182
  * where the scene's theme would not give them: the author's own, and any a
130
- * theme never set.
183
+ * theme never set. A page holding a sequence's root is a sequence page holding
184
+ * its data; the parts drawn from it, and anything else on that page, are not read.
185
+ * A graph page takes the notation, layout, and direction its scene page keeps; with no notation there, the one the
186
+ * scene's diagram type writes (`diagramNotationOf`).
131
187
  */
132
188
  declare function documentFromScene<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>, opts?: DocumentFromSceneOptions): DiagramDocument;
133
189
 
190
+ /** Which kinds of page a scene would export as, read from its top-level nodes alone. */
191
+
192
+ /**
193
+ * The kinds of page `documentFromScene` would make of `scene`, without making
194
+ * them: a page holding a plot's root is a plot page, one holding a sequence's
195
+ * root a sequence page, any other a graph page. Cheap enough to ask on every
196
+ * menu render.
197
+ */
198
+ declare function scenePageKinds<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): ReadonlySet<PageKind>;
199
+
134
200
  /** A {@link DiagramDocument} as scene specs and document values: what every import draws from. */
135
201
 
136
202
  /** Options for {@link documentSpecs}. */
@@ -142,21 +208,84 @@ interface DocumentSpecsOptions<TLayer extends string = 'main'> {
142
208
  /** Measures text. Default an estimate from character count. */
143
209
  measure?: MeasureRowText;
144
210
  }
145
- /** What {@link documentSpecs} returns: the nodes, and the `core:pages` and `diagram:theme` document values. */
211
+ /** What {@link documentSpecs} returns: the nodes, and the `core:pages`, `diagram:theme`, and `diagram:type` document values. */
146
212
  interface DocumentScene<TLayer extends string = 'main'> {
147
213
  specs: DiagramSpec<TLayer>[];
148
214
  doc: SerializedDoc;
149
215
  }
150
216
  /**
151
217
  * The scene a document draws as. Its pages are placed with core's
152
- * `layoutPages`, as a scene's `core:pages` places them. A page whose nodes
153
- * all have a place is drawn where they say; any other page is laid out first,
154
- * with `layered`, its placed nodes pinned (and grows to hold the result). Then
155
- * every page, moved to its place, and the edges between pages are drawn by one
156
- * `diagramScene` call with no layout, so the scene holds places and routes and
157
- * no layout runs again.
218
+ * `layoutPages`, as a scene's `core:pages` places them. A graph page whose
219
+ * nodes all have a place is drawn where they say; any other is laid out
220
+ * first, with `layered`, its placed nodes pinned (and grows to hold the
221
+ * result). A sequence page is drawn from its data, one root holding it, and
222
+ * grows to hold its layout. A plot page is one plot root filling its page, its
223
+ * marks under it (`plotSpecs`), drawn in the theme's colors. A graph page's
224
+ * notation, layout, and direction go on its `core:pages` page, under
225
+ * `meta['diagram:graph']`, where `documentFromScene` reads them back. When
226
+ * every graph page's notation is one a diagram type claims, the scene opens as
227
+ * that type (`diagram:type`), its stencils with it.
158
228
  */
159
229
  declare function documentSpecs<TLayer extends string = 'main'>(document: DiagramDocument, opts?: DocumentSpecsOptions<TLayer>): DocumentScene<TLayer>;
230
+ /**
231
+ * One more page for a scene: `page` as its `core:pages` lists it, and the nodes it draws as once placed where
232
+ * `placeAt` puts it, as `documentSpecs` draws a page. A graph page with no place for a node is laid out first; a
233
+ * sequence page grows to hold its layout.
234
+ */
235
+ declare function pageSpecs<TLayer extends string = 'main'>(page: DocumentPage, placeAt: (page: Page) => PlacedPage, opts?: DocumentSpecsOptions<TLayer>): {
236
+ page: Page;
237
+ specs: DiagramSpec<TLayer>[];
238
+ };
239
+
240
+ /** Templates: a new page of every kind a document holds, from an empty graph of each diagram type to a small chart. */
241
+
242
+ /** Where a menu lists a template: with the diagrams, as a sequence diagram, or with the charts. */
243
+ type PageTemplateGroup = 'diagram' | 'sequence' | 'chart';
244
+ /** A new page of one kind. */
245
+ interface PageTemplate {
246
+ id: string;
247
+ label: string;
248
+ icon?: DiagramType['icon'];
249
+ group: PageTemplateGroup;
250
+ /** The diagram type a document made from it takes: the one a graph template draws. */
251
+ diagramType?: string;
252
+ /** The page, under `id`. `size` is a graph page's; a sequence or a chart takes the size its content needs. */
253
+ page(id: string, size?: {
254
+ width: number;
255
+ height: number;
256
+ }): DocumentPage;
257
+ }
258
+ /** A graph page's size when none is asked for: letter, landscape, at 96 per inch. */
259
+ declare const TEMPLATE_PAGE_SIZE: {
260
+ readonly width: 1056;
261
+ readonly height: 816;
262
+ };
263
+ /** Every template, by id, in menu order: each diagram type's, the sequence diagram's, then each chart kind's. */
264
+ declare const PAGE_TEMPLATES: Readonly<Record<string, PageTemplate>>;
265
+
266
+ /** A template as a new scene, or as one more page of one. */
267
+
268
+ /** A new document of one page made from `template`, as {@link documentSpecs} draws it, with the diagram type the
269
+ * template names. */
270
+ declare function templateSpecs<TLayer extends string = 'main'>(template: PageTemplate, opts?: DocumentSpecsOptions<TLayer> & {
271
+ size?: {
272
+ width: number;
273
+ height: number;
274
+ };
275
+ }): DocumentScene<TLayer>;
276
+ /** Options for {@link addTemplatePage}. */
277
+ interface AddTemplatePageOptions<TLayer extends string = 'main'> extends DocumentSpecsOptions<TLayer> {
278
+ /** Where the page goes in the page order. Default last. */
279
+ index?: number;
280
+ /** A graph page's size. Default the template's. */
281
+ size?: {
282
+ width: number;
283
+ height: number;
284
+ };
285
+ }
286
+ /** Adds a page made from `template` to `scene`, and what it draws on it, as one undo entry, `Add page`. Returns the new
287
+ * page's id. */
288
+ declare function addTemplatePage<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>, template: PageTemplate, opts?: AddTemplatePageOptions<TLayer>): string;
160
289
 
161
290
  /**
162
291
  * One page of a diagram scene as `SvgNode`s: the nodes whose box meets the
@@ -245,4 +374,4 @@ declare function exportPagesPdf(scene: PageExportScene, pages: readonly PageRect
245
374
  skipped: readonly PdfSkip[];
246
375
  }>;
247
376
 
248
- export { DIAGRAM_DOT, DIAGRAM_FORMATS, DIAGRAM_JSON, DIAGRAM_MERMAID, DRAW_XML, type DiagramDocument, type DiagramFormat, type DocumentFromSceneOptions, type DocumentPage, type DocumentScene, type DocumentSpecsOptions, type ExportPagePngOptions, type ExportPageSvgOptions, type ExportPagesPdfOptions, type ExportResult, type ImportResult, type PageExportScene, type PageRect, type WriteOptions, detectFormat, documentFromScene, documentSpecs, exportPagePng, exportPageSvg, exportPagesPdf, pageSvgNodes, readDocument, writtenPage };
377
+ export { type AddTemplatePageOptions, DIAGRAM_DOT, DIAGRAM_FORMATS, DIAGRAM_JSON, DIAGRAM_MERMAID, DRAW_XML, type DiagramDocument, type DiagramFormat, type DocumentFromSceneOptions, type DocumentPage, type DocumentScene, type DocumentSpecsOptions, type ExportPagePngOptions, type ExportPageSvgOptions, type ExportPagesPdfOptions, type ExportResult, GRAPH_PAGE_META_KEY, type GraphPage, type GraphPageSettings, type ImportResult, PAGE_TEMPLATES, type PageBase, type PageExportScene, type PageKind, type PageLayout, type PageRect, type PageTemplate, type PageTemplateGroup, type PlotPage, type SequencePage, TEMPLATE_PAGE_SIZE, type WriteOptions, addTemplatePage, detectFormat, documentFromScene, documentSpecs, exportPagePng, exportPageSvg, exportPagesPdf, formatHolds, formatTakes, graphPageOf, graphPageSettingsOf, graphPagesOf, pageSpecs, pageSvgNodes, plotThemeOf, readDocument, readMermaidXychart, scenePageKinds, templateSpecs, writeMermaidXychart, writtenPage };