@diagrammo/dgmo 0.85.0 → 0.87.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 (122) hide show
  1. package/dist/advanced.d.ts +45 -9
  2. package/dist/advanced.js +83 -64
  3. package/dist/block.js +11 -23
  4. package/dist/chart-meta.js +3 -3
  5. package/dist/{chunk-FAV4RDVT.js → chunk-2A4DQAXG.js} +1 -1
  6. package/dist/{chunk-LRNIYQ6Y.js → chunk-33FQ6HNV.js} +1 -1
  7. package/dist/{chunk-JIPUBFAM.js → chunk-5KGM44TG.js} +2 -2
  8. package/dist/{chunk-23RWHHI5.js → chunk-5NU7NII2.js} +14 -2
  9. package/dist/{chunk-QJ2JOLFN.js → chunk-6I5UN4FD.js} +2 -2
  10. package/dist/{chunk-HMWZQ53A.js → chunk-7WGCQE2E.js} +1 -1
  11. package/dist/{chunk-NSS2EF5B.js → chunk-AAMCXQPP.js} +1 -1
  12. package/dist/{chunk-XD7XL3SF.js → chunk-AGO3PFGS.js} +1 -1
  13. package/dist/{chunk-JVRDKR5R.js → chunk-AQIBKTPG.js} +1 -1
  14. package/dist/{chunk-RYZNNXUV.js → chunk-AUS7V54P.js} +1 -1
  15. package/dist/{chunk-ITLPDLBH.js → chunk-B7CU2HW5.js} +3 -3
  16. package/dist/{chunk-X2VEJ5NT.js → chunk-BMUWF77V.js} +2 -2
  17. package/dist/{chunk-6HOVLE3H.js → chunk-BT2WH2XT.js} +1 -1
  18. package/dist/{chunk-3MXJMZAN.js → chunk-BV6PKJUS.js} +105 -28
  19. package/dist/{chunk-7256EOAE.js → chunk-CFBY77QJ.js} +129 -10
  20. package/dist/{chunk-BP4BT5TH.js → chunk-CLQIXD2C.js} +1 -1
  21. package/dist/{chunk-OZTDXMRQ.js → chunk-D4M25W6R.js} +1 -1
  22. package/dist/{chunk-A4PHPKNR.js → chunk-DAX42XVH.js} +1 -1
  23. package/dist/{chunk-C5FEPGSJ.js → chunk-DSG6GBUQ.js} +3 -3
  24. package/dist/{chunk-JLGN6S4B.js → chunk-EJS3K44P.js} +148 -80
  25. package/dist/{chunk-YWHGWLY6.js → chunk-FBKPSU3A.js} +5 -5
  26. package/dist/{chunk-5JO443MB.js → chunk-HAH5X345.js} +1 -1
  27. package/dist/{chunk-HBCAY3KR.js → chunk-HTNLLT34.js} +1 -1
  28. package/dist/{chunk-PEFPTQ3Z.js → chunk-HTPZDZKQ.js} +3 -3
  29. package/dist/{chunk-XXAZ5C4K.js → chunk-IJ27L3R4.js} +1 -1
  30. package/dist/{chunk-OGKP2XGJ.js → chunk-J7Q4VPBJ.js} +2 -2
  31. package/dist/{chunk-VDMG3MM4.js → chunk-JD4RHBNX.js} +1 -1
  32. package/dist/{chunk-HATTVAWX.js → chunk-LJFXC3IP.js} +1 -1
  33. package/dist/{chunk-REUEU5PU.js → chunk-LL6D3MES.js} +3 -3
  34. package/dist/{chunk-ULCB4BLF.js → chunk-LPXC4I4R.js} +19 -2
  35. package/dist/{chunk-BIQYWWJ2.js → chunk-LSQGJN5H.js} +2 -2
  36. package/dist/{chunk-T327J6CE.js → chunk-MSXRNVHB.js} +2 -2
  37. package/dist/{chunk-V3LJIPS4.js → chunk-N7XRBWXA.js} +3 -3
  38. package/dist/{chunk-UJRMARNX.js → chunk-NJIGYGZS.js} +19 -1
  39. package/dist/{chunk-WMIKC5XO.js → chunk-NPWVOFB3.js} +1 -1
  40. package/dist/{chunk-2QIGN7KL.js → chunk-O6MMQQLF.js} +9 -5
  41. package/dist/{chunk-MIZ46MOF.js → chunk-P5V63L35.js} +2 -2
  42. package/dist/{chunk-ADAGWVWJ.js → chunk-PJQZWJ5I.js} +70 -48
  43. package/dist/{chunk-RMAJIB6S.js → chunk-PUVTFUNH.js} +4 -4
  44. package/dist/{chunk-ETBXIKK6.js → chunk-Q2U5635V.js} +1 -1
  45. package/dist/chunk-QT3KDDXK.js +436 -0
  46. package/dist/{chunk-UNSN56OJ.js → chunk-R2K5L4UA.js} +1 -1
  47. package/dist/{chunk-SLMAAJPE.js → chunk-RGI64U3A.js} +1 -1
  48. package/dist/{chunk-ZGHV4QLK.js → chunk-RJNZHFNG.js} +1 -1
  49. package/dist/{chunk-FWNJDD4U.js → chunk-S7W75MIM.js} +3 -20
  50. package/dist/{chunk-ZUUNTOMG.js → chunk-UIUXIDBX.js} +1 -1
  51. package/dist/{chunk-XIYVGO24.js → chunk-UMRZPRAN.js} +3 -3
  52. package/dist/{chunk-67USWFFE.js → chunk-UY6TEJJH.js} +1 -1
  53. package/dist/{chunk-UGYZ5Q5Y.js → chunk-VJ2KROJ5.js} +1 -1
  54. package/dist/{chunk-PSH5DT26.js → chunk-WZ23JJ7U.js} +3 -3
  55. package/dist/{chunk-IIEI7VK5.js → chunk-XCKNFFKM.js} +2 -0
  56. package/dist/{chunk-Y7FTHOMD.js → chunk-XIFK423U.js} +129 -225
  57. package/dist/{chunk-LQSNWE3W.js → chunk-XUDGZJTJ.js} +3 -3
  58. package/dist/{chunk-D7D5YK4J.js → chunk-XVSJYAK7.js} +15 -3
  59. package/dist/{chunk-J2ICADUM.js → chunk-XWYR5T52.js} +3 -3
  60. package/dist/{chunk-QNKHJ5CK.js → chunk-YK4N6TBN.js} +4 -2
  61. package/dist/{chunk-NJS62BAG.js → chunk-YMG7JDEY.js} +17 -7
  62. package/dist/chunk-Z6DXWULM.js +23 -0
  63. package/dist/{chunk-PBEH52A2.js → chunk-ZNNVI5SR.js} +1 -1
  64. package/dist/cloud-reference.d.ts +11 -1
  65. package/dist/cloud-reference.js +1 -1
  66. package/dist/{dgmo-router-VILEOWQ2.js → dgmo-router-F2DSURTR.js} +4 -4
  67. package/dist/{dimensions-F3Y5T54V.js → dimensions-6QAWEGIR.js} +2 -2
  68. package/dist/{flowchart-renderer-TGQL4S4L.js → flowchart-renderer-THC6Y7EH.js} +8 -7
  69. package/dist/index.d.ts +68 -1
  70. package/dist/index.js +81 -13
  71. package/dist/{layout-WZCXK7DF.js → layout-5NPBHV5F.js} +2 -2
  72. package/dist/{layout-WXVGMSI3.js → layout-5VSMZPMG.js} +2 -2
  73. package/dist/{layout-CFTB7GKV.js → layout-BOVTLAOT.js} +7 -6
  74. package/dist/{layout-K6NI4VCS.js → layout-CUFA7XJD.js} +2 -2
  75. package/dist/{layout-6JARW64V.js → layout-EHTYSMV4.js} +6 -5
  76. package/dist/{layout-NB7TKAWN.js → layout-LUQKAGGT.js} +2 -2
  77. package/dist/{layout-YELNBVPC.js → layout-P6DTD4RT.js} +6 -5
  78. package/dist/{layout-PT7IKVCV.js → layout-QX5T6ZNE.js} +2 -2
  79. package/dist/layout-RANVNAKI.js +10 -0
  80. package/dist/{layout-FMKSENGL.js → layout-XUW444O5.js} +5 -4
  81. package/dist/{layout-search-GRJEBAUF.js → layout-search-VKSBUCDM.js} +67 -33
  82. package/dist/live-link-resolve.d.ts +21 -3
  83. package/dist/live-link-resolve.js +47 -17
  84. package/dist/{parser-6TXNMXNB.js → parser-XYY5NHP4.js} +2 -2
  85. package/dist/{renderer-YSH5EX54.js → renderer-222OTVQV.js} +3 -2
  86. package/dist/{renderer-DRLLZZ6P.js → renderer-2KVQGGTT.js} +5 -4
  87. package/dist/{renderer-E5EKGIX2.js → renderer-2OOPQ4CH.js} +4 -3
  88. package/dist/{renderer-OV3GUJRR.js → renderer-36JH4OMC.js} +4 -4
  89. package/dist/{renderer-G72GMBKA.js → renderer-3CTUWCO7.js} +7 -6
  90. package/dist/{renderer-XS42HEXW.js → renderer-3FF4QVGW.js} +4 -3
  91. package/dist/{renderer-R6VJDZFO.js → renderer-4BHOELCE.js} +4 -3
  92. package/dist/{renderer-EU27D4S6.js → renderer-4BIMRO4X.js} +5 -4
  93. package/dist/{renderer-HV6ZYJMC.js → renderer-4NZZ5J2F.js} +2 -2
  94. package/dist/{renderer-UGA5DGKR.js → renderer-A4LMOBZL.js} +9 -8
  95. package/dist/{renderer-3AOOLHJU.js → renderer-A6ACVK4X.js} +3 -2
  96. package/dist/{renderer-HWEGM72C.js → renderer-BICLPGAH.js} +2 -2
  97. package/dist/{renderer-ML2CLU25.js → renderer-DCFQDBGJ.js} +4 -3
  98. package/dist/{renderer-ADX4DWRJ.js → renderer-ELYPDPNF.js} +3 -2
  99. package/dist/{renderer-OXSLLQQC.js → renderer-KVAQE7PQ.js} +2 -2
  100. package/dist/{renderer-X4KFEKUK.js → renderer-LODEVN3V.js} +5 -4
  101. package/dist/{renderer-WUDVV363.js → renderer-NHPKNNBD.js} +6 -5
  102. package/dist/{renderer-TF3NTKP3.js → renderer-NX4YKWCJ.js} +2 -2
  103. package/dist/{renderer-P7LQBDU2.js → renderer-QM23SWAM.js} +2 -2
  104. package/dist/{renderer-5YOF2AHJ.js → renderer-R34GJL7U.js} +9 -8
  105. package/dist/{renderer-FBAFYSIQ.js → renderer-RAATRF6W.js} +3 -3
  106. package/dist/{renderer-FXX3XSNW.js → renderer-RENDISO5.js} +2 -2
  107. package/dist/{renderer-3F4K2X57.js → renderer-RMGN3Q7D.js} +2 -2
  108. package/dist/{renderer-FQAZ3KNU.js → renderer-SDUSBCJI.js} +4 -3
  109. package/dist/{renderer-OVDVFPAI.js → renderer-WH2HWE5Z.js} +5 -4
  110. package/dist/{renderer-J3O2KVTL.js → renderer-X5A5VIRD.js} +6 -5
  111. package/dist/{renderer-WRWVFEX7.js → renderer-XRALFCIN.js} +6 -4
  112. package/dist/{renderer-3XRDFNYF.js → renderer-YDAVCRTP.js} +5 -4
  113. package/dist/{renderer-5A6VWIVX.js → renderer-YIZ7G4HU.js} +5 -5
  114. package/dist/{state-renderer-B4ZF6ESS.js → state-renderer-5IQ4OJUI.js} +8 -7
  115. package/docs/language-reference.md +12 -12
  116. package/gallery/fixtures/journey-map.dgmo +28 -0
  117. package/gallery/fixtures/mindmap.dgmo +31 -0
  118. package/gallery/fixtures/pert.dgmo +27 -0
  119. package/gallery/fixtures/wireframe.dgmo +30 -0
  120. package/package.json +3 -3
  121. package/dist/chunk-WTTIODDV.js +0 -9
  122. package/dist/layout-JQ3IPJYR.js +0 -9
