@milehimikey/em 1.5.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 (91) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +62 -17
  2. package/.claude/skills/event-modeling/reference/conform.md +33 -6
  3. package/.claude/skills/event-modeling/reference/em-dsl.md +138 -43
  4. package/.claude/skills/event-modeling/reference/extract.md +12 -2
  5. package/.claude/skills/event-modeling/reference/methodology.md +29 -14
  6. package/.claude/skills/event-modeling/templates/conformance-report.md +9 -7
  7. package/.claude/skills/event-modeling/templates/slice.md +6 -1
  8. package/.claude/skills/event-modeling/templates/state.md +8 -0
  9. package/README.md +1 -0
  10. package/dist/catalog/build.d.ts +33 -0
  11. package/dist/catalog/build.js +117 -0
  12. package/dist/catalog/build.js.map +1 -0
  13. package/dist/catalog/classify.d.ts +14 -0
  14. package/dist/catalog/classify.js +44 -0
  15. package/dist/catalog/classify.js.map +1 -0
  16. package/dist/catalog/pages.d.ts +45 -0
  17. package/dist/catalog/pages.js +174 -0
  18. package/dist/catalog/pages.js.map +1 -0
  19. package/dist/catalog/sliceDoc.d.ts +10 -0
  20. package/dist/catalog/sliceDoc.js +20 -0
  21. package/dist/catalog/sliceDoc.js.map +1 -0
  22. package/dist/cli/catalog-inputs.d.ts +16 -0
  23. package/dist/cli/catalog-inputs.js +18 -0
  24. package/dist/cli/catalog-inputs.js.map +1 -0
  25. package/dist/cli/glossary-inputs.d.ts +15 -0
  26. package/dist/cli/glossary-inputs.js +17 -0
  27. package/dist/cli/glossary-inputs.js.map +1 -0
  28. package/dist/cli/render-inputs.d.ts +27 -0
  29. package/dist/cli/render-inputs.js +41 -0
  30. package/dist/cli/render-inputs.js.map +1 -0
  31. package/dist/cli.js +188 -10
  32. package/dist/cli.js.map +1 -1
  33. package/dist/emit/diffJson.d.ts +1 -1
  34. package/dist/emit/diffJson.js +8 -1
  35. package/dist/emit/diffJson.js.map +1 -1
  36. package/dist/emit/dot.d.ts +7 -0
  37. package/dist/emit/dot.js +10 -4
  38. package/dist/emit/dot.js.map +1 -1
  39. package/dist/emit/glossaryJson.d.ts +13 -0
  40. package/dist/emit/glossaryJson.js +36 -0
  41. package/dist/emit/glossaryJson.js.map +1 -0
  42. package/dist/emit/json.d.ts +16 -2
  43. package/dist/emit/json.js +57 -6
  44. package/dist/emit/json.js.map +1 -1
  45. package/dist/emit/theme.d.ts +3 -0
  46. package/dist/emit/theme.js +19 -0
  47. package/dist/emit/theme.js.map +1 -1
  48. package/dist/model/diff.d.ts +36 -1
  49. package/dist/model/diff.js +198 -35
  50. package/dist/model/diff.js.map +1 -1
  51. package/dist/model/edges.d.ts +12 -1
  52. package/dist/model/edges.js +19 -8
  53. package/dist/model/edges.js.map +1 -1
  54. package/dist/model/glossary.d.ts +68 -0
  55. package/dist/model/glossary.js +138 -0
  56. package/dist/model/glossary.js.map +1 -0
  57. package/dist/model/model.d.ts +35 -0
  58. package/dist/model/model.js +41 -0
  59. package/dist/model/model.js.map +1 -1
  60. package/dist/model/validate.js +98 -1
  61. package/dist/model/validate.js.map +1 -1
  62. package/dist/parser/ast.d.ts +20 -0
  63. package/dist/parser/parser.js +158 -29
  64. package/dist/parser/parser.js.map +1 -1
  65. package/dist/render/drawNotes.d.ts +7 -3
  66. package/dist/render/drawNotes.js +75 -28
  67. package/dist/render/drawNotes.js.map +1 -1
  68. package/dist/render/render.d.ts +20 -1
  69. package/dist/render/render.js +52 -14
  70. package/dist/render/render.js.map +1 -1
  71. package/dist/render/serve.js +155 -0
  72. package/dist/render/serve.js.map +1 -1
  73. package/dist/render/sliceDiagram.d.ts +11 -0
  74. package/dist/render/sliceDiagram.js +198 -0
  75. package/dist/render/sliceDiagram.js.map +1 -0
  76. package/dist/render/sliceOverlay.d.ts +21 -0
  77. package/dist/render/sliceOverlay.js +111 -0
  78. package/dist/render/sliceOverlay.js.map +1 -0
  79. package/dist/render/sliceStatus.d.ts +5 -0
  80. package/dist/render/sliceStatus.js +26 -0
  81. package/dist/render/sliceStatus.js.map +1 -0
  82. package/dist/render/statusOverlay.d.ts +16 -0
  83. package/dist/render/statusOverlay.js +110 -0
  84. package/dist/render/statusOverlay.js.map +1 -0
  85. package/dist/render/svgGeometry.d.ts +30 -0
  86. package/dist/render/svgGeometry.js +33 -14
  87. package/dist/render/svgGeometry.js.map +1 -1
  88. package/dist/render/watch.d.ts +1 -1
  89. package/dist/render/watch.js +5 -2
  90. package/dist/render/watch.js.map +1 -1
  91. package/package.json +3 -2
