@jarenjs/mermaid 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +282 -0
- package/dist/types/ast.d.ts +210 -0
- package/dist/types/component/index.d.ts +81 -0
- package/dist/types/errors.d.ts +27 -0
- package/dist/types/index.d.ts +97 -0
- package/dist/types/interactive.d.ts +36 -0
- package/dist/types/layout/flowchart.d.ts +12 -0
- package/dist/types/layout/sequence.d.ts +12 -0
- package/dist/types/layout/state.d.ts +19 -0
- package/dist/types/parser/class.d.ts +11 -0
- package/dist/types/parser/config.d.ts +61 -0
- package/dist/types/parser/er.d.ts +11 -0
- package/dist/types/parser/flowchart.d.ts +26 -0
- package/dist/types/parser/gantt.d.ts +11 -0
- package/dist/types/parser/index.d.ts +22 -0
- package/dist/types/parser/pie.d.ts +11 -0
- package/dist/types/parser/sequence.d.ts +16 -0
- package/dist/types/parser/state.d.ts +22 -0
- package/dist/types/plugin.d.ts +50 -0
- package/dist/types/render/error.d.ts +23 -0
- package/dist/types/render/flowchart.d.ts +18 -0
- package/dist/types/render/index.d.ts +23 -0
- package/dist/types/render/misc.d.ts +54 -0
- package/dist/types/render/sequence.d.ts +16 -0
- package/dist/types/styles.d.ts +81 -0
- package/dist/types/theme.d.ts +47 -0
- package/dist/types/to-mermaid.d.ts +20 -0
- package/dist/types/utils.d.ts +47 -0
- package/docs/MERMAID-FORMAT.md +242 -0
- package/package.json +84 -0
- package/schemas/jaren-mermaid-ast.schema.json +78 -0
- package/schemas/jaren-workflow.schema.json +28 -0
- package/src/ast.js +252 -0
- package/src/component/index.js +109 -0
- package/src/errors.js +35 -0
- package/src/index.js +155 -0
- package/src/interactive.js +244 -0
- package/src/layout/flowchart.js +352 -0
- package/src/layout/sequence.js +178 -0
- package/src/layout/state.js +65 -0
- package/src/parser/class.js +90 -0
- package/src/parser/config.js +215 -0
- package/src/parser/er.js +86 -0
- package/src/parser/flowchart.js +413 -0
- package/src/parser/gantt.js +49 -0
- package/src/parser/index.js +122 -0
- package/src/parser/pie.js +32 -0
- package/src/parser/sequence.js +156 -0
- package/src/parser/state.js +137 -0
- package/src/plugin.js +76 -0
- package/src/render/error.js +55 -0
- package/src/render/flowchart.js +249 -0
- package/src/render/index.js +93 -0
- package/src/render/misc.js +135 -0
- package/src/render/sequence.js +152 -0
- package/src/styles.js +181 -0
- package/src/theme.js +180 -0
- package/src/to-mermaid.js +317 -0
- package/src/utils.js +64 -0
- package/styles/mermaid.css +115 -0
- package/stylesheets/dag-to-flowchart.jslt.json +62 -0
- package/stylesheets/flowchart-to-dag.jslt.json +29 -0
- package/stylesheets/state-to-workflow.jslt.json +26 -0
- package/stylesheets/workflow-to-state.jslt.json +41 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Theme tokens. `createTheme` resolves a
|
|
3
|
+
* theme name (or overrides) into a flat token object of concrete colors
|
|
4
|
+
* *and* a matching set of `--mm-*` CSS custom properties.
|
|
5
|
+
*
|
|
6
|
+
* Both are used at once, deliberately: the render pass writes the
|
|
7
|
+
* concrete colors as SVG presentation attributes (so `toSvgString()` is
|
|
8
|
+
* a valid, self-colored standalone SVG with no CSS), and also stamps the
|
|
9
|
+
* `--mm-*` variables inline on the root `<svg>` plus a `class` on every
|
|
10
|
+
* shape, which `styles/mermaid.css` maps back to `var(--mm-*)`. Because
|
|
11
|
+
* the stamp is an inline style it beats every stylesheet rule — so the
|
|
12
|
+
* stamp itself is the re-theming hook: the `'host'` theme stamps each
|
|
13
|
+
* linked variable as `var(--<host-token>, <concrete>)` (see `HOST_VARS`),
|
|
14
|
+
* making diagrams follow a host's light/dark tokens live, with no
|
|
15
|
+
* re-render — memoized vnodes stay valid across a theme flip.
|
|
16
|
+
*
|
|
17
|
+
* Only the token tables live here; the resolution mechanics are shared
|
|
18
|
+
* (`@jarenjs/view/helpers` `resolveTheme`).
|
|
19
|
+
*
|
|
20
|
+
* The error-box tokens are `err*`, not `error*`, and must stay that way:
|
|
21
|
+
* every root stamps every token, `mm-error` is the class marking a root as
|
|
22
|
+
* an error box, and a `--mm-error-*` stamp would put that marker on every
|
|
23
|
+
* healthy diagram — so anything testing the marker by substring would read
|
|
24
|
+
* a working render as a failure.
|
|
25
|
+
*/
|
|
26
|
+
/** @type {Record<string, Record<string, string>>} */
|
|
27
|
+
declare const THEMES: Record<string, Record<string, string>>;
|
|
28
|
+
/**
|
|
29
|
+
* Host custom-property links for the `'host'` theme: token key → the host
|
|
30
|
+
* token it should follow (the site token vocabulary, docs/DESIGN.md §2). The
|
|
31
|
+
* default theme's concrete colors remain as `var()` fallbacks, so the
|
|
32
|
+
* same SVG is standalone-valid outside any host.
|
|
33
|
+
* @type {Record<string, string>}
|
|
34
|
+
*/
|
|
35
|
+
export declare const HOST_VARS: Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a theme. The name `'host'` resolves the default tokens linked
|
|
38
|
+
* to the host token vocabulary via {@link HOST_VARS}.
|
|
39
|
+
* @param {string | Record<string, any>} [nameOrOverrides]
|
|
40
|
+
* @returns {{ name: string, tokens: Record<string, string>, cssVars: Record<string, string> }}
|
|
41
|
+
*/
|
|
42
|
+
export declare function createTheme(nameOrOverrides?: string | Record<string, any>): {
|
|
43
|
+
name: string;
|
|
44
|
+
tokens: Record<string, string>;
|
|
45
|
+
cssVars: Record<string, string>;
|
|
46
|
+
};
|
|
47
|
+
export { THEMES };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Canonical Mermaid printer: AST → text (MERMAID-FORMAT §5).
|
|
3
|
+
* The reverse arrow of `parseMermaid`, mirroring
|
|
4
|
+
* `@jarenjs/md`'s `to-md.js`: one specialized closure per diagram type,
|
|
5
|
+
* a **round-trip fixed point** — `parseMermaid(toMermaid(doc))`
|
|
6
|
+
* deep-equals `doc.ast` for the fully-modeled types.
|
|
7
|
+
*
|
|
8
|
+
* The printer is *canonical*, not verbatim: it does not preserve source
|
|
9
|
+
* whitespace or comments (the stored Markdown fence `value` gives
|
|
10
|
+
* verbatim round-trip while a node is untransformed). Its job is
|
|
11
|
+
* to re-emit an edited AST as editable Mermaid text.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Print a `DiagramDocument` (or a bare AST with a `diagram` field) to
|
|
15
|
+
* canonical Mermaid text. Config re-emits as a `%%{init}%%` header when
|
|
16
|
+
* present.
|
|
17
|
+
* @param {any} docOrAst
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
export declare function toMermaid(docOrAst: any): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Small shared helpers for the mermaid engine.
|
|
3
|
+
*
|
|
4
|
+
* `hashContent` and `coord` are re-exported rather than re-implemented, so
|
|
5
|
+
* this package agrees with the rest of the suite by construction:
|
|
6
|
+
* `hashContent` is the one content fingerprint (the same source produces
|
|
7
|
+
* the same vnode `key`, `meta.hash` and memo key whether it flows through
|
|
8
|
+
* the Markdown engine or this one), and `coord` is the one SVG coordinate
|
|
9
|
+
* quantization, so a layout pass emits geometry at exactly the precision
|
|
10
|
+
* the renderer would round it to — which is what keeps the golden JSON and
|
|
11
|
+
* the SVG it renders to byte-stable instead of carrying float noise.
|
|
12
|
+
*/
|
|
13
|
+
export { hashContent } from '@jarenjs/core/string';
|
|
14
|
+
export { coord } from '@jarenjs/view/helpers';
|
|
15
|
+
/**
|
|
16
|
+
* The first whitespace-delimited token of a line: everything up to the
|
|
17
|
+
* first space or tab (the whole line when there is neither). The
|
|
18
|
+
* grammars are line-oriented and keyword-led, so this is how both the
|
|
19
|
+
* type dispatcher and the sequence parser read a line's keyword —
|
|
20
|
+
* no allocation beyond the returned slice.
|
|
21
|
+
* @param {string} line
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
export declare function firstToken(line: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Split a source string into lines, dropping a single trailing newline
|
|
27
|
+
* and normalizing CRLF/CR to LF first. Comment/blank stripping is the
|
|
28
|
+
* caller's job (each dialect handles its own comment marker).
|
|
29
|
+
* @param {string} source
|
|
30
|
+
* @returns {string[]}
|
|
31
|
+
*/
|
|
32
|
+
export declare function toLines(source: string): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Accumulate a `{ ... }` body that may span multiple lines. `head` is
|
|
35
|
+
* the text after the opening brace on the current line; lines are
|
|
36
|
+
* consumed until one contains the closing brace. Returns the body text
|
|
37
|
+
* before the `}` and the advanced line index so the caller's loop can
|
|
38
|
+
* continue after the block (class and ER entity bodies share this).
|
|
39
|
+
* @param {string[]} lines
|
|
40
|
+
* @param {number} li - index of the line the `{` sits on
|
|
41
|
+
* @param {string} head - text after the opening brace
|
|
42
|
+
* @returns {{ body: string, li: number }}
|
|
43
|
+
*/
|
|
44
|
+
export declare function collectBraceBody(lines: string[], li: number, head: string): {
|
|
45
|
+
body: string;
|
|
46
|
+
li: number;
|
|
47
|
+
};
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# The Jaren Mermaid Format
|
|
2
|
+
|
|
3
|
+
**Version 0.1 — Specification**
|
|
4
|
+
|
|
5
|
+
Module: `@jarenjs/mermaid`. This document is the normative contract for
|
|
6
|
+
the `DiagramDocument` — the JSON vocabulary a Mermaid diagram parses into
|
|
7
|
+
— and the bidirectional round-trip between Mermaid text and that AST. It
|
|
8
|
+
plays the role for diagrams that [MD-FORMAT](../../md/docs/MD-FORMAT.md)
|
|
9
|
+
plays for Markdown.
|
|
10
|
+
|
|
11
|
+
## 1. Introduction
|
|
12
|
+
|
|
13
|
+
### 1.1 What this format is
|
|
14
|
+
|
|
15
|
+
`@jarenjs/mermaid` parses Mermaid diagram source into a plain-JSON,
|
|
16
|
+
**geometry-free** AST wrapped in a `DiagramDocument` envelope. The AST
|
|
17
|
+
carries the diagram's *meaning* — a directed graph, an ordered
|
|
18
|
+
interaction, a finite state machine — and no coordinates: layout is a
|
|
19
|
+
separate, later pass. This makes the AST a faithful, lossless,
|
|
20
|
+
schema-validated semantic model that other engines project via JSLT, and
|
|
21
|
+
a fixed point of the canonical printer `toMermaid`.
|
|
22
|
+
|
|
23
|
+
### 1.2 Non-goals (v0.1)
|
|
24
|
+
|
|
25
|
+
Pixel-identity with browser-Mermaid is an explicit **non-goal**. Labels
|
|
26
|
+
are SVG `<text>` (Mermaid `htmlLabels: false` semantics), because
|
|
27
|
+
`@jarenjs/view` 0.1 has no `foreignObject`. Layout is a deterministic
|
|
28
|
+
approximation over a headless text-metrics table (no `getBBox`). See
|
|
29
|
+
§6.
|
|
30
|
+
|
|
31
|
+
## 2. The DiagramDocument envelope
|
|
32
|
+
|
|
33
|
+
Every parse returns a fresh, never-mutated object:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{ "$mermaid": "0.1",
|
|
37
|
+
"diagram": "flowchart",
|
|
38
|
+
"config": { "theme": "default", "flowchart": { "curve": "basis" } },
|
|
39
|
+
"ast": { "...type-specific, monomorphic, geometry-free..." },
|
|
40
|
+
"meta": { "hash": "8k41x2", "direction": "TD", "title": null } }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Field | Meaning |
|
|
44
|
+
|-------|---------|
|
|
45
|
+
| `$mermaid` | format version (`"0.1"`) |
|
|
46
|
+
| `diagram` | canonical type: `flowchart`, `sequence`, `class`, `er`, `state`, `gantt`, `pie`, or a secondary type name |
|
|
47
|
+
| `config` | plain-JSON config from front-matter + `%%{init}%%` (§3) |
|
|
48
|
+
| `ast` | the type-specific AST (§4) |
|
|
49
|
+
| `meta.hash` | `hashContent(source)` — FNV-1a 32-bit, base-36; the identity key across the suite |
|
|
50
|
+
| `meta.direction` | flowchart direction, else `null` |
|
|
51
|
+
| `meta.title` | front-matter `title`, else `null` |
|
|
52
|
+
|
|
53
|
+
## 3. Config
|
|
54
|
+
|
|
55
|
+
Two sources merge into `config` (plain JSON), in source order:
|
|
56
|
+
|
|
57
|
+
1. A leading `---` front-matter block (a YAML subset): `title` lifts to
|
|
58
|
+
`meta.title`, `config:` merges into `config`.
|
|
59
|
+
2. `%%{init: { … }}%%` directives (relaxed JSON: single quotes and bare
|
|
60
|
+
keys tolerated).
|
|
61
|
+
|
|
62
|
+
Whole-line `%%` comments are stripped. The **raw source is never
|
|
63
|
+
consumed** — it stays in the Markdown fence `value`, so `toMarkdown`
|
|
64
|
+
round-trips byte-for-byte while a node is untransformed (§5).
|
|
65
|
+
|
|
66
|
+
The engine may not statically import `@jarenjs/md`; a host with the md
|
|
67
|
+
frontmatter parser can inject it via `parseMermaidConfig(source, {
|
|
68
|
+
parseFrontmatter })`, otherwise the built-in YAML subset is used.
|
|
69
|
+
|
|
70
|
+
## 4. The AST vocabulary
|
|
71
|
+
|
|
72
|
+
Every node is born from a constructor in `src/ast.js` with a fixed
|
|
73
|
+
member order (monomorphic; the structural hash is deterministic).
|
|
74
|
+
|
|
75
|
+
### 4.1 flowchart
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
{ direction, nodes[], edges[], subgraphs[], classDefs[], classes[], styles[] }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| Shape | Members |
|
|
82
|
+
|-------|---------|
|
|
83
|
+
| node | `{ id, label, shape }` — shape ∈ rect, round, stadium, subroutine, cylinder, circle, doublecircle, diamond, hexagon, parallelogram, parallelogram_alt, trapezoid, trapezoid_alt, asymmetric |
|
|
84
|
+
| edge | `{ from, to, stroke, head, tail, length, label }` — stroke ∈ solid/thick/dotted; head/tail ∈ none/arrow/circle/cross; `length` preserved so the printer is a fixed point |
|
|
85
|
+
| subgraph | `{ id, label, direction, nodes[] }` |
|
|
86
|
+
| classDef / class / style | `{ name, styles }` / `{ node, name }` / `{ node, styles }` |
|
|
87
|
+
|
|
88
|
+
A node `id` is alphanumeric/underscore (plus Unicode letters); `-` and
|
|
89
|
+
`.` are **excluded** so an id can never swallow a following link
|
|
90
|
+
operator — `A-->B` parses as an edge, not as a node with id `A-`. This
|
|
91
|
+
is a deliberate parser-correctness deviation from upstream Mermaid,
|
|
92
|
+
whose grammar permits those characters in ids.
|
|
93
|
+
|
|
94
|
+
### 4.2 sequence
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
{ participants[], statements[], autonumber }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`participants`: `{ id, label, kind }` (kind ∈ participant/actor).
|
|
101
|
+
`statements` is ordered; each is a `message`
|
|
102
|
+
(`{ kind, from, to, text, line, head, activation }`), a `note`
|
|
103
|
+
(`{ kind, placement, actors[], text }`), an `activate`/`deactivate`
|
|
104
|
+
(`{ kind, actor }`), or a `block`
|
|
105
|
+
(`{ kind:'block', blockType, branches:[{ label, statements[] }] }`) for
|
|
106
|
+
loop/opt/alt/par/critical/break.
|
|
107
|
+
|
|
108
|
+
### 4.3 class / er / state / gantt / pie
|
|
109
|
+
|
|
110
|
+
Class: `{ classes:[{ name, label, members[] }], relations[] }`. ER:
|
|
111
|
+
`{ entities:[{ name, attributes[] }], relationships[] }`. State:
|
|
112
|
+
`{ states:[{ id, label }], transitions:[{ from, to, label, event,
|
|
113
|
+
guard, effect, parent }] }` — a transition's `label` is the verbatim
|
|
114
|
+
text after `:` (what renderers draw and `toMermaid` prints), and
|
|
115
|
+
`event`/`guard`/`effect` are its UML reading, parsed as
|
|
116
|
+
`event [guard] / effect` with every part optional: the first `[` opens
|
|
117
|
+
the guard (nesting counted), the effect starts at the first `/` after
|
|
118
|
+
it (or the first `/` at all when there is no guard), and a label that
|
|
119
|
+
fits no pattern — an unmatched `[`, or text between `]` and `/` — reads
|
|
120
|
+
whole as the event, which keeps plain labels meaning what they always
|
|
121
|
+
meant. Gantt: `{ meta, sections:[{ name, tasks[] }] }`. Pie:
|
|
122
|
+
`{ title, showData, slices:[{ label, value }] }`.
|
|
123
|
+
|
|
124
|
+
### 4.4 Secondary types
|
|
125
|
+
|
|
126
|
+
mindmap, gitGraph, journey, timeline, quadrantChart, requirement
|
|
127
|
+
parse-accept into `{ diagram, lines[] }` and render an honest "not yet
|
|
128
|
+
laid out" placeholder — counted in the coverage scorecard.
|
|
129
|
+
|
|
130
|
+
## 5. The bidirectional round trip
|
|
131
|
+
|
|
132
|
+
`toMermaid(doc)` is a **canonical** printer (one closure per diagram
|
|
133
|
+
type) and the reverse of `parseMermaid`. The normative contract:
|
|
134
|
+
|
|
135
|
+
> **`parseMermaid(toMermaid(doc))` deep-equals `doc.ast`** for the
|
|
136
|
+
> fully-modeled types (flowchart, sequence).
|
|
137
|
+
|
|
138
|
+
The flowchart printer emits in a fixed order — node declarations (AST
|
|
139
|
+
order) → subgraph membership → edges → classDef/class/style — and it is
|
|
140
|
+
that ordering which makes node order and edge order a fixed point on the
|
|
141
|
+
round trip. Subgraphs, however, round-trip **structurally, not as a
|
|
142
|
+
deep-equal fixed point**: they emit their members by bare id and a
|
|
143
|
+
re-parse reconstructs membership, so the shape survives but the strict
|
|
144
|
+
`nodes[]` ordering of a subgraph is not guaranteed to be `===`-identical
|
|
145
|
+
to the original.
|
|
146
|
+
|
|
147
|
+
The printer is canonical, not verbatim: it does not preserve source
|
|
148
|
+
whitespace or comments. Two round-trip modes coexist:
|
|
149
|
+
|
|
150
|
+
- **verbatim**, via the raw source stored in the Markdown fence `value`
|
|
151
|
+
while a node is *untransformed*;
|
|
152
|
+
- **canonical**, via `toMermaid` once the AST changes or the engine is
|
|
153
|
+
used standalone.
|
|
154
|
+
|
|
155
|
+
Because there is no per-plugin `toMarkdown` hook, a JSLT-transformed
|
|
156
|
+
diagram round-trips through `toMarkdown` only when its fence `value` is
|
|
157
|
+
refreshed with `toMermaid(newDoc)` — `refreshMermaidFence` is that
|
|
158
|
+
primitive.
|
|
159
|
+
|
|
160
|
+
### 5.1 Semantic projections
|
|
161
|
+
|
|
162
|
+
The geometry-free AST doubles as a domain model, and four JSLT
|
|
163
|
+
stylesheets in [`stylesheets/`](../stylesheets/) project it both ways —
|
|
164
|
+
plain data documents, no code:
|
|
165
|
+
|
|
166
|
+
| stylesheet | from → to |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `state-to-workflow.jslt.json` | state DiagramDocument → executable machine (`@jarenjs/flow`'s jaren-fsm superset shape) |
|
|
169
|
+
| `workflow-to-state.jslt.json` | machine document → state AST (print with `toMermaid`) |
|
|
170
|
+
| `flowchart-to-dag.jslt.json` | flowchart DiagramDocument → jaren-dag **skeleton** (every node a `task` stub named by its id; edge labels become `port`s verbatim) |
|
|
171
|
+
| `dag-to-flowchart.jslt.json` | jaren-dag document → flowchart AST |
|
|
172
|
+
|
|
173
|
+
The forward state projection maps the parsed UML parts: `event` and
|
|
174
|
+
`guard` carry over when present, and an `effect` becomes
|
|
175
|
+
`effects: [{ "run": <effect text> }]` — the effect label **is** the
|
|
176
|
+
registry name by convention. The reverse composes the label from the
|
|
177
|
+
machine's parts and stays consistent with the parser by construction.
|
|
178
|
+
|
|
179
|
+
The dag projection renders each node kind as a fixed flowchart shape:
|
|
180
|
+
|
|
181
|
+
| kind | shape |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `input` | stadium |
|
|
184
|
+
| `output` | doublecircle |
|
|
185
|
+
| `const` | circle |
|
|
186
|
+
| `query` | rect |
|
|
187
|
+
| `jslt` | round |
|
|
188
|
+
| `task` | subroutine |
|
|
189
|
+
|
|
190
|
+
Edge decorations print into the edge label as `port` / `port · select`
|
|
191
|
+
(joined with ` · `).
|
|
192
|
+
|
|
193
|
+
**Lossiness is documented, not hidden.** Diagrams are pictures of
|
|
194
|
+
machines; the document is the truth. A structured (non-string) guard
|
|
195
|
+
prints as the `[…]` placeholder; an effect prints its `run` name only
|
|
196
|
+
(multiple effects join with `, `), dropping any `with`; a structured
|
|
197
|
+
edge `select` prints as `…`. Round-tripping is exact for string guards
|
|
198
|
+
and bare run names, and deliberately lossy beyond that. Division of
|
|
199
|
+
labor: a cyclic flowchart **projects** to a dag skeleton without
|
|
200
|
+
complaint — acyclicity is `compileDag`'s job (`JF0016`), not the
|
|
201
|
+
projection's.
|
|
202
|
+
|
|
203
|
+
## 6. Layout & metrics (informative)
|
|
204
|
+
|
|
205
|
+
Layout is a separate pure pass (`layoutDiagram`) producing a
|
|
206
|
+
`PositionedDiagram` scene graph. Node/label sizes come from
|
|
207
|
+
`measureText`, a per-codepoint advance-width table for a default
|
|
208
|
+
sans-serif — an approximation, no `getBBox`, no DOM. Flowchart layout is
|
|
209
|
+
a compact dagre-lite (longest-path ranks, banded coordinates, straight
|
|
210
|
+
border-clipped edges); sequence layout resolves lifelines, message
|
|
211
|
+
y-advance, activation bars, notes and block frames. Geometry is
|
|
212
|
+
deterministic, so golden-JSON tests catch drift.
|
|
213
|
+
|
|
214
|
+
**State diagrams lay out through the flowchart engine** via an adapter
|
|
215
|
+
(`layoutState`), not a second algorithm: states become rounded nodes,
|
|
216
|
+
transition `label`s become edge labels verbatim, and the `[*]`
|
|
217
|
+
pseudo-states become synthetic `__start`/`__end` nodes (a fixed-size
|
|
218
|
+
filled `statedot` and an empty-labeled `doublecircle` ring — shapes
|
|
219
|
+
only the adapter produces; flowchart source cannot spell them).
|
|
220
|
+
Composite states stay flattened in v1, their recorded `parent` not yet
|
|
221
|
+
drawn as a cluster. `compileMermaid(source).toLayout()` exposes the
|
|
222
|
+
scene as a cached projection — reference-equal on repeated calls, null
|
|
223
|
+
for types without a geometric layout — so an editor hit-tests against
|
|
224
|
+
pure geometry without re-running layout.
|
|
225
|
+
|
|
226
|
+
## 7. Rendering
|
|
227
|
+
|
|
228
|
+
`diagramToVnode` turns a `PositionedDiagram` into a tagged-array SVG
|
|
229
|
+
vnode rooted at `['svg', …]`, which the `@jarenjs/view` patcher creates
|
|
230
|
+
in the SVG namespace. It is synchronous, complete and **error-safe**: a
|
|
231
|
+
parse/layout failure returns an error vnode, never throws. `toSvgString`
|
|
232
|
+
is `renderToString` of that vnode — a valid standalone SVG with no
|
|
233
|
+
browser.
|
|
234
|
+
|
|
235
|
+
**Stable identity for editors.** In flowchart and state output, every
|
|
236
|
+
node group carries `data-id="<AST node id>"` and every edge group
|
|
237
|
+
carries `data-edge="<index>" data-from="<id>" data-to="<id>"`, where
|
|
238
|
+
the index is the edge's AST position — which is also its docPath tail,
|
|
239
|
+
so a click maps to a document member without translation. These are
|
|
240
|
+
plain data props on the vnodes: SSR emits them, event delegation reads
|
|
241
|
+
them, and the state renderer's root additionally carries the
|
|
242
|
+
`mm-state` class for theming.
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jarenjs/mermaid",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.34.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"types": "./dist/types/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"default": "./src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./component": {
|
|
15
|
+
"types": "./dist/types/component/index.d.ts",
|
|
16
|
+
"default": "./src/component/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./styles": {
|
|
19
|
+
"types": "./dist/types/styles.d.ts",
|
|
20
|
+
"default": "./src/styles.js"
|
|
21
|
+
},
|
|
22
|
+
"./interactive": {
|
|
23
|
+
"types": "./dist/types/interactive.d.ts",
|
|
24
|
+
"default": "./src/interactive.js"
|
|
25
|
+
},
|
|
26
|
+
"./plugin": {
|
|
27
|
+
"types": "./dist/types/plugin.d.ts",
|
|
28
|
+
"default": "./src/plugin.js"
|
|
29
|
+
},
|
|
30
|
+
"./theme": {
|
|
31
|
+
"types": "./dist/types/theme.d.ts",
|
|
32
|
+
"default": "./src/theme.js"
|
|
33
|
+
},
|
|
34
|
+
"./styles/mermaid.css": "./styles/mermaid.css",
|
|
35
|
+
"./schemas/*": "./schemas/*",
|
|
36
|
+
"./stylesheets/*": "./stylesheets/*",
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist/types/",
|
|
41
|
+
"src/",
|
|
42
|
+
"docs/",
|
|
43
|
+
"schemas/",
|
|
44
|
+
"stylesheets/",
|
|
45
|
+
"styles/"
|
|
46
|
+
],
|
|
47
|
+
"description": "A native, headless Mermaid clone: diagrams-as-code parsed to a geometry-free JSON AST and rendered as pure-vnode SVG through @jarenjs/view — SSR-able, structurally shared, bidirectional (parseMermaid ⇄ toMermaid)",
|
|
48
|
+
"author": "joham",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/jklarenbeek/jarenjs.git",
|
|
52
|
+
"directory": "components/mermaid"
|
|
53
|
+
},
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=24"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public",
|
|
60
|
+
"registry": "https://registry.npmjs.org/"
|
|
61
|
+
},
|
|
62
|
+
"keywords": [
|
|
63
|
+
"jaren",
|
|
64
|
+
"json",
|
|
65
|
+
"mermaid",
|
|
66
|
+
"diagram",
|
|
67
|
+
"flowchart",
|
|
68
|
+
"sequence",
|
|
69
|
+
"svg",
|
|
70
|
+
"headless",
|
|
71
|
+
"ast",
|
|
72
|
+
"parser"
|
|
73
|
+
],
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "npm run build:types",
|
|
76
|
+
"build:types": "tsc -p tsconfig.json",
|
|
77
|
+
"prepack": "npm run build:types"
|
|
78
|
+
},
|
|
79
|
+
"dependencies": {
|
|
80
|
+
"@jarenjs/charts": "^0.34.0",
|
|
81
|
+
"@jarenjs/core": "^0.34.0",
|
|
82
|
+
"@jarenjs/view": "^0.34.0"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-mermaid-ast/0.1",
|
|
4
|
+
"title": "Jaren Mermaid diagram document",
|
|
5
|
+
"description": "The DiagramDocument envelope and per-type AST vocabulary of @jarenjs/mermaid (normative prose in docs/MERMAID-FORMAT.md). Draft-neutral: no $ref siblings, no unevaluated*, no $dynamic*. The AST is geometry-free — layout is a separate pass.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["$mermaid", "diagram", "config", "ast", "meta"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$mermaid": { "const": "0.1" },
|
|
10
|
+
"diagram": { "type": "string" },
|
|
11
|
+
"config": { "type": "object" },
|
|
12
|
+
"ast": { "type": "object" },
|
|
13
|
+
"meta": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["hash", "direction", "title"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"hash": { "type": "string" },
|
|
18
|
+
"direction": { "type": ["string", "null"] },
|
|
19
|
+
"title": { "type": ["string", "null"] }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"$defs": {
|
|
24
|
+
"flowNode": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["id", "label", "shape"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"id": { "type": "string" },
|
|
29
|
+
"label": { "type": "string" },
|
|
30
|
+
"shape": { "type": "string" }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"flowEdge": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"required": ["from", "to", "stroke", "head", "tail", "length", "label"],
|
|
36
|
+
"properties": {
|
|
37
|
+
"from": { "type": "string" },
|
|
38
|
+
"to": { "type": "string" },
|
|
39
|
+
"stroke": { "enum": ["solid", "thick", "dotted"] },
|
|
40
|
+
"head": { "enum": ["none", "arrow", "circle", "cross"] },
|
|
41
|
+
"tail": { "enum": ["none", "arrow", "circle", "cross"] },
|
|
42
|
+
"length": { "type": "integer" },
|
|
43
|
+
"label": { "type": ["string", "null"] }
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"flowchartAst": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"required": ["direction", "nodes", "edges", "subgraphs", "classDefs", "classes", "styles"],
|
|
49
|
+
"properties": {
|
|
50
|
+
"direction": { "type": "string" },
|
|
51
|
+
"nodes": { "type": "array", "items": { "$ref": "#/$defs/flowNode" } },
|
|
52
|
+
"edges": { "type": "array", "items": { "$ref": "#/$defs/flowEdge" } },
|
|
53
|
+
"subgraphs": { "type": "array" },
|
|
54
|
+
"classDefs": { "type": "array" },
|
|
55
|
+
"classes": { "type": "array" },
|
|
56
|
+
"styles": { "type": "array" }
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"seqParticipant": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": ["id", "label", "kind"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"id": { "type": "string" },
|
|
64
|
+
"label": { "type": "string" },
|
|
65
|
+
"kind": { "enum": ["participant", "actor"] }
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"sequenceAst": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"required": ["participants", "statements", "autonumber"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"participants": { "type": "array", "items": { "$ref": "#/$defs/seqParticipant" } },
|
|
73
|
+
"statements": { "type": "array" },
|
|
74
|
+
"autonumber": { "type": "boolean" }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-workflow/0.1",
|
|
4
|
+
"title": "Jaren workflow / finite state machine",
|
|
5
|
+
"description": "The domain document a state DiagramDocument projects to via stylesheets/state-to-workflow.jslt.json. Draft-neutral. This is the machine an @jarenjs/app transition table drives: events are actions.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["initial", "states", "transitions"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"initial": { "type": ["string", "null"] },
|
|
10
|
+
"states": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": { "type": "string" }
|
|
13
|
+
},
|
|
14
|
+
"transitions": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"items": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"required": ["from", "event", "to"],
|
|
19
|
+
"properties": {
|
|
20
|
+
"from": { "type": "string" },
|
|
21
|
+
"event": { "type": ["string", "null"] },
|
|
22
|
+
"guard": { "type": ["string", "null"] },
|
|
23
|
+
"to": { "type": "string" }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|