@@ -1,243 +1,38 @@
1
1
  import {
2
+ LABEL_REACH_WIDE,
2
3
  NODE_HEIGHT,
3
- NODE_WIDTH
4
- } from "./chunk-WTTIODDV.js";
4
+ NODE_WIDTH,
5
+ placeEdgeLabels
6
+ } from "./chunk-QT3KDDXK.js";
5
7
  import {
6
8
  buildPlacedNotes
7
- } from "./chunk-HBCAY3KR.js";
9
+ } from "./chunk-HTNLLT34.js";
8
10
  import {
9
11
  noteCanvasShift
10
12
  } from "./chunk-6EK55ART.js";
11
13
  import {
12
14
  measureText,
13
- truncateText,
14
15
  wrapTextToWidth
15
16
  } from "./chunk-KAO7JL4I.js";
16
17
  import {
17
18
  resolveNotes
18
19
  } from "./chunk-O753JUEZ.js";
19
20
 
20
- // src/boxes-and-lines/label-placement.ts
21
- var EDGE_LABEL_FONT_SIZE = 11;
22
- var LABEL_MAX_WIDTH = 160;
23
- var MAX_LABEL_LINES = 3;
24
- var LABEL_LINE_HEIGHT = 1.3;
25
- var H_PAD = 6;
26
- var V_PAD = 3;
27
- var BOX_CLEAR_PAD = 4;
28
- var PERP_STEP = 8;
29
- var PERP_MAX = 40;
30
- var SLIDE_SAMPLES = 9;
31
- function measureEdgeLabel(label, fontSize = EDGE_LABEL_FONT_SIZE, maxWidth = LABEL_MAX_WIDTH) {
32
- let lines = wrapTextToWidth(label, fontSize, maxWidth, { hardBreak: true });
33
- if (lines.length > MAX_LABEL_LINES) {
34
- const kept = lines.slice(0, MAX_LABEL_LINES - 1);
35
- const rest = lines.slice(MAX_LABEL_LINES - 1).join(" ");
36
- kept.push(truncateText(rest, fontSize, maxWidth));
37
- lines = kept;
38
- }
39
- let textW = 0;
40
- for (const ln of lines) {
41
- const w = measureText(ln, fontSize);
42
- if (w > textW) textW = w;
43
- }
44
- return {
45
- lines,
46
- width: textW + 2 * H_PAD,
47
- height: lines.length * fontSize * LABEL_LINE_HEIGHT + 2 * V_PAD
48
- };
49
- }
50
- function edgePolylineMidpoint(points) {
51
- return pointAtArcFraction(points, 0.5).p;
52
- }
53
- function pointAtArcFraction(points, t) {
54
- if (points.length === 0) return { p: { x: 0, y: 0 }, nx: 0, ny: 1 };
55
- if (points.length === 1)
56
- return { p: { x: points[0].x, y: points[0].y }, nx: 0, ny: 1 };
57
- const segLen = [];
58
- let total = 0;
59
- for (let k = 1; k < points.length; k++) {
60
- const len = Math.hypot(
61
- points[k].x - points[k - 1].x,
62
- points[k].y - points[k - 1].y
63
- );
64
- segLen.push(len);
65
- total += len;
66
- }
67
- let want = total * Math.max(0, Math.min(1, t));
68
- for (let k = 1; k < points.length; k++) {
69
- const len = segLen[k - 1];
70
- if (want <= len || k === points.length - 1) {
71
- const f = len === 0 ? 0 : Math.min(1, want / len);
72
- const ax = points[k - 1].x;
73
- const ay = points[k - 1].y;
74
- const bx = points[k].x;
75
- const by = points[k].y;
76
- const dx = bx - ax;
77
- const dy = by - ay;
78
- const dlen = Math.hypot(dx, dy) || 1;
79
- return {
80
- p: { x: ax + dx * f, y: ay + dy * f },
81
- nx: -dy / dlen,
82
- ny: dx / dlen
83
- };
84
- }
85
- want -= len;
86
- }
87
- const last = points[points.length - 1];
88
- return { p: { x: last.x, y: last.y }, nx: 0, ny: 1 };
89
- }
90
- function rectFromCenter(cx, cy, w, h) {
91
- return {
92
- minX: cx - w / 2,
93
- minY: cy - h / 2,
94
- maxX: cx + w / 2,
95
- maxY: cy + h / 2
96
- };
97
- }
98
- function overlapsAny(cx, cy, w, h, obstacles, pad) {
99
- const minX = cx - w / 2 - pad;
100
- const minY = cy - h / 2 - pad;
101
- const maxX = cx + w / 2 + pad;
102
- const maxY = cy + h / 2 + pad;
103
- for (const o of obstacles) {
104
- if (minX < o.maxX && maxX > o.minX && minY < o.maxY && maxY > o.minY)
105
- return true;
106
- }
107
- return false;
108
- }
109
- function slideFractions() {
110
- const out = [0.5];
111
- for (let k = 1; k <= SLIDE_SAMPLES; k++) {
112
- const off = k * 0.08;
113
- if (0.5 - off >= 0.12) out.push(0.5 - off);
114
- if (0.5 + off <= 0.88) out.push(0.5 + off);
115
- }
116
- return out;
117
- }
118
- function findClearPosition(w, h, points, obstacles) {
119
- const ts = slideFractions();
120
- for (const t of ts) {
121
- const { p } = pointAtArcFraction(points, t);
122
- if (!overlapsAny(p.x, p.y, w, h, obstacles, BOX_CLEAR_PAD)) return p;
123
- }
124
- for (let mag = PERP_STEP; mag <= PERP_MAX; mag += PERP_STEP) {
125
- for (const t of ts) {
126
- const { p, nx, ny } = pointAtArcFraction(points, t);
127
- for (const sign of [-1, 1]) {
128
- const x = p.x + nx * mag * sign;
129
- const y = p.y + ny * mag * sign;
130
- if (!overlapsAny(x, y, w, h, obstacles, BOX_CLEAR_PAD)) return { x, y };
131
- }
132
- }
133
- }
134
- return null;
135
- }
136
- function resolveLabelOverlaps(boxes) {
137
- const MAX_PASSES = 8;
138
- const PAD = 4;
139
- for (let pass = 0; pass < MAX_PASSES; pass++) {
140
- let moved = false;
141
- for (let i = 0; i < boxes.length; i++) {
142
- for (let j = i + 1; j < boxes.length; j++) {
143
- const a = boxes[i];
144
- const b = boxes[j];
145
- const dx = Math.abs(a.cx - b.cx);
146
- const dy = Math.abs(a.cy - b.cy);
147
- const overlapX = (a.w + b.w) / 2 + PAD - dx;
148
- const overlapY = (a.h + b.h) / 2 + PAD - dy;
149
- if (overlapX > 0 && overlapY > 0) {
150
- const shift = overlapY / 2 + 1;
151
- if (a.cy < b.cy) {
152
- a.cy -= shift;
153
- b.cy += shift;
154
- } else {
155
- a.cy += shift;
156
- b.cy -= shift;
157
- }
158
- moved = true;
159
- }
160
- }
161
- }
162
- if (!moved) break;
163
- }
164
- }
165
- function placeEdgeLabels(layout, opts) {
166
- const fontSize = opts?.fontSize ?? EDGE_LABEL_FONT_SIZE;
167
- const maxWidth = opts?.maxWidth ?? LABEL_MAX_WIDTH;
168
- const obstacles = [];
169
- for (const n of layout.nodes)
170
- obstacles.push(rectFromCenter(n.x, n.y, n.width, n.height));
171
- for (const g of layout.groups)
172
- if (g.collapsed)
173
- obstacles.push(rectFromCenter(g.x, g.y, g.width, g.height));
174
- const boxes = [];
175
- layout.edges.forEach((e, idx) => {
176
- if (!e.label || e.points.length < 2) return;
177
- const m = measureEdgeLabel(e.label, fontSize, maxWidth);
178
- const mid = edgePolylineMidpoint(e.points);
179
- let cy = mid.y;
180
- if (e.parallelCount > 1 && e.yOffset !== 0)
181
- cy += (e.yOffset < 0 ? -1 : 1) * (m.height / 2);
182
- boxes.push({
183
- edgeIdx: idx,
184
- cx: mid.x,
185
- cy,
186
- w: m.width,
187
- h: m.height,
188
- lines: m.lines,
189
- resolved: true
190
- });
191
- });
192
- for (const box of boxes) {
193
- if (!overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
194
- continue;
195
- const e = layout.edges[box.edgeIdx];
196
- const clear = findClearPosition(box.w, box.h, e.points, obstacles);
197
- if (clear) {
198
- box.cx = clear.x;
199
- box.cy = clear.y;
200
- } else {
201
- box.resolved = false;
202
- }
203
- }
204
- resolveLabelOverlaps(boxes);
205
- for (const box of boxes) {
206
- if (overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
207
- box.resolved = false;
208
- }
209
- const boxByIdx = /* @__PURE__ */ new Map();
210
- for (const b of boxes) boxByIdx.set(b.edgeIdx, b);
211
- const edges = layout.edges.map((e, idx) => {
212
- const b = boxByIdx.get(idx);
213
- if (!b) return e;
214
- return {
215
- ...e,
216
- labelX: b.cx,
217
- labelY: b.cy,
218
- labelWidth: b.w,
219
- labelHeight: b.h,
220
- labelLines: b.lines
221
- };
222
- });
223
- return {
224
- layout: { ...layout, edges },
225
- unresolved: boxes.filter((b) => !b.resolved).map((b) => b.edgeIdx)
226
- };
227
- }
228
-
229
21
  // src/boxes-and-lines/layout.ts
230
22
  var MARGIN = 40;
231
23
  var MAX_PARALLEL_EDGES = 5;
232
24
  var PARALLEL_SPACING = 22;
25
+ var DETOUR_TOLERANCE = 2 * PARALLEL_SPACING;
26
+ var MIN_PORT_GAP = 16;
27
+ var PORT_CORNER_CLEAR = 6;
233
28
  var DESC_NODE_WIDTH = 140;
234
29
  var DESC_FONT_SIZE = 10;
235
30
  var DESC_LINE_HEIGHT = 1.4;
236
31
  var DESC_PADDING = 8;
237
32
  var SEPARATOR_GAP = 4;
238
33
  var MAX_DESC_LINES = 6;
239
- var MAX_LABEL_LINES2 = 3;
240
- var LABEL_LINE_HEIGHT2 = 1.3;
34
+ var MAX_LABEL_LINES = 3;
35
+ var LABEL_LINE_HEIGHT = 1.3;
241
36
  var LABEL_PAD = 12;
242
37
  var VALUE_ROW_FONT = 11;
243
38
  var VALUE_ROW_H = SEPARATOR_GAP + VALUE_ROW_FONT * DESC_LINE_HEIGHT + DESC_PADDING;
@@ -279,9 +74,9 @@ function estimateLabelLines(label, nodeWidth = NODE_WIDTH) {
279
74
  current = word;
280
75
  }
281
76
  }
282
- if (lines <= MAX_LABEL_LINES2) return Math.min(lines, MAX_LABEL_LINES2);
77
+ if (lines <= MAX_LABEL_LINES) return Math.min(lines, MAX_LABEL_LINES);
283
78
  }
