@diagrammo/dgmo 0.48.2 → 0.49.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 (216) hide show
  1. package/.cursorrules +8 -3
  2. package/.github/copilot-instructions.md +8 -3
  3. package/.windsurfrules +8 -3
  4. package/SKILL.md +8 -3
  5. package/dist/advanced.d.ts +444 -1
  6. package/dist/advanced.js +208 -146
  7. package/dist/auto.css +63 -0
  8. package/dist/auto.js +202 -102
  9. package/dist/auto.mjs +6849 -824
  10. package/dist/block.css +63 -0
  11. package/dist/block.d.ts +13 -5
  12. package/dist/block.js +137 -57
  13. package/dist/{charts-d3-QOZHKTCT.js → charts-d3-EW3YQTEA.js} +9 -8
  14. package/dist/chunk-26RVGM44.js +331 -0
  15. package/dist/{chunk-44CM72SQ.js → chunk-2WMBWZY2.js} +3 -3
  16. package/dist/{chunk-L66GEFB6.js → chunk-35BBZDFE.js} +4 -4
  17. package/dist/{chunk-4GRI7MEB.js → chunk-3BIAFYD2.js} +6 -6
  18. package/dist/chunk-3NFJWFNP.js +1474 -0
  19. package/dist/{chunk-76OWZ3BA.js → chunk-4BD7EVPK.js} +2 -2
  20. package/dist/{chunk-MITI6NOL.js → chunk-4FIV4K4Z.js} +9 -9
  21. package/dist/chunk-4HVNZ7WF.js +574 -0
  22. package/dist/{chunk-X5LCYCL5.js → chunk-4NQ3IKUM.js} +2 -2
  23. package/dist/{chunk-PWIHZ6NJ.js → chunk-4UDKKGHH.js} +4 -4
  24. package/dist/{chunk-6NYWYNBI.js → chunk-4XK3FLFB.js} +3 -3
  25. package/dist/chunk-4YNP6O47.js +607 -0
  26. package/dist/{chunk-3MQQNXPD.js → chunk-5FGDJIJX.js} +3 -3
  27. package/dist/{chunk-WCT3NYCV.js → chunk-5LWQH6QV.js} +1 -1
  28. package/dist/chunk-5XCM7UUW.js +261 -0
  29. package/dist/{chunk-RYEGFMUO.js → chunk-5ZZLLBFK.js} +2 -2
  30. package/dist/{chunk-EB6FPQUP.js → chunk-65RTDUZH.js} +3 -3
  31. package/dist/chunk-6CLKNXVP.js +664 -0
  32. package/dist/{chunk-H6PI3KJT.js → chunk-6IUAK7RZ.js} +2 -2
  33. package/dist/{chunk-ZGR7ULFJ.js → chunk-6SA7FJE4.js} +31 -19
  34. package/dist/{chunk-LIOHW36Z.js → chunk-6VE3VLWM.js} +3 -3
  35. package/dist/{chunk-AN2MOO5T.js → chunk-7OVSJI2V.js} +3 -3
  36. package/dist/{chunk-YAA6IID3.js → chunk-7UEMZREK.js} +2 -2
  37. package/dist/{chunk-FCIIHT24.js → chunk-7VLUGEMJ.js} +7 -7
  38. package/dist/{chunk-VOKGH627.js → chunk-AHGF2YYA.js} +13 -3
  39. package/dist/{chunk-3R5POCTZ.js → chunk-AJ2PEY7J.js} +6 -6
  40. package/dist/{chunk-PBHE5UG6.js → chunk-ALRHIR6I.js} +46 -12
  41. package/dist/{chunk-7JEGTRLD.js → chunk-B57V3DWN.js} +3 -3
  42. package/dist/{chunk-ILKORZ3H.js → chunk-BCT5G2VN.js} +7 -7
  43. package/dist/{chunk-TYS6CSS5.js → chunk-BCYVE3N5.js} +2 -2
  44. package/dist/{chunk-ZBWMSD5N.js → chunk-BGJV5ATD.js} +4 -4
  45. package/dist/{chunk-G2H74XJZ.js → chunk-BPIJLRYE.js} +8 -8
  46. package/dist/{chunk-YGJTDXVR.js → chunk-C4GQDB5L.js} +2 -2
  47. package/dist/{chunk-Y2ZE3MSG.js → chunk-C63UWAKM.js} +2 -2
  48. package/dist/{chunk-QEBUY5NY.js → chunk-CEHZOPGM.js} +4 -4
  49. package/dist/{chunk-725LDASD.js → chunk-CLD2OKV7.js} +2 -2
  50. package/dist/{chunk-5QXL4NYS.js → chunk-CLR4A2N2.js} +10 -5
  51. package/dist/{chunk-WBRZJOEC.js → chunk-CMCR5WBW.js} +3 -3
  52. package/dist/{chunk-SML67VPV.js → chunk-D5AVQCDN.js} +3 -3
  53. package/dist/{chunk-2LACINWN.js → chunk-DDTJ5N7Q.js} +9 -9
  54. package/dist/{chunk-FITOLXSX.js → chunk-EOFPYPAU.js} +26 -2
  55. package/dist/{chunk-B65G5DNL.js → chunk-EYN7VFKL.js} +3 -3
  56. package/dist/{chunk-4OHACBGX.js → chunk-F2L5GCQ4.js} +2 -2
  57. package/dist/{chunk-RVMRZAPB.js → chunk-F743CM2N.js} +3 -3
  58. package/dist/{chunk-3JLKYGBQ.js → chunk-F7BFXWNQ.js} +2 -2
  59. package/dist/{chunk-WCSOJFU3.js → chunk-FMKTYMP4.js} +3 -3
  60. package/dist/{chunk-RTLBSQ3G.js → chunk-FUCAB3KQ.js} +3 -3
  61. package/dist/{chunk-SGZYXELT.js → chunk-GB3YZTAM.js} +9 -6
  62. package/dist/{chunk-QLCCJEJI.js → chunk-HBVD67XG.js} +2 -2
  63. package/dist/{chunk-PBAHEPLQ.js → chunk-HGN652MU.js} +7 -6
  64. package/dist/{chunk-3SMGIJDD.js → chunk-HHXOONLX.js} +13 -1
  65. package/dist/{chunk-QOOUC2J5.js → chunk-HQCHYB5F.js} +2 -2
  66. package/dist/chunk-IAEC4KLA.js +492 -0
  67. package/dist/{chunk-J5FIXQRT.js → chunk-IBC35BTK.js} +4 -4
  68. package/dist/{chunk-NURGB5Z4.js → chunk-IFK3GIUW.js} +2 -2
  69. package/dist/{chunk-FANX6UJR.js → chunk-IHLSHCPZ.js} +11 -11
  70. package/dist/{chunk-GMR5MFHJ.js → chunk-IUK7AGD4.js} +2 -2
  71. package/dist/chunk-IVKC4GID.js +32 -0
  72. package/dist/{chunk-OJR3MQYV.js → chunk-JFPY3UJ7.js} +296 -57
  73. package/dist/{chunk-5MLYVTYM.js → chunk-K6HNAY5V.js} +2 -2
  74. package/dist/{chunk-P235SVAH.js → chunk-KQAEKMIX.js} +2 -2
  75. package/dist/{chunk-7DTWEFUE.js → chunk-KUBBJ2CD.js} +10 -10
  76. package/dist/{chunk-BUVJFRPY.js → chunk-KV6Y2HE6.js} +4 -4
  77. package/dist/{chunk-OURHMODR.js → chunk-LG4TWW4U.js} +13 -13
  78. package/dist/{chunk-XAKEUMEJ.js → chunk-LVHPTBO3.js} +3 -3
  79. package/dist/{chunk-ERLBPDGU.js → chunk-MJCG5K2O.js} +2 -2
  80. package/dist/{chunk-THXCFRTU.js → chunk-N4J5KGWG.js} +12 -12
  81. package/dist/{chunk-MZSBFAPJ.js → chunk-NVT6C2VE.js} +9 -9
  82. package/dist/{chunk-AELOBFW5.js → chunk-O5MQPK7E.js} +3 -3
  83. package/dist/{chunk-JM4LEPY3.js → chunk-O7NUCTFU.js} +2 -2
  84. package/dist/{chunk-JED6ZDEE.js → chunk-O7WP4ZD4.js} +2 -2
  85. package/dist/{chunk-ZO27FGJS.js → chunk-PH5X4GIP.js} +2 -2
  86. package/dist/{chunk-B7JUCQUA.js → chunk-PU3AUQPA.js} +4 -4
  87. package/dist/{chunk-YGJZWZTY.js → chunk-PZFVK3IH.js} +2 -2
  88. package/dist/chunk-QQJUTGQH.js +135 -0
  89. package/dist/{chunk-HSH73DP6.js → chunk-RI4NIMFP.js} +99 -39
  90. package/dist/{chunk-33LCUH4H.js → chunk-RTERS6S5.js} +3 -3
  91. package/dist/{chunk-T3UBLACL.js → chunk-SCQ5VPUF.js} +19 -5
  92. package/dist/chunk-SGHSY3LU.js +377 -0
  93. package/dist/{chunk-SRTU6HMQ.js → chunk-SJEGO2QL.js} +8 -8
  94. package/dist/chunk-SQDFK5ZQ.js +273 -0
  95. package/dist/{chunk-PZMETGLY.js → chunk-SVDWQGX2.js} +296 -121
  96. package/dist/{chunk-Z5CGSFJB.js → chunk-T7IKPCMI.js} +4 -4
  97. package/dist/chunk-TC3YWBFO.js +233 -0
  98. package/dist/{chunk-EVCVDTXE.js → chunk-TLKMTQYL.js} +3 -3
  99. package/dist/{chunk-IJZFN3HM.js → chunk-TWQUYVP3.js} +4 -4
  100. package/dist/{chunk-IZMRON6C.js → chunk-UVCXZEGU.js} +2 -2
  101. package/dist/{chunk-CKNVO24D.js → chunk-UWDO3O52.js} +2 -2
  102. package/dist/{chunk-RJE5JTVK.js → chunk-VC3TR4NL.js} +7 -7
  103. package/dist/{chunk-CI2G3OEB.js → chunk-X2CH42BN.js} +9 -9
  104. package/dist/{chunk-6HO5ZTXK.js → chunk-XCC2RENT.js} +2 -2
  105. package/dist/chunk-XHVV4NVW.js +51 -0
  106. package/dist/{chunk-ZJEL4H2R.js → chunk-XJ7J6WOO.js} +9 -9
  107. package/dist/{chunk-ULOEATBZ.js → chunk-XKTN7CZF.js} +106 -29
  108. package/dist/{chunk-IPWPWM6R.js → chunk-YM2BSPYW.js} +111 -10
  109. package/dist/{chunk-BUPDIHML.js → chunk-YRVLP7UU.js} +3 -3
  110. package/dist/{chunk-T2OE3YQB.js → chunk-YTN572Q2.js} +3 -3
  111. package/dist/{chunk-WVAJFAIA.js → chunk-YYX3SU6H.js} +3 -3
  112. package/dist/{chunk-MO5YHHMQ.js → chunk-ZDUQGL3N.js} +2 -2
  113. package/dist/{chunk-Y3J7VR52.js → chunk-ZNQX6QWH.js} +30 -2
  114. package/dist/{chunk-UWN33VNX.js → chunk-ZREZOIGS.js} +8 -8
  115. package/dist/{chunk-6SM5NJQH.js → chunk-ZTLWVRNS.js} +4 -4
  116. package/dist/{chunk-RUNNI6VE.js → chunk-ZXL6MB4R.js} +3 -3
  117. package/dist/cli.cjs +159 -122
  118. package/dist/dgmo-router-ODKPEJ3P.js +79 -0
  119. package/dist/dimensions-WSIJXT5Q.js +22 -0
  120. package/dist/editor.js +4 -1
  121. package/dist/element.js +203 -103
  122. package/dist/element.mjs +6849 -824
  123. package/dist/{flowchart-parser-ZNSKIBVR.js → flowchart-parser-IVZN7CQN.js} +4 -4
  124. package/dist/{flowchart-renderer-SNDSII4D.js → flowchart-renderer-GMI2U3DD.js} +9 -9
  125. package/dist/highlight.js +7 -1
  126. package/dist/index.d.ts +5 -1
  127. package/dist/index.js +193 -232
  128. package/dist/{layout-HRBZPFCN.js → layout-34CLNICW.js} +5 -5
  129. package/dist/{layout-5RA6QWR6.js → layout-4Z5JM7PT.js} +6 -6
  130. package/dist/layout-6RDJHS6E.js +9 -0
  131. package/dist/{layout-7QXCNOVS.js → layout-7H4H46CS.js} +3 -3
  132. package/dist/{layout-46N4JJYK.js → layout-7SKMFYHL.js} +5 -5
  133. package/dist/{layout-HWDSRX5E.js → layout-D42XE4RE.js} +6 -6
  134. package/dist/{layout-EE6GE46Y.js → layout-DC4TVXER.js} +5 -5
  135. package/dist/layout-FGQWRYYW.js +14 -0
  136. package/dist/layout-GZ2CWONU.js +14 -0
  137. package/dist/layout-HQMBPJVS.js +10 -0
  138. package/dist/{layout-UM5F4DJB.js → layout-K2ZN3Q42.js} +5 -5
  139. package/dist/{layout-S2QBYSK2.js → layout-NXG63LWH.js} +3 -3
  140. package/dist/{layout-N76NR4ZO.js → layout-PUQCB6AN.js} +7 -7
  141. package/dist/{layout-HYZPWAK5.js → layout-QNUQLEHU.js} +3 -3
  142. package/dist/layout-S3EK4IYO.js +14 -0
  143. package/dist/{layout-search-OYV77HHS.js → layout-search-QS44PI5R.js} +6 -6
  144. package/dist/{parser-KNSQSKQO.js → parser-4BKIFG2C.js} +5 -5
  145. package/dist/{parser-OV7NOBW6.js → parser-4ROBKVUR.js} +4 -4
  146. package/dist/parser-667RGSUW.js +13 -0
  147. package/dist/{parser-IXSZZQHY.js → parser-BL7Z72EY.js} +4 -4
  148. package/dist/{parser-AQETEVRW.js → parser-CLMFZWCC.js} +4 -4
  149. package/dist/{parser-XKRP7QKP.js → parser-D6ONJQFR.js} +3 -3
  150. package/dist/{parser-2EWECDSA.js → parser-DERPY5KC.js} +4 -4
  151. package/dist/{parser-4OGOBJJ4.js → parser-GFSOPDB7.js} +4 -4
  152. package/dist/{parser-2MFP2H2E.js → parser-HVJFTOXL.js} +3 -3
  153. package/dist/{parser-VQP2UJ6W.js → parser-IKZFTLOJ.js} +3 -3
  154. package/dist/parser-J3P2D6IP.js +13 -0
  155. package/dist/{parser-PQ3POX45.js → parser-JD7OQ2E6.js} +4 -4
  156. package/dist/{parser-O5J4VBII.js → parser-K647FRI4.js} +3 -3
  157. package/dist/{parser-NGU3ETO6.js → parser-KPMN5KKE.js} +4 -4
  158. package/dist/{parser-4FL2CMDG.js → parser-MBJSBTL5.js} +4 -4
  159. package/dist/{parser-TFZFBVWN.js → parser-MCJWF5G3.js} +3 -3
  160. package/dist/{parser-NRAI6O25.js → parser-MRBPXXIV.js} +4 -4
  161. package/dist/{parser-6BTRLKRZ.js → parser-OBQG655V.js} +4 -4
  162. package/dist/{parser-3STE62GB.js → parser-QJYS7KQQ.js} +4 -4
  163. package/dist/{parser-XDVELHRK.js → parser-SMBJO3XN.js} +4 -4
  164. package/dist/{parser-TRTHDFPP.js → parser-TITK4YW6.js} +5 -5
  165. package/dist/{parser-I6KXEYFK.js → parser-TNYX5S7E.js} +4 -4
  166. package/dist/{parser-Y6WTBU76.js → parser-UBGPTIZD.js} +3 -3
  167. package/dist/parser-UGA23PYK.js +14 -0
  168. package/dist/{parser-N5O4IHUN.js → parser-WISPH7LW.js} +4 -4
  169. package/dist/{parser-CFZD5IBL.js → parser-XWUDFJ7P.js} +4 -4
  170. package/dist/parser-ZKF5K2H2.js +15 -0
  171. package/dist/{parser-ZSDPZYQP.js → parser-ZYC3XGNS.js} +4 -4
  172. package/dist/{renderer-AWWAF6XD.js → renderer-4IUPINAE.js} +10 -10
  173. package/dist/renderer-6L3NBEY5.js +24 -0
  174. package/dist/{renderer-ZRNG54CW.js → renderer-6T3KK4S3.js} +11 -11
  175. package/dist/{renderer-KQB7UGDS.js → renderer-7RH2SI3B.js} +3 -3
  176. package/dist/{renderer-6EEZHMH4.js → renderer-A5VFZQQK.js} +9 -9
  177. package/dist/{renderer-5MYBMBYO.js → renderer-BB5PREMY.js} +5 -5
  178. package/dist/{renderer-BYCNMBF7.js → renderer-CGW7QPNA.js} +10 -10
  179. package/dist/{renderer-QIWVHWZO.js → renderer-DAXMXIKD.js} +3 -3
  180. package/dist/{renderer-7MVLWWQD.js → renderer-DKESNMU5.js} +9 -9
  181. package/dist/{renderer-UV6MI64M.js → renderer-HVCU2C3Z.js} +3 -3
  182. package/dist/{renderer-VACVQYML.js → renderer-LWGJFT3D.js} +8 -8
  183. package/dist/{renderer-PGX2TKVS.js → renderer-MFJ7HY2H.js} +8 -8
  184. package/dist/{renderer-B2OMQIQG.js → renderer-MJDDU27W.js} +15 -15
  185. package/dist/{renderer-4JHRUBE6.js → renderer-QL5KO6SK.js} +10 -10
  186. package/dist/{renderer-W4M6QLIJ.js → renderer-QNHUXCRL.js} +14 -14
  187. package/dist/{renderer-ZGNTUWC4.js → renderer-ROV756WT.js} +8 -8
  188. package/dist/{renderer-YY2OSF6E.js → renderer-SQ3JUAI7.js} +9 -9
  189. package/dist/renderer-THD6V5TL.js +22 -0
  190. package/dist/{renderer-5NDVNDAZ.js → renderer-TPIBXNOS.js} +9 -9
  191. package/dist/{renderer-RPOA5YPZ.js → renderer-TYXRHMRI.js} +13 -13
  192. package/dist/{renderer-N43WR5GV.js → renderer-UZF3JROD.js} +11 -11
  193. package/dist/{renderer-6W5462RB.js → renderer-V3JEMWMM.js} +9 -9
  194. package/dist/{renderer-JGBKUCZD.js → renderer-VFUVRNLK.js} +4 -4
  195. package/dist/{renderer-Z7I3W2PP.js → renderer-VICQ4WNY.js} +8 -8
  196. package/dist/renderer-W5RMOURB.js +21 -0
  197. package/dist/renderer-XEKIRPI6.js +23 -0
  198. package/dist/{renderer-CLJHQPBJ.js → renderer-YHXPN3GF.js} +12 -12
  199. package/dist/renderer-radial-MHO2VDXN.js +21 -0
  200. package/dist/{resolver-NOAA7ID7.js → resolver-FHBCO3PL.js} +3 -3
  201. package/dist/{state-parser-Z2V23ATR.js → state-parser-RX3GXOIB.js} +4 -4
  202. package/dist/{state-renderer-B4DQBM6M.js → state-renderer-AJUKGNWU.js} +9 -9
  203. package/docs/ai-integration.md +8 -3
  204. package/docs/language-reference.md +179 -5
  205. package/gallery/fixtures/family.dgmo +23 -0
  206. package/gallery/fixtures/sketch.dgmo +22 -0
  207. package/package.json +1 -1
  208. package/dist/chunk-ODZVHU3R.js +0 -153
  209. package/dist/dgmo-router-X35VLZLM.js +0 -73
  210. package/dist/dimensions-J3FHIMJ6.js +0 -22
  211. package/dist/layout-5T6RH5M6.js +0 -14
  212. package/dist/layout-G23F7LDH.js +0 -9
  213. package/dist/layout-OB3RI63S.js +0 -14
  214. package/dist/parser-JAL2HD4I.js +0 -13
  215. package/dist/renderer-SMIBCRVB.js +0 -24
  216. package/dist/renderer-radial-C32GPJQ7.js +0 -21
