@meltemi/diagrams-assist 1.9.0 → 1.10.17
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/dist/guide.js +5 -5
- package/dist/guide.js.map +1 -1
- package/package.json +4 -4
package/dist/guide.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Generated by `pnpm assist:guide` from skills/meltemi-diagrams/reference; do not edit.
|
|
2
2
|
/** The language references of the agent skill, by page. */
|
|
3
3
|
export const GUIDE_PAGES = {
|
|
4
|
-
language: "# The Meltemi language (0.2), flow profile\n\nA `.meltemi` file is UTF-8 text with LF or CRLF line breaks. Statements end at a line break or\n`;`; `//` and `/* */` are comments and are preserved by every tool. Identifiers match\n`[A-Za-z_][A-Za-z0-9_]*`; strings are double-quoted with `\\\"`, `\\\\`, `\\n` and `\\t` escapes;\nnumbers may carry a unit (`1200ms`, `2s`, `50%`).\n\n## File structure\n\n```text\nlanguage \"0.2\" // optional header, must come first\n\nimport \"../themes/company.meltemi\" as company // imports go before the blocks\nimport corporate from \"../themes/company.meltemi\"\n\ntheme <id> { <token> <value> ... } // optional, see themes.md\n\ndiagram <id>: flow { ... } // one or more diagrams\n```\n\n## Projects and imports\n\nA folder with `project.json` (`{ \"name\": \"...\", \"language\": \"0.2\" }`) is a project; files in it\nimport each other with relative paths (`./`, `../`, ending in `.meltemi`, never URLs).\n`import \"<path>\" as <ns>` makes `<ns>.<id>` available; `import <id> [as <alias>] from \"<path>\"`\nbinds ids directly. Use imported themes by name: `theme \"corporate\"`, `theme \"company.base\"`,\n`extends \"company.base\"`. Import cycles are errors (`module.cycle`). Validate a whole project with\n`pnpm mdg validate <project folder>`; the full rules are in the repository's\n`docs/language/projects.md`.\n\nEvery block compiles: `flow`, `free`, `sequence` and `c4` diagrams render, `story` blocks play\n([stories.md](stories.md)) and `export` profiles write deliveries (below); free compositions are described in [free.md](free.md), sequence\ndiagrams in [sequence.md](sequence.md), and models with C4 views in [c4.md](c4.md). Any element block may hold `link <diagram>.<element>`, which ties\nit to an element of another diagram.\n\n## Inside `diagram <id>: flow { }`\n\n| Statement | Meaning |\n| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |\n| `title \"...\"`, `description \"...\"` | SVG `<title>` and `<desc>`; the title also appears in the brand frame |\n| `layout auto`, `layout assisted`, `layout manual` | ELK arranges everything, the unplaced boxes only, or nothing (layout.md) |\n| `routing orthogonal` | connector shape: `auto` (default), `orthogonal`, `polyline`, `bezier` |\n| `theme \"<name>\"` | built-in or file theme; default the project theme or `technical.light` |\n| `preset document`, `preset slide`, `canvas (w, h)` | canvas size and limits (see themes.md) |\n| `class id { ... }`, `rule kind { ... }` | reusable styles and styles for every element of a kind (themes.md) |\n| `direction LR` | flow of the automatic layout (`LR`, `RL`, `TB`, `BT`) |\n| `layers back, main, front`; `layer back` in a block | paint order, back to front; `main` always exists; elements, groups and relations take `layer` |\n| `id[\"Label\"]` | node (rounded box) |\n| `id[(\"Label\")]` | data store (cylinder) |\n| `node id \"Label\"`, `decision id \"Label?\"`, `data id \"Label\"` | typed declarations; label defaults to the id |\n| `group id \"Label\" { ... }` | band holding declarations, relations and nested groups |\n| `group id \"\" { ... }` | a plain container: no title band, padding 16 px all round (free.md) |\n| `note id \"Line one\\nLine two\"` | free text, left-aligned, no border |\n| `tag id \"PKCE\"` | small pill with mono text |\n| `a -> b`, `a --> b`, `a <-> b`, `a <--> b` | solid or dashed relation, one way or two-way; undeclared ids become nodes |\n| `a -> b: \"Label\" as id` | relation with label and id |\n| `{ description \"...\" }` after an element or relation | second text line of a box; description of a relation |\n| `{ assumption \"why\" }` after a relation | an inferred relation, drawn dashed and muted until someone confirms it |\n| `{ class a, b }` after an element, group or relation | apply classes |\n| `{ port in west 30% }` after an element | a named port on a side, at a percentage along it (50 % by default) |\n| `placement { ... }` | positions, sizes, routes, label anchors (below) |\n| `style <t>[, <t>...] { ... }` | visual properties for elements or named relations |\n| `animate <t>[, <t>...] { ... }` | motion effects: flow particles, a moving dash, a pulse, a trace (below) |\n\nElements and relations share one namespace per diagram. Anonymous relations get the key\n`<from>-><to>#<n>` in tools and JSON; name a relation (`as id`) before routing or styling it.\n\n## Placement\n\n```text\nplacement {\n place api at (360, 180) // centre of the box\n size api (200, 56) // width, height\n route create from api.east to orders.west via [(520, 180)]\n label create at 50% offset (0, -16) // position of the relation label\n row api, orders // constraints: align, row, column,\n distance orders, db 80 // order and distance\n}\n```\n\nPorts: `north`, `south`, `east`, `west`, `auto` (`from api` means `api.auto`) and the ports an\nelement declares with `port`; an undeclared name is `placement.unknown-port`. A route's endpoints\nmust be the relation's source and target, in that order. Each element has at most one `place` and\none `size`; each relation at most one `route` and one `label`. Several `placement` blocks may\nexist; order does not matter.\n\n## Styles\n\n```text\nstyle api, db { fill \"#ebe5fd\"; stroke \"#6a4fd0\"; color \"#33247a\" }\nstyle obo, ciba, proxy { line dashed }\nstyle r1, r2 { fill \"#0B1830\"; color \"#FFFFFF\" } // dark label pills\nstyle legendBox { fill \"none\" }\n```\n\n| Property | Element | Relation |\n| --------------- | ------------------------------------------------------------------------------ | ------------------------------------------ |\n| `fill` | background (`\"none\"` for no fill) | label pill background |\n| `stroke` | outline colour | connector and arrowhead colour |\n| `color` | label and description text | label pill text |\n| `line` | `solid`, `dashed` or `dotted` outline | overrides the arrow style |\n| `width` | outline width (0 to 16) | connector width |\n| `radius` | corner radius (0 to 64) of boxes, groups | not allowed |\n| `shape` | `node` only: `hexagon`, `icon`… (free.md) | not allowed |\n| `pattern` | `hatch`, `dots`, `cross` or `none` | not allowed |\n| `arrowhead` | not allowed | head at the target (below) |\n| `arrowtail` | not allowed | head at the source (below) |\n| `routing` | not allowed | `auto`, `orthogonal`, `polyline`, `bezier` |\n| `corners` | not allowed | `sharp` or `rounded` bends |\n| `class` | apply classes | apply classes |\n| `icon` | `\"pack:name\"`, placed by `iconPosition` | inside the label pill |\n| `iconColor` | colour of a monochrome icon | same |\n| `iconPosition` | `left`, `right`, `top`, `bottom`, `center` | not allowed |\n| `labelPosition` | label edge of a centred icon or `shape icon`: `top`, `bottom`, `left`, `right` | not allowed |\n| `iconSize` | `auto` or 8 to 128: the icon's side | not allowed |\n\nColours are quoted (`\"#6a4fd0\"`) or colour token names that follow the theme (`danger`, `accent`,\n`muted`, `success`, `warning`, `info`, …).\n\nHeads (`arrowhead` at the target, `arrowtail` at the source): `triangle`, `open`, `none`,\n`triangleOutline` (UML generalisation), `diamond` and `diamondOutline` (composition and\naggregation), `circle`, `circleOutline`, `bar`, `cross`, and the ER crow's feet `many`,\n`oneOrMany`, `onlyOne`, `zeroOrOne`, `zeroOrMany`. A relation has no start head unless it is\nwritten `<->`/`<-->` (the end's head at both ends) or has `arrowtail`:\n\n```text\ncustomer -> order as places\nstyle places { arrowtail onlyOne; arrowhead zeroOrMany } // one customer places zero or more orders\napi <-> cache // two-way\nstyle slow { line dotted; corners rounded } // dotted, rounded bends\n```\n\nSetting the same property twice for a target across statements is `prop.duplicate`; different\nproperties in different statements are fine.\n\n## Animate\n\n```text\nanimate r1, r2 { effect flow; particles 2 } // particles along named relations\nanimate save { effect dash; direction reverse } // a dash moving along a line or outline\nanimate db { effect pulse; duration 1500ms; repeat 3; delay 300ms; color accent }\nanimate r1 { effect trace } // a highlight that fills the relation, then restarts\n```\n\n`effect` is required. `duration` 100 ms to 60 s (the theme's `flowDuration`, `dashDuration`,\n`pulseDuration` or `traceDuration`, else 1200 ms for flow, 1500 ms for trace and 1 s otherwise),\n`repeat` `forever` (default) or 1 to 1000, `delay`, `direction forward|reverse` (flow, dash and\ntrace), `particles 1..8` (flow), `color` (flow, pulse and trace; a trace defaults to `accent`).\nFlow and trace run on relations only; dash on relations and boxes with an outline; pulse on\nelements, relations, notes and fragments. One effect of each\nkind per target (`animate.conflict`). Exports are static unless you ask:\n`pnpm mdg export sheet.meltemi --motion animated` writes an animated SVG (CSS keyframes, static\nfallback), `--at 1.5s` the frame at that time. Use the static export for documents and slides.\n\n## Stories\n\n`story <id> for <diagram> { scene ... }` walks through a diagram: `reveal`, `hide`, `highlight`,\n`focus`, `camera`, `trace` and `hold` on one clock, `parallel` blocks, and `view` with a\n`transition` to change diagram. `pnpm mdg export sheet.meltemi --story <id> --at 2s` writes a\nframe. See [stories.md](stories.md).\n\n## Export profiles and links\n\n```text\nexport slide for checkout {\n format png // svg, png, pdf, html (player), mp4, webm, gif\n scale 2 // png pixels per unit\n size (1920, 1080) // png, pdf, video: fitted and centred\n background transparent // or a quoted colour or a token\n file \"slides/checkout.png\"\n}\n```\n\nPlugins installed in `project.json` may declare diagram types (`diagram s: erd { entity a \"A\" }`):\ntheir kinds are boxes with the type's style and properties; `pnpm mdg plugins <project>` lists them.\n`pnpm mdg convert <file> --to json` writes the file's model JSON for other tools.\n`pnpm mdg init <folder> --template flow|c4|sequence|free|purchase` starts a project;\n`pnpm mdg preview <project>` serves its catalog and reloads it as files change;\n`pnpm mdg review <project> --base main` lists what changed by id (renames included).\n`pnpm mdg build catalog <project>` writes one offline HTML catalog of the project (settings in\n`project.json` `catalog`; comments and source stay out unless `--source`). In the catalog and in\n`--format html` pages, a double click (or the plus sign) on a system opens its container view, on\na container its component view, on an element with `link` the diagram it names: write those\nviews (`level` + `scope`) and links so readers can drill down; descriptions show on hover.\n`pnpm mdg export sheet.meltemi --profiles` writes every profile; `--profile slide` one.\n`--format mp4|webm|gif` records a story (`--story`) or effects (`--duration` when they repeat\nforever) with the installed browser and FFmpeg: 1080p at 30 fps, GIF 960 × 540 at 15 fps. Options\nof another format are `export.invalid-option`; a video of effects that repeat forever needs\n`duration` (`export.missing-duration`). Elements, groups and model entities take\n`url \"https://…\"`: SVG, PDF and HTML exports keep the link.\n`pnpm mdg login`, `push`, `pull`, `publish` and `projects` keep a project in step with its copy in\nthe optional cloud service, which `project.json` records in `cloud`, and\n`pnpm mdg review <project> --base cloud` compares with it; run them only when asked (see SKILL.md).\n\n## Diagnostics and fixes\n\n| Code | Cause and fix |\n| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `syntax.expected-token`, `syntax.unexpected-token` | malformed statement; check quotes, `->`, parentheses, one statement per line |\n| `syntax.unterminated-string` | a string reached the end of the line; close it, use `\\n` for line breaks |\n| `syntax.unexpected-character` | characters such as `=>` or `#` outside strings; arrows are `->`, `-->`, `<->`, `<-->` |\n| `ref.duplicate-id` | the id is already an element, relation or top-level block; rename one |\n| `ref.unknown-element` | target of `place`, `size`, `style` or a qualified endpoint does not exist; declare it |\n| `ref.unknown-relation` | `route`/`label` on an unnamed relation; add `as <id>` |\n| `placement.route-mismatch` | `route` endpoints are reversed or wrong; use the relation's source then target |\n| `placement.duplicate` | second `place`/`size` for an element or `route`/`label` for a relation; delete one |\n| `placement.unknown-port` | the route names a port the element does not declare; the hint lists its ports |\n| `prop.unknown` | unknown word; the hint lists what the block accepts |\n| `prop.invalid-value` | wrong value shape; the message names the expected form; `shape` only on `node` and `box` |\n| `prop.duplicate` | property set twice for the same target; keep one |\n| `layout.missing-placement` | manual layout and an element without `place`, or a group with only one of `place`/`size` |\n| `layout.mode-unsupported` | a host without a layout engine met unplaced boxes; the command line always has ELK |\n| `layout.placement-ignored` | `place` or waypoints in `layout auto`; write `layout assisted` or `manual` |\n| `layout.constraint-conflict` | placed boxes break a constraint; move a `place` or change the constraint |\n| `layout.overlap` | two boxes overlap, or a box crosses a group edge; move it or add `distance`; a stack goes outer first |\n| `layout.label-under-connector` | a label sits on another arrow; move it with `label r at <n>% offset (dx, dy)` |\n| `layout.container-margin` | a box touches the edge or title band of its sized group; enlarge the group or move it |\n| `layout.connector-overlap` | two arrows run along each other; use other ports, a waypoint or `routing bezier` |\n| `layout.connector-crossing` | an arrow runs through a box it does not join; move the box or add a waypoint |\n| `layout.unknown-theme` | theme name is neither built in nor in the file; warning, renders with the default |\n| `theme.unknown-base`, `theme.cycle` | `extends` names a missing theme or loops; point it at a built-in theme |\n| `theme.unknown-class` | a class is not in the diagram, its theme or the built-in ones; declare it or fix the name |\n| `asset.unknown-icon`, `asset.unknown-pack` | the icon name or pack does not exist; search with `pnpm mdg assets icons <words>` |\n| `layout.too-large` | the sheet exceeds its `preset` or `canvas`; split it into views instead of shrinking |\n| `layout.text-overflow` | an explicit `size` is too narrow for its text; use the width in the hint |\n| `layout.icon-missing` | `shape icon` without an `icon`: a dashed placeholder is drawn; add the icon or change the shape |\n| `layout.label-position-ignored` | `labelPosition` without `iconPosition center` or `shape icon`; add one of them or remove it |\n| `animate.missing-effect`, `animate.invalid-target` | write `effect`; flow and trace on named relations, dash on lines and boxes, pulse anywhere |\n| `animate.conflict` | one effect of each kind per target; merge the two statements |\n| `story.*` | stories; see [stories.md](stories.md) |\n| `export.*` | export profiles: a known target, a `format`, options of that format, a video `duration` |\n| `ref.unknown-link` | a `link` names a diagram or element that does not exist; fix it or remove the link |\n| `ref.unknown-layer`, `asset.*-image`, `layout.image-unavailable` | free compositions; see [free.md](free.md) |\n| `export.no-diagram` | no flow or free diagram in the file |\n",
|
|
5
|
-
sequence: "# Sequence diagrams\n\nUse `diagram <id>: sequence { }` for interactions over time: who calls whom, in what order, with\nwhich alternatives. Participants stand left to right in source order and events go top to bottom\nin source order. Never write `place`, `size` or `route` here; the layout is computed.\n\n## Participants\n\n`participant`, `actor`, `boundary`, `control`, `entity`, `database`, `collections` and `queue`,\neach `<kind> <id> \"Label\"`. `id[\"Label\"]` is a participant and `id[(\"Label\")]` a database. A\nmessage end that is never declared becomes a participant labelled with its id, placed at its first\nmention. `group backend \"Backend\" { participant api \"API\" }` draws a box around participants.\n\n## Messages and events\n\n```text\ncustomer -> api: \"Pay\" as pay // call: solid, filled arrowhead\napi --> customer: \"Receipt\" // reply: dashed, open arrowhead\napi -> events: \"Paid\" as paid { kind async } // async: solid, open arrowhead\napi <-> cache: \"Sync\" // two-way: a head at both ends\napi -> api: \"Validate\" // self-call\nactivate api // bar from the message before; deactivate ends it\ncreate order // the head appears at this row\ndestroy order // the lifeline ends with a cross\nnote over api, bank \"Timeout 3 s\" // also: note left of api \"…\", note right of api \"…\"\ndivider \"Fulfilment\"\ndelay \"Two days later\"\nautonumber // numbers every message from 1 (autonumber 10: from 10)\n```\n\n## Fragments\n\n```text\nalt result \"Approved\" {\n bank --> api: \"OK\"\n} else \"Declined\" {\n bank --> api: \"Declined\"\n}\nopt coupon \"Coupon applied\" { … }\nloop polling \"Until shipped\" { … }\npar { … } and \"Notify\" { … }\nbreak \"Fraud\" { … }\ncritical \"Shipment\" { … } option \"Carrier down\" { … }\nregion \"Audit\" { … }\n```\n\nWrite `else`, `and` and `option` on the line of the closing `}`. Give a fragment an id (`alt\nresult …`) when a scenario or `space` must name it. Fragments hold events only: declare every\nparticipant at the diagram level first.\n\n## Scenarios\n\n```text\nscenario approved \"Payment approved\" {\n choose result \"Approved\" // or: choose result 1\n repeat polling 3 // 1 to 100 passes\n skip coupon // opt or break not taken; take coupon keeps it (the default)\n}\n```\n\n`pnpm mdg export sheet.meltemi --scenario approved` writes `sheet.approved.svg` with only that\npath; without `--scenario` every branch is drawn.\n\n## Spacing and styles\n\n```text\nplacement {\n space authorize 28
|
|
6
|
-
c4: "# Models and C4 views\n\nUse a `model` block for the architecture (who uses what, which containers a system has) and C4\nviews to draw it at one level each. Declare entities once in the model; views only list them.\n\n## Model\n\n```text\nmodel commerce {\n person customer \"Cliente\" { description \"Compra en línea\" }\n system shop \"Commerce\" {\n container web \"Web\" { technology \"React\" }\n container api \"API\" {\n technology \"Kotlin\"\n component orders \"Pedidos\"\n }\n datastore db \"Pedidos\" { technology \"PostgreSQL\" }\n api -> db: \"SQL\" as persist // inside shop: api is shop.api\n }\n system bank \"Banco\" { external }\n\n customer -> shop.web: \"Compra\" as visit\n shop.web -> shop.api: \"HTTPS\" as request { technology \"JSON/HTTPS\" }\n shop.api.orders -> bank: \"Autoriza\" as authorize\n}\n```\n\n- Persons and systems at the root; containers and datastores in systems; components in containers\n (`model.invalid-nesting` otherwise).\n- Entities are named by path: `shop.api.orders`. Relations use `->` only and name paths.\n- `external` marks persons and systems outside the organisation (drawn grey and dashed).\n\n## Views\n\n```text\ndiagram containers: c4 using commerce {\n level container // context | container | component\n scope shop // a system; a container for component views\n include customer, shop.web, shop.api, shop.db\n label shop.api \"API de pedidos\" // local text, the model keeps \"API\"\n direction LR\n layout assisted\n placement { place shop.api at (420, 120) }\n style shop.db { fill accent }\n}\n```\n\n- Without `include`, a view draws its default set: everything at the context level, otherwise\n the scope's children and the entities related to them. `exclude` removes entities.\n- A container view draws the scope's containers and datastores (inside a dashed boundary),\n persons and other systems; a component view the scope's components, the other containers of the\n system, persons and other systems. Anything else is `c4.out-of-level`.\n- Relations come from the model; ends inside hidden entities are lifted to the drawn ancestor\n (at the context level `customer -> shop.web` becomes `customer -> shop`). Two relations to the\n same pair stay two arrows; to draw one, write a combination in the view:\n `customer -> shop: \"Usa\" as uses`. A combination with no model relation behind it is\n `c4.unsupported-relation`.\n- Placement, styles and routes take paths: `route request from shop.web.east to shop.api.west`.\n\n## Files\n\nKeep the model in `models/` and the views in `diagrams/` of a project, and import the model:\n\n```text\nimport commerce from \"../models/commerce.meltemi\"\ndiagram context: c4 using commerce { level context }\n```\n\nSequence and free objects link to entities with `link commerce.shop.api`; a participant without its\nown label shows the entity's.\n\n## Diagnostics\n\n| Code | Fix |\n| ------------------------- | ------------------------------------------------------------------------ |\n| `c4.invalid-level` | add `level context`, `level container` or `level component` |\n| `c4.scope-required` | add `scope <system>` (container view) or `scope <container>` (component) |\n| `c4.invalid-scope` | scope a system or a container; datastores have no components |\n| `c4.out-of-level` | remove the entity from `include`, or change the level |\n| `c4.unsupported-relation` | add the relation to the model, or remove the combination |\n| `ref.unknown-entity` | use the full path from the model's root (`shop.api`, not `api`) |\n| `ref.unknown-model` | declare or import the model named after `using` |\n| `model.invalid-nesting` | move the entity into the right parent |\n| `model.invalid-relation` | relate separate entities, not an entity and its parent |\n",
|
|
7
|
-
free: "# Free compositions\n\nUse `diagram <id>: free { }` for a slide, an executive picture or an explanation that does not fit\na flow notation. Objects are placed by hand (`layout manual` is the default); relations,\n`placement`, constraints, `style`, `class`, `rule`, `animate`, themes, `preset` and `canvas` work\nas in flow diagrams.\n\n## Objects\n\n| Declaration | Object | Default size |\n| ----------------------------- | ----------------------------------------------------------------------- | ------------------------------------- |\n| `box <id> \"Label\"`, `id[\"L\"]` | a shape with a label and an optional `description` | its shape's size, widened to its text |\n| `id[(\"Label\")]` | a box with the `cylinder` shape | 160 × 64 |\n| `text <id> \"Text\\nmore\"` | measured text without a frame | its extent |\n| `icon <id> \"tabler:server\"` | an icon of the packs; find names with `pnpm mdg assets icons <w>` | 48 × 48 |\n| `image <id> \"img/logo.png\"` | a PNG, JPEG, GIF, WebP or SVG file of the project, relative to the file | its pixel size, at most 640 wide |\n| `group <id> \"Label\" { ... }` | a band holding objects and relations; `group <id> \"\" { }` is plain | written, or fitted to children |\n\n`node`, `decision`, `data`, `note` and `tag` are flow kinds and are refused here.\n\n## Shapes and corners\n\n`style a { shape hexagon }` draws a box (or a flow `node`) as one of twenty-four shapes. Each has a\ndefault size (technical themes; it scales with the label size), keeps its text inside its outline\nand grows until the text fits, and connectors end on its outline:\n\n| Size | Shapes |\n| --------- | ------------------------------------------------------------------------------------ |\n| 160 × 56 | `rounded` (default), `rectangle`, `pill`, `parallelogram`, `arrow`, `framed`, `flag` |\n| 160 × 64 | `cylinder`, `trapezoid`, `hexagon` |\n| 160 × 72 | `ellipse`, `octagon`, `document`, `callout` |\n| 160 × 80 | `diamond` |\n| 160 × 96 | `triangle`, `pentagon` |\n| 160 × 104 | `person` |\n| 160 × 120 | `agent` |\n| 120 × 120 | `square`, `circle`, `star` |\n| 180 × 100 | `cloud` |\n| 64 × 64 | `icon` (or its `iconSize`), the label outside (below) |\n\n- `square` and `circle` are a rectangle and an ellipse with a square default size; `arrow` points\n right; `framed` has two inner lines (a subroutine); `callout` has a tail at the bottom left.\n- `person` is a round head over a body; `agent` is an AI agent, a robot head (eyes, mouth and an\n antenna) over a body. Both keep the text in the body; an end on the north side meets the head.\n- `\\n` breaks a box's or a node's label into lines, as in a `text`.\n- A `size` too narrow for the text inside a shape is `layout.text-overflow`; use the hint's width.\n- A pinned end (`route r from a.east to d.north 30%`) is drawn where the side's normal meets the\n outline, so arrows touch diamonds, ellipses and stars.\n- `radius 0..64` sets the corner radius of boxes, nodes, notes and groups (`radius 0` squares\n them); `rectangle`, `square` and `framed` are always square, the other shapes ignore it. The\n theme's `radius` (8) and `groupRadius` (14) are the defaults.\n- Decisions, data stores, notes, tags, groups, texts, icons and images refuse `shape`, in `style`\n statements as in rules: their kind is their drawing. Only `rule box` and `rule node` may set it.\n\nFree-only text properties (refused in flow diagrams):\n\n- `fontSize 6..160`, `fontWeight 400|500|600|700`, `textAlign left|center|right` on boxes and\n text (text defaults to 1.6 × the label size, left aligned);\n- a text, icon or image with `fill`, `stroke` or `pattern` gets a frame.\n\n## Icons in boxes and as shapes\n\nFlow diagrams take these too. The `icon` of a node, decision, data store or box goes beside the\ntext (`iconPosition left`, the default, or `right`), above or below it (`top`, `bottom`), or at the\ncentre as the picture of the box (`center`), with the label in a band at the `labelPosition` edge\ninside (`bottom` by default). `shape icon` draws the element as its icon alone, with the label\noutside at `labelPosition`, as provider icons are drawn in architecture diagrams:\n\n```text\nbox gateway \"API Gateway\"\nbox fn \"Checkout\"\nbox orders \"Orders\"\nbox tile \"Orders DB\"\ngateway -> fn\nfn -> orders\n\nstyle gateway { icon \"aws:api-gateway\"; shape icon } // label below the icon\nstyle fn { icon \"aws:lambda\"; shape icon; labelPosition top } // label above\nstyle orders { icon \"aws:dynamodb\"; shape icon; fill surface; stroke line } // framed\nstyle tile { icon \"tabler:database\"; iconPosition center } // 120 × 120 tile\n```\n\n- `iconSize` is the icon's side: 8 to 128 (px or unitless) or `auto`. Beside the text the theme's\n 18 px is the default; a number grows a box without `size`, and is clamped (never cut) in a box\n with a written `size`, where `auto` fills the room. A centred icon is as large as fits; a\n written `size` without room for an 8 px icon beside the label band is `layout.text-overflow`.\n- An icon shape is a 64 × 64 square (or `iconSize`; a written `size` wins, the icon fitted\n inside), with nothing behind the icon unless `fill`, `stroke` or `pattern` frames it; a frame\n adds half the padding on each side (an 80 × 80 square around a 64 px icon). Arrows, ports and\n anchors end on the square (`to fn.south` is the icon's bottom edge); groups and the canvas\n count the label too, and a box over the label is `layout.overlap`, so leave room for it. A\n connector on the label's side is drawn from beyond the label; other routes go around it.\n- `labelPosition` does nothing unless the icon is the centre piece: `layout.label-position-ignored`.\n `shape icon` without an `icon` is `layout.icon-missing` and draws a dashed placeholder.\n- A free `icon <id> \"pack:name\"` object is an icon without text; for a labelled icon use a box\n with `shape icon`.\n\n## Coordinates\n\n- `place` is the centre, as in flow diagrams.\n- **Inside a group, `place` is relative to the group's interior**: its top-left corner plus\n `groupPadding` (16) across and the header down: `groupHeader` (40) in the technical themes, or\n `groupPadding` (16) for an untitled group (`\"\"`, no description, no icon). A child at\n `(w/2, h/2)` touches the interior's top-left corner. Moving the group moves its children.\n- Every group needs a `place`. Without `size` it fits its children: padding + rightmost child edge\n - padding wide, header + lowest child edge + padding tall.\n- Route waypoints are always diagram coordinates.\n\n## Containers, stacks and bands\n\nThese are the pictures of a visual editor's containers and backdrops; flow diagrams take them too.\n\n```text\ngroup outer \"\" { // nested plain rectangles: untitled groups\n group inner \"\" {\n box core \"Core\"\n }\n}\nbox plate \"\" // a stack: the outer box first, empty label\nbox card \"Card\"\ngroup band \"\" { layer back } // a band behind arranged boxes (layers back, main)\n\nstyle outer { fill \"#dae8fc\"; stroke \"#6c8ebf\"; radius 0 }\nstyle inner { fill \"#fff2cc\"; stroke \"#d6b656\"; radius 0 }\nstyle band { fill \"#f5f5f5\"; radius 0 }\n```\n\n- **Untitled groups** draw only their outline, have no title band and cut no gap in connectors;\n children keep half the padding (8 px) from every edge (`layout.container-margin`).\n- **Stacks**: a box lying strictly inside a box declared before it on the same layer is not\n `layout.overlap`; a partial overlap still is, so are two boxes that coincide, and so is a stack\n declared inner first (its hint says to declare the outer box first). A\n container's own label may lie over its children, but two labels at the same point are\n `layout.label-overlap`: leave the outer labels empty.\n- **Bands**: a group with `place` and `size` is neither arranged by the engine nor an obstacle, so\n on a back layer it stays behind the boxes `layout assisted` arranges (pin one box with `place`\n so the others arrange around it). Boxes and groups on different layers are not checked against\n each other, and connectors cross them straight. In `layout auto` a group's `place` is ignored:\n make the band a group holding its boxes.\n\n## Layers, order and links\n\n- `layers back, main, front` lists layers back to front; `main` is the default and comes last\n when unlisted. Put `layer front` in an object's or relation's block.\n- Within a layer, groups paint first, then objects in source order, then relations: declare what\n must be on top later.\n- `link checkout.api` in a block records that the object stands for element `api` of `diagram\ncheckout` (same file), `link co.api` through `import checkout as co from \"...\"`, or `link\nviews.checkout.api` through `import \"...\" as views`. Identity only; nothing is kept in step.\n\n## From a flow diagram\n\n`pnpm mdg convert sheet.meltemi --to free [--diagram <id>] [--id <new>]` adds a free copy of the\nflow diagram as drawn: every box with `place` and `size`, children inside their copied groups,\nlayers, routes, labels, styles and animations, a `link` from each object to its source, and\nclasses `from_decision`, `from_data`, `from_note`, `from_tag` for the drawing of the flow kinds.\nEdit the copy freely; the flow diagram is unchanged.\n\n## Diagnostics\n\n| Code | Fix |\n| ------------------------------- | -------------------------------------------------------------------------------------------- |\n| `ref.unknown-layer` | add the layer to `layers` (the hint gives the statement) or fix its name |\n| `ref.unknown-link` | the diagram or element does not exist; fix the name or remove the `link` |\n| `asset.outside-project` | image paths are relative, use `/`, no URLs, and stay inside the project |\n| `asset.unknown-image` | the file does not exist next to the path written |\n| `asset.unsupported-image` | not a PNG, JPEG, GIF, WebP or SVG image, broken, or over 8 MB |\n| `layout.image-unavailable` | the host could not read the image; a placeholder is drawn |\n| `layout.missing-placement` | an object or a group has no `place` |\n| `layout.overlap` | boxes overlap partly, or a stack is declared inner first: declare the outer box first |\n| `layout.icon-missing` | `shape icon` without an `icon` style: add `icon \"pack:name\"` or choose another shape |\n| `layout.label-position-ignored` | `labelPosition` only moves the label of `iconPosition center` and `shape icon` elements |\n| `prop.invalid-value` | `fontSize`, `fontWeight` and `textAlign` are free-only; `shape` is for `node` and `box` only |\n",
|
|
8
|
-
stories: "# Stories\n\nA `story` tells a diagram step by step for a walkthrough, a talk or a recorded demo. It never\nchanges the diagram: scenes reveal, hide, highlight, focus, frame and
|
|
4
|
+
language: "# The Meltemi language (0.2), flow profile\n\nA `.meltemi` file is UTF-8 text with LF or CRLF line breaks. Statements end at a line break or\n`;`; `//` and `/* */` are comments and are preserved by every tool. Identifiers match\n`[A-Za-z_][A-Za-z0-9_]*`; strings are double-quoted with `\\\"`, `\\\\`, `\\n` and `\\t` escapes;\nnumbers may carry a unit (`1200ms`, `2s`, `50%`).\n\n## File structure\n\n```text\nlanguage \"0.2\" // optional header, must come first\n\nimport \"../themes/company.meltemi\" as company // imports go before the blocks\nimport corporate from \"../themes/company.meltemi\"\n\ntheme <id> { <token> <value> ... } // optional, see themes.md\n\ndiagram <id>: flow { ... } // one or more diagrams\n```\n\n## Projects and imports\n\nA folder with `project.json` (`{ \"name\": \"...\", \"language\": \"0.2\" }`) is a project; files in it\nimport each other with relative paths (`./`, `../`, ending in `.meltemi`, never URLs).\n`import \"<path>\" as <ns>` makes `<ns>.<id>` available; `import <id> [as <alias>] from \"<path>\"`\nbinds ids directly. Use imported themes by name: `theme \"corporate\"`, `theme \"company.base\"`,\n`extends \"company.base\"`. Import cycles are errors (`module.cycle`). Validate a whole project with\n`pnpm mdg validate <project folder>`; the full rules are in the repository's\n`docs/language/projects.md`.\n\nEvery block compiles: `flow`, `free`, `sequence` and `c4` diagrams render, `story` blocks play\n([stories.md](stories.md)) and `export` profiles write deliveries (below); free compositions are described in [free.md](free.md), sequence\ndiagrams in [sequence.md](sequence.md), and models with C4 views in [c4.md](c4.md). Any element block may hold `link <diagram>.<element>`, which ties\nit to an element of another diagram.\n\n## Inside `diagram <id>: flow { }`\n\n| Statement | Meaning |\n| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |\n| `title \"...\"`, `description \"...\"` | SVG `<title>` and `<desc>`; the title also appears in the brand frame |\n| `layout auto`, `layout assisted`, `layout manual` | ELK arranges everything, the unplaced boxes only, or nothing (layout.md) |\n| `routing orthogonal` | connector shape: `auto` (default), `orthogonal`, `polyline`, `bezier` |\n| `theme \"<name>\"` | built-in or file theme; default the project theme or `technical.light` |\n| `preset document`, `preset slide`, `preset board`, `canvas (w, h)` | canvas size and limits; `board` grows with a whiteboard (see themes.md) |\n| `class id { ... }`, `rule kind { ... }` | reusable styles and styles for every element of a kind (themes.md) |\n| `direction LR` | flow of the automatic layout (`LR`, `RL`, `TB`, `BT`) |\n| `layers back, main, front`; `layer back` in a block | paint order, back to front; `main` always exists; elements, groups and relations take `layer` |\n| `id[\"Label\"]` | node (rounded box) |\n| `id[(\"Label\")]` | data store (cylinder) |\n| `node id \"Label\"`, `decision id \"Label?\"`, `data id \"Label\"` | typed declarations; label defaults to the id |\n| `group id \"Label\" { ... }` | band holding declarations, relations and nested groups |\n| `group id \"\" { ... }` | a plain container: no title band, padding 16 px all round (free.md) |\n| `note id \"Line one\\nLine two\" [on element]` | free text, left-aligned, no border; `on` attaches it to an element with a dotted leader |\n| `tag id \"PKCE\"` | small pill with mono text |\n| `a -> b`, `a --> b`, `a <-> b`, `a <--> b` | solid or dashed relation, one way or two-way; undeclared ids become nodes |\n| `a -- b`, `a --- b` | a solid or dashed line without heads between two elements |\n| `a -> b: \"Label\" as id` | relation with label and id |\n| `{ description \"...\" }` after an element or relation | second text line of a box; description of a relation |\n| `{ assumption \"why\" }` after a relation | an inferred relation, drawn dashed and muted until someone confirms it |\n| `{ class a, b }` after an element, group or relation | apply classes |\n| `{ port in west 30% }` after an element | a named port on a side, at a percentage along it (50 % by default) |\n| `placement { ... }` | positions, sizes, routes, label anchors (below) |\n| `style <t>[, <t>...] { ... }` | visual properties for elements or named relations |\n| `animate <t>[, <t>...] { ... }` | motion effects: flow particles, a moving dash, a pulse, a trace (below) |\n\nElements and relations share one namespace per diagram. Anonymous relations get the key\n`<from>-><to>#<n>` in tools and JSON; name a relation (`as id`) before routing or styling it.\n\n## Placement\n\n```text\nplacement {\n place api at (360, 180) // centre of the box\n size api (200, 56) // width, height\n route create from api.east to orders.west via [(520, 180)]\n label create at 50% offset (0, -16) // position of the relation label\n row api, orders // constraints: align, row, column,\n distance orders, db 80 // order and distance\n}\n```\n\nPorts: `north`, `south`, `east`, `west`, `auto` (`from api` means `api.auto`) and the ports an\nelement declares with `port`; an undeclared name is `placement.unknown-port`. A route's endpoints\nmust be the relation's source and target, in that order. Each element has at most one `place` and\none `size`; each relation at most one `route` and one `label`. Several `placement` blocks may\nexist; order does not matter.\n\n## Styles\n\n```text\nstyle api, db { fill \"#ebe5fd\"; stroke \"#6a4fd0\"; color \"#33247a\" }\nstyle obo, ciba, proxy { line dashed }\nstyle r1, r2 { fill \"#0B1830\"; color \"#FFFFFF\" } // dark label pills\nstyle legendBox { fill \"none\" }\n```\n\n| Property | Element | Relation |\n| --------------- | ------------------------------------------------------------------------------ | ------------------------------------------ |\n| `fill` | background (`\"none\"` for no fill) | label pill background |\n| `stroke` | outline colour | connector and arrowhead colour |\n| `color` | label and description text | label pill text |\n| `line` | `solid`, `dashed` or `dotted` outline | overrides the arrow style |\n| `width` | outline width (0 to 64; a pen's stroke 1 to 64) | connector width |\n| `radius` | corner radius (0 to 64) of boxes, groups | not allowed |\n| `shape` | `node` only: `hexagon`, `icon`… (free.md) | not allowed |\n| `pattern` | `hatch`, `dots`, `cross` or `none` | not allowed |\n| `arrowhead` | not allowed | head at the target (below) |\n| `arrowtail` | not allowed | head at the source (below) |\n| `routing` | not allowed | `auto`, `orthogonal`, `polyline`, `bezier` |\n| `corners` | not allowed | `sharp` or `rounded` bends |\n| `class` | apply classes | apply classes |\n| `icon` | `\"pack:name\"`, placed by `iconPosition` | inside the label pill |\n| `iconColor` | colour of a monochrome icon | same |\n| `iconPosition` | `left`, `right`, `top`, `bottom`, `center` | not allowed |\n| `labelPosition` | label edge of a centred icon or `shape icon`: `top`, `bottom`, `left`, `right` | not allowed |\n| `iconSize` | `auto` or 8 to 128: the icon's side | not allowed |\n\nColours are quoted (`\"#6a4fd0\"`) or colour token names that follow the theme (`danger`, `accent`,\n`muted`, `success`, `warning`, `info`, …).\n\nHeads (`arrowhead` at the target, `arrowtail` at the source): `triangle`, `open`, `none`,\n`triangleOutline` (UML generalisation), `diamond` and `diamondOutline` (composition and\naggregation), `circle`, `circleOutline`, `bar`, `cross`, and the ER crow's feet `many`,\n`oneOrMany`, `onlyOne`, `zeroOrOne`, `zeroOrMany`. A relation has no start head unless it is\nwritten `<->`/`<-->` (the end's head at both ends) or has `arrowtail`. A line `--` (dashed `---`)\nhas no head at either end unless `arrowhead` or `arrowtail` is written; write it for an\nassociation, a link to a note or a plain connection, instead of `arrowhead none`:\n\n```text\ncustomer -> order as places\nstyle places { arrowtail onlyOne; arrowhead zeroOrMany } // one customer places zero or more orders\napi <-> cache // two-way\napi -- db // a line, no heads\nnote1 --- api // a dashed line, no heads\nstyle slow { line dotted; corners rounded } // dotted, rounded bends\n```\n\nThe six tokens are `->`, `-->`, `--`, `---`, `<->`, `<-->`; `--->` is a syntax error. Sequence\ndiagrams take the four arrows and C4 views `->` and `-->`: another token is `prop.invalid-value`.\n\nSetting the same property twice for a target across statements is `prop.duplicate`; different\nproperties in different statements are fine.\n\n## Animate\n\n```text\nanimate r1, r2 { effect flow; particles 2 } // particles along named relations\nanimate save { effect dash; direction reverse } // a dash moving along a line or outline\nanimate db { effect pulse; duration 1500ms; repeat 3; delay 300ms; color accent }\nanimate r1 { effect trace } // a highlight that fills the relation, then restarts\n```\n\n`effect` is required. `duration` 100 ms to 60 s (the theme's `flowDuration`, `dashDuration`,\n`pulseDuration` or `traceDuration`, else 1200 ms for flow, 1500 ms for trace and 1 s otherwise),\n`repeat` `forever` (default) or 1 to 1000, `delay`, `direction forward|reverse` (flow, dash and\ntrace), `particles 1..8` (flow), `color` (flow, pulse and trace; a trace defaults to `accent`).\nFlow and trace run on relations (and free lines and pens) only; dash on relations, free lines,\npens, boxes with an outline and notes or texts that style an outline (`fill`, `stroke` or\n`pattern`); pulse on elements, relations, notes and fragments. One effect of each kind per target\n(`animate.conflict`). Exports are static unless you ask:\n`pnpm mdg export sheet.meltemi --motion animated` writes an animated SVG (CSS keyframes, static\nfallback), `--at 1.5s` the frame at that time. Use the static export for documents and slides.\n\n## Stories\n\n`story <id> for <diagram> { scene ... }` walks through a diagram: `reveal`, `hide`, `highlight`,\n`focus`, `camera`, `trace`, `draw` (pens, lines and relations drawn again) and `hold` on one\nclock, `parallel` blocks, and `view` with a `transition` to change diagram.\n`pnpm mdg export sheet.meltemi --story <id> --at 2s` writes a frame. See [stories.md](stories.md).\n\n## Export profiles and links\n\n```text\nexport slide for checkout {\n format png // svg, png, pdf, html (player), mp4, webm, gif\n scale 2 // png pixels per unit\n size (1920, 1080) // png, pdf, video: fitted and centred\n background transparent // or a quoted colour or a token\n file \"slides/checkout.png\"\n}\n```\n\nPlugins installed in `project.json` may declare diagram types (`diagram s: erd { entity a \"A\" }`):\ntheir kinds are boxes with the type's style and properties; `pnpm mdg plugins <project>` lists them.\n`pnpm mdg convert <file> --to json` writes the file's model JSON for other tools.\n`pnpm mdg init <folder> --template flow|c4|sequence|free|purchase` starts a project;\n`pnpm mdg preview <project>` serves its catalog and reloads it as files change;\n`pnpm mdg review <project> --base main` lists what changed by id (renames included).\n`pnpm mdg build catalog <project>` writes one offline HTML catalog of the project (settings in\n`project.json` `catalog`; comments and source stay out unless `--source`). In the catalog and in\n`--format html` pages, a double click (or the plus sign) on a system opens its container view, on\na container its component view, on an element with `link` the diagram it names: write those\nviews (`level` + `scope`) and links so readers can drill down; descriptions show on hover.\n`pnpm mdg export sheet.meltemi --profiles` writes every profile; `--profile slide` one.\n`--format mp4|webm|gif` records a story (`--story`) or effects with the installed browser and\nFFmpeg: 1080p at 30 fps, GIF 960 × 540 at 15 fps. Effects that repeat forever last one loop of their\ncycles (6 s with the defaults) unless `--duration` or a profile's `duration` says otherwise, and the\nexport notes the length it chose; a transparent MP4 takes the theme's background. Options of\nanother format are `export.invalid-option`. Elements, groups and model entities take\n`url \"https://…\"`: SVG, PDF and HTML exports keep the link.\n`pnpm mdg login`, `push`, `pull`, `publish` and `projects` keep a project in step with its copy in\nthe optional cloud service, which `project.json` records in `cloud`, and\n`pnpm mdg review <project> --base cloud` compares with it; run them only when asked (see SKILL.md).\n\n## Diagnostics and fixes\n\n| Code | Cause and fix |\n| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `syntax.expected-token`, `syntax.unexpected-token` | malformed statement; check quotes, `->`, parentheses, one statement per line |\n| `syntax.unterminated-string` | a string reached the end of the line; close it, use `\\n` for line breaks |\n| `syntax.unexpected-character` | characters such as `=>`, `--->` or `#` outside strings; the relation tokens are `->`, `-->`, `--`, `---`, `<->`, `<-->` |\n| `ref.duplicate-id` | the id is already an element, relation or top-level block; rename one |\n| `ref.unknown-element` | target of `place`, `size`, `style` or a qualified endpoint does not exist; declare it |\n| `ref.unknown-relation` | `route`/`label` on an unnamed relation; add `as <id>` |\n| `placement.route-mismatch` | `route` endpoints are reversed or wrong; use the relation's source then target |\n| `placement.duplicate` | second `place`/`size` for an element or `route`/`label` for a relation; delete one |\n| `placement.unknown-port` | the route names a port the element does not declare; the hint lists its ports |\n| `prop.unknown` | unknown word; the hint lists what the block accepts |\n| `prop.invalid-value` | wrong value shape; the message names the expected form; `shape` only on `node` and `box`; a relation token the profile refuses (`--` in a sequence) |\n| `prop.duplicate` | property set twice for the same target; keep one |\n| `layout.missing-placement` | manual layout and an element without `place`, or a group with only one of `place`/`size` |\n| `layout.mode-unsupported` | a host without a layout engine met unplaced boxes; the command line always has ELK |\n| `layout.placement-ignored` | `place` or waypoints in `layout auto`; write `layout assisted` or `manual` |\n| `layout.constraint-conflict` | placed boxes break a constraint; move a `place` or change the constraint |\n| `layout.overlap` | two boxes overlap, or a box crosses a group edge; move it or add `distance`; a stack goes outer first |\n| `layout.label-under-connector` | a label sits on another arrow; move it with `label r at <n>% offset (dx, dy)` |\n| `layout.container-margin` | a box touches the edge or title band of its sized group; enlarge the group or move it |\n| `layout.connector-overlap` | two arrows run along each other; use other ports, a waypoint or `routing bezier` |\n| `layout.connector-crossing` | an arrow runs through a box it does not join; move the box or add a waypoint |\n| `layout.unknown-theme` | theme name is neither built in nor in the file; warning, renders with the default |\n| `theme.unknown-base`, `theme.cycle` | `extends` names a missing theme or loops; point it at a built-in theme |\n| `theme.unknown-class` | a class is not in the diagram, its theme or the built-in ones; declare it or fix the name |\n| `asset.unknown-icon`, `asset.unknown-pack` | the icon name or pack does not exist; search with `pnpm mdg assets icons <words>` |\n| `layout.too-large` | the sheet exceeds its `preset` or `canvas`; split it into views instead of shrinking, or write `preset board` for a whiteboard |\n| `layout.text-overflow` | an explicit `size` is too narrow for its text; use the width in the hint |\n| `layout.unmeasured-font` | a theme's or a style's `font` names no bundled or project family, or characters no font has: name a bundled family |\n| `layout.icon-missing` | `shape icon` without an `icon`: a dashed placeholder is drawn; add the icon or change the shape |\n| `layout.label-position-ignored` | `labelPosition` without `iconPosition center` or `shape icon`; add one of them or remove it |\n| `animate.missing-effect`, `animate.invalid-target` | write `effect`; flow and trace on named relations, free lines and pens, dash on lines, pens and boxes, pulse anywhere |\n| `animate.conflict` | one effect of each kind per target; merge the two statements |\n| `story.*` | stories; see [stories.md](stories.md) |\n| `export.*` | export profiles: a known target, a `format`, options of that format, a video `duration` |\n| `ref.unknown-link` | a `link` names a diagram or element that does not exist; fix it or remove the link |\n| `ref.unknown-layer`, `asset.*-image`, `layout.image-unavailable` | free compositions; see [free.md](free.md) |\n| `export.no-diagram` | no flow or free diagram in the file |\n",
|
|
5
|
+
sequence: "# Sequence diagrams\n\nUse `diagram <id>: sequence { }` for interactions over time: who calls whom, in what order, with\nwhich alternatives. Participants stand left to right in source order and events go top to bottom\nin source order. Never write `place`, `size` or `route` here; the layout is computed.\n\n## Participants\n\n`participant`, `actor`, `boundary`, `control`, `entity`, `database`, `collections` and `queue`,\neach `<kind> <id> \"Label\"`. `id[\"Label\"]` is a participant and `id[(\"Label\")]` a database. A\nmessage end that is never declared becomes a participant labelled with its id, placed at its first\nmention. `group backend \"Backend\" { participant api \"API\" }` draws a box around participants.\n\n## Messages and events\n\n```text\ncustomer -> api: \"Pay\" as pay // call: solid, filled arrowhead\napi --> customer: \"Receipt\" // reply: dashed, open arrowhead\napi -> events: \"Paid\" as paid { kind async } // async: solid, open arrowhead\napi <-> cache: \"Sync\" // two-way: a head at both ends\napi -> api: \"Validate\" // self-call\nactivate api // bar from the message before; deactivate ends it\ncreate order // the head appears at this row\ndestroy order // the lifeline ends with a cross\nnote over api, bank \"Timeout 3 s\" // also: note left of api \"…\", note right of api \"…\"\ndivider \"Fulfilment\"\ndelay \"Two days later\"\nautonumber // numbers every message from 1 (autonumber 10: from 10)\n```\n\nA message label stays on one line and holds its two participants apart as far as it needs; a\nnegative `gap` or a `spacing` below 40 pulls them closer than that by its amount, and the label then\nwraps at the room left between the lifelines, a taller row instead of a refused gap. Keep labels as\nsentences and never shorten them to fit. Write `\\n` only for a break of your own\n(`\"Authorize\\nthe card\"`). A self-call's label stays on one line beside its loop, broken only at\n`\\n`. A pull stops where the heads or a label's longest word no longer fit.\n\nWhen a call causes other events, write them in the call's block and answer it with `return`: the\nreceiver is active for the whole block (one bar, no `activate`/`deactivate` to balance), and\n`return` is the dashed reply to the caller.\n\n```text\ncustomer -> api: \"Pay\" as pay {\n api -> bank: \"Authorize\" as authorize {\n return \"Approved\" as approved // bank --> api\n }\n note over api \"Stores the receipt\"\n return \"Receipt\" as receipt // api --> customer; nothing may follow it in this list\n}\n```\n\nA block may hold properties (`kind async`) beside its events, fragments may sit inside it and\ncalls with blocks inside fragments. `return` outside a call is an error.\n\n## Tags\n\n```text\nparticipant bank \"Bank\" { tag \"external\" } // a badge over the head\napi -> bank: \"Authorize\" as authorize {\n tag \"PKCE\" // pills on the arrow, next to the source\n tag \"v2\"\n}\n```\n\n`tag \"Text\"` marks a message (calls, replies, self-calls, `return`) or a participant with a short\nword: a protocol, a version, a property. Write it in the owner's block, one line of text, several\nin order, no id; in a call's block it belongs to the call, beside its events. Never write it at\nthe diagram level, in a group block or in a fragment (`prop.unknown`), never empty, and never the\nsame text twice on one owner (`prop.duplicate`). `tag <id> \"Text\"` is the flow profile's pill and\nis refused here. Tags take the theme's `tag` colours; there is no `style` or `rule` for them.\n\n## Fragments\n\n```text\nalt result \"Approved\" {\n bank --> api: \"OK\"\n} else \"Declined\" {\n bank --> api: \"Declined\"\n}\nopt coupon \"Coupon applied\" { … }\nloop polling \"Until shipped\" { … }\npar { … } and \"Notify\" { … }\nbreak \"Fraud\" { … }\ncritical \"Shipment\" { … } option \"Carrier down\" { … }\nregion \"Audit\" { … }\n```\n\nWrite `else`, `and` and `option` on the line of the closing `}`. Give a fragment an id (`alt\nresult …`) when a scenario or `space` must name it. Fragments hold events only: declare every\nparticipant at the diagram level first.\n\n## Scenarios\n\n```text\nscenario approved \"Payment approved\" {\n choose result \"Approved\" // or: choose result 1\n repeat polling 3 // 1 to 100 passes\n skip coupon // opt or break not taken; take coupon keeps it (the default)\n}\n```\n\n`pnpm mdg export sheet.meltemi --scenario approved` writes `sheet.approved.svg` with only that\npath; without `--scenario` every branch is drawn.\n\n## Spacing and styles\n\n```text\nspacing 56 // distance between neighbouring heads, before gaps (40 by default)\nplacement {\n space authorize 28 // more room above a named message, note or fragment\n space settled 20 after // or below it (the last event lengthens the lifelines)\n gap bank 40 // more room left of a participant\n gap ledger 24 after // or right of it\n gap api -12 // a negative gap brings it closer, its labels wrapping, down to the heads\n label pay at 50% offset (0, -4)\n}\nstyle result { stroke accent } // notes and fragments with ids take styles\nrule fragment { stroke muted }\n```\n\n## Diagnostics\n\n| Code | Fix |\n| --------------------- | ---------------------------------------------------------------------------- |\n| `sequence.activation` | `deactivate p` without an open `activate p`: add the `activate` or remove it |\n| `sequence.return` | `return` outside a call's block, or an event after the `return`: move it |\n| `sequence.lifecycle` | an event after `destroy`, `create` after the participant appeared, or twice |\n| `layout.gap-floor` | (warning) a negative `gap` below what heads, words or tags need: drawn wider |\n| `prop.invalid-value` | a negative `gap` on the outer side, or `space`; `tag \"\"` or `tag id \"x\"` |\n| `prop.duplicate` | the same `tag` text twice in one block |\n| `sequence.scenario` | the scenario names an unknown fragment, branch or loop count |\n| `ref.unknown-element` | declare the participant (`participant x \"X\"`); groups are not participants |\n| `prop.unknown` | a flow or free kind (`node`), a declaration in a fragment, a stray `tag` |\n\nBranches of `alt`, `par` and `critical` are checked separately, each from the state before the\nfragment; the first branch's state continues after it. Activations open at the end are fine.\n\n## Converting\n\nImport Mermaid and PlantUML sequences with `pnpm mdg import`, which maps them one to one: `participant`/`actor` (and the\nPlantUML kinds), `->>` and `->` to `->`, `-->>` and `-->` to `-->`, `-)` to `-> … { kind async }`,\n`+`/`-` suffixes to `activate`/`deactivate`, `Note over A,B: text` to `note over a, b \"text\"`,\n`rect`/`group` to `region`, `box` to `group`, `== x ==` to `divider \"x\"`, `...` to `delay`.\n",
|
|
6
|
+
c4: "# Models and C4 views\n\nUse a `model` block for the architecture (who uses what, which containers a system has) and C4\nviews to draw it at one level each. Declare entities once in the model; views only list them.\n\n## Model\n\n```text\nmodel commerce {\n person customer \"Cliente\" { description \"Compra en línea\" }\n system shop \"Commerce\" {\n container web \"Web\" { technology \"React\" }\n container api \"API\" {\n technology \"Kotlin\"\n component orders \"Pedidos\"\n }\n datastore db \"Pedidos\" { technology \"PostgreSQL\" }\n api -> db: \"SQL\" as persist // inside shop: api is shop.api\n }\n system bank \"Banco\" { external }\n\n customer -> shop.web: \"Compra\" as visit\n shop.web -> shop.api: \"HTTPS\" as request { technology \"JSON/HTTPS\" }\n shop.api.orders -> bank: \"Autoriza\" as authorize\n}\n```\n\n- Persons and systems at the root; containers and datastores in systems; components in containers\n (`model.invalid-nesting` otherwise).\n- Entities are named by path: `shop.api.orders`. Relations use `->` only and name paths.\n- `external` marks persons and systems outside the organisation (drawn grey and dashed).\n\n## Views\n\n```text\ndiagram containers: c4 using commerce {\n level container // context | container | component\n scope shop // a system; a container for component views\n include customer, shop.web, shop.api, shop.db\n label shop.api \"API de pedidos\" // local text, the model keeps \"API\"\n direction LR\n layout assisted\n placement { place shop.api at (420, 120) }\n style shop.db { fill accent }\n}\n```\n\n- Without `include`, a view draws its default set: everything at the context level, otherwise\n the scope's children and the entities related to them. `exclude` removes entities.\n- A container view draws the scope's containers and datastores (inside a dashed boundary),\n persons and other systems; a component view the scope's components, the other containers of the\n system, persons and other systems. Anything else is `c4.out-of-level`.\n- Relations come from the model; ends inside hidden entities are lifted to the drawn ancestor\n (at the context level `customer -> shop.web` becomes `customer -> shop`). Two relations to the\n same pair stay two arrows; to draw one, write a combination in the view:\n `customer -> shop: \"Usa\" as uses`. A combination with no model relation behind it is\n `c4.unsupported-relation`.\n- Placement, styles and routes take paths: `route request from shop.web.east to shop.api.west`.\n\n## Notes in views\n\nA view accepts `note <id> \"Text\" [on <entity path>]` beside its lists: an annotation of the view,\nnever of the model and never an entity (`include`, `exclude`, `label` and relations cannot name it).\n`note cache \"Cached 5 min\" on shop.api` draws a note with a dotted leader to `shop.api`, which the\nview must draw (else `ref.unknown-element`). It takes `class` and `url`, the view's `style`, `class`,\n`rule` and `animate`, and a `place` in the view's `placement`.\n\n## Files\n\nKeep the model in `models/` and the views in `diagrams/` of a project, and import the model:\n\n```text\nimport commerce from \"../models/commerce.meltemi\"\ndiagram context: c4 using commerce { level context }\n```\n\nSequence and free objects link to entities with `link commerce.shop.api`; a participant without its\nown label shows the entity's.\n\n## Diagnostics\n\n| Code | Fix |\n| ------------------------- | ------------------------------------------------------------------------ |\n| `c4.invalid-level` | add `level context`, `level container` or `level component` |\n| `c4.scope-required` | add `scope <system>` (container view) or `scope <container>` (component) |\n| `c4.invalid-scope` | scope a system or a container; datastores have no components |\n| `c4.out-of-level` | remove the entity from `include`, or change the level |\n| `c4.unsupported-relation` | add the relation to the model, or remove the combination |\n| `ref.unknown-entity` | use the full path from the model's root (`shop.api`, not `api`) |\n| `ref.unknown-model` | declare or import the model named after `using` |\n| `model.invalid-nesting` | move the entity into the right parent |\n| `model.invalid-relation` | relate separate entities, not an entity and its parent |\n",
|
|
7
|
+
free: "# Free compositions\n\nUse `diagram <id>: free { }` for a slide, an executive picture, a whiteboard, a kanban board or an\nexplanation that does not fit a flow notation. Objects are placed by hand (`layout manual` is the\ndefault); relations, `placement`, constraints, `style`, `class`, `rule`, `animate`, themes,\n`preset` and `canvas` work as in flow diagrams. A whiteboard writes `preset board`: the canvas\ngrows with its content and never reports `layout.too-large`.\n\n## Objects\n\n| Declaration | Object | Default size |\n| --------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------- |\n| `box <id> \"Label\"`, `id[\"L\"]` | a shape with a label and an optional `description` | its shape's size, widened to its text |\n| `id[(\"Label\")]` | a box with the `cylinder` shape | 160 × 64 |\n| `text <id> \"Text\\nmore\"` | measured text without a frame | its extent |\n| `icon <id> \"tabler:server\"` | an icon of the packs; find names with `pnpm mdg assets icons <w>` | 48 × 48 |\n| `image <id> \"img/logo.png\"` | a PNG, JPEG, GIF, WebP or SVG file of the project, relative to the file | its pixel size, at most 640 wide |\n| `note <id> \"Text\" [on <element>]` | an annotation: text with no frame until styled, and a dotted leader to the element it is attached to (below) | its text's extent |\n| `sticky <id> \"Text\"` | a sticky note: a square card whose text wraps and shrinks to fit (below) | 160 × 160, never grown |\n| `group <id> \"Label\" { ... }` | a band holding objects and relations; `group <id> \"\" { }` is plain; `layout column` makes it a list (below) | written, or fitted to children |\n| `line <id> from (x, y) to (x, y)` | a line that joins no element: a divider, a bracket, an underline (below) | the bounding box of its points |\n| `pen <id> [(x, y), (x, y), ...]` | a freehand stroke: handwriting, a drawn arrow, a highlighter mark (below) | its points' box plus half its width |\n\n`node`, `decision`, `data` and `tag` are flow kinds and are refused here; `note`, `sticky` and\n`pen` are free kinds. Relations take the lines without heads `a -- b` and `a --- b` too.\n\n## Notes\n\n`note remark \"Checked at the gateway\" on gateway { layer front }` is text at the theme's note size in\nthe muted colour, left-aligned and without a frame. `style remark { fill \"#fff7d6\"; stroke warning;\nline dashed }` gives it an outline (any of `fill`, `stroke` or `pattern` does); `font`, `fontSize`,\n`fontWeight`, `textAlign`, `radius`, `color`, `icon` and `rotation` apply, `shape` does not. The\nblock takes `description`, `class`, `layer`, `link` and `url`. `on <element>` attaches the note to\nany other element of the diagram: a dotted leader joins them, `layout auto` keeps them near each\nother, renaming the element rewrites the clause, and deleting the element leaves the note. An\nunknown target is `ref.unknown-element`; a note on itself, `prop.invalid-value`. A note with an\noutline can take `animate remark { effect dash }`; without one, only a pulse.\n\n## Stickies\n\n```text\nsticky cache \"Cache the catalogue at the edge before the sale\"\nsticky retry \"Retry payments\\nonce\"\nstyle retry { fill \"#f8bbd0\"; rotation 4 }\n```\n\n- A 160 × 160 card (scaled with the theme's `labelSize`, or its `size`) that never grows: the\n text wraps at spaces inside the theme's `padding`, at `fontSize` or 1.2 × the label size\n (15.6 px), and shrinks 1 px at a time down to 8 px until it fits. Text that still does not fit\n is `layout.text-overflow` (warning) and clipped: shorten it or use the `size` in the hint.\n- Colours: the theme's `sticky` card (`#fff59d` on light themes) and `stickyText` text; `fill`\n and `color` override them. Common fills: yellow `#fff59d`, pink `#f8bbd0`, green `#c8e6c9`,\n blue `#bbdefb`, orange `#ffe0b2`, purple `#e1bee7`. A soft shadow, `radius` 2, an outline only\n with `stroke`. Text is centred (`textAlign`, `verticalAlign`); an `icon` sits top left.\n- Takes `fill`, `stroke`, `color`, `line`, `width`, `fontSize`, `fontWeight`, `textAlign`,\n `verticalAlign`, `radius`, `rotation`, `opacity`, `icon`, `iconColor`, `iconSize`; `shape`,\n `pattern`, `iconPosition` and `labelPosition` are `prop.invalid-value`. `rule sticky { }` styles\n them all.\n- A sticky is a box for everything else: `place`, `size`, ports, constraints, relation endpoints\n (`cache -> retry`), stories and effects. `sticky s` without text is `prop.invalid-value`.\n\n## Shapes and corners\n\n`style a { shape hexagon }` draws a box (or a flow `node`) as one of twenty-four shapes. Each has a\ndefault size (technical themes; it scales with the label size), keeps its text inside its outline\nand grows until the text fits, and connectors end on its outline:\n\n| Size | Shapes |\n| --------- | ------------------------------------------------------------------------------------ |\n| 160 × 56 | `rounded` (default), `rectangle`, `pill`, `parallelogram`, `arrow`, `framed`, `flag` |\n| 160 × 64 | `cylinder`, `trapezoid`, `hexagon` |\n| 160 × 72 | `ellipse`, `octagon`, `document`, `callout` |\n| 160 × 80 | `diamond` |\n| 160 × 96 | `triangle`, `pentagon` |\n| 160 × 104 | `person` |\n| 160 × 120 | `agent` |\n| 120 × 120 | `square`, `circle`, `star` |\n| 180 × 100 | `cloud` |\n| 64 × 64 | `icon` (or its `iconSize`), the label outside (below) |\n\n- `square` and `circle` are a rectangle and an ellipse with a square default size; `arrow` points\n right; `framed` has two inner lines (a subroutine); `callout` has a tail at the bottom left.\n- `person` is a round head over a body; `agent` is an AI agent, a robot head (eyes, mouth and an\n antenna) over a body. Both keep the text in the body; an end on the north side meets the head.\n- `\\n` breaks a box's or a node's label into lines, as in a `text`.\n- A `size` too narrow for the text inside a shape is `layout.text-overflow`; use the hint's width.\n- A pinned end (`route r from a.east to d.north 30%`) is drawn where the side's normal meets the\n outline, so arrows touch diamonds, ellipses and stars.\n- `radius 0..64` sets the corner radius of boxes, nodes, notes and groups (`radius 0` squares\n them); `rectangle`, `square` and `framed` are always square, the other shapes ignore it. The\n theme's `radius` (8) and `groupRadius` (14) are the defaults.\n- Decisions, data stores, notes, tags, groups, texts, icons and images refuse `shape`, in `style`\n statements as in rules: their kind is their drawing. Only `rule box` and `rule node` may set it.\n\nText in every profile: `font \"<Family>\"`, `fontSize 6..160` and `fontWeight 400|500|600|700` on\nwhatever draws text (text defaults to 1.6 × the label size, stickies to 1.2 × at 400). Free-only\ntext properties (refused in flow diagrams):\n\n- `textAlign left|center|right` on boxes, stickies and text (text is left aligned by default);\n- `verticalAlign top|middle|bottom` on boxes (`middle` by default) moves the label, description\n and icon to the top or bottom of the shape's text band, the theme's padding from its edge:\n `style card { shape rectangle; textAlign left; verticalAlign top }` is draw.io's top-left text.\n Ellipses and other narrowing shapes move a wide block back towards the middle until it fits;\n cylinders keep it between the rims, documents above the wave, callouts above the tail. On\n `triangle`, `pentagon`, `star`, `person`, `agent` and `icon` shapes, with `iconPosition center`,\n and on texts, icons, images, groups and lines it moves nothing (`layout.vertical-align-ignored`);\n- `opacity 0..1` fades an element, a group, a line or a pen (`style draft { opacity 0.5 }`);\n relations refuse it;\n- a text, icon or image with `fill`, `stroke` or `pattern` gets a frame.\n\n## Lines\n\n```text\nline divider \"Phase 2\" from (40, 160) to (520, 160) // a labelled divider\nline bracket from (40, 220) to (520, 280) via [(280, 220)] // bends in a via list\ngroup notes \"Notes\" {\n text remark \"Read first\"\n line underline from (0, 22) to (120, 22) // in the group's interior\n}\nstyle divider { line dashed; stroke muted }\nstyle bracket { routing orthogonal; corners rounded; arrowhead triangle }\n```\n\n- A `line` joins nothing: use it for dividers, brackets, underlines and strokes; between two\n boxes write a relation (`a -- b`) so it follows them.\n- Its points are written in the statement, in the parent group's interior like `place`; it takes\n no `place`, `size` or `rotation` (`prop.invalid-value`).\n- It takes a relation's styles: `stroke`, `width`, `line`, `arrowhead` and `arrowtail` (none by\n default), `routing` (`polyline` by default, `orthogonal`, `bezier`), `corners`, and `color`,\n `fill`, `icon` for its label pill; a box's properties (`shape`, `radius`, the text properties)\n are `prop.invalid-value`. `rule line { }` styles every line.\n- The label is the optional second argument, on a pill at 50 %;\n `label divider at 20% offset (0, -14)` in `placement` moves it. `flow`, `dash`, `pulse` and\n `trace` run along a line.\n- A line whose points coincide is drawn as a dot: `layout.degenerate-line`. Flow, sequence and C4\n diagrams refuse `line` (`prop.unknown`).\n\n## Pen strokes\n\n```text\npen marker [(40, 70), (280, 70)] // declared first: under the text\ntext heading \"Why the cache helps\"\npen arrow [(216, 140), (250, 128.5), (290, 130), (336, 140)]\npen dot [(400, 60)] // one point is a dot\nstyle marker { stroke highlight; width 20; opacity 0.35 } // a highlighter mark\nstyle arrow { width 4; arrowhead triangle }\n```\n\n- A freehand stroke: one smooth path through the midpoints of its points (round caps and joins).\n Use it for handwriting, drawn arrows, circles and underlines; use a `line` for a straight\n divider and a relation between two boxes.\n- `pen <id> [(x, y), ...]`: at least one tuple of plain numbers, decimals kept, in the parent\n group's interior like `place`; no label, no `place`, `size` or `rotation`\n (`prop.invalid-value`). `pen p [10, 20]`, `pen p []` and `pen p` are `prop.invalid-value` with\n the form `pen <id> [(x, y), (x, y), ...]` in the hint.\n- Styles: `stroke` (the theme's `ink`), `width` 1..64 (3; `width 0` is refused), `line`,\n `opacity`, `arrowhead`, `arrowtail` (none). Anything else (`fill`, `shape`, `routing`, text\n properties) is `prop.invalid-value`. `rule pen { }` styles every pen.\n- The highlighter is `stroke highlight; width 20; opacity 0.35`; declare it before the text it\n marks so the text is painted over it.\n- Pens take no part in `layout.overlap` or the label checks, are never moved by the engine or a\n list, are not jittered by sketch themes, and run `flow`, `dash`, `pulse` and `trace` like a line.\n A story's `draw a, b over 2s` replays them ([stories.md](stories.md)).\n- `pnpm mdg format` prints a long point list filled to 100 columns, not one point per line.\n\n## Icons in boxes and as shapes\n\nFlow diagrams take these too. The `icon` of a node, decision, data store or box goes beside the\ntext (`iconPosition left`, the default, or `right`), above or below it (`top`, `bottom`), or at the\ncentre as the picture of the box (`center`), with the label in a band at the `labelPosition` edge\ninside (`bottom` by default). `shape icon` draws the element as its icon alone, with the label\noutside at `labelPosition`, as provider icons are drawn in architecture diagrams:\n\n```text\nbox gateway \"API Gateway\"\nbox fn \"Checkout\"\nbox orders \"Orders\"\nbox tile \"Orders DB\"\ngateway -> fn\nfn -> orders\n\nstyle gateway { icon \"aws:api-gateway\"; shape icon } // label below the icon\nstyle fn { icon \"aws:lambda\"; shape icon; labelPosition top } // label above\nstyle orders { icon \"aws:dynamodb\"; shape icon; fill surface; stroke line } // framed\nstyle tile { icon \"tabler:database\"; iconPosition center } // 120 × 120 tile\n```\n\n- `iconSize` is the icon's side: 8 to 128 (px or unitless) or `auto`. Beside the text the theme's\n 18 px is the default; a number grows a box without `size`, and is clamped (never cut) in a box\n with a written `size`, where `auto` fills the room. A centred icon is as large as fits; a\n written `size` without room for an 8 px icon beside the label band is `layout.text-overflow`.\n- An icon shape is a 64 × 64 square (or `iconSize`; a written `size` wins, the icon fitted\n inside), with nothing behind the icon unless `fill`, `stroke` or `pattern` frames it; a frame\n adds half the padding on each side (an 80 × 80 square around a 64 px icon). Arrows, ports and\n anchors end on the square (`to fn.south` is the icon's bottom edge); groups and the canvas\n count the label too, and a box over the label is `layout.overlap`, so leave room for it. A\n connector on the label's side is drawn from beyond the label; other routes go around it.\n- `labelPosition` does nothing unless the icon is the centre piece: `layout.label-position-ignored`.\n `shape icon` without an `icon` is `layout.icon-missing` and draws a dashed placeholder.\n- A free `icon <id> \"pack:name\"` object is an icon without text; for a labelled icon use a box\n with `shape icon`.\n\n## Coordinates\n\n- `place` is the centre, as in flow diagrams.\n- **Inside a group, `place` is relative to the group's interior**: its top-left corner plus\n `groupPadding` (16) across and the header down: `groupHeader` (40) in the technical themes, or\n `groupPadding` (16) for an untitled group (`\"\"`, no description, no icon). A child at\n `(w/2, h/2)` touches the interior's top-left corner. Moving the group moves its children.\n- Every group needs a `place`. Without `size` it fits its children: padding + rightmost child edge\n - padding wide, header + lowest child edge + padding tall.\n- Route waypoints are always diagram coordinates.\n\n## Containers, stacks and bands\n\nThese are the pictures of a visual editor's containers and backdrops; flow diagrams take them too.\n\n```text\ngroup outer \"\" { // nested plain rectangles: untitled groups\n group inner \"\" {\n box core \"Core\"\n }\n}\nbox plate \"\" // a stack: the outer box first, empty label\nbox card \"Card\"\ngroup band \"\" { layer back } // a band behind arranged boxes (layers back, main)\n\nstyle outer { fill \"#dae8fc\"; stroke \"#6c8ebf\"; radius 0 }\nstyle inner { fill \"#fff2cc\"; stroke \"#d6b656\"; radius 0 }\nstyle band { fill \"#f5f5f5\"; radius 0 }\n```\n\n- **Untitled groups** draw only their outline, have no title band and cut no gap in connectors;\n children keep half the padding (8 px) from every edge (`layout.container-margin`).\n- **Stacks**: a box lying strictly inside a box declared before it on the same layer is not\n `layout.overlap`; a partial overlap still is, so are two boxes that coincide, and so is a stack\n declared inner first (its hint says to declare the outer box first). A\n container's own label may lie over its children, but two labels at the same point are\n `layout.label-overlap`: leave the outer labels empty.\n- **Bands**: a group with `place` and `size` is neither arranged by the engine nor an obstacle, so\n on a back layer it stays behind the boxes `layout assisted` arranges (pin one box with `place`\n so the others arrange around it). Boxes and groups on different layers are not checked against\n each other, and connectors cross them straight. In `layout auto` a group's `place` is ignored:\n make the band a group holding its boxes.\n\n## Lists and kanban boards\n\n```text\ndiagram board: free {\n preset board\n group kanban \"\" {\n layout row // the columns side by side\n spacing 24\n align stretch // every column as tall as the tallest\n group todo \"To do\" {\n layout column // the cards top to bottom, in source order\n spacing 12\n sticky cache \"Cache the catalogue\"\n sticky retry \"Retry payments once\"\n }\n group done \"Done\" {\n layout column\n sticky login \"Sign in with passkeys\"\n }\n }\n placement {\n place kanban at (360, 300) // only the outer list needs a place\n }\n}\n```\n\n- `layout column|row` in a free group block makes the group a list: its elements and groups go one\n after the other in source order from the interior's corner, `spacing <n>` apart (0..400; half\n `groupPadding`, 8, by default). To reorder cards, reorder their declarations.\n- `align start|center|end|stretch` places children across the axis (`start` by default);\n `stretch` gives a child without a written `size` the interior's full width (column) or height\n (row).\n- Children keep their size; the list is derived from them, and a written `size` on the list is\n only its least size. A list inside a list needs no `place`.\n- A `place` on a child of a list is `layout.place-ignored` (warning): remove it. Lines and pens in a\n list keep their written points. A group that is not a list, inside a list, still places its own\n children with `place`.\n- The engine treats a list as one box in every mode; `pnpm mdg layout` writes a `place` for it and\n nothing for its children.\n- `spacing` or `align` without `layout` is `prop.invalid-value` (add `layout column`); `layout grid`\n lists `column, row`; `layout auto` belongs to the diagram, not to a group. Flow diagrams refuse\n the three.\n\n## Layers, order and links\n\n- `layers back, main, front` lists layers back to front; `main` is the default and comes last\n when unlisted. Put `layer front` in an object's or relation's block.\n- Within a layer, groups paint first, then objects (lines included) in source order, then\n relations: declare what must be on top later.\n- `link checkout.api` in a block records that the object stands for element `api` of `diagram\ncheckout` (same file), `link co.api` through `import checkout as co from \"...\"`, or `link\nviews.checkout.api` through `import \"...\" as views`. Identity only; nothing is kept in step.\n\n## From a flow diagram\n\n`pnpm mdg convert sheet.meltemi --to free [--diagram <id>] [--id <new>]` adds a free copy of the\nflow diagram as drawn: every box with `place` and `size`, children inside their copied groups,\nlayers, routes, labels, styles and animations, a `link` from each object to its source, and\nclasses `from_decision`, `from_data`, `from_tag` for the drawing of the flow kinds; notes stay notes,\nwith their `on` clause.\nEdit the copy freely; the flow diagram is unchanged.\n\n## Diagnostics\n\n| Code | Fix |\n| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `ref.unknown-layer` | add the layer to `layers` (the hint gives the statement) or fix its name |\n| `ref.unknown-link` | the diagram or element does not exist; fix the name or remove the `link` |\n| `asset.outside-project` | image paths are relative, use `/`, no URLs, and stay inside the project |\n| `asset.unknown-image` | the file does not exist next to the path written |\n| `asset.unsupported-image` | not a PNG, JPEG, GIF, WebP or SVG image, broken, or over 8 MB |\n| `layout.image-unavailable` | the host could not read the image; a placeholder is drawn |\n| `layout.missing-placement` | an object or a group has no `place` (children of a list need none; a group that is not a list, inside a list, places its own children) |\n| `layout.place-ignored` | a child of a list (`layout column\\|row`) has a `place`, which the list overrides: remove the `place`, or the group's `layout` |\n| `layout.text-overflow` | a `size` too narrow for a box's text, or a sticky whose text does not fit even at 8 px: shorten the text or use the size in the hint |\n| `layout.too-large` | the content exceeds `preset document` or `preset slide`: split it into views, or write `preset board` for a whiteboard |\n| `layout.overlap` | boxes overlap partly, or a stack is declared inner first: declare the outer box first |\n| `layout.icon-missing` | `shape icon` without an `icon` style: add `icon \"pack:name\"` or choose another shape |\n| `layout.label-position-ignored` | `labelPosition` only moves the label of `iconPosition center` and `shape icon` elements |\n| `layout.vertical-align-ignored` | `verticalAlign` moves nothing on this shape or kind: choose a shape with a text band (`rectangle`, `ellipse`, `cylinder`), set `labelPosition` with a centred icon, or remove it |\n| `layout.degenerate-line` | a line's points coincide: move an end or add a bend with `via` |\n| `prop.invalid-value` | `textAlign`, `verticalAlign` and `opacity` are free-only; `font`, `fontSize` and `fontWeight` are refused on icons, images and pens; `shape` is for `node` and `box` only; a line or a pen takes no `place`, `size` or `rotation`; a pen needs `[(x, y), ...]` and `width` 1..64; a sticky needs its text; `spacing` and `align` need `layout column\\|row`; `opacity` is refused on relations |\n",
|
|
8
|
+
stories: "# Stories\n\nA `story` tells a diagram step by step for a walkthrough, a talk or a recorded demo. It never\nchanges the diagram: scenes reveal, hide, highlight, focus, frame, trace and draw parts of it on one\ndeterministic clock, and may switch to another view of the same system. Write the diagram first,\nthen the story below it (or in another file that imports the diagram).\n\n```text\nstory purchase for checkout {\n title \"A purchase\"\n\n scene overview {\n reveal client, api, orders, db over 800ms\n hold 1s\n }\n\n scene processing {\n trace request over 1200ms\n parallel {\n trace save over 1200ms\n highlight db over 1200ms\n }\n hold 2s\n }\n}\n```\n\n## Vocabulary\n\n| Statement | Default | Meaning |\n| ------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |\n| `scene <id> [\"Title\"] { ... }` | | a step; scenes run one after the other |\n| `view <diagram>` | | first in a scene: the diagram it shows from now on |\n| `transition cut\\|fade\\|morph [over <time>]` | 800 ms | needed when the view changes; `morph` moves boxes with the same id, `cut` takes 0 |\n| `reveal a, b [over <time>]` | 600 ms | fades in; targets first revealed start hidden |\n| `hide a, b [over <time>]` | 600 ms | fades out |\n| `highlight a [over <time>]` | 1 s | accent outline for the action's time |\n| `focus a, b` / `focus all` | 600 ms | dims the rest to 25 % until the end of the scene; `all` clears it |\n| `camera a, b` / `camera all` | 800 ms | frames the targets plus 40 px; `all` frames the whole view |\n| `trace r [over <time>]` | 1.2 s | draws one named relation from source to target in the accent colour |\n| `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 |\n| `hold <time>` | | waits |\n| `parallel { ... }` | | actions that start together; lasts as long as the longest |\n\nTargets are ids of the scene's view: elements, named relations (`as <id>`), named notes and\nfragments; entity paths (`shop.api`) in C4 views. Times are `800ms` or `1.5s`, up to `600s`.\n\n## Rules that save iterations\n\n- Name every relation you want to trace: `api -> db: \"SQL\" as save`.\n- A whiteboard's explanation is a `draw` of its pens in the order they were written\n (`draw title, arrow over 2s`): each takes a share of the time proportional to its length, in its\n own colour, and stays drawn ([free.md](free.md#pen-strokes)).\n- Reveal everything that should appear; anything never revealed is visible from the start. An\n arrow appears with its two ends, so reveal the boxes, not the relations.\n- Two actions on the same target and channel must not overlap (`story.conflict`): put a `reveal`\n and a `hide` of the same box in sequence, not in one `parallel`.\n- A change of `view` needs a `transition` (`story.missing-transition`); use `morph` between views\n that share ids (two C4 views of one model), `fade` otherwise.\n- Keep scenes short (a few seconds) and end the story with a `hold` so the last state stays.\n\n## Diagnostics\n\n| Code | Fix |\n| -------------------------- | ----------------------------------------------------------------------------- |\n| `story.unknown-diagram` | the diagram after `for` or `view` is not in the file or imported |\n| `ref.unknown-element` | the target is not an id of the scene's view; use the suggested id |\n| `story.invalid-target` | `trace` needs a named relation; `draw` a pen, a free line or a named relation |\n| `story.missing-transition` | add `transition cut`, `fade` or `morph` after `view` |\n| `story.conflict` | two overlapping actions on one target and channel; sequence them |\n| `story.empty-scene` | warning: give the scene an action or remove it |\n| `prop.invalid-value` | a time without `ms` or `s`, beyond 600 s, or a statement out of place |\n\n## Checking a story\n\n```bash\npnpm mdg validate path/to/sheet.meltemi\n```\n\n```bash\npnpm mdg export path/to/sheet.meltemi --story purchase --at 2.4s\n```\n\n`--story` writes `<stem>.<story>.svg`: the end of the story, or the frame at `--at`. Export a few\ninstants (the start of each scene, the middle of a trace) and look at them; the canvas in VS Code\nplays the story with a timeline.\n\nTo share the story, write its HTML player, an offline page with pause, progress, scenes, reduced\nmotion, a static view, a text summary, zoom, hover cards and a detail panel (`--embed` writes a\nfragment for an existing page):\n\n```bash\npnpm mdg export path/to/sheet.meltemi --story purchase --format html\n```\n",
|
|
9
9
|
};
|
|
10
10
|
//# sourceMappingURL=guide.js.map
|
package/dist/guide.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guide.js","sourceRoot":"","sources":["../src/guide.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAqC;IAC3D,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"guide.js","sourceRoot":"","sources":["../src/guide.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAqC;IAC3D,QAAQ,EAAE,g6wBAAg6wB;IAC16wB,QAAQ,EAAE,wjOAAwjO;IAClkO,EAAE,EAAE,+gJAA+gJ;IACnhJ,IAAI,EAAE,kk2BAAkk2B;IACxk2B,OAAO,EAAE,qiMAAqiM;CAC/iM,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meltemi/diagrams-assist",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.17",
|
|
4
4
|
"description": "Optional AI assistance for Meltemi sources: model adapters, reviewable proposals against a known revision, partial acceptance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"dist"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@meltemi/diagrams-i18n": "1.
|
|
30
|
-
"@meltemi/diagrams-model": "1.
|
|
31
|
-
"@meltemi/diagrams-language": "1.
|
|
29
|
+
"@meltemi/diagrams-i18n": "1.10.17",
|
|
30
|
+
"@meltemi/diagrams-model": "1.10.17",
|
|
31
|
+
"@meltemi/diagrams-language": "1.10.17"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|