@meltemi/diagrams-cli 1.9.0 → 1.11.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.
Files changed (46) hide show
  1. package/agent-setup/prompt.md +83 -0
  2. package/dist/assets-command.d.ts.map +1 -1
  3. package/dist/assets-command.js +15 -0
  4. package/dist/assets-command.js.map +1 -1
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +11 -1
  8. package/dist/cli.js.map +1 -1
  9. package/dist/export-command.d.ts.map +1 -1
  10. package/dist/export-command.js +16 -3
  11. package/dist/export-command.js.map +1 -1
  12. package/dist/init-command.d.ts.map +1 -1
  13. package/dist/init-command.js +6 -3
  14. package/dist/init-command.js.map +1 -1
  15. package/dist/messages/en.d.ts +45 -7
  16. package/dist/messages/en.d.ts.map +1 -1
  17. package/dist/messages/en.js +82 -9
  18. package/dist/messages/en.js.map +1 -1
  19. package/dist/messages/es.d.ts.map +1 -1
  20. package/dist/messages/es.js +79 -8
  21. package/dist/messages/es.js.map +1 -1
  22. package/dist/messages/index.d.ts +41 -6
  23. package/dist/messages/index.d.ts.map +1 -1
  24. package/dist/skill-command.d.ts +48 -0
  25. package/dist/skill-command.d.ts.map +1 -0
  26. package/dist/skill-command.js +148 -0
  27. package/dist/skill-command.js.map +1 -0
  28. package/package.json +25 -18
  29. package/skills/.skills-sync.json +5 -0
  30. package/skills/meltemi-diagrams/SKILL.md +232 -0
  31. package/skills/meltemi-diagrams/examples/architecture.meltemi +73 -0
  32. package/skills/meltemi-diagrams/examples/platform.meltemi +139 -0
  33. package/skills/meltemi-diagrams/examples/sequence.meltemi +52 -0
  34. package/skills/meltemi-diagrams/examples/slide.meltemi +60 -0
  35. package/skills/meltemi-diagrams/examples/story.meltemi +53 -0
  36. package/skills/meltemi-diagrams/reference/c4.md +92 -0
  37. package/skills/meltemi-diagrams/reference/cloud.md +19 -0
  38. package/skills/meltemi-diagrams/reference/converting.md +101 -0
  39. package/skills/meltemi-diagrams/reference/free.md +327 -0
  40. package/skills/meltemi-diagrams/reference/language.md +245 -0
  41. package/skills/meltemi-diagrams/reference/layout.md +165 -0
  42. package/skills/meltemi-diagrams/reference/sequence.md +143 -0
  43. package/skills/meltemi-diagrams/reference/stories.md +94 -0
  44. package/skills/meltemi-diagrams/reference/themes.md +148 -0
  45. package/templates/kanban/diagrams/board.meltemi +57 -0
  46. package/templates/whiteboard/diagrams/whiteboard.meltemi +59 -0