284
- return MAX_LABEL_LINES2;
79
+ return MAX_LABEL_LINES;
285
80
  }
286
81
  function computeNodeSize(node, reserveValueRow) {
287
82
  if (!node.description || node.description.length === 0) {
@@ -289,7 +84,7 @@ function computeNodeSize(node, reserveValueRow) {
289
84
  }
290
85
  const w = DESC_NODE_WIDTH;
291
86
  const labelLines = estimateLabelLines(node.label, w);
292
- const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT2 + LABEL_PAD;
87
+ const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT + LABEL_PAD;
293
88
  const maxTextWidth = w - 24;
294
89
  let totalRenderedLines = 0;
295
90
  for (const line of node.description) {
@@ -312,7 +107,7 @@ function computeNodeSize(node, reserveValueRow) {
312
107
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
313
108
  }
314
109
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
315
- const { layoutBoxesAndLinesSearch } = await import("./layout-search-GRJEBAUF.js");
110
+ const { layoutBoxesAndLinesSearch } = await import("./layout-search-VKSBUCDM.js");
316
111
  const searchOpts = {
317
112
  ...layoutOptions?.hideDescriptions !== void 0 && {
318
113
  hideDescriptions: layoutOptions.hideDescriptions
@@ -334,7 +129,9 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
334
129
  topConfigs = cfgs;
335
130
  }
336
131
  });
337
- let placed = placeEdgeLabels(applyParallelEdgeOffsets(searched));
132
+ const labelOpts = { groups: parsed.groups };
133
+ let chosen = applyParallelEdgeOffsets(spreadCrowdedPorts(searched));
134
+ let placed = placeEdgeLabels(chosen, labelOpts);
338
135
  if (placed.unresolved.length > 0) {
339
136
  const relaid = await layoutBoxesAndLinesSearch(parsed, collapseInfo, {
340
137
  ...searchOpts,
@@ -344,9 +141,19 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
344
141
  // first search surfaced no dagre candidates.
345
142
  ...topConfigs !== void 0 && topConfigs.length > 0 && { configs: topConfigs }
346
143
  });
347
- const relaidPlaced = placeEdgeLabels(applyParallelEdgeOffsets(relaid));
348
- if (relaidPlaced.unresolved.length < placed.unresolved.length)
144
+ const relaidChosen = applyParallelEdgeOffsets(spreadCrowdedPorts(relaid));
145
+ const relaidPlaced = placeEdgeLabels(relaidChosen, labelOpts);
146
+ if (relaidPlaced.unresolved.length < placed.unresolved.length) {
349
147
  placed = relaidPlaced;
148
+ chosen = relaidChosen;
149
+ }
150
+ }
151
+ if (placed.unresolved.length > 0) {
152
+ const wide = placeEdgeLabels(chosen, {
153
+ ...labelOpts,
154
+ perpMax: LABEL_REACH_WIDE
155
+ });
156
+ if (wide.unresolved.length < placed.unresolved.length) placed = wide;
350
157
  }
351
158
  return attachNotes(placed.layout, parsed, layoutOptions?.collapsedNotes);
352
159
  }
@@ -431,6 +238,102 @@ function attachNotes(layout, parsed, collapsedNotes) {
431
238
  height: bbMaxY + shiftY + MARGIN
432
239
  };
433
240
  }
