@milehimikey/em 1.4.0 → 1.6.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 (95) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +121 -30
  2. package/.claude/skills/event-modeling/reference/conform.md +36 -7
  3. package/.claude/skills/event-modeling/reference/em-dsl.md +210 -48
  4. package/.claude/skills/event-modeling/reference/extract.md +24 -7
  5. package/.claude/skills/event-modeling/reference/methodology.md +129 -32
  6. package/.claude/skills/event-modeling/templates/conformance-report.md +9 -7
  7. package/.claude/skills/event-modeling/templates/model-readme.md +9 -0
  8. package/.claude/skills/event-modeling/templates/slice.md +20 -1
  9. package/.claude/skills/event-modeling/templates/state.md +8 -0
  10. package/README.md +1 -0
  11. package/dist/catalog/build.d.ts +33 -0
  12. package/dist/catalog/build.js +117 -0
  13. package/dist/catalog/build.js.map +1 -0
  14. package/dist/catalog/classify.d.ts +14 -0
  15. package/dist/catalog/classify.js +44 -0
  16. package/dist/catalog/classify.js.map +1 -0
  17. package/dist/catalog/pages.d.ts +45 -0
  18. package/dist/catalog/pages.js +174 -0
  19. package/dist/catalog/pages.js.map +1 -0
  20. package/dist/catalog/sliceDoc.d.ts +10 -0
  21. package/dist/catalog/sliceDoc.js +20 -0
  22. package/dist/catalog/sliceDoc.js.map +1 -0
  23. package/dist/cli/catalog-inputs.d.ts +16 -0
  24. package/dist/cli/catalog-inputs.js +18 -0
  25. package/dist/cli/catalog-inputs.js.map +1 -0
  26. package/dist/cli/glossary-inputs.d.ts +15 -0
  27. package/dist/cli/glossary-inputs.js +17 -0
  28. package/dist/cli/glossary-inputs.js.map +1 -0
  29. package/dist/cli/render-inputs.d.ts +27 -0
  30. package/dist/cli/render-inputs.js +41 -0
  31. package/dist/cli/render-inputs.js.map +1 -0
  32. package/dist/cli.js +188 -10
  33. package/dist/cli.js.map +1 -1
  34. package/dist/emit/diffJson.d.ts +1 -1
  35. package/dist/emit/diffJson.js +8 -1
  36. package/dist/emit/diffJson.js.map +1 -1
  37. package/dist/emit/dot.d.ts +7 -0
  38. package/dist/emit/dot.js +10 -4
  39. package/dist/emit/dot.js.map +1 -1
  40. package/dist/emit/glossaryJson.d.ts +13 -0
  41. package/dist/emit/glossaryJson.js +36 -0
  42. package/dist/emit/glossaryJson.js.map +1 -0
  43. package/dist/emit/json.d.ts +16 -2
  44. package/dist/emit/json.js +57 -6
  45. package/dist/emit/json.js.map +1 -1
  46. package/dist/emit/theme.d.ts +3 -0
  47. package/dist/emit/theme.js +19 -0
  48. package/dist/emit/theme.js.map +1 -1
  49. package/dist/model/diff.d.ts +36 -1
  50. package/dist/model/diff.js +198 -35
  51. package/dist/model/diff.js.map +1 -1
  52. package/dist/model/edges.d.ts +12 -1
  53. package/dist/model/edges.js +26 -24
  54. package/dist/model/edges.js.map +1 -1
  55. package/dist/model/glossary.d.ts +68 -0
  56. package/dist/model/glossary.js +138 -0
  57. package/dist/model/glossary.js.map +1 -0
  58. package/dist/model/model.d.ts +35 -0
  59. package/dist/model/model.js +41 -0
  60. package/dist/model/model.js.map +1 -1
  61. package/dist/model/validate.js +252 -1
  62. package/dist/model/validate.js.map +1 -1
  63. package/dist/parser/ast.d.ts +20 -0
  64. package/dist/parser/parser.js +158 -29
  65. package/dist/parser/parser.js.map +1 -1
  66. package/dist/render/drawEdges.d.ts +3 -1
  67. package/dist/render/drawEdges.js +119 -7
  68. package/dist/render/drawEdges.js.map +1 -1
  69. package/dist/render/drawNotes.d.ts +7 -3
  70. package/dist/render/drawNotes.js +75 -28
  71. package/dist/render/drawNotes.js.map +1 -1
  72. package/dist/render/render.d.ts +20 -1
  73. package/dist/render/render.js +57 -17
  74. package/dist/render/render.js.map +1 -1
  75. package/dist/render/serve.js +155 -0
  76. package/dist/render/serve.js.map +1 -1
  77. package/dist/render/sliceDiagram.d.ts +11 -0
  78. package/dist/render/sliceDiagram.js +198 -0
  79. package/dist/render/sliceDiagram.js.map +1 -0
  80. package/dist/render/sliceOverlay.d.ts +21 -0
  81. package/dist/render/sliceOverlay.js +111 -0
  82. package/dist/render/sliceOverlay.js.map +1 -0
  83. package/dist/render/sliceStatus.d.ts +5 -0
  84. package/dist/render/sliceStatus.js +26 -0
  85. package/dist/render/sliceStatus.js.map +1 -0
  86. package/dist/render/statusOverlay.d.ts +16 -0
  87. package/dist/render/statusOverlay.js +110 -0
  88. package/dist/render/statusOverlay.js.map +1 -0
  89. package/dist/render/svgGeometry.d.ts +43 -0
  90. package/dist/render/svgGeometry.js +69 -3
  91. package/dist/render/svgGeometry.js.map +1 -1
  92. package/dist/render/watch.d.ts +1 -1
  93. package/dist/render/watch.js +5 -2
  94. package/dist/render/watch.js.map +1 -1
  95. package/package.json +3 -2
