@diagrammo/dgmo 0.49.0 → 0.50.1

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 (198) hide show
  1. package/.cursorrules +6 -2
  2. package/.github/copilot-instructions.md +6 -2
  3. package/.windsurfrules +6 -2
  4. package/SKILL.md +6 -2
  5. package/dist/advanced.js +557 -195
  6. package/dist/auto.js +129 -119
  7. package/dist/auto.mjs +19496 -12761
  8. package/dist/block.js +145 -48
  9. package/dist/{charts-d3-EW3YQTEA.js → charts-d3-LR6D3D7A.js} +5 -5
  10. package/dist/{chunk-5FGDJIJX.js → chunk-2C56LEML.js} +3 -3
  11. package/dist/{chunk-XJ7J6WOO.js → chunk-2KGXHFYK.js} +4 -4
  12. package/dist/{chunk-T7IKPCMI.js → chunk-3LH5CLP4.js} +3 -3
  13. package/dist/{chunk-ZDUQGL3N.js → chunk-3LKM6FMW.js} +2 -2
  14. package/dist/{chunk-4FIV4K4Z.js → chunk-3NODSS6A.js} +7 -7
  15. package/dist/{chunk-4BD7EVPK.js → chunk-3P2DKJCG.js} +2 -2
  16. package/dist/{chunk-TC3YWBFO.js → chunk-3UIKSPFB.js} +35 -9
  17. package/dist/{chunk-SJEGO2QL.js → chunk-3XGZF2QJ.js} +4 -4
  18. package/dist/chunk-3XPUDB7B.js +420 -0
  19. package/dist/{chunk-O5MQPK7E.js → chunk-3Y2DRD7S.js} +3 -3
  20. package/dist/chunk-3YPY3OXD.js +354 -0
  21. package/dist/{chunk-N4J5KGWG.js → chunk-4MJ7M6YL.js} +8 -8
  22. package/dist/{chunk-JFPY3UJ7.js → chunk-4S52SK6G.js} +8 -8
  23. package/dist/{chunk-ALRHIR6I.js → chunk-5GJYGP5Z.js} +4 -4
  24. package/dist/{chunk-SQDFK5ZQ.js → chunk-5OWVC2A6.js} +3 -3
  25. package/dist/{chunk-7VLUGEMJ.js → chunk-66NVRO4K.js} +4 -4
  26. package/dist/{chunk-ZREZOIGS.js → chunk-66RUL3XF.js} +6 -6
  27. package/dist/{chunk-MJCG5K2O.js → chunk-6BFZKBGD.js} +2 -2
  28. package/dist/{chunk-2WMBWZY2.js → chunk-6HJBFVLO.js} +3 -3
  29. package/dist/{chunk-XCC2RENT.js → chunk-6IAA2QQ2.js} +2 -2
  30. package/dist/{chunk-B57V3DWN.js → chunk-7L3R6I3U.js} +3 -3
  31. package/dist/chunk-7PQYTZMA.js +681 -0
  32. package/dist/{chunk-6SA7FJE4.js → chunk-7QKLIMRZ.js} +10 -10
  33. package/dist/{chunk-YTN572Q2.js → chunk-7TV5Y53Y.js} +2 -2
  34. package/dist/{chunk-6CLKNXVP.js → chunk-BBGDM4NQ.js} +457 -78
  35. package/dist/{chunk-3NFJWFNP.js → chunk-BDXC7MDK.js} +1045 -124
  36. package/dist/{chunk-D5AVQCDN.js → chunk-BH3UYK36.js} +3 -3
  37. package/dist/chunk-BTUTZMPT.js +482 -0
  38. package/dist/{chunk-LVHPTBO3.js → chunk-C2CVAM3U.js} +4 -4
  39. package/dist/{chunk-BCT5G2VN.js → chunk-DFTFPYUD.js} +5 -5
  40. package/dist/{chunk-KV6Y2HE6.js → chunk-DSWVNQVS.js} +4 -4
  41. package/dist/{chunk-4YNP6O47.js → chunk-DZXYF2F5.js} +13 -3
  42. package/dist/{chunk-FUCAB3KQ.js → chunk-EAMEADTV.js} +3 -3
  43. package/dist/chunk-FA6K3FWM.js +940 -0
  44. package/dist/{chunk-HBVD67XG.js → chunk-FJUNPCYI.js} +2 -2
  45. package/dist/{chunk-6VE3VLWM.js → chunk-GBE54KY6.js} +6 -5
  46. package/dist/{chunk-YRVLP7UU.js → chunk-GKC3HILQ.js} +4 -4
  47. package/dist/{chunk-ZXL6MB4R.js → chunk-GL5UZ6NY.js} +3 -3
  48. package/dist/{chunk-GB3YZTAM.js → chunk-H73FP42Q.js} +5 -5
  49. package/dist/{chunk-4UDKKGHH.js → chunk-HEVYAHLW.js} +2 -2
  50. package/dist/{chunk-DDTJ5N7Q.js → chunk-HLJVEAOF.js} +6 -6
  51. package/dist/{chunk-CLR4A2N2.js → chunk-HTMN2Z6U.js} +4 -4
  52. package/dist/{chunk-TLKMTQYL.js → chunk-I2WKW7C7.js} +3 -3
  53. package/dist/{chunk-IUK7AGD4.js → chunk-I7F2A35O.js} +2 -2
  54. package/dist/{chunk-IBC35BTK.js → chunk-IUMPEA64.js} +2 -2
  55. package/dist/{chunk-5XCM7UUW.js → chunk-J6K7RP6P.js} +100 -36
  56. package/dist/{chunk-AJ2PEY7J.js → chunk-JJUOHGX2.js} +2 -2
  57. package/dist/{chunk-CEHZOPGM.js → chunk-KFZYNWWO.js} +2 -2
  58. package/dist/{chunk-4NQ3IKUM.js → chunk-KHEYDLMT.js} +5 -5
  59. package/dist/{chunk-YM2BSPYW.js → chunk-LE4MYMLI.js} +2 -2
  60. package/dist/{chunk-PU3AUQPA.js → chunk-M5RXSRO2.js} +2 -2
  61. package/dist/{chunk-XKTN7CZF.js → chunk-MCZ5LBZD.js} +4 -4
  62. package/dist/{chunk-35BBZDFE.js → chunk-NU3XLTGP.js} +4 -4
  63. package/dist/{chunk-BPIJLRYE.js → chunk-NWED7CYO.js} +3 -3
  64. package/dist/{chunk-YYX3SU6H.js → chunk-OMNGQPEZ.js} +257 -101
  65. package/dist/{chunk-BGJV5ATD.js → chunk-ONA5KPV3.js} +2 -2
  66. package/dist/{chunk-ZNQX6QWH.js → chunk-OTAL43EH.js} +6 -2
  67. package/dist/{chunk-F743CM2N.js → chunk-OTQKUHRS.js} +3 -3
  68. package/dist/{chunk-KUBBJ2CD.js → chunk-P7CE7LXL.js} +6 -6
  69. package/dist/{chunk-UVCXZEGU.js → chunk-PFHVU7UA.js} +2 -2
  70. package/dist/{chunk-5ZZLLBFK.js → chunk-PGEGPSCZ.js} +2 -2
  71. package/dist/chunk-Q4V4XQWV.js +158 -0
  72. package/dist/{chunk-IHLSHCPZ.js → chunk-QJP6PG7F.js} +5 -5
  73. package/dist/{chunk-EYN7VFKL.js → chunk-QROYX7TT.js} +3 -3
  74. package/dist/chunk-QXKWYZNR.js +379 -0
  75. package/dist/{chunk-VC3TR4NL.js → chunk-RZ6YKEUJ.js} +3 -3
  76. package/dist/{chunk-K6HNAY5V.js → chunk-SCHXJTZL.js} +2 -2
  77. package/dist/{chunk-7OVSJI2V.js → chunk-SQLNBA64.js} +3 -3
  78. package/dist/{chunk-65RTDUZH.js → chunk-SXRTRMEE.js} +3 -3
  79. package/dist/{chunk-3BIAFYD2.js → chunk-T4VOVZT5.js} +2 -2
  80. package/dist/{chunk-UWDO3O52.js → chunk-TN35C5KM.js} +2 -2
  81. package/dist/{chunk-IFK3GIUW.js → chunk-TSA237IM.js} +2 -2
  82. package/dist/{chunk-DKEPS5LH.js → chunk-U2HGPLZS.js} +36 -2
  83. package/dist/{chunk-4HVNZ7WF.js → chunk-VBYSIOOY.js} +3 -3
  84. package/dist/{chunk-ZTLWVRNS.js → chunk-VDOOQZUD.js} +2 -2
  85. package/dist/{chunk-RTERS6S5.js → chunk-VUJGMJE6.js} +4 -4
  86. package/dist/{chunk-X2CH42BN.js → chunk-VVW3F3MD.js} +7 -7
  87. package/dist/{chunk-RI4NIMFP.js → chunk-VVZRV54S.js} +75 -35
  88. package/dist/{chunk-LG4TWW4U.js → chunk-WW32K72E.js} +8 -8
  89. package/dist/{chunk-NVT6C2VE.js → chunk-X6EZPSW6.js} +4 -4
  90. package/dist/{chunk-KQAEKMIX.js → chunk-XDXNDSKO.js} +2 -2
  91. package/dist/{chunk-FMKTYMP4.js → chunk-XJLKLU4D.js} +3 -3
  92. package/dist/chunk-Y4RNXEAO.js +952 -0
  93. package/dist/{chunk-26RVGM44.js → chunk-YIA4LEX4.js} +15 -7
  94. package/dist/chunk-YNRDWYZP.js +295 -0
  95. package/dist/{chunk-SGHSY3LU.js → chunk-Z6XJQ6KO.js} +81 -6
  96. package/dist/{chunk-SVDWQGX2.js → chunk-ZAD4HV2G.js} +139 -78
  97. package/dist/cli.cjs +153 -144
  98. package/dist/clock.d.ts +10 -0
  99. package/dist/clock.js +235 -0
  100. package/dist/countdown.d.ts +12 -0
  101. package/dist/countdown.js +450 -0
  102. package/dist/dgmo-router-YTU3ENAX.js +84 -0
  103. package/dist/{dimensions-WSIJXT5Q.js → dimensions-OTBKC5M6.js} +5 -5
  104. package/dist/editor.js +39 -1
  105. package/dist/element.js +131 -121
  106. package/dist/element.mjs +19496 -12761
  107. package/dist/{flowchart-parser-IVZN7CQN.js → flowchart-parser-OE4WHLTF.js} +4 -4
  108. package/dist/{flowchart-renderer-GMI2U3DD.js → flowchart-renderer-GPCSMTOR.js} +6 -6
  109. package/dist/highlight.d.ts +17 -1
  110. package/dist/highlight.js +156 -5
  111. package/dist/index.js +50 -45
  112. package/dist/{layout-HQMBPJVS.js → layout-46366KBT.js} +2 -2
  113. package/dist/{layout-D42XE4RE.js → layout-5BG6DFCU.js} +3 -3
  114. package/dist/{layout-K2ZN3Q42.js → layout-7MZ5NF2N.js} +4 -4
  115. package/dist/{layout-4Z5JM7PT.js → layout-FWODLIMP.js} +4 -4
  116. package/dist/{layout-GZ2CWONU.js → layout-J36454XU.js} +4 -4
  117. package/dist/{layout-S3EK4IYO.js → layout-LD6XTLD6.js} +4 -4
  118. package/dist/{parser-CLMFZWCC.js → parser-36HVKU5U.js} +5 -5
  119. package/dist/{parser-MCJWF5G3.js → parser-4AI5ZN5F.js} +3 -3
  120. package/dist/{parser-DERPY5KC.js → parser-4NO6VZ6S.js} +4 -4
  121. package/dist/parser-4WQQQXCG.js +13 -0
  122. package/dist/{parser-4ROBKVUR.js → parser-5OXGPNBF.js} +5 -5
  123. package/dist/{parser-XWUDFJ7P.js → parser-AGVYTQXV.js} +4 -4
  124. package/dist/{parser-TITK4YW6.js → parser-AXKBDGNO.js} +5 -5
  125. package/dist/parser-B5FVS7O6.js +11 -0
  126. package/dist/parser-CSFUN3CT.js +14 -0
  127. package/dist/parser-CVMEDULU.js +15 -0
  128. package/dist/{parser-KPMN5KKE.js → parser-D5VILGN2.js} +4 -4
  129. package/dist/{parser-IKZFTLOJ.js → parser-DFFEDKHH.js} +3 -3
  130. package/dist/{parser-JD7OQ2E6.js → parser-H3XEPFPV.js} +4 -4
  131. package/dist/{parser-UBGPTIZD.js → parser-HYRTF3DO.js} +3 -3
  132. package/dist/{parser-K647FRI4.js → parser-IBWNAPY4.js} +3 -3
  133. package/dist/{parser-4BKIFG2C.js → parser-IJBMRXMC.js} +5 -5
  134. package/dist/{parser-MBJSBTL5.js → parser-JJR52VAT.js} +5 -5
  135. package/dist/{parser-SMBJO3XN.js → parser-K3XXLMUW.js} +4 -4
  136. package/dist/{parser-OBQG655V.js → parser-KCR2WS3V.js} +4 -4
  137. package/dist/{parser-HVJFTOXL.js → parser-LWKKBNSA.js} +3 -3
  138. package/dist/{parser-ZYC3XGNS.js → parser-MPCE5JBG.js} +4 -4
  139. package/dist/{parser-QJYS7KQQ.js → parser-MTOMTWTD.js} +4 -4
  140. package/dist/{parser-GFSOPDB7.js → parser-O4HIUN6G.js} +4 -4
  141. package/dist/{parser-BL7Z72EY.js → parser-PSA4UBSP.js} +4 -4
  142. package/dist/{parser-TNYX5S7E.js → parser-RV4M6AS2.js} +4 -4
  143. package/dist/parser-RZPVAQVX.js +11 -0
  144. package/dist/{parser-UGA23PYK.js → parser-TGQZJ5OT.js} +4 -4
  145. package/dist/parser-TXND652E.js +13 -0
  146. package/dist/{parser-WISPH7LW.js → parser-VKOJM7KS.js} +4 -4
  147. package/dist/{parser-ZKF5K2H2.js → parser-W4PQUGX3.js} +4 -4
  148. package/dist/{parser-MRBPXXIV.js → parser-WAV5JRPT.js} +4 -4
  149. package/dist/{parser-D6ONJQFR.js → parser-YS7TOS2K.js} +3 -3
  150. package/dist/pert.d.ts +8 -0
  151. package/dist/{renderer-VFUVRNLK.js → renderer-2AZ5HJFK.js} +4 -4
  152. package/dist/{renderer-V3JEMWMM.js → renderer-2QPMJ2DA.js} +6 -6
  153. package/dist/renderer-45PSOL3E.js +19 -0
  154. package/dist/{renderer-BB5PREMY.js → renderer-4ITCHBT7.js} +4 -4
  155. package/dist/{renderer-XEKIRPI6.js → renderer-5DSEWF74.js} +5 -5
  156. package/dist/{renderer-QNHUXCRL.js → renderer-6OKF44KS.js} +9 -9
  157. package/dist/renderer-72LGR2YF.js +14 -0
  158. package/dist/{renderer-YHXPN3GF.js → renderer-7SD6MSBS.js} +7 -7
  159. package/dist/renderer-A5VE5BCI.js +24 -0
  160. package/dist/{renderer-A5VFZQQK.js → renderer-AUW2BE6G.js} +6 -6
  161. package/dist/{renderer-LWGJFT3D.js → renderer-B5TBNZJE.js} +5 -5
  162. package/dist/{renderer-4IUPINAE.js → renderer-BSR2O2H6.js} +7 -7
  163. package/dist/{renderer-DKESNMU5.js → renderer-CYB4LK3G.js} +6 -6
  164. package/dist/{renderer-MFJ7HY2H.js → renderer-FF24KZCI.js} +5 -5
  165. package/dist/{renderer-CGW7QPNA.js → renderer-FPE3J5VZ.js} +7 -7
  166. package/dist/{renderer-6T3KK4S3.js → renderer-FYSVXL7M.js} +7 -7
  167. package/dist/{renderer-THD6V5TL.js → renderer-HDCCZURL.js} +9 -7
  168. package/dist/{renderer-TPIBXNOS.js → renderer-JCCHNECZ.js} +6 -6
  169. package/dist/{renderer-QL5KO6SK.js → renderer-KS7YZJE2.js} +7 -7
  170. package/dist/{renderer-SQ3JUAI7.js → renderer-NGXZTRK6.js} +6 -6
  171. package/dist/{renderer-MJDDU27W.js → renderer-ODUUOPVT.js} +10 -10
  172. package/dist/renderer-PJM35LJN.js +17 -0
  173. package/dist/{renderer-TYXRHMRI.js → renderer-PPIHO5FI.js} +9 -9
  174. package/dist/{renderer-W5RMOURB.js → renderer-PSMMZCKH.js} +6 -6
  175. package/dist/{renderer-UZF3JROD.js → renderer-QAOBLRXV.js} +8 -8
  176. package/dist/{renderer-ROV756WT.js → renderer-R2WNIC2C.js} +5 -5
  177. package/dist/{renderer-VICQ4WNY.js → renderer-SBS3YWU5.js} +5 -5
  178. package/dist/{renderer-6L3NBEY5.js → renderer-UYMQQCJK.js} +6 -6
  179. package/dist/{renderer-radial-MHO2VDXN.js → renderer-radial-F7X5F2VU.js} +6 -6
  180. package/dist/{state-parser-RX3GXOIB.js → state-parser-3REXRK3R.js} +4 -4
  181. package/dist/{state-renderer-AJUKGNWU.js → state-renderer-XYLNRWXR.js} +6 -6
  182. package/docs/ai-integration.md +6 -2
  183. package/docs/language-reference.md +253 -22
  184. package/gallery/fixtures/body.dgmo +17 -0
  185. package/gallery/fixtures/bracket.dgmo +30 -0
  186. package/gallery/fixtures/clock.dgmo +8 -0
  187. package/gallery/fixtures/countdown.dgmo +3 -0
  188. package/gallery/fixtures/goal.dgmo +4 -0
  189. package/package.json +12 -4
  190. package/dist/advanced.d.ts +0 -6162
  191. package/dist/auto.d.ts +0 -54
  192. package/dist/block.d.ts +0 -123
  193. package/dist/dgmo-router-ODKPEJ3P.js +0 -79
  194. package/dist/element.d.ts +0 -62
  195. package/dist/index.d.ts +0 -166
  196. package/dist/parser-667RGSUW.js +0 -13
  197. package/dist/parser-J3P2D6IP.js +0 -13
  198. package/dist/themes-1-CuKpeH.d.ts +0 -908
