@milehimikey/em 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +183 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +160 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/emit/dot.d.ts +3 -0
  7. package/dist/emit/dot.js +150 -0
  8. package/dist/emit/dot.js.map +1 -0
  9. package/dist/emit/theme.d.ts +9 -0
  10. package/dist/emit/theme.js +35 -0
  11. package/dist/emit/theme.js.map +1 -0
  12. package/dist/layout/grid.d.ts +33 -0
  13. package/dist/layout/grid.js +101 -0
  14. package/dist/layout/grid.js.map +1 -0
  15. package/dist/model/edges.d.ts +8 -0
  16. package/dist/model/edges.js +78 -0
  17. package/dist/model/edges.js.map +1 -0
  18. package/dist/model/model.d.ts +49 -0
  19. package/dist/model/model.js +109 -0
  20. package/dist/model/model.js.map +1 -0
  21. package/dist/model/validate.d.ts +13 -0
  22. package/dist/model/validate.js +127 -0
  23. package/dist/model/validate.js.map +1 -0
  24. package/dist/parser/ast.d.ts +44 -0
  25. package/dist/parser/ast.js +10 -0
  26. package/dist/parser/ast.js.map +1 -0
  27. package/dist/parser/lexer.d.ts +6 -0
  28. package/dist/parser/lexer.js +46 -0
  29. package/dist/parser/lexer.js.map +1 -0
  30. package/dist/parser/parser.d.ts +6 -0
  31. package/dist/parser/parser.js +207 -0
  32. package/dist/parser/parser.js.map +1 -0
  33. package/dist/pipeline.d.ts +11 -0
  34. package/dist/pipeline.js +16 -0
  35. package/dist/pipeline.js.map +1 -0
  36. package/dist/render/drawEdges.d.ts +7 -0
  37. package/dist/render/drawEdges.js +83 -0
  38. package/dist/render/drawEdges.js.map +1 -0
  39. package/dist/render/drawNotes.d.ts +13 -0
  40. package/dist/render/drawNotes.js +117 -0
  41. package/dist/render/drawNotes.js.map +1 -0
  42. package/dist/render/render.d.ts +9 -0
  43. package/dist/render/render.js +106 -0
  44. package/dist/render/render.js.map +1 -0
  45. package/dist/render/svgGeometry.d.ts +10 -0
  46. package/dist/render/svgGeometry.js +47 -0
  47. package/dist/render/svgGeometry.js.map +1 -0
  48. package/dist/render/watch.d.ts +2 -0
  49. package/dist/render/watch.js +18 -0
  50. package/dist/render/watch.js.map +1 -0
  51. package/dist/templates.d.ts +1 -0
  52. package/dist/templates.js +53 -0
  53. package/dist/templates.js.map +1 -0
  54. package/package.json +61 -0