241
+ function spreadCrowdedPorts(layout) {
242
+ const boxes = /* @__PURE__ */ new Map();
243
+ for (const n of layout.nodes)
244
+ boxes.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
245
+ for (const g of layout.groups)
246
+ if (g.collapsed)
247
+ boxes.set("__group_" + g.label, {
248
+ x: g.x,
249
+ y: g.y,
250
+ w: g.width,
251
+ h: g.height
252
+ });
253
+ const faces = /* @__PURE__ */ new Map();
254
+ const onSide = (box, p) => {
255
+ const E = 1;
256
+ const l = box.x - box.w / 2;
257
+ const r = box.x + box.w / 2;
258
+ const t = box.y - box.h / 2;
259
+ const b = box.y + box.h / 2;
260
+ if (p.x < l - E || p.x > r + E || p.y < t - E || p.y > b + E) return null;
261
+ if (Math.abs(p.y - t) <= E) return "top";
262
+ if (Math.abs(p.y - b) <= E) return "bottom";
263
+ if (Math.abs(p.x - l) <= E) return "left";
264
+ if (Math.abs(p.x - r) <= E) return "right";
265
+ return null;
266
+ };
267
+ layout.edges.forEach((e, i) => {
268
+ if (e.source === e.target || e.points.length < 2) return;
269
+ for (const last of [false, true]) {
270
+ const id = last ? e.target : e.source;
271
+ const box = boxes.get(id);
272
+ const p = last ? e.points[e.points.length - 1] : e.points[0];
273
+ if (!box) continue;
274
+ const side = onSide(box, p);
275
+ if (!side) continue;
276
+ const horiz = side === "top" || side === "bottom";
277
+ const key = `${id}\0${side}`;
278
+ let f = faces.get(key);
279
+ if (!f) faces.set(key, f = { box, horiz, ends: [] });
280
+ f.ends.push({ edge: i, last, along: horiz ? p.x : p.y });
281
+ }
282
+ });
283
+ const moved = /* @__PURE__ */ new Map();
284
+ for (const { box, horiz, ends } of faces.values()) {
285
+ if (ends.length < 2) continue;
286
+ ends.sort((a, b) => a.along - b.along || a.edge - b.edge);
287
+ let crowded = false;
288
+ for (let k = 1; k < ends.length; k++)
289
+ if (ends[k].along - ends[k - 1].along < MIN_PORT_GAP) crowded = true;
290
+ if (!crowded) continue;
291
+ const half = (horiz ? box.w : box.h) / 2 - PORT_CORNER_CLEAR;
292
+ const ctr = horiz ? box.x : box.y;
293
+ if (half <= 0) continue;
294
+ const gap = Math.min(MIN_PORT_GAP, 2 * half / (ends.length - 1));
295
+ const mean = ends.reduce((acc, e) => acc + e.along, 0) / ends.length;
296
+ const span = gap * (ends.length - 1);
297
+ const first = Math.max(
298
+ ctr - half,
299
+ Math.min(ctr + half - span, mean - span / 2)
300
+ );
301
+ ends.forEach((e, k) => {
302
+ moved.set(`${e.edge}:${e.last}`, { along: first + k * gap, horiz });
303
+ });
304
+ }
305
+ if (moved.size === 0) return layout;
306
+ return {
307
+ ...layout,
308
+ edges: layout.edges.map((e, i) => {
309
+ const a = moved.get(`${i}:false`);
310
+ const b = moved.get(`${i}:true`);
311
+ if (a === void 0 && b === void 0) return e;
312
+ const pts = e.points.map((p) => ({ x: p.x, y: p.y }));
313
+ const shift = (idx, to) => {
314
+ if (to.horiz) pts[idx].x = to.along;
315
+ else pts[idx].y = to.along;
316
+ };
317
+ if (a !== void 0) shift(0, a);
318
+ if (b !== void 0) shift(pts.length - 1, b);
319
+ return { ...e, points: pts };
320
+ })
321
+ };
322
+ }
323
+ function routeDetour(points) {
324
+ const s = points[0];
325
+ const t = points[points.length - 1];
326
+ if (!s || !t) return 0;
327
+ const dx = t.x - s.x;
328
+ const dy = t.y - s.y;
329
+ const len = Math.hypot(dx, dy);
330
+ let worst = 0;
331
+ for (const p of points) {
332
+ const d = len < 1e-6 ? Math.hypot(p.x - s.x, p.y - s.y) : Math.abs((p.x - s.x) * dy - (p.y - s.y) * dx) / len;
333
+ if (d > worst) worst = d;
334
+ }
335
+ return worst;
336
+ }
434
337
  function applyParallelEdgeOffsets(layout) {
435
338
  const groups = /* @__PURE__ */ new Map();
436
339
  layout.edges.forEach((e, i) => {
@@ -451,8 +354,11 @@ function applyParallelEdgeOffsets(layout) {
451
354
  layout.edges.length
452
355
  ).fill(void 0);
453
356
  for (const [key, idxs] of groups) {
454
- const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
357
+ const kept = idxs.slice(0, MAX_PARALLEL_EDGES);
455
358
  for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
359
+ const capped = kept.filter(
360
+ (idx) => routeDetour(layout.edges[idx].points) <= DETOUR_TOLERANCE
361
+ );
456
362
  if (capped.length < 2) continue;
457
363
  const sep = key.indexOf("\0");
458
364
  const ca = centre.get(key.slice(0, sep));
@@ -517,8 +423,6 @@ function applyParallelEdgeOffsets(layout) {
517
423
  }
518
424
 
519
425
  export {
520
- EDGE_LABEL_FONT_SIZE,
521
- measureEdgeLabel,
522
426
  computeNodeSize,
523
427
  layoutBoxesAndLines
524
428
  };
@@ -3,14 +3,14 @@ import {
3
3
  } from "./chunk-26EVL454.js";
4
4
  import {
5
5
  layoutGraph
6
- } from "./chunk-PBEH52A2.js";
6
+ } from "./chunk-ZNNVI5SR.js";
7
7
  import {
8
8
  NOTE_BADGE_RADIUS,
9
9
  noteConnectorPoints,
10
10
  renderNoteBadge,
11
11
  renderNoteBox,
12
12
  renderNoteConnector
13
- } from "./chunk-5JO443MB.js";
13
+ } from "./chunk-HAH5X345.js";
14
14
  import {
15
15
  appendArrowheadMarkers
16
16
  } from "./chunk-XBJJP2HN.js";
@@ -26,7 +26,7 @@ import {
26
26
  EDGE_LABEL_KNOCKOUT_OPACITY,
27
27
  EDGE_STROKE_WIDTH,
28
28
  NODE_STROKE_WIDTH
29
- } from "./chunk-IIEI7VK5.js";
29
+ } from "./chunk-XCKNFFKM.js";
30
30
  import {
31
31
  serializeSvg
32
32
  } from "./chunk-LD2JSKU3.js";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-LNO3ASX5.js";
4
4
  import {
5
5
  parseInlineMarkdown
6
- } from "./chunk-FWNJDD4U.js";
6
+ } from "./chunk-S7W75MIM.js";
7
7
  import {
8
8
  TITLE_FONT_WEIGHT
9
9
  } from "./chunk-VLC6WNRD.js";