@@ -1,9 +1,12 @@
1
- import {
2
- renderNodeCard
3
- } from "./chunk-AHGF2YYA.js";
4
1
  import {
5
2
  renderInlineText
6
3
  } from "./chunk-3HDBAXF5.js";
4
+ import {
5
+ wrapDescriptionLines
6
+ } from "./chunk-BMR4CIEO.js";
7
+ import {
8
+ renderNodeCard
9
+ } from "./chunk-AHGF2YYA.js";
7
10
  import {
8
11
  CARD_RADIUS,
9
12
  HEADER_HEIGHT,
@@ -11,15 +14,12 @@ import {
11
14
  NODE_STROKE_WIDTH,
12
15
  SEPARATOR_GAP
13
16
  } from "./chunk-3CIOJ5YJ.js";
14
- import {
15
- wrapDescriptionLines
16
- } from "./chunk-BMR4CIEO.js";
17
17
  import {
18
18
  formatDateLabel
19
- } from "./chunk-ZREZOIGS.js";
19
+ } from "./chunk-66RUL3XF.js";
20
20
  import {
21
21
  renderIntegratedLegend
22
- } from "./chunk-4UDKKGHH.js";
22
+ } from "./chunk-HEVYAHLW.js";
23
23
  import {
24
24
  TITLE_FONT_SIZE,
25
25
  TITLE_FONT_WEIGHT,
@@ -41,7 +41,7 @@ import {
41
41
  import {
42
42
  resolveActiveTagGroup,
43
43
  resolveTagColor
44
- } from "./chunk-HBVD67XG.js";
44
+ } from "./chunk-FJUNPCYI.js";
45
45
  import {
46
46
  resolveColor
47
47
  } from "./chunk-TLVIHFRO.js";
@@ -987,4 +987,4 @@ export {
987
987
  focusEventLine,
988
988
  renderEventLineForExport
989
989
  };
990
- //# sourceMappingURL=chunk-6SA7FJE4.js.map
990
+ //# sourceMappingURL=chunk-7QKLIMRZ.js.map
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  injectDefaultTagMetadata,
13
13
  resolveTagColor
14
- } from "./chunk-HBVD67XG.js";
14
+ } from "./chunk-FJUNPCYI.js";
15
15
 