@@ -1,21 +1,28 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // Draws "has notes" and "has issues" affordances on top of a Graphviz-rendered SVG:
2
+ // Draws "has notes", "has issues", and "has an accepted divergence" affordances on top of
3
+ // a Graphviz-rendered SVG:
3
4
  // - a small folded-corner (dog-ear) marker per noted box, top-right, amber, carrying
4
5
  // a footnote number, linked to the note file;
5
6
  // - a small folded-corner marker per element with an open `issue`, top-left, red,
6
7
  // carrying its own footnote number — no link (there's nothing to open), just a
7
8
  // `<title>` tooltip, since the full text lives in the legend;
9
+ // - a small folded-corner marker per element with a `divergence`, bottom-right, teal,
10
+ // same tooltip-only treatment as `issue` (inline text, no file to link to) — the
11
+ // *resolved* counterpart to issue's red: right side reads "documented" (note,
12
+ // divergence), left reads "open" (issue);
8
13
  // - a legend appended below the diagram: a "Notes" section mapping each amber number
9
- // to its element and note file, and (when present) an "Issues" section mapping each
10
- // red number to its element and issue text.
11
- // An element may carry both a note and an issue at once: the two markers sit on
12
- // opposite top corners of the box, so they never overlap, and each keeps its own
13
- // independent numbering (a "1" note and a "1" issue on the same box are unambiguous —
14
- // distinct color, distinct corner, distinct legend section).
15
- // In SVG the note markers/rows are anchors (click to open the markdown); issue
16
- // markers/rows never are, since inline text has no file to link to. Raster output
17
- // (PNG/PDF) can't carry links or tooltips either way, so the legend is what tells you
18
- // which note or issue belongs to which element there.
14
+ // to its element and note file, an "Issues" section mapping each red number to its
15
+ // element and issue text, and an "Accepted Divergences" section mapping each teal
16
+ // number to its element and divergence text — each present only when non-empty.
17
+ // An element may carry a note, an issue, and a divergence all at once: note/issue sit on
18
+ // the top corners, divergence sits at bottom-right, so none of them overlap, and each
19
+ // keeps its own independent numbering (a "1" note, a "1" issue, and a "1" divergence on
20
+ // the same box are unambiguous — distinct color, distinct corner, distinct legend
21
+ // section).
22
+ // In SVG the note markers/rows are anchors (click to open the markdown); issue and
23
+ // divergence markers/rows never are, since inline text has no file to link to. Raster
24
+ // output (PNG/PDF) can't carry links or tooltips either way, so the legend is what tells
25
+ // you which note, issue, or divergence belongs to which element there.
19
26
  //
20
27
  // Marker coordinates come from parseNodeRects (the box coordinate space, inside
21
28
  // Graphviz's transform group). The legend is laid out in the SVG's root/viewBox
@@ -26,6 +33,8 @@ const FILL = "#F4C430"; // sticky-note amber (notes)
26
33
  const STROKE = "#7A5200"; // dark amber — reads on light and orange fills alike
27
34
  const ISSUE_FILL = "#E53935"; // red sticky (open issues)
28
35
  const ISSUE_STROKE = "#8B0000"; // dark red
36
+ const DIVERGENCE_FILL = "#26A69A"; // muted teal (accepted divergences — settled, not open)
37
+ const DIVERGENCE_STROKE = "#00695C"; // dark teal
29
38
  /** Elements carrying a note, in document order. Index + 1 is the footnote number. */
