@diagrammo/dgmo 0.45.0 → 0.47.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 (250) hide show
  1. package/dist/advanced.cjs +31 -25
  2. package/dist/advanced.d.cts +3 -855
  3. package/dist/advanced.d.ts +3 -855
  4. package/dist/advanced.js +541 -70101
  5. package/dist/analyzer-UYKBXAKR.js +15 -0
  6. package/dist/auto.cjs +500 -341
  7. package/dist/auto.css +342 -159
  8. package/dist/auto.d.cts +10 -8
  9. package/dist/auto.d.ts +10 -8
  10. package/dist/auto.js +373 -265
  11. package/dist/auto.mjs +500 -341
  12. package/dist/block.cjs +31 -25
  13. package/dist/block.js +83 -69411
  14. package/dist/calculator-LDYQCDDY.js +11 -0
  15. package/dist/charts-d3-QOZHKTCT.js +25 -0
  16. package/dist/chunk-2DXMZRWJ.js +800 -0
  17. package/dist/chunk-2LACINWN.js +786 -0
  18. package/dist/chunk-33LCUH4H.js +893 -0
  19. package/dist/chunk-3CIOJ5YJ.js +35 -0
  20. package/dist/chunk-3HDBAXF5.js +86 -0
  21. package/dist/chunk-3JLKYGBQ.js +290 -0
  22. package/dist/chunk-3MQQNXPD.js +415 -0
  23. package/dist/chunk-3R5POCTZ.js +967 -0
  24. package/dist/chunk-3SMGIJDD.js +112 -0
  25. package/dist/chunk-44CM72SQ.js +409 -0
  26. package/dist/chunk-4GRI7MEB.js +1604 -0
  27. package/dist/chunk-4OHACBGX.js +606 -0
  28. package/dist/chunk-5DH3KYFH.js +21 -0
  29. package/dist/chunk-5MLYVTYM.js +76 -0
  30. package/dist/chunk-5QXL4NYS.js +553 -0
  31. package/dist/chunk-5WRI5ZAA.js +31 -0
  32. package/dist/chunk-6EK55ART.js +117 -0
  33. package/dist/chunk-6FZYCKXB.js +2065 -0
  34. package/dist/chunk-6HO5ZTXK.js +277 -0
  35. package/dist/chunk-6KFWC4CO.js +7 -0
  36. package/dist/chunk-6MTWIQVO.js +121 -0
  37. package/dist/chunk-6NYWYNBI.js +329 -0
  38. package/dist/chunk-6SM5NJQH.js +620 -0
  39. package/dist/chunk-725LDASD.js +422 -0
  40. package/dist/chunk-76OWZ3BA.js +130 -0
  41. package/dist/chunk-7JEGTRLD.js +338 -0
  42. package/dist/chunk-A44USUV7.js +923 -0
  43. package/dist/chunk-A4GOY3PE.js +25 -0
  44. package/dist/chunk-A4VFQH5G.js +251 -0
  45. package/dist/chunk-AELOBFW5.js +375 -0
  46. package/dist/chunk-AN2MOO5T.js +522 -0
  47. package/dist/chunk-BMR4CIEO.js +41 -0
  48. package/dist/chunk-BS7SV7C3.js +51 -0
  49. package/dist/chunk-BUPDIHML.js +613 -0
  50. package/dist/chunk-BUVJFRPY.js +887 -0
  51. package/dist/chunk-C3Z4DX3V.js +112 -0
  52. package/dist/chunk-CI2G3OEB.js +1818 -0
  53. package/dist/chunk-CKNVO24D.js +54 -0
  54. package/dist/chunk-CUVHQHZ7.js +391 -0
  55. package/dist/chunk-CXK4DZH3.js +114 -0
  56. package/dist/chunk-DKEPS5LH.js +315 -0
  57. package/dist/chunk-DU7W62N5.js +487 -0
  58. package/dist/chunk-DZAQBW35.js +751 -0
  59. package/dist/chunk-EB6FPQUP.js +316 -0
  60. package/dist/chunk-EMS2SK26.js +376 -0
  61. package/dist/chunk-ERLBPDGU.js +510 -0
  62. package/dist/chunk-EVCVDTXE.js +732 -0
  63. package/dist/chunk-FCIIHT24.js +384 -0
  64. package/dist/chunk-FFUN5D66.js +97 -0
  65. package/dist/chunk-FITOLXSX.js +714 -0
  66. package/dist/chunk-FMDSF3OV.js +68 -0
  67. package/dist/chunk-FNB3PNGF.js +410 -0
  68. package/dist/chunk-G2H74XJZ.js +2104 -0
  69. package/dist/chunk-GMR5MFHJ.js +339 -0
  70. package/dist/chunk-H6PI3KJT.js +305 -0
  71. package/dist/chunk-I46X5VUA.js +59 -0
  72. package/dist/chunk-I5IGB56C.js +255 -0
  73. package/dist/chunk-IH6ZMVQ5.js +3950 -0
  74. package/dist/chunk-IJZFN3HM.js +463 -0
  75. package/dist/chunk-IPWPWM6R.js +3079 -0
  76. package/dist/chunk-IYULMRBE.js +23 -0
  77. package/dist/chunk-IZMRON6C.js +167 -0
  78. package/dist/chunk-J5FIXQRT.js +813 -0
  79. package/dist/chunk-JED6ZDEE.js +73 -0
  80. package/dist/chunk-JEUJOX5X.js +136 -0
  81. package/dist/chunk-JM4LEPY3.js +436 -0
  82. package/dist/chunk-L3UHP6TD.js +101 -0
  83. package/dist/chunk-L66GEFB6.js +524 -0
  84. package/dist/chunk-LIOHW36Z.js +469 -0
  85. package/dist/chunk-LP7EC4MI.js +16 -0
  86. package/dist/chunk-MITI6NOL.js +548 -0
  87. package/dist/chunk-MO5YHHMQ.js +168 -0
  88. package/dist/chunk-MZSBFAPJ.js +885 -0
  89. package/dist/chunk-NNAAJVBU.js +9 -0
  90. package/dist/chunk-NOTA6TEI.js +47 -0
  91. package/dist/chunk-NURGB5Z4.js +335 -0
  92. package/dist/chunk-NX4EQS3U.js +1062 -0
  93. package/dist/chunk-NXMESY4J.js +1 -0
  94. package/dist/chunk-OJR3MQYV.js +1458 -0
  95. package/dist/chunk-OURHMODR.js +454 -0
  96. package/dist/chunk-P235SVAH.js +1057 -0
  97. package/dist/chunk-P4RH4OSP.js +1038 -0
  98. package/dist/chunk-P6T66CCZ.js +226 -0
  99. package/dist/chunk-PBAHEPLQ.js +223 -0
  100. package/dist/chunk-PBHE5UG6.js +1530 -0
  101. package/dist/chunk-PDKUANDZ.js +35 -0
  102. package/dist/chunk-PWIHZ6NJ.js +296 -0
  103. package/dist/chunk-QEBUY5NY.js +1685 -0
  104. package/dist/chunk-QLCCJEJI.js +253 -0
  105. package/dist/chunk-QOOUC2J5.js +52 -0
  106. package/dist/chunk-QYTPPDAO.js +402 -0
  107. package/dist/chunk-R3XI2VC7.js +801 -0
  108. package/dist/chunk-RCERWOJR.js +154 -0
  109. package/dist/chunk-RJE5JTVK.js +362 -0
  110. package/dist/chunk-RTLBSQ3G.js +1155 -0
  111. package/dist/chunk-RUNNI6VE.js +448 -0
  112. package/dist/chunk-RVMRZAPB.js +354 -0
  113. package/dist/chunk-RYEGFMUO.js +955 -0
  114. package/dist/chunk-RZCF2VVM.js +528 -0
  115. package/dist/chunk-SML67VPV.js +1025 -0
  116. package/dist/chunk-SQ7IY53Q.js +44 -0
  117. package/dist/chunk-SQAG3UXH.js +161 -0
  118. package/dist/chunk-SRTU6HMQ.js +1851 -0
  119. package/dist/chunk-T2OE3YQB.js +550 -0
  120. package/dist/chunk-T3UBLACL.js +299 -0
  121. package/dist/chunk-TLVIHFRO.js +296 -0
  122. package/dist/chunk-TYS6CSS5.js +52 -0
  123. package/dist/chunk-U6EQI3U6.js +130 -0
  124. package/dist/chunk-UJ4FJLBF.js +86 -0
  125. package/dist/chunk-ULOEATBZ.js +347 -0
  126. package/dist/chunk-UNCCZ3NU.js +127 -0
  127. package/dist/chunk-USZW2HHU.js +197 -0
  128. package/dist/chunk-UWN33VNX.js +1610 -0
  129. package/dist/chunk-UZ7EZW3B.js +40 -0
  130. package/dist/chunk-VOKGH627.js +37 -0
  131. package/dist/chunk-WBRZJOEC.js +204 -0
  132. package/dist/chunk-WCSOJFU3.js +380 -0
  133. package/dist/chunk-WCT3NYCV.js +160 -0
  134. package/dist/chunk-WVAJFAIA.js +529 -0
  135. package/dist/chunk-X5LCYCL5.js +394 -0
  136. package/dist/chunk-XAKEUMEJ.js +1256 -0
  137. package/dist/chunk-Y2ZE3MSG.js +590 -0
  138. package/dist/chunk-Y3J7VR52.js +646 -0
  139. package/dist/chunk-Y5QGVTVC.js +33 -0
  140. package/dist/chunk-YAA6IID3.js +548 -0
  141. package/dist/chunk-YGJTDXVR.js +692 -0
  142. package/dist/chunk-YRYKTYMQ.js +186 -0
  143. package/dist/chunk-YYVTBZZC.js +61 -0
  144. package/dist/chunk-Z5CGSFJB.js +647 -0
  145. package/dist/chunk-Z72XRXD4.js +787 -0
  146. package/dist/chunk-ZGR7ULFJ.js +978 -0
  147. package/dist/chunk-ZJEL4H2R.js +851 -0
  148. package/dist/chunk-ZO27FGJS.js +554 -0
  149. package/dist/cli.cjs +112 -112
  150. package/dist/collapse-CLPJD2RL.js +11 -0
  151. package/dist/collapse-SLINJQW6.js +9 -0
  152. package/dist/collapse-ZWUFFCK2.js +9 -0
  153. package/dist/compute-G55RG53N.js +8 -0
  154. package/dist/dgmo-router-DZCIDUO5.js +73 -0
  155. package/dist/dimensions-J3FHIMJ6.js +22 -0
  156. package/dist/element.cjs +492 -325
  157. package/dist/element.js +367 -259
  158. package/dist/element.mjs +492 -325
  159. package/dist/flowchart-parser-ZNSKIBVR.js +21 -0
  160. package/dist/flowchart-renderer-ZAEOL4DX.js +35 -0
  161. package/dist/index.cjs +34 -28
  162. package/dist/index.d.cts +3 -469
  163. package/dist/index.d.ts +3 -469
  164. package/dist/index.js +137 -69679
  165. package/dist/layout-46N4JJYK.js +17 -0
  166. package/dist/layout-5RA6QWR6.js +26 -0
  167. package/dist/layout-5T6RH5M6.js +14 -0
  168. package/dist/layout-7QXCNOVS.js +13 -0
  169. package/dist/layout-EE6GE46Y.js +17 -0
  170. package/dist/layout-G23F7LDH.js +9 -0
  171. package/dist/layout-HRBZPFCN.js +23 -0
  172. package/dist/layout-HWDSRX5E.js +20 -0
  173. package/dist/layout-HYZPWAK5.js +18 -0
  174. package/dist/layout-N76NR4ZO.js +19 -0
  175. package/dist/layout-OB3RI63S.js +14 -0
  176. package/dist/layout-S2QBYSK2.js +15 -0
  177. package/dist/layout-UM5F4DJB.js +15 -0
  178. package/dist/layout-search-OYV77HHS.js +1655 -0
  179. package/dist/load-data-EHDDZ5ZS.js +8 -0
  180. package/dist/palettes-PRJ2RI6D.js +56 -0
  181. package/dist/parser-2MFP2H2E.js +11 -0
  182. package/dist/parser-3STE62GB.js +21 -0
  183. package/dist/parser-4FL2CMDG.js +18 -0
  184. package/dist/parser-4OGOBJJ4.js +13 -0
  185. package/dist/parser-6BTRLKRZ.js +15 -0
  186. package/dist/parser-AQETEVRW.js +21 -0
  187. package/dist/parser-CFZD5IBL.js +12 -0
  188. package/dist/parser-FUSKFGZN.js +16 -0
  189. package/dist/parser-I6KXEYFK.js +13 -0
  190. package/dist/parser-IXSZZQHY.js +18 -0
  191. package/dist/parser-JAL2HD4I.js +13 -0
  192. package/dist/parser-KNSQSKQO.js +20 -0
  193. package/dist/parser-N5O4IHUN.js +13 -0
  194. package/dist/parser-NGU3ETO6.js +16 -0
  195. package/dist/parser-NRAI6O25.js +19 -0
  196. package/dist/parser-O5J4VBII.js +11 -0
  197. package/dist/parser-OV7NOBW6.js +21 -0
  198. package/dist/parser-PQ3POX45.js +23 -0
  199. package/dist/parser-TFZFBVWN.js +11 -0
  200. package/dist/parser-TRTHDFPP.js +18 -0
  201. package/dist/parser-VQP2UJ6W.js +11 -0
  202. package/dist/parser-XDVELHRK.js +13 -0
  203. package/dist/parser-XKRP7QKP.js +11 -0
  204. package/dist/parser-Y6WTBU76.js +14 -0
  205. package/dist/parser-ZSDPZYQP.js +12 -0
  206. package/dist/renderer-2XQQQ6QR.js +28 -0
  207. package/dist/renderer-5NDVNDAZ.js +26 -0
  208. package/dist/renderer-6EEZHMH4.js +26 -0
  209. package/dist/renderer-6W5462RB.js +43 -0
  210. package/dist/renderer-7MVLWWQD.js +22 -0
  211. package/dist/renderer-AWWAF6XD.js +33 -0
  212. package/dist/renderer-BYCNMBF7.js +34 -0
  213. package/dist/renderer-CLJHQPBJ.js +29 -0
  214. package/dist/renderer-GHPZMFU2.js +15 -0
  215. package/dist/renderer-JGBKUCZD.js +16 -0
  216. package/dist/renderer-KQB7UGDS.js +19 -0
  217. package/dist/renderer-PGX2TKVS.js +24 -0
  218. package/dist/renderer-QIWVHWZO.js +15 -0
  219. package/dist/renderer-RPOA5YPZ.js +35 -0
  220. package/dist/renderer-SMIBCRVB.js +24 -0
  221. package/dist/renderer-T7YKC5VP.js +28 -0
  222. package/dist/renderer-TOCOVLHS.js +37 -0
  223. package/dist/renderer-UWA5XS74.js +39 -0
  224. package/dist/renderer-VACVQYML.js +25 -0
  225. package/dist/renderer-W4M6QLIJ.js +37 -0
  226. package/dist/renderer-WZRREQ2P.js +23 -0
  227. package/dist/renderer-YY2OSF6E.js +27 -0
  228. package/dist/renderer-Z7I3W2PP.js +24 -0
  229. package/dist/renderer-ZGNTUWC4.js +22 -0
  230. package/dist/renderer-ZTR3DER3.js +19 -0
  231. package/dist/renderer-radial-C32GPJQ7.js +21 -0
  232. package/dist/resolver-NOAA7ID7.js +13 -0
  233. package/dist/state-collapse-HCGD6IVJ.js +8 -0
  234. package/dist/state-parser-Z2V23ATR.js +18 -0
  235. package/dist/state-renderer-RFYHSP4K.js +34 -0
  236. package/dist/themes-1-CuKpeH.d.cts +908 -0
  237. package/dist/themes-1-CuKpeH.d.ts +908 -0
  238. package/package.json +1 -1
  239. package/src/auto/index.ts +55 -57
  240. package/src/auto/shared.ts +142 -142
  241. package/src/auto/styles.ts +44 -199
  242. package/src/c4/renderer.ts +2 -2
  243. package/src/element/index.ts +29 -21
  244. package/src/infra/renderer.ts +1 -1
  245. package/src/render.ts +34 -14
  246. package/src/sitemap/renderer.ts +1 -1
  247. package/src/timeline/renderer.ts +1 -1
  248. package/src/utils/hover-styles.ts +6 -1
  249. package/src/utils/legend-d3.ts +1 -4
  250. package/src/utils/legend-svg.ts +1 -1