16
16
  // src/mindmap/text-wrap.ts
17
17
  var H_PAD = 16;
@@ -547,4 +547,4 @@ export {
547
547
  computeNodeText,
548
548
  layoutMindmap
549
549
  };
550
- //# sourceMappingURL=chunk-YTN572Q2.js.map
550
+ //# sourceMappingURL=chunk-7TV5Y53Y.js.map
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-3CIOJ5YJ.js";
9
9
  import {
10
10
  renderIntegratedLegend
11
- } from "./chunk-4UDKKGHH.js";
11
+ } from "./chunk-HEVYAHLW.js";
12
12
  import {
13
13
  getMaxLegendReservedHeight
14
14
  } from "./chunk-7UEMZREK.js";
@@ -28,7 +28,7 @@ import {
28
28
  resolveActiveTagGroup,
29
29
  resolveTagColor,
30
30
  tagAttrKey
31
- } from "./chunk-HBVD67XG.js";
31
+ } from "./chunk-FJUNPCYI.js";
32
32
 
33
33
  // src/sketch/renderer.ts
34
34
  import * as d3 from "d3-selection";
@@ -338,7 +338,35 @@ function renderSketch(container, parsed, layout, palette, isDark, options = {})
338
338
  const showTitle = !!parsed.title;
339
339
  const titleOffset = showTitle ? 40 : 0;