@@ -0,0 +1,165 @@
1
+ # Layout: modes, constraints, ports, coordinates, sizes, anchors, groups, frame
2
+
3
+ ## Modes
4
+
5
+ | Mode | Use it for | Positions |
6
+ | ---------------- | -------------------------------------------- | ------------------------------------------------------------- |
7
+ | `auto` (default) | new sheets, quick drafts, generated diagrams | ELK arranges every box in the `direction`; `place` is ignored |
8
+ | `assisted` | a sheet where some boxes must stay put | placed boxes stay; ELK places the others around them |
9
+ | `manual` | conversions and pixel-exact sheets | every box needs a `place` |
10
+
11
+ - ELK keeps the source order within each column, so declare boxes in reading order.
12
+ - Primary arrows (`->`) are kept straighter than secondary ones (`-->`).
13
+ - `pnpm mdg layout sheet.meltemi` writes the arranged positions into the file and switches `auto`
14
+ to `assisted`; `--all` arranges everything anew and writes the bends ELK draws as waypoints with
15
+ free ends, so the connectors look as they did in `auto`; `--check` only reports.
16
+
17
+ ## Constraints
18
+
19
+ Constraints go in `placement` and hold in every mode. Placed boxes never move; free ones do.
20
+
21
+ ```text
22
+ placement {
23
+ align api, orders, db top // also bottom, left, right, middle, center
24
+ row client, api // equal vertical centres
25
+ column api, cache // equal horizontal centres
26
+ order a, b, c horizontal // left to right in this order (axis defaults to direction)
27
+ distance api, db 80 // at least 80 px between the boxes
28
+ }
29
+ ```
30
+
31
+ If placed boxes break a constraint, `layout.constraint-conflict` names them and how far off they
32
+ are; the sheet still renders.
33
+
34
+ ## Ports and connector shapes
35
+
36
+ ```text
37
+ bus["Event bus"] {
38
+ port in west 30%
39
+ port out east
40
+ }
41
+ placement {
42
+ route publish from api.east to bus.in
43
+ }
44
+ style notify { routing bezier }
45
+ ```
46
+
47
+ - A named port is an exact point on a side, 50 % along it by default; compass ports (`east`) only
48
+ choose the side.
49
+ - `routing` is `auto` (anchor rules below, or ELK's routes in `auto` mode), `orthogonal`,
50
+ `polyline` or `bezier`; set it for the diagram or per relation in a `style`.
51
+ - Two or more arrows between the same boxes are spread 16 px apart automatically.
52
+
53
+ ## Coordinates
54
+
55
+ - Pixel units, origin at the top-left, y grows downwards. The canvas is computed from the content
56
+ plus a 20 px margin, so start wherever you like; coordinates from an existing SVG can be copied
57
+ as they are.
58
+ - `place <id> at (x, y)` is the centre of the box. From a rectangle `x, y, w, h` the centre is
59
+ `(x + w/2, y + h/2)`.
60
+ - Use a grid: multiples of 20 for centres, row pitch 70 to 90 px for 56 px boxes, column pitch of
61
+ the box width plus 40 px.
62
+
63
+ ## Sizes
64
+
65
+ | Kind | Default | Give `size` when |
66
+ | --------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------ |
67
+ | `node` | 160 x 56, wider when its text needs it | you want it larger than its text, or match an original |
68
+ | `node` with `shape` | the shape's size (free.md), grown until the text fits inside the shape | the same |
69
+ | `shape icon` | 64 x 64 (or `iconSize`; 16 more when framed), the label outside it | you want a larger icon; `iconSize 96` says it better |
70
+ | `iconPosition center` | 120 x 120, wider for a long label | the same; a small `size` leaves no room for the icon |
71
+ | `decision` | 160 x 80, grown until its text fits the diamond | the same |
72
+ | `data` | 160 x 64, wider when its text needs it | the same |
73
+ | `group` | fits children + 16 px, 40 px top (16 px untitled), and title | you want a band of a fixed size (then also `place`) |
74
+ | `tag` | text width + 18, height 18 | never, unless matching an original |
75
+ | `note` | longest line, 18 px per line | never, unless matching an original |
76
+
77
+ Text is measured with the bundled fonts (Inter, IBM Plex Sans, Cascadia Mono and the families a
78
+ `font` names), at its `fontSize`, so boxes without `size` always fit their label and descriptions. A `size` that is too narrow is reported as
79
+ `layout.text-overflow` with a hint that gives a width that fits: follow it. Aligned columns of boxes
80
+ look best with one shared `size` that fits the longest label.
81
+
82
+ Text layout inside a box: label centred; with a description the label moves up 8 px and the
83
+ description sits 10 px below the centre; extra description lines add 16 px each (`\n`).
84
+
85
+ ## Arrows and anchors
86
+
87
+ A relation without `route` uses `auto` ports on both ends. The exporter picks the side facing the
88
+ other box and then:
89
+
90
+ 1. facing parallel sides (south to north, east to west) meet at the centre of the overlap of
91
+ their extents, so aligned boxes get straight arrows and a wide band under a box gets a vertical
92
+ arrow under that box;
93
+ 2. without overlap, each side uses its own centre (the arrow is diagonal: align the boxes or add a
94
+ waypoint);
95
+ 3. perpendicular sides get one elbow;
96
+ 4. with waypoints, each side anchors at the projection of its neighbouring waypoint, clamped to the
97
+ side;
98
+ 5. free ends that land less than 16 px apart on one side of a box spread 16 px apart (more for
99
+ labelled relations), ordered by where their other ends are; a straight arrow moves whole.
100
+
101
+ Waypoint example, a line that leaves to the right and drops onto the top of a wide gateway:
102
+
103
+ ```text
104
+ route ext from clients.east to gateway.north via [(1035, 48)]
105
+ ```
106
+
107
+ On a decision, a data store, a tag or a node with a `shape`, the point found on the rectangle then
108
+ slides along the side's normal onto the outline, so arrows touch the diamond, the cylinder's rim
109
+ or the hexagon; `d.north 5%` still reads as a point of the north side.
110
+
111
+ The polyline is 1.3 px with a filled 8 px arrowhead; `-->` and `line dashed` dash it, `line dotted`
112
+ dots it, `<->` puts the head at both ends and `corners rounded` rounds its bends.
113
+
114
+ ## Relation labels
115
+
116
+ `a -> b: "text" as r` draws a pill centred on the route at 50 % unless `label r at <n>% offset
117
+ (dx, dy)` moves it. For vertical arrows use `offset (16, 0)`; for horizontal ones `offset (0, -16)`.
118
+ Style the pill with `style r { fill "#0B1830"; color "#FFFFFF" }` for a dark step badge; one
119
+ character labels become circles.
120
+
121
+ ## Groups
122
+
123
+ ```text
124
+ group runtime "AgentCore Runtime" {
125
+ node a "Agent A"
126
+ node b "Agent B"
127
+ }
128
+ placement {
129
+ place a at (337, 358); size a (155, 56)
130
+ place b at (509, 358); size b (155, 56)
131
+ // runtime fits a and b, or:
132
+ place runtime at (510, 390); size runtime (540, 200)
133
+ }
134
+ ```
135
+
136
+ A group is drawn first (rounded band, radius 14, or its own `radius`) with its label at the
137
+ top-left (20 px in, 18 px down) and an optional description under it. Children keep diagram
138
+ coordinates. Nested groups work. Arrows may start or end at a group; the anchor rules treat it as
139
+ a box. `group backend "" { ... }` (empty label, no description, no icon) is a plain container: no
140
+ title, no 40 px band (the padding is 16 px all round), no gap cut in arrows; with
141
+ `style backend { fill "#dae8fc"; radius 0 }` it is a flat coloured rectangle. A connector between
142
+ two boxes of a group, or of a box they are stacked in, is never routed around that container.
143
+
144
+ ## Z-order
145
+
146
+ Groups (outer first), then nodes, data, decisions and notes in source order, then relations with
147
+ their labels, then tags, then the brand frame. A box declared after a box that holds it entirely
148
+ is drawn over it and is not `layout.overlap` (a stack: declare the outer box first). Place tags on
149
+ box corners (`place t at (x + w/2, y)` for the top-right corner) and they stay on top. With
150
+ `layers back, main`, anything with `layer back` is drawn first and is not checked against the
151
+ boxes of `main`.
152
+
153
+ ## Legend and notes
154
+
155
+ - A legend is a row of tags under the sheet, each styled with a category's `fill`, `stroke` and
156
+ `color`; add a white dashed tag for "proposed" and a dark one for "brand label" if used.
157
+ - Notes are left-aligned text blocks: `place` their centre; a 4-line note is about 72 px tall.
158
+
159
+ ## Frame
160
+
161
+ With the default theme the exporter adds a 44 px footer: hairline, Meltemi mark, the `brand`
162
+ text and the diagram `title`; the `stage.*` themes put a taller strip on top. Nothing inside the
163
+ sheet changes. Use `theme "editorial.light"` or a file theme with `frame none` for a bare sheet
164
+ (see themes.md). Sizes in this guide are those of the technical themes; the stage themes use larger
165
+ type, so give explicit `size` values more room or leave them out.
@@ -0,0 +1,143 @@
1
+ # Sequence diagrams
2
+
3
+ Use `diagram <id>: sequence { }` for interactions over time: who calls whom, in what order, with
4
+ which alternatives. Participants stand left to right in source order and events go top to bottom
5
+ in source order. Never write `place`, `size` or `route` here; the layout is computed.
6
+
7
+ ## Participants
8
+
9
+ `participant`, `actor`, `boundary`, `control`, `entity`, `database`, `collections` and `queue`,
10
+ each `<kind> <id> "Label"`. `id["Label"]` is a participant and `id[("Label")]` a database. A
11
+ message end that is never declared becomes a participant labelled with its id, placed at its first
12
+ mention. `group backend "Backend" { participant api "API" }` draws a box around participants.
13
+
14
+ ## Messages and events
15
+
16
+ ```text
17
+ customer -> api: "Pay" as pay // call: solid, filled arrowhead
18
+ api --> customer: "Receipt" // reply: dashed, open arrowhead
19
+ api -> events: "Paid" as paid { kind async } // async: solid, open arrowhead
20
+ api <-> cache: "Sync" // two-way: a head at both ends
21
+ api -> api: "Validate" // self-call
22
+ activate api // bar from the message before; deactivate ends it
23
+ create order // the head appears at this row
24
+ destroy order // the lifeline ends with a cross
25
+ note over api, bank "Timeout 3 s" // also: note left of api "…", note right of api "…"
26
+ divider "Fulfilment"
27
+ delay "Two days later"
28
+ autonumber // numbers every message from 1 (autonumber 10: from 10)
29
+ ```
30
+
31
+ A message label stays on one line and holds its two participants apart as far as it needs; a
32
+ negative `gap` or a `spacing` below 40 pulls them closer than that by its amount, and the label then
33
+ wraps at the room left between the lifelines, a taller row instead of a refused gap. Keep labels as
34
+ sentences and never shorten them to fit. Write `\n` only for a break of your own
35
+ (`"Authorize\nthe card"`). A self-call's label stays on one line beside its loop, broken only at
36
+ `\n`. A pull stops where the heads or a label's longest word no longer fit.
37
+
38
+ When a call causes other events, write them in the call's block and answer it with `return`: the
39
+ receiver is active for the whole block (one bar, no `activate`/`deactivate` to balance), and
40
+ `return` is the dashed reply to the caller.
41
+
42
+ ```text
43
+ customer -> api: "Pay" as pay {
44
+ api -> bank: "Authorize" as authorize {
45
+ return "Approved" as approved // bank --> api
46
+ }
47
+ note over api "Stores the receipt"
48
+ return "Receipt" as receipt // api --> customer; nothing may follow it in this list
49
+ }
50
+ ```
51
+
52
+ A block may hold properties (`kind async`) beside its events, fragments may sit inside it and
53
+ calls with blocks inside fragments. `return` outside a call is an error.
54
+
55
+ ## Tags
56
+
57
+ ```text
58
+ participant bank "Bank" { tag "external" } // a badge over the head
59
+ api -> bank: "Authorize" as authorize {
60
+ tag "PKCE" // pills on the arrow, next to the source
61
+ tag "v2"
62
+ }
63
+ ```
64
+
65
+ `tag "Text"` marks a message (calls, replies, self-calls, `return`) or a participant with a short
66
+ word: a protocol, a version, a property. Write it in the owner's block, one line of text, several
67
+ in order, no id; in a call's block it belongs to the call, beside its events. Never write it at
68
+ the diagram level, in a group block or in a fragment (`prop.unknown`), never empty, and never the
69
+ same text twice on one owner (`prop.duplicate`). `tag <id> "Text"` is the flow profile's pill and
70
+ is refused here. Tags take the theme's `tag` colours; there is no `style` or `rule` for them.
71
+
72
+ ## Fragments
73
+
74
+ ```text
75
+ alt result "Approved" {
76
+ bank --> api: "OK"
77
+ } else "Declined" {
78
+ bank --> api: "Declined"
79
+ }
80
+ opt coupon "Coupon applied" { … }
81
+ loop polling "Until shipped" { … }
82
+ par { … } and "Notify" { … }
83
+ break "Fraud" { … }
84
+ critical "Shipment" { … } option "Carrier down" { … }
85
+ region "Audit" { … }
86
+ ```
87
+
88
+ Write `else`, `and` and `option` on the line of the closing `}`. Give a fragment an id (`alt
89
+ result …`) when a scenario or `space` must name it. Fragments hold events only: declare every
90
+ participant at the diagram level first.
91
+
92
+ ## Scenarios
93
+
94
+ ```text
95
+ scenario approved "Payment approved" {
96
+ choose result "Approved" // or: choose result 1
97
+ repeat polling 3 // 1 to 100 passes
98
+ skip coupon // opt or break not taken; take coupon keeps it (the default)
99
+ }
100
+ ```
101
+
102
+ `pnpm mdg export sheet.meltemi --scenario approved` writes `sheet.approved.svg` with only that
103
+ path; without `--scenario` every branch is drawn.
104
+
105
+ ## Spacing and styles
106
+
107
+ ```text
108
+ spacing 56 // distance between neighbouring heads, before gaps (40 by default)
109
+ placement {
110
+ space authorize 28 // more room above a named message, note or fragment
111
+ space settled 20 after // or below it (the last event lengthens the lifelines)
112
+ gap bank 40 // more room left of a participant
113
+ gap ledger 24 after // or right of it
114
+ gap api -12 // a negative gap brings it closer, its labels wrapping, down to the heads
115
+ label pay at 50% offset (0, -4)
116
+ }
117
+ style result { stroke accent } // notes and fragments with ids take styles
118
+ rule fragment { stroke muted }
119
+ ```
120
+
121
+ ## Diagnostics
122
+
123
+ | Code | Fix |
124
+ | --------------------- | ---------------------------------------------------------------------------- |
125
+ | `sequence.activation` | `deactivate p` without an open `activate p`: add the `activate` or remove it |
126
+ | `sequence.return` | `return` outside a call's block, or an event after the `return`: move it |
127
+ | `sequence.lifecycle` | an event after `destroy`, `create` after the participant appeared, or twice |
128
+ | `layout.gap-floor` | (warning) a negative `gap` below what heads, words or tags need: drawn wider |
129
+ | `prop.invalid-value` | a negative `gap` on the outer side, or `space`; `tag ""` or `tag id "x"` |
130
+ | `prop.duplicate` | the same `tag` text twice in one block |
131
+ | `sequence.scenario` | the scenario names an unknown fragment, branch or loop count |
132
+ | `ref.unknown-element` | declare the participant (`participant x "X"`); groups are not participants |
133
+ | `prop.unknown` | a flow or free kind (`node`), a declaration in a fragment, a stray `tag` |
134
+
135
+ Branches of `alt`, `par` and `critical` are checked separately, each from the state before the
136
+ fragment; the first branch's state continues after it. Activations open at the end are fine.
137
+
138
+ ## Converting
139
+
140
+ Import Mermaid and PlantUML sequences with `pnpm mdg import`, which maps them one to one: `participant`/`actor` (and the
141
+ PlantUML kinds), `->>` and `->` to `->`, `-->>` and `-->` to `-->`, `-)` to `-> … { kind async }`,
142
+ `+`/`-` suffixes to `activate`/`deactivate`, `Note over A,B: text` to `note over a, b "text"`,
143
+ `rect`/`group` to `region`, `box` to `group`, `== x ==` to `divider "x"`, `...` to `delay`.
@@ -0,0 +1,94 @@
1
+ # Stories
2
+
3
+ A `story` tells a diagram step by step for a walkthrough, a talk or a recorded demo. It never
4
+ changes the diagram: scenes reveal, hide, highlight, focus, frame, trace and draw parts of it on one
5
+ deterministic clock, and may switch to another view of the same system. Write the diagram first,
6
+ then the story below it (or in another file that imports the diagram).
7
+
8
+ ```text
9
+ story purchase for checkout {
10
+ title "A purchase"
11
+
12
+ scene overview {
13
+ reveal client, api, orders, db over 800ms
14
+ hold 1s
15
+ }
16
+
17
+ scene processing {
18
+ trace request over 1200ms
19
+ parallel {
20
+ trace save over 1200ms
21
+ highlight db over 1200ms
22
+ }
23
+ hold 2s
24
+ }
25
+ }
26
+ ```
27
+
28
+ ## Vocabulary
29
+
30
+ | Statement | Default | Meaning |
31
+ | ------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
32
+ | `scene <id> ["Title"] { ... }` | | a step; scenes run one after the other |
33
+ | `view <diagram>` | | first in a scene: the diagram it shows from now on |
34
+ | `transition cut\|fade\|morph [over <time>]` | 800 ms | needed when the view changes; `morph` moves boxes with the same id, `cut` takes 0 |
35
+ | `reveal a, b [over <time>]` | 600 ms | fades in; targets first revealed start hidden |
36
+ | `hide a, b [over <time>]` | 600 ms | fades out |
37
+ | `highlight a [over <time>]` | 1 s | accent outline for the action's time |
38
+ | `focus a, b` / `focus all` | 600 ms | dims the rest to 25 % until the end of the scene; `all` clears it |
39
+ | `camera a, b` / `camera all` | 800 ms | frames the targets plus 40 px; `all` frames the whole view |
40
+ | `trace r [over <time>]` | 1.2 s | draws one named relation from source to target in the accent colour |
41
+ | `draw a, b [over <time>]` | 800 ms | replays pens, free lines and named relations as drawn, one after another, and keeps them; targets first drawn start hidden |
42
+ | `hold <time>` | | waits |
43
+ | `parallel { ... }` | | actions that start together; lasts as long as the longest |
44
+
45
+ Targets are ids of the scene's view: elements, named relations (`as <id>`), named notes and
46
+ fragments; entity paths (`shop.api`) in C4 views. Times are `800ms` or `1.5s`, up to `600s`.
47
+
48
+ ## Rules that save iterations
49
+
50
+ - Name every relation you want to trace: `api -> db: "SQL" as save`.
51
+ - A whiteboard's explanation is a `draw` of its pens in the order they were written
52
+ (`draw title, arrow over 2s`): each takes a share of the time proportional to its length, in its
53
+ own colour, and stays drawn ([free.md](free.md#pen-strokes)).
54
+ - Reveal everything that should appear; anything never revealed is visible from the start. An
55
+ arrow appears with its two ends, so reveal the boxes, not the relations.
56
+ - Two actions on the same target and channel must not overlap (`story.conflict`): put a `reveal`
57
+ and a `hide` of the same box in sequence, not in one `parallel`.
58
+ - A change of `view` needs a `transition` (`story.missing-transition`); use `morph` between views
59
+ that share ids (two C4 views of one model), `fade` otherwise.
60
+ - Keep scenes short (a few seconds) and end the story with a `hold` so the last state stays.
61
+
62
+ ## Diagnostics
63
+
64
+ | Code | Fix |
65
+ | -------------------------- | ----------------------------------------------------------------------------- |
66
+ | `story.unknown-diagram` | the diagram after `for` or `view` is not in the file or imported |
67
+ | `ref.unknown-element` | the target is not an id of the scene's view; use the suggested id |
68
+ | `story.invalid-target` | `trace` needs a named relation; `draw` a pen, a free line or a named relation |
69
+ | `story.missing-transition` | add `transition cut`, `fade` or `morph` after `view` |
70
+ | `story.conflict` | two overlapping actions on one target and channel; sequence them |
71
+ | `story.empty-scene` | warning: give the scene an action or remove it |
72
+ | `prop.invalid-value` | a time without `ms` or `s`, beyond 600 s, or a statement out of place |
73
+
74
+ ## Checking a story
75
+
76
+ ```bash
77
+ pnpm mdg validate path/to/sheet.meltemi
78
+ ```
79
+
80
+ ```bash
81
+ pnpm mdg export path/to/sheet.meltemi --story purchase --at 2.4s
82
+ ```
83
+
84
+ `--story` writes `<stem>.<story>.svg`: the end of the story, or the frame at `--at`. Export a few
85
+ instants (the start of each scene, the middle of a trace) and look at them; the canvas in VS Code
86
+ plays the story with a timeline.
87
+
88
+ To share the story, write its HTML player, an offline page with pause, progress, scenes, reduced
89
+ motion, a static view, a text summary, zoom, hover cards and a detail panel (`--embed` writes a
90
+ fragment for an existing page):
91
+
92
+ ```bash
93
+ pnpm mdg export path/to/sheet.meltemi --story purchase --format html
94
+ ```
@@ -0,0 +1,148 @@
1
+ # Themes and styles
2
+
3
+ A theme sets palette, type, spacing, strokes, arrowheads and the brand frame. The diagram picks it
4
+ with `theme "<name>"`; a project can set a default in `project.json`
5
+ (`"theme": "themes/company.meltemi#corporate"`); `meltemi-diagrams export --theme <name>`
6
+ overrides both.
7
+
8
+ ## Built in
9
+
10
+ | Name | Use it for | Look |
11
+ | --------------------------- | ------------------------- | ------------------------------------------------------------------- |
12
+ | `technical.light` (default) | architecture review | Sea Mist background, Inter, Aegean tags, footer with the mark |
13
+ | `technical.dark` | architecture review, dark | Meltemi Night background, cyan tags, footer with the mark |
14
+ | `editorial.light` | documents, long reading | white sheet, IBM Plex, quiet greys, open arrowheads, no frame |
15
+ | `editorial.dark` | documents, dark | dark neutral sheet, IBM Plex, no frame |
16
+ | `stage.light` | slides and video | high contrast, 16 px labels, 2 px connectors, 48 px margins, header |
17
+ | `stage.dark` | slides and video, dark | the same on a near-black background |
18
+ | `sketch.light` | whiteboards, early ideas | technical light with strokes painted by hand and rounded bends |
19
+ | `sketch.dark` | whiteboards, dark | the same on the technical dark palette |
20
+
21
+ Explicit quoted colours in styles are kept in every theme, so category colours of a sheet survive
22
+ a theme change. Colour token names (`stroke danger`, `fill surface`) follow the theme instead.
23
+
24
+ ## Theme blocks
25
+
26
+ ```text
27
+ theme acme {
28
+ extends "technical.light"
29
+ accent "#6a4fd0" // start of the mark gradient
30
+ accentEnd "#a594f0" // end of the mark gradient
31
+ tag "#6a4fd0" // tag pill fill
32
+ band "#f6f3fe" // group fill
33
+ bandLine "#6a4fd0" // group outline
34
+ brand "Acme · Meltemi Diagrams"
35
+ frame footer // none | footer | header
36
+
37
+ class partner { line dashed; stroke accent } // reusable styles for every diagram
38
+ rule data { pattern dots } // every data store
39
+ }
40
+
41
+ diagram platform: flow {
42
+ theme "acme"
43
+ ...
44
+ }
45
+ ```
46
+
47
+ Tokens (numbers without a unit or with `px`):
48
+
49
+ | Tokens | Value |
50
+ | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
51
+ | `background`, `surface`, `ink`, `muted`, `line`, `connector`, `band`, `bandLine`, `tag`, `tagText`, `accent`, `accentEnd` | quoted colour |
52
+ | `success`, `warning`, `danger`, `info` | quoted colour (signals) |
53
+ | `highlight`, `sticky`, `stickyText` | quoted colour (highlighter strokes, stickies) |
54
+ | `font`, `mono` | quoted font stack; name a bundled family |
55
+ | `labelSize`, `descriptionSize`, `groupTitleSize`, `noteSize`, `tagSize`, `relationLabelSize`, `frameSize` | 6 to 48 (technical: 13, 11, 13, 11, 10.5, 11, 12) |
56
+ | `labelWeight` | 400, 500, 600 or 700 |
57
+ | `padding`, `groupPadding`, `groupHeader`, `margin` | 0 to 200 (technical: 16, 16, 40, 20) |
58
+ | `radius`, `groupRadius` | 0 to 64 (technical: 8, 14); defaults of `radius` |
59
+ | `strokeWidth`, `connectorWidth` | 0 to 8 (technical: 1, 1.3) |
60
+ | `arrowhead`, `arrowSize`, `corners` | a head (language.md); 0 to 32; `sharp`, `rounded` |
61
+ | `sketch` | `none`, `light`, `rough`: strokes painted by hand |
62
+ | `frame`, `brand`, `mark` | `none`/`footer`/`header`; text; `meltemi`/`none` |
63
+ | `flowDuration`, `dashDuration`, `pulseDuration`, `traceDuration` | `100ms` to `60s`; default speed of each effect |
64
+ | `selection`, `guide` | colour of the canvas's selection and guides only |
65
+
66
+ A theme without `extends` inherits from `technical.light`. Share a theme between files with an
67
+ import (`import corporate from "../themes/company.meltemi"`) or make it the project default.
68
+
69
+ Bare sheet with the Meltemi palette:
70
+
71
+ ```text
72
+ theme bare { extends "technical.light"; frame none }
73
+ ```
74
+
75
+ Company wordmark without the Meltemi mark:
76
+
77
+ ```text
78
+ theme acme { frame header; mark none; brand "Acme Architecture"; font "Merriweather" }
79
+ ```
80
+
81
+ The bundled families, which every export measures, embeds and outlines: sans Inter, IBM Plex
82
+ Sans, Roboto, Open Sans, Lato, Montserrat, Poppins, Nunito; serif Merriweather, Lora, Playfair
83
+ Display; mono Cascadia Mono, Roboto Mono, Source Code Pro; handwriting Patrick Hand, Caveat,
84
+ Comic Neue. A system family (`Georgia`, `Arial`) is measured with estimates
85
+ (`layout.unmeasured-font`) and left to the viewer.
86
+
87
+ A whiteboard look: `theme "sketch.light"`, or `sketch rough` in a theme block
88
+ (`theme hand { extends "technical.light"; sketch rough }`). Every outline, connector, head, pill
89
+ and band is drawn twice with a small seeded wobble; text, icons and images stay crisp, the layout
90
+ is unchanged and the same file always renders the same bytes. `sketch` is a theme token only:
91
+ `style a { sketch rough }` is `prop.unknown`. Name relations whose wobble must not move when
92
+ statements are reordered (anonymous keys change with the order).
93
+
94
+ ## Classes, rules and patterns
95
+
96
+ ```text
97
+ diagram platform: flow {
98
+ class hot { stroke danger; width 2 }
99
+ rule relation { arrowhead open }
100
+ api["API"] { class hot }
101
+ legacy["Legacy"] { class deprecated } // built in: hatch, muted
102
+ proxy["Proxy"] { class proposed } // built in: dashed, warning colour
103
+ style db { class critical; fill "#fff" }
104
+ }
105
+ ```
106
+
107
+ - Built-in classes: `highlight`, `critical`, `proposed`, `deprecated`, `external`.
108
+ - Order: theme tokens, theme rules, diagram rules, classes (declaration block, then `style`
109
+ statements), explicit `style` properties. The last value wins.
110
+ - `pattern hatch|dots|cross` marks state without relying on colour; use it with a legend.
111
+ - `width` sets an outline, connector or pen stroke width (0 to 64); `arrowhead` and `arrowtail` a
112
+ relation's, a free line's or a pen's heads at the target and the source; `line dotted` dots it;
113
+ `corners rounded` rounds its bends. `opacity 0..1` fades free elements, groups, lines and pens.
114
+ - The whiteboard tokens: `stroke highlight` colours a highlighter stroke, and stickies take
115
+ `sticky` and `stickyText` (`#fff59d` and `#1f2328` on light themes); pens take `ink`.
116
+ - The theme's `radius` and `groupRadius` are only defaults: `style api { radius 0 }` squares one
117
+ node, `rule group { radius 0 }` every group, and a class may carry `radius` too. Square corners
118
+ everywhere: `theme square { extends "technical.light"; radius 0; groupRadius 0 }`.
119
+ - `rule node { shape hexagon }` (or `rule box`) gives every node a shape; rules for other kinds
120
+ cannot set `shape`.
121
+ - An unknown class is `theme.unknown-class` (warning) in `validate`.
122
+
123
+ ## Icons
124
+
125
+ - `style api { icon "tabler:server" }` draws a Tabler icon; classes and rules carry icons too
126
+ (`rule data { icon "tabler:database" }`). Provider sets are built in: `aws:lambda`,
127
+ `azure:virtual-machine`, `gcp:cloud-run`, `kubernetes:pod`, `logos:postgresql`; find names with
128
+ `pnpm mdg assets icons <word>`.
129
+ - Search names with `pnpm mdg assets icons <words>`. An unknown name is `asset.unknown-icon` in
130
+ `validate`, with close names in the hint.
131
+ - Project packs live in `assets/icons/` (Iconify JSON or a folder of SVG files) and need a licence
132
+ in `assets/licenses.json`. `pnpm mdg assets list` shows every pack and its licence.
133
+ - `iconSize` and `iconPosition` are theme tokens; the stage themes put icons above the label.
134
+ `iconColor accent` colours monochrome icons; provider icons keep their colours.
135
+ - Per element, `iconPosition left|right|top|bottom|center` places the icon (`center`: the picture
136
+ of the box, its label at the `labelPosition` edge), `iconSize 32` or `auto` sizes it (8 to 128),
137
+ and `shape icon` draws the element as its icon with the label outside (free.md).
138
+
139
+ ## Presets
140
+
141
+ - `preset document`: warns above 1,200 px wide.
142
+ - `preset slide`: a 16:9 canvas, warns above 1,920 × 1,080 px. Pair it with a `stage.*` theme.
143
+ - `preset board`: the canvas grows with the content and never warns; for whiteboards and kanban
144
+ boards.
145
+ - `canvas (1280, 720)`: a fixed canvas with the content centred.
146
+
147
+ `layout.too-large` means the diagram should be split into views, not shrunk, or that a whiteboard
148
+ needs `preset board`.
@@ -0,0 +1,57 @@
1
+ language "0.2"
2
+
3
+ // A kanban board: a row of three columns, and in each column a list of sticky cards. The lists
4
+ // place the cards in the order they are written, so moving a card is moving its line: drag it to
5
+ // another column on the canvas, or cut its declaration and paste it in another group below. The
6
+ // board grows with its cards (preset board).
7
+ diagram board: free {
8
+ title "Team board"
9
+ theme "technical.light"
10
+ preset board
11
+ layout manual
12
+
13
+ text heading "Sprint 12"
14
+ text goal "Goal: a checkout that takes one page"
15
+
16
+ group columns "" {
17
+ layout row
18
+ align stretch
19
+ spacing 40
20
+
21
+ group todo "To do" {
22
+ layout column
23
+ spacing 12
24
+ sticky invoices "Send invoices as PDF"
25
+ sticky search "Search the catalogue by colour"
26
+ sticky reviews "Show reviews on the product page"
27
+ }
28
+
29
+ group doing "Doing" {
30
+ layout column
31
+ spacing 12
32
+ sticky checkout "One-page checkout"
33
+ sticky cache "Cache the catalogue"
34
+ }
35
+
36
+ group done "Done" {
37
+ layout column
38
+ spacing 12
39
+ sticky signin "Sign in with Google"
40
+ }
41
+ }
42
+
43
+ checkout -> signin: "needs" as needs
44
+ search --> cache: "waits for" as waits
45
+
46
+ placement {
47
+ place heading at (384, 50)
48
+ place goal at (384, 90)
49
+ place columns at (384, 424)
50
+ }
51
+
52
+ style heading { fontSize 32; fontWeight 700 }
53
+ style goal { color muted }
54
+ style invoices { fill "#bbdefb" }
55
+ style cache { fill "#bbdefb" }
56
+ style signin { fill "#bbdefb" }
57
+ }