30
39
  export function notedElements(model) {
31
40
  return model.elements.filter((el) => el.note);
@@ -34,6 +43,10 @@ export function notedElements(model) {
34
43
  export function issuedElements(model) {
35
44
  return model.elements.filter((el) => el.issue);
36
45
  }
46
+ /** Elements carrying an accepted divergence, in document order. Index + 1 is the footnote number. */
47
+ export function divergedElements(model) {
48
+ return model.elements.filter((el) => el.divergence);
49
+ }
37
50
  const rawNote = (el) => el.note ?? "";
38
51
  /** SVG group with a numbered corner marker per noted element (inside the graph group). */
39
52
  export function buildNoteMarkers(model, rects, hrefOf = rawNote) {
@@ -57,15 +70,29 @@ export function buildIssueMarkers(model, rects) {
57
70
  });
58
71
  return `<g class="em-issues">${markers.join("")}</g>`;
59
72
  }
60
- /** The dog-ear glyph + footnote number, anchored at one top corner of the box. */
61
- function cornerGlyph(r, num, corner, fill, stroke) {
62
- const y = r.top + INSET; // y grows downward, so this nudges down from the top edge
73
+ /** SVG group with a numbered corner marker per element with an accepted divergence. */
74
+ export function buildDivergenceMarkers(model, rects) {
75
+ const markers = [];
76
+ divergedElements(model).forEach((el, i) => {
77
+ const r = rects.get(el.id);
78
+ if (!r)
79
+ return;
80
+ markers.push(divergenceMarker(r, el.divergence ?? "", i + 1));
81
+ });
82
+ return `<g class="em-divergences">${markers.join("")}</g>`;
83
+ }
84
+ /** The dog-ear glyph + footnote number, anchored at one corner of the box. */
85
+ function cornerGlyph(r, num, corner, vAlign, fill, stroke) {
63
86
  const x = corner === "right" ? r.right - INSET : r.left + INSET;
64
87
  const foldX = corner === "right" ? x - FOLD : x + FOLD;
65
- const tri = `M${n(foldX)},${n(y)} L${n(x)},${n(y)} L${n(x)},${n(y + FOLD)} Z`;
66
- const crease = `M${n(foldX)},${n(y)} L${n(x)},${n(y + FOLD)}`;
88
+ // y grows downward: a top marker nudges down from the top edge and folds further down;
89
+ // a bottom marker nudges up from the bottom edge and folds further up.
90
+ const y = vAlign === "top" ? r.top + INSET : r.bottom - INSET;
91
+ const foldY = vAlign === "top" ? y + FOLD : y - FOLD;
92
+ const tri = `M${n(foldX)},${n(y)} L${n(x)},${n(y)} L${n(x)},${n(foldY)} Z`;
93
+ const crease = `M${n(foldX)},${n(y)} L${n(x)},${n(foldY)}`;
67
94
  const numX = corner === "right" ? foldX - 2 : foldX + 2;
68
- const numY = y + 9;
95
+ const numY = vAlign === "top" ? y + 9 : y - 5;
69
96
  const anchor = corner === "right" ? "end" : "start";
70
97
  return (`<path d="${tri}" fill="${fill}" stroke="${stroke}" stroke-width="1" stroke-linejoin="round"/>` +
71
98
  `<path d="${crease}" fill="none" stroke="${stroke}" stroke-width="0.7"/>` +
@@ -77,7 +104,7 @@ function noteMarker(r, note, num) {
77
104
  const href = esc(note);
78
105
  return (`<a xlink:href="${href}" href="${href}" target="_blank">` +
79
106
  `<title>${num}. ${href}</title>` +
80
- cornerGlyph(r, num, "right", FILL, STROKE) +
107
+ cornerGlyph(r, num, "right", "top", FILL, STROKE) +
81
108
  `</a>`);
82
109
  }
83
110
  /**
@@ -87,7 +114,16 @@ function noteMarker(r, note, num) {
87
114
  * read the full issue text (tooltips don't survive rasterization either).
88
115
  */
89
116
  function issueMarker(r, issue, num) {
90
- return `<g><title>${num}. ${esc(issue)}</title>${cornerGlyph(r, num, "left", ISSUE_FILL, ISSUE_STROKE)}</g>`;
117
+ return `<g><title>${num}. ${esc(issue)}</title>${cornerGlyph(r, num, "left", "top", ISSUE_FILL, ISSUE_STROKE)}</g>`;
118
+ }
119
+ /**
120
+ * A folded-corner glyph + footnote number at the box's bottom-right, marking an
121
+ * accepted divergence — the resolved counterpart to `issueMarker`'s open question.
122
+ * Same tooltip-only treatment (no link target); the legend is the reliable read.
123
+ */
124
+ function divergenceMarker(r, divergence, num) {
125
+ return (`<g><title>${num}. ${esc(divergence)}</title>` +
126
+ `${cornerGlyph(r, num, "right", "bottom", DIVERGENCE_FILL, DIVERGENCE_STROKE)}</g>`);
91
127
  }
92
128
  // ---- legend ----
93
129
  const PAD_X = 16;
@@ -97,14 +133,15 @@ const LINE_H = 18;
97
133
  const PAD_BOTTOM = 14;
98
134
  /**
99
135
  * Grow the canvas and append a legend below the diagram: a "Notes" section (if any
100
- * element has a note) and an "Issues" section (if any element has an open issue).
101
- * Returns the SVG unchanged if there's nothing to show or the dimensions can't be
102
- * parsed.
136
+ * element has a note), an "Issues" section (if any element has an open issue), and an
137
+ * "Accepted Divergences" section (if any element has one). Returns the SVG unchanged
138
+ * if there's nothing to show or the dimensions can't be parsed.
103
139
  */
104
140
  export function appendNoteLegend(svg, model, hrefOf = rawNote) {
105
141
  const noted = notedElements(model);
106
142
  const issued = issuedElements(model);
107
- if (noted.length === 0 && issued.length === 0)
143
+ const diverged = divergedElements(model);
144
+ if (noted.length === 0 && issued.length === 0 && diverged.length === 0)
108
145
  return svg;
109
146
  const vb = /viewBox="([\d.eE+-]+)\s+([\d.eE+-]+)\s+([\d.eE+-]+)\s+([\d.eE+-]+)"/.exec(svg);
110
147
  const hpt = /height="([\d.eE+-]+)pt"/.exec(svg);
@@ -116,7 +153,8 @@ export function appendNoteLegend(svg, model, hrefOf = rawNote) {
116
153
  const vh = +vb[4];
117
154
  const notesH = noted.length > 0 ? HEAD_H + noted.length * LINE_H : 0;
118
155
  const issuesH = issued.length > 0 ? HEAD_H + issued.length * LINE_H : 0;
119
- const legendH = PAD_TOP + notesH + issuesH + PAD_BOTTOM;
156
+ const divergencesH = diverged.length > 0 ? HEAD_H + diverged.length * LINE_H : 0;
157
+ const legendH = PAD_TOP + notesH + issuesH + divergencesH + PAD_BOTTOM;
120
158
  const newVh = vh + legendH;
121
159
  const newHpt = (+hpt[1]) * (newVh / vh); // keep the pt:viewBox ratio (handles scaling)
122
160
  const top = minY + vh; // legend sits just below the diagram, in root coords
@@ -143,6 +181,15 @@ export function appendNoteLegend(svg, model, hrefOf = rawNote) {
143
181
  .join("");
144
182
  sectionY += issuesH;
145
183
  }
184
+ if (diverged.length > 0) {
185
+ sections +=
186
+ `<text x="${n(minX + PAD_X)}" y="${n(sectionY + 14)}" font-family="Helvetica" ` +
187
+ `font-weight="bold" font-size="12" fill="${DIVERGENCE_STROKE}">Accepted Divergences</text>`;
188
+ sections += diverged
189
+ .map((el, i) => row(el, el.divergence ?? "", i + 1, minX + PAD_X, sectionY + HEAD_H + i * LINE_H + 13, "divergence"))
190
+ .join("");
191
+ sectionY += divergencesH;
192
+ }
146
193
  const legend = `<g class="em-note-legend">` +
147
194
  `<rect x="${n(minX)}" y="${n(top)}" width="${n(vw)}" height="${n(legendH)}" fill="#FFFFFF"/>` +
148
195
  `<line x1="${n(minX)}" y1="${n(top)}" x2="${n(minX + vw)}" y2="${n(top)}" stroke="#D0D0D0" stroke-width="1"/>` +
@@ -152,18 +199,18 @@ export function appendNoteLegend(svg, model, hrefOf = rawNote) {
152
199
  }
153
200
  /**
154
201
  * One legend row: "N. Element name — text". Notes rows link to the note file;
155
- * issue rows carry the full issue text and have no link target.
202
+ * issue and divergence rows carry their full text and have no link target.
156
203
  */
157
204
  function row(el, hrefOrText, num, x, y, kind = "note") {
158
- const color = kind === "issue" ? ISSUE_STROKE : STROKE;
159
- const label = esc(kind === "issue" ? el.issue ?? "" : el.note ?? ""); // readable text
205
+ const color = kind === "issue" ? ISSUE_STROKE : kind === "divergence" ? DIVERGENCE_STROKE : STROKE;
206
+ const label = esc(kind === "issue" ? el.issue ?? "" : kind === "divergence" ? el.divergence ?? "" : el.note ?? ""); // readable text
160
207
  const name = esc(el.name);
161
208
  const text = `<text x="${n(x)}" y="${n(y)}" font-family="Helvetica" font-size="11" fill="#202124">` +
162
209
  `<tspan font-weight="bold" fill="${color}">${num}.</tspan>` +
163
210
  `<tspan dx="6">${name}</tspan>` +
164
211
  `<tspan dx="6" fill="#5F6368">— ${label}</tspan>` +
165
212
  `</text>`;
166
- if (kind === "issue")
213
+ if (kind === "issue" || kind === "divergence")
167
214
  return text; // nothing to link to
168
215
  const href = esc(hrefOrText); // resolved link target
169
216
  return `<a xlink:href="${href}" href="${href}" target="_blank">${text}</a>`;
@@ -1 +1 @@
1
- {"version":3,"file":"drawNotes.js","sourceRoot":"","sources":["../../src/render/drawNotes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"drawNotes.js","sourceRoot":"","sources":["../../src/render/drawNotes.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,25 @@
1
1
  import { NormalizedModel } from "../model/model.js";
2
+ import { Grid } from "../layout/grid.js";
2
3
  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
+ /** Run the Graphviz WASM layout pass only — the part shared by every caller that
5
+ * needs to compose the same layout for more than one output location (a full
6
+ * diagram and a slice diagram of it, note hrefs recomputed for each — see
7
+ * buildCatalog and `em render --slice`), so it isn't paid for twice. */
8
+ export declare function layoutDot(dot: string): Promise<string>;
9
+ /**
10
+ * Compose a laid-out SVG with edges, notes, slice tags/metadata, and the legend —
11
+ * the pure/string half of rendering, split out from layoutDot's WASM call. `outDir`
12
+ * is where the composed SVG will ultimately be written; note links are authored
13
+ * relative to `baseDir` (the .em file) and get rewritten relative to `outDir` here,
14
+ * so a caller composing the *same* layout for two different output locations (e.g.
15
+ * a full diagram and a slice diagram one directory deeper) must call this twice,
16
+ * once per real outDir, rather than reuse one composed string for both.
17
+ */
18
+ export declare function composeSvg(rawSvg: string, model: NormalizedModel, grid: Grid, baseDir: string, outDir: string): string;
19
+ /** Serialize a composed SVG string to `outPath` in the requested format — split out
20
+ * so a caller can transform the composed string in between composing and writing it. */
21
+ export declare function writeRendered(svg: string, outPath: string, format?: string): Promise<void>;
22
+ export declare function renderDot(dot: string, model: NormalizedModel, grid: Grid, outPath: string, format?: string, baseDir?: string): Promise<void>;
4
23
  /**
5
24
  * Resolve a note path (authored relative to the .em file) into an href relative
6
25
  * to the output SVG's directory, so links stay valid wherever the SVG is
@@ -12,9 +12,12 @@ import { writeFile } from "node:fs/promises";
12
12
  import { dirname, extname, isAbsolute, relative, resolve, sep } from "node:path";
13
13
  import { Graphviz } from "@hpcc-js/wasm-graphviz";
14
14
  import { Resvg } from "@resvg/resvg-js";
15
- import { parseNodeRects } from "./svgGeometry.js";
15
+ import { fitCanvas, parseNodeRects } from "./svgGeometry.js";
16
16
  import { buildEdgeOverlay } from "./drawEdges.js";
17
- import { buildNoteMarkers, buildIssueMarkers, appendNoteLegend } from "./drawNotes.js";
17
+ import { buildNoteMarkers, buildIssueMarkers, buildDivergenceMarkers, appendNoteLegend } from "./drawNotes.js";
18
+ import { sliceOverlayIds, tagSliceAttrs, buildSliceOverlay } from "./sliceOverlay.js";
19
+ import { readSliceStatuses } from "./sliceStatus.js";
20
+ import { applyStatusColors, appendStatusLegend } from "./statusOverlay.js";
18
21
  const RSVG_BIN = process.env.EM_RSVG || "rsvg-convert";
19
22
  // The WASM Graphviz module is loaded once and reused across renders.
20
23
  let graphvizModule;
@@ -25,13 +28,31 @@ export function formatFromPath(outPath, fallback = "svg") {
25
28
  const ext = extname(outPath).replace(/^\./, "").toLowerCase();
26
29
  return ext || fallback;
27
30
  }
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);
31
+ /** Run the Graphviz WASM layout pass only — the part shared by every caller that
32
+ * needs to compose the same layout for more than one output location (a full
33
+ * diagram and a slice diagram of it, note hrefs recomputed for each — see
34
+ * buildCatalog and `em render --slice`), so it isn't paid for twice. */
35
+ export async function layoutDot(dot) {
33
36
  const gv = await graphviz();
34
- const svg = withOverlays(gv.layout(dot, "svg", "dot"), model, hrefOf);
37
+ return gv.layout(dot, "svg", "dot");
38
+ }
39
+ /**
40
+ * Compose a laid-out SVG with edges, notes, slice tags/metadata, and the legend —
41
+ * the pure/string half of rendering, split out from layoutDot's WASM call. `outDir`
42
+ * is where the composed SVG will ultimately be written; note links are authored
43
+ * relative to `baseDir` (the .em file) and get rewritten relative to `outDir` here,
44
+ * so a caller composing the *same* layout for two different output locations (e.g.
45
+ * a full diagram and a slice diagram one directory deeper) must call this twice,
46
+ * once per real outDir, rather than reuse one composed string for both.
47
+ */
48
+ export function composeSvg(rawSvg, model, grid, baseDir, outDir) {
49
+ const hrefOf = (el) => noteHref(el.note ?? "", baseDir, outDir);
50
+ const statuses = readSliceStatuses(model, baseDir);
51
+ return withOverlays(rawSvg, model, grid, hrefOf, statuses);
52
+ }
53
+ /** Serialize a composed SVG string to `outPath` in the requested format — split out
54
+ * so a caller can transform the composed string in between composing and writing it. */
55
+ export async function writeRendered(svg, outPath, format = formatFromPath(outPath)) {
35
56
  if (format === "svg") {
36
57
  await writeFile(outPath, svg, "utf8");
37
58
  return;
@@ -49,6 +70,12 @@ export async function renderDot(dot, model, outPath, format = formatFromPath(out
49
70
  throw new Error(`format '${format}' is not built in (svg and png are). Install librsvg ` +
50
71
  `(provides '${RSVG_BIN}') to render '${format}', or use -o file.svg / -o file.png.`);
51
72
  }
73
+ export async function renderDot(dot, model, grid, outPath, format = formatFromPath(outPath), baseDir = process.cwd()) {
74
+ const outDir = dirname(outPath);
75
+ const raw = await layoutDot(dot);
76
+ const svg = composeSvg(raw, model, grid, baseDir, outDir);
77
+ await writeRendered(svg, outPath, format);
78
+ }
52
79
  /**
53
80
  * Resolve a note path (authored relative to the .em file) into an href relative
54
81
  * to the output SVG's directory, so links stay valid wherever the SVG is
@@ -63,26 +90,39 @@ export function noteHref(note, baseDir, outDir) {
63
90
  return rel.split(sep).join("/"); // posix separators for URLs
64
91
  }
65
92
  /** 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);
93
+ function withOverlays(svg, model, grid, hrefOf, statuses) {
94
+ const rects = parseNodeRects(svg, new Set([...model.byId.keys(), ...sliceOverlayIds(grid)]));
95
+ const { defs, group, bbox } = buildEdgeOverlay(model, rects);
69
96
  const notes = buildNoteMarkers(model, rects, hrefOf);
70
97
  const issues = buildIssueMarkers(model, rects);
71
- let out = svg;
72
- // arrowhead markers go just inside <svg …>
73
- out = out.replace(/(<svg\b[^>]*>)/, `$1${defs}`);
98
+ const divergences = buildDivergenceMarkers(model, rects);
99
+ // an edge detour can run outside the box grid Graphviz sized the canvas for, so make
100
+ // room before anything else measures or appends to the viewBox
101
+ let out = fitCanvas(svg, bbox);
102
+ // recolor each slice's header cell per its doc status (src/render/sliceStatus.ts) —
103
+ // a scoped string replace keyed on each header's own <title> id, independent of
104
+ // tagSliceAttrs/buildSliceOverlay below, so the ordering here isn't load-bearing.
105
+ out = applyStatusColors(out, grid, statuses);
106
+ // tag each element's node group with data-slice="<index>" so the storyboard
107
+ // viewer (`em watch --serve`) can highlight/zoom to one slice at a time
108
+ // client-side, purely from what's already in the SVG.
109
+ out = tagSliceAttrs(out, model);
110
+ const sliceOverlay = buildSliceOverlay(out, grid, rects);
111
+ // arrowhead markers + slice metadata/style go just inside <svg …>
112
+ out = out.replace(/(<svg\b[^>]*>)/, `$1${defs}${sliceOverlay}`);
74
113
  // edges go under the boxes: just before the first node group
75
114
  const nodeAt = out.search(/<g\b[^>]*class="node"[^>]*>/);
76
115
  if (nodeAt >= 0)
77
116
  out = out.slice(0, nodeAt) + group + out.slice(nodeAt);
78
117
  else
79
118
  out = out.replace(/<\/svg>/, `${group}</svg>`);
80
- // note/issue markers go on top of the boxes — inside the graph transform group
81
- // (so they share the box coordinate space) but after every node, as the last
119
+ // note/issue/divergence markers go on top of the boxes — inside the graph transform
120
+ // group (so they share the box coordinate space) but after every node, as the last
82
121
  // children of that group, making them the topmost clickable layer.
83
- out = out.replace(/(<\/g>\s*)(<\/svg>)/, `${notes}${issues}$1$2`);
122
+ out = out.replace(/(<\/g>\s*)(<\/svg>)/, `${notes}${issues}${divergences}$1$2`);
84
123
  // grow the canvas and append the legend below the diagram (root coords)
85
124
  out = appendNoteLegend(out, model, hrefOf);
125
+ out = appendStatusLegend(out, grid, statuses);
86
126
  return out;
87
127
  }
88
128
  /** Convert an SVG string to the requested format with an optional system rsvg-convert. */
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render/render.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render/render.ts"],"names":[],"mappings":"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"}
@@ -135,6 +135,14 @@ async function serveStatic(pathname, root, res) {
135
135
  // Served viewer page. Same double-buffered, no-flicker swap as the static
136
136
  // live.html fallback, but driven by SSE push instead of a poll loop — the
137
137
  // browser reloads only when the server says the SVG actually changed.
138
+ //
139
+ // Also carries stakeholder review mode: a Prev/Next storyboard that highlights
140
+ // and pans/zooms to one slice at a time, reading the `data-slice`
141
+ // attributes/`<metadata id="em-slices">` that src/render/sliceOverlay.ts
142
+ // embeds in every rendered SVG. Review-mode state (`reviewMode`/`activeSlice`)
143
+ // deliberately lives outside reload()'s closure and is re-applied after every
144
+ // SSE-triggered swap — a facilitator hand-editing an `issue "..."` clause live
145
+ // during a session should see it appear without losing their place.
138
146
  const VIEWER_HTML = `<!doctype html>
139
147
  <html lang="en">
140
148
  <head>
@@ -150,6 +158,26 @@ const VIEWER_HTML = `<!doctype html>
150
158
  header .dot { width: 9px; height: 9px; border-radius: 50%; background: #34c759; transition: background .3s; }
151
159
  header .dot.stale { background: #f0a020; }
152
160
  header .stamp { margin-left: auto; opacity: .7; font-variant-numeric: tabular-nums; }
161
+ .btn {
162
+ background: #3b4754; color: #fff; border: 0; border-radius: 4px;
163
+ padding: .3rem .7rem; font-size: 13px; cursor: pointer; font-family: inherit;
164
+ }
165
+ .btn:hover { background: #4b5763; }
166
+ .btn:disabled { opacity: .4; cursor: default; }
167
+ .btn.active { background: #2f6fed; }
168
+ #storyboard {
169
+ display: flex; align-items: center; gap: .5rem; padding: .4rem .9rem;
170
+ background: #e8eaed; border-bottom: 1px solid #d0d3d7;
171
+ }
172
+ #storyboard[hidden] { display: none; }
173
+ #storyboard .nav { background: none; border: 0; font-size: 18px; line-height: 1; cursor: pointer; color: #3b4754; padding: 0 .3rem; }
174
+ #storyboard .nav:disabled { opacity: .3; cursor: default; }
175
+ #filmstrip { display: flex; gap: .35rem; overflow-x: auto; }
176
+ #filmstrip .slice {
177
+ padding: .25rem .6rem; border-radius: 12px; background: #fff; border: 1px solid #d0d3d7;
178
+ font-size: 12px; white-space: nowrap; cursor: pointer; color: #3b4754; font-family: inherit;
179
+ }
180
+ #filmstrip .slice.active { background: #2f6fed; border-color: #2f6fed; color: #fff; font-weight: 600; }
153
181
  #stage { padding: 1rem; }
154
182
  object { width: 100%; height: calc(100vh - 60px); border: 0; }
155
183
  </style>
@@ -158,8 +186,14 @@ const VIEWER_HTML = `<!doctype html>
158
186
  <header>
159
187
  <span class="dot" id="dot"></span>
160
188
  <span>Event Model — live (push)</span>
189
+ <button class="btn" id="reviewToggle" title="Step through one slice at a time">Review mode</button>
161
190
  <span class="stamp" id="stamp">connecting…</span>
162
191
  </header>
192
+ <nav id="storyboard" hidden>
193
+ <button class="nav" id="prevSlice" aria-label="Previous slice">‹</button>
194
+ <div id="filmstrip"></div>
195
+ <button class="nav" id="nextSlice" aria-label="Next slice">›</button>
196
+ </nav>
163
197
  <div id="stage">
164
198
  <object id="svg" type="image/svg+xml"></object>
165
199
  </div>
@@ -170,8 +204,21 @@ const VIEWER_HTML = `<!doctype html>
170
204
  const stage = document.getElementById("stage");
171
205
  const stamp = document.getElementById("stamp");
172
206
  const dot = document.getElementById("dot");
207
+ const reviewToggle = document.getElementById("reviewToggle");
208
+ const storyboard = document.getElementById("storyboard");
209
+ const filmstrip = document.getElementById("filmstrip");
210
+ const prevBtn = document.getElementById("prevSlice");
211
+ const nextBtn = document.getElementById("nextSlice");
173
212
  let current = document.getElementById("svg");
174
213
 
214
+ // --- storyboard state: outside reload()'s closure on purpose, see the
215
+ // file-level comment above — an SSE reload must not reset these. ---
216
+ let reviewMode = false;
217
+ let activeSlice = null; // slice index, or null for "show all"
218
+ let slices = []; // [{index, name, x0, x1}], from this load's <metadata>
219
+ let rowLabels = null; // {x0, x1} | null, the swimlane label column's range
220
+ let naturalViewBox = null; // this load's pre-zoom viewBox, cached once per load
221
+
175
222
  // Double-buffer: load into a hidden <object>, swap on load, so the shared
176
223
  // screen never flashes white during a reload. Keep <object> (not <img>) so
177
224
  // note "..." links inside the SVG stay clickable.
@@ -185,10 +232,118 @@ const VIEWER_HTML = `<!doctype html>
185
232
  current = next;
186
233
  stamp.textContent = new Date().toLocaleTimeString();
187
234
  dot.classList.remove("stale");
235
+ onSvgLoaded();
188
236
  }, { once: true });
189
237
  next.setAttribute("data", SVG_FILE + "?t=" + Date.now());
190
238
  }
191
239
 
240
+ function readSliceMeta(doc) {
241
+ const el = doc.getElementById("em-slices");
242
+ if (!el) return null;
243
+ try { return JSON.parse(el.textContent); } catch { return null; }
244
+ }
245
+
246
+ // The currently-loaded SVG's own document + root element, or null before
247
+ // the first load / if the served file isn't a well-formed SVG.
248
+ function currentSvgDoc() {
249
+ const doc = current.contentDocument;
250
+ const svgRoot = doc && doc.querySelector("svg");
251
+ return doc && svgRoot ? { doc, svgRoot } : null;
252
+ }
253
+
254
+ // Re-read this load's slice metadata + natural viewBox, then reapply
255
+ // whatever storyboard state was already in effect (a fresh SSE push mid-
256
+ // review shouldn't reset the facilitator's position).
257
+ function onSvgLoaded() {
258
+ const loaded = currentSvgDoc();
259
+ if (!loaded) return;
260
+
261
+ naturalViewBox = loaded.svgRoot.getAttribute("viewBox");
262
+ const meta = readSliceMeta(loaded.doc);
263
+ slices = (meta && meta.slices) || [];
264
+ rowLabels = (meta && meta.rowLabels) || null;
265
+ // The model may have changed shape (slice added/removed) between loads.
266
+ if (activeSlice !== null && !slices.some((s) => s.index === activeSlice)) {
267
+ activeSlice = slices.length ? slices[0].index : null;
268
+ }
269
+ render();
270
+ }
271
+
272
+ // Apply reviewMode/activeSlice to the currently-loaded SVG + the control bar.
273
+ // Safe to call directly from UI handlers (Prev/Next/toggle/arrow keys) — it
274
+ // never re-reads metadata, just re-applies the cached state.
275
+ function render() {
276
+ storyboard.hidden = slices.length === 0;
277
+ reviewToggle.classList.toggle("active", reviewMode);
278
+ reviewToggle.disabled = slices.length === 0;
279
+ renderFilmstrip();
280
+
281
+ const loaded = currentSvgDoc();
282
+ if (!loaded) return;
283
+ const { doc, svgRoot } = loaded;
284
+
285
+ doc.querySelectorAll("[data-slice]").forEach((node) => {
286
+ const dim = reviewMode && activeSlice !== null &&
287
+ Number(node.getAttribute("data-slice")) !== activeSlice;
288
+ node.classList.toggle("em-slice-dim", dim);
289
+ });
290
+
291
+ if (!naturalViewBox) return;
292
+ if (!reviewMode || activeSlice === null) {
293
+ svgRoot.setAttribute("viewBox", naturalViewBox);
294
+ return;
295
+ }
296
+ const slice = slices.find((s) => s.index === activeSlice);
297
+ if (!slice) return;
298
+ const [nx, ny, nw, nh] = naturalViewBox.split(/\\s+/).map(Number);
299
+ const PAD = 24; // breathing room around the framed slice
300
+ let x0 = rowLabels ? Math.min(rowLabels.x0, slice.x0) : slice.x0;
301
+ let x1 = slice.x1;
302
+ x0 = Math.max(nx, x0 - PAD);
303
+ x1 = Math.min(nx + nw, x1 + PAD);
304
+ svgRoot.setAttribute("viewBox", x0 + " " + ny + " " + Math.max(1, x1 - x0) + " " + nh);
305
+ }
306
+
307
+ function renderFilmstrip() {
308
+ filmstrip.innerHTML = "";
309
+ for (const s of slices) {
310
+ const b = document.createElement("button");
311
+ b.className = "slice" + (s.index === activeSlice ? " active" : "");
312
+ b.textContent = s.name;
313
+ b.addEventListener("click", () => goTo(s.index));
314
+ filmstrip.appendChild(b);
315
+ }
316
+ const first = slices[0], last = slices[slices.length - 1];
317
+ prevBtn.disabled = activeSlice === null || !first || activeSlice <= first.index;
318
+ nextBtn.disabled = activeSlice === null || !last || activeSlice >= last.index;
319
+ }
320
+
321
+ function goTo(index) {
322
+ activeSlice = index;
323
+ render();
324
+ }
325
+
326
+ function step(delta) {
327
+ if (!slices.length) return;
328
+ if (activeSlice === null) { goTo(slices[0].index); return; }
329
+ const i = slices.findIndex((s) => s.index === activeSlice);
330
+ const next = slices[Math.min(slices.length - 1, Math.max(0, i + delta))];
331
+ if (next) goTo(next.index);
332
+ }
333
+
334
+ reviewToggle.addEventListener("click", () => {
335
+ reviewMode = !reviewMode;
336
+ if (reviewMode && activeSlice === null && slices.length) activeSlice = slices[0].index;
337
+ render();
338
+ });
339
+ prevBtn.addEventListener("click", () => step(-1));
340
+ nextBtn.addEventListener("click", () => step(1));
341
+ document.addEventListener("keydown", (e) => {
342
+ if (!reviewMode) return;
343
+ if (e.key === "ArrowLeft") step(-1);
344
+ else if (e.key === "ArrowRight") step(1);
345
+ });
346
+
192
347
  reload();
193
348
 
194
349
  const es = new EventSource("/__events");