340
340
  const legendGroups = parsed.options.noLegend ? [] : tagGroups;
341
- const contentWidth = layout.width + 2 * DIAGRAM_PADDING;
341
+ const edgeGeom = sketchEdgeGeometry(layout);
342
+ let minX = 0;
343
+ let minY = 0;
344
+ let maxX = layout.width;
345
+ let maxY = layout.height;
346
+ for (let i = 0; i < edgeGeom.length; i++) {
347
+ const g = edgeGeom[i];
348
+ if (!g) continue;
349
+ const nums = (g.d.match(/-?[\d.]+/g) ?? []).map(Number);
350
+ for (let k = 0; k + 1 < nums.length; k += 2) {
351
+ minX = Math.min(minX, nums[k]);
352
+ maxX = Math.max(maxX, nums[k]);
353
+ minY = Math.min(minY, nums[k + 1]);
354
+ maxY = Math.max(maxY, nums[k + 1]);
355
+ }
356
+ const label = layout.edges[i]?.label;
357
+ if (label) {
358
+ const hw = measureText(label, EDGE_LABEL_FONT_SIZE) / 2 + 6;
359
+ const hh = EDGE_LABEL_FONT_SIZE / 2 + 4;
360
+ minX = Math.min(minX, g.mid.x - hw);
361
+ maxX = Math.max(maxX, g.mid.x + hw);
362
+ minY = Math.min(minY, g.mid.y - hh);
363
+ maxY = Math.max(maxY, g.mid.y + hh);
364
+ }
365
+ }
366
+ const extraLeft = Math.max(0, -minX);
367
+ const extraTop = Math.max(0, -minY);
368
+ const boundW = maxX + extraLeft;
369
+ const contentWidth = boundW + 2 * DIAGRAM_PADDING;
342
370
  const width = Math.max(contentWidth, options.exportDims?.width ?? 0);
343
371
  const legendHeight = legendGroups.length > 0 ? getMaxLegendReservedHeight(
344
372
  {
@@ -348,9 +376,9 @@ function renderSketch(container, parsed, layout, palette, isDark, options = {})
348
376
  },
349
377
  width
350
378
  ) : 0;
351
- const contentTop = titleOffset + legendHeight + DIAGRAM_PADDING;
379
+ const contentTop = titleOffset + legendHeight + DIAGRAM_PADDING + extraTop;
352
380
  const height = Math.max(
353
- contentTop + layout.height + DIAGRAM_PADDING,
381
+ contentTop + maxY + DIAGRAM_PADDING,
354
382
  options.exportDims?.height ?? 0
355
383
  );
356
384
  d3.select(container).selectAll("*").remove();
@@ -389,44 +417,23 @@ function renderSketch(container, parsed, layout, palette, isDark, options = {})
389
417
  });
390
418
  legendG.selectAll("[data-legend-group]").classed("sk-legend-group", true);
391
419
  }
392
- const contentX = DIAGRAM_PADDING + Math.max(0, (width - contentWidth) / 2);
420
+ const contentX = DIAGRAM_PADDING + extraLeft + Math.max(0, (width - contentWidth) / 2);
393
421
  const root = svg.append("g").attr("class", "sk-root").attr("transform", `translate(${contentX},${contentTop})`);
394
- const rectById = /* @__PURE__ */ new Map();
395
- for (const node of layout.nodes) {
396
- rectById.set(node.id, { x: node.x, y: node.y, w: node.w, h: node.h });
397
- }
398
- for (const box of layout.boxes) {
399
- rectById.set(box.id, { x: box.x, y: box.y, w: box.w, h: box.h });
400
- }
401
- const portsById = /* @__PURE__ */ new Map();
402
- for (const box of layout.boxes) {
403
- const kids = layout.nodes.filter((n) => n.boxLabel === box.label);
404
- const ys = kids.map((k) => k.y + k.h / 2);
405
- const xs = kids.map((k) => k.x + k.w / 2);
406
- ys.push(box.y + box.h / 2);
407
- xs.push(box.x + box.w / 2);
408
- portsById.set(box.id, { ys, xs });
409
- }
410
422
  const boxLayer = root.append("g").attr("class", "sk-boxes");
411
423
  for (const box of layout.boxes) {
412
424
  drawBoxFrame(boxLayer, box, colorsFor(box.metadata, true), palette);
413
425
  }
414
426
  const edgeLayer = root.append("g").attr("class", "sk-edges");
415
427
  const labelLayerData = [];