@@ -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
@@ -14,7 +14,10 @@ import { Graphviz } from "@hpcc-js/wasm-graphviz";
14
14
  import { Resvg } from "@resvg/resvg-js";
15
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,28 +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()));
93
+ function withOverlays(svg, model, grid, hrefOf, statuses) {
94
+ const rects = parseNodeRects(svg, new Set([...model.byId.keys(), ...sliceOverlayIds(grid)]));
68
95
  const { defs, group, bbox } = buildEdgeOverlay(model, rects);
69
96
  const notes = buildNoteMarkers(model, rects, hrefOf);
70
97
  const issues = buildIssueMarkers(model, rects);
98
+ const divergences = buildDivergenceMarkers(model, rects);
71
99
  // an edge detour can run outside the box grid Graphviz sized the canvas for, so make
72
100
  // room before anything else measures or appends to the viewBox
73
101
  let out = fitCanvas(svg, bbox);
74
- // arrowhead markers go just inside <svg …>
75
- out = out.replace(/(<svg\b[^>]*>)/, `$1${defs}`);
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}`);
76
113
  // edges go under the boxes: just before the first node group
77
114
  const nodeAt = out.search(/<g\b[^>]*class="node"[^>]*>/);
78
115
  if (nodeAt >= 0)
79
116
  out = out.slice(0, nodeAt) + group + out.slice(nodeAt);
80
117
  else
81
118
  out = out.replace(/<\/svg>/, `${group}</svg>`);
82
- // note/issue markers go on top of the boxes — inside the graph transform group
83
- // (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
84
121
  // children of that group, making them the topmost clickable layer.
85
- out = out.replace(/(<\/g>\s*)(<\/svg>)/, `${notes}${issues}$1$2`);
122
+ out = out.replace(/(<\/g>\s*)(<\/svg>)/, `${notes}${issues}${divergences}$1$2`);
86
123
  // grow the canvas and append the legend below the diagram (root coords)
87
124
  out = appendNoteLegend(out, model, hrefOf);
125
+ out = appendStatusLegend(out, grid, statuses);
88
126
  return out;
89
127
  }
90
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");
@@ -1 +1 @@
1
- {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/render/serve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/render/serve.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
+ export interface SliceDiagramResult {
4
+ model: NormalizedModel;
5
+ grid: Grid;
6
+ dot: string;
7
+ }
8
+ /** Build a small, standalone diagram for one slice — real Graphviz layout, not a crop.
9
+ * `opts` is forwarded to layout() unchanged, so `--keep-empty-lanes` behaves the same
10
+ * way here as it does for the full model instead of being silently dropped. */
11
+ export declare function buildSliceDiagram(model: NormalizedModel, sliceIndex: number, opts?: LayoutOptions): SliceDiagramResult;
@@ -0,0 +1,198 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Renders a single slice's own diagram, matching the canonical shape of
3
+ // whichever of the 4 Event Modeling patterns it is (State Change, State View,
4
+ // Automation, Translation) — instead of cropping the full multi-slice diagram
5
+ // down to one column (which forces a narrow crop to still show a wide swimlane
6
+ // label, and can pull in most of a neighboring slice; see git history).
7
+ //
8
+ // The key trick: every piece of "immediate context" a slice needs to draw its
9
+ // own shape is already one hop away in the compiled model — a view's upstream
10
+ // event(s) resolve via `from` (resolveFromSource, shared with semanticEdges),
11
+ // and an automation/translation's watched view is just whatever `view` sits in
12
+ // the same slice. So this builds a tiny *synthetic* NormalizedModel — reusing
13
+ // the real, already-normalized Element objects (same ids, same fields, same
14
+ // notes), shallow-cloned only to reassign which of a handful of new columns
15
+ // they land in — and runs it through the exact same layout()/emitDot() the
16
+ // full model uses, unmodified. Edge-drawing comes free too: semanticEdges()
17
+ // resolves `from` via byName, so as long as the synthetic model's byName map
18
+ // is populated, the arrows get drawn by the existing inference with zero new
19
+ // edge-drawing code.
20
+ //
21
+ // This never chases more than one hop away (previous slice for a triggerless
22
+ // State Change; a view's own `from` sources for State View; the next slice for
23
+ // Automation/Translation — semanticEdges itself never looks further than i+1),
24
+ // so a slice diagram can never balloon into showing most of the model, the way
25
+ // a naive crop or a naive "just extract this slice and recompile" both could.
26
+ import { AUTOMATION_KINDS } from "../parser/ast.js";
27
+ import { normalizeName } from "../model/model.js";
28
+ import { resolveFromSource } from "../model/edges.js";
29
+ import { layout } from "../layout/grid.js";
30
+ import { emitDot } from "../emit/dot.js";
31
+ import { classifySlicePattern } from "../catalog/classify.js";
32
+ /** Build a small, standalone diagram for one slice — real Graphviz layout, not a crop.
33
+ * `opts` is forwarded to layout() unchanged, so `--keep-empty-lanes` behaves the same
34
+ * way here as it does for the full model instead of being silently dropped. */
35
+ export function buildSliceDiagram(model, sliceIndex, opts = {}) {
36
+ const slice = model.slices[sliceIndex];
37
+ const pattern = classifySlicePattern(slice);
38
+ const columns = mergeColumns(collectColumns(model, sliceIndex, pattern));
39
+ const synthetic = assembleSyntheticModel(model, columns, slice.name);
40
+ const grid = layout(synthetic, opts);
41
+ const dot = emitDot(synthetic, grid);
42
+ return { model: synthetic, grid, dot };
43
+ }
44
+ function collectColumns(model, sliceIndex, pattern) {
45
+ switch (pattern) {
46
+ case "state-change":
47
+ return columnsForStateChange(model, sliceIndex);
48
+ case "state-view":
49
+ return columnsForStateView(model, sliceIndex);
50
+ case "automation":
51
+ case "translation":
52
+ return columnsForReaction(model, sliceIndex);
53
+ default: // "unclassified"
54
+ return [{ sourceSliceIndex: sliceIndex, elements: model.slices[sliceIndex].elements }];
55
+ }
56
+ }
57
+ /** State Change: UI -> Command -> Event, all in this slice. If there's no local
58
+ * UI, the command's real trigger is the previous slice's automation/translation
59
+ * reaction — pull in just that one element as earlier context. */
60
+ function columnsForStateChange(model, i) {
61
+ const slice = model.slices[i];
62
+ const hasUi = slice.elements.some((e) => e.kind === "ui");
63
+ const cols = [{ sourceSliceIndex: i, elements: slice.elements }];
64
+ if (!hasUi) {
65
+ const prev = model.slices[i - 1];
66
+ const trigger = prev?.elements.find((e) => AUTOMATION_KINDS.has(e.kind));
67
+ if (trigger)
68
+ cols.push({ sourceSliceIndex: i - 1, elements: [trigger] });
69
+ }
70
+ return cols;
71
+ }
72
+ /** State View: Event(s) -> View -> UI. Each view's `from` names resolve to their
73
+ * real source Elements (same rule semanticEdges uses) — one column per distinct
74
+ * origin slice, so a multi-source view (e.g. a dashboard reading two events from
75
+ * two different, possibly far-apart slices) naturally gets one column per source,
76
+ * not a single blown-out column. */
77
+ function columnsForStateView(model, i) {
78
+ const slice = model.slices[i];
79
+ const cols = [];
80
+ for (const view of slice.elements.filter((e) => e.kind === "view")) {
81
+ for (const name of view.from ?? []) {
82
+ const src = resolveFromSource(model, view, name);
83
+ if (src)
84
+ cols.push({ sourceSliceIndex: src.sliceIndex, elements: [src] });
85
+ }
86
+ }
87
+ cols.push({ sourceSliceIndex: i, elements: slice.elements });
88
+ return cols;
89
+ }
90
+ /** Automation/Translation share one shape: [View ->] Automation (this slice),
91
+ * then Command -> Event (the immediately next slice — never further, mirroring
92
+ * semanticEdges' own i+1-only rule). The watched view is same-slice adjacency,
93
+ * not a `from` hop, so it's already included via this slice's own elements —
94
+ * deliberately not chased any further back. Only command/event are pulled from
95
+ * the next slice; a stray `ui` there belongs to *that* slice's own diagram. */
96
+ function columnsForReaction(model, i) {
97
+ const slice = model.slices[i];
98
+ const cols = [{ sourceSliceIndex: i, elements: slice.elements }];
99
+ const next = model.slices[i + 1];
100
+ const forward = next?.elements.filter((e) => e.kind === "command" || e.kind === "event") ?? [];
101
+ if (forward.length > 0)
102
+ cols.push({ sourceSliceIndex: i + 1, elements: forward });
103
+ return cols;
104
+ }
105
+ /** Merge columns sharing a source slice (two `from` names can resolve to the same
106
+ * origin slice), dedupe elements by id, then sort chronologically. `from`/reaction
107
+ * wiring only ever points backward or to the immediate next slice, so a plain
108
+ * ascending sort by real slice index is sufficient for correct left-to-right
109
+ * placement across every pattern — no pattern-specific ordering code needed. */
110
+ function mergeColumns(columns) {
111
+ const bySource = new Map();
112
+ for (const col of columns) {
113
+ let elements = bySource.get(col.sourceSliceIndex);
114
+ if (!elements) {
115
+ elements = new Map();
116
+ bySource.set(col.sourceSliceIndex, elements);
117
+ }
118
+ for (const el of col.elements)
119
+ elements.set(el.id, el);
120
+ }
121
+ return [...bySource.entries()]
122
+ .map(([sourceSliceIndex, elements]) => ({ sourceSliceIndex, elements: [...elements.values()] }))
123
+ .sort((a, b) => a.sourceSliceIndex - b.sourceSliceIndex);
124
+ }
125
+ /**
126
+ * Assemble a small, valid NormalizedModel from the given columns — real Element
127
+ * objects, shallow-cloned only to reassign `sliceIndex` to their new synthetic
128
+ * column (never mutating the real model). Two details matter for correctness:
129
+ *
130
+ * - `byId` keeps each clone's *original* id — everything downstream (emitDot's
131
+ * node ids, styleFor, note markers, parseNodeRects) keys off Element.id, so
132
+ * preserving it is what keeps the whole rest of the render pipeline working
133
+ * completely unmodified.
134
+ * - `byName` is populated from the clones actually included, using their
135
+ * untouched `name`/`from` strings — this is the entire trick that makes edge
136
+ * drawing free: semanticEdges() (called later, unmodified, inside
137
+ * composeSvg) resolves `from` via byName exactly as it does for the full
138
+ * model, so it finds the clones and draws the arrows automatically.
139
+ *
140
+ * `personas`/`contexts` are derived from only the included elements, not
141
+ * copied from the full model — copying them was the exact class of bug that
142
+ * made the previous crop-based approach barely narrower than the full diagram
143
+ * (an unused row still reserves its own swimlane).
144
+ */
145
+ function assembleSyntheticModel(model, columns, title) {
146
+ const slices = [];
147
+ const elements = [];
148
+ const byId = new Map();
149
+ const byName = new Map();
150
+ columns.forEach((col, syntheticIndex) => {
151
+ const origin = model.slices[col.sourceSliceIndex];
152
+ const cloned = col.elements.map((el) => ({ ...el, sliceIndex: syntheticIndex }));
153
+ slices.push({ name: origin.name, index: syntheticIndex, source: origin.source, elements: cloned, line: origin.line });
154
+ for (const el of cloned) {
155
+ elements.push(el);
156
+ byId.set(el.id, el);
157
+ const key = normalizeName(el.name);
158
+ const bucket = byName.get(key);
159
+ if (bucket)
160
+ bucket.push(el);
161
+ else
162
+ byName.set(key, [el]);
163
+ }
164
+ });
165
+ return {
166
+ name: title,
167
+ personas: dedupeDefined(elements.filter((e) => e.kind === "ui").map((e) => e.persona)),
168
+ contexts: dedupeDefined(elements.filter((e) => e.kind === "event").map((e) => e.context)),
169
+ hasAutomation: elements.some((e) => AUTOMATION_KINDS.has(e.kind)),
170
+ slices,
171
+ elements,
172
+ byId,
173
+ byName,
174
+ // v1 gap, deliberate: explicit DSL `arrow` statements are rare (they exist for
175
+ // relationships semanticEdges' pattern inference can't derive on its own) and
176
+ // dropping them from a single-slice extraction is narrow, not silent — a
177
+ // follow-up could remap model.arrows entries whose both endpoints resolve
178
+ // through an original-id -> cloned-id map, but it's not needed to ship this.
179
+ arrows: [],
180
+ // Unused by layout()/emitDot() — fieldLabel() prints a field's type as a raw
181
+ // string, never resolves it — so passthrough is free and avoids handing back
182
+ // broken/empty maps for no benefit.
183
+ types: model.types,
184
+ typesByName: model.typesByName,
185
+ };
186
+ }
187
+ function dedupeDefined(values) {
188
+ const seen = new Set();
189
+ const out = [];
190
+ for (const v of values) {
191
+ if (v && !seen.has(v)) {
192
+ seen.add(v);
193
+ out.push(v);
194
+ }
195
+ }
196
+ return out;
197
+ }
198
+ //# sourceMappingURL=sliceDiagram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sliceDiagram.js","sourceRoot":"","sources":["../../src/render/sliceDiagram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { NormalizedModel } from "../model/model.js";
2
+ import { Grid } from "../layout/grid.js";
3
+ import { Rect } from "./svgGeometry.js";
4
+ /** Every non-element node id the slice overlay needs a rect for (header cells),
5
+ * to fold into the same parseNodeRects() call as element ids. */
6
+ export declare function sliceOverlayIds(grid: Grid): string[];
7
+ /** Splice `data-slice="<index>"` onto every element's node group. */
8
+ export declare function tagSliceAttrs(svg: string, model: NormalizedModel): string;
9
+ /** `<metadata>` (slice ranges, as JSON) + `<style>` (the dim class) block, spliced
10
+ * just inside the root `<svg>`. `svg` is scanned (post-fitCanvas/tagSliceAttrs is
11
+ * fine — neither touches `<text>` elements) for the row-label column's x-range.
12
+ * Returns "" if no slice has a rect — the model is empty, or the SVG shape wasn't
13
+ * what parseNodeRects expected.
14
+ *
15
+ * rects (from parseNodeRects) and the row-label `<text>` x positions scanned below
16
+ * are both in graph-space — the coordinate system *inside* Graphviz's
17
+ * `<g class="graph" transform="scale(...) translate(...)">` wrapper — not the root
18
+ * SVG/viewBox space the served viewer sets `viewBox` in. Every x0/x1 here goes
19
+ * through readGraphTransform()'s toRoot() before it's embedded, the same
20
+ * conversion fitCanvas() applies to the edge-detour bbox for the same reason. */
21
+ export declare function buildSliceOverlay(svg: string, grid: Grid, rects: Map<string, Rect>): string;