@@ -339,15 +339,27 @@ function renderGauge(svg, parsed, metrics, paint) {
339
339
  g.append("line").attr("x1", cx).attr("y1", cy).attr("x2", nx).attr("y2", ny).attr("stroke", paint.text).attr("stroke-width", 5).attr("stroke-linecap", "round");
340
340
  g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 11).attr("fill", paint.text);
341
341
  const nowLabel = compactNumber(parsed.now);
342
- const nowSize = Math.min(
342
+ const bandInner = r - stroke / 2;
343
+ let nowSize = Math.min(
343
344
  r * 0.62,
344
345
  140,
345
346
  r * 0.95 / Math.max(1, nowLabel.length * 0.6)
346
347
  );
348
+ if (parsed.hasTarget) {
349
+ const shoulderX = (bandInner - 26) * Math.SQRT1_2;
350
+ const shoulderW = Math.max(
351
+ measureText(compactNumber(parsed.target * 0.25), 18),
352
+ measureText(compactNumber(parsed.target * 0.75), 18)
353
+ );
354
+ const halfClear = shoulderX - shoulderW - 8;
355
+ const widthPerPx = measureText(nowLabel, 100, { bold: true }) / 100;
356
+ if (widthPerPx > 0 && halfClear > 0) {
357
+ nowSize = Math.min(nowSize, 2 * halfClear / widthPerPx);
358
+ }
359
+ }
347
360
  const nowY = cy - r * 0.42;
348
361
  g.append("text").attr("x", cx).attr("y", nowY + nowSize * 0.35).attr("text-anchor", "middle").attr("fill", paint.muted).attr("font-family", FONT_FAMILY).attr("font-size", nowSize).attr("font-weight", "bold").text(nowLabel);
349
362
  if (parsed.hasTarget) {
350
- const bandInner = r - stroke / 2;
351
363
  for (const f of [0, 0.25, 0.5, 0.75, 1]) {
352
364
  const isTarget = f === 1;
353
365
  const ang = Math.PI - f * Math.PI;
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  layoutClassDiagram
3
- } from "./chunk-BP4BT5TH.js";
3
+ } from "./chunk-CLQIXD2C.js";
4
4
  import {
5
5
  NOTE_BADGE_RADIUS,
6
6
  noteConnectorPoints,
7
7
  renderNoteBadge,
8
8
  renderNoteBox,
9
9
  renderNoteConnector
10
- } from "./chunk-5JO443MB.js";
10
+ } from "./chunk-HAH5X345.js";
11
11
  import {
12
12
  ScaleContext
13
13
  } from "./chunk-24YPR5EH.js";