416
- for (const edge of layout.edges) {
417
- const source = rectById.get(edge.sourceId);
418
- const target = rectById.get(edge.targetId);
419
- if (!source || !target) continue;
428
+ for (let ei = 0; ei < layout.edges.length; ei++) {
429
+ const edge = layout.edges[ei];
430
+ const geo = edgeGeom[ei];
431
+ if (!geo) continue;
420
432
  const color = flowColor(edge);
421
433
  const hex = color.replace("#", "");
422
- const { d, mid } = edgePath(
423
- source,
424
- target,
425
- portsById.get(edge.sourceId),
426
- portsById.get(edge.targetId)
427
- );
434
+ const { d, mid } = geo;
428
435
  const g = edgeLayer.append("g").attr("class", "sk-edge-group").attr("data-from", edge.sourceId).attr("data-to", edge.targetId).attr("data-line-number", edge.lineNumber);
429
- const path = g.append("path").attr("d", d).attr("fill", "none").attr("stroke", color).attr("stroke-width", EDGE_STROKE_WIDTH);
436
+ const path = g.append("path").attr("d", geo.dRender ?? d).attr("fill", "none").attr("stroke", color).attr("stroke-width", EDGE_STROKE_WIDTH);
430
437
  if (edge.dashed) path.attr("stroke-dasharray", DASH);
431
438
  if (edge.heads === "one" || edge.heads === "both") {
432
439
  path.attr("marker-end", `url(#sk-arrow-${hex})`);
@@ -442,10 +449,78 @@ function renderSketch(container, parsed, layout, palette, isDark, options = {})
442
449
  text: edge.label,
443
450
  color,
444
451
  from: edge.sourceId,
445
- to: edge.targetId
452
+ to: edge.targetId,
453
+ ei,
454
+ cp: (d.match(/-?[\d.]+/g) ?? []).map(Number)
446
455
  });
447
456
  }
448
457
  }
458
+ {
459
+ const labelW = (t) => t.length * EDGE_LABEL_FONT_SIZE * 0.56;
460
+ const halfH = EDGE_LABEL_FONT_SIZE / 2 + 3;
461
+ const GAP = 4;
462
+ const shapeRects = [
463
+ ...layout.nodes.map((n) => ({ x: n.x, y: n.y, w: n.w, h: n.h })),
464
+ ...layout.boxes.map((b) => ({ x: b.x, y: b.y, w: b.w, h: b.h }))
465
+ ];
466
+ const cubicPt = (cp, t) => {
467
+ const u = 1 - t;
468
+ const a = u * u * u;
469
+ const b = 3 * u * u * t;
470
+ const c = 3 * u * t * t;
471
+ const dd = t * t * t;
472
+ return {
473
+ x: a * cp[0] + b * cp[2] + c * cp[4] + dd * cp[6],
474
+ y: a * cp[1] + b * cp[3] + c * cp[5] + dd * cp[7]
475
+ };
476
+ };
477
+ const allPolys = edgeGeom.map((g) => {
478
+ if (!g) return null;
479
+ const cp = (g.d.match(/-?[\d.]+/g) ?? []).map(Number);
480
+ if (cp.length < 8) return null;
481
+ const pts = [];
482
+ for (let s = 0; s <= 32; s++) pts.push(cubicPt(cp, s / 32));
483
+ return pts;
484
+ });
485
+ const covers = (cx, cy, hw, ox, oy, ohw, ohh) => Math.abs(cx - ox) < hw + ohw + GAP && Math.abs(cy - oy) < halfH + ohh + GAP;
486
+ const placed = [];
487
+ const TS = [0.5, 0.4, 0.6, 0.32, 0.68, 0.25, 0.75, 0.18, 0.82];
488
+ for (const l of labelLayerData) {
489
+ const hw = labelW(l.text) / 2 + 3;
490
+ let bestT = 0.5;
491
+ let bestScore = Infinity;
492
+ for (const t of TS) {
493
+ const p2 = cubicPt(l.cp, t);
494
+ let labelHits = 0;
495
+ for (const q of placed)
496
+ if (covers(p2.x, p2.y, hw, q.x, q.y, q.hw, halfH)) labelHits++;
497
+ let shapeHits = 0;
498
+ for (const r of shapeRects)
499
+ if (covers(p2.x, p2.y, hw, r.x + r.w / 2, r.y + r.h / 2, r.w / 2, r.h / 2))
500
+ shapeHits++;
501
+ let lineHits = 0;
502
+ for (let k = 0; k < allPolys.length; k++) {
503
+ if (k === l.ei) continue;
504
+ const poly = allPolys[k];
505
+ if (poly?.some((pt) => covers(p2.x, p2.y, hw, pt.x, pt.y, 0, 0)))
506
+ lineHits++;
507
+ }
508
+ if (labelHits === 0 && shapeHits === 0 && lineHits === 0) {
509
+ bestT = t;
510
+ break;
511
+ }
512
+ const score = labelHits * 10 + shapeHits * 6 + lineHits * 4 + Math.abs(t - 0.5);
513
+ if (score < bestScore) {
514
+ bestScore = score;
515
+ bestT = t;
516
+ }
517
+ }
518
+ const p = cubicPt(l.cp, bestT);
519
+ l.x = p.x;
520
+ l.y = p.y;
521
+ placed.push({ x: p.x, y: p.y, hw });
522
+ }
523
+ }
449
524
  const nodeLayer = root.append("g").attr("class", "sk-nodes");
450
525
  for (const node of layout.nodes) {
451
526
  let n = node;
@@ -465,9 +540,7 @@ function renderSketch(container, parsed, layout, palette, isDark, options = {})
465
540
  const labelLayer = root.append("g").attr("class", "sk-edge-labels");
466
541
  for (const l of labelLayerData) {
467
542
  const g = labelLayer.append("g").attr("class", "sk-edge-label").attr("data-from", l.from).attr("data-to", l.to);
468
- const textWidth = l.text.length * EDGE_LABEL_FONT_SIZE * 0.56;
469
- g.append("rect").attr("x", l.x - textWidth / 2 - 3).attr("y", l.y - EDGE_LABEL_FONT_SIZE / 2 - 3).attr("width", textWidth + 6).attr("height", EDGE_LABEL_FONT_SIZE + 6).attr("rx", 3).attr("fill", palette.bg);
470
- g.append("text").attr("x", l.x).attr("y", l.y).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", EDGE_LABEL_FONT_SIZE).attr("fill", l.color).text(l.text);
543
+ g.append("text").attr("x", l.x).attr("y", l.y).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", EDGE_LABEL_FONT_SIZE).attr("fill", l.color).attr("stroke", palette.bg).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("paint-order", "stroke").text(l.text);
471
544
  }
472
545
  if (options.onClickItem) {
473
546
  const handler = options.onClickItem;
@@ -600,55 +673,360 @@ function drawNode(layer, node, colors, palette, isDark, tagGroups) {
600
673
  });
601
674
  }
602
675
  }
