@diagrammo/dgmo 0.75.0 → 0.76.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 (43) hide show
  1. package/dist/advanced.d.ts +40 -1
  2. package/dist/advanced.js +181 -19
  3. package/dist/block.css +20 -3
  4. package/dist/block.d.ts +10 -2
  5. package/dist/block.js +29 -12
  6. package/dist/chart-meta.js +6 -6
  7. package/dist/{chunk-LYLRNAX2.js → chunk-3EGIU5WY.js} +2 -2
  8. package/dist/{chunk-2NRIGEHZ.js → chunk-46KVXDUJ.js} +10 -3
  9. package/dist/{chunk-M3IVOGZE.js → chunk-4CRLM3IT.js} +26 -20
  10. package/dist/chunk-7IATMZSA.js +72 -0
  11. package/dist/{chunk-6DYZ2JM6.js → chunk-BZQCAZZZ.js} +203 -29
  12. package/dist/{chunk-ZMKCG3FT.js → chunk-C576CU77.js} +2 -2
  13. package/dist/{chunk-DSH7NJ2G.js → chunk-EGXJWRO6.js} +2 -2
  14. package/dist/{chunk-RT7ACF77.js → chunk-EGY3TS62.js} +6 -6
  15. package/dist/{chunk-TDHD3HNO.js → chunk-F4X5NVKY.js} +37 -16
  16. package/dist/{chunk-BFTRW24X.js → chunk-FMZEU7MF.js} +64 -23
  17. package/dist/{chunk-R5JPMW2F.js → chunk-FTTB64OJ.js} +5 -5
  18. package/dist/{chunk-IGQW7TKM.js → chunk-GFXFRKUG.js} +2 -2
  19. package/dist/{chunk-F4CPZRG2.js → chunk-JLJ7SP4X.js} +2 -2
  20. package/dist/{chunk-SUA56ZSB.js → chunk-LTTZX6RD.js} +6 -2
  21. package/dist/{chunk-GE5RO4BS.js → chunk-N6MZSBOL.js} +5 -1
  22. package/dist/{chunk-DU3OIBQE.js → chunk-NWNSXHA5.js} +6 -2
  23. package/dist/{chunk-QUYYL3LP.js → chunk-OBYZU23V.js} +4 -4
  24. package/dist/{chunk-XCAEHWQB.js → chunk-PAW4RMH3.js} +2 -2
  25. package/dist/{chunk-XFLLYY3V.js → chunk-TF76LCZS.js} +33 -49
  26. package/dist/{dgmo-router-KYNLRE7S.js → dgmo-router-A3LIHT6H.js} +7 -7
  27. package/dist/{dimensions-NLQ64ED5.js → dimensions-7NYBSZRJ.js} +3 -3
  28. package/dist/{flowchart-parser-LY5GGQL2.js → flowchart-parser-NVBP3C5D.js} +2 -2
  29. package/dist/{flowchart-renderer-RIJ5FZKO.js → flowchart-renderer-QYGQ64BM.js} +5 -5
  30. package/dist/index.js +8 -8
  31. package/dist/{layout-UJ62RQQ3.js → layout-2OJ25STS.js} +2 -2
  32. package/dist/{layout-DAYQ46PS.js → layout-LAS67TK3.js} +3 -3
  33. package/dist/{parser-LUOINP6H.js → parser-QYAIFXLP.js} +3 -3
  34. package/dist/{parser-L7RXUKYN.js → parser-WVE6SKB7.js} +2 -2
  35. package/dist/{parser-JYIZEKAS.js → parser-ZYAFHXKQ.js} +2 -2
  36. package/dist/{renderer-WDSBFDQ2.js → renderer-E6NHTVN6.js} +3 -3
  37. package/dist/{renderer-A2YWSBPY.js → renderer-F6WSR7UM.js} +3 -3
  38. package/dist/{renderer-UYYBJ7OB.js → renderer-IH45QV6Z.js} +9 -9
  39. package/dist/{renderer-73QHYZ2Q.js → renderer-LE3ZKHNS.js} +3 -3
  40. package/dist/{state-renderer-454ADT7E.js → state-renderer-UIE3AQM6.js} +4 -4
  41. package/docs/language-reference.md +3 -3
  42. package/package.json +2 -2
  43. package/dist/chunk-PDKUANDZ.js +0 -35
@@ -942,6 +942,7 @@ function stripNodeDecorations(name) {
942
942
  }