@@ -18,7 +18,7 @@ import {
18
18
  EDGE_LABEL_KNOCKOUT_OPACITY,
19
19
  EDGE_STROKE_WIDTH,
20
20
  NODE_STROKE_WIDTH
21
- } from "./chunk-IIEI7VK5.js";
21
+ } from "./chunk-XCKNFFKM.js";
22
22
  import {
23
23
  extractExportSvg,
24
24
  runInExportContainer
@@ -1,7 +1,9 @@
1
1
  import {
2
- parseInlineMarkdown,
2
+ parseInlineMarkdown
3
+ } from "./chunk-S7W75MIM.js";
4
+ import {
3
5
  safeHref
4
- } from "./chunk-FWNJDD4U.js";
6
+ } from "./chunk-Z6DXWULM.js";
5
7
  import {
6
8
  ScaleContext
7
9
  } from "./chunk-24YPR5EH.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  wrapDescriptionLines
3
- } from "./chunk-WMIKC5XO.js";
3
+ } from "./chunk-NPWVOFB3.js";
4
4
  import {
5
5
  renderInlineText
6
- } from "./chunk-FWNJDD4U.js";
6
+ } from "./chunk-S7W75MIM.js";
7
7
  import {
8
8
  ScaleContext
9
9
  } from "./chunk-24YPR5EH.js";