603
- function edgePath(source, target, sourcePorts, targetPorts) {
676
+ function assignEdgeSides(edges, rectById) {
677
+ const cx = (r) => r.x + r.w / 2;
678
+ const cy = (r) => r.y + r.h / 2;
679
+ const facing = (dx, dy) => Math.abs(dx) >= Math.abs(dy) ? dx >= 0 ? "R" : "L" : dy >= 0 ? "B" : "T";
680
+ return edges.map((e) => {
681
+ const s = rectById.get(e.sourceId);
682
+ const t = rectById.get(e.targetId);
683
+ if (!s || !t) return null;
684
+ const dx = cx(t) - cx(s);
685
+ const dy = cy(t) - cy(s);
686
+ const xOverlap = Math.min(s.x + s.w, t.x + t.w) - Math.max(s.x, t.x);
687
+ const yOverlap = Math.min(s.y + s.h, t.y + t.h) - Math.max(s.y, t.y);
688
+ if (xOverlap >= 0 && xOverlap >= yOverlap) {
689
+ const below = dy >= 0;
690
+ return { s: below ? "B" : "T", t: below ? "T" : "B" };
691
+ }
692
+ if (yOverlap >= 0 && yOverlap >= xOverlap) {
693
+ const right = dx >= 0;
694
+ return { s: right ? "R" : "L", t: right ? "L" : "R" };
695
+ }
696
+ return { s: facing(dx, dy), t: facing(-dx, -dy) };
697
+ });
698
+ }
699
+ function edgePath(source, target, sSide, tSide, sourcePorts, targetPorts) {
604
700
  const acx = source.x + source.w / 2;
605
701
  const acy = source.y + source.h / 2;
606
702
  const bcx = target.x + target.w / 2;
607
703
  const bcy = target.y + target.h / 2;
608
- const horiz = Math.abs(bcx - acx) >= Math.abs(bcy - acy);
609
- let p0;
610
- let p1;
611
- let h0;
612
- let h1;
613
704
  const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
614
- const CORNER_FRAC = 0.25;
615
- const midClamp = (v, lo, hi) => {
616
- const m = (hi - lo) * CORNER_FRAC;
617
- return clamp(v, lo + m, hi - m);
618
- };
619
705
  const nearest = (arr, v) => arr.reduce((a, b) => Math.abs(b - v) < Math.abs(a - v) ? b : a, arr[0]);
620
- if (horiz) {
621
- const sign = bcx >= acx ? 1 : -1;
622
- const y0 = sourcePorts ? midClamp(nearest(sourcePorts.ys, bcy), source.y, source.y + source.h) : acy;
623
- const y1 = targetPorts ? midClamp(nearest(targetPorts.ys, acy), target.y, target.y + target.h) : bcy;
624
- p0 = { x: sign > 0 ? source.x + source.w : source.x, y: y0 };
625
- p1 = { x: sign > 0 ? target.x : target.x + target.w, y: y1 };
626
- const k = Math.max(
627
- CURVE_HANDLE_MIN,
628
- Math.min(CURVE_HANDLE_MAX, Math.abs(p1.x - p0.x) / 2)
629
- );
630
- h0 = { x: p0.x + sign * k, y: p0.y };
631
- h1 = { x: p1.x - sign * k, y: p1.y };
632
- } else {
633
- const sign = bcy >= acy ? 1 : -1;
634
- const x0 = sourcePorts ? midClamp(nearest(sourcePorts.xs, bcx), source.x, source.x + source.w) : acx;
635
- const x1 = targetPorts ? midClamp(nearest(targetPorts.xs, acx), target.x, target.x + target.w) : bcx;
636
- p0 = { x: x0, y: sign > 0 ? source.y + source.h : source.y };
637
- p1 = { x: x1, y: sign > 0 ? target.y : target.y + target.h };
638
- const k = Math.max(
639
- CURVE_HANDLE_MIN,
640
- Math.min(CURVE_HANDLE_MAX, Math.abs(p1.y - p0.y) / 2)
641
- );
642
- h0 = { x: p0.x, y: p0.y + sign * k };
643
- h1 = { x: p1.x, y: p1.y - sign * k };
644
- }
706
+ const attach = (side, rect, ports, toward) => {
707
+ if (side === "L" || side === "R") {
708
+ const x2 = side === "L" ? rect.x : rect.x + rect.w;
709
+ const y2 = ports ? clamp(nearest(ports.ys, toward.y), rect.y, rect.y + rect.h) : rect.y + rect.h / 2;
710
+ return { x: x2, y: y2 };
711
+ }
712
+ const y = side === "T" ? rect.y : rect.y + rect.h;
713
+ const x = ports ? clamp(nearest(ports.xs, toward.x), rect.x, rect.x + rect.w) : rect.x + rect.w / 2;
714
+ return { x, y };
715
+ };
716
+ const normal = (side) => side === "L" ? { x: -1, y: 0 } : side === "R" ? { x: 1, y: 0 } : side === "T" ? { x: 0, y: -1 } : { x: 0, y: 1 };
717
+ const p0 = attach(sSide, source, sourcePorts, { x: bcx, y: bcy });
718
+ const p1 = attach(tSide, target, targetPorts, { x: acx, y: acy });
719
+ const n0 = normal(sSide);
720
+ const n1 = normal(tSide);
721
+ const k = Math.max(
722
+ CURVE_HANDLE_MIN,
723
+ Math.min(CURVE_HANDLE_MAX, Math.hypot(p1.x - p0.x, p1.y - p0.y) / 2)
724
+ );
725
+ const h0 = { x: p0.x + n0.x * k, y: p0.y + n0.y * k };
726
+ const h1 = { x: p1.x + n1.x * k, y: p1.y + n1.y * k };
645
727
  return {
646
728
  d: `M ${p0.x} ${p0.y} C ${h0.x} ${h0.y}, ${h1.x} ${h1.y}, ${p1.x} ${p1.y}`,
647
- // Centered ON the line (no vertical offset): the opaque label halo masks
648
- // the segment behind it cleanly, so the label reads as sitting on the line
649
- // rather than floating awkwardly just above it.
650
- mid: { x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2 }
729
+ // Centered ON the line at its ARC midpoint (t=0.5 on the cubic), not the
730
+ // endpoint average: for a straight edge the two coincide, but for a curve
731
+ // that bulges out to route around a shape the arc midpoint sits at the
732
+ // bulge apex — in open space — while the endpoint average would land on the
733
+ // very shape the edge detoured past. The opaque halo masks the line behind.
734
+ mid: {
735
+ x: 0.125 * p0.x + 0.375 * h0.x + 0.375 * h1.x + 0.125 * p1.x,
736
+ y: 0.125 * p0.y + 0.375 * h0.y + 0.375 * h1.y + 0.125 * p1.y
737
+ },
738
+ p0,
739
+ h0,
740
+ h1,
741
+ p1
742
+ };
743
+ }
744
+ var SIDES = ["T", "B", "L", "R"];
745
+ function cubicAt(g, t) {
746
+ const u = 1 - t;
747
+ const a = u * u * u;
748
+ const b = 3 * u * u * t;
749
+ const c = 3 * u * t * t;
750
+ const dd = t * t * t;
751
+ return {
752
+ x: a * g.p0.x + b * g.h0.x + c * g.h1.x + dd * g.p1.x,
753
+ y: a * g.p0.y + b * g.h0.y + c * g.h1.y + dd * g.p1.y
754
+ };
755
+ }
756
+ var EDGE_OBSTACLE_INSET = 6;
757
+ var EDGE_SAMPLES = 24;
758
+ var POLY_SAMPLES = 19;
759
+ function polyline(g) {
760
+ const pts = [];
761
+ for (let i = 0; i <= POLY_SAMPLES; i++)
762
+ pts.push(cubicAt(g, i / POLY_SAMPLES));
763
+ return pts;
764
+ }
765
+ function segmentsCross(p, p2, q, q2) {
766
+ const o = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
767
+ const d1 = o(p, p2, q);
768
+ const d2 = o(p, p2, q2);
769
+ const d32 = o(q, q2, p);
770
+ const d4 = o(q, q2, p2);
771
+ return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d32 > 0 && d4 < 0 || d32 < 0 && d4 > 0);
772
+ }
773
+ function polyCrossings(a, b) {
774
+ let n = 0;
775
+ for (let i = 0; i + 1 < a.length; i++)
776
+ for (let j = 0; j + 1 < b.length; j++)
777
+ if (segmentsCross(a[i], a[i + 1], b[j], b[j + 1])) n++;
778
+ return n;
779
+ }
780
+ function polyIntersection(a, b) {
781
+ for (let i = 0; i + 1 < a.length; i++) {
782
+ const p = a[i];
783
+ const p2 = a[i + 1];
784
+ for (let j = 0; j + 1 < b.length; j++) {
785
+ const q = b[j];
786
+ const q2 = b[j + 1];
787
+ if (!segmentsCross(p, p2, q, q2)) continue;
788
+ const rx = p2.x - p.x;
789
+ const ry = p2.y - p.y;
790
+ const sx = q2.x - q.x;
791
+ const sy = q2.y - q.y;
792
+ const denom = rx * sy - ry * sx;
793
+ if (denom === 0) continue;
794
+ const t = ((q.x - p.x) * sy - (q.y - p.y) * sx) / denom;
795
+ return { x: p.x + t * rx, y: p.y + t * ry };
796
+ }
797
+ }
798
+ return null;
799
+ }
800
+ var HOP_R = 7;
801
+ function hoppedPath(g, hops) {
802
+ if (hops.length === 0) return null;
803
+ const N = 49;
804
+ const pts = [];
805
+ for (let s = 0; s <= N; s++) pts.push(cubicAt(g, s / N));
806
+ const onSeg = /* @__PURE__ */ new Map();
807
+ for (const hp of hops) {
808
+ let bestSeg = -1;
809
+ let bestD = Infinity;
810
+ let bestPt = null;
811
+ for (let k = 1; k <= N; k++) {
812
+ const a = pts[k - 1];
813
+ const b = pts[k];
814
+ const dx = b.x - a.x;
815
+ const dy = b.y - a.y;
816
+ const len2 = dx * dx + dy * dy || 1;
817
+ let f = ((hp.x - a.x) * dx + (hp.y - a.y) * dy) / len2;
818
+ f = Math.max(0, Math.min(1, f));
819
+ const px = a.x + f * dx;
820
+ const py = a.y + f * dy;
821
+ const d2 = Math.hypot(hp.x - px, hp.y - py);
822
+ if (d2 < bestD) {
823
+ bestD = d2;
824
+ bestSeg = k;
825
+ bestPt = { x: px, y: py };
826
+ }
827
+ }
828
+ if (bestSeg >= 3 && bestSeg <= N - 3 && bestPt && !onSeg.has(bestSeg))
829
+ onSeg.set(bestSeg, bestPt);
830
+ }
831
+ if (onSeg.size === 0) return null;
832
+ let d = `M ${pts[0].x} ${pts[0].y}`;
833
+ for (let k = 1; k <= N; k++) {
834
+ const a = pts[k - 1];
835
+ const b = pts[k];
836
+ const hp = onSeg.get(k);
837
+ if (hp) {
838
+ const segLen = Math.hypot(b.x - a.x, b.y - a.y) || 1;
839
+ const dir = { x: (b.x - a.x) / segLen, y: (b.y - a.y) / segLen };
840
+ let perp = { x: dir.y, y: -dir.x };
841
+ if (perp.y > 0) perp = { x: -perp.x, y: -perp.y };
842
+ const A = { x: hp.x - dir.x * HOP_R, y: hp.y - dir.y * HOP_R };
843
+ const B = { x: hp.x + dir.x * HOP_R, y: hp.y + dir.y * HOP_R };
844
+ const k1 = {
845
+ x: A.x + perp.x * HOP_R * 1.33,
846
+ y: A.y + perp.y * HOP_R * 1.33
847
+ };
848
+ const k2 = {
849
+ x: B.x + perp.x * HOP_R * 1.33,
850
+ y: B.y + perp.y * HOP_R * 1.33
851
+ };
852
+ d += ` L ${A.x} ${A.y} C ${k1.x} ${k1.y}, ${k2.x} ${k2.y}, ${B.x} ${B.y} L ${b.x} ${b.y}`;
853
+ } else {
854
+ d += ` L ${b.x} ${b.y}`;
855
+ }
856
+ }
857
+ return d;
858
+ }
859
+ function pathCrossings(g, obstacles) {
860
+ if (obstacles.length === 0) return 0;
861
+ let hits = 0;
862
+ for (let i = 1; i < EDGE_SAMPLES; i++) {
863
+ const p = cubicAt(g, i / EDGE_SAMPLES);
864
+ for (const r of obstacles) {
865
+ const x0 = r.x + EDGE_OBSTACLE_INSET;
866
+ const y0 = r.y + EDGE_OBSTACLE_INSET;
867
+ const x1 = r.x + r.w - EDGE_OBSTACLE_INSET;
868
+ const y1 = r.y + r.h - EDGE_OBSTACLE_INSET;
869
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1) {
870
+ hits++;
871
+ break;
872
+ }
873
+ }
874
+ }
875
+ return hits;
876
+ }
877
+ function sketchEdgeGeometry(layout, offsets) {
878
+ const off = (id) => offsets?.get(id) ?? { dx: 0, dy: 0 };
879
+ const rectById = /* @__PURE__ */ new Map();
880
+ for (const n of layout.nodes) {
881
+ const o = off(n.id);
882
+ rectById.set(n.id, { x: n.x + o.dx, y: n.y + o.dy, w: n.w, h: n.h });
883
+ }
884
+ for (const b of layout.boxes) {
885
+ const o = off(b.id);
886
+ rectById.set(b.id, { x: b.x + o.dx, y: b.y + o.dy, w: b.w, h: b.h });
887
+ }
888
+ const portsById = /* @__PURE__ */ new Map();
889
+ for (const box of layout.boxes) {
890
+ const o = off(box.id);
891
+ const kids = layout.nodes.filter((n) => n.boxLabel === box.label);
892
+ const ys = kids.map((k) => k.y + off(k.id).dy + k.h / 2);
893
+ const xs = kids.map((k) => k.x + off(k.id).dx + k.w / 2);
894
+ ys.push(box.y + o.dy + box.h / 2);
895
+ xs.push(box.x + o.dx + box.w / 2);
896
+ portsById.set(box.id, { ys, xs });
897
+ }
898
+ const sides = assignEdgeSides(layout.edges, rectById);
899
+ const boxIdByLabel = /* @__PURE__ */ new Map();
900
+ for (const b of layout.boxes) boxIdByLabel.set(b.label, b.id);
901
+ const boxOf = (id) => {
902
+ const n = layout.nodes.find((m) => m.id === id);
903
+ return n?.boxLabel ? boxIdByLabel.get(n.boxLabel) : void 0;
904
+ };
905
+ const obstacles = [];
906
+ for (const [id, rect] of rectById) obstacles.push({ id, rect });
907
+ const normalOf = (s) => s === "L" ? { x: -1, y: 0 } : s === "R" ? { x: 1, y: 0 } : s === "T" ? { x: 0, y: -1 } : { x: 0, y: 1 };
908
+ const facingCost = (from, to, side) => {
909
+ const dx = to.x + to.w / 2 - (from.x + from.w / 2);
910
+ const dy = to.y + to.h / 2 - (from.y + from.h / 2);
911
+ const len = Math.hypot(dx, dy) || 1;
912
+ const n = normalOf(side);
913
+ return 1 - (n.x * dx + n.y * dy) / len;
651
914
  };
915
+ const ctx = layout.edges.map((edge) => {
916
+ const source = rectById.get(edge.sourceId);
917
+ const target = rectById.get(edge.targetId);
918
+ if (!source || !target) return null;
919
+ const exclude = new Set(
920
+ [
921
+ edge.sourceId,
922
+ edge.targetId,
923
+ boxOf(edge.sourceId),
924
+ boxOf(edge.targetId)
925
+ ].filter((x) => x !== void 0)
926
+ );
927
+ return {
928
+ source,
929
+ target,
930
+ sPorts: portsById.get(edge.sourceId),
931
+ tPorts: portsById.get(edge.targetId),
932
+ obstacleRects: obstacles.filter((o) => !exclude.has(o.id)).map((o) => o.rect),
933
+ adjacent: /* @__PURE__ */ new Set()
934
+ };
935
+ });
936
+ for (let a = 0; a < layout.edges.length; a++) {
937
+ for (let b = a + 1; b < layout.edges.length; b++) {
938
+ const ea = layout.edges[a];
939
+ const eb = layout.edges[b];
940
+ if (ea.sourceId === eb.sourceId || ea.sourceId === eb.targetId || ea.targetId === eb.sourceId || ea.targetId === eb.targetId) {
941
+ ctx[a]?.adjacent.add(b);
942
+ ctx[b]?.adjacent.add(a);
943
+ }
944
+ }
945
+ }
946
+ const W_NODE = 100;
947
+ const W_EDGE = 8;
948
+ const scoreOf = (c, g, poly, self, polys2, s, t) => {
949
+ let edgeCross = 0;
950
+ for (let j = 0; j < polys2.length; j++) {
951
+ if (j === self || c.adjacent.has(j)) continue;
952
+ const pj = polys2[j];
953
+ if (pj) edgeCross += polyCrossings(poly, pj);
954
+ }
955
+ return pathCrossings(g, c.obstacleRects) * W_NODE + edgeCross * W_EDGE + facingCost(c.source, c.target, s) + facingCost(c.target, c.source, t);
956
+ };
957
+ const chosen = layout.edges.map(
958
+ (_, i) => sides[i] ?? { s: "R", t: "L" }
959
+ );
960
+ const geoms = ctx.map(
961
+ (c, i) => c ? edgePath(
962
+ c.source,
963
+ c.target,
964
+ chosen[i].s,
965
+ chosen[i].t,
966
+ c.sPorts,
967
+ c.tPorts
968
+ ) : null
969
+ );
970
+ const polys = geoms.map((g) => g ? polyline(g) : null);
971
+ for (let pass = 0; pass < 3; pass++) {
972
+ let changed = false;
973
+ for (let i = 0; i < ctx.length; i++) {
974
+ const c = ctx[i];
975
+ const g = geoms[i];
976
+ const p = polys[i];
977
+ if (!c || !g || !p) continue;
978
+ const cur = chosen[i];
979
+ const curScore = scoreOf(c, g, p, i, polys, cur.s, cur.t);
980
+ if (curScore < W_EDGE) continue;
981
+ let bestScore = curScore;
982
+ let bestG = g;
983
+ let bestP = p;
984
+ let bestSide = cur;
985
+ for (const s of SIDES) {
986
+ for (const t of SIDES) {
987
+ const cg = edgePath(c.source, c.target, s, t, c.sPorts, c.tPorts);
988
+ const cp = polyline(cg);
989
+ const sc = scoreOf(c, cg, cp, i, polys, s, t);
990
+ if (sc < bestScore) {
991
+ bestScore = sc;
992
+ bestG = cg;
993
+ bestP = cp;
994
+ bestSide = { s, t };
995
+ }
996
+ }
997
+ }
998
+ if (bestSide !== cur) {
999
+ chosen[i] = bestSide;
1000
+ geoms[i] = bestG;
1001
+ polys[i] = bestP;
1002
+ changed = true;
1003
+ }
1004
+ }
1005
+ if (!changed) break;
1006
+ }
1007
+ const hopsFor = layout.edges.map(() => []);
1008
+ for (let i = 0; i < polys.length; i++) {
1009
+ for (let j = i + 1; j < polys.length; j++) {
1010
+ if (ctx[i]?.adjacent.has(j)) continue;
1011
+ const a = polys[i];
1012
+ const b = polys[j];
1013
+ if (!a || !b) continue;
1014
+ const pt = polyIntersection(a, b);
1015
+ if (pt) hopsFor[j].push(pt);
1016
+ }
1017
+ }
1018
+ return layout.edges.map((edge, i) => {
1019
+ const g = geoms[i];
1020
+ if (!g) return null;
1021
+ const dRender = hoppedPath(g, hopsFor[i]);
1022
+ return {
1023
+ sourceId: edge.sourceId,
1024
+ targetId: edge.targetId,
1025
+ d: g.d,
1026
+ mid: g.mid,
1027
+ ...dRender && { dRender }
1028
+ };
1029
+ });
652
1030
  }
653
1031
  function renderSketchForExport(container, parsed, layout, palette, isDark, options = {}) {
654
1032
  renderSketch(container, parsed, layout, palette, isDark, {
@@ -659,6 +1037,7 @@ function renderSketchForExport(container, parsed, layout, palette, isDark, optio
659
1037
 
660
1038
  export {
661
1039
  renderSketch,
1040
+ sketchEdgeGeometry,
662
1041
  renderSketchForExport
663
1042
  };
664
- //# sourceMappingURL=chunk-6CLKNXVP.js.map
1043
+ //# sourceMappingURL=chunk-BBGDM4NQ.js.map