943
943
  function extractSymbols(docText) {
944
944
  const entities = [];
945
+ const seen = /* @__PURE__ */ new Set();
945
946
  let inMetadata = true;
946
947
  let inTagGroup = false;
947
948
  for (const rawLine of docText.split("\n")) {
@@ -976,7 +977,10 @@ function extractSymbols(docText) {
976
977
  const m = COMPONENT_RE.exec(line);
977
978
  if (m) {
978
979
  const name = stripNodeDecorations((m[1] ?? m[2] ?? "").trim());
979
- if (name && !entities.includes(name)) entities.push(name);
980
+ if (name && !seen.has(name)) {
981
+ seen.add(name);
982
+ entities.push(name);
983
+ }
980
984
  }
981
985
  } else {
982
986
  if (inTagGroup) continue;
@@ -991,7 +995,10 @@ function extractSymbols(docText) {
991
995
  const m = COMPONENT_RE.exec(line);
992
996
  if (m) {
993
997
  const name = stripNodeDecorations((m[1] ?? m[2] ?? "").trim());
994
- if (name && !entities.includes(name)) entities.push(name);
998
+ if (name && !seen.has(name)) {
999
+ seen.add(name);
1000
+ entities.push(name);
1001
+ }
995
1002
  }
996
1003
  }
997
1004
  }
@@ -1004,4 +1011,4 @@ export {
1004
1011
  parseInfra,
1005
1012
  extractSymbols
1006
1013
  };
1007
- //# sourceMappingURL=chunk-2NRIGEHZ.js.map
1014
+ //# sourceMappingURL=chunk-46KVXDUJ.js.map
@@ -45,7 +45,7 @@ import {
45
45
  parseVenn,
46
46
  parseVisualization,
47
47
  parseWordcloud
48
- } from "./chunk-R5JPMW2F.js";
48
+ } from "./chunk-FTTB64OJ.js";
49
49
  import {
50
50
  resolveActiveTagGroup,
51
51
  tagAttrKey
@@ -1947,7 +1947,7 @@ function resolveArcChordOverride(content, detectedType, palette) {
1947
1947
  }
1948
1948
  async function renderForExport(content, theme, palette, viewState, options) {
1949
1949
  const exportMode = options?.exportMode ?? false;
1950
- const { parseDgmoChartType } = await import("./dgmo-router-KYNLRE7S.js");
1950
+ const { parseDgmoChartType } = await import("./dgmo-router-A3LIHT6H.js");
1951
1951
  const detectedTypeRaw = parseDgmoChartType(content);
1952
1952
  const override = resolveArcChordOverride(content, detectedTypeRaw, palette);
1953
1953
  const renderContent = override?.content ?? content;
@@ -2187,9 +2187,9 @@ async function exportKanban(ctx) {
2187
2187
  }
2188
2188
  async function exportClass(ctx) {
2189
2189
  const { content, theme, palette, exportMode } = ctx;
2190
- const { parseClassDiagram } = await import("./parser-JYIZEKAS.js");
2190
+ const { parseClassDiagram } = await import("./parser-ZYAFHXKQ.js");
2191
2191
  const { layoutClassDiagram } = await import("./layout-ROORJQ4X.js");
2192
- const { renderClassDiagram } = await import("./renderer-A2YWSBPY.js");
2192
+ const { renderClassDiagram } = await import("./renderer-F6WSR7UM.js");
2193
2193
  const effectivePalette = await resolveExportPalette(theme, palette);
2194
2194
  const classParsed = parseClassDiagram(content, effectivePalette);
2195
2195
  if (classParsed.error || classParsed.classes.length === 0) return "";
@@ -2248,13 +2248,13 @@ async function exportEr(ctx) {
2248
2248
  }
2249
2249
  async function exportSketch(ctx) {
2250
2250
  const { content, theme, viewState, exportMode } = ctx;
2251
- const { parseSketch } = await import("./parser-LUOINP6H.js");
2251
+ const { parseSketch } = await import("./parser-QYAIFXLP.js");
2252
2252
  const effectivePalette = await resolveExportPalette(theme, ctx.palette);
2253
2253
  const parsed = parseSketch(content, effectivePalette);
2254
2254
  if (parsed.error || parsed.nodes.length === 0 && parsed.boxes.length === 0) {
2255
2255
  return "";
2256
2256
  }
2257
- const { layoutSketch } = await import("./layout-DAYQ46PS.js");
2257
+ const { layoutSketch } = await import("./layout-LAS67TK3.js");
2258
2258
  const { renderSketchForExport } = await import("./renderer-DKW273R7.js");
2259
2259
  const collapsedBoxes = viewState?.cg ? new Set(viewState.cg) : void 0;
2260
2260
  const skLayout = layoutSketch(parsed, {
@@ -2506,9 +2506,9 @@ async function exportC4(ctx) {
2506
2506
  }
2507
2507
  async function exportFlowchart(ctx) {
2508
2508
  const { content, theme, palette } = ctx;
2509
- const { parseFlowchart } = await import("./flowchart-parser-LY5GGQL2.js");
2510
- const { layoutGraph } = await import("./layout-UJ62RQQ3.js");
2511
- const { renderFlowchart } = await import("./flowchart-renderer-RIJ5FZKO.js");
2509
+ const { parseFlowchart } = await import("./flowchart-parser-NVBP3C5D.js");
2510
+ const { layoutGraph } = await import("./layout-2OJ25STS.js");
2511
+ const { renderFlowchart } = await import("./flowchart-renderer-QYGQ64BM.js");
2512
2512
  const effectivePalette = await resolveExportPalette(theme, palette);
2513
2513
  const fcParsed = parseFlowchart(content, effectivePalette);
2514
2514
  if (fcParsed.error || fcParsed.nodes.length === 0) return "";
@@ -2527,10 +2527,10 @@ async function exportFlowchart(ctx) {
2527
2527
  }
2528
2528
  async function exportInfra(ctx) {
2529
2529
  const { content, theme, palette, viewState } = ctx;
2530
- const { parseInfra } = await import("./parser-L7RXUKYN.js");
2530
+ const { parseInfra } = await import("./parser-WVE6SKB7.js");
2531
2531
  const { computeInfra } = await import("./compute-JAP5E2E4.js");
2532
2532
  const { layoutInfra } = await import("./layout-4LNMXLVK.js");
2533
- const { renderInfra, computeInfraLegendGroups } = await import("./renderer-WDSBFDQ2.js");
2533
+ const { renderInfra, computeInfraLegendGroups } = await import("./renderer-E6NHTVN6.js");
2534
2534
  const effectivePalette = await resolveExportPalette(theme, palette);
2535
2535
  const infraParsed = parseInfra(content);
2536
2536
  if (infraParsed.error || infraParsed.nodes.length === 0) return "";
@@ -2667,8 +2667,8 @@ async function exportGantt(ctx) {
2667
2667
  async function exportState(ctx) {
2668
2668
  const { content, theme, palette, viewState } = ctx;
2669
2669
  const { parseState } = await import("./state-parser-TPA67IXM.js");
2670
- const { layoutGraph } = await import("./layout-UJ62RQQ3.js");
2671
- const { renderState } = await import("./state-renderer-454ADT7E.js");
2670
+ const { layoutGraph } = await import("./layout-2OJ25STS.js");
2671
+ const { renderState } = await import("./state-renderer-UIE3AQM6.js");
2672
2672
  const { collapseStateGroups } = await import("./state-collapse-HCGD6IVJ.js");
2673
2673
  const effectivePalette = await resolveExportPalette(theme, palette);
2674
2674
  const stateParsed = parseState(content, effectivePalette);
@@ -2769,8 +2769,8 @@ async function exportMap(ctx) {
2769
2769
  const { content, theme, palette, options } = ctx;
2770
2770
  const { parseMap } = await import("./parser-GCVSRQPY.js");
2771
2771
  const { resolveMap } = await import("./resolver-H5E6V7LJ.js");
2772
- const { renderMapForExport } = await import("./renderer-73QHYZ2Q.js");
2773
- const { mapExportDimensions } = await import("./dimensions-NLQ64ED5.js");
2772
+ const { renderMapForExport } = await import("./renderer-LE3ZKHNS.js");
2773
+ const { mapExportDimensions } = await import("./dimensions-7NYBSZRJ.js");
2774
2774
  const effectivePalette = await resolveExportPalette(theme, palette);
2775
2775
  const mapParsed = parseMap(content, effectivePalette);
2776
2776
  const { data: mapData, reason } = await resolveMapDataSource(
@@ -2864,7 +2864,7 @@ async function exportTreemap(ctx) {
2864
2864
  async function exportLiveLink(ctx) {
2865
2865
  const { content, theme, palette } = ctx;
2866
2866
  const { parseLiveLink } = await import("./parser-WQNT7T2P.js");
2867
- const { renderLiveLinkCard } = await import("./renderer-UYYBJ7OB.js");
2867
+ const { renderLiveLinkCard } = await import("./renderer-IH45QV6Z.js");
2868
2868
  const effectivePalette = await resolveExportPalette(theme, palette);
2869
2869
  return renderLiveLinkCard(parseLiveLink(content), effectivePalette, theme);
2870
2870
  }
@@ -3340,7 +3340,9 @@ var HOVER_SPECS = {
3340
3340
  hoverAttr: "data-activity-id",
3341
3341
  edgeSelector: ".pert-edge",
3342
3342
  fromAttr: "data-source",
3343
- toAttr: "data-target"
3343
+ toAttr: "data-target",
3344
+ legend: true,
3345
+ emphasis: "dim"
3344
3346
  },
3345
3347
  // ── CROSS-FREE tag-group charts — active group discovered at scan time ──
3346
3348
  // The mark carries `data-tag-<slug>` for the ONE active tag group; the
@@ -3389,7 +3391,9 @@ var HOVER_SPECS = {
3389
3391
  hoverAttr: "data-node-id",
3390
3392
  edgeSelector: ".st-edge-group",
3391
3393
  fromAttr: "data-source",
3392
- toAttr: "data-target"
3394
+ toAttr: "data-target",
3395
+ legend: true,
3396
+ emphasis: "dim"
3393
3397
  },
3394
3398
  "boxes-and-lines": {
3395
3399
  markSelector: ".bl-node",
@@ -3451,7 +3455,9 @@ var HOVER_SPECS = {
3451
3455
  hoverAttr: "data-node-id",
3452
3456
  edgeSelector: ".er-edge-group",
3453
3457
  fromAttr: "data-source",
3454
- toAttr: "data-target"
3458
+ toAttr: "data-target",
3459
+ legend: true,
3460
+ emphasis: "dim"
3455
3461
  },
3456
3462
  org: {
3457
3463
  markSelector: ".org-node",
@@ -3806,4 +3812,4 @@ export {
3806
3812
  renderForExport,
3807
3813
  render
3808
3814
  };
3809
- //# sourceMappingURL=chunk-M3IVOGZE.js.map
3815
+ //# sourceMappingURL=chunk-4CRLM3IT.js.map
@@ -0,0 +1,72 @@
1
+ // src/utils/fit-canvas.ts
2
+ function fitDiagramToCanvas(p) {
3
+ const scaleX = (p.width - p.padding * 2) / p.diagramW;
4
+ let scale;
5
+ let canvasHeight;
6
+ if (p.exportMode) {
7
+ scale = Math.min(p.maxScale, scaleX);
8
+ canvasHeight = p.titleHeight + p.diagramH * scale + p.padding * 2;
9
+ } else {
10
+ const availH = p.height - p.titleHeight;
11
+ const scaleY = (availH - p.padding * 2) / p.diagramH;
12
+ scale = Math.min(p.maxScale, scaleX, scaleY);
13
+ canvasHeight = p.height;
14
+ }
15
+ const scaledW = p.diagramW * scale;
16
+ return {
17
+ scale,
18
+ offsetX: (p.width - scaledW) / 2,
19
+ offsetY: p.titleHeight + p.padding,
20
+ canvasHeight
21
+ };
22
+ }
23
+
24
+ // src/graph/edge-spline.ts
25
+ import * as d3Shape from "d3-shape";
26
+ var lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
27
+ function edgeSplinePath(points) {
28
+ return lineGenerator(points);
29
+ }
30
+ function selfLoopArcPath(points) {
31
+ if (points.length < 3) return null;
32
+ const p0 = points[0];
33
+ const p3 = points[points.length - 1];
34
+ const chordX = p3.x - p0.x;
35
+ const chordY = p3.y - p0.y;
36
+ const chordLen = Math.hypot(chordX, chordY);
37
+ if (chordLen === 0) return null;
38
+ let apex = points[Math.floor(points.length / 2)];
39
+ let farthest = -1;
40
+ for (let i = 1; i < points.length - 1; i++) {
41
+ const pt = points[i];
42
+ const away = Math.abs((pt.x - p0.x) * chordY - (pt.y - p0.y) * chordX) / chordLen;
43
+ if (away > farthest) {
44
+ farthest = away;
45
+ apex = pt;
46
+ }
47
+ }
48
+ let ux = apex.x - (p0.x + p3.x) / 2;
49
+ let uy = apex.y - (p0.y + p3.y) / 2;
50
+ const uLen = Math.hypot(ux, uy);
51
+ if (uLen === 0) return null;
52
+ ux /= uLen;
53
+ uy /= uLen;
54
+ const a1 = points[1];
55
+ const a2 = points[points.length - 2];
56
+ const needX = (8 * apex.x - p0.x - p3.x) / 3;
57
+ const needY = (8 * apex.y - p0.y - p3.y) / 3;
58
+ const t = Math.abs(ux) >= Math.abs(uy) ? (needX - (a1.x + a2.x)) / (2 * ux) : (needY - (a1.y + a2.y)) / (2 * uy);
59
+ if (!Number.isFinite(t)) return null;
60
+ const c1x = a1.x + ux * t;
61
+ const c1y = a1.y + uy * t;
62
+ const c2x = a2.x + ux * t;
63
+ const c2y = a2.y + uy * t;
64
+ return `M${p0.x},${p0.y}C${c1x},${c1y},${c2x},${c2y},${p3.x},${p3.y}`;
65
+ }
66
+
67
+ export {
68
+ fitDiagramToCanvas,
69
+ edgeSplinePath,
70
+ selfLoopArcPath
71
+ };
72
+ //# sourceMappingURL=chunk-7IATMZSA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SKETCH_DIAGNOSTIC_CODES
3
- } from "./chunk-RT7ACF77.js";
3
+ } from "./chunk-EGY3TS62.js";
4
4
  import {
5
5
  makeDgmoError
6
6
  } from "./chunk-5SI6T5A5.js";
@@ -94,15 +94,85 @@ function resolveAutoFlags(f = {}) {
94
94
  function unitRect(c, r) {
95
95
  return { c, r, w: SKETCH_SEP, h: SKETCH_SEP };
96
96
  }
97
+ var childBoxAt = /* @__PURE__ */ new Map();
98
+ function nearestFreeRect(base, occupied) {
99
+ if (!occupied.hits(base)) return { c: base.c, r: base.r };
100
+ for (let d = 1; d <= 200; d++) {
101
+ const candidates = [];
102
+ for (let dc = -d; dc <= d; dc++) {
103
+ const dr = d - Math.abs(dc);
104
+ candidates.push({ c: base.c + dc, r: base.r + dr });
105
+ if (dr !== 0) candidates.push({ c: base.c + dc, r: base.r - dr });
106
+ }
107
+ candidates.sort((a, b) => {
108
+ const costA = (a.r < base.r ? 2 : 0) + (a.c < base.c ? 1 : 0);
109
+ const costB = (b.r < base.r ? 2 : 0) + (b.c < base.c ? 1 : 0);
110
+ return costA - costB || a.r - b.r || a.c - b.c;
111
+ });
112
+ for (const cand of candidates) {
113
+ if (!occupied.hits({ ...base, c: cand.c, r: cand.r })) return cand;
114
+ }
115
+ }
116
+ return { c: base.c, r: base.r };
117
+ }
97
118
  function rectsOverlap(a, b) {
98
119
  return a.c < b.c + b.w && b.c < a.c + a.w && a.r < b.r + b.h && b.r < a.r + a.h;
99
120
  }
100
- function collidesAny(rect, occupied) {
101
- for (const o of occupied) if (rectsOverlap(rect, o)) return true;
102
- return false;
121
+ var CELL = SKETCH_SEP;
122
+ var OVERSIZED_CELLS = 64;
123
+ function cellRange(rect) {
124
+ return [
125
+ Math.floor(rect.c / CELL),
126
+ Math.floor(rect.r / CELL),
127
+ Math.floor((rect.c + rect.w) / CELL),
128
+ Math.floor((rect.r + rect.h) / CELL)
129
+ ];
130
+ }
131
+ function cellSpan(range) {
132
+ return (range[2] - range[0] + 1) * (range[3] - range[1] + 1);
103
133
  }
134
+ var Occupancy = class {
135
+ grid = /* @__PURE__ */ new Map();
136
+ oversized = [];
137
+ /** Every rect, in insertion order — the extent and flow-origin maths want it. */
138
+ rects = [];
139
+ add(rect) {
140
+ this.rects.push(rect);
141
+ const range = cellRange(rect);
142
+ if (cellSpan(range) > OVERSIZED_CELLS) {
143
+ this.oversized.push(rect);
144
+ return;
145
+ }
146
+ const [c0, r0, c1, r1] = range;
147
+ for (let c = c0; c <= c1; c++) {
148
+ for (let r = r0; r <= r1; r++) {
149
+ const key = `${c},${r}`;
150
+ const bucket = this.grid.get(key);
151
+ if (bucket) bucket.push(rect);
152
+ else this.grid.set(key, [rect]);
153
+ }
154
+ }
155
+ }
156
+ hits(rect) {
157
+ for (const o of this.oversized) if (rectsOverlap(rect, o)) return true;
158
+ const range = cellRange(rect);
159
+ if (cellSpan(range) > OVERSIZED_CELLS) {
160
+ for (const o of this.rects) if (rectsOverlap(rect, o)) return true;
161
+ return false;
162
+ }
163
+ const [c0, r0, c1, r1] = range;
164
+ for (let c = c0; c <= c1; c++) {
165
+ for (let r = r0; r <= r1; r++) {
166
+ const bucket = this.grid.get(`${c},${r}`);
167
+ if (!bucket) continue;
168
+ for (const o of bucket) if (rectsOverlap(rect, o)) return true;
169
+ }
170
+ }
171
+ return false;
172
+ }
173
+ };
104
174
  function nearestFree(base, occupied) {
105
- if (!collidesAny(base, occupied)) return { c: base.c, r: base.r };
175
+ if (!occupied.hits(base)) return { c: base.c, r: base.r };
106
176
  for (let d = 1; d <= 200; d++) {
107
177
  const candidates = [];
108
178
  for (let dc = -d; dc <= d; dc++) {
@@ -116,17 +186,28 @@ function nearestFree(base, occupied) {
116
186
  return costA - costB || a.r - b.r || a.c - b.c;
117
187
  });
118
188
  for (const cand of candidates) {
119
- if (!collidesAny({ ...base, c: cand.c, r: cand.r }, occupied)) {
189
+ if (!occupied.hits({ ...base, c: cand.c, r: cand.r })) {
120
190
  return cand;
121
191
  }
122
192
  }
123
193
  }
124
194
  return { c: base.c, r: base.r };
125
195
  }
126
- function layoutChildren(box, nodesById, warn, flags) {
127
- const occupied = [];
196
+ function layoutChildren(box, nodesById, warn, flags, childBoxRects = /* @__PURE__ */ new Map()) {
197
+ const occupied = new Occupancy();
128
198
  const children = [];
199
+ const localBoxes = [];
129
200
  const pending = [];
201
+ for (const boxId of box.childBoxes) {
202
+ const rect = childBoxRects.get(boxId);
203
+ if (!rect) continue;
204
+ const at = childBoxAt.get(boxId) ?? null;
205
+ const base = at ?? { c: 0, r: 0 };
206
+ const want = { ...rect, c: base.c, r: base.r };
207
+ const spot = flags.resolveOverlap ? nearestFreeRect(want, occupied) : { c: want.c, r: want.r };
208
+ occupied.add({ ...rect, c: spot.c, r: spot.r });
209
+ localBoxes.push({ boxId, c: spot.c, r: spot.r, rect });
210
+ }
130
211
  for (const id of box.children) {
131
212
  const node = nodesById.get(id);
132
213
  if (!node) continue;
@@ -139,25 +220,25 @@ function layoutChildren(box, nodesById, warn, flags) {
139
220
  if (spot.c !== node.at.c || spot.r !== node.at.r) {
140
221
  warn(node.lineNumber, node.label);
141
222
  }
142
- occupied.push(unitRect(spot.c, spot.r));
223
+ occupied.add(unitRect(spot.c, spot.r));
143
224
  children.push({ node, c: spot.c, r: spot.r });
144
225
  }
145
226
  if (!flags.flowPlaceUnpositioned) {
146
227
  for (const node of pending) children.push({ node, c: 0, r: 0 });
147
228
  pending.length = 0;
148
229
  }
149
- let flowRow = occupied.length > 0 ? Math.max(...occupied.map((o) => o.r)) + SKETCH_SEP : 0;
230
+ let flowRow = occupied.rects.length > 0 ? Math.max(...occupied.rects.map((o) => o.r)) + SKETCH_SEP : 0;
150
231
  let flowCol = 0;
151
232
  const wrapAt = Math.max(
152
233
  SKETCH_SEP * 3,
153
- occupied.length > 0 ? Math.max(...occupied.map((o) => o.c)) + SKETCH_SEP : SKETCH_SEP * 3
234
+ occupied.rects.length > 0 ? Math.max(...occupied.rects.map((o) => o.c)) + SKETCH_SEP : SKETCH_SEP * 3
154
235
  );
155
236
  for (const node of pending) {
156
237
  let placed = false;
157
238
  while (!placed) {
158
239
  const rect = unitRect(flowCol, flowRow);
159
- if (!collidesAny(rect, occupied)) {
160
- occupied.push(rect);
240
+ if (!occupied.hits(rect)) {
241
+ occupied.add(rect);
161
242
  children.push({ node, c: flowCol, r: flowRow });
162
243
  placed = true;
163
244
  }
@@ -169,16 +250,16 @@ function layoutChildren(box, nodesById, warn, flags) {
169
250
  }
170
251
  }
171
252
  let extent;
172
- if (occupied.length === 0) {
253
+ if (occupied.rects.length === 0) {
173
254
  extent = { c: 0, r: 0, w: SKETCH_SEP, h: SKETCH_SEP };
174
255
  } else {
175
- const cMin = Math.min(...occupied.map((o) => o.c));
176
- const rMin = Math.min(...occupied.map((o) => o.r));
177
- const cMax = Math.max(...occupied.map((o) => o.c + o.w));
178
- const rMax = Math.max(...occupied.map((o) => o.r + o.h));
256
+ const cMin = Math.min(...occupied.rects.map((o) => o.c));
257
+ const rMin = Math.min(...occupied.rects.map((o) => o.r));
258
+ const cMax = Math.max(...occupied.rects.map((o) => o.c + o.w));
259
+ const rMax = Math.max(...occupied.rects.map((o) => o.r + o.h));
179
260
  extent = { c: cMin, r: rMin, w: cMax - cMin, h: rMax - rMin };
180
261
  }
181
- return { children, extent };
262
+ return { children, boxes: localBoxes, extent };
182
263
  }
183
264
  function layoutSketch(parsed, options = {}) {
184
265
  const diagnostics = [];
@@ -200,11 +281,41 @@ function layoutSketch(parsed, options = {}) {
200
281
  const bandHs = SKETCH_GEOMETRY.bandPx / SKETCH_HALF_SLOT_Y;
201
282
  const boxLocal = /* @__PURE__ */ new Map();
202
283
  const inExpandedBox = /* @__PURE__ */ new Set();
284
+ const boxById = new Map(collapsed.boxes.map((b) => [b.id, b]));
285
+ const nestedBoxIds = new Set(
286
+ collapsed.boxes.filter((b) => b.parentBoxId !== null).map((b) => b.id)
287
+ );
288
+ childBoxAt.clear();
203
289
  for (const box of collapsed.boxes) {
290
+ if (!nestedBoxIds.has(box.id)) continue;
291
+ childBoxAt.set(box.id, box.at);
204
292
  const local = layoutChildren(box, nodesById, overlapWarn, flags);
205
293
  boxLocal.set(box.id, local);
206
294
  for (const child of local.children) inExpandedBox.add(child.node.id);
207
295
  }
296
+ const childBoxRects = /* @__PURE__ */ new Map();
297
+ for (const box of collapsed.boxes) {
298
+ if (!nestedBoxIds.has(box.id)) continue;
299
+ const local = boxLocal.get(box.id);
300
+ childBoxRects.set(box.id, {
301
+ c: 0,
302
+ r: 0,
303
+ w: local.extent.w + 2 * padHsX,
304
+ h: local.extent.h + bandHs + padHsY
305
+ });
306
+ }
307
+ for (const box of collapsed.boxes) {
308
+ if (nestedBoxIds.has(box.id)) continue;
309
+ const local = layoutChildren(
310
+ box,
311
+ nodesById,
312
+ overlapWarn,
313
+ flags,
314
+ childBoxRects
315
+ );
316
+ boxLocal.set(box.id, local);
317
+ for (const child of local.children) inExpandedBox.add(child.node.id);
318
+ }
208
319
  const allNodesById = new Map(parsed.nodes.map((n) => [n.id, n]));
209
320
  const noWarn = () => {
210
321
  };
@@ -242,6 +353,7 @@ function layoutSketch(parsed, options = {}) {
242
353
  rootUnits.push({ kind: "virtual", box });
243
354
  }
244
355
  for (const box of collapsed.boxes) {
356
+ if (nestedBoxIds.has(box.id)) continue;
245
357
  rootUnits.push({ kind: "box", box });
246
358
  }
247
359
  const lineOf = (u) => u.kind === "shape" ? u.node.lineNumber : u.box.lineNumber;
@@ -274,7 +386,7 @@ function layoutSketch(parsed, options = {}) {
274
386
  }
275
387
  return unitRect(c, r);
276
388
  };
277
- const occupied = [];
389
+ const occupied = new Occupancy();
278
390
  const placedAt = /* @__PURE__ */ new Map();
279
391
  const pending = [];
280
392
  for (const unit of rootUnits) {
@@ -285,7 +397,7 @@ function layoutSketch(parsed, options = {}) {
285
397
  }
286
398
  const base = rectFor(unit, at.c, at.r);
287
399
  let spot = { c: at.c, r: at.r };
288
- if (flags.resolveOverlap && unit.kind === "shape" && collidesAny(base, occupied)) {
400
+ if (flags.resolveOverlap && unit.kind === "shape" && occupied.hits(base)) {
289
401
  const freed = nearestFree(base, occupied);
290
402
  spot = {
291
403
  c: at.c + (freed.c - base.c),
@@ -293,28 +405,28 @@ function layoutSketch(parsed, options = {}) {
293
405
  };
294
406
  overlapWarn(unit.node.lineNumber, unit.node.label);
295
407
  }
296
- occupied.push(rectFor(unit, spot.c, spot.r));
408
+ occupied.add(rectFor(unit, spot.c, spot.r));
297
409
  placedAt.set(unit, spot);
298
410
  }
299
411
  if (!flags.flowPlaceUnpositioned) {
300
412
  for (const unit of pending) {
301
- occupied.push(rectFor(unit, 0, 0));
413
+ occupied.add(rectFor(unit, 0, 0));
302
414
  placedAt.set(unit, { c: 0, r: 0 });
303
415
  }
304
416
  pending.length = 0;
305
417
  }
306
- let flowRow = occupied.length > 0 ? Math.ceil(Math.max(...occupied.map((o) => o.r + o.h))) : 0;
418
+ let flowRow = occupied.rects.length > 0 ? Math.ceil(Math.max(...occupied.rects.map((o) => o.r + o.h))) : 0;
307
419
  let flowCol = 0;
308
420
  const wrapAt = Math.max(
309
421
  SKETCH_SEP * 4,
310
- occupied.length > 0 ? Math.ceil(Math.max(...occupied.map((o) => o.c + o.w))) : SKETCH_SEP * 4
422
+ occupied.rects.length > 0 ? Math.ceil(Math.max(...occupied.rects.map((o) => o.c + o.w))) : SKETCH_SEP * 4
311
423
  );
312
424
  for (const unit of pending) {
313
425
  let placed = false;
314
426
  while (!placed) {
315
427
  const rect = rectFor(unit, flowCol, flowRow);
316
- if (!collidesAny(rect, occupied)) {
317
- occupied.push(rect);
428
+ if (!occupied.hits(rect)) {
429
+ occupied.add(rect);
318
430
  placedAt.set(unit, { c: flowCol, r: flowRow });
319
431
  placed = true;
320
432
  }
@@ -409,6 +521,64 @@ function layoutSketch(parsed, options = {}) {
409
521
  maxX = origin.x + SKETCH_FOOT_W;
410
522
  maxY = minY;
411
523
  }
524
+ for (const inner of local.boxes) {
525
+ const innerBox = boxById.get(inner.boxId);
526
+ const innerLocal = boxLocal.get(inner.boxId);
527
+ if (!innerBox || !innerLocal) continue;
528
+ const innerRectOrigin = toPx(spot.c + inner.c, spot.r + inner.r);
529
+ const innerContentX = innerRectOrigin.x + SKETCH_GEOMETRY.boxPadPx - innerLocal.extent.c * SKETCH_HALF_SLOT_X;
530
+ const innerContentY = innerRectOrigin.y + SKETCH_GEOMETRY.bandPx - innerLocal.extent.r * SKETCH_HALF_SLOT_Y;
531
+ let iMinX = Infinity;
532
+ let iMinY = Infinity;
533
+ let iMaxX = -Infinity;
534
+ let iMaxY = -Infinity;
535
+ for (const gc of innerLocal.children) {
536
+ const gx = innerContentX + gc.c * SKETCH_HALF_SLOT_X;
537
+ const gy = innerContentY + gc.r * SKETCH_HALF_SLOT_Y;
538
+ nodes.push({
539
+ id: gc.node.id,
540
+ label: gc.node.label,
541
+ shape: gc.node.shape,
542
+ metadata: gc.node.metadata,
543
+ ...gc.node.description && { description: gc.node.description },
544
+ boxLabel: innerBox.label,
545
+ lineNumber: gc.node.lineNumber,
546
+ slot: { c: spot.c + inner.c, r: spot.r + inner.r },
547
+ x: gx,
548
+ y: gy,
549
+ w: SKETCH_FOOT_W,
550
+ h: SKETCH_FOOT_H
551
+ });
552
+ iMinX = Math.min(iMinX, gx);
553
+ iMinY = Math.min(iMinY, gy);
554
+ iMaxX = Math.max(iMaxX, gx + SKETCH_FOOT_W);
555
+ iMaxY = Math.max(iMaxY, gy + SKETCH_FOOT_H);
556
+ }
557
+ if (iMinX === Infinity) {
558
+ iMinX = innerContentX;
559
+ iMinY = innerContentY + SKETCH_GEOMETRY.bandPx;
560
+ iMaxX = innerContentX + SKETCH_FOOT_W;
561
+ iMaxY = iMinY;
562
+ }
563
+ const iFrame = {
564
+ x: iMinX - SKETCH_GEOMETRY.boxPadPx,
565
+ y: iMinY - SKETCH_GEOMETRY.bandPx,
566
+ w: iMaxX - iMinX + 2 * SKETCH_GEOMETRY.boxPadPx,
567
+ h: iMaxY - iMinY + SKETCH_GEOMETRY.bandPx + SKETCH_GEOMETRY.boxPadPx
568
+ };
569
+ boxes.push({
570
+ id: innerBox.id,
571
+ label: innerBox.label,
572
+ metadata: innerBox.metadata,
573
+ lineNumber: innerBox.lineNumber,
574
+ ...iFrame,
575
+ bandH: SKETCH_GEOMETRY.bandPx
576
+ });
577
+ minX = Math.min(minX, iFrame.x);
578
+ minY = Math.min(minY, iFrame.y);
579
+ maxX = Math.max(maxX, iFrame.x + iFrame.w);
580
+ maxY = Math.max(maxY, iFrame.y + iFrame.h);
581
+ }
412
582
  boxes.push({
413
583
  id: unit.box.id,
414
584
  label: unit.box.label,
@@ -507,9 +677,13 @@ function layoutSketch(parsed, options = {}) {
507
677
  width = Math.max(width, b.x + b.w);
508
678
  height = Math.max(height, b.y + b.h);
509
679
  }
680
+ const orderedBoxes = [
681
+ ...boxes.filter((b) => !nestedBoxIds.has(b.id)),
682
+ ...boxes.filter((b) => nestedBoxIds.has(b.id))
683
+ ];
510
684
  return {
511
685
  nodes,
512
- boxes,
686
+ boxes: orderedBoxes,
513
687
  edges: collapsed.edges,
514
688
  width,
515
689
  height,
@@ -532,4 +706,4 @@ export {
532
706
  SKETCH_AUTO_LAYOUT_DEFAULTS,
533
707
  layoutSketch
534
708
  };
535
- //# sourceMappingURL=chunk-6DYZ2JM6.js.map
709
+ //# sourceMappingURL=chunk-BZQCAZZZ.js.map
@@ -44,7 +44,7 @@ import {
44
44
  } from "./chunk-MTGGOCND.js";
45
45
  import {
46
46
  parseClassDiagram
47
- } from "./chunk-SUA56ZSB.js";
47
+ } from "./chunk-LTTZX6RD.js";
48
48
  import {
49
49
  fillModeFromOptions,
50
50
  legendInlineRequested,
@@ -448,4 +448,4 @@ export {
448
448
  renderClassDiagram,
449
449
  renderClassDiagramForExport
450
450
  };
451
- //# sourceMappingURL=chunk-ZMKCG3FT.js.map
451
+ //# sourceMappingURL=chunk-C576CU77.js.map
@@ -6,7 +6,7 @@ import {
6
6
  mapLegendConfig,
7
7
  mapLegendGroups,
8
8
  parsePathRings
9
- } from "./chunk-BFTRW24X.js";
9
+ } from "./chunk-FMZEU7MF.js";
10
10
  import {
11
11
  TITLE_FONT_SIZE,
12
12
  TITLE_FONT_WEIGHT,
@@ -781,4 +781,4 @@ export {
781
781
  renderMap,
782
782
  renderMapForExport
783
783
  };
784
- //# sourceMappingURL=chunk-DSH7NJ2G.js.map
784
+ //# sourceMappingURL=chunk-EGXJWRO6.js.map
@@ -1,6 +1,6 @@
1
1
  // src/sketch/diagnostics.ts
2
2
  var SKETCH_DIAGNOSTIC_CODES = {
3
- NESTED_BOX: "E_SKETCH_NESTED_BOX",
3
+ BOX_DEPTH: "E_SKETCH_BOX_DEPTH",
4
4
  AMBIGUOUS_TARGET: "E_SKETCH_AMBIGUOUS_TARGET",
5
5
  UNKNOWN_SHAPE: "W_SKETCH_UNKNOWN_SHAPE",
6
6
  OVERLAP_RESOLVED: "W_SKETCH_OVERLAP_RESOLVED",
@@ -8,12 +8,12 @@ var SKETCH_DIAGNOSTIC_CODES = {
8
8
  };
9
9
  var SKETCH_DIAGNOSTICS = [
10
10
  {
11
- code: SKETCH_DIAGNOSTIC_CODES.NESTED_BOX,
11
+ code: SKETCH_DIAGNOSTIC_CODES.BOX_DEPTH,
12
12
  severity: "error",
13
13
  chartType: "sketch",
14
- title: "Nested box",
15
- message: (p) => `Box "${String(p.label ?? "?")}" is nested inside another box \u2014 sketch boxes are one level only; its shapes join the outer box`,
16
- hint: "Move the inner [Box] to the top level.",
14
+ title: "Box nested too deep",
15
+ message: (p) => `Box "${String(p.label ?? "?")}" is nested ${String(p.depth ?? "?")} levels deep \u2014 sketch boxes nest to depth ${String(p.max ?? "?")} (decision #58); its shapes join the box above it`,
16
+ hint: "Move the inner [Box] up a level \u2014 two levels of bracket is the bound.",
17
17
  example: "sketch\n[Outer]\n [Inner]\n Shape"
18
18
  },
19
19
  {
@@ -58,4 +58,4 @@ export {
58
58
  SKETCH_DIAGNOSTIC_CODES,
59
59
  SKETCH_DIAGNOSTICS
60
60
  };
61
- //# sourceMappingURL=chunk-RT7ACF77.js.map
61
+ //# sourceMappingURL=chunk-EGY3TS62.js.map