@@ -0,0 +1,1685 @@
1
+ import {
2
+ LEGEND_CAPSULE_PAD,
3
+ LEGEND_DOT_R,
4
+ LEGEND_ENTRY_DOT_GAP,
5
+ LEGEND_ENTRY_FONT_SIZE,
6
+ LEGEND_ENTRY_TRAIL,
7
+ LEGEND_HEIGHT,
8
+ LEGEND_PILL_FONT_SIZE,
9
+ LEGEND_PILL_PAD,
10
+ measureLegendText
11
+ } from "./chunk-QOOUC2J5.js";
12
+ import {
13
+ measureText,
14
+ wrapTextToWidth
15
+ } from "./chunk-WCT3NYCV.js";
16
+ import {
17
+ tagAttrKey
18
+ } from "./chunk-QLCCJEJI.js";
19
+
20
+ // src/c4/layout.ts
21
+ import dagre from "@dagrejs/dagre";
22
+ var gNode = (g, name) => g.node(name);
23
+ var gEdge = (g, v, w) => g.edge(v, w);
24
+ var MIN_NODE_WIDTH = 160;
25
+ var MAX_NODE_WIDTH = 260;
26
+ var TYPE_LABEL_HEIGHT = 18;
27
+ var DIVIDER_GAP = 6;
28
+ var NAME_HEIGHT = 20;
29
+ var NAME_FONT_SIZE = 14;
30
+ var DESC_LINE_HEIGHT = 16;
31
+ var DESC_FONT_SIZE = 11;
32
+ var CARD_V_PAD = 14;
33
+ var CARD_H_PAD = 20;
34
+ var META_LINE_HEIGHT = 16;
35
+ var META_FONT_SIZE = 11;
36
+ var MARGIN = 40;
37
+ var BOUNDARY_PAD = 40;
38
+ var GROUP_BOUNDARY_PAD = 24;
39
+ var EDGE_NODE_COLLISION_WEIGHT = 5e3;
40
+ function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
41
+ let penalty = 0;
42
+ for (const edge of edgeList) {
43
+ const sx = nodePositions.get(edge.source);
44
+ const tx = nodePositions.get(edge.target);
45
+ if (sx == null || tx == null) continue;
46
+ const dist = Math.abs(sx - tx);
47
+ const weight = Math.min(
48
+ degrees.get(edge.source) ?? 1,
49
+ degrees.get(edge.target) ?? 1
50
+ );
51
+ penalty += dist * weight;
52
+ }
53
+ if (nodeGeometry) {
54
+ for (const edge of edgeList) {
55
+ const geomA = nodeGeometry.get(edge.source);
56
+ const geomB = nodeGeometry.get(edge.target);
57
+ if (!geomA || !geomB) continue;
58
+ const ax = nodePositions.get(edge.source) ?? 0;
59
+ const bx = nodePositions.get(edge.target) ?? 0;
60
+ const ay = geomA.y;
61
+ const by = geomB.y;
62
+ if (ay === by) continue;
63
+ const edgeMinX = Math.min(ax, bx);
64
+ const edgeMaxX = Math.max(ax, bx);
65
+ const edgeMinY = Math.min(ay, by);
66
+ const edgeMaxY = Math.max(ay, by);
67
+ for (const [name, geomC] of nodeGeometry) {
68
+ if (name === edge.source || name === edge.target) continue;
69
+ const cx = nodePositions.get(name) ?? 0;
70
+ const cy = geomC.y;
71
+ const hw = geomC.width / 2;
72
+ const hh = geomC.height / 2;
73
+ if (cx + hw > edgeMinX && cx - hw < edgeMaxX && cy + hh > edgeMinY && cy - hh < edgeMaxY) {
74
+ penalty += EDGE_NODE_COLLISION_WEIGHT;
75
+ }
76
+ }
77
+ }
78
+ }
79
+ return penalty;
80
+ }
81
+ function reduceCrossings(g, edgeList, nodeGroupMap) {
82
+ if (edgeList.length < 2) return;
83
+ const degrees = /* @__PURE__ */ new Map();
84
+ for (const edge of edgeList) {
85
+ degrees.set(edge.source, (degrees.get(edge.source) ?? 0) + 1);
86
+ degrees.set(edge.target, (degrees.get(edge.target) ?? 0) + 1);
87
+ }
88
+ const nodeGeometry = /* @__PURE__ */ new Map();
89
+ for (const name of g.nodes()) {
90
+ const pos = gNode(g, name);
91
+ if (pos)
92
+ nodeGeometry.set(name, {
93
+ y: pos.y,
94
+ width: pos.width,
95
+ height: pos.height
96
+ });
97
+ }
98
+ const rankMap = /* @__PURE__ */ new Map();
99
+ for (const name of g.nodes()) {
100
+ const pos = gNode(g, name);
101
+ if (!pos) continue;
102
+ const rankY = Math.round(pos.y);
103
+ if (!rankMap.has(rankY)) rankMap.set(rankY, []);
104
+ rankMap.get(rankY).push(name);
105
+ }
106
+ for (const [, rankNodes] of rankMap) {
107
+ rankNodes.sort((a, b) => gNode(g, a).x - gNode(g, b).x);
108
+ }
109
+ let anyMoved = false;
110
+ for (const [, rankNodes] of rankMap) {
111
+ if (rankNodes.length < 2) continue;
112
+ const partitions = [];
113
+ if (nodeGroupMap && nodeGroupMap.size > 0) {
114
+ const groupBuckets = /* @__PURE__ */ new Map();
115
+ const ungrouped = [];
116
+ for (const name of rankNodes) {
117
+ const grp = nodeGroupMap.get(name);
118
+ if (grp) {
119
+ if (!groupBuckets.has(grp)) groupBuckets.set(grp, []);
120
+ groupBuckets.get(grp).push(name);
121
+ } else {
122
+ ungrouped.push(name);
123
+ }
124
+ }
125
+ for (const bucket of groupBuckets.values()) {
126
+ if (bucket.length >= 2) partitions.push(bucket);
127
+ }
128
+ if (ungrouped.length >= 2) partitions.push(ungrouped);
129
+ } else {
130
+ partitions.push(rankNodes);
131
+ }
132
+ for (const partition of partitions) {
133
+ if (partition.length < 2) continue;
134
+ const xSlots = partition.map((name) => gNode(g, name).x).sort((a, b) => a - b);
135
+ const basePositions = /* @__PURE__ */ new Map();
136
+ for (const name of g.nodes()) {
137
+ const pos = gNode(g, name);
138
+ if (pos) basePositions.set(name, pos.x);
139
+ }
140
+ const currentPenalty = computeEdgePenalty(
141
+ edgeList,
142
+ basePositions,
143
+ degrees,
144
+ nodeGeometry
145
+ );
146
+ let bestPerm = [...partition];
147
+ let bestPenalty = currentPenalty;
148
+ if (partition.length <= 8) {
149
+ const perms = permutations(partition);
150
+ for (const perm of perms) {
151
+ const testPositions = new Map(basePositions);
152
+ for (let i = 0; i < perm.length; i++) {
153
+ testPositions.set(perm[i], xSlots[i]);
154
+ }
155
+ const penalty = computeEdgePenalty(
156
+ edgeList,
157
+ testPositions,
158
+ degrees,
159
+ nodeGeometry
160
+ );
161
+ if (penalty < bestPenalty) {
162
+ bestPenalty = penalty;
163
+ bestPerm = [...perm];
164
+ }
165
+ }
166
+ } else {
167
+ const workingOrder = [...partition];
168
+ let improved = true;
169
+ let passes = 0;
170
+ while (improved && passes < 10) {
171
+ improved = false;
172
+ passes++;
173
+ for (let i = 0; i < workingOrder.length - 1; i++) {
174
+ const testPositions = new Map(basePositions);
175
+ for (let k = 0; k < workingOrder.length; k++) {
176
+ testPositions.set(workingOrder[k], xSlots[k]);
177
+ }
178
+ const before = computeEdgePenalty(
179
+ edgeList,
180
+ testPositions,
181
+ degrees,
182
+ nodeGeometry
183
+ );
184
+ [workingOrder[i], workingOrder[i + 1]] = [
185
+ workingOrder[i + 1],
186
+ workingOrder[i]
187
+ ];
188
+ const testPositions2 = new Map(basePositions);
189
+ for (let k = 0; k < workingOrder.length; k++) {
190
+ testPositions2.set(workingOrder[k], xSlots[k]);
191
+ }
192
+ const after = computeEdgePenalty(
193
+ edgeList,
194
+ testPositions2,
195
+ degrees,
196
+ nodeGeometry
197
+ );
198
+ if (after < before) {
199
+ improved = true;
200
+ if (after < bestPenalty) {
201
+ bestPenalty = after;
202
+ bestPerm = [...workingOrder];
203
+ }
204
+ } else {
205
+ [workingOrder[i], workingOrder[i + 1]] = [
206
+ workingOrder[i + 1],
207
+ workingOrder[i]
208
+ ];
209
+ }
210
+ }
211
+ }
212
+ }
213
+ if (bestPerm.some((name, i) => name !== partition[i])) {
214
+ for (let i = 0; i < bestPerm.length; i++) {
215
+ gNode(g, bestPerm[i]).x = xSlots[i];
216
+ const rankIdx = rankNodes.indexOf(partition[i]);
217
+ if (rankIdx >= 0) rankNodes[rankIdx] = bestPerm[i];
218
+ }
219
+ anyMoved = true;
220
+ }
221
+ }
222
+ }
223
+ if (anyMoved) {
224
+ for (const edge of edgeList) {
225
+ const edgeData = gEdge(g, edge.source, edge.target);
226
+ if (!edgeData) continue;
227
+ const srcPos = gNode(g, edge.source);
228
+ const tgtPos = gNode(g, edge.target);
229
+ if (!srcPos || !tgtPos) continue;
230
+ const srcBottom = { x: srcPos.x, y: srcPos.y + srcPos.height / 2 };
231
+ const tgtTop = { x: tgtPos.x, y: tgtPos.y - tgtPos.height / 2 };
232
+ const midY = (srcBottom.y + tgtTop.y) / 2;
233
+ edgeData.points = [
234
+ srcBottom,
235
+ { x: srcBottom.x, y: midY },
236
+ { x: tgtTop.x, y: midY },
237
+ tgtTop
238
+ ];
239
+ }
240
+ }
241
+ }
242
+ function permutations(arr) {
243
+ const result = [];
244
+ const a = [...arr];
245
+ const n = a.length;
246
+ const c = new Array(n).fill(0);
247
+ result.push([...a]);
248
+ let i = 0;
249
+ while (i < n) {
250
+ if (c[i] < i) {
251
+ if (i % 2 === 0) {
252
+ [a[0], a[i]] = [a[i], a[0]];
253
+ } else {
254
+ [a[c[i]], a[i]] = [a[i], a[c[i]]];
255
+ }
256
+ result.push([...a]);
257
+ c[i]++;
258
+ i = 0;
259
+ } else {
260
+ c[i] = 0;
261
+ i++;
262
+ }
263
+ }
264
+ return result;
265
+ }
266
+ function buildOwnershipMap(elements) {
267
+ const map = /* @__PURE__ */ new Map();
268
+ function walk(el, ancestor) {
269
+ map.set(el.name, ancestor);
270
+ for (const child of el.children) {
271
+ walk(child, ancestor);
272
+ }
273
+ for (const group of el.groups) {
274
+ for (const child of group.children) {
275
+ walk(child, ancestor);
276
+ }
277
+ }
278
+ }
279
+ for (const el of elements) {
280
+ walk(el, el.name);
281
+ }
282
+ return map;
283
+ }
284
+ function collectAllRelationships(elements, _ownerMap) {
285
+ const result = [];
286
+ function walk(el) {
287
+ for (const rel of el.relationships) {
288
+ result.push({ sourceName: el.name, rel });
289
+ }
290
+ for (const child of el.children) {
291
+ walk(child);
292
+ }
293
+ for (const group of el.groups) {
294
+ for (const child of group.children) {
295
+ walk(child);
296
+ }
297
+ }
298
+ }
299
+ for (const el of elements) {
300
+ walk(el);
301
+ }
302
+ return result;
303
+ }
304
+ function rollUpContextRelationships(parsed) {
305
+ const ownerMap = buildOwnershipMap(parsed.elements);
306
+ const allRels = collectAllRelationships(parsed.elements, ownerMap);
307
+ const topLevelNames = new Set(parsed.elements.map((e) => e.name));
308
+ const explicitKeys = /* @__PURE__ */ new Set();
309
+ const explicit = [];
310
+ const nested = [];
311
+ for (const { sourceName, rel } of allRels) {
312
+ const sourceAncestor = ownerMap.get(sourceName) ?? sourceName;
313
+ const targetAncestor = ownerMap.get(rel.target) ?? rel.target;
314
+ if (sourceAncestor === targetAncestor) continue;
315
+ const entry = {
316
+ sourceName: sourceAncestor,
317
+ targetName: targetAncestor,
318
+ ...rel.label !== void 0 && { label: rel.label },
319
+ ...rel.technology !== void 0 && { technology: rel.technology },
320
+ arrowType: rel.arrowType,
321
+ lineNumber: rel.lineNumber
322
+ };
323
+ if (topLevelNames.has(sourceName) && sourceName === sourceAncestor) {
324
+ const key = `${sourceAncestor}\u2192${targetAncestor}`;
325
+ explicitKeys.add(key);
326
+ explicit.push(entry);
327
+ } else {
328
+ nested.push(entry);
329
+ }
330
+ }
331
+ const result = [...explicit];
332
+ const seenKeys = new Set(explicitKeys);
333
+ for (const rel of nested) {
334
+ const key = `${rel.sourceName}\u2192${rel.targetName}`;
335
+ if (!seenKeys.has(key)) {
336
+ seenKeys.add(key);
337
+ result.push(rel);
338
+ }
339
+ }
340
+ return result;
341
+ }
342
+ function resolveNodeColor(el, tagGroups, activeGroupName, ancestors) {
343
+ const colorMeta = el.metadata["color"];
344
+ if (colorMeta) return colorMeta;
345
+ if (!activeGroupName) return void 0;
346
+ const group = tagGroups.find(
347
+ (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
348
+ );
349
+ if (!group) return void 0;
350
+ const key = tagAttrKey(group.name);
351
+ let metaValue = el.metadata[key];
352
+ if (!metaValue && ancestors) {
353
+ for (const ancestor of ancestors) {
354
+ metaValue = ancestor.metadata[key];
355
+ if (metaValue) break;
356
+ }
357
+ }
358
+ const resolvedValue = metaValue ?? group.defaultValue;
359
+ if (!resolvedValue) return "#999999";
360
+ return group.entries.find(
361
+ (e) => e.value.toLowerCase() === resolvedValue.toLowerCase()
362
+ )?.color ?? "#999999";
363
+ }
364
+ var META_EXCLUDE_KEYS = /* @__PURE__ */ new Set([
365
+ "description",
366
+ "tech",
367
+ "technology",
368
+ "is a"
369
+ ]);
370
+ function collectCardMetadata(metadata) {
371
+ const entries = [];
372
+ const tech = metadata["tech"] ?? metadata["technology"];
373
+ if (tech) entries.push({ key: "Technology", value: tech });
374
+ for (const [k, v] of Object.entries(metadata)) {
375
+ if (META_EXCLUDE_KEYS.has(k.toLowerCase())) continue;
376
+ const displayKey = k.charAt(0).toUpperCase() + k.slice(1);
377
+ entries.push({ key: displayKey, value: v });
378
+ }
379
+ return entries;
380
+ }
381
+ function computeC4NodeDimensions(el, options) {
382
+ const nameWidth = measureText(el.name, NAME_FONT_SIZE) + CARD_H_PAD * 2;
383
+ let width = Math.max(MIN_NODE_WIDTH, Math.min(MAX_NODE_WIDTH, nameWidth));
384
+ if (options?.showTechnology) {
385
+ let height2 = CARD_V_PAD + NAME_HEIGHT;
386
+ const desc2 = el.description?.join("\n");
387
+ if (desc2) {
388
+ const contentWidth = width - CARD_H_PAD * 2;
389
+ const lines = wrapTextToWidth(desc2, DESC_FONT_SIZE, contentWidth);
390
+ height2 += lines.length * DESC_LINE_HEIGHT;
391
+ }
392
+ const metaEntries = collectCardMetadata(el.metadata);
393
+ if (metaEntries.length > 0) {
394
+ height2 += DIVIDER_GAP;
395
+ height2 += metaEntries.length * META_LINE_HEIGHT;
396
+ const maxMetaWidth = Math.max(
397
+ ...metaEntries.map(
398
+ (e) => measureText(`${e.key}: ${e.value}`, META_FONT_SIZE) + CARD_H_PAD * 2
399
+ )
400
+ );
401
+ if (maxMetaWidth > width) width = Math.min(MAX_NODE_WIDTH, maxMetaWidth);
402
+ }
403
+ height2 += CARD_V_PAD;
404
+ return { width, height: height2 };
405
+ }
406
+ let height = CARD_V_PAD + TYPE_LABEL_HEIGHT + DIVIDER_GAP + NAME_HEIGHT;
407
+ const desc = el.description?.join("\n");
408
+ if (desc) {
409
+ const contentWidth = width - CARD_H_PAD * 2;
410
+ const lines = wrapTextToWidth(desc, DESC_FONT_SIZE, contentWidth);
411
+ height += lines.length * DESC_LINE_HEIGHT;
412
+ }
413
+ height += CARD_V_PAD;
414
+ return { width, height };
415
+ }
416
+ function computeLegendGroups(tagGroups) {
417
+ const result = [];
418
+ for (const group of tagGroups) {
419
+ const entries = [];
420
+ for (const entry of group.entries) {
421
+ entries.push({ value: entry.value, color: entry.color });
422
+ }
423
+ if (entries.length === 0) continue;
424
+ const nameW = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD * 2;
425
+ let capsuleW = LEGEND_CAPSULE_PAD;
426
+ for (const e of entries) {
427
+ capsuleW += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + measureLegendText(e.value, LEGEND_ENTRY_FONT_SIZE) + LEGEND_ENTRY_TRAIL;
428
+ }
429
+ capsuleW += LEGEND_CAPSULE_PAD;
430
+ result.push({
431
+ name: group.name,
432
+ entries,
433
+ x: 0,
434
+ y: 0,
435
+ width: nameW + capsuleW,
436
+ height: LEGEND_HEIGHT
437
+ });
438
+ }
439
+ return result;
440
+ }
441
+ function computeAdaptiveSpacing(edges) {
442
+ const outDegree = /* @__PURE__ */ new Map();
443
+ for (const edge of edges) {
444
+ if (edge.label || edge.technology) {
445
+ outDegree.set(edge.sourceName, (outDegree.get(edge.sourceName) ?? 0) + 1);
446
+ }
447
+ }
448
+ const maxFanOut = Math.max(0, ...outDegree.values());
449
+ const nodesep = Math.max(80, 60 + maxFanOut * 20);
450
+ const ranksep = Math.max(140, 100 + maxFanOut * 35);
451
+ const edgesep = Math.max(30, 20 + maxFanOut * 8);
452
+ return { nodesep, ranksep, edgesep };
453
+ }
454
+ function layoutC4Context(parsed, activeTagGroup) {
455
+ const contextElements = parsed.elements.filter(
456
+ (el) => el.type === "person" || el.type === "system"
457
+ );
458
+ if (contextElements.length === 0) {
459
+ return {
460
+ nodes: [],
461
+ edges: [],
462
+ legend: [],
463
+ groupBoundaries: [],
464
+ width: 0,
465
+ height: 0
466
+ };
467
+ }
468
+ const contextRels = rollUpContextRelationships(parsed);
469
+ const spacing = computeAdaptiveSpacing(contextRels);
470
+ const g = new dagre.graphlib.Graph();
471
+ g.setGraph({
472
+ rankdir: "TB",
473
+ nodesep: spacing.nodesep,
474
+ ranksep: spacing.ranksep,
475
+ edgesep: spacing.edgesep
476
+ });
477
+ g.setDefaultEdgeLabel(() => ({}));
478
+ const nameToElement = /* @__PURE__ */ new Map();
479
+ for (const el of contextElements) {
480
+ nameToElement.set(el.name, el);
481
+ const dims = computeC4NodeDimensions(el);
482
+ g.setNode(el.name, { width: dims.width, height: dims.height });
483
+ }
484
+ const validRels = [];
485
+ for (const rel of contextRels) {
486
+ if (nameToElement.has(rel.sourceName) && nameToElement.has(rel.targetName)) {
487
+ validRels.push(rel);
488
+ g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
489
+ }
490
+ }
491
+ dagre.layout(g);
492
+ reduceCrossings(
493
+ g,
494
+ validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
495
+ );
496
+ const nodes = contextElements.map(
497
+ (el) => {
498
+ const pos = g.node(el.name);
499
+ const color = resolveNodeColor(
500
+ el,
501
+ parsed.tagGroups,
502
+ activeTagGroup ?? null
503
+ );
504
+ const hasContainers = el.children.some((c) => c.type === "container") || el.groups.some((g2) => g2.children.some((c) => c.type === "container"));
505
+ const description = el.description?.join("\n");
506
+ const drillable = hasContainers || el.importPath ? true : void 0;
507
+ return {
508
+ id: el.name,
509
+ name: el.name,
510
+ type: el.type,
511
+ ...description !== void 0 && { description },
512
+ metadata: el.metadata,
513
+ lineNumber: el.lineNumber,
514
+ ...color !== void 0 && { color },
515
+ ...drillable !== void 0 && { drillable },
516
+ ...el.importPath !== void 0 && { importPath: el.importPath },
517
+ x: pos.x,
518
+ y: pos.y,
519
+ width: pos.width,
520
+ height: pos.height
521
+ };
522
+ }
523
+ );
524
+ const edges = validRels.map(
525
+ (rel) => {
526
+ const edgeData = g.edge(rel.sourceName, rel.targetName);
527
+ return {
528
+ source: rel.sourceName,
529
+ target: rel.targetName,
530
+ arrowType: rel.arrowType,
531
+ ...rel.label !== void 0 && { label: rel.label },
532
+ ...rel.technology !== void 0 && { technology: rel.technology },
533
+ lineNumber: rel.lineNumber,
534
+ points: edgeData?.points ?? []
535
+ };
536
+ }
537
+ );
538
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
539
+ for (const node of nodes) {
540
+ const left = node.x - node.width / 2;
541
+ const top = node.y - node.height / 2;
542
+ const right = node.x + node.width / 2;
543
+ const bottom = node.y + node.height / 2;
544
+ if (left < minX) minX = left;
545
+ if (top < minY) minY = top;
546
+ if (right > maxX) maxX = right;
547
+ if (bottom > maxY) maxY = bottom;
548
+ }
549
+ for (const edge of edges) {
550
+ for (const pt of edge.points) {
551
+ if (pt.x < minX) minX = pt.x;
552
+ if (pt.y < minY) minY = pt.y;
553
+ if (pt.x > maxX) maxX = pt.x;
554
+ if (pt.y > maxY) maxY = pt.y;
555
+ }
556
+ }
557
+ if (nodes.length > 0) {
558
+ const shiftX = MARGIN - minX;
559
+ const shiftY = MARGIN - minY;
560
+ for (const node of nodes) {
561
+ node.x += shiftX;
562
+ node.y += shiftY;
563
+ }
564
+ for (const edge of edges) {
565
+ for (const pt of edge.points) {
566
+ pt.x += shiftX;
567
+ pt.y += shiftY;
568
+ }
569
+ }
570
+ }
571
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN * 2 : 0;
572
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN * 2 : 0;
573
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
574
+ if (legendGroups.length > 0) {
575
+ const legendY = totalHeight + MARGIN;
576
+ let legendX = MARGIN;
577
+ for (const lg of legendGroups) {
578
+ lg.x = legendX;
579
+ lg.y = legendY;
580
+ legendX += lg.width + 12;
581
+ }
582
+ const legendRight = legendX;
583
+ const legendBottom = legendY + LEGEND_HEIGHT;
584
+ if (legendRight > totalWidth) totalWidth = legendRight;
585
+ if (legendBottom > totalHeight) totalHeight = legendBottom;
586
+ }
587
+ return {
588
+ nodes,
589
+ edges,
590
+ legend: legendGroups,
591
+ groupBoundaries: [],
592
+ width: totalWidth,
593
+ height: totalHeight
594
+ };
595
+ }
596
+ function layoutC4Containers(parsed, systemName, activeTagGroup) {
597
+ const system = parsed.elements.find(
598
+ (el) => el.name.toLowerCase() === systemName.toLowerCase()
599
+ );
600
+ if (!system) {
601
+ return {
602
+ nodes: [],
603
+ edges: [],
604
+ legend: [],
605
+ groupBoundaries: [],
606
+ width: 0,
607
+ height: 0
608
+ };
609
+ }
610
+ const containers = [];
611
+ for (const child of system.children) {
612
+ if (child.type === "container") containers.push(child);
613
+ }
614
+ for (const group of system.groups) {
615
+ for (const child of group.children) {
616
+ if (child.type === "container") containers.push(child);
617
+ }
618
+ }
619
+ if (containers.length === 0) {
620
+ return {
621
+ nodes: [],
622
+ edges: [],
623
+ legend: [],
624
+ groupBoundaries: [],
625
+ width: 0,
626
+ height: 0
627
+ };
628
+ }
629
+ const containerNames = new Set(containers.map((c) => c.name.toLowerCase()));
630
+ const topElementsByName = /* @__PURE__ */ new Map();
631
+ for (const el of parsed.elements) {
632
+ topElementsByName.set(el.name.toLowerCase(), el);
633
+ }
634
+ const externalNames = /* @__PURE__ */ new Set();
635
+ for (const container of containers) {
636
+ for (const rel of container.relationships) {
637
+ const targetLower = rel.target.toLowerCase();
638
+ if (!containerNames.has(targetLower)) {
639
+ externalNames.add(targetLower);
640
+ }
641
+ }
642
+ }
643
+ const ownerMap = buildOwnershipMap(parsed.elements);
644
+ const allRels = collectAllRelationships(parsed.elements, ownerMap);
645
+ for (const { sourceName, rel } of allRels) {
646
+ const sourceAncestor = ownerMap.get(sourceName) ?? sourceName;
647
+ if (sourceAncestor.toLowerCase() === system.name.toLowerCase()) continue;
648
+ if (containerNames.has(rel.target.toLowerCase())) {
649
+ externalNames.add(sourceAncestor.toLowerCase());
650
+ }
651
+ }
652
+ const externals = [];
653
+ for (const name of externalNames) {
654
+ const el = topElementsByName.get(name);
655
+ if (el) externals.push(el);
656
+ }
657
+ const elementToGroup = /* @__PURE__ */ new Map();
658
+ for (const group of system.groups) {
659
+ for (const child of group.children) {
660
+ if (child.type === "container") {
661
+ elementToGroup.set(child.name, {
662
+ name: group.name,
663
+ lineNumber: group.lineNumber
664
+ });
665
+ }
666
+ }
667
+ }
668
+ const hasGroups = elementToGroup.size > 0;
669
+ const g = hasGroups ? new dagre.graphlib.Graph({ compound: true }) : new dagre.graphlib.Graph();
670
+ g.setDefaultEdgeLabel(() => ({}));
671
+ if (hasGroups) {
672
+ const seenGroups = /* @__PURE__ */ new Set();
673
+ for (const { name } of elementToGroup.values()) {
674
+ if (!seenGroups.has(name)) {
675
+ seenGroups.add(name);
676
+ g.setNode("__group_" + name, {});
677
+ }
678
+ }
679
+ }
680
+ const nameToElement = /* @__PURE__ */ new Map();
681
+ for (const el of containers) {
682
+ nameToElement.set(el.name, el);
683
+ const dims = computeC4NodeDimensions(el, { showTechnology: true });
684
+ g.setNode(el.name, { width: dims.width, height: dims.height });
685
+ const grp = elementToGroup.get(el.name);
686
+ if (grp) {
687
+ g.setParent(el.name, "__group_" + grp.name);
688
+ }
689
+ }
690
+ for (const el of externals) {
691
+ nameToElement.set(el.name, el);
692
+ const dims = computeC4NodeDimensions(el);
693
+ g.setNode(el.name, { width: dims.width, height: dims.height });
694
+ }
695
+ const containerRels = [];
696
+ const seenEdgeKeys = /* @__PURE__ */ new Set();
697
+ for (const container of containers) {
698
+ for (const rel of container.relationships) {
699
+ const targetEl = nameToElement.get(rel.target);
700
+ if (!targetEl) continue;
701
+ const key = `${container.name}\u2192${rel.target}`;
702
+ if (!seenEdgeKeys.has(key)) {
703
+ seenEdgeKeys.add(key);
704
+ containerRels.push({
705
+ sourceName: container.name,
706
+ targetName: rel.target,
707
+ ...rel.label !== void 0 && { label: rel.label },
708
+ ...rel.technology !== void 0 && { technology: rel.technology },
709
+ arrowType: rel.arrowType,
710
+ lineNumber: rel.lineNumber
711
+ });
712
+ }
713
+ }
714
+ }
715
+ for (const { sourceName, rel } of allRels) {
716
+ const sourceAncestor = ownerMap.get(sourceName) ?? sourceName;
717
+ if (sourceAncestor.toLowerCase() === system.name.toLowerCase()) continue;
718
+ if (containerNames.has(rel.target.toLowerCase())) {
719
+ const externalEl = nameToElement.get(sourceAncestor);
720
+ if (!externalEl) continue;
721
+ const key = `${sourceAncestor}\u2192${rel.target}`;
722
+ if (!seenEdgeKeys.has(key)) {
723
+ seenEdgeKeys.add(key);
724
+ containerRels.push({
725
+ sourceName: sourceAncestor,
726
+ targetName: rel.target,
727
+ ...rel.label !== void 0 && { label: rel.label },
728
+ ...rel.technology !== void 0 && { technology: rel.technology },
729
+ arrowType: rel.arrowType,
730
+ lineNumber: rel.lineNumber
731
+ });
732
+ }
733
+ }
734
+ }
735
+ const spacing = computeAdaptiveSpacing(containerRels);
736
+ g.setGraph({
737
+ rankdir: "TB",
738
+ nodesep: spacing.nodesep,
739
+ ranksep: spacing.ranksep,
740
+ edgesep: spacing.edgesep
741
+ });
742
+ for (const rel of containerRels) {
743
+ if (nameToElement.has(rel.sourceName) && nameToElement.has(rel.targetName)) {
744
+ g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
745
+ }
746
+ }
747
+ dagre.layout(g);
748
+ const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
749
+ reduceCrossings(
750
+ g,
751
+ containerRels.filter(
752
+ (r) => nameToElement.has(r.sourceName) && nameToElement.has(r.targetName)
753
+ ).map((r) => ({ source: r.sourceName, target: r.targetName })),
754
+ nodeGroupMap
755
+ );
756
+ const nodes = [];
757
+ for (const el of containers) {
758
+ const pos = g.node(el.name);
759
+ const color = resolveNodeColor(
760
+ el,
761
+ parsed.tagGroups,
762
+ activeTagGroup ?? null
763
+ );
764
+ const tech = el.metadata["tech"] ?? el.metadata["technology"];
765
+ const hasComponents = el.children.some((c) => c.type === "component") || el.groups.some((grp) => grp.children.some((c) => c.type === "component"));
766
+ const description = el.description?.join("\n");
767
+ const drillable = hasComponents || el.importPath ? true : void 0;
768
+ nodes.push({
769
+ id: el.name,
770
+ name: el.name,
771
+ type: "container",
772
+ ...description !== void 0 && { description },
773
+ metadata: el.metadata,
774
+ lineNumber: el.lineNumber,
775
+ ...color !== void 0 && { color },
776
+ shape: el.shape,
777
+ ...tech !== void 0 && { technology: tech },
778
+ ...drillable !== void 0 && { drillable },
779
+ ...el.importPath !== void 0 && { importPath: el.importPath },
780
+ x: pos.x,
781
+ y: pos.y,
782
+ width: pos.width,
783
+ height: pos.height
784
+ });
785
+ }
786
+ for (const el of externals) {
787
+ const pos = g.node(el.name);
788
+ const color = resolveNodeColor(
789
+ el,
790
+ parsed.tagGroups,
791
+ activeTagGroup ?? null
792
+ );
793
+ const description = el.description?.join("\n");
794
+ nodes.push({
795
+ id: el.name,
796
+ name: el.name,
797
+ type: el.type,
798
+ ...description !== void 0 && { description },
799
+ metadata: el.metadata,
800
+ lineNumber: el.lineNumber,
801
+ ...color !== void 0 && { color },
802
+ x: pos.x,
803
+ y: pos.y,
804
+ width: pos.width,
805
+ height: pos.height
806
+ });
807
+ }
808
+ const edges = containerRels.filter(
809
+ (rel) => nameToElement.has(rel.sourceName) && nameToElement.has(rel.targetName)
810
+ ).map((rel) => {
811
+ const edgeData = g.edge(rel.sourceName, rel.targetName);
812
+ return {
813
+ source: rel.sourceName,
814
+ target: rel.targetName,
815
+ arrowType: rel.arrowType,
816
+ ...rel.label !== void 0 && { label: rel.label },
817
+ ...rel.technology !== void 0 && { technology: rel.technology },
818
+ lineNumber: rel.lineNumber,
819
+ points: edgeData?.points ?? []
820
+ };
821
+ });
822
+ const containerNodes = nodes.filter((n) => n.type === "container");
823
+ let bMinX = Infinity, bMinY = Infinity, bMaxX = -Infinity, bMaxY = -Infinity;
824
+ for (const n of containerNodes) {
825
+ const left = n.x - n.width / 2;
826
+ const top = n.y - n.height / 2;
827
+ const right = n.x + n.width / 2;
828
+ const bottom = n.y + n.height / 2;
829
+ if (left < bMinX) bMinX = left;
830
+ if (top < bMinY) bMinY = top;
831
+ if (right > bMaxX) bMaxX = right;
832
+ if (bottom > bMaxY) bMaxY = bottom;
833
+ }
834
+ const boundary = {
835
+ label: system.name,
836
+ typeLabel: "system",
837
+ lineNumber: system.lineNumber,
838
+ x: bMinX - BOUNDARY_PAD,
839
+ y: bMinY - BOUNDARY_PAD,
840
+ width: bMaxX - bMinX + BOUNDARY_PAD * 2,
841
+ height: bMaxY - bMinY + BOUNDARY_PAD * 2
842
+ };
843
+ const groupBoundaries = [];
844
+ if (hasGroups) {
845
+ const nodeMap = new Map(containerNodes.map((n) => [n.name, n]));
846
+ const seenGroups = /* @__PURE__ */ new Map();
847
+ for (const [elName, grp] of elementToGroup) {
848
+ const node = nodeMap.get(elName);
849
+ if (!node) continue;
850
+ if (!seenGroups.has(grp.name)) {
851
+ seenGroups.set(grp.name, { lineNumber: grp.lineNumber, members: [] });
852
+ }
853
+ seenGroups.get(grp.name).members.push(node);
854
+ }
855
+ for (const [groupName, { lineNumber, members }] of seenGroups) {
856
+ if (members.length === 0) continue;
857
+ let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
858
+ for (const m of members) {
859
+ const left = m.x - m.width / 2;
860
+ const top = m.y - m.height / 2;
861
+ const right = m.x + m.width / 2;
862
+ const bottom = m.y + m.height / 2;
863
+ if (left < gMinX) gMinX = left;
864
+ if (top < gMinY) gMinY = top;
865
+ if (right > gMaxX) gMaxX = right;
866
+ if (bottom > gMaxY) gMaxY = bottom;
867
+ }
868
+ groupBoundaries.push({
869
+ label: groupName,
870
+ typeLabel: "group",
871
+ lineNumber,
872
+ x: gMinX - GROUP_BOUNDARY_PAD,
873
+ y: gMinY - GROUP_BOUNDARY_PAD,
874
+ width: gMaxX - gMinX + GROUP_BOUNDARY_PAD * 2,
875
+ height: gMaxY - gMinY + GROUP_BOUNDARY_PAD * 2
876
+ });
877
+ }
878
+ }
879
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
880
+ for (const node of nodes) {
881
+ const left = node.x - node.width / 2;
882
+ const top = node.y - node.height / 2;
883
+ const right = node.x + node.width / 2;
884
+ const bottom = node.y + node.height / 2;
885
+ if (left < minX) minX = left;
886
+ if (top < minY) minY = top;
887
+ if (right > maxX) maxX = right;
888
+ if (bottom > maxY) maxY = bottom;
889
+ }
890
+ if (boundary.x < minX) minX = boundary.x;
891
+ if (boundary.y < minY) minY = boundary.y;
892
+ if (boundary.x + boundary.width > maxX) maxX = boundary.x + boundary.width;
893
+ if (boundary.y + boundary.height > maxY) maxY = boundary.y + boundary.height;
894
+ for (const gb of groupBoundaries) {
895
+ if (gb.x < minX) minX = gb.x;
896
+ if (gb.y < minY) minY = gb.y;
897
+ if (gb.x + gb.width > maxX) maxX = gb.x + gb.width;
898
+ if (gb.y + gb.height > maxY) maxY = gb.y + gb.height;
899
+ }
900
+ for (const edge of edges) {
901
+ for (const pt of edge.points) {
902
+ if (pt.x < minX) minX = pt.x;
903
+ if (pt.y < minY) minY = pt.y;
904
+ if (pt.x > maxX) maxX = pt.x;
905
+ if (pt.y > maxY) maxY = pt.y;
906
+ }
907
+ }
908
+ const shiftX = MARGIN - minX;
909
+ const shiftY = MARGIN - minY;
910
+ for (const node of nodes) {
911
+ node.x += shiftX;
912
+ node.y += shiftY;
913
+ }
914
+ boundary.x += shiftX;
915
+ boundary.y += shiftY;
916
+ for (const gb of groupBoundaries) {
917
+ gb.x += shiftX;
918
+ gb.y += shiftY;
919
+ }
920
+ for (const edge of edges) {
921
+ for (const pt of edge.points) {
922
+ pt.x += shiftX;
923
+ pt.y += shiftY;
924
+ }
925
+ }
926
+ let totalWidth = maxX - minX + MARGIN * 2;
927
+ let totalHeight = maxY - minY + MARGIN * 2;
928
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
929
+ if (legendGroups.length > 0) {
930
+ const legendY = totalHeight + MARGIN;
931
+ let legendX = MARGIN;
932
+ for (const lg of legendGroups) {
933
+ lg.x = legendX;
934
+ lg.y = legendY;
935
+ legendX += lg.width + 12;
936
+ }
937
+ const legendRight = legendX;
938
+ const legendBottom = legendY + LEGEND_HEIGHT;
939
+ if (legendRight > totalWidth) totalWidth = legendRight;
940
+ if (legendBottom > totalHeight) totalHeight = legendBottom;
941
+ }
942
+ return {
943
+ nodes,
944
+ edges,
945
+ legend: legendGroups,
946
+ boundary,
947
+ groupBoundaries,
948
+ width: totalWidth,
949
+ height: totalHeight
950
+ };
951
+ }
952
+ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
953
+ const system = parsed.elements.find(
954
+ (el) => el.name.toLowerCase() === systemName.toLowerCase()
955
+ );
956
+ if (!system) {
957
+ return {
958
+ nodes: [],
959
+ edges: [],
960
+ legend: [],
961
+ groupBoundaries: [],
962
+ width: 0,
963
+ height: 0
964
+ };
965
+ }
966
+ let targetContainer;
967
+ for (const child of system.children) {
968
+ if (child.type === "container" && child.name.toLowerCase() === containerName.toLowerCase()) {
969
+ targetContainer = child;
970
+ break;
971
+ }
972
+ }
973
+ if (!targetContainer) {
974
+ for (const group of system.groups) {
975
+ for (const child of group.children) {
976
+ if (child.type === "container" && child.name.toLowerCase() === containerName.toLowerCase()) {
977
+ targetContainer = child;
978
+ break;
979
+ }
980
+ }
981
+ if (targetContainer) break;
982
+ }
983
+ }
984
+ if (!targetContainer) {
985
+ return {
986
+ nodes: [],
987
+ edges: [],
988
+ legend: [],
989
+ groupBoundaries: [],
990
+ width: 0,
991
+ height: 0
992
+ };
993
+ }
994
+ const components = [];
995
+ for (const child of targetContainer.children) {
996
+ if (child.type === "component") components.push(child);
997
+ }
998
+ for (const group of targetContainer.groups) {
999
+ for (const child of group.children) {
1000
+ if (child.type === "component") components.push(child);
1001
+ }
1002
+ }
1003
+ if (components.length === 0) {
1004
+ return {
1005
+ nodes: [],
1006
+ edges: [],
1007
+ legend: [],
1008
+ groupBoundaries: [],
1009
+ width: 0,
1010
+ height: 0
1011
+ };
1012
+ }
1013
+ const componentNames = new Set(components.map((c) => c.name.toLowerCase()));
1014
+ const topElementsByName = /* @__PURE__ */ new Map();
1015
+ for (const el of parsed.elements) {
1016
+ topElementsByName.set(el.name.toLowerCase(), el);
1017
+ for (const child of el.children) {
1018
+ if (child.type === "container") {
1019
+ topElementsByName.set(child.name.toLowerCase(), child);
1020
+ }
1021
+ }
1022
+ for (const group of el.groups) {
1023
+ for (const child of group.children) {
1024
+ if (child.type === "container") {
1025
+ topElementsByName.set(child.name.toLowerCase(), child);
1026
+ }
1027
+ }
1028
+ }
1029
+ }
1030
+ const externalNames = /* @__PURE__ */ new Set();
1031
+ for (const component of components) {
1032
+ for (const rel of component.relationships) {
1033
+ const targetLower = rel.target.toLowerCase();
1034
+ if (!componentNames.has(targetLower)) {
1035
+ externalNames.add(targetLower);
1036
+ }
1037
+ }
1038
+ }
1039
+ const ownerMap = buildOwnershipMap(parsed.elements);
1040
+ const allRels = collectAllRelationships(parsed.elements, ownerMap);
1041
+ for (const { sourceName, rel } of allRels) {
1042
+ if (componentNames.has(sourceName.toLowerCase())) continue;
1043
+ if (componentNames.has(rel.target.toLowerCase())) {
1044
+ const sourceAncestor = ownerMap.get(sourceName) ?? sourceName;
1045
+ if (sourceAncestor.toLowerCase() === targetContainer.name.toLowerCase())
1046
+ continue;
1047
+ if (sourceAncestor.toLowerCase() === system.name.toLowerCase()) {
1048
+ const sourceLower = sourceName.toLowerCase();
1049
+ if (topElementsByName.has(sourceLower)) {
1050
+ externalNames.add(sourceLower);
1051
+ } else {
1052
+ externalNames.add(sourceAncestor.toLowerCase());
1053
+ }
1054
+ } else {
1055
+ externalNames.add(sourceAncestor.toLowerCase());
1056
+ }
1057
+ }
1058
+ }
1059
+ const externals = [];
1060
+ for (const name of externalNames) {
1061
+ const el = topElementsByName.get(name);
1062
+ if (el) externals.push(el);
1063
+ }
1064
+ const elementToGroup = /* @__PURE__ */ new Map();
1065
+ for (const group of targetContainer.groups) {
1066
+ for (const child of group.children) {
1067
+ if (child.type === "component") {
1068
+ elementToGroup.set(child.name, {
1069
+ name: group.name,
1070
+ lineNumber: group.lineNumber
1071
+ });
1072
+ }
1073
+ }
1074
+ }
1075
+ const hasGroups = elementToGroup.size > 0;
1076
+ const g = hasGroups ? new dagre.graphlib.Graph({ compound: true }) : new dagre.graphlib.Graph();
1077
+ g.setDefaultEdgeLabel(() => ({}));
1078
+ if (hasGroups) {
1079
+ const seenGroups = /* @__PURE__ */ new Set();
1080
+ for (const { name } of elementToGroup.values()) {
1081
+ if (!seenGroups.has(name)) {
1082
+ seenGroups.add(name);
1083
+ g.setNode("__group_" + name, {});
1084
+ }
1085
+ }
1086
+ }
1087
+ const nameToElement = /* @__PURE__ */ new Map();
1088
+ for (const el of components) {
1089
+ nameToElement.set(el.name, el);
1090
+ const dims = computeC4NodeDimensions(el, { showTechnology: true });
1091
+ g.setNode(el.name, { width: dims.width, height: dims.height });
1092
+ const grp = elementToGroup.get(el.name);
1093
+ if (grp) {
1094
+ g.setParent(el.name, "__group_" + grp.name);
1095
+ }
1096
+ }
1097
+ for (const el of externals) {
1098
+ nameToElement.set(el.name, el);
1099
+ const dims = computeC4NodeDimensions(el);
1100
+ g.setNode(el.name, { width: dims.width, height: dims.height });
1101
+ }
1102
+ const componentRels = [];
1103
+ const seenEdgeKeys = /* @__PURE__ */ new Set();
1104
+ for (const component of components) {
1105
+ for (const rel of component.relationships) {
1106
+ const targetEl = nameToElement.get(rel.target);
1107
+ if (!targetEl) continue;
1108
+ const key = `${component.name}\u2192${rel.target}`;
1109
+ if (!seenEdgeKeys.has(key)) {
1110
+ seenEdgeKeys.add(key);
1111
+ componentRels.push({
1112
+ sourceName: component.name,
1113
+ targetName: rel.target,
1114
+ ...rel.label !== void 0 && { label: rel.label },
1115
+ ...rel.technology !== void 0 && { technology: rel.technology },
1116
+ arrowType: rel.arrowType,
1117
+ lineNumber: rel.lineNumber
1118
+ });
1119
+ }
1120
+ }
1121
+ }
1122
+ for (const { sourceName, rel } of allRels) {
1123
+ if (componentNames.has(sourceName.toLowerCase())) continue;
1124
+ if (!componentNames.has(rel.target.toLowerCase())) continue;
1125
+ const sourceAncestor = ownerMap.get(sourceName) ?? sourceName;
1126
+ if (sourceAncestor.toLowerCase() === targetContainer.name.toLowerCase())
1127
+ continue;
1128
+ let resolvedSource;
1129
+ if (nameToElement.has(sourceName)) {
1130
+ resolvedSource = sourceName;
1131
+ } else if (nameToElement.has(sourceAncestor)) {
1132
+ resolvedSource = sourceAncestor;
1133
+ }
1134
+ if (!resolvedSource) continue;
1135
+ const key = `${resolvedSource}\u2192${rel.target}`;
1136
+ if (!seenEdgeKeys.has(key)) {
1137
+ seenEdgeKeys.add(key);
1138
+ componentRels.push({
1139
+ sourceName: resolvedSource,
1140
+ targetName: rel.target,
1141
+ ...rel.label !== void 0 && { label: rel.label },
1142
+ ...rel.technology !== void 0 && { technology: rel.technology },
1143
+ arrowType: rel.arrowType,
1144
+ lineNumber: rel.lineNumber
1145
+ });
1146
+ }
1147
+ }
1148
+ const spacing = computeAdaptiveSpacing(componentRels);
1149
+ g.setGraph({
1150
+ rankdir: "TB",
1151
+ nodesep: spacing.nodesep,
1152
+ ranksep: spacing.ranksep,
1153
+ edgesep: spacing.edgesep
1154
+ });
1155
+ for (const rel of componentRels) {
1156
+ if (nameToElement.has(rel.sourceName) && nameToElement.has(rel.targetName)) {
1157
+ g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
1158
+ }
1159
+ }
1160
+ dagre.layout(g);
1161
+ const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
1162
+ reduceCrossings(
1163
+ g,
1164
+ componentRels.filter(
1165
+ (r) => nameToElement.has(r.sourceName) && nameToElement.has(r.targetName)
1166
+ ).map((r) => ({ source: r.sourceName, target: r.targetName })),
1167
+ nodeGroupMap
1168
+ );
1169
+ const ancestors = [targetContainer, system];
1170
+ const nodes = [];
1171
+ for (const el of components) {
1172
+ const pos = g.node(el.name);
1173
+ const color = resolveNodeColor(
1174
+ el,
1175
+ parsed.tagGroups,
1176
+ activeTagGroup ?? null,
1177
+ ancestors
1178
+ );
1179
+ const tech = el.metadata["tech"] ?? el.metadata["technology"];
1180
+ const hasComponents = el.children.some((c) => c.type === "component") || el.groups.some((grp) => grp.children.some((c) => c.type === "component"));
1181
+ const description = el.description?.join("\n");
1182
+ const drillable = hasComponents || el.importPath ? true : void 0;
1183
+ nodes.push({
1184
+ id: el.name,
1185
+ name: el.name,
1186
+ type: "component",
1187
+ ...description !== void 0 && { description },
1188
+ metadata: el.metadata,
1189
+ lineNumber: el.lineNumber,
1190
+ ...color !== void 0 && { color },
1191
+ shape: el.shape,
1192
+ ...tech !== void 0 && { technology: tech },
1193
+ ...drillable !== void 0 && { drillable },
1194
+ ...el.importPath !== void 0 && { importPath: el.importPath },
1195
+ x: pos.x,
1196
+ y: pos.y,
1197
+ width: pos.width,
1198
+ height: pos.height
1199
+ });
1200
+ }
1201
+ for (const el of externals) {
1202
+ const pos = g.node(el.name);
1203
+ const color = resolveNodeColor(
1204
+ el,
1205
+ parsed.tagGroups,
1206
+ activeTagGroup ?? null
1207
+ );
1208
+ const description = el.description?.join("\n");
1209
+ nodes.push({
1210
+ id: el.name,
1211
+ name: el.name,
1212
+ type: el.type,
1213
+ ...description !== void 0 && { description },
1214
+ metadata: el.metadata,
1215
+ lineNumber: el.lineNumber,
1216
+ ...color !== void 0 && { color },
1217
+ x: pos.x,
1218
+ y: pos.y,
1219
+ width: pos.width,
1220
+ height: pos.height
1221
+ });
1222
+ }
1223
+ const edges = componentRels.filter(
1224
+ (rel) => nameToElement.has(rel.sourceName) && nameToElement.has(rel.targetName)
1225
+ ).map((rel) => {
1226
+ const edgeData = g.edge(rel.sourceName, rel.targetName);
1227
+ return {
1228
+ source: rel.sourceName,
1229
+ target: rel.targetName,
1230
+ arrowType: rel.arrowType,
1231
+ ...rel.label !== void 0 && { label: rel.label },
1232
+ ...rel.technology !== void 0 && { technology: rel.technology },
1233
+ lineNumber: rel.lineNumber,
1234
+ points: edgeData?.points ?? []
1235
+ };
1236
+ });
1237
+ const componentNodes = nodes.filter((n) => n.type === "component");
1238
+ let bMinX = Infinity, bMinY = Infinity, bMaxX = -Infinity, bMaxY = -Infinity;
1239
+ for (const n of componentNodes) {
1240
+ const left = n.x - n.width / 2;
1241
+ const top = n.y - n.height / 2;
1242
+ const right = n.x + n.width / 2;
1243
+ const bottom = n.y + n.height / 2;
1244
+ if (left < bMinX) bMinX = left;
1245
+ if (top < bMinY) bMinY = top;
1246
+ if (right > bMaxX) bMaxX = right;
1247
+ if (bottom > bMaxY) bMaxY = bottom;
1248
+ }
1249
+ const boundary = {
1250
+ label: targetContainer.name,
1251
+ typeLabel: "container",
1252
+ lineNumber: targetContainer.lineNumber,
1253
+ x: bMinX - BOUNDARY_PAD,
1254
+ y: bMinY - BOUNDARY_PAD,
1255
+ width: bMaxX - bMinX + BOUNDARY_PAD * 2,
1256
+ height: bMaxY - bMinY + BOUNDARY_PAD * 2
1257
+ };
1258
+ const groupBoundaries = [];
1259
+ if (hasGroups) {
1260
+ const nodeMap = new Map(componentNodes.map((n) => [n.name, n]));
1261
+ const seenGroups = /* @__PURE__ */ new Map();
1262
+ for (const [elName, grp] of elementToGroup) {
1263
+ const node = nodeMap.get(elName);
1264
+ if (!node) continue;
1265
+ if (!seenGroups.has(grp.name)) {
1266
+ seenGroups.set(grp.name, { lineNumber: grp.lineNumber, members: [] });
1267
+ }
1268
+ seenGroups.get(grp.name).members.push(node);
1269
+ }
1270
+ for (const [groupName, { lineNumber, members }] of seenGroups) {
1271
+ if (members.length === 0) continue;
1272
+ let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
1273
+ for (const m of members) {
1274
+ const left = m.x - m.width / 2;
1275
+ const top = m.y - m.height / 2;
1276
+ const right = m.x + m.width / 2;
1277
+ const bottom = m.y + m.height / 2;
1278
+ if (left < gMinX) gMinX = left;
1279
+ if (top < gMinY) gMinY = top;
1280
+ if (right > gMaxX) gMaxX = right;
1281
+ if (bottom > gMaxY) gMaxY = bottom;
1282
+ }
1283
+ groupBoundaries.push({
1284
+ label: groupName,
1285
+ typeLabel: "group",
1286
+ lineNumber,
1287
+ x: gMinX - GROUP_BOUNDARY_PAD,
1288
+ y: gMinY - GROUP_BOUNDARY_PAD,
1289
+ width: gMaxX - gMinX + GROUP_BOUNDARY_PAD * 2,
1290
+ height: gMaxY - gMinY + GROUP_BOUNDARY_PAD * 2
1291
+ });
1292
+ }
1293
+ }
1294
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
1295
+ for (const node of nodes) {
1296
+ const left = node.x - node.width / 2;
1297
+ const top = node.y - node.height / 2;
1298
+ const right = node.x + node.width / 2;
1299
+ const bottom = node.y + node.height / 2;
1300
+ if (left < minX) minX = left;
1301
+ if (top < minY) minY = top;
1302
+ if (right > maxX) maxX = right;
1303
+ if (bottom > maxY) maxY = bottom;
1304
+ }
1305
+ if (boundary.x < minX) minX = boundary.x;
1306
+ if (boundary.y < minY) minY = boundary.y;
1307
+ if (boundary.x + boundary.width > maxX) maxX = boundary.x + boundary.width;
1308
+ if (boundary.y + boundary.height > maxY) maxY = boundary.y + boundary.height;
1309
+ for (const gb of groupBoundaries) {
1310
+ if (gb.x < minX) minX = gb.x;
1311
+ if (gb.y < minY) minY = gb.y;
1312
+ if (gb.x + gb.width > maxX) maxX = gb.x + gb.width;
1313
+ if (gb.y + gb.height > maxY) maxY = gb.y + gb.height;
1314
+ }
1315
+ for (const edge of edges) {
1316
+ for (const pt of edge.points) {
1317
+ if (pt.x < minX) minX = pt.x;
1318
+ if (pt.y < minY) minY = pt.y;
1319
+ if (pt.x > maxX) maxX = pt.x;
1320
+ if (pt.y > maxY) maxY = pt.y;
1321
+ }
1322
+ }
1323
+ const shiftX = MARGIN - minX;
1324
+ const shiftY = MARGIN - minY;
1325
+ for (const node of nodes) {
1326
+ node.x += shiftX;
1327
+ node.y += shiftY;
1328
+ }
1329
+ boundary.x += shiftX;
1330
+ boundary.y += shiftY;
1331
+ for (const gb of groupBoundaries) {
1332
+ gb.x += shiftX;
1333
+ gb.y += shiftY;
1334
+ }
1335
+ for (const edge of edges) {
1336
+ for (const pt of edge.points) {
1337
+ pt.x += shiftX;
1338
+ pt.y += shiftY;
1339
+ }
1340
+ }
1341
+ let totalWidth = maxX - minX + MARGIN * 2;
1342
+ let totalHeight = maxY - minY + MARGIN * 2;
1343
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
1344
+ if (legendGroups.length > 0) {
1345
+ const legendY = totalHeight + MARGIN;
1346
+ let legendX = MARGIN;
1347
+ for (const lg of legendGroups) {
1348
+ lg.x = legendX;
1349
+ lg.y = legendY;
1350
+ legendX += lg.width + 12;
1351
+ }
1352
+ const legendRight = legendX;
1353
+ const legendBottom = legendY + LEGEND_HEIGHT;
1354
+ if (legendRight > totalWidth) totalWidth = legendRight;
1355
+ if (legendBottom > totalHeight) totalHeight = legendBottom;
1356
+ }
1357
+ return {
1358
+ nodes,
1359
+ edges,
1360
+ legend: legendGroups,
1361
+ boundary,
1362
+ groupBoundaries,
1363
+ width: totalWidth,
1364
+ height: totalHeight
1365
+ };
1366
+ }
1367
+ function resolveContainerRef(parsed, refName) {
1368
+ const lower = refName.toLowerCase();
1369
+ for (const el of parsed.elements) {
1370
+ for (const child of el.children) {
1371
+ if (child.type === "container" && child.name.toLowerCase() === lower)
1372
+ return child;
1373
+ }
1374
+ for (const group of el.groups) {
1375
+ for (const child of group.children) {
1376
+ if (child.type === "container" && child.name.toLowerCase() === lower)
1377
+ return child;
1378
+ }
1379
+ }
1380
+ }
1381
+ return void 0;
1382
+ }
1383
+ function collectDeploymentRefs(nodes, parsed, parentId, refs, infraIds, infraParents) {
1384
+ for (const node of nodes) {
1385
+ const infraId = `__infra_${node.name}`;
1386
+ infraIds.set(infraId, node);
1387
+ infraParents.set(infraId, parentId);
1388
+ for (const ref of node.containerRefs) {
1389
+ const el = resolveContainerRef(parsed, ref);
1390
+ if (el) {
1391
+ refs.push({
1392
+ refName: ref,
1393
+ element: el,
1394
+ infraId,
1395
+ deployLineNumber: node.lineNumber
1396
+ });
1397
+ }
1398
+ }
1399
+ collectDeploymentRefs(
1400
+ node.children,
1401
+ parsed,
1402
+ infraId,
1403
+ refs,
1404
+ infraIds,
1405
+ infraParents
1406
+ );
1407
+ }
1408
+ }
1409
+ function layoutC4Deployment(parsed, activeTagGroup) {
1410
+ if (parsed.deployment.length === 0) {
1411
+ return {
1412
+ nodes: [],
1413
+ edges: [],
1414
+ legend: [],
1415
+ groupBoundaries: [],
1416
+ width: 0,
1417
+ height: 0
1418
+ };
1419
+ }
1420
+ const refs = [];
1421
+ const infraIds = /* @__PURE__ */ new Map();
1422
+ const infraParents = /* @__PURE__ */ new Map();
1423
+ collectDeploymentRefs(
1424
+ parsed.deployment,
1425
+ parsed,
1426
+ null,
1427
+ refs,
1428
+ infraIds,
1429
+ infraParents
1430
+ );
1431
+ if (refs.length === 0) {
1432
+ return {
1433
+ nodes: [],
1434
+ edges: [],
1435
+ legend: [],
1436
+ groupBoundaries: [],
1437
+ width: 0,
1438
+ height: 0
1439
+ };
1440
+ }
1441
+ const seenRefs = /* @__PURE__ */ new Map();
1442
+ for (const ref of refs) {
1443
+ const key = ref.element.name.toLowerCase();
1444
+ if (!seenRefs.has(key)) seenRefs.set(key, ref);
1445
+ }
1446
+ const refEntries = [...seenRefs.values()];
1447
+ const refNames = new Set(refEntries.map((r) => r.element.name.toLowerCase()));
1448
+ const nameToElement = /* @__PURE__ */ new Map();
1449
+ for (const r of refEntries) {
1450
+ nameToElement.set(r.element.name, r.element);
1451
+ }
1452
+ const g = new dagre.graphlib.Graph({ compound: true });
1453
+ g.setDefaultEdgeLabel(() => ({}));
1454
+ for (const [infraId] of infraIds) {
1455
+ g.setNode(infraId, {});
1456
+ const parentId = infraParents.get(infraId);
1457
+ if (parentId) g.setParent(infraId, parentId);
1458
+ }
1459
+ for (const r of refEntries) {
1460
+ const dims = computeC4NodeDimensions(r.element, { showTechnology: true });
1461
+ g.setNode(r.element.name, { width: dims.width, height: dims.height });
1462
+ g.setParent(r.element.name, r.infraId);
1463
+ }
1464
+ const deployRels = [];
1465
+ const seenEdgeKeys = /* @__PURE__ */ new Set();
1466
+ for (const r of refEntries) {
1467
+ for (const rel of r.element.relationships) {
1468
+ if (refNames.has(rel.target.toLowerCase())) {
1469
+ const key = `${r.element.name}\u2192${rel.target}`;
1470
+ if (!seenEdgeKeys.has(key)) {
1471
+ seenEdgeKeys.add(key);
1472
+ deployRels.push({
1473
+ sourceName: r.element.name,
1474
+ targetName: rel.target,
1475
+ ...rel.label !== void 0 && { label: rel.label },
1476
+ ...rel.technology !== void 0 && { technology: rel.technology },
1477
+ arrowType: rel.arrowType,
1478
+ lineNumber: rel.lineNumber
1479
+ });
1480
+ }
1481
+ }
1482
+ }
1483
+ }
1484
+ const spacing = computeAdaptiveSpacing(deployRels);
1485
+ g.setGraph({
1486
+ rankdir: "TB",
1487
+ nodesep: spacing.nodesep,
1488
+ ranksep: spacing.ranksep,
1489
+ edgesep: spacing.edgesep
1490
+ });
1491
+ for (const rel of deployRels) {
1492
+ if (nameToElement.has(rel.sourceName) && nameToElement.has(rel.targetName)) {
1493
+ g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
1494
+ }
1495
+ }
1496
+ dagre.layout(g);
1497
+ const nodeInfraMap = /* @__PURE__ */ new Map();
1498
+ for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
1499
+ reduceCrossings(
1500
+ g,
1501
+ deployRels.filter(
1502
+ (r) => nameToElement.has(r.sourceName) && nameToElement.has(r.targetName)
1503
+ ).map((r) => ({ source: r.sourceName, target: r.targetName })),
1504
+ nodeInfraMap
1505
+ );
1506
+ const nodes = [];
1507
+ for (const r of refEntries) {
1508
+ const pos = g.node(r.element.name);
1509
+ const color = resolveNodeColor(
1510
+ r.element,
1511
+ parsed.tagGroups,
1512
+ activeTagGroup ?? null
1513
+ );
1514
+ const tech = r.element.metadata["tech"] ?? r.element.metadata["technology"];
1515
+ const description = r.element.description?.join("\n");
1516
+ nodes.push({
1517
+ id: r.element.name,
1518
+ name: r.element.name,
1519
+ type: "container",
1520
+ ...description !== void 0 && { description },
1521
+ metadata: r.element.metadata,
1522
+ lineNumber: r.element.lineNumber,
1523
+ ...color !== void 0 && { color },
1524
+ shape: r.element.shape,
1525
+ ...tech !== void 0 && { technology: tech },
1526
+ x: pos.x,
1527
+ y: pos.y,
1528
+ width: pos.width,
1529
+ height: pos.height
1530
+ });
1531
+ }
1532
+ const edges = deployRels.filter(
1533
+ (rel) => nameToElement.has(rel.sourceName) && nameToElement.has(rel.targetName)
1534
+ ).map((rel) => {
1535
+ const edgeData = g.edge(rel.sourceName, rel.targetName);
1536
+ return {
1537
+ source: rel.sourceName,
1538
+ target: rel.targetName,
1539
+ arrowType: rel.arrowType,
1540
+ ...rel.label !== void 0 && { label: rel.label },
1541
+ ...rel.technology !== void 0 && { technology: rel.technology },
1542
+ lineNumber: rel.lineNumber,
1543
+ points: edgeData?.points ?? []
1544
+ };
1545
+ });
1546
+ const groupBoundaries = [];
1547
+ const nodeMap = new Map(nodes.map((n) => [n.name, n]));
1548
+ const infraMembers = /* @__PURE__ */ new Map();
1549
+ for (const r of refEntries) {
1550
+ const members = infraMembers.get(r.infraId) ?? [];
1551
+ const node = nodeMap.get(r.element.name);
1552
+ if (node) members.push(node);
1553
+ infraMembers.set(r.infraId, members);
1554
+ }
1555
+ const infraBounds = /* @__PURE__ */ new Map();
1556
+ function computeInfraBounds(infraId) {
1557
+ if (infraBounds.has(infraId)) return infraBounds.get(infraId);
1558
+ let bMinX = Infinity, bMinY = Infinity, bMaxX = -Infinity, bMaxY = -Infinity;
1559
+ let hasContent = false;
1560
+ const members = infraMembers.get(infraId) ?? [];
1561
+ for (const m of members) {
1562
+ hasContent = true;
1563
+ const left = m.x - m.width / 2;
1564
+ const top = m.y - m.height / 2;
1565
+ const right = m.x + m.width / 2;
1566
+ const bottom = m.y + m.height / 2;
1567
+ if (left < bMinX) bMinX = left;
1568
+ if (top < bMinY) bMinY = top;
1569
+ if (right > bMaxX) bMaxX = right;
1570
+ if (bottom > bMaxY) bMaxY = bottom;
1571
+ }
1572
+ for (const [childId, parentId] of infraParents) {
1573
+ if (parentId === infraId) {
1574
+ const childBounds = computeInfraBounds(childId);
1575
+ if (childBounds) {
1576
+ hasContent = true;
1577
+ if (childBounds.x < bMinX) bMinX = childBounds.x;
1578
+ if (childBounds.y < bMinY) bMinY = childBounds.y;
1579
+ if (childBounds.x + childBounds.width > bMaxX)
1580
+ bMaxX = childBounds.x + childBounds.width;
1581
+ if (childBounds.y + childBounds.height > bMaxY)
1582
+ bMaxY = childBounds.y + childBounds.height;
1583
+ }
1584
+ }
1585
+ }
1586
+ if (!hasContent) return null;
1587
+ const bounds = {
1588
+ x: bMinX - BOUNDARY_PAD,
1589
+ y: bMinY - BOUNDARY_PAD,
1590
+ width: bMaxX - bMinX + BOUNDARY_PAD * 2,
1591
+ height: bMaxY - bMinY + BOUNDARY_PAD * 2
1592
+ };
1593
+ infraBounds.set(infraId, bounds);
1594
+ return bounds;
1595
+ }
1596
+ for (const [infraId, node] of infraIds) {
1597
+ const bounds = computeInfraBounds(infraId);
1598
+ if (bounds) {
1599
+ const shapeLabel = node.shape !== "default" ? node.shape : "node";
1600
+ groupBoundaries.push({
1601
+ label: node.name,
1602
+ typeLabel: shapeLabel,
1603
+ lineNumber: node.lineNumber,
1604
+ ...bounds
1605
+ });
1606
+ }
1607
+ }
1608
+ groupBoundaries.sort((a, b) => b.width * b.height - a.width * a.height);
1609
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
1610
+ for (const node of nodes) {
1611
+ const left = node.x - node.width / 2;
1612
+ const top = node.y - node.height / 2;
1613
+ const right = node.x + node.width / 2;
1614
+ const bottom = node.y + node.height / 2;
1615
+ if (left < minX) minX = left;
1616
+ if (top < minY) minY = top;
1617
+ if (right > maxX) maxX = right;
1618
+ if (bottom > maxY) maxY = bottom;
1619
+ }
1620
+ for (const gb of groupBoundaries) {
1621
+ if (gb.x < minX) minX = gb.x;
1622
+ if (gb.y < minY) minY = gb.y;
1623
+ if (gb.x + gb.width > maxX) maxX = gb.x + gb.width;
1624
+ if (gb.y + gb.height > maxY) maxY = gb.y + gb.height;
1625
+ }
1626
+ for (const edge of edges) {
1627
+ for (const pt of edge.points) {
1628
+ if (pt.x < minX) minX = pt.x;
1629
+ if (pt.y < minY) minY = pt.y;
1630
+ if (pt.x > maxX) maxX = pt.x;
1631
+ if (pt.y > maxY) maxY = pt.y;
1632
+ }
1633
+ }
1634
+ const shiftX = MARGIN - minX;
1635
+ const shiftY = MARGIN - minY;
1636
+ for (const node of nodes) {
1637
+ node.x += shiftX;
1638
+ node.y += shiftY;
1639
+ }
1640
+ for (const gb of groupBoundaries) {
1641
+ gb.x += shiftX;
1642
+ gb.y += shiftY;
1643
+ }
1644
+ for (const edge of edges) {
1645
+ for (const pt of edge.points) {
1646
+ pt.x += shiftX;
1647
+ pt.y += shiftY;
1648
+ }
1649
+ }
1650
+ let totalWidth = maxX - minX + MARGIN * 2;
1651
+ let totalHeight = maxY - minY + MARGIN * 2;
1652
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
1653
+ if (legendGroups.length > 0) {
1654
+ const legendY = totalHeight + MARGIN;
1655
+ let legendX = MARGIN;
1656
+ for (const lg of legendGroups) {
1657
+ lg.x = legendX;
1658
+ lg.y = legendY;
1659
+ legendX += lg.width + 12;
1660
+ }
1661
+ const legendRight = legendX;
1662
+ const legendBottom = legendY + LEGEND_HEIGHT;
1663
+ if (legendRight > totalWidth) totalWidth = legendRight;
1664
+ if (legendBottom > totalHeight) totalHeight = legendBottom;
1665
+ }
1666
+ return {
1667
+ nodes,
1668
+ edges,
1669
+ legend: legendGroups,
1670
+ groupBoundaries,
1671
+ width: totalWidth,
1672
+ height: totalHeight
1673
+ };
1674
+ }
1675
+
1676
+ export {
1677
+ rollUpContextRelationships,
1678
+ collectCardMetadata,
1679
+ computeC4NodeDimensions,
1680
+ layoutC4Context,
1681
+ layoutC4Containers,
1682
+ layoutC4Components,
1683
+ layoutC4Deployment
1684
+ };
1685
+ //# sourceMappingURL=chunk-QEBUY5NY.js.map