@@ -13,7 +13,7 @@ import {
13
13
  COLLAPSE_BAR_HEIGHT,
14
14
  EDGE_DASH,
15
15
  HAIRLINE_DASH
16
- } from "./chunk-IIEI7VK5.js";
16
+ } from "./chunk-XCKNFFKM.js";
17
17
  import {
18
18
  layoutInlineHeader
19
19
  } from "./chunk-3DOQOXXI.js";
@@ -842,7 +842,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
842
842
  }
843
843
  msgToLastStep.set(step.messageIndex, si);
844
844
  });
845
- const findAssociatedLastStep = (note) => {
845
+ const closestPrecedingMsgIndex = (note) => {
846
846
  let closestMsgIndex = -1;
847
847
  let closestLine = -1;
848
848
  for (let mi = 0; mi < messages.length; mi++) {
@@ -852,9 +852,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
852
852
  closestMsgIndex = mi;
853
853
  }
854
854
  }
855
- if (closestMsgIndex >= 0 && hiddenMsgIndices.has(closestMsgIndex)) {
856
- return -1;
857
- }
855
+ return closestMsgIndex;
856
+ };
857
+ const isNoteHiddenByCollapse = (note) => {
858
+ const closestMsgIndex = closestPrecedingMsgIndex(note);
859
+ return closestMsgIndex >= 0 && hiddenMsgIndices.has(closestMsgIndex);
860
+ };
861
+ const findAssociatedLastStep = (note) => {
862
+ if (isNoteHiddenByCollapse(note)) return -1;
863
+ const closestMsgIndex = closestPrecedingMsgIndex(note);
858
864
  if (closestMsgIndex < 0) return -1;
859
865
  return msgToLastStep.get(closestMsgIndex) ?? -1;
860
866
  };
@@ -942,6 +948,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
942
948
  for (let i = 0; i < els.length; i++) {
943
949
  const el = els[i];
944
950
  if (isSequenceNote(el)) {
951
+ if (isNoteHiddenByCollapse(el)) {
952
+ while (i + 1 < els.length && isSequenceNote(els[i + 1])) i++;
953
+ continue;
954
+ }
945
955
  const firstOffset = noteOffsetBelow(el);
946
956
  let totalExtent = firstOffset;
947
957
  let j = i;
@@ -0,0 +1,23 @@
1
+ // src/utils/safe-href.ts
2
+ var ALLOWED_SCHEMES = ["http", "https", "mailto"];
3
+ var SCHEME_RE = /^([a-z][a-z0-9+.-]*):/i;
4
+ var LEADING_TRIM_RE = /^[\x00-\x20]+/;
5
+ var URL_STRIP_RE = /[\x09\x0A\x0D]/g;
6
+ function safeHref(url) {
7
+ if (typeof url !== "string") return null;
8
+ const trimmed = url.replace(URL_STRIP_RE, "").replace(LEADING_TRIM_RE, "");
9
+ if (trimmed.length === 0) return null;
10
+ const match = SCHEME_RE.exec(trimmed);
11
+ if (!match) {
12
+ return url;
13
+ }
14
+ const scheme = match[1].toLowerCase();
15
+ if (ALLOWED_SCHEMES.includes(scheme)) {
16
+ return url;
17
+ }
18
+ return null;
19
+ }
20
+
21
+ export {
22
+ safeHref
23
+ };
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-6EK55ART.js";
5
5
  import {
6
6
  noteBoxSize
7
- } from "./chunk-ZGHV4QLK.js";
7
+ } from "./chunk-RJNZHFNG.js";
8
8
  import {
9
9
  resolveNotes
10
10
  } from "./chunk-O753JUEZ.js";
@@ -79,7 +79,17 @@ interface ResolveUrlOptions {
79
79
  /** Origin of the Cloud API. */
80
80
  base?: string;
81
81
  }
82
- /** The URL to fetch a reference's source from. Always the current revision. */
82
+ /**
83
+ * The URL to fetch a reference's source from. Always the current revision.
84
+ *
85
+ * 🔴 Throws on an id that is not id-shaped, `undefined` and `''` included. The
86
+ * parsers above never produce one, so a bad id here means a caller built the
87
+ * reference by hand. Interpolating it anyway sent
88
+ * `GET /public/diagrams/undefined/source` to the Cloud API 56 times in one day
89
+ * from a Node process nobody could name, because a 404 on the server carries no
90
+ * stack. Refusing here makes that caller fail in its own process, where the
91
+ * stack names it (#772).
92
+ */
83
93
  declare function referenceSourceUrl(ref: CloudReference, options?: ResolveUrlOptions): string;
84
94
  /** The human share link for a reference — what a "view this in Diagrammo" link points at. */
85
95
  declare function referenceShareUrl(ref: CloudReference, options?: {
@@ -7,7 +7,7 @@ import {
7
7
  parseCloudReferenceUrl,
8
8
  referenceShareUrl,
9
9
  referenceSourceUrl
10
- } from "./chunk-23RWHHI5.js";
10
+ } from "./chunk-5NU7NII2.js";
11
11
  import "./chunk-5WRI5ZAA.js";
12
12
  export {
13
13
  CLOUD_API_BASE,