@@ -0,0 +1,664 @@
1
+ import {
2
+ renderCollapseBar,
3
+ renderNodeCard
4
+ } from "./chunk-AHGF2YYA.js";
5
+ import {
6
+ CARD_RADIUS,
7
+ CONTAINER_RADIUS
8
+ } from "./chunk-3CIOJ5YJ.js";
9
+ import {
10
+ renderIntegratedLegend
11
+ } from "./chunk-4UDKKGHH.js";
12
+ import {
13
+ getMaxLegendReservedHeight
14
+ } from "./chunk-7UEMZREK.js";
15
+ import {
16
+ FONT_FAMILY
17
+ } from "./chunk-6KFWC4CO.js";
18
+ import {
19
+ measureText,
20
+ wrapTextToWidth
21
+ } from "./chunk-5LWQH6QV.js";
22
+ import {
23
+ contrastText,
24
+ mix,
25
+ shapeFill
26
+ } from "./chunk-P6T66CCZ.js";
27
+ import {
28
+ resolveActiveTagGroup,
29
+ resolveTagColor,
30
+ tagAttrKey
31
+ } from "./chunk-HBVD67XG.js";
32
+
33
+ // src/sketch/renderer.ts
34
+ import * as d3 from "d3-selection";
35
+
36
+ // src/sketch/markdown-card.ts
37
+ var BOLD_WIDTH_FACTOR = 1.06;
38
+ var INDENT_PX = 12;
39
+ var BULLET_HANG_PX = 12;
40
+ var ELLIPSIS = "\u2026";
41
+ var BULLET = "\u2022";
42
+ function segWidth(seg, fontSize) {
43
+ const w = measureText(seg.text, fontSize);
44
+ return seg.bold ? w * BOLD_WIDTH_FACTOR : w;
45
+ }
46
+ function parseInline(text) {
47
+ const segments = [];
48
+ const regex = /\*\*(.+?)\*\*|\[([^\]]*)\]\(([^)]*)\)|((?:(?!\*\*|\[).)+)/gs;
49
+ let m;
50
+ while ((m = regex.exec(text)) !== null) {
51
+ if (m[1] !== void 0) {
52
+ segments.push({ text: m[1], bold: true });
53
+ } else if (m[2] !== void 0) {
54
+ segments.push({ text: m[2], bold: false, ...m[3] && { href: m[3] } });
55
+ } else if (m[4] !== void 0) {
56
+ segments.push({ text: m[4], bold: false });
57
+ }
58
+ }
59
+ return segments;
60
+ }
61
+ function segmentsToWords(segments) {
62
+ const words = [];
63
+ let current = [];
64
+ for (const seg of segments) {
65
+ const parts = seg.text.split(/(\s+)/);
66
+ for (const part of parts) {
67
+ if (part === "") continue;
68
+ if (/^\s+$/.test(part)) {
69
+ if (current.length > 0) {
70
+ words.push(current);
71
+ current = [];
72
+ }
73
+ } else {
74
+ current.push({
75
+ text: part,
76
+ bold: seg.bold,
77
+ ...seg.href !== void 0 && { href: seg.href }
78
+ });
79
+ }
80
+ }
81
+ }
82
+ if (current.length > 0) words.push(current);
83
+ return words;
84
+ }
85
+ function wordWidth(word, fontSize) {
86
+ let w = 0;
87
+ for (const piece of word) w += segWidth(piece, fontSize);
88
+ return w;
89
+ }
90
+ function coalesce(pieces) {
91
+ const out = [];
92
+ for (const p of pieces) {
93
+ const last = out[out.length - 1];
94
+ if (last?.bold === p.bold && last.href === p.href) {
95
+ last.text += p.text;
96
+ } else {
97
+ out.push({
98
+ text: p.text,
99
+ bold: p.bold,
100
+ ...p.href !== void 0 && { href: p.href }
101
+ });
102
+ }
103
+ }
104
+ return out;
105
+ }
106
+ function wrapSegments(segments, fontSize, avail, textX, bulletX) {
107
+ const words = segmentsToWords(segments);
108
+ if (words.length === 0) {
109
+ return [
110
+ { segments: [], x: textX, ...bulletX !== void 0 && { bulletX } }
111
+ ];
112
+ }
113
+ const spaceW = measureText(" ", fontSize);
114
+ const lines = [];
115
+ let currentPieces = [];
116
+ let currentW = 0;
117
+ let first = true;
118
+ const flush = () => {
119
+ lines.push({
120
+ segments: coalesce(currentPieces),
121
+ x: textX,
122
+ // only the FIRST visual line of a bullet carries the marker
123
+ ...first && bulletX !== void 0 && { bulletX }
124
+ });
125
+ first = false;
126
+ currentPieces = [];
127
+ currentW = 0;
128
+ };
129
+ for (const word of words) {
130
+ const ww = wordWidth(word, fontSize);
131
+ const addW = currentPieces.length === 0 ? ww : spaceW + ww;
132
+ if (currentPieces.length > 0 && currentW + addW > avail) {
133
+ flush();
134
+ currentPieces.push(...word);
135
+ currentW = ww;
136
+ } else {
137
+ if (currentPieces.length > 0) {
138
+ currentPieces.push({ text: " ", bold: false });
139
+ currentW += spaceW;
140
+ }
141
+ currentPieces.push(...word);
142
+ currentW += ww;
143
+ }
144
+ }
145
+ flush();
146
+ return lines;
147
+ }
148
+ function parseLogicalLine(raw) {
149
+ let i = 0;
150
+ let level = 0;
151
+ let spaceRun = 0;
152
+ while (i < raw.length) {
153
+ const ch = raw[i];
154
+ if (ch === " ") {
155
+ level += 1;
156
+ i += 1;
157
+ spaceRun = 0;
158
+ } else if (ch === " ") {
159
+ spaceRun += 1;
160
+ i += 1;
161
+ if (spaceRun === 2) {
162
+ level += 1;
163
+ spaceRun = 0;
164
+ }
165
+ } else {
166
+ break;
167
+ }
168
+ }
169
+ let content = raw.slice(i);
170
+ let bullet = false;
171
+ if (content.startsWith("- ") || content.startsWith("* ")) {
172
+ bullet = true;
173
+ content = content.slice(2);
174
+ }
175
+ return { level, bullet, content };
176
+ }
177
+ function drawMarkdownBlock(container, text, opts) {
178
+ const { width, fontSize, lineHeight, color, linkColor, maxLines } = opts;
179
+ const visualLines = [];
180
+ for (const raw of text.split("\n")) {
181
+ const { level, bullet, content } = parseLogicalLine(raw);
182
+ const indentX = level * INDENT_PX;
183
+ const textX = bullet ? indentX + BULLET_HANG_PX : indentX;
184
+ const avail = Math.max(1, width - textX);
185
+ const segments = parseInline(content);
186
+ const wrapped = wrapSegments(
187
+ segments,
188
+ fontSize,
189
+ avail,
190
+ textX,
191
+ bullet ? indentX : void 0
192
+ );
193
+ visualLines.push(...wrapped);
194
+ }
195
+ let shown = visualLines;
196
+ let clamped = false;
197
+ if (maxLines !== void 0 && visualLines.length > maxLines) {
198
+ shown = visualLines.slice(0, maxLines);
199
+ clamped = true;
200
+ }
201
+ for (let li = 0; li < shown.length; li++) {
202
+ const line = shown[li];
203
+ const y = fontSize + li * lineHeight;
204
+ const isLast = li === shown.length - 1;
205
+ if (line.bulletX !== void 0) {
206
+ container.append("text").attr("x", line.bulletX).attr("y", y).attr("fill", color).attr("font-size", fontSize).text(BULLET);
207
+ }
208
+ const textEl = container.append("text").attr("x", line.x).attr("y", y).attr("font-size", fontSize);
209
+ let cursorX = line.x;
210
+ const segs = line.segments.slice();
211
+ if (clamped && isLast) {
212
+ segs.push({ text: ELLIPSIS, bold: false });
213
+ }
214
+ for (const seg of segs) {
215
+ const tspan = textEl.append("tspan").attr("x", cursorX).attr("fill", seg.href !== void 0 ? linkColor : color).text(seg.text);
216
+ if (seg.bold) tspan.attr("font-weight", "bold");
217
+ const w = segWidth(seg, fontSize);
218
+ if (seg.href !== void 0) {
219
+ tspan.attr("data-href", seg.href);
220
+ container.append("line").attr("x1", cursorX).attr("y1", y + 1.5).attr("x2", cursorX + w).attr("y2", y + 1.5).attr("stroke", linkColor).attr("stroke-width", 1);
221
+ }
222
+ cursorX += w;
223
+ }
224
+ }
225
+ return shown.length * lineHeight;
226
+ }
227
+
228
+ // src/sketch/renderer.ts
229
+ var DIAGRAM_PADDING = 20;
230
+ var TITLE_Y = 32;
231
+ var TITLE_FONT_SIZE = 18;
232
+ var NODE_STROKE_WIDTH = 2;
233
+ var EDGE_STROKE_WIDTH = 2;
234
+ var EDGE_HIT_WIDTH = 18;
235
+ var ARROWHEAD_W = 12;
236
+ var ARROWHEAD_H = 8;
237
+ var DASH = "6 3";
238
+ var BAND_LABEL_FONT_SIZE = 19;
239
+ var BAND_LABEL_OPACITY = 0.55;
240
+ var NOTE_FONT_SIZE = 11;
241
+ var COLLAPSE_BAR_HEIGHT = 4;
242
+ var EDGE_LABEL_FONT_SIZE = 12;
243
+ var CURVE_HANDLE_MIN = 24;
244
+ var CURVE_HANDLE_MAX = 90;
245
+ function drawNoteBody(g, w, h, colors) {
246
+ const f = 14;
247
+ g.append("path").attr(
248
+ "d",
249
+ `M0 2 a2 2 0 0 1 2 -2 h${w - f - 2} l${f} ${f} v${h - f - 2} a2 2 0 0 1 -2 2 h-${w - 4} a2 2 0 0 1 -2 -2 Z`
250
+ ).attr("fill", colors.fill).attr("stroke", colors.stroke).attr("stroke-width", NODE_STROKE_WIDTH);
251
+ g.append("path").attr("d", `M${w - f} 0 v${f} h${f}`).attr("fill", "none").attr("stroke", colors.stroke).attr("stroke-width", 1.2);
252
+ }
253
+ function drawTypeBadge(g, kind, color, bx, by) {
254
+ const b = g.append("g").attr("transform", `translate(${bx},${by})`).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1.3).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
255
+ switch (kind) {
256
+ case "database":
257
+ b.append("ellipse").attr("cx", 8).attr("cy", 3).attr("rx", 6).attr("ry", 2.4);
258
+ b.append("path").attr("d", "M2 3 v10 a6 2.4 0 0 0 12 0 v-10");
259
+ return;
260
+ case "queue":
261
+ b.append("path").attr("d", "M3 1 h8 a3 6.5 0 0 1 0 13 h-8 Z");
262
+ b.append("ellipse").attr("cx", 3).attr("cy", 7.5).attr("rx", 3).attr("ry", 6.5);
263
+ return;
264
+ case "document":
265
+ b.append("path").attr("d", "M2 1 h9 v11 q-4.5 2 -9 0 q0 0 0 0 Z");
266
+ return;
267
+ case "person":
268
+ b.append("circle").attr("cx", 8).attr("cy", 5).attr("r", 3);
269
+ b.append("path").attr("d", "M2 15 a6 5 0 0 1 12 0");
270
+ return;
271
+ default:
272
+ return;
273
+ }
274
+ }
275
+ function metaRows(metadata, tagGroups) {
276
+ const rows = [];
277
+ for (const grp of tagGroups) {
278
+ const v = metadata[tagAttrKey(grp.name)];
279
+ if (v !== void 0) rows.push([grp.name, v]);
280
+ }
281
+ return rows;
282
+ }
283
+ var CARD_HEADER_H = 34;
284
+ var CARD_LABEL_MAX = 15;
285
+ var CARD_LABEL_MIN = 11;
286
+ var CARD_META_FONT = 12;
287
+ var CARD_TITLE_MAX = 30;
288
+ function fitWrapped(label, maxWidth, maxFont = CARD_LABEL_MAX, minFont = CARD_LABEL_MIN, maxLines = 2) {
289
+ for (let fs = maxFont; fs >= minFont; fs--) {
290
+ const lines2 = wrapTextToWidth(label, fs, maxWidth, { hardBreak: true });
291
+ if (lines2.length <= maxLines) return { lines: lines2, fontSize: fs };
292
+ }
293
+ const lines = wrapTextToWidth(label, minFont, maxWidth, { hardBreak: true });
294
+ if (lines.length <= maxLines) return { lines, fontSize: minFont };
295
+ const kept = lines.slice(0, maxLines);
296
+ let last = kept[maxLines - 1];
297
+ while (last.length > 1 && measureText(`${last}\u2026`, minFont) > maxWidth)
298
+ last = last.slice(0, -1);
299
+ kept[maxLines - 1] = `${last}\u2026`;
300
+ return { lines: kept, fontSize: minFont };
301
+ }
302
+ function renderSketch(container, parsed, layout, palette, isDark, options = {}) {
303
+ const {
304
+ activeTagGroup,
305
+ exportMode = false,
306
+ hideDescriptions = false
307
+ } = options;
308
+ const hideDesc = hideDescriptions || parsed.options.noDescriptions;
309
+ const neutralFill = mix(palette.textMuted, palette.bg, 12);
310
+ const tagGroups = [...parsed.tagGroups];
311
+ const activeName = resolveActiveTagGroup(
312
+ tagGroups,
313
+ void 0,
314
+ activeTagGroup
315
+ );
316
+ const activeKey = activeName === null ? null : tagAttrKey(activeName);
317
+ const colorsFor = (metadata, isContainer = false) => {
318
+ const tagged = activeKey !== null && metadata[activeKey] !== void 0;
319
+ const tagColor = tagged ? resolveTagColor(metadata, tagGroups, activeName, isContainer) : void 0;
320
+ if (!tagColor) {
321
+ return {
322
+ fill: neutralFill,
323
+ stroke: palette.textMuted,
324
+ text: palette.text
325
+ };
326
+ }
327
+ const fill = shapeFill(palette, tagColor, isDark, {
328
+ solid: parsed.options.solidFill
329
+ });
330
+ return {
331
+ fill,
332
+ stroke: tagColor,
333
+ // Label text takes the shape's own (tag) color — but for solid fills the
334
+ // tag color would vanish into the fill, so keep a contrast color there.
335
+ text: parsed.options.solidFill ? contrastText(fill, palette.textOnFillLight, palette.textOnFillDark) : tagColor
336
+ };
337
+ };
338
+ const showTitle = !!parsed.title;
339
+ const titleOffset = showTitle ? 40 : 0;
340
+ const legendGroups = parsed.options.noLegend ? [] : tagGroups;
341
+ const contentWidth = layout.width + 2 * DIAGRAM_PADDING;
342
+ const width = Math.max(contentWidth, options.exportDims?.width ?? 0);
343
+ const legendHeight = legendGroups.length > 0 ? getMaxLegendReservedHeight(
344
+ {
345
+ groups: legendGroups,
346
+ position: { placement: "top-center", titleRelation: "below-title" },
347
+ mode: exportMode ? "export" : "preview"
348
+ },
349
+ width
350
+ ) : 0;
351
+ const contentTop = titleOffset + legendHeight + DIAGRAM_PADDING;
352
+ const height = Math.max(
353
+ contentTop + layout.height + DIAGRAM_PADDING,
354
+ options.exportDims?.height ?? 0
355
+ );
356
+ d3.select(container).selectAll("*").remove();
357
+ const svg = d3.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).style("font-family", FONT_FAMILY);
358
+ const defs = svg.append("defs");
359
+ const edgeColorFor = (metadata) => {
360
+ if (activeKey !== null && metadata[activeKey] !== void 0) {
361
+ const c = resolveTagColor(metadata, tagGroups, activeName);
362
+ if (c && c !== "#999999") return c;
363
+ }
364
+ return palette.textMuted;
365
+ };
366
+ const flowColor = (edge) => edgeColorFor(edge.metadata);
367
+ const edgeColors = new Set(layout.edges.map((e) => flowColor(e)));
368
+ for (const color of edgeColors) {
369
+ const hex = color.replace("#", "");
370
+ defs.append("marker").attr("id", `sk-arrow-${hex}`).attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", ARROWHEAD_W).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("markerUnits", "userSpaceOnUse").attr("orient", "auto").append("polygon").attr("points", `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`).attr("fill", color);
371
+ defs.append("marker").attr("id", `sk-arrow-rev-${hex}`).attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", 0).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("markerUnits", "userSpaceOnUse").attr("orient", "auto").append("polygon").attr(
372
+ "points",
373
+ `${ARROWHEAD_W},0 0,${ARROWHEAD_H / 2} ${ARROWHEAD_W},${ARROWHEAD_H}`
374
+ ).attr("fill", color);
375
+ }
376
+ if (showTitle) {
377
+ svg.append("text").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", 700).attr("fill", palette.text).text(parsed.title);
378
+ }
379
+ if (legendGroups.length > 0) {
380
+ const legendG = svg.append("g").attr("transform", `translate(0,${titleOffset + 4})`);
381
+ renderIntegratedLegend(legendG, {
382
+ groups: legendGroups,
383
+ ...activeName !== null && { activeGroup: activeName },
384
+ mode: exportMode ? "export" : "preview",
385
+ showInactivePills: true,
386
+ palette,
387
+ isDark,
388
+ width
389
+ });
390
+ legendG.selectAll("[data-legend-group]").classed("sk-legend-group", true);
391
+ }
392
+ const contentX = DIAGRAM_PADDING + Math.max(0, (width - contentWidth) / 2);
393
+ 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
+ const boxLayer = root.append("g").attr("class", "sk-boxes");
411
+ for (const box of layout.boxes) {
412
+ drawBoxFrame(boxLayer, box, colorsFor(box.metadata, true), palette);
413
+ }
414
+ const edgeLayer = root.append("g").attr("class", "sk-edges");
415
+ 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;
420
+ const color = flowColor(edge);
421
+ const hex = color.replace("#", "");
422
+ const { d, mid } = edgePath(
423
+ source,
424
+ target,
425
+ portsById.get(edge.sourceId),
426
+ portsById.get(edge.targetId)
427
+ );
428
+ 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);
430
+ if (edge.dashed) path.attr("stroke-dasharray", DASH);
431
+ if (edge.heads === "one" || edge.heads === "both") {
432
+ path.attr("marker-end", `url(#sk-arrow-${hex})`);
433
+ }
434
+ if (edge.heads === "both") {
435
+ path.attr("marker-start", `url(#sk-arrow-rev-${hex})`);
436
+ }
437
+ g.append("path").attr("class", "sk-edge-hit").attr("d", d).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", EDGE_HIT_WIDTH).attr("stroke-linecap", "round").attr("pointer-events", "stroke");
438
+ if (edge.label) {
439
+ labelLayerData.push({
440
+ x: mid.x,
441
+ y: mid.y,
442
+ text: edge.label,
443
+ color,
444
+ from: edge.sourceId,
445
+ to: edge.targetId
446
+ });
447
+ }
448
+ }
449
+ const nodeLayer = root.append("g").attr("class", "sk-nodes");
450
+ for (const node of layout.nodes) {
451
+ let n = node;
452
+ if (hideDesc && node.description) {
453
+ n = { ...node };
454
+ delete n.description;
455
+ }
456
+ drawNode(
457
+ nodeLayer,
458
+ n,
459
+ colorsFor(node.metadata),
460
+ palette,
461
+ isDark,
462
+ hideDesc ? [] : tagGroups
463
+ );
464
+ }
465
+ const labelLayer = root.append("g").attr("class", "sk-edge-labels");
466
+ for (const l of labelLayerData) {
467
+ 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);
471
+ }
472
+ if (options.onClickItem) {
473
+ const handler = options.onClickItem;
474
+ svg.selectAll("[data-line-number]").on("click", function() {
475
+ const ln = Number(this.getAttribute("data-line-number"));
476
+ if (Number.isFinite(ln) && ln > 0) handler(ln);
477
+ });
478
+ }
479
+ }
480
+ function drawBoxFrame(layer, box, colors, palette) {
481
+ const g = layer.append("g").attr("class", "sk-box").attr("data-node-id", box.id).attr("data-group-toggle", box.label).attr("data-line-number", box.lineNumber);
482
+ g.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.w).attr("height", box.h).attr("rx", CONTAINER_RADIUS).attr("fill", colors.fill).attr("fill-opacity", 0.4).attr("stroke", colors.stroke).attr("stroke-opacity", 0.7).attr("stroke-width", NODE_STROKE_WIDTH);
483
+ const fit = fitWrapped(box.label, box.w - 16, BAND_LABEL_FONT_SIZE, 12, 2);
484
+ const lineH = fit.fontSize * 1.2;
485
+ const bandMid = box.y + box.bandH / 2;
486
+ const startY = bandMid - (fit.lines.length - 1) * lineH / 2;
487
+ for (let li = 0; li < fit.lines.length; li++) {
488
+ g.append("text").attr("x", box.x + box.w / 2).attr("y", startY + li * lineH).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", fit.fontSize).attr("font-weight", 800).attr("fill", palette.text).attr("opacity", BAND_LABEL_OPACITY).text(fit.lines[li]);
489
+ }
490
+ }
491
+ function drawNode(layer, node, colors, palette, isDark, tagGroups) {
492
+ void isDark;
493
+ const g = layer.append("g").attr("class", node.isCollapsedBox ? "sk-node sk-box-collapsed" : "sk-node").attr("transform", `translate(${node.x},${node.y})`).attr("data-node-id", node.id).attr("data-line-number", node.lineNumber);
494
+ for (const [k, v] of Object.entries(node.metadata)) {
495
+ g.attr(`data-tag-${k}`, v);
496
+ }
497
+ if (node.isCollapsedBox) {
498
+ g.attr("data-group-toggle", node.label);
499
+ }
500
+ if (node.shape === "note") {
501
+ drawNoteBody(g, node.w, node.h, colors);
502
+ const lines = wrapTextToWidth(node.label, NOTE_FONT_SIZE, node.w - 34);
503
+ const lineHeight = NOTE_FONT_SIZE + 4;
504
+ lines.slice(0, 5).forEach((line, i) => {
505
+ g.append("text").attr("x", 10).attr("y", 20 + i * lineHeight).attr("font-size", NOTE_FONT_SIZE).attr("fill", colors.text).text(line);
506
+ });
507
+ } else {
508
+ const rows = node.isCollapsedBox ? [] : metaRows(node.metadata, tagGroups);
509
+ const badge = node.shape !== "rectangle";
510
+ const labelInset = badge ? 22 : 0;
511
+ const solid = colors.stroke === colors.fill;
512
+ const ruleColor = solid ? colors.text : colors.stroke;
513
+ if (node.description && !node.isCollapsedBox) {
514
+ const fitH = fitWrapped(
515
+ node.label,
516
+ node.w - 24 - labelInset,
517
+ CARD_LABEL_MAX,
518
+ CARD_LABEL_MIN,
519
+ 1
520
+ );
521
+ renderNodeCard(g, {
522
+ width: node.w,
523
+ height: node.h,
524
+ rx: CARD_RADIUS,
525
+ fill: colors.fill,
526
+ stroke: colors.stroke,
527
+ strokeWidth: NODE_STROKE_WIDTH,
528
+ label: fitH.lines[0] ?? node.label,
529
+ labelColor: colors.text,
530
+ labelFontSize: fitH.fontSize,
531
+ headerHeight: CARD_HEADER_H
532
+ });
533
+ g.append("line").attr("x1", 0).attr("y1", CARD_HEADER_H).attr("x2", node.w).attr("y2", CARD_HEADER_H).attr("stroke", ruleColor).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
534
+ const inset = 12;
535
+ const bodyGap = 8;
536
+ const lh = CARD_META_FONT + 4;
537
+ const avail = node.h - CARD_HEADER_H - bodyGap - 8;
538
+ const body = g.append("g").attr("class", "sk-desc").attr("transform", `translate(${inset} ${CARD_HEADER_H + bodyGap})`);
539
+ drawMarkdownBlock(body, node.description, {
540
+ width: node.w - inset * 2,
541
+ fontSize: CARD_META_FONT,
542
+ lineHeight: lh,
543
+ color: colors.text,
544
+ // match the header label (contrast-aware in solid)
545
+ linkColor: colors.text,
546
+ maxLines: Math.max(1, Math.floor(avail / lh))
547
+ });
548
+ if (badge) {
549
+ drawTypeBadge(g, node.shape, colors.text, 10, (CARD_HEADER_H - 16) / 2);
550
+ }
551
+ return;
552
+ }
553
+ const headerH = rows.length ? CARD_HEADER_H : node.h;
554
+ const fillTitle = rows.length === 0;
555
+ const fit = fitWrapped(
556
+ node.label,
557
+ node.w - 24 - labelInset,
558
+ fillTitle ? CARD_TITLE_MAX : CARD_LABEL_MAX,
559
+ CARD_LABEL_MIN,
560
+ fillTitle ? 3 : 2
561
+ );
562
+ renderNodeCard(g, {
563
+ width: node.w,
564
+ height: node.h,
565
+ rx: CARD_RADIUS,
566
+ fill: colors.fill,
567
+ stroke: colors.stroke,
568
+ strokeWidth: NODE_STROKE_WIDTH,
569
+ label: fit.lines.join(" "),
570
+ labelLines: fit.lines,
571
+ labelColor: colors.text,
572
+ labelFontSize: fit.fontSize,
573
+ headerHeight: headerH,
574
+ ...rows.length ? {
575
+ meta: {
576
+ rows,
577
+ fontSize: CARD_META_FONT,
578
+ lineHeight: CARD_META_FONT + 5,
579
+ separatorGap: 8,
580
+ separatorColor: ruleColor,
581
+ textColor: solid ? colors.text : palette.text,
582
+ keyX: 12
583
+ }
584
+ } : {}
585
+ });
586
+ if (badge) {
587
+ drawTypeBadge(g, node.shape, colors.text, 10, (CARD_HEADER_H - 16) / 2);
588
+ }
589
+ }
590
+ if (node.isCollapsedBox) {
591
+ renderCollapseBar(g, {
592
+ width: node.w,
593
+ height: node.h,
594
+ barHeight: COLLAPSE_BAR_HEIGHT,
595
+ inset: 0,
596
+ rx: CARD_RADIUS,
597
+ fill: colors.stroke === palette.textMuted ? palette.textMuted : colors.stroke,
598
+ clipId: `sk-clip-${node.id.replace(/[^a-zA-Z0-9_-]/g, "")}-${node.lineNumber}`,
599
+ className: "sk-collapse-bar"
600
+ });
601
+ }
602
+ }
603
+ function edgePath(source, target, sourcePorts, targetPorts) {
604
+ const acx = source.x + source.w / 2;
605
+ const acy = source.y + source.h / 2;
606
+ const bcx = target.x + target.w / 2;
607
+ 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
+ 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
+ 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
+ }
645
+ return {
646
+ 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 }
651
+ };
652
+ }
653
+ function renderSketchForExport(container, parsed, layout, palette, isDark, options = {}) {
654
+ renderSketch(container, parsed, layout, palette, isDark, {
655
+ ...options,
656
+ exportMode: options.exportMode ?? true
657
+ });
658
+ }
659
+
660
+ export {
661
+ renderSketch,
662
+ renderSketchForExport
663
+ };
664
+ //# sourceMappingURL=chunk-6CLKNXVP.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  measureText
3
- } from "./chunk-WCT3NYCV.js";
3
+ } from "./chunk-5LWQH6QV.js";
4
4
 
5
5
  // src/wireframe/layout.ts
6
6
  var DESKTOP_WIDTH = 1200;
@@ -302,4 +302,4 @@ function computeFieldAlignX(children) {
302
302
  export {
303
303
  layoutWireframe
304
304
  };
305
- //# sourceMappingURL=chunk-H6PI3KJT.js.map
305
+ //# sourceMappingURL=chunk-6IUAK7RZ.js.map