@@ -0,0 +1,207 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Line-oriented parser for the `.em` slice-first DSL.
3
+ //
4
+ // Grammar (whitespace-tolerant, `#` starts a comment):
5
+ //
6
+ // model "Name"
7
+ // persona Name
8
+ // context Name
9
+ // slice "Name" {
10
+ // ui <free text> [@Persona]
11
+ // command <free text>
12
+ // view <free text> [from "Event"[, "Event2" ...]]
13
+ // event <free text> [@Context]
14
+ // automation|processor|saga|translation <free text>
15
+ // }
16
+ // arrow <From Element> -> <To Element>
17
+ import { splitQuotedList, stripComment, unquote } from "./lexer.js";
18
+ export class ParseError extends Error {
19
+ line;
20
+ constructor(message, line) {
21
+ super(`line ${line}: ${message}`);
22
+ this.line = line;
23
+ this.name = "ParseError";
24
+ }
25
+ }
26
+ const ELEMENT_KEYWORDS = new Set([
27
+ "ui",
28
+ "command",
29
+ "view",
30
+ "event",
31
+ "automation",
32
+ "processor",
33
+ "saga",
34
+ "translation",
35
+ ]);
36
+ export function parse(source) {
37
+ const model = {
38
+ name: "",
39
+ personas: [],
40
+ contexts: [],
41
+ slices: [],
42
+ arrows: [],
43
+ };
44
+ const rawLines = source.split(/\r?\n/);
45
+ let currentSlice = null;
46
+ let currentElement = null; // open `{ … }` field block
47
+ for (let i = 0; i < rawLines.length; i++) {
48
+ const lineNo = i + 1;
49
+ const line = stripComment(rawLines[i]).trim();
50
+ if (line.length === 0)
51
+ continue;
52
+ // End of a block: an element's field block closes before its slice.
53
+ if (line === "}") {
54
+ if (currentElement) {
55
+ currentElement = null;
56
+ continue;
57
+ }
58
+ if (currentSlice) {
59
+ model.slices.push(currentSlice);
60
+ currentSlice = null;
61
+ continue;
62
+ }
63
+ throw new ParseError("unexpected '}'", lineNo);
64
+ }
65
+ // Inside an open field block: each line is a field declaration.
66
+ if (currentElement) {
67
+ for (const spec of line.split(",")) {
68
+ const f = parseFieldSpec(spec);
69
+ if (f)
70
+ (currentElement.fields ??= []).push(f);
71
+ }
72
+ continue;
73
+ }
74
+ const [keyword, ...rest] = splitFirstWord(line);
75
+ const remainder = rest.join(" ").trim();
76
+ // Inside a slice: only element declarations are allowed.
77
+ if (currentSlice) {
78
+ if (!ELEMENT_KEYWORDS.has(keyword)) {
79
+ throw new ParseError(`'${keyword}' is not valid inside a slice (expected ui/command/view/event/automation/processor/saga/translation or '}')`, lineNo);
80
+ }
81
+ // An element may open a `{ … }` field block (inline or multi-line).
82
+ const braceAt = remainder.indexOf("{");
83
+ if (braceAt >= 0) {
84
+ const el = parseElement(keyword, remainder.slice(0, braceAt).trim(), lineNo);
85
+ el.fields = [];
86
+ currentSlice.elements.push(el);
87
+ const after = remainder.slice(braceAt + 1);
88
+ const closeAt = after.indexOf("}");
89
+ const inner = closeAt >= 0 ? after.slice(0, closeAt) : after;
90
+ for (const spec of inner.split(",")) {
91
+ const f = parseFieldSpec(spec);
92
+ if (f)
93
+ el.fields.push(f);
94
+ }
95
+ if (closeAt < 0)
96
+ currentElement = el; // block stays open across lines
97
+ continue;
98
+ }
99
+ currentSlice.elements.push(parseElement(keyword, remainder, lineNo));
100
+ continue;
101
+ }
102
+ // Top level.
103
+ switch (keyword) {
104
+ case "model":
105
+ model.name = unquote(remainder);
106
+ break;
107
+ case "persona":
108
+ pushUnique(model.personas, unquote(remainder));
109
+ break;
110
+ case "context":
111
+ pushUnique(model.contexts, unquote(remainder));
112
+ break;
113
+ case "slice": {
114
+ const open = remainder.lastIndexOf("{");
115
+ if (open < 0)
116
+ throw new ParseError("slice must open a block with '{'", lineNo);
117
+ const name = unquote(remainder.slice(0, open).trim());
118
+ if (!name)
119
+ throw new ParseError("slice requires a name", lineNo);
120
+ currentSlice = { name, elements: [], line: lineNo };
121
+ break;
122
+ }
123
+ case "arrow":
124
+ model.arrows.push(parseArrow(remainder, lineNo));
125
+ break;
126
+ default:
127
+ throw new ParseError(`unknown keyword '${keyword}'`, lineNo);
128
+ }
129
+ }
130
+ if (currentElement) {
131
+ throw new ParseError(`field block for "${currentElement.name}" is missing a closing '}'`, currentElement.line);
132
+ }
133
+ if (currentSlice) {
134
+ throw new ParseError(`slice "${currentSlice.name}" is missing a closing '}'`, currentSlice.line);
135
+ }
136
+ if (!model.name)
137
+ model.name = "Event Model";
138
+ return model;
139
+ }
140
+ function parseElement(kind, raw, line) {
141
+ let rest = raw;
142
+ const node = { kind, name: "", line };
143
+ // `note "path.md"` clause (valid on any element). Pulled off first because
144
+ // the `from` clause below greedily consumes to end-of-line.
145
+ const noteMatch = rest.match(/\snote\s+"([^"]*)"/i);
146
+ if (noteMatch && noteMatch.index !== undefined) {
147
+ node.note = noteMatch[1];
148
+ rest = (rest.slice(0, noteMatch.index) + rest.slice(noteMatch.index + noteMatch[0].length)).trim();
149
+ }
150
+ // `from "A", "B"` clause (view only, but parsed wherever present).
151
+ const fromMatch = rest.match(/\sfrom\s+(.+)$/i);
152
+ if (fromMatch && fromMatch.index !== undefined) {
153
+ node.from = splitQuotedList(fromMatch[1]);
154
+ rest = rest.slice(0, fromMatch.index).trim();
155
+ }
156
+ // Trailing `@Tag` (persona for ui, context for event).
157
+ const tagMatch = rest.match(/(?:^|\s)@(\S+)\s*$/);
158
+ if (tagMatch && tagMatch.index !== undefined) {
159
+ const tag = tagMatch[1];
160
+ if (kind === "ui")
161
+ node.persona = tag;
162
+ else if (kind === "event")
163
+ node.context = tag;
164
+ else
165
+ throw new ParseError(`'@${tag}' tag is only valid on ui (persona) or event (context)`, line);
166
+ rest = rest.slice(0, tagMatch.index).trim();
167
+ }
168
+ node.name = unquote(rest);
169
+ if (!node.name)
170
+ throw new ParseError(`${kind} requires a name`, line);
171
+ return node;
172
+ }
173
+ /** Parse one field spec: `name` or `name: Type`. Returns null for blanks. */
174
+ function parseFieldSpec(raw) {
175
+ const s = raw.trim();
176
+ if (!s)
177
+ return null;
178
+ const colon = s.indexOf(":");
179
+ if (colon >= 0) {
180
+ const name = unquote(s.slice(0, colon).trim());
181
+ const type = unquote(s.slice(colon + 1).trim());
182
+ return name ? { name, ...(type ? { type } : {}) } : null;
183
+ }
184
+ const name = unquote(s);
185
+ return name ? { name } : null;
186
+ }
187
+ function parseArrow(raw, line) {
188
+ const parts = raw.split("->");
189
+ if (parts.length !== 2)
190
+ throw new ParseError("arrow must be of the form 'A -> B'", line);
191
+ const from = unquote(parts[0].trim());
192
+ const to = unquote(parts[1].trim());
193
+ if (!from || !to)
194
+ throw new ParseError("arrow endpoints required", line);
195
+ return { from, to, line };
196
+ }
197
+ function splitFirstWord(line) {
198
+ const idx = line.search(/\s/);
199
+ if (idx < 0)
200
+ return [line];
201
+ return [line.slice(0, idx), line.slice(idx + 1)];
202
+ }
203
+ function pushUnique(arr, v) {
204
+ if (v && !arr.includes(v))
205
+ arr.push(v);
206
+ }
207
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parser/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { NormalizedModel } from "./model/model.js";
2
+ import { Grid, LayoutOptions } from "./layout/grid.js";
3
+ import { Diagnostic } from "./model/validate.js";
4
+ export type CompileOptions = LayoutOptions;
5
+ export interface CompileResult {
6
+ model: NormalizedModel;
7
+ grid: Grid;
8
+ diagnostics: Diagnostic[];
9
+ dot: string;
10
+ }
11
+ export declare function compile(source: string, opts?: CompileOptions): CompileResult;
@@ -0,0 +1,16 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Ties the stages together: source -> AST -> model -> grid -> diagnostics -> DOT.
3
+ import { parse } from "./parser/parser.js";
4
+ import { normalize } from "./model/model.js";
5
+ import { layout } from "./layout/grid.js";
6
+ import { validate } from "./model/validate.js";
7
+ import { emitDot } from "./emit/dot.js";
8
+ export function compile(source, opts = {}) {
9
+ const ast = parse(source);
10
+ const model = normalize(ast);
11
+ const grid = layout(model, opts);
12
+ const diagnostics = validate(model, grid);
13
+ const dot = emitDot(model, grid);
14
+ return { model, grid, diagnostics, dot };
15
+ }
16
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,kFAAkF;AAElF,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAmB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAuB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAc,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAWxC,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,OAAuB,EAAE;IAC/D,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { NormalizedModel } from "../model/model.js";
2
+ import { Rect } from "./svgGeometry.js";
3
+ export interface EdgeOverlay {
4
+ defs: string;
5
+ group: string;
6
+ }
7
+ export declare function buildEdgeOverlay(model: NormalizedModel, rects: Map<string, Rect>): EdgeOverlay;
@@ -0,0 +1,83 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Turns the model's semantic edges into SVG, drawn over the Graphviz grid.
3
+ //
4
+ // Coordinates come from parseNodeRects (same user space as the SVG). y grows
5
+ // downward, so the visually-upper box has the smaller cy. We draw:
6
+ // - within a slice (same column): a straight vertical, facing-edge centre to
7
+ // facing-edge centre.
8
+ // - across slices: a cubic bezier that leaves/enters perpendicular to the
9
+ // facing edges and arcs through the channel between the boxes.
10
+ // Arrowheads are per-colour markers referenced with marker-end.
11
+ import { semanticEdges } from "../model/edges.js";
12
+ const STROKE = 1.5;
13
+ const MIN_BOW = 24;
14
+ const MAX_BOW = 90;
15
+ const SAME_ROW_EPS = 10; // |cy| difference under this counts as the same row
16
+ export function buildEdgeOverlay(model, rects) {
17
+ const paths = [];
18
+ const colors = new Set();
19
+ for (const e of semanticEdges(model)) {
20
+ const f = rects.get(e.from);
21
+ const t = rects.get(e.to);
22
+ if (!f || !t)
23
+ continue;
24
+ const fSlice = model.byId.get(e.from)?.sliceIndex;
25
+ const tSlice = model.byId.get(e.to)?.sliceIndex;
26
+ const d = fSlice !== undefined && fSlice === tSlice ? straight(f, t) : curve(f, t);
27
+ colors.add(e.color);
28
+ paths.push(`<path fill="none" stroke="${e.color}" stroke-width="${STROKE}" ` +
29
+ `d="${d}" marker-end="url(#${markerId(e.color)})"/>`);
30
+ }
31
+ const markers = [...colors].map(marker).join("");
32
+ return {
33
+ defs: markers ? `<defs>${markers}</defs>` : "",
34
+ group: `<g class="em-edges">${paths.join("")}</g>`,
35
+ };
36
+ }
37
+ /** Straight vertical between two same-column boxes, facing edge to facing edge. */
38
+ function straight(f, t) {
39
+ const x = (f.cx + t.cx) / 2;
40
+ if (f.cy <= t.cy) {
41
+ // f is upper: leave its bottom, enter t's top (arrow points down)
42
+ return `M${n(x)},${n(f.bottom)} L${n(x)},${n(t.top)}`;
43
+ }
44
+ // f is lower: leave its top, enter t's bottom (arrow points up)
45
+ return `M${n(x)},${n(f.top)} L${n(x)},${n(t.bottom)}`;
46
+ }
47
+ /** Smooth cubic between two boxes in different columns. */
48
+ function curve(f, t) {
49
+ const dy = t.cy - f.cy;
50
+ if (Math.abs(dy) <= SAME_ROW_EPS) {
51
+ // same band: leave/enter on the facing side, bow horizontally
52
+ const k = clamp(0.5 * Math.abs(t.cx - f.cx), MIN_BOW, MAX_BOW);
53
+ if (t.cx >= f.cx) {
54
+ return cubic(f.right, f.cy, f.right + k, f.cy, t.left - k, t.cy, t.left, t.cy);
55
+ }
56
+ return cubic(f.left, f.cy, f.left - k, f.cy, t.right + k, t.cy, t.right, t.cy);
57
+ }
58
+ const k = clamp(0.6 * Math.abs(dy), MIN_BOW, MAX_BOW);
59
+ if (dy < 0) {
60
+ // t is above f: leave f's top, enter t's bottom
61
+ return cubic(f.cx, f.top, f.cx, f.top - k, t.cx, t.bottom + k, t.cx, t.bottom);
62
+ }
63
+ // t is below f: leave f's bottom, enter t's top
64
+ return cubic(f.cx, f.bottom, f.cx, f.bottom + k, t.cx, t.top - k, t.cx, t.top);
65
+ }
66
+ function cubic(x0, y0, x1, y1, x2, y2, x3, y3) {
67
+ return `M${n(x0)},${n(y0)} C${n(x1)},${n(y1)} ${n(x2)},${n(y2)} ${n(x3)},${n(y3)}`;
68
+ }
69
+ function marker(color) {
70
+ return (`<marker id="${markerId(color)}" markerWidth="9" markerHeight="9" refX="6.5" refY="3" ` +
71
+ `orient="auto" markerUnits="userSpaceOnUse">` +
72
+ `<path d="M0,0 L7,3 L0,6 Z" fill="${color}"/></marker>`);
73
+ }
74
+ function markerId(color) {
75
+ return `em-arrow-${color.replace(/[^0-9a-z]/gi, "")}`;
76
+ }
77
+ function clamp(v, lo, hi) {
78
+ return Math.max(lo, Math.min(hi, v));
79
+ }
80
+ function n(v) {
81
+ return Math.round(v * 100) / 100;
82
+ }
83
+ //# sourceMappingURL=drawEdges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawEdges.js","sourceRoot":"","sources":["../../src/render/drawEdges.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Element, NormalizedModel } from "../model/model.js";
2
+ import { Rect } from "./svgGeometry.js";
3
+ /** Elements carrying a note, in document order. Index + 1 is the footnote number. */
4
+ export declare function notedElements(model: NormalizedModel): Element[];
5
+ /** How to turn an element's note into the link href (default: the raw note path). */
6
+ export type HrefOf = (el: Element) => string;
7
+ /** SVG group with a numbered corner marker per noted element (inside the graph group). */
8
+ export declare function buildNoteMarkers(model: NormalizedModel, rects: Map<string, Rect>, hrefOf?: HrefOf): string;
9
+ /**
10
+ * Grow the canvas and append a notes legend below the diagram. Returns the SVG
11
+ * unchanged if there are no notes or the dimensions can't be parsed.
12
+ */
13
+ export declare function appendNoteLegend(svg: string, model: NormalizedModel, hrefOf?: HrefOf): string;
@@ -0,0 +1,117 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Draws "has notes" affordances on top of a Graphviz-rendered SVG:
3
+ // - a small folded-corner (dog-ear) marker in each noted box's top-right
4
+ // corner, carrying a footnote number;
5
+ // - a legend appended below the diagram mapping each number to its element
6
+ // and note file.
7
+ // In SVG the markers and legend rows are anchors (click to open the markdown);
8
+ // raster output (PNG/PDF) can't carry links, so the footnote number + legend
9
+ // are what tell you which note belongs to which element.
10
+ //
11
+ // Marker coordinates come from parseNodeRects (the box coordinate space, inside
12
+ // Graphviz's transform group). The legend is laid out in the SVG's root/viewBox
13
+ // space, so it is appended after the diagram and the canvas is grown to fit.
14
+ const FOLD = 13; // leg length of the dog-ear, in user units
15
+ const INSET = 5; // pull off the bounding corner so it clears rounded box corners
16
+ const FILL = "#F4C430"; // sticky-note amber
17
+ const STROKE = "#7A5200"; // dark amber — reads on light and orange fills alike
18
+ /** Elements carrying a note, in document order. Index + 1 is the footnote number. */
19
+ export function notedElements(model) {
20
+ return model.elements.filter((el) => el.note);
21
+ }
22
+ const rawNote = (el) => el.note ?? "";
23
+ /** SVG group with a numbered corner marker per noted element (inside the graph group). */
24
+ export function buildNoteMarkers(model, rects, hrefOf = rawNote) {
25
+ const markers = [];
26
+ notedElements(model).forEach((el, i) => {
27
+ const r = rects.get(el.id);
28
+ if (!r)
29
+ return;
30
+ markers.push(marker(r, hrefOf(el), i + 1));
31
+ });
32
+ return `<g class="em-notes">${markers.join("")}</g>`;
33
+ }
34
+ /** A folded-corner glyph + footnote number at the box's top-right, linked to the note. */
35
+ function marker(r, note, num) {
36
+ const href = esc(note);
37
+ // Dog-ear inset from the box's top-right corner: a filled triangle whose
38
+ // right-angle sits on the box face, with a fold crease across its hypotenuse.
39
+ const x = r.right - INSET;
40
+ const y = r.top + INSET; // y grows downward, so this nudges down from the top edge
41
+ const tri = `M${n(x - FOLD)},${n(y)} L${n(x)},${n(y)} L${n(x)},${n(y + FOLD)} Z`;
42
+ const crease = `M${n(x - FOLD)},${n(y)} L${n(x)},${n(y + FOLD)}`;
43
+ // footnote number, just left of the fold, inside the box
44
+ const numX = x - FOLD - 2;
45
+ const numY = y + 9;
46
+ return (`<a xlink:href="${href}" href="${href}" target="_blank">` +
47
+ `<title>${num}. ${href}</title>` +
48
+ `<path d="${tri}" fill="${FILL}" stroke="${STROKE}" stroke-width="1" stroke-linejoin="round"/>` +
49
+ `<path d="${crease}" fill="none" stroke="${STROKE}" stroke-width="0.7"/>` +
50
+ `<text x="${n(numX)}" y="${n(numY)}" text-anchor="end" font-family="Helvetica" ` +
51
+ `font-weight="bold" font-size="10" fill="${STROKE}">${num}</text>` +
52
+ `</a>`);
53
+ }
54
+ // ---- legend ----
55
+ const PAD_X = 16;
56
+ const PAD_TOP = 14;
57
+ const HEAD_H = 22;
58
+ const LINE_H = 18;
59
+ const PAD_BOTTOM = 14;
60
+ /**
61
+ * Grow the canvas and append a notes legend below the diagram. Returns the SVG
62
+ * unchanged if there are no notes or the dimensions can't be parsed.
63
+ */
64
+ export function appendNoteLegend(svg, model, hrefOf = rawNote) {
65
+ const noted = notedElements(model);
66
+ if (noted.length === 0)
67
+ return svg;
68
+ const vb = /viewBox="([\d.eE+-]+)\s+([\d.eE+-]+)\s+([\d.eE+-]+)\s+([\d.eE+-]+)"/.exec(svg);
69
+ const hpt = /height="([\d.eE+-]+)pt"/.exec(svg);
70
+ if (!vb || !hpt)
71
+ return svg; // can't safely resize — leave markers only
72
+ const minX = +vb[1];
73
+ const minY = +vb[2];
74
+ const vw = +vb[3];
75
+ const vh = +vb[4];
76
+ const legendH = PAD_TOP + HEAD_H + noted.length * LINE_H + PAD_BOTTOM;
77
+ const newVh = vh + legendH;
78
+ const newHpt = (+hpt[1]) * (newVh / vh); // keep the pt:viewBox ratio (handles scaling)
79
+ const top = minY + vh; // legend sits just below the diagram, in root coords
80
+ let out = svg;
81
+ out = out.replace(hpt[0], `height="${n(newHpt)}pt"`);
82
+ out = out.replace(vb[0], `viewBox="${vb[1]} ${vb[2]} ${vb[3]} ${n(newVh)}"`);
83
+ const rows = noted
84
+ .map((el, i) => row(el, hrefOf(el), i + 1, minX + PAD_X, top + PAD_TOP + HEAD_H + i * LINE_H + 13))
85
+ .join("");
86
+ const legend = `<g class="em-note-legend">` +
87
+ `<rect x="${n(minX)}" y="${n(top)}" width="${n(vw)}" height="${n(legendH)}" fill="#FFFFFF"/>` +
88
+ `<line x1="${n(minX)}" y1="${n(top)}" x2="${n(minX + vw)}" y2="${n(top)}" stroke="#D0D0D0" stroke-width="1"/>` +
89
+ `<text x="${n(minX + PAD_X)}" y="${n(top + PAD_TOP + 14)}" font-family="Helvetica" ` +
90
+ `font-weight="bold" font-size="12" fill="#202124">Notes</text>` +
91
+ rows +
92
+ `</g>`;
93
+ return out.replace(/(<\/g>\s*)(<\/svg>)/, `$1${legend}$2`);
94
+ }
95
+ /** One legend row: "N. Element name — path", linked to the note. */
96
+ function row(el, note, num, x, y) {
97
+ const href = esc(note); // resolved link target
98
+ const label = esc(el.note ?? ""); // authored path, shown as the readable label
99
+ const name = esc(el.name);
100
+ return (`<a xlink:href="${href}" href="${href}" target="_blank">` +
101
+ `<text x="${n(x)}" y="${n(y)}" font-family="Helvetica" font-size="11" fill="#202124">` +
102
+ `<tspan font-weight="bold" fill="${STROKE}">${num}.</tspan>` +
103
+ `<tspan dx="6">${name}</tspan>` +
104
+ `<tspan dx="6" fill="#5F6368">— ${label}</tspan>` +
105
+ `</text></a>`);
106
+ }
107
+ function esc(s) {
108
+ return s
109
+ .replace(/&/g, "&amp;")
110
+ .replace(/</g, "&lt;")
111
+ .replace(/>/g, "&gt;")
112
+ .replace(/"/g, "&quot;");
113
+ }
114
+ function n(v) {
115
+ return Math.round(v * 100) / 100;
116
+ }
117
+ //# sourceMappingURL=drawNotes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawNotes.js","sourceRoot":"","sources":["../../src/render/drawNotes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { NormalizedModel } from "../model/model.js";
2
+ export declare function formatFromPath(outPath: string, fallback?: string): string;
3
+ export declare function renderDot(dot: string, model: NormalizedModel, outPath: string, format?: string, baseDir?: string): Promise<void>;
4
+ /**
5
+ * Resolve a note path (authored relative to the .em file) into an href relative
6
+ * to the output SVG's directory, so links stay valid wherever the SVG is
7
+ * written. URLs and absolute paths are passed through untouched.
8
+ */
9
+ export declare function noteHref(note: string, baseDir: string, outDir: string): string;
@@ -0,0 +1,106 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Renders a model to an image.
3
+ //
4
+ // Graphviz (bundled as WebAssembly) lays out the grid and renders boxes/labels
5
+ // to SVG; it does NOT route the arrows. We read each box's rectangle back out of
6
+ // that SVG, draw the edges ourselves (straight within a slice, curved across
7
+ // slices) plus the note markers, and inject them. SVG is written directly; PNG
8
+ // is rasterized in-process with resvg. PDF/other formats use an optional system
9
+ // `rsvg-convert` if one is installed. No system Graphviz is required.
10
+ import { spawn } from "node:child_process";
11
+ import { writeFile } from "node:fs/promises";
12
+ import { dirname, extname, isAbsolute, relative, resolve, sep } from "node:path";
13
+ import { Graphviz } from "@hpcc-js/wasm-graphviz";
14
+ import { Resvg } from "@resvg/resvg-js";
15
+ import { parseNodeRects } from "./svgGeometry.js";
16
+ import { buildEdgeOverlay } from "./drawEdges.js";
17
+ import { buildNoteMarkers, appendNoteLegend } from "./drawNotes.js";
18
+ const RSVG_BIN = process.env.EM_RSVG || "rsvg-convert";
19
+ // The WASM Graphviz module is loaded once and reused across renders.
20
+ let graphvizModule;
21
+ function graphviz() {
22
+ return (graphvizModule ??= Graphviz.load());
23
+ }
24
+ export function formatFromPath(outPath, fallback = "svg") {
25
+ const ext = extname(outPath).replace(/^\./, "").toLowerCase();
26
+ return ext || fallback;
27
+ }
28
+ export async function renderDot(dot, model, outPath, format = formatFromPath(outPath), baseDir = process.cwd()) {
29
+ // Note links are authored relative to the .em file (baseDir); rewrite them
30
+ // relative to the output SVG so they resolve wherever the SVG is written.
31
+ const outDir = dirname(outPath);
32
+ const hrefOf = (el) => noteHref(el.note ?? "", baseDir, outDir);
33
+ const gv = await graphviz();
34
+ const svg = withOverlays(gv.layout(dot, "svg", "dot"), model, hrefOf);
35
+ if (format === "svg") {
36
+ await writeFile(outPath, svg, "utf8");
37
+ return;
38
+ }
39
+ if (format === "png") {
40
+ const png = new Resvg(svg).render().asPng();
41
+ await writeFile(outPath, png);
42
+ return;
43
+ }
44
+ // Other formats (pdf, ps, …) aren't built in; use a system rsvg-convert if present.
45
+ if (await hasBin(RSVG_BIN)) {
46
+ await rsvgConvert(svg, outPath, format);
47
+ return;
48
+ }
49
+ throw new Error(`format '${format}' is not built in (svg and png are). Install librsvg ` +
50
+ `(provides '${RSVG_BIN}') to render '${format}', or use -o file.svg / -o file.png.`);
51
+ }
52
+ /**
53
+ * Resolve a note path (authored relative to the .em file) into an href relative
54
+ * to the output SVG's directory, so links stay valid wherever the SVG is
55
+ * written. URLs and absolute paths are passed through untouched.
56
+ */
57
+ export function noteHref(note, baseDir, outDir) {
58
+ if (!note)
59
+ return note;
60
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(note) || isAbsolute(note))
61
+ return note;
62
+ const rel = relative(outDir, resolve(baseDir, note)) || note;
63
+ return rel.split(sep).join("/"); // posix separators for URLs
64
+ }
65
+ /** Draw the semantic edges and note markers into a Graphviz-rendered SVG. */
66
+ function withOverlays(svg, model, hrefOf) {
67
+ const rects = parseNodeRects(svg, new Set(model.byId.keys()));
68
+ const { defs, group } = buildEdgeOverlay(model, rects);
69
+ const notes = buildNoteMarkers(model, rects, hrefOf);
70
+ let out = svg;
71
+ // arrowhead markers go just inside <svg …>
72
+ out = out.replace(/(<svg\b[^>]*>)/, `$1${defs}`);
73
+ // edges go under the boxes: just before the first node group
74
+ const nodeAt = out.search(/<g\b[^>]*class="node"[^>]*>/);
75
+ if (nodeAt >= 0)
76
+ out = out.slice(0, nodeAt) + group + out.slice(nodeAt);
77
+ else
78
+ out = out.replace(/<\/svg>/, `${group}</svg>`);
79
+ // note markers go on top of the boxes — inside the graph transform group
80
+ // (so they share the box coordinate space) but after every node, as the last
81
+ // child of that group, making them the topmost clickable layer.
82
+ out = out.replace(/(<\/g>\s*)(<\/svg>)/, `${notes}$1$2`);
83
+ // grow the canvas and append the legend below the diagram (root coords)
84
+ out = appendNoteLegend(out, model, hrefOf);
85
+ return out;
86
+ }
87
+ /** Convert an SVG string to the requested format with an optional system rsvg-convert. */
88
+ function rsvgConvert(svg, outPath, format) {
89
+ return new Promise((res, rej) => {
90
+ const child = spawn(RSVG_BIN, [`--format=${format}`, "-o", outPath]);
91
+ let err = "";
92
+ child.stderr.on("data", (d) => (err += d.toString()));
93
+ child.on("error", (e) => rej(new Error(`failed to run '${RSVG_BIN}': ${e.message}`)));
94
+ child.on("close", (code) => code === 0 ? res() : rej(new Error(`${RSVG_BIN} exited with code ${code}: ${err.trim()}`)));
95
+ child.stdin.write(svg);
96
+ child.stdin.end();
97
+ });
98
+ }
99
+ function hasBin(bin) {
100
+ return new Promise((res) => {
101
+ const child = spawn(bin, ["--version"]);
102
+ child.on("error", () => res(false));
103
+ child.on("close", (code) => res(code === 0));
104
+ });
105
+ }
106
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render/render.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,+BAA+B;AAC/B,EAAE;AACF,+EAA+E;AAC/E,iFAAiF;AACjF,6EAA6E;AAC7E,+EAA+E;AAC/E,gFAAgF;AAChF,sEAAsE;AAEtE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEpE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,cAAc,CAAC;AAEvD,qEAAqE;AACrE,IAAI,cAA6C,CAAC;AAClD,SAAS,QAAQ;IACf,OAAO,CAAC,cAAc,KAAK,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,QAAQ,GAAG,KAAK;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9D,OAAO,GAAG,IAAI,QAAQ,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,KAAsB,EACtB,OAAe,EACf,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,EAChC,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE;IAEvB,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,CAAC,EAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAEzE,MAAM,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAEtE,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACrB,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QAC5C,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,oFAAoF;IACpF,IAAI,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,MAAM,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,MAAM,IAAI,KAAK,CACb,WAAW,MAAM,uDAAuD;QACtE,cAAc,QAAQ,iBAAiB,MAAM,sCAAsC,CACtF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,OAAe,EAAE,MAAc;IACpE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3E,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC;IAC7D,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,4BAA4B;AAC/D,CAAC;AAED,6EAA6E;AAC7E,SAAS,YAAY,CACnB,GAAW,EACX,KAAsB,EACtB,MAA+B;IAE/B,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAErD,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,2CAA2C;IAC3C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IACjD,6DAA6D;IAC7D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,6BAA6B,CAAC,CAAC;IACzD,IAAI,MAAM,IAAI,CAAC;QAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;;QACnE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC;IACpD,yEAAyE;IACzE,6EAA6E;IAC7E,gEAAgE;IAChE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC;IACzD,wEAAwE;IACxE,GAAG,GAAG,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0FAA0F;AAC1F,SAAS,WAAW,CAAC,GAAW,EAAE,OAAe,EAAE,MAAc;IAC/D,OAAO,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,YAAY,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACrE,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACtD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,kBAAkB,QAAQ,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACtF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CACzB,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,QAAQ,qBAAqB,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAC3F,CAAC;QACF,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,MAAM,CAAC,GAAW;IACzB,OAAO,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;QACxC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACpC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface Rect {
2
+ left: number;
3
+ right: number;
4
+ top: number;
5
+ bottom: number;
6
+ cx: number;
7
+ cy: number;
8
+ }
9
+ /** Map each requested node id to its rectangle in SVG coordinates. */
10
+ export declare function parseNodeRects(svg: string, ids: Set<string>): Map<string, Rect>;