@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.
- package/agent-setup/prompt.md +83 -0
- package/dist/assets-command.d.ts.map +1 -1
- package/dist/assets-command.js +15 -0
- package/dist/assets-command.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -1
- package/dist/cli.js.map +1 -1
- package/dist/export-command.d.ts.map +1 -1
- package/dist/export-command.js +16 -3
- package/dist/export-command.js.map +1 -1
- package/dist/init-command.d.ts.map +1 -1
- package/dist/init-command.js +6 -3
- package/dist/init-command.js.map +1 -1
- package/dist/messages/en.d.ts +45 -7
- package/dist/messages/en.d.ts.map +1 -1
- package/dist/messages/en.js +82 -9
- package/dist/messages/en.js.map +1 -1
- package/dist/messages/es.d.ts.map +1 -1
- package/dist/messages/es.js +79 -8
- package/dist/messages/es.js.map +1 -1
- package/dist/messages/index.d.ts +41 -6
- package/dist/messages/index.d.ts.map +1 -1
- package/dist/skill-command.d.ts +48 -0
- package/dist/skill-command.d.ts.map +1 -0
- package/dist/skill-command.js +148 -0
- package/dist/skill-command.js.map +1 -0
- package/package.json +25 -18
- package/skills/.skills-sync.json +5 -0
- package/skills/meltemi-diagrams/SKILL.md +232 -0
- package/skills/meltemi-diagrams/examples/architecture.meltemi +73 -0
- package/skills/meltemi-diagrams/examples/platform.meltemi +139 -0
- package/skills/meltemi-diagrams/examples/sequence.meltemi +52 -0
- package/skills/meltemi-diagrams/examples/slide.meltemi +60 -0
- package/skills/meltemi-diagrams/examples/story.meltemi +53 -0
- package/skills/meltemi-diagrams/reference/c4.md +92 -0
- package/skills/meltemi-diagrams/reference/cloud.md +19 -0
- package/skills/meltemi-diagrams/reference/converting.md +101 -0
- package/skills/meltemi-diagrams/reference/free.md +327 -0
- package/skills/meltemi-diagrams/reference/language.md +245 -0
- package/skills/meltemi-diagrams/reference/layout.md +165 -0
- package/skills/meltemi-diagrams/reference/sequence.md +143 -0
- package/skills/meltemi-diagrams/reference/stories.md +94 -0
- package/skills/meltemi-diagrams/reference/themes.md +148 -0
- package/templates/kanban/diagrams/board.meltemi +57 -0
- 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
|
+
}
|