@diagrammo/dgmo 0.92.1 → 0.94.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 (174) hide show
  1. package/dist/advanced.d.ts +103 -38
  2. package/dist/advanced.js +457 -423
  3. package/dist/block.d.ts +2 -2
  4. package/dist/block.js +75 -70
  5. package/dist/{chart-meta-BBqLnv5q.d.ts → chart-meta-BbjsRY7o.d.ts} +2 -2
  6. package/dist/chart-meta.d.ts +3 -3
  7. package/dist/chart-meta.js +25 -19
  8. package/dist/{chart-types-CbXlZFLd.d.ts → chart-types-Cppnp31G.d.ts} +173 -4
  9. package/dist/{charts-d3-QUUDVETP.js → charts-d3-SZV2SFTM.js} +7 -6
  10. package/dist/{chunk-ADNIO3XY.js → chunk-2FDQEQID.js} +5 -5
  11. package/dist/{chunk-QUGC7GWB.js → chunk-2HSHWPTO.js} +15 -15
  12. package/dist/{chunk-L5NW3PVL.js → chunk-2I4UQG7W.js} +1 -1
  13. package/dist/{chunk-2YAFDND3.js → chunk-2MS623GW.js} +4 -6
  14. package/dist/{chunk-73EYNV4O.js → chunk-2XMTZ5JR.js} +4 -6
  15. package/dist/{chunk-ZJAK6C7Y.js → chunk-3PP746YM.js} +6 -1
  16. package/dist/{chunk-MDVJYRJT.js → chunk-3TWBMU7K.js} +22 -22
  17. package/dist/{chunk-FTUMRSGT.js → chunk-3ZP3QC47.js} +8 -288
  18. package/dist/{chunk-J2E7EUER.js → chunk-4EX4OKGZ.js} +86 -23
  19. package/dist/{chunk-Y2V5AEVG.js → chunk-4SP6OUO7.js} +1952 -1824
  20. package/dist/{chunk-HIWHIYLD.js → chunk-4TEPDQHJ.js} +19 -19
  21. package/dist/{chunk-DEFBUZWV.js → chunk-5HAYT43L.js} +70 -54
  22. package/dist/{chunk-6WVX4KNP.js → chunk-5ZUZZDW5.js} +97 -297
  23. package/dist/{chunk-X5BUX2GK.js → chunk-6JCTW74W.js} +7 -5
  24. package/dist/{chunk-Q6W2K33N.js → chunk-6XLMTAEX.js} +1 -1
  25. package/dist/{chunk-ONYO5WJW.js → chunk-7IOZ6JE4.js} +21 -21
  26. package/dist/chunk-7SXV7NUS.js +416 -0
  27. package/dist/{chunk-NPWVOFB3.js → chunk-7WEYXYLC.js} +1 -1
  28. package/dist/{chunk-GE7ICBX6.js → chunk-AM42TLSZ.js} +102 -97
  29. package/dist/{chunk-WLLO3PZ3.js → chunk-AYUSD4Z6.js} +45 -19
  30. package/dist/{chunk-45QUTYPD.js → chunk-B25IWGDH.js} +69 -20
  31. package/dist/{chunk-JKST4RTT.js → chunk-BH2SF4TT.js} +6 -6
  32. package/dist/{chunk-6OIMUSDX.js → chunk-C6G36F5S.js} +9 -9
  33. package/dist/{chunk-DRFGIOZ6.js → chunk-CIHHTS5B.js} +4 -6
  34. package/dist/chunk-CYTFO2LF.js +209 -0
  35. package/dist/{chunk-Q2UFOKPB.js → chunk-DDIOWNHS.js} +22 -22
  36. package/dist/{chunk-Q6ZQX2HX.js → chunk-DLJJOCIT.js} +4 -6
  37. package/dist/{chunk-26BBJUBW.js → chunk-DNM3XZCK.js} +4 -6
  38. package/dist/{chunk-AUS7V54P.js → chunk-ERT7XDQW.js} +1 -1
  39. package/dist/{chunk-7ENDOHAA.js → chunk-FMAVHTYB.js} +919 -927
  40. package/dist/{chunk-URQIWIKX.js → chunk-FWVE373X.js} +4 -4
  41. package/dist/{chunk-VOR32OHN.js → chunk-G3HZ4PGE.js} +13 -15
  42. package/dist/{chunk-RBFJT23E.js → chunk-GWQRTA5W.js} +6 -6
  43. package/dist/chunk-GXCDD6NG.js +24 -0
  44. package/dist/{chunk-3PFMGNOM.js → chunk-HHVIE45A.js} +26 -11
  45. package/dist/{chunk-MXAP37W6.js → chunk-HI6R27YA.js} +16 -7
  46. package/dist/{chunk-QQPNCXAO.js → chunk-I37S3EZE.js} +18 -18
  47. package/dist/{chunk-YITHYQCL.js → chunk-IGJEKO2P.js} +8 -8
  48. package/dist/{chunk-BZX7QCXJ.js → chunk-IHN6WDEY.js} +15 -15
  49. package/dist/{chunk-QT3KDDXK.js → chunk-IN7XI6NO.js} +0 -7
  50. package/dist/{chunk-4QLGGQNO.js → chunk-IZHGT5HD.js} +44 -18
  51. package/dist/{chunk-5SSTVFPX.js → chunk-JT2BG7LM.js} +5 -7
  52. package/dist/{chunk-2BJW7FSF.js → chunk-KBEWVCK3.js} +1 -1
  53. package/dist/{chunk-DUPZ2BBY.js → chunk-KBHT5XEY.js} +143 -6
  54. package/dist/{chunk-V4D2ZQBE.js → chunk-KRPLP5LP.js} +6 -1
  55. package/dist/{chunk-QKY6W4NA.js → chunk-KSZB7TIY.js} +25 -25
  56. package/dist/{chunk-6ZL2XYM7.js → chunk-MLY5ALOV.js} +6 -6
  57. package/dist/{chunk-VSOZZYLK.js → chunk-NERQSSYD.js} +20 -9
  58. package/dist/{chunk-OQW4ETJX.js → chunk-NM5V5EKY.js} +6 -1
  59. package/dist/{chunk-EBSFSOJC.js → chunk-NOQB4KMV.js} +22 -22
  60. package/dist/{chunk-6BNNLVAR.js → chunk-NPDVWFAQ.js} +6 -1
  61. package/dist/{chunk-FSJ3ZA6N.js → chunk-OSSTOSXU.js} +12 -12
  62. package/dist/chunk-P7TRFL4P.js +54 -0
  63. package/dist/{chunk-A4PNWAUW.js → chunk-PGSGJLUR.js} +6 -1
  64. package/dist/{chunk-S7W75MIM.js → chunk-QKPOIJBT.js} +23 -5
  65. package/dist/{chunk-YO6AQU4A.js → chunk-QPQWY5RU.js} +12 -12
  66. package/dist/{chunk-NREU4JFE.js → chunk-RBVFNUAL.js} +6 -8
  67. package/dist/{chunk-5EBYPETE.js → chunk-RESEJSCV.js} +3 -5
  68. package/dist/{chunk-BRSM6Z34.js → chunk-RITTLIDJ.js} +14 -14
  69. package/dist/{chunk-MX4SZLSV.js → chunk-S5Y3OO3V.js} +16 -8
  70. package/dist/{chunk-O5WUZGPV.js → chunk-TASRGDYP.js} +18 -18
  71. package/dist/{chunk-HXULJZA2.js → chunk-TDAGJJPZ.js} +6 -1
  72. package/dist/chunk-UCHZJJIT.js +421 -0
  73. package/dist/{chunk-42UJ23H3.js → chunk-UIEKW4SG.js} +1 -0
  74. package/dist/{chunk-WFA6IULY.js → chunk-URRAWU5D.js} +5 -0
  75. package/dist/{chunk-IGKFOHBG.js → chunk-VBMRZCMQ.js} +13 -13
  76. package/dist/{chunk-JRWUHLRP.js → chunk-VDY2GR6U.js} +51 -8
  77. package/dist/{chunk-EJS3K44P.js → chunk-VGAMBOOC.js} +5 -3
  78. package/dist/{chunk-B5PEKSRH.js → chunk-VSAKEMDD.js} +6 -1
  79. package/dist/chunk-WTTIODDV.js +9 -0
  80. package/dist/{chunk-JLU52ZDT.js → chunk-XJQ6Z3FO.js} +10 -10
  81. package/dist/{chunk-KTGZDMDX.js → chunk-XKCRKD2B.js} +140 -53
  82. package/dist/{chunk-LRP5FXST.js → chunk-YQMIKX54.js} +61 -24
  83. package/dist/{chunk-H5UEVW6Q.js → chunk-ZD3QCNB7.js} +1 -1
  84. package/dist/clock.js +168 -0
  85. package/dist/completion-registry.js +3 -1
  86. package/dist/countdown.js +36 -395
  87. package/dist/{dgmo-router-4UAPPIKN.js → dgmo-router-E5O7RJSW.js} +50 -43
  88. package/dist/{dimensions-SCNVYLRF.js → dimensions-DS6FWLRY.js} +3 -3
  89. package/dist/{d3-BThYBTQk.d.ts → export-context-Ceh3wr0E.d.ts} +2 -8
  90. package/dist/{flowchart-parser-NA75YQ77.js → flowchart-parser-NZXASOZG.js} +8 -3
  91. package/dist/{flowchart-renderer-6MGGX4RC.js → flowchart-renderer-PTCRIANF.js} +17 -19
  92. package/dist/index.d.ts +8 -7
  93. package/dist/index.js +80 -74
  94. package/dist/{layout-MBVBAMPA.js → layout-3TSVAGWH.js} +2 -2
  95. package/dist/layout-ISSBWBH7.js +24 -0
  96. package/dist/layout-IT4ZTV6C.js +24 -0
  97. package/dist/{layout-BNLFQHSB.js → layout-U3AH6M7J.js} +9 -10
  98. package/dist/layout-VC3MXRB4.js +29 -0
  99. package/dist/layout-VDCJOZSH.js +11 -0
  100. package/dist/{layout-search-PMYL3VWS.js → layout-search-BBX2BMEF.js} +11 -12
  101. package/dist/{palettes-IN7ZFRJM.js → palettes-4Y7CL2QL.js} +4 -2
  102. package/dist/{parser-QYISOAGR.js → parser-4RFY7D52.js} +8 -3
  103. package/dist/{parser-3MK56JZC.js → parser-4Y6PODW6.js} +2 -2
  104. package/dist/{parser-GX5YLRIP.js → parser-7QKQ4P4P.js} +8 -3
  105. package/dist/{parser-2NWH5JUS.js → parser-A3AN76RQ.js} +2 -1
  106. package/dist/{parser-LCJ76UXS.js → parser-BT4NI6JY.js} +5 -1
  107. package/dist/{parser-FXDS4JDK.js → parser-F7NDRPMA.js} +2 -2
  108. package/dist/{parser-BES6LV3G.js → parser-GDLKUBTO.js} +5 -1
  109. package/dist/{parser-SEUTXN6U.js → parser-KJPPUN4D.js} +2 -2
  110. package/dist/{parser-SIKL4PLF.js → parser-KMKVFET2.js} +5 -1
  111. package/dist/{parser-5LW5JDYG.js → parser-NL73FSFI.js} +5 -1
  112. package/dist/{parser-VA7ZQQTA.js → parser-NUU3DVW4.js} +5 -1
  113. package/dist/{parser-XZUKNZPB.js → parser-R3LPEQIT.js} +7 -1
  114. package/dist/{parser-X23GRTQJ.js → parser-RCIEKN7Y.js} +5 -1
  115. package/dist/{parser-L6RWDXQN.js → parser-TAYRTCPR.js} +8 -3
  116. package/dist/redraw-AHW7EUUF.js +1698 -0
  117. package/dist/redraw-STZ4WJAI.js +73 -0
  118. package/dist/{renderer-LM4ICK6I.js → renderer-2LYX6NHN.js} +4 -5
  119. package/dist/{renderer-NHTR7IRR.js → renderer-3NLDX7DQ.js} +4 -5
  120. package/dist/{renderer-TO2NVJP3.js → renderer-4SE7R22T.js} +50 -45
  121. package/dist/{renderer-3IHQI4A4.js → renderer-53B523PM.js} +11 -10
  122. package/dist/{renderer-3JY6JAQJ.js → renderer-7OGGV327.js} +5 -6
  123. package/dist/{renderer-2FVUPBGV.js → renderer-AM7P4LN7.js} +1 -1
  124. package/dist/{renderer-5VKPB4LL.js → renderer-BXD5KVSR.js} +5 -5
  125. package/dist/{renderer-OAR5VPH4.js → renderer-DAZWHNLI.js} +2 -2
  126. package/dist/{renderer-WIU5BJUK.js → renderer-DDWDCM2K.js} +2 -2
  127. package/dist/{renderer-3QAFDXEY.js → renderer-FMTLSOCX.js} +16 -12
  128. package/dist/{renderer-JEJT3YET.js → renderer-HX6ICPDX.js} +2 -3
  129. package/dist/{renderer-NDA2FFYB.js → renderer-ISQJ2ELA.js} +5 -5
  130. package/dist/{renderer-XGG2UNXT.js → renderer-IXBOT6DU.js} +3 -4
  131. package/dist/{renderer-S3YCLJKL.js → renderer-J3GPDK73.js} +20 -22
  132. package/dist/{renderer-DVNMCYHZ.js → renderer-J7ZSTJUE.js} +15 -15
  133. package/dist/{renderer-ALWVK3EJ.js → renderer-JJ4QW3SK.js} +6 -6
  134. package/dist/{renderer-RUSFL7WU.js → renderer-JYHA6R2D.js} +8 -7
  135. package/dist/{renderer-5TZVFQ7K.js → renderer-KJ3V5BYP.js} +7 -8
  136. package/dist/{renderer-RSK6QFWN.js → renderer-LNP23NSM.js} +7 -9
  137. package/dist/{renderer-ZHGHTSNG.js → renderer-M5DUMITP.js} +13 -12
  138. package/dist/{renderer-O6DY46AV.js → renderer-MWZC7TMD.js} +7 -7
  139. package/dist/{renderer-DC7C5Q3G.js → renderer-NOYLNVTI.js} +2 -2
  140. package/dist/{renderer-ISSDGK54.js → renderer-O6RP52CQ.js} +3 -3
  141. package/dist/{renderer-ZGY7P4BM.js → renderer-OUDHH5MV.js} +19 -22
  142. package/dist/{renderer-QGIHLZOJ.js → renderer-PHLCYVSN.js} +9 -10
  143. package/dist/{renderer-VPIELEE5.js → renderer-QTPWRRJ7.js} +8 -9
  144. package/dist/{renderer-LQP3AGEJ.js → renderer-RUSQJFFC.js} +3 -4
  145. package/dist/{renderer-MM27BSBO.js → renderer-TINHO2IO.js} +3 -4
  146. package/dist/{renderer-7ZZG5HR3.js → renderer-UTEEJDGB.js} +9 -9
  147. package/dist/{renderer-J5FKF423.js → renderer-W4MUMXJE.js} +7 -4
  148. package/dist/{renderer-XO57LTU4.js → renderer-WPN2SXGT.js} +5 -5
  149. package/dist/{renderer-ZZ6MWVB7.js → renderer-XCLARXCK.js} +4 -4
  150. package/dist/{renderer-J666LZXJ.js → renderer-XOWDERU2.js} +8 -8
  151. package/dist/{state-parser-TIR2ORJY.js → state-parser-KPVW5CRI.js} +8 -3
  152. package/dist/{state-renderer-G6SUXU4J.js → state-renderer-ZVEI265C.js} +18 -21
  153. package/dist/{tag-groups-DsoVpAY-.d.ts → tag-groups-DydewK3D.d.ts} +2 -173
  154. package/dist/{themes-DRNjxX7Q.d.ts → themes-DzIH3Hjv.d.ts} +3 -2
  155. package/docs/ai-integration.md +1 -1
  156. package/docs/language-reference.md +23 -2
  157. package/gallery/fixtures/whiteboard.dgmo +3 -0
  158. package/package.json +2 -6
  159. package/dist/chunk-3Y63RFJL.js +0 -75
  160. package/dist/chunk-5DH3KYFH.js +0 -20
  161. package/dist/chunk-O753JUEZ.js +0 -67
  162. package/dist/chunk-RJNZHFNG.js +0 -51
  163. package/dist/chunk-Z6DXWULM.js +0 -23
  164. package/dist/layout-5USEVQBY.js +0 -25
  165. package/dist/layout-J3AGFZZ7.js +0 -10
  166. package/dist/layout-S3RLVPH4.js +0 -25
  167. package/dist/layout-WRG4APSJ.js +0 -32
  168. package/dist/{chunk-DGUGE67L.js → chunk-CDYV46JE.js} +3 -3
  169. package/dist/{chunk-TKAB3FTK.js → chunk-DRQSB72S.js} +11 -11
  170. package/dist/{chunk-ZKP77MB3.js → chunk-FVTYJUXR.js} +5 -5
  171. package/dist/{chunk-MA42PIGJ.js → chunk-H5NVCOEJ.js} +4 -4
  172. package/dist/{chunk-BEA4D7OS.js → chunk-IXHWS2HI.js} +3 -3
  173. package/dist/{chunk-SOHZDW6N.js → chunk-UXNA2H2V.js} +3 -3
  174. package/dist/{chunk-NO3UX6AP.js → chunk-V2NQV7VL.js} +4 -4
@@ -1,10 +1,8 @@
1
1
  import {
2
- layoutBracket
3
- } from "./chunk-ZI6WC24V.js";
2
+ renderDataChartD3
3
+ } from "./chunk-YQMIKX54.js";
4
4
  import {
5
5
  REGISTRY_BY_ID,
6
- choosePalette,
7
- extractPaletteDirective,
8
6
  getRenderCategory,
9
7
  parseArc,
10
8
  parseDgmo,
@@ -14,46 +12,69 @@ import {
14
12
  parseVenn,
15
13
  parseVisualization,
16
14
  parseWordcloud
17
- } from "./chunk-6WVX4KNP.js";
15
+ } from "./chunk-5ZUZZDW5.js";
16
+ import {
17
+ parseChart,
18
+ parseFunctionChart,
19
+ parseFunnel,
20
+ parseHeatmap,
21
+ parseSankey,
22
+ parseScatter
23
+ } from "./chunk-H6BT45SN.js";
24
+ import {
25
+ parsePyramid
26
+ } from "./chunk-3PP746YM.js";
27
+ import {
28
+ parseRing
29
+ } from "./chunk-NM5V5EKY.js";
30
+ import {
31
+ parseTreemap
32
+ } from "./chunk-3KVUFHUJ.js";
18
33
  import {
19
34
  MAP_DX
20
35
  } from "./chunk-EOYPX6SF.js";
21
36
  import {
22
- parseCountdown
23
- } from "./chunk-2BJW7FSF.js";
37
+ computeQuadrantPointLabels
38
+ } from "./chunk-KMU65FYM.js";
24
39
  import {
25
- parseClock
26
- } from "./chunk-NXU4N5WT.js";
40
+ parseVersionControl
41
+ } from "./chunk-2ZW4NBYX.js";
27
42
  import {
28
- parseBracket
29
- } from "./chunk-2WQ3YZMN.js";
43
+ parseMap
44
+ } from "./chunk-AULZD45V.js";
30
45
  import {
31
- parseBody
32
- } from "./chunk-JXGSTBW6.js";
46
+ parseSketch
47
+ } from "./chunk-WZBZXZ6T.js";
33
48
  import {
34
- renderDataChartD3
35
- } from "./chunk-LRP5FXST.js";
49
+ parseWhiteboard
50
+ } from "./chunk-5HAYT43L.js";
36
51
  import {
37
- computeQuadrantPointLabels
38
- } from "./chunk-KMU65FYM.js";
52
+ parseSwimlane
53
+ } from "./chunk-J7Q4AB23.js";
39
54
  import {
40
- parseChart,
41
- parseFunctionChart,
42
- parseFunnel,
43
- parseHeatmap,
44
- parseSankey,
45
- parseScatter
46
- } from "./chunk-H6BT45SN.js";
55
+ parseMindmap
56
+ } from "./chunk-XIUGSUE4.js";
47
57
  import {
48
- renderTimeline
49
- } from "./chunk-O5WUZGPV.js";
58
+ parseTechRadar
59
+ } from "./chunk-EBVVMUAA.js";
50
60
  import {
51
- ScaleContext
52
- } from "./chunk-24YPR5EH.js";
61
+ parseKanban
62
+ } from "./chunk-LKZCUFOW.js";
53
63
  import {
54
- GRID_DASH,
55
- scaleDash
56
- } from "./chunk-XCKNFFKM.js";
64
+ parseInfra
65
+ } from "./chunk-UXNA2H2V.js";
66
+ import {
67
+ parseJourneyMap
68
+ } from "./chunk-PGSGJLUR.js";
69
+ import {
70
+ parseGoal
71
+ } from "./chunk-7PJBJ2DU.js";
72
+ import {
73
+ parseEventLine
74
+ } from "./chunk-3GUEYQGL.js";
75
+ import {
76
+ renderTimeline
77
+ } from "./chunk-TASRGDYP.js";
57
78
  import {
58
79
  EXPORT_HEIGHT,
59
80
  EXPORT_WIDTH,
@@ -65,34 +86,34 @@ import {
65
86
  renderChartTitle,
66
87
  resolveExportPalette,
67
88
  showTooltip
68
- } from "./chunk-Q6W2K33N.js";
89
+ } from "./chunk-6XLMTAEX.js";
69
90
  import {
70
- legendInlineSupported
71
- } from "./chunk-42UJ23H3.js";
91
+ parseTimelineDate
92
+ } from "./chunk-BQO24BDM.js";
72
93
  import {
73
- compactNumber
74
- } from "./chunk-JILEYZ2V.js";
94
+ parseFamily
95
+ } from "./chunk-R6EGUTIV.js";
75
96
  import {
76
- FONT_FAMILY
77
- } from "./chunk-ICLB27PZ.js";
97
+ parseGantt
98
+ } from "./chunk-PVWIBKV2.js";
78
99
  import {
79
- parseTechRadar
80
- } from "./chunk-EBVVMUAA.js";
100
+ parseClock
101
+ } from "./chunk-NXU4N5WT.js";
81
102
  import {
82
- parseCycle
83
- } from "./chunk-6BNNLVAR.js";
103
+ parseCountdown
104
+ } from "./chunk-KBEWVCK3.js";
84
105
  import {
85
- parseJourneyMap
86
- } from "./chunk-A4PNWAUW.js";
106
+ parseCycle
107
+ } from "./chunk-NPDVWFAQ.js";
87
108
  import {
88
- parsePyramid
89
- } from "./chunk-ZJAK6C7Y.js";
109
+ parseBracket
110
+ } from "./chunk-2WQ3YZMN.js";
90
111
  import {
91
- parseRing
92
- } from "./chunk-OQW4ETJX.js";
112
+ layoutBracket
113
+ } from "./chunk-ZI6WC24V.js";
93
114
  import {
94
- parseTreemap
95
- } from "./chunk-3KVUFHUJ.js";
115
+ parseC4
116
+ } from "./chunk-IXHWS2HI.js";
96
117
  import {
97
118
  parseBlock
98
119
  } from "./chunk-BRGRIZQB.js";
@@ -100,83 +121,64 @@ import {
100
121
  isBlockNode
101
122
  } from "./chunk-XSVWUOUZ.js";
102
123
  import {
103
- parseGoal
104
- } from "./chunk-7PJBJ2DU.js";
105
- import {
106
- parseMap
107
- } from "./chunk-AULZD45V.js";
124
+ parseBody
125
+ } from "./chunk-JXGSTBW6.js";
108
126
  import {
109
127
  parseBoxesAndLines
110
- } from "./chunk-73EYNV4O.js";
111
- import {
112
- parseSketch
113
- } from "./chunk-WZBZXZ6T.js";
114
- import {
115
- parseWhiteboard
116
- } from "./chunk-DEFBUZWV.js";
117
- import {
118
- parseSwimlane
119
- } from "./chunk-J7Q4AB23.js";
120
- import {
121
- parseFamily
122
- } from "./chunk-R6EGUTIV.js";
128
+ } from "./chunk-2XMTZ5JR.js";
123
129
  import {
124
- parseMindmap
125
- } from "./chunk-XIUGSUE4.js";
130
+ ScaleContext
131
+ } from "./chunk-24YPR5EH.js";
126
132
  import {
127
- parseEventLine
128
- } from "./chunk-3GUEYQGL.js";
133
+ legendInlineSupported
134
+ } from "./chunk-UIEKW4SG.js";
129
135
  import {
130
- parseTimelineDate
131
- } from "./chunk-BQO24BDM.js";
136
+ GRID_DASH,
137
+ scaleDash
138
+ } from "./chunk-XCKNFFKM.js";
132
139
  import {
133
- parseVersionControl
134
- } from "./chunk-2ZW4NBYX.js";
140
+ compactNumber
141
+ } from "./chunk-JILEYZ2V.js";
135
142
  import {
136
- parseKanban
137
- } from "./chunk-LKZCUFOW.js";
143
+ parseRaci
144
+ } from "./chunk-KRPLP5LP.js";
138
145
  import {
139
- parseC4
140
- } from "./chunk-BEA4D7OS.js";
146
+ parseLiveLink
147
+ } from "./chunk-KBDMEOLI.js";
141
148
  import {
142
- parseInfra
143
- } from "./chunk-SOHZDW6N.js";
149
+ parseSequenceDgmo
150
+ } from "./chunk-VSAKEMDD.js";
144
151
  import {
145
- parseGantt
146
- } from "./chunk-PVWIBKV2.js";
152
+ parseOrg
153
+ } from "./chunk-5OTA2PUH.js";
147
154
  import {
148
- parseRaci
149
- } from "./chunk-V4D2ZQBE.js";
155
+ parseSitemap
156
+ } from "./chunk-ZECJ7Q6C.js";
150
157
  import {
151
158
  parsePert
152
159
  } from "./chunk-CDM6XMOT.js";
153
- import {
154
- parseLiveLink
155
- } from "./chunk-KBDMEOLI.js";
156
- import {
157
- getPalette
158
- } from "./chunk-7ENDOHAA.js";
159
- import {
160
- parseSequenceDgmo
161
- } from "./chunk-B5PEKSRH.js";
162
160
  import {
163
161
  parseFlowchart
164
- } from "./chunk-26BBJUBW.js";
162
+ } from "./chunk-DNM3XZCK.js";
165
163
  import {
166
164
  parseState
167
- } from "./chunk-2YAFDND3.js";
165
+ } from "./chunk-2MS623GW.js";
168
166
  import {
169
- parseClassDiagram
170
- } from "./chunk-Q6ZQX2HX.js";
167
+ choosePalette,
168
+ extractPaletteDirective
169
+ } from "./chunk-CYTFO2LF.js";
171
170
  import {
172
171
  parseERDiagram
173
- } from "./chunk-DRFGIOZ6.js";
172
+ } from "./chunk-CIHHTS5B.js";
174
173
  import {
175
- parseOrg
176
- } from "./chunk-5OTA2PUH.js";
174
+ parseClassDiagram
175
+ } from "./chunk-DLJJOCIT.js";
177
176
  import {
178
- parseSitemap
179
- } from "./chunk-ZECJ7Q6C.js";
177
+ getPalette
178
+ } from "./chunk-FMAVHTYB.js";
179
+ import {
180
+ FONT_FAMILY
181
+ } from "./chunk-ICLB27PZ.js";
180
182
  import {
181
183
  parseWireframe
182
184
  } from "./chunk-GI5R5B3S.js";
@@ -198,209 +200,19 @@ import {
198
200
  makeDgmoError
199
201
  } from "./chunk-BYU54C3S.js";
200
202
 
201
- // src/slope/renderer.ts
203
+ // src/arc/renderer.ts
202
204
  import * as d3Scale from "d3-scale";
203
- import * as d3Shape from "d3-shape";
205
+ import * as d3Selection from "d3-selection";
204
206
  import * as d3Array from "d3-array";
205
- function resolveVerticalCollisions(items, minGap, maxY) {
206
- if (items.length === 0) return [];
207
- const sorted = items.map((it, i) => ({ ...it, idx: i })).sort((a, b) => a.naturalY - b.naturalY);
208
- const adjustedY = new Array(items.length);
209
- let prevBottom = -Infinity;
210
- for (const item of sorted) {
211
- const halfH = item.height / 2;
212
- let top = Math.max(item.naturalY - halfH, prevBottom + minGap);
213
- if (maxY !== void 0) {
214
- top = Math.min(top, maxY - item.height);
215
- }
216
- adjustedY[item.idx] = top + halfH;
217
- prevBottom = top + item.height;
218
- }
219
- return adjustedY;
220
- }
221
- var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
222
- var SLOPE_LABEL_FONT_SIZE = 14;
223
- function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
224
- const { periods, data } = parsed;
225
- const title = parsed.noTitle ? null : parsed.title;
226
- if (data.length === 0 || periods.length < 2) return;
227
- const init = initD3Chart(container, palette, exportDims);
228
- if (!init) return;
229
- const { svg, width, height, textColor, mutedColor, bgColor, colors } = init;
230
- const idealWidth = SLOPE_MARGIN.left + periods.length * 100 + 150;
231
- const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
232
- const sMarginTop = ctx.aesthetic(SLOPE_MARGIN.top);
233
- const sMarginBottom = ctx.aesthetic(SLOPE_MARGIN.bottom);
234
- const sMarginLeft = ctx.aesthetic(SLOPE_MARGIN.left);
235
- const sLabelFontSize = ctx.text(SLOPE_LABEL_FONT_SIZE);
236
- const sPeriodFont = ctx.text(18);
237
- const sValueFont = ctx.text(16);
238
- const sPeriodHeaderY = ctx.structural(15);
239
- const sDash = scaleDash(GRID_DASH, (n) => ctx.structural(n));
240
- const sLineStroke = ctx.structural(2.5);
241
- const sHitWidth = ctx.structural(14);
242
- const sPointR = ctx.structural(4);
243
- const sPointStroke = ctx.structural(1.5);
244
- const sValueLabelXOff = ctx.structural(10);
245
- const sLabelGap = ctx.structural(10);
246
- svg.attr("preserveAspectRatio", "xMidYMin meet");
247
- if (ctx.isBelowFloor) {
248
- svg.attr("width", "100%");
207
+ function orderArcNodes(links, order, groups) {
208
+ const nodeSet = /* @__PURE__ */ new Set();
209
+ for (const link of links) {
210
+ nodeSet.add(link.source);
211
+ nodeSet.add(link.target);
249
212
  }
250
- const maxLabelText = data.reduce((longest, item) => {
251
- const text = `${item.values[item.values.length - 1]} \u2014 ${item.label}`;
252
- return text.length > longest.length ? text : longest;
253
- }, "");
254
- const estimatedLabelWidth = measureText(maxLabelText, sLabelFontSize);
255
- const maxRightMargin = Math.floor(width * 0.35);
256
- const rightMargin = Math.min(
257
- Math.max(estimatedLabelWidth + ctx.aesthetic(30), ctx.aesthetic(120)),
258
- maxRightMargin
259
- );
260
- const innerWidth = width - sMarginLeft - rightMargin;
261
- const innerHeight = height - sMarginTop - sMarginBottom;
262
- const allValues2 = data.flatMap((d) => d.values);
263
- const [minVal, maxVal] = d3Array.extent(allValues2);
264
- const valuePadding = (maxVal - minVal) * 0.1 || 1;
265
- const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
266
- const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
267
- const g = svg.append("g").attr("transform", `translate(${sMarginLeft},${sMarginTop})`);
268
- const tooltip = createTooltip(container, palette, isDark);
269
- renderChartTitle(
270
- svg,
271
- title,
272
- parsed.titleLineNumber,
273
- width,
274
- textColor,
275
- onClickItem
276
- );
277
- for (const period of periods) {
278
- const x = xScale(period);
279
- g.append("text").attr("x", x).attr("y", -sPeriodHeaderY).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sPeriodFont).attr("font-weight", "bold").text(period);
280
- g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", sDash);
281
- }
282
- const lineGen = d3Shape.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
283
- const seriesInfo = data.map((item, idx) => {
284
- const color = item.color ?? colors[idx % colors.length];
285
- const firstVal = item.values[0];
286
- const lastVal = item.values[item.values.length - 1];
287
- const absChange = lastVal - firstVal;
288
- const pctChange = firstVal !== 0 ? absChange / firstVal * 100 : null;
289
- const sign = absChange > 0 ? "+" : "";
290
- const tipLines = [`${sign}${parseFloat(absChange.toFixed(2))}`];
291
- if (pctChange !== null) tipLines.push(`${sign}${pctChange.toFixed(1)}%`);
292
- const tipHtml = tipLines.join("<br>");
293
- const lastX = xScale(periods[periods.length - 1]);
294
- const labelText = `${lastVal} \u2014 ${item.label}`;
295
- const availableWidth = rightMargin - ctx.aesthetic(15);
296
- let labelLineCount = 1;
297
- let wrappedLines = null;
298
- if (measureText(labelText, sLabelFontSize) > availableWidth) {
299
- const lines = wrapTextToWidth(labelText, sLabelFontSize, availableWidth);
300
- labelLineCount = lines.length;
301
- wrappedLines = lines;
302
- }
303
- const lineHeight = sLabelFontSize * 1.2;
304
- const labelHeight = labelLineCount === 1 ? sLabelFontSize : labelLineCount * lineHeight;
305
- return {
306
- item,
307
- idx,
308
- color,
309
- firstVal,
310
- lastVal,
311
- tipHtml,
312
- lastX,
313
- labelText,
314
- wrappedLines,
315
- labelHeight
316
- };
317
- });
318
- const leftLabelHeight = sValueFont * 1.25;
319
- const leftLabelCollisions = /* @__PURE__ */ new Map();
320
- for (let pi = 0; pi < periods.length - 1; pi++) {
321
- const entries = data.map((item) => ({
322
- // pi is in-bounds by loop guard against periods.length, and each data row has periods.length values.
323
- naturalY: yScale(item.values[pi]),
324
- height: leftLabelHeight
325
- }));
326
- leftLabelCollisions.set(
327
- pi,
328
- resolveVerticalCollisions(entries, 4, innerHeight)
329
- );
330
- }
331
- const rightEntries = seriesInfo.map((si) => ({
332
- naturalY: yScale(si.lastVal),
333
- height: Math.max(si.labelHeight, sLabelFontSize * 1.4)
334
- }));
335
- const rightAdjustedY = resolveVerticalCollisions(
336
- rightEntries,
337
- 4,
338
- innerHeight
339
- );
340
- data.forEach((item, idx) => {
341
- const si = seriesInfo[idx];
342
- const color = si.color;
343
- const seriesG = g.append("g").attr("class", "slope-series").attr("data-line-number", String(item.lineNumber));
344
- seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", color).attr("stroke-width", sLineStroke).attr("d", lineGen);
345
- seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", sHitWidth).attr("d", lineGen).style("cursor", onClickItem ? "pointer" : "default").on(
346
- "mouseenter",
347
- (event) => showTooltip(tooltip, si.tipHtml, event)
348
- ).on(
349
- "mousemove",
350
- (event) => showTooltip(tooltip, si.tipHtml, event)
351
- ).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
352
- if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
353
- });
354
- item.values.forEach((val, i) => {
355
- const x = xScale(periods[i]);
356
- const y = yScale(val);
357
- seriesG.append("circle").attr("cx", x).attr("cy", y).attr("r", sPointR).attr("fill", color).attr("stroke", bgColor).attr("stroke-width", sPointStroke).style("cursor", onClickItem ? "pointer" : "default").on(
358
- "mouseenter",
359
- (event) => showTooltip(tooltip, si.tipHtml, event)
360
- ).on(
361
- "mousemove",
362
- (event) => showTooltip(tooltip, si.tipHtml, event)
363
- ).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
364
- if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
365
- });
366
- const isFirst = i === 0;
367
- const isLast = i === periods.length - 1;
368
- if (!isLast) {
369
- const adjustedY = leftLabelCollisions.get(i)[idx];
370
- seriesG.append("text").attr("x", isFirst ? x - sValueLabelXOff : x).attr("y", adjustedY).attr("dy", "0.35em").attr("text-anchor", isFirst ? "end" : "middle").attr("fill", color).attr("font-size", sValueFont).text(val.toString());
371
- }
372
- });
373
- const adjustedLastY = rightAdjustedY[idx];
374
- const labelEl = seriesG.append("text").attr("x", si.lastX + sLabelGap).attr("y", adjustedLastY).attr("text-anchor", "start").attr("fill", color).attr("font-size", sLabelFontSize).attr("font-weight", "normal");
375
- if (!si.wrappedLines) {
376
- labelEl.attr("dy", "0.35em").text(si.labelText);
377
- } else {
378
- const lineHeight = sLabelFontSize * 1.2;
379
- const totalHeight = (si.wrappedLines.length - 1) * lineHeight;
380
- const startDy = -totalHeight / 2;
381
- si.wrappedLines.forEach((line2, li) => {
382
- labelEl.append("tspan").attr("x", si.lastX + sLabelGap).attr(
383
- "dy",
384
- li === 0 ? `${startDy + sLabelFontSize * 0.35}px` : `${lineHeight}px`
385
- ).text(line2);
386
- });
387
- }
388
- });
389
- }
390
-
391
- // src/arc/renderer.ts
392
- import * as d3Scale2 from "d3-scale";
393
- import * as d3Selection from "d3-selection";
394
- import * as d3Array2 from "d3-array";
395
- function orderArcNodes(links, order, groups) {
396
- const nodeSet = /* @__PURE__ */ new Set();
397
- for (const link of links) {
398
- nodeSet.add(link.source);
399
- nodeSet.add(link.target);
400
- }
401
- const allNodes = Array.from(nodeSet);
402
- if (order === "name") {
403
- return allNodes.slice().sort((a, b) => a.localeCompare(b));
213
+ const allNodes = Array.from(nodeSet);
214
+ if (order === "name") {
215
+ return allNodes.slice().sort((a, b) => a.localeCompare(b));
404
216
  }
405
217
  if (order === "degree") {
406
218
  const degree = /* @__PURE__ */ new Map();
@@ -556,8 +368,8 @@ function renderArcDiagram(container, parsed, palette, isDark, onClickItem, expor
556
368
  groupNodeSets.set(group.name, new Set(group.nodes));
557
369
  }
558
370
  const values = links.map((l) => l.value);
559
- const [minVal, maxVal] = d3Array2.extent(values);
560
- const strokeScale = d3Scale2.scaleLinear().domain([minVal, maxVal]).range([sStrokeMin, sStrokeMax]);
371
+ const [minVal, maxVal] = d3Array.extent(values);
372
+ const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([sStrokeMin, sStrokeMax]);
561
373
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
562
374
  renderChartTitle(
563
375
  svg,
@@ -642,7 +454,7 @@ function renderArcDiagram(container, parsed, palette, isDark, onClickItem, expor
642
454
  return g2 ? groupColorByName.get(g2) : void 0;
643
455
  };
644
456
  if (isVertical) {
645
- const yScale = d3Scale2.scalePoint().domain(nodes).range([0, innerHeight]).padding(0.5);
457
+ const yScale = d3Scale.scalePoint().domain(nodes).range([0, innerHeight]).padding(0.5);
646
458
  const baseX = innerWidth / 2;
647
459
  if (arcNodeGroups.length > 0) {
648
460
  const bandPad = (yScale.step?.() ?? 20) * 0.4;
@@ -702,7 +514,7 @@ function renderArcDiagram(container, parsed, palette, isDark, onClickItem, expor
702
514
  nodeG.append("text").attr("x", baseX - sNodeLabelXOffset).attr("y", y).attr("dy", "0.35em").attr("text-anchor", "end").attr("fill", textColor).attr("font-size", sNodeLabelFont).text(node);
703
515
  }
704
516
  } else {
705
- const xScale = d3Scale2.scalePoint().domain(nodes).range([0, innerWidth]).padding(0.5);
517
+ const xScale = d3Scale.scalePoint().domain(nodes).range([0, innerWidth]).padding(0.5);
706
518
  const arcStep = xScale.step();
707
519
  const widestLabelChars = Math.max(...nodes.map((n) => n.length));
708
520
  const labelWidthPx = widestLabelChars * sNodeLabelFont * 0.6;
@@ -827,175 +639,541 @@ function renderArcDiagram(container, parsed, palette, isDark, onClickItem, expor
827
639
  }
828
640
  }
829
641
 
830
- // src/wordcloud/renderer.ts
642
+ // src/quadrant/renderer.ts
643
+ import * as d3Scale2 from "d3-scale";
831
644
  import * as d3Selection2 from "d3-selection";
832
- import cloud from "d3-cloud";
833
- function getRotateFn(mode) {
834
- if (mode === "mixed") return () => Math.random() > 0.5 ? 0 : 90;
835
- if (mode === "angled") return () => Math.round(Math.random() * 30 - 15);
836
- return () => 0;
837
- }
838
- function hasCanvas2d() {
839
- try {
840
- if (typeof document === "undefined") return false;
841
- const canvas = document.createElement("canvas");
842
- return typeof canvas.getContext === "function" && !!canvas.getContext("2d");
843
- } catch {
844
- return false;
845
- }
846
- }
847
- function estimateWordWidth(text, size) {
848
- return measureText(text, size, { bold: true });
849
- }
850
- function layoutWordsNoCanvas(words, width, height, padding, rotateFn) {
851
- const sorted = [...words].sort((a, b) => b.size - a.size);
852
- const placed = [];
853
- const maxR = Math.sqrt(width * width + height * height) / 2;
854
- const aspect = width > 0 ? height / width : 1;
855
- for (const w of sorted) {
856
- const rotate = rotateFn();
857
- const rawW = estimateWordWidth(w.text, w.size) + padding * 2;
858
- const rawH = w.size + padding * 2;
859
- const rad = rotate * Math.PI / 180;
860
- const cos = Math.abs(Math.cos(rad));
861
- const sin = Math.abs(Math.sin(rad));
862
- const halfW = (rawW * cos + rawH * sin) / 2;
863
- const halfH = (rawW * sin + rawH * cos) / 2;
864
- let spot = null;
865
- for (let t = 0; t < 4e3; t++) {
866
- const a = t * 0.25;
867
- const r = a * 1.4;
868
- if (r > maxR) break;
869
- const x = Math.cos(a) * r;
870
- const y = Math.sin(a) * r * aspect;
871
- if (x - halfW < -width / 2 || x + halfW > width / 2 || y - halfH < -height / 2 || y + halfH > height / 2) {
872
- continue;
873
- }
874
- let collides = false;
875
- for (const p of placed) {
876
- if (Math.abs(x - p.x) < halfW + p.halfW && Math.abs(y - p.y) < halfH + p.halfH) {
877
- collides = true;
878
- break;
879
- }
880
- }
881
- if (!collides) {
882
- spot = { x, y };
883
- break;
884
- }
885
- }
886
- if (!spot) continue;
887
- placed.push({ ...w, rotate, x: spot.x, y: spot.y, halfW, halfH });
888
- }
889
- return placed;
890
- }
891
- function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
892
- const { words, cloudOptions } = parsed;
645
+ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
646
+ const {
647
+ quadrantLabels,
648
+ quadrantPoints,
649
+ quadrantXAxis,
650
+ quadrantYAxis,
651
+ quadrantTitleLineNumber,
652
+ quadrantXAxisLineNumber,
653
+ quadrantYAxisLineNumber
654
+ } = parsed;
893
655
  const title = parsed.noTitle ? null : parsed.title;
894
- if (words.length === 0) return;
656
+ if (quadrantPoints.length === 0) return;
895
657
  const init = initD3Chart(container, palette, exportDims);
896
658
  if (!init) return;
897
- const { svg, width, height, textColor, colors } = init;
898
- const idealWidth = Math.max(400, words.length * 30);
659
+ const { svg, width, height, textColor } = init;
660
+ const borderColor = palette.border;
661
+ const idealWidth = 600;
899
662
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
900
- const sTitleHeight = title ? ctx.aesthetic(40) : 0;
901
- const cloudHeight = height - sTitleHeight;
902
- const sPadding = ctx.structural(2);
903
663
  svg.attr("preserveAspectRatio", "xMidYMid meet");
904
664
  if (ctx.isBelowFloor) {
905
665
  svg.attr("width", "100%");
906
666
  }
907
- const { minSize, maxSize } = cloudOptions;
908
- const sMinSize = ctx.text(minSize);
909
- const sMaxSize = ctx.text(maxSize);
910
- const weights = words.map((w) => w.weight);
911
- const minWeight = Math.min(...weights);
912
- const maxWeight = Math.max(...weights);
913
- const range = maxWeight - minWeight || 1;
914
- const fontSize = (weight) => {
915
- const t = (weight - minWeight) / range;
916
- return sMinSize + Math.sqrt(t) * (sMaxSize - sMinSize);
667
+ const defaultColors = [
668
+ palette.colors.blue,
669
+ palette.colors.green,
670
+ palette.colors.yellow,
671
+ palette.colors.purple
672
+ ];
673
+ const hasXAxis = !!quadrantXAxis;
674
+ const hasYAxis = !!quadrantYAxis;
675
+ const margin = {
676
+ top: title ? ctx.aesthetic(60) : ctx.aesthetic(30),
677
+ right: ctx.aesthetic(30),
678
+ bottom: hasXAxis ? ctx.aesthetic(70) : ctx.aesthetic(40),
679
+ left: hasYAxis ? ctx.aesthetic(80) : ctx.aesthetic(40)
917
680
  };
918
- const rotateFn = getRotateFn(cloudOptions.rotate);
681
+ const chartWidth = width - margin.left - margin.right;
682
+ const chartHeight = height - margin.top - margin.bottom;
683
+ const xScale = d3Scale2.scaleLinear().domain([0, 1]).range([0, chartWidth]);
684
+ const yScale = d3Scale2.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
919
685
  renderChartTitle(
920
686
  svg,
921
687
  title,
922
- parsed.titleLineNumber,
688
+ quadrantTitleLineNumber,
923
689
  width,
924
690
  textColor,
925
691
  onClickItem
926
692
  );
927
- const g = svg.append("g").attr(
928
- "transform",
929
- `translate(${width / 2},${sTitleHeight + cloudHeight / 2})`
930
- );
931
- const sized = words.map((w) => ({ ...w, size: fontSize(w.weight) }));
932
- const draw = (layoutWords) => {
933
- g.selectAll("text").data(layoutWords).join("text").style("font-size", (d) => `${d.size}px`).style("font-family", FONT_FAMILY).style("font-weight", "bold").style("fill", (_d, i) => colors[i % colors.length]).style(
934
- "cursor",
935
- (d) => onClickItem && d.lineNumber ? "pointer" : "default"
936
- ).attr("text-anchor", "middle").attr("transform", (d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`).attr("data-line-number", (d) => {
937
- const ln = d.lineNumber;
938
- return ln ? String(ln) : null;
939
- }).text((d) => d.text).on("click", (_event, d) => {
940
- const ln = d.lineNumber;
941
- if (onClickItem && ln) onClickItem(ln);
942
- });
693
+ const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
694
+ const bg = themeBaseBg(palette, isDark);
695
+ const getQuadrantColor = (label, defaultIdx) => {
696
+ return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
943
697
  };
944
- if (!hasCanvas2d()) {
945
- draw(layoutWordsNoCanvas(sized, width, cloudHeight, sPadding, rotateFn));
946
- return;
947
- }
948
- cloud().size([width, cloudHeight]).words(sized).padding(sPadding).rotate(rotateFn).fontSize((d) => d.size).font(FONT_FAMILY).on("end", draw).start();
949
- }
950
- function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
951
- return new Promise((resolve) => {
952
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
953
- const { words, cloudOptions } = parsed;
954
- const title = parsed.noTitle ? null : parsed.title;
955
- if (words.length === 0) {
956
- resolve();
957
- return;
698
+ const getQuadrantFill = (label, defaultIdx) => {
699
+ return mix(getQuadrantColor(label, defaultIdx), bg, 30);
700
+ };
701
+ const quadrantDefs = [
702
+ {
703
+ position: "top-left",
704
+ x: 0,
705
+ y: 0,
706
+ w: chartWidth / 2,
707
+ h: chartHeight / 2,
708
+ labelX: chartWidth / 4,
709
+ labelY: chartHeight / 4,
710
+ label: quadrantLabels.topLeft,
711
+ colorIdx: 1
712
+ // green
713
+ },
714
+ {
715
+ position: "top-right",
716
+ x: chartWidth / 2,
717
+ y: 0,
718
+ w: chartWidth / 2,
719
+ h: chartHeight / 2,
720
+ labelX: chartWidth * 3 / 4,
721
+ labelY: chartHeight / 4,
722
+ label: quadrantLabels.topRight,
723
+ colorIdx: 0
724
+ // blue
725
+ },
726
+ {
727
+ position: "bottom-left",
728
+ x: 0,
729
+ y: chartHeight / 2,
730
+ w: chartWidth / 2,
731
+ h: chartHeight / 2,
732
+ labelX: chartWidth / 4,
733
+ labelY: chartHeight * 3 / 4,
734
+ label: quadrantLabels.bottomLeft,
735
+ colorIdx: 2
736
+ // yellow
737
+ },
738
+ {
739
+ position: "bottom-right",
740
+ x: chartWidth / 2,
741
+ y: chartHeight / 2,
742
+ w: chartWidth / 2,
743
+ h: chartHeight / 2,
744
+ labelX: chartWidth * 3 / 4,
745
+ labelY: chartHeight * 3 / 4,
746
+ label: quadrantLabels.bottomRight,
747
+ colorIdx: 3
748
+ // purple
958
749
  }
959
- const width = exportDims?.width ?? container.clientWidth;
960
- const height = exportDims?.height ?? container.clientHeight;
961
- if (width <= 0 || height <= 0) {
962
- resolve();
963
- return;
750
+ ];
751
+ const quadrantRects = chartG.selectAll("rect.quadrant").data(quadrantDefs).enter().append("rect").attr("class", "quadrant").attr("x", (d) => d.x).attr("y", (d) => d.y).attr("width", (d) => d.w).attr("height", (d) => d.h).attr("fill", (d) => getQuadrantFill(d.label, d.colorIdx)).attr("stroke", (d) => getQuadrantColor(d.label, d.colorIdx)).attr("stroke-width", ctx.structural(2));
752
+ const shadowColor = isDark ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.4)";
753
+ const quadrantLabelColor = mix(textColor, bg, 35);
754
+ const LABEL_MAX_FONT = ctx.text(48);
755
+ const LABEL_MIN_FONT = ctx.text(14);
756
+ const LABEL_PAD = ctx.aesthetic(40);
757
+ const quadrantLabelLayout = (text, qw2, qh2) => {
758
+ const availW = qw2 - LABEL_PAD;
759
+ const availH = qh2 - LABEL_PAD;
760
+ if (measureText(text, LABEL_MAX_FONT, { bold: true }) <= availW) {
761
+ const fs = Math.min(LABEL_MAX_FONT, availH);
762
+ return {
763
+ lines: [text],
764
+ fontSize: Math.max(LABEL_MIN_FONT, Math.round(fs))
765
+ };
964
766
  }
965
- const titleHeight = title ? 40 : 0;
966
- const cloudHeight = height - titleHeight;
967
- const textColor = palette.text;
968
- const bgColor = palette.bg;
969
- const colors = getSeriesColors(palette);
970
- const { minSize, maxSize } = cloudOptions;
971
- const weights = words.map((w) => w.weight);
972
- const minWeight = Math.min(...weights);
973
- const maxWeight = Math.max(...weights);
974
- const range = maxWeight - minWeight || 1;
975
- const fontSize = (weight) => {
976
- const t = (weight - minWeight) / range;
977
- return minSize + Math.sqrt(t) * (maxSize - minSize);
767
+ const wrapLines = (fs) => wrapTextToWidth(text, fs, availW, { bold: true });
768
+ let lo = LABEL_MIN_FONT;
769
+ let hi = LABEL_MAX_FONT;
770
+ let bestLines = wrapLines(lo);
771
+ let bestFs = lo;
772
+ while (lo <= hi) {
773
+ const mid = Math.round((lo + hi) / 2);
774
+ const lines = wrapLines(mid);
775
+ const totalH = lines.length * mid * 1.2;
776
+ const maxLineW = Math.max(
777
+ ...lines.map((l) => measureText(l, mid, { bold: true }))
778
+ );
779
+ if (maxLineW <= availW && totalH <= availH) {
780
+ bestFs = mid;
781
+ bestLines = lines;
782
+ lo = mid + 1;
783
+ } else {
784
+ hi = mid - 1;
785
+ }
786
+ }
787
+ return { lines: bestLines, fontSize: Math.max(LABEL_MIN_FONT, bestFs) };
788
+ };
789
+ const qw = chartWidth / 2;
790
+ const qh = chartHeight / 2;
791
+ const quadrantDefsWithLabel = quadrantDefs.filter((d) => d.label !== null);
792
+ const labelLayouts = new Map(
793
+ quadrantDefsWithLabel.map((d) => [
794
+ d.label.text,
795
+ quadrantLabelLayout(d.label.text, qw, qh)
796
+ ])
797
+ );
798
+ const renderWatermarkOriginal = (textEl, d) => {
799
+ const layout = labelLayouts.get(d.label.text);
800
+ const el = d3Selection2.select(textEl);
801
+ el.text(null).attr("font-size", layout.fontSize).attr("font-weight", "bold");
802
+ if (layout.lines.length === 1) {
803
+ el.text(layout.lines[0]);
804
+ } else {
805
+ const lineH = layout.fontSize * 1.2;
806
+ const totalH = layout.lines.length * lineH;
807
+ const startY = -totalH / 2 + lineH / 2;
808
+ layout.lines.forEach((line2, i) => {
809
+ el.append("tspan").attr("x", d.labelX).attr("dy", i === 0 ? `${startY}px` : `${lineH}px`).text(line2);
810
+ });
811
+ }
812
+ };
813
+ const quadrantLabelTexts = chartG.selectAll("text.quadrant-label").data(quadrantDefsWithLabel).enter().append("text").attr("class", "quadrant-label").attr("x", (d) => d.labelX).attr("y", (d) => d.labelY).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", quadrantLabelColor).attr(
814
+ "data-line-number",
815
+ (d) => d.label?.lineNumber ? String(d.label.lineNumber) : null
816
+ ).style(
817
+ "cursor",
818
+ (d) => onClickItem && d.label?.lineNumber ? "pointer" : "default"
819
+ ).each(function(d) {
820
+ renderWatermarkOriginal(this, d);
821
+ });
822
+ if (onClickItem) {
823
+ quadrantLabelTexts.on("click", (_, d) => {
824
+ if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
825
+ }).on("mouseenter", function() {
826
+ d3Selection2.select(this).attr("opacity", 0.7);
827
+ }).on("mouseleave", function() {
828
+ d3Selection2.select(this).attr("opacity", 1);
829
+ });
830
+ }
831
+ const sAxisFont = ctx.text(18);
832
+ const sAxisPad = ctx.aesthetic(20);
833
+ const sAxisX = ctx.aesthetic(22);
834
+ if (quadrantXAxis) {
835
+ const xLowLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", margin.left + chartWidth / 4).attr("y", height - sAxisPad).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr(
836
+ "data-line-number",
837
+ quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null
838
+ ).style(
839
+ "cursor",
840
+ onClickItem && quadrantXAxisLineNumber ? "pointer" : "default"
841
+ ).text(quadrantXAxis[0]);
842
+ const xHighLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", margin.left + chartWidth * 3 / 4).attr("y", height - sAxisPad).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr(
843
+ "data-line-number",
844
+ quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null
845
+ ).style(
846
+ "cursor",
847
+ onClickItem && quadrantXAxisLineNumber ? "pointer" : "default"
848
+ ).text(quadrantXAxis[1]);
849
+ if (onClickItem && quadrantXAxisLineNumber) {
850
+ [xLowLabel, xHighLabel].forEach((label) => {
851
+ label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
852
+ d3Selection2.select(this).attr("opacity", 0.7);
853
+ }).on("mouseleave", function() {
854
+ d3Selection2.select(this).attr("opacity", 1);
855
+ });
856
+ });
857
+ }
858
+ }
859
+ if (quadrantYAxis) {
860
+ const yMidBottom = margin.top + chartHeight * 3 / 4;
861
+ const yMidTop = margin.top + chartHeight / 4;
862
+ const yLowLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", sAxisX).attr("y", yMidBottom).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr("transform", `rotate(-90, ${sAxisX}, ${yMidBottom})`).attr(
863
+ "data-line-number",
864
+ quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null
865
+ ).style(
866
+ "cursor",
867
+ onClickItem && quadrantYAxisLineNumber ? "pointer" : "default"
868
+ ).text(quadrantYAxis[0]);
869
+ const yHighLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", sAxisX).attr("y", yMidTop).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr("transform", `rotate(-90, ${sAxisX}, ${yMidTop})`).attr(
870
+ "data-line-number",
871
+ quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null
872
+ ).style(
873
+ "cursor",
874
+ onClickItem && quadrantYAxisLineNumber ? "pointer" : "default"
875
+ ).text(quadrantYAxis[1]);
876
+ if (onClickItem && quadrantYAxisLineNumber) {
877
+ [yLowLabel, yHighLabel].forEach((label) => {
878
+ label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
879
+ d3Selection2.select(this).attr("opacity", 0.7);
880
+ }).on("mouseleave", function() {
881
+ d3Selection2.select(this).attr("opacity", 1);
882
+ });
883
+ });
884
+ }
885
+ }
886
+ chartG.append("line").attr("x1", chartWidth / 2).attr("y1", 0).attr("x2", chartWidth / 2).attr("y2", chartHeight).attr("stroke", borderColor).attr("stroke-width", 1);
887
+ chartG.append("line").attr("x1", 0).attr("y1", chartHeight / 2).attr("x2", chartWidth).attr("y2", chartHeight / 2).attr("stroke", borderColor).attr("stroke-width", 1);
888
+ const getPointQuadrant = (x, y) => {
889
+ if (x >= 0.5 && y >= 0.5) return "top-right";
890
+ if (x < 0.5 && y >= 0.5) return "top-left";
891
+ if (x < 0.5 && y < 0.5) return "bottom-left";
892
+ return "bottom-right";
893
+ };
894
+ const POINT_RADIUS = ctx.structural(6);
895
+ const POINT_LABEL_FONT_SIZE = ctx.text(12);
896
+ const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
897
+ const layout = labelLayouts.get(d.label.text);
898
+ const totalW = Math.max(
899
+ ...layout.lines.map((l) => measureText(l, layout.fontSize))
900
+ );
901
+ const totalH = layout.lines.length * layout.fontSize * 1.2;
902
+ return {
903
+ x: d.labelX - totalW / 2,
904
+ y: d.labelY - totalH / 2,
905
+ w: totalW,
906
+ h: totalH
978
907
  };
979
- const rotateFn = getRotateFn(cloudOptions.rotate);
980
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMid meet").style("background", bgColor);
981
- renderChartTitle(svg, title, parsed.titleLineNumber, width, textColor);
982
- const g = svg.append("g").attr(
983
- "transform",
984
- `translate(${width / 2},${titleHeight + cloudHeight / 2})`
908
+ });
909
+ const pointPixels = quadrantPoints.map((point) => ({
910
+ label: point.label,
911
+ cx: xScale(point.x),
912
+ cy: yScale(point.y)
913
+ }));
914
+ const placedPointLabels = computeQuadrantPointLabels(
915
+ pointPixels,
916
+ { left: 0, top: 0, right: chartWidth, bottom: chartHeight },
917
+ quadrantLabelObstacles,
918
+ POINT_RADIUS,
919
+ POINT_LABEL_FONT_SIZE,
920
+ // Point labels are drawn at 700 here.
921
+ true
922
+ );
923
+ const pointsG = chartG.append("g").attr("class", "points");
924
+ quadrantPoints.forEach((point, i) => {
925
+ const cx = xScale(point.x);
926
+ const cy = yScale(point.y);
927
+ const quadrant = getPointQuadrant(point.x, point.y);
928
+ const quadDef = quadrantDefs.find((d) => d.position === quadrant);
929
+ const pointColor = quadDef?.label?.color ?? defaultColors[quadDef?.colorIdx ?? 0];
930
+ const placed = placedPointLabels[i];
931
+ const pointG = pointsG.append("g").attr("class", "point-group").attr("data-line-number", String(point.lineNumber)).attr("data-quadrant", quadrant);
932
+ if (placed.connectorLine) {
933
+ pointG.append("line").attr("x1", placed.connectorLine.x1).attr("y1", placed.connectorLine.y1).attr("x2", placed.connectorLine.x2).attr("y2", placed.connectorLine.y2).attr("stroke", pointColor).attr("stroke-width", 1).attr("opacity", 0.5);
934
+ }
935
+ pointG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", POINT_RADIUS).attr("fill", bg).attr("stroke", pointColor).attr("stroke-width", ctx.structural(2));
936
+ const labelText = pointG.append("text").attr("x", placed.x).attr("y", placed.y).attr("text-anchor", placed.anchor).attr("dominant-baseline", "central").attr("fill", textColor).attr("font-size", POINT_LABEL_FONT_SIZE).attr("font-weight", "bold").style("text-shadow", `0 1px 2px ${shadowColor}`).style("transition", "y 120ms ease-out");
937
+ labelText.append("tspan").text(point.label);
938
+ const coordsTspan = labelText.append("tspan").attr("class", "point-coords").attr("x", placed.x).attr("dy", `${POINT_LABEL_FONT_SIZE}px`).attr("font-size", ctx.text(10)).attr("font-weight", "normal").attr("visibility", "hidden").text(`${point.x.toFixed(2)}, ${point.y.toFixed(2)}`);
939
+ const COORDS_LINE_H = ctx.structural(14);
940
+ const bumpDy = placed.y < cy ? -COORDS_LINE_H : COORDS_LINE_H;
941
+ pointG.style("cursor", onClickItem ? "pointer" : "default").on("mouseenter", () => {
942
+ pointG.select("circle").attr("r", ctx.structural(8));
943
+ labelText.attr("y", placed.y + bumpDy);
944
+ coordsTspan.attr("visibility", "visible");
945
+ quadrantRects.attr(
946
+ "opacity",
947
+ (qd) => qd.position === quadrant ? 1 : 0.3
948
+ );
949
+ quadrantLabelTexts.attr(
950
+ "opacity",
951
+ (qd) => qd.position === quadrant ? 1 : 0.3
952
+ );
953
+ pointsG.selectAll("g.point-group").filter((_, j) => j !== i).attr("opacity", 0.3);
954
+ }).on("mouseleave", () => {
955
+ pointG.select("circle").attr("r", POINT_RADIUS);
956
+ labelText.attr("y", placed.y);
957
+ coordsTspan.attr("visibility", "hidden");
958
+ quadrantRects.attr("opacity", 1);
959
+ quadrantLabelTexts.attr("opacity", 1);
960
+ pointsG.selectAll("g.point-group").attr("opacity", 1);
961
+ }).on("click", () => {
962
+ if (onClickItem && point.lineNumber) onClickItem(point.lineNumber);
963
+ });
964
+ });
965
+ quadrantRects.style("cursor", onClickItem ? "pointer" : "default").on("mouseenter", function(_, d) {
966
+ quadrantRects.attr(
967
+ "opacity",
968
+ (qd) => qd.position === d.position ? 1 : 0.3
985
969
  );
986
- const sized = words.map((w) => ({ ...w, size: fontSize(w.weight) }));
987
- const draw = (layoutWords) => {
988
- g.selectAll("text").data(layoutWords).join("text").style("font-size", (d) => `${d.size}px`).style("font-family", FONT_FAMILY).style("font-weight", "bold").style("fill", (_d, i) => colors[i % colors.length]).attr("text-anchor", "middle").attr(
989
- "transform",
990
- (d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`
991
- ).text((d) => d.text);
992
- resolve();
970
+ quadrantLabelTexts.attr(
971
+ "opacity",
972
+ (qd) => qd.position === d.position ? 1 : 0.3
973
+ );
974
+ pointsG.selectAll("g.point-group").each(function(_2, i) {
975
+ const pt = quadrantPoints[i];
976
+ const ptQuad = getPointQuadrant(pt.x, pt.y);
977
+ d3Selection2.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
978
+ });
979
+ }).on("mouseleave", () => {
980
+ quadrantRects.attr("opacity", 1);
981
+ quadrantLabelTexts.attr("opacity", 1);
982
+ pointsG.selectAll("g.point-group").attr("opacity", 1);
983
+ }).on("click", (_, d) => {
984
+ if (onClickItem && d.label?.lineNumber) {
985
+ onClickItem(d.label.lineNumber);
986
+ }
987
+ });
988
+ }
989
+
990
+ // src/slope/renderer.ts
991
+ import * as d3Scale3 from "d3-scale";
992
+ import * as d3Shape from "d3-shape";
993
+ import * as d3Array2 from "d3-array";
994
+ function resolveVerticalCollisions(items, minGap, maxY) {
995
+ if (items.length === 0) return [];
996
+ const sorted = items.map((it, i) => ({ ...it, idx: i })).sort((a, b) => a.naturalY - b.naturalY);
997
+ const adjustedY = new Array(items.length);
998
+ let prevBottom = -Infinity;
999
+ for (const item of sorted) {
1000
+ const halfH = item.height / 2;
1001
+ let top = Math.max(item.naturalY - halfH, prevBottom + minGap);
1002
+ if (maxY !== void 0) {
1003
+ top = Math.min(top, maxY - item.height);
1004
+ }
1005
+ adjustedY[item.idx] = top + halfH;
1006
+ prevBottom = top + item.height;
1007
+ }
1008
+ return adjustedY;
1009
+ }
1010
+ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
1011
+ var SLOPE_LABEL_FONT_SIZE = 14;
1012
+ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
1013
+ const { periods, data } = parsed;
1014
+ const title = parsed.noTitle ? null : parsed.title;
1015
+ if (data.length === 0 || periods.length < 2) return;
1016
+ const init = initD3Chart(container, palette, exportDims);
1017
+ if (!init) return;
1018
+ const { svg, width, height, textColor, mutedColor, bgColor, colors } = init;
1019
+ const idealWidth = SLOPE_MARGIN.left + periods.length * 100 + 150;
1020
+ const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
1021
+ const sMarginTop = ctx.aesthetic(SLOPE_MARGIN.top);
1022
+ const sMarginBottom = ctx.aesthetic(SLOPE_MARGIN.bottom);
1023
+ const sMarginLeft = ctx.aesthetic(SLOPE_MARGIN.left);
1024
+ const sLabelFontSize = ctx.text(SLOPE_LABEL_FONT_SIZE);
1025
+ const sPeriodFont = ctx.text(18);
1026
+ const sValueFont = ctx.text(16);
1027
+ const sPeriodHeaderY = ctx.structural(15);
1028
+ const sDash = scaleDash(GRID_DASH, (n) => ctx.structural(n));
1029
+ const sLineStroke = ctx.structural(2.5);
1030
+ const sHitWidth = ctx.structural(14);
1031
+ const sPointR = ctx.structural(4);
1032
+ const sPointStroke = ctx.structural(1.5);
1033
+ const sValueLabelXOff = ctx.structural(10);
1034
+ const sLabelGap = ctx.structural(10);
1035
+ svg.attr("preserveAspectRatio", "xMidYMin meet");
1036
+ if (ctx.isBelowFloor) {
1037
+ svg.attr("width", "100%");
1038
+ }
1039
+ const maxLabelText = data.reduce((longest, item) => {
1040
+ const text = `${item.values[item.values.length - 1]} \u2014 ${item.label}`;
1041
+ return text.length > longest.length ? text : longest;
1042
+ }, "");
1043
+ const estimatedLabelWidth = measureText(maxLabelText, sLabelFontSize);
1044
+ const maxRightMargin = Math.floor(width * 0.35);
1045
+ const rightMargin = Math.min(
1046
+ Math.max(estimatedLabelWidth + ctx.aesthetic(30), ctx.aesthetic(120)),
1047
+ maxRightMargin
1048
+ );
1049
+ const innerWidth = width - sMarginLeft - rightMargin;
1050
+ const innerHeight = height - sMarginTop - sMarginBottom;
1051
+ const allValues2 = data.flatMap((d) => d.values);
1052
+ const [minVal, maxVal] = d3Array2.extent(allValues2);
1053
+ const valuePadding = (maxVal - minVal) * 0.1 || 1;
1054
+ const yScale = d3Scale3.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
1055
+ const xScale = d3Scale3.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
1056
+ const g = svg.append("g").attr("transform", `translate(${sMarginLeft},${sMarginTop})`);
1057
+ const tooltip = createTooltip(container, palette, isDark);
1058
+ renderChartTitle(
1059
+ svg,
1060
+ title,
1061
+ parsed.titleLineNumber,
1062
+ width,
1063
+ textColor,
1064
+ onClickItem
1065
+ );
1066
+ for (const period of periods) {
1067
+ const x = xScale(period);
1068
+ g.append("text").attr("x", x).attr("y", -sPeriodHeaderY).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sPeriodFont).attr("font-weight", "bold").text(period);
1069
+ g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", sDash);
1070
+ }
1071
+ const lineGen = d3Shape.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
1072
+ const seriesInfo = data.map((item, idx) => {
1073
+ const color = item.color ?? colors[idx % colors.length];
1074
+ const firstVal = item.values[0];
1075
+ const lastVal = item.values[item.values.length - 1];
1076
+ const absChange = lastVal - firstVal;
1077
+ const pctChange = firstVal !== 0 ? absChange / firstVal * 100 : null;
1078
+ const sign = absChange > 0 ? "+" : "";
1079
+ const tipLines = [`${sign}${parseFloat(absChange.toFixed(2))}`];
1080
+ if (pctChange !== null) tipLines.push(`${sign}${pctChange.toFixed(1)}%`);
1081
+ const tipHtml = tipLines.join("<br>");
1082
+ const lastX = xScale(periods[periods.length - 1]);
1083
+ const labelText = `${lastVal} \u2014 ${item.label}`;
1084
+ const availableWidth = rightMargin - ctx.aesthetic(15);
1085
+ let labelLineCount = 1;
1086
+ let wrappedLines = null;
1087
+ if (measureText(labelText, sLabelFontSize) > availableWidth) {
1088
+ const lines = wrapTextToWidth(labelText, sLabelFontSize, availableWidth);
1089
+ labelLineCount = lines.length;
1090
+ wrappedLines = lines;
1091
+ }
1092
+ const lineHeight = sLabelFontSize * 1.2;
1093
+ const labelHeight = labelLineCount === 1 ? sLabelFontSize : labelLineCount * lineHeight;
1094
+ return {
1095
+ item,
1096
+ idx,
1097
+ color,
1098
+ firstVal,
1099
+ lastVal,
1100
+ tipHtml,
1101
+ lastX,
1102
+ labelText,
1103
+ wrappedLines,
1104
+ labelHeight
993
1105
  };
994
- if (!hasCanvas2d()) {
995
- draw(layoutWordsNoCanvas(sized, width, cloudHeight, 2, rotateFn));
996
- return;
1106
+ });
1107
+ const leftLabelHeight = sValueFont * 1.25;
1108
+ const leftLabelCollisions = /* @__PURE__ */ new Map();
1109
+ for (let pi = 0; pi < periods.length - 1; pi++) {
1110
+ const entries = data.map((item) => ({
1111
+ // pi is in-bounds by loop guard against periods.length, and each data row has periods.length values.
1112
+ naturalY: yScale(item.values[pi]),
1113
+ height: leftLabelHeight
1114
+ }));
1115
+ leftLabelCollisions.set(
1116
+ pi,
1117
+ resolveVerticalCollisions(entries, 4, innerHeight)
1118
+ );
1119
+ }
1120
+ const rightEntries = seriesInfo.map((si) => ({
1121
+ naturalY: yScale(si.lastVal),
1122
+ height: Math.max(si.labelHeight, sLabelFontSize * 1.4)
1123
+ }));
1124
+ const rightAdjustedY = resolveVerticalCollisions(
1125
+ rightEntries,
1126
+ 4,
1127
+ innerHeight
1128
+ );
1129
+ data.forEach((item, idx) => {
1130
+ const si = seriesInfo[idx];
1131
+ const color = si.color;
1132
+ const seriesG = g.append("g").attr("class", "slope-series").attr("data-line-number", String(item.lineNumber));
1133
+ seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", color).attr("stroke-width", sLineStroke).attr("d", lineGen);
1134
+ seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", sHitWidth).attr("d", lineGen).style("cursor", onClickItem ? "pointer" : "default").on(
1135
+ "mouseenter",
1136
+ (event) => showTooltip(tooltip, si.tipHtml, event)
1137
+ ).on(
1138
+ "mousemove",
1139
+ (event) => showTooltip(tooltip, si.tipHtml, event)
1140
+ ).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
1141
+ if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
1142
+ });
1143
+ item.values.forEach((val, i) => {
1144
+ const x = xScale(periods[i]);
1145
+ const y = yScale(val);
1146
+ seriesG.append("circle").attr("cx", x).attr("cy", y).attr("r", sPointR).attr("fill", color).attr("stroke", bgColor).attr("stroke-width", sPointStroke).style("cursor", onClickItem ? "pointer" : "default").on(
1147
+ "mouseenter",
1148
+ (event) => showTooltip(tooltip, si.tipHtml, event)
1149
+ ).on(
1150
+ "mousemove",
1151
+ (event) => showTooltip(tooltip, si.tipHtml, event)
1152
+ ).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
1153
+ if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
1154
+ });
1155
+ const isFirst = i === 0;
1156
+ const isLast = i === periods.length - 1;
1157
+ if (!isLast) {
1158
+ const adjustedY = leftLabelCollisions.get(i)[idx];
1159
+ seriesG.append("text").attr("x", isFirst ? x - sValueLabelXOff : x).attr("y", adjustedY).attr("dy", "0.35em").attr("text-anchor", isFirst ? "end" : "middle").attr("fill", color).attr("font-size", sValueFont).text(val.toString());
1160
+ }
1161
+ });
1162
+ const adjustedLastY = rightAdjustedY[idx];
1163
+ const labelEl = seriesG.append("text").attr("x", si.lastX + sLabelGap).attr("y", adjustedLastY).attr("text-anchor", "start").attr("fill", color).attr("font-size", sLabelFontSize).attr("font-weight", "normal");
1164
+ if (!si.wrappedLines) {
1165
+ labelEl.attr("dy", "0.35em").text(si.labelText);
1166
+ } else {
1167
+ const lineHeight = sLabelFontSize * 1.2;
1168
+ const totalHeight = (si.wrappedLines.length - 1) * lineHeight;
1169
+ const startDy = -totalHeight / 2;
1170
+ si.wrappedLines.forEach((line2, li) => {
1171
+ labelEl.append("tspan").attr("x", si.lastX + sLabelGap).attr(
1172
+ "dy",
1173
+ li === 0 ? `${startDy + sLabelFontSize * 0.35}px` : `${lineHeight}px`
1174
+ ).text(line2);
1175
+ });
997
1176
  }
998
- cloud().size([width, cloudHeight]).words(sized).padding(2).rotate(rotateFn).fontSize((d) => d.size).font(FONT_FAMILY).on("end", draw).start();
999
1177
  });
1000
1178
  }
1001
1179
 
@@ -1465,985 +1643,484 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
1465
1643
  sy /= excluded.length;
1466
1644
  const dx = centroid.x - sx;
1467
1645
  const dy = centroid.y - sy;
1468
- const m = Math.sqrt(dx * dx + dy * dy);
1469
- if (m >= 1e-6) {
1470
- const nx = Math.abs(dx / m) < 1e-9 ? 0 : dx / m;
1471
- const ny = Math.abs(dy / m) < 1e-9 ? 0 : dy / m;
1472
- return { x: nx, y: ny };
1473
- }
1474
- }
1475
- if (n === 3) return { x: 0, y: -1 };
1476
- return { x: 0, y: 1 };
1477
- }
1478
- function lensExit(c0, dir, idxs) {
1479
- let minT = Infinity;
1480
- for (const i of idxs) {
1481
- const c = circles[i];
1482
- const dx = c0.x - c.x;
1483
- const dy = c0.y - c.y;
1484
- const B = dx * dir.x + dy * dir.y;
1485
- const C = dx * dx + dy * dy - c.r * c.r;
1486
- const disc = B * B - C;
1487
- if (disc < 0) continue;
1488
- const t = -B + Math.sqrt(disc);
1489
- if (t > 0 && t < minT) minT = t;
1490
- }
1491
- if (!isFinite(minT)) return { x: c0.x, y: c0.y };
1492
- return { x: c0.x + dir.x * minT, y: c0.y + dir.y * minT };
1493
- }
1494
- function unionExit(c0, dir, idxs) {
1495
- let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
1496
- for (const c of circles) {
1497
- minX = Math.min(minX, c.x - c.r);
1498
- maxX = Math.max(maxX, c.x + c.r);
1499
- minY = Math.min(minY, c.y - c.r);
1500
- maxY = Math.max(maxY, c.y + c.r);
1501
- }
1502
- const STEP = 3;
1503
- const MAX_ITERS = 400;
1504
- const axisAligned = dir.x === 0 || dir.y === 0;
1505
- let p = { x: c0.x, y: c0.y };
1506
- let leftOverlap = false;
1507
- for (let i = 0; i < MAX_ITERS; i++) {
1508
- const next = { x: p.x + dir.x * STEP, y: p.y + dir.y * STEP };
1509
- p = next;
1510
- if (!leftOverlap) {
1511
- leftOverlap = !idxs.every((ci) => pointInCircle(next, circles[ci]));
1512
- if (!leftOverlap) continue;
1513
- }
1514
- const insideAny = circles.some((c) => pointInCircle(next, c));
1515
- if (insideAny) continue;
1516
- if (axisAligned) {
1517
- const passedX = dir.x > 0 ? next.x >= maxX : next.x <= minX;
1518
- const passedY = dir.y > 0 ? next.y >= maxY : next.y <= minY;
1519
- if (dir.x !== 0 && !passedX) continue;
1520
- if (dir.y !== 0 && !passedY) continue;
1521
- }
1522
- break;
1523
- }
1524
- return p;
1525
- }
1526
- for (const ov of vennOverlaps) {
1527
- if (!ov.label) continue;
1528
- const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
1529
- if (idxs.some((idx) => idx < 0)) continue;
1530
- const lines = wrappedOverlapLabels.get(ov) ?? [ov.label];
1531
- const inside = circles.map((_, j) => idxs.includes(j));
1532
- const centroid = regionCentroid(circles, inside);
1533
- const dir = overlapOutwardDir(centroid, idxs);
1534
- const isTriple = idxs.length === 3 && n === 3;
1535
- const padBoost = isTriple && dir.y < 0 ? 32 : 0;
1536
- const leaderPad = OVERLAP_LEADER_PAD + padBoost;
1537
- const lensPt = lensExit(centroid, dir, idxs);
1538
- const farExit = unionExit(centroid, dir, idxs);
1539
- const stubEndX = farExit.x + dir.x * leaderPad;
1540
- const stubEndY = farExit.y + dir.y * leaderPad;
1541
- const horizontal = Math.abs(dir.x) >= Math.abs(dir.y);
1542
- let textAnchor;
1543
- let baseline = "central";
1544
- if (horizontal) {
1545
- textAnchor = dir.x >= 0 ? "start" : "end";
1546
- } else {
1547
- textAnchor = "middle";
1548
- baseline = dir.y >= 0 ? "hanging" : "auto";
1549
- }
1550
- let textX, textY;
1551
- if (horizontal) {
1552
- const sign = dir.x >= 0 ? 1 : -1;
1553
- textX = stubEndX + sign * OVERLAP_TEXT_GAP;
1554
- textY = stubEndY;
1555
- } else {
1556
- const sign = dir.y >= 0 ? 1 : -1;
1557
- textX = stubEndX;
1558
- textY = stubEndY + sign * OVERLAP_TEXT_GAP;
1559
- }
1560
- const blockW = lines.reduce(
1561
- (m, l) => Math.max(m, measureText(l, OVERLAP_FONT, { bold: true })),
1562
- 0
1563
- );
1564
- const blockH = lines.length * OVERLAP_LINE_H;
1565
- if (textAnchor === "start") textX = Math.min(textX, width - blockW - 4);
1566
- else if (textAnchor === "end") textX = Math.max(textX, blockW + 4);
1567
- else
1568
- textX = Math.max(blockW / 2 + 4, Math.min(width - blockW / 2 - 4, textX));
1569
- let topY, bottomY;
1570
- if (baseline === "hanging") {
1571
- topY = textY;
1572
- bottomY = textY + blockH;
1573
- } else if (baseline === "auto") {
1574
- bottomY = textY;
1575
- topY = textY - blockH;
1576
- } else {
1577
- topY = textY - blockH / 2;
1578
- bottomY = textY + blockH / 2;
1579
- }
1580
- if (topY < sTitleHeight + 6) textY += sTitleHeight + 6 - topY;
1581
- else if (bottomY > height - 4) textY -= bottomY - (height - 4);
1582
- const startY = baseline === "hanging" ? textY : baseline === "auto" ? textY - (lines.length - 1) * OVERLAP_LINE_H : textY - (lines.length - 1) * OVERLAP_LINE_H / 2;
1583
- const leaderStartX = isTriple ? centroid.x : lensPt.x;
1584
- const leaderStartY = isTriple ? centroid.y : lensPt.y;
1585
- let tinted = setColors[idxs[0]];
1586
- for (let k = 1; k < idxs.length; k++) {
1587
- const pct = k / (k + 1) * 100;
1588
- tinted = mix(tinted, setColors[idxs[k]], pct);
1589
- }
1590
- const overlapColor = mix(tinted, textColor, 90);
1591
- const ovKey = [...idxs].sort((a, b) => a - b).join("-");
1592
- const ovLabelG = labelGroup.append("g").style("cursor", "default").on("mouseenter", () => showRegionOverlay(idxs)).on("mouseleave", () => hideAllOverlays());
1593
- registerLabel(ovKey, ovLabelG);
1594
- const labelRects = setLabelBBoxes.filter((b) => b !== null);
1595
- const segments = clipLineByRects(
1596
- leaderStartX,
1597
- leaderStartY,
1598
- stubEndX,
1599
- stubEndY,
1600
- labelRects,
1601
- 4
1602
- );
1603
- for (const seg of segments) {
1604
- ovLabelG.append("line").attr("x1", seg.x1).attr("y1", seg.y1).attr("x2", seg.x2).attr("y2", seg.y2).attr("stroke", overlapColor).attr("stroke-width", ctx.structural(1.25)).attr("opacity", 0.85);
1605
- }
1606
- const textEl = ovLabelG.append("text").attr("text-anchor", textAnchor).attr("dominant-baseline", baseline).attr("fill", overlapColor).attr("font-size", OVERLAP_FONT).attr("font-weight", "bold");
1607
- lines.forEach((line2, i) => {
1608
- const tspan = textEl.append("tspan").attr("x", textX);
1609
- if (i === 0) tspan.attr("y", startY);
1610
- else tspan.attr("dy", OVERLAP_LINE_H);
1611
- tspan.text(line2);
1612
- });
1613
- }
1614
- const hoverGroup = svg.append("g");
1615
- circles.forEach((c, i) => {
1616
- hoverGroup.append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", c.r).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", String(vennSets[i].lineNumber)).style("cursor", onClickItem ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
1617
- showRegionOverlay([i]);
1618
- }).on("mouseleave", () => {
1619
- hideAllOverlays();
1620
- }).on("click", function() {
1621
- this.blur?.();
1622
- if (onClickItem && vennSets[i].lineNumber)
1623
- onClickItem(vennSets[i].lineNumber);
1624
- });
1625
- });
1626
- const overlayR = scaledR * 0.35;
1627
- const subsets = [];
1628
- if (n === 2) {
1629
- subsets.push({
1630
- idxs: [0, 1],
1631
- sets: [vennSets[0].name, vennSets[1].name].sort()
1632
- });
1633
- } else {
1634
- for (let a = 0; a < n; a++) {
1635
- for (let b = a + 1; b < n; b++) {
1636
- subsets.push({
1637
- idxs: [a, b],
1638
- sets: [vennSets[a].name, vennSets[b].name].sort()
1639
- });
1646
+ const m = Math.sqrt(dx * dx + dy * dy);
1647
+ if (m >= 1e-6) {
1648
+ const nx = Math.abs(dx / m) < 1e-9 ? 0 : dx / m;
1649
+ const ny = Math.abs(dy / m) < 1e-9 ? 0 : dy / m;
1650
+ return { x: nx, y: ny };
1640
1651
  }
1641
1652
  }
1642
- subsets.push({
1643
- idxs: [0, 1, 2],
1644
- sets: [vennSets[0].name, vennSets[1].name, vennSets[2].name].sort()
1645
- });
1646
- }
1647
- for (const subset of subsets) {
1648
- const { idxs, sets } = subset;
1649
- const inside = circles.map((_, j) => idxs.includes(j));
1650
- const centroid = regionCentroid(circles, inside);
1651
- const declaredOv = vennOverlaps.find(
1652
- (ov) => ov.sets.length === sets.length && ov.sets.every((s, k) => s === sets[k])
1653
- );
1654
- hoverGroup.append("circle").attr("cx", centroid.x).attr("cy", centroid.y).attr("r", overlayR).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", declaredOv ? String(declaredOv.lineNumber) : "").style("cursor", onClickItem && declaredOv ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
1655
- showRegionOverlay(idxs);
1656
- }).on("mouseleave", () => {
1657
- hideAllOverlays();
1658
- }).on("click", function() {
1659
- this.blur?.();
1660
- if (onClickItem && declaredOv) onClickItem(declaredOv.lineNumber);
1661
- });
1662
- }
1663
- }
1664
-
1665
- // src/quadrant/renderer.ts
1666
- import * as d3Scale3 from "d3-scale";
1667
- import * as d3Selection3 from "d3-selection";
1668
- function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
1669
- const {
1670
- quadrantLabels,
1671
- quadrantPoints,
1672
- quadrantXAxis,
1673
- quadrantYAxis,
1674
- quadrantTitleLineNumber,
1675
- quadrantXAxisLineNumber,
1676
- quadrantYAxisLineNumber
1677
- } = parsed;
1678
- const title = parsed.noTitle ? null : parsed.title;
1679
- if (quadrantPoints.length === 0) return;
1680
- const init = initD3Chart(container, palette, exportDims);
1681
- if (!init) return;
1682
- const { svg, width, height, textColor } = init;
1683
- const borderColor = palette.border;
1684
- const idealWidth = 600;
1685
- const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
1686
- svg.attr("preserveAspectRatio", "xMidYMid meet");
1687
- if (ctx.isBelowFloor) {
1688
- svg.attr("width", "100%");
1653
+ if (n === 3) return { x: 0, y: -1 };
1654
+ return { x: 0, y: 1 };
1689
1655
  }
1690
- const defaultColors = [
1691
- palette.colors.blue,
1692
- palette.colors.green,
1693
- palette.colors.yellow,
1694
- palette.colors.purple
1695
- ];
1696
- const hasXAxis = !!quadrantXAxis;
1697
- const hasYAxis = !!quadrantYAxis;
1698
- const margin = {
1699
- top: title ? ctx.aesthetic(60) : ctx.aesthetic(30),
1700
- right: ctx.aesthetic(30),
1701
- bottom: hasXAxis ? ctx.aesthetic(70) : ctx.aesthetic(40),
1702
- left: hasYAxis ? ctx.aesthetic(80) : ctx.aesthetic(40)
1703
- };
1704
- const chartWidth = width - margin.left - margin.right;
1705
- const chartHeight = height - margin.top - margin.bottom;
1706
- const xScale = d3Scale3.scaleLinear().domain([0, 1]).range([0, chartWidth]);
1707
- const yScale = d3Scale3.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
1708
- renderChartTitle(
1709
- svg,
1710
- title,
1711
- quadrantTitleLineNumber,
1712
- width,
1713
- textColor,
1714
- onClickItem
1715
- );
1716
- const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
1717
- const bg = themeBaseBg(palette, isDark);
1718
- const getQuadrantColor = (label, defaultIdx) => {
1719
- return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
1720
- };
1721
- const getQuadrantFill = (label, defaultIdx) => {
1722
- return mix(getQuadrantColor(label, defaultIdx), bg, 30);
1723
- };
1724
- const quadrantDefs = [
1725
- {
1726
- position: "top-left",
1727
- x: 0,
1728
- y: 0,
1729
- w: chartWidth / 2,
1730
- h: chartHeight / 2,
1731
- labelX: chartWidth / 4,
1732
- labelY: chartHeight / 4,
1733
- label: quadrantLabels.topLeft,
1734
- colorIdx: 1
1735
- // green
1736
- },
1737
- {
1738
- position: "top-right",
1739
- x: chartWidth / 2,
1740
- y: 0,
1741
- w: chartWidth / 2,
1742
- h: chartHeight / 2,
1743
- labelX: chartWidth * 3 / 4,
1744
- labelY: chartHeight / 4,
1745
- label: quadrantLabels.topRight,
1746
- colorIdx: 0
1747
- // blue
1748
- },
1749
- {
1750
- position: "bottom-left",
1751
- x: 0,
1752
- y: chartHeight / 2,
1753
- w: chartWidth / 2,
1754
- h: chartHeight / 2,
1755
- labelX: chartWidth / 4,
1756
- labelY: chartHeight * 3 / 4,
1757
- label: quadrantLabels.bottomLeft,
1758
- colorIdx: 2
1759
- // yellow
1760
- },
1761
- {
1762
- position: "bottom-right",
1763
- x: chartWidth / 2,
1764
- y: chartHeight / 2,
1765
- w: chartWidth / 2,
1766
- h: chartHeight / 2,
1767
- labelX: chartWidth * 3 / 4,
1768
- labelY: chartHeight * 3 / 4,
1769
- label: quadrantLabels.bottomRight,
1770
- colorIdx: 3
1771
- // purple
1656
+ function lensExit(c0, dir, idxs) {
1657
+ let minT = Infinity;
1658
+ for (const i of idxs) {
1659
+ const c = circles[i];
1660
+ const dx = c0.x - c.x;
1661
+ const dy = c0.y - c.y;
1662
+ const B = dx * dir.x + dy * dir.y;
1663
+ const C = dx * dx + dy * dy - c.r * c.r;
1664
+ const disc = B * B - C;
1665
+ if (disc < 0) continue;
1666
+ const t = -B + Math.sqrt(disc);
1667
+ if (t > 0 && t < minT) minT = t;
1772
1668
  }
1773
- ];
1774
- const quadrantRects = chartG.selectAll("rect.quadrant").data(quadrantDefs).enter().append("rect").attr("class", "quadrant").attr("x", (d) => d.x).attr("y", (d) => d.y).attr("width", (d) => d.w).attr("height", (d) => d.h).attr("fill", (d) => getQuadrantFill(d.label, d.colorIdx)).attr("stroke", (d) => getQuadrantColor(d.label, d.colorIdx)).attr("stroke-width", ctx.structural(2));
1775
- const shadowColor = isDark ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.4)";
1776
- const quadrantLabelColor = mix(textColor, bg, 35);
1777
- const LABEL_MAX_FONT = ctx.text(48);
1778
- const LABEL_MIN_FONT = ctx.text(14);
1779
- const LABEL_PAD = ctx.aesthetic(40);
1780
- const quadrantLabelLayout = (text, qw2, qh2) => {
1781
- const availW = qw2 - LABEL_PAD;
1782
- const availH = qh2 - LABEL_PAD;
1783
- if (measureText(text, LABEL_MAX_FONT, { bold: true }) <= availW) {
1784
- const fs = Math.min(LABEL_MAX_FONT, availH);
1785
- return {
1786
- lines: [text],
1787
- fontSize: Math.max(LABEL_MIN_FONT, Math.round(fs))
1788
- };
1669
+ if (!isFinite(minT)) return { x: c0.x, y: c0.y };
1670
+ return { x: c0.x + dir.x * minT, y: c0.y + dir.y * minT };
1671
+ }
1672
+ function unionExit(c0, dir, idxs) {
1673
+ let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
1674
+ for (const c of circles) {
1675
+ minX = Math.min(minX, c.x - c.r);
1676
+ maxX = Math.max(maxX, c.x + c.r);
1677
+ minY = Math.min(minY, c.y - c.r);
1678
+ maxY = Math.max(maxY, c.y + c.r);
1789
1679
  }
1790
- const wrapLines = (fs) => wrapTextToWidth(text, fs, availW, { bold: true });
1791
- let lo = LABEL_MIN_FONT;
1792
- let hi = LABEL_MAX_FONT;
1793
- let bestLines = wrapLines(lo);
1794
- let bestFs = lo;
1795
- while (lo <= hi) {
1796
- const mid = Math.round((lo + hi) / 2);
1797
- const lines = wrapLines(mid);
1798
- const totalH = lines.length * mid * 1.2;
1799
- const maxLineW = Math.max(
1800
- ...lines.map((l) => measureText(l, mid, { bold: true }))
1801
- );
1802
- if (maxLineW <= availW && totalH <= availH) {
1803
- bestFs = mid;
1804
- bestLines = lines;
1805
- lo = mid + 1;
1806
- } else {
1807
- hi = mid - 1;
1680
+ const STEP = 3;
1681
+ const MAX_ITERS = 400;
1682
+ const axisAligned = dir.x === 0 || dir.y === 0;
1683
+ let p = { x: c0.x, y: c0.y };
1684
+ let leftOverlap = false;
1685
+ for (let i = 0; i < MAX_ITERS; i++) {
1686
+ const next = { x: p.x + dir.x * STEP, y: p.y + dir.y * STEP };
1687
+ p = next;
1688
+ if (!leftOverlap) {
1689
+ leftOverlap = !idxs.every((ci) => pointInCircle(next, circles[ci]));
1690
+ if (!leftOverlap) continue;
1691
+ }
1692
+ const insideAny = circles.some((c) => pointInCircle(next, c));
1693
+ if (insideAny) continue;
1694
+ if (axisAligned) {
1695
+ const passedX = dir.x > 0 ? next.x >= maxX : next.x <= minX;
1696
+ const passedY = dir.y > 0 ? next.y >= maxY : next.y <= minY;
1697
+ if (dir.x !== 0 && !passedX) continue;
1698
+ if (dir.y !== 0 && !passedY) continue;
1808
1699
  }
1700
+ break;
1809
1701
  }
1810
- return { lines: bestLines, fontSize: Math.max(LABEL_MIN_FONT, bestFs) };
1811
- };
1812
- const qw = chartWidth / 2;
1813
- const qh = chartHeight / 2;
1814
- const quadrantDefsWithLabel = quadrantDefs.filter((d) => d.label !== null);
1815
- const labelLayouts = new Map(
1816
- quadrantDefsWithLabel.map((d) => [
1817
- d.label.text,
1818
- quadrantLabelLayout(d.label.text, qw, qh)
1819
- ])
1820
- );
1821
- const renderWatermarkOriginal = (textEl, d) => {
1822
- const layout = labelLayouts.get(d.label.text);
1823
- const el = d3Selection3.select(textEl);
1824
- el.text(null).attr("font-size", layout.fontSize).attr("font-weight", "bold");
1825
- if (layout.lines.length === 1) {
1826
- el.text(layout.lines[0]);
1702
+ return p;
1703
+ }
1704
+ for (const ov of vennOverlaps) {
1705
+ if (!ov.label) continue;
1706
+ const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
1707
+ if (idxs.some((idx) => idx < 0)) continue;
1708
+ const lines = wrappedOverlapLabels.get(ov) ?? [ov.label];
1709
+ const inside = circles.map((_, j) => idxs.includes(j));
1710
+ const centroid = regionCentroid(circles, inside);
1711
+ const dir = overlapOutwardDir(centroid, idxs);
1712
+ const isTriple = idxs.length === 3 && n === 3;
1713
+ const padBoost = isTriple && dir.y < 0 ? 32 : 0;
1714
+ const leaderPad = OVERLAP_LEADER_PAD + padBoost;
1715
+ const lensPt = lensExit(centroid, dir, idxs);
1716
+ const farExit = unionExit(centroid, dir, idxs);
1717
+ const stubEndX = farExit.x + dir.x * leaderPad;
1718
+ const stubEndY = farExit.y + dir.y * leaderPad;
1719
+ const horizontal = Math.abs(dir.x) >= Math.abs(dir.y);
1720
+ let textAnchor;
1721
+ let baseline = "central";
1722
+ if (horizontal) {
1723
+ textAnchor = dir.x >= 0 ? "start" : "end";
1724
+ } else {
1725
+ textAnchor = "middle";
1726
+ baseline = dir.y >= 0 ? "hanging" : "auto";
1727
+ }
1728
+ let textX, textY;
1729
+ if (horizontal) {
1730
+ const sign = dir.x >= 0 ? 1 : -1;
1731
+ textX = stubEndX + sign * OVERLAP_TEXT_GAP;
1732
+ textY = stubEndY;
1827
1733
  } else {
1828
- const lineH = layout.fontSize * 1.2;
1829
- const totalH = layout.lines.length * lineH;
1830
- const startY = -totalH / 2 + lineH / 2;
1831
- layout.lines.forEach((line2, i) => {
1832
- el.append("tspan").attr("x", d.labelX).attr("dy", i === 0 ? `${startY}px` : `${lineH}px`).text(line2);
1833
- });
1734
+ const sign = dir.y >= 0 ? 1 : -1;
1735
+ textX = stubEndX;
1736
+ textY = stubEndY + sign * OVERLAP_TEXT_GAP;
1834
1737
  }
1835
- };
1836
- const quadrantLabelTexts = chartG.selectAll("text.quadrant-label").data(quadrantDefsWithLabel).enter().append("text").attr("class", "quadrant-label").attr("x", (d) => d.labelX).attr("y", (d) => d.labelY).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", quadrantLabelColor).attr(
1837
- "data-line-number",
1838
- (d) => d.label?.lineNumber ? String(d.label.lineNumber) : null
1839
- ).style(
1840
- "cursor",
1841
- (d) => onClickItem && d.label?.lineNumber ? "pointer" : "default"
1842
- ).each(function(d) {
1843
- renderWatermarkOriginal(this, d);
1844
- });
1845
- if (onClickItem) {
1846
- quadrantLabelTexts.on("click", (_, d) => {
1847
- if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
1848
- }).on("mouseenter", function() {
1849
- d3Selection3.select(this).attr("opacity", 0.7);
1850
- }).on("mouseleave", function() {
1851
- d3Selection3.select(this).attr("opacity", 1);
1852
- });
1853
- }
1854
- const sAxisFont = ctx.text(18);
1855
- const sAxisPad = ctx.aesthetic(20);
1856
- const sAxisX = ctx.aesthetic(22);
1857
- if (quadrantXAxis) {
1858
- const xLowLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", margin.left + chartWidth / 4).attr("y", height - sAxisPad).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr(
1859
- "data-line-number",
1860
- quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null
1861
- ).style(
1862
- "cursor",
1863
- onClickItem && quadrantXAxisLineNumber ? "pointer" : "default"
1864
- ).text(quadrantXAxis[0]);
1865
- const xHighLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", margin.left + chartWidth * 3 / 4).attr("y", height - sAxisPad).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr(
1866
- "data-line-number",
1867
- quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null
1868
- ).style(
1869
- "cursor",
1870
- onClickItem && quadrantXAxisLineNumber ? "pointer" : "default"
1871
- ).text(quadrantXAxis[1]);
1872
- if (onClickItem && quadrantXAxisLineNumber) {
1873
- [xLowLabel, xHighLabel].forEach((label) => {
1874
- label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
1875
- d3Selection3.select(this).attr("opacity", 0.7);
1876
- }).on("mouseleave", function() {
1877
- d3Selection3.select(this).attr("opacity", 1);
1878
- });
1879
- });
1738
+ const blockW = lines.reduce(
1739
+ (m, l) => Math.max(m, measureText(l, OVERLAP_FONT, { bold: true })),
1740
+ 0
1741
+ );
1742
+ const blockH = lines.length * OVERLAP_LINE_H;
1743
+ if (textAnchor === "start") textX = Math.min(textX, width - blockW - 4);
1744
+ else if (textAnchor === "end") textX = Math.max(textX, blockW + 4);
1745
+ else
1746
+ textX = Math.max(blockW / 2 + 4, Math.min(width - blockW / 2 - 4, textX));
1747
+ let topY, bottomY;
1748
+ if (baseline === "hanging") {
1749
+ topY = textY;
1750
+ bottomY = textY + blockH;
1751
+ } else if (baseline === "auto") {
1752
+ bottomY = textY;
1753
+ topY = textY - blockH;
1754
+ } else {
1755
+ topY = textY - blockH / 2;
1756
+ bottomY = textY + blockH / 2;
1880
1757
  }
1881
- }
1882
- if (quadrantYAxis) {
1883
- const yMidBottom = margin.top + chartHeight * 3 / 4;
1884
- const yMidTop = margin.top + chartHeight / 4;
1885
- const yLowLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", sAxisX).attr("y", yMidBottom).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr("transform", `rotate(-90, ${sAxisX}, ${yMidBottom})`).attr(
1886
- "data-line-number",
1887
- quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null
1888
- ).style(
1889
- "cursor",
1890
- onClickItem && quadrantYAxisLineNumber ? "pointer" : "default"
1891
- ).text(quadrantYAxis[0]);
1892
- const yHighLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", sAxisX).attr("y", yMidTop).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", sAxisFont).attr("transform", `rotate(-90, ${sAxisX}, ${yMidTop})`).attr(
1893
- "data-line-number",
1894
- quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null
1895
- ).style(
1896
- "cursor",
1897
- onClickItem && quadrantYAxisLineNumber ? "pointer" : "default"
1898
- ).text(quadrantYAxis[1]);
1899
- if (onClickItem && quadrantYAxisLineNumber) {
1900
- [yLowLabel, yHighLabel].forEach((label) => {
1901
- label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
1902
- d3Selection3.select(this).attr("opacity", 0.7);
1903
- }).on("mouseleave", function() {
1904
- d3Selection3.select(this).attr("opacity", 1);
1905
- });
1906
- });
1758
+ if (topY < sTitleHeight + 6) textY += sTitleHeight + 6 - topY;
1759
+ else if (bottomY > height - 4) textY -= bottomY - (height - 4);
1760
+ const startY = baseline === "hanging" ? textY : baseline === "auto" ? textY - (lines.length - 1) * OVERLAP_LINE_H : textY - (lines.length - 1) * OVERLAP_LINE_H / 2;
1761
+ const leaderStartX = isTriple ? centroid.x : lensPt.x;
1762
+ const leaderStartY = isTriple ? centroid.y : lensPt.y;
1763
+ let tinted = setColors[idxs[0]];
1764
+ for (let k = 1; k < idxs.length; k++) {
1765
+ const pct = k / (k + 1) * 100;
1766
+ tinted = mix(tinted, setColors[idxs[k]], pct);
1907
1767
  }
1908
- }
1909
- chartG.append("line").attr("x1", chartWidth / 2).attr("y1", 0).attr("x2", chartWidth / 2).attr("y2", chartHeight).attr("stroke", borderColor).attr("stroke-width", 1);
1910
- chartG.append("line").attr("x1", 0).attr("y1", chartHeight / 2).attr("x2", chartWidth).attr("y2", chartHeight / 2).attr("stroke", borderColor).attr("stroke-width", 1);
1911
- const getPointQuadrant = (x, y) => {
1912
- if (x >= 0.5 && y >= 0.5) return "top-right";
1913
- if (x < 0.5 && y >= 0.5) return "top-left";
1914
- if (x < 0.5 && y < 0.5) return "bottom-left";
1915
- return "bottom-right";
1916
- };
1917
- const POINT_RADIUS = ctx.structural(6);
1918
- const POINT_LABEL_FONT_SIZE = ctx.text(12);
1919
- const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
1920
- const layout = labelLayouts.get(d.label.text);
1921
- const totalW = Math.max(
1922
- ...layout.lines.map((l) => measureText(l, layout.fontSize))
1768
+ const overlapColor = mix(tinted, textColor, 90);
1769
+ const ovKey = [...idxs].sort((a, b) => a - b).join("-");
1770
+ const ovLabelG = labelGroup.append("g").style("cursor", "default").on("mouseenter", () => showRegionOverlay(idxs)).on("mouseleave", () => hideAllOverlays());
1771
+ registerLabel(ovKey, ovLabelG);
1772
+ const labelRects = setLabelBBoxes.filter((b) => b !== null);
1773
+ const segments = clipLineByRects(
1774
+ leaderStartX,
1775
+ leaderStartY,
1776
+ stubEndX,
1777
+ stubEndY,
1778
+ labelRects,
1779
+ 4
1923
1780
  );
1924
- const totalH = layout.lines.length * layout.fontSize * 1.2;
1925
- return {
1926
- x: d.labelX - totalW / 2,
1927
- y: d.labelY - totalH / 2,
1928
- w: totalW,
1929
- h: totalH
1930
- };
1931
- });
1932
- const pointPixels = quadrantPoints.map((point) => ({
1933
- label: point.label,
1934
- cx: xScale(point.x),
1935
- cy: yScale(point.y)
1936
- }));
1937
- const placedPointLabels = computeQuadrantPointLabels(
1938
- pointPixels,
1939
- { left: 0, top: 0, right: chartWidth, bottom: chartHeight },
1940
- quadrantLabelObstacles,
1941
- POINT_RADIUS,
1942
- POINT_LABEL_FONT_SIZE,
1943
- // Point labels are drawn at 700 here.
1944
- true
1945
- );
1946
- const pointsG = chartG.append("g").attr("class", "points");
1947
- quadrantPoints.forEach((point, i) => {
1948
- const cx = xScale(point.x);
1949
- const cy = yScale(point.y);
1950
- const quadrant = getPointQuadrant(point.x, point.y);
1951
- const quadDef = quadrantDefs.find((d) => d.position === quadrant);
1952
- const pointColor = quadDef?.label?.color ?? defaultColors[quadDef?.colorIdx ?? 0];
1953
- const placed = placedPointLabels[i];
1954
- const pointG = pointsG.append("g").attr("class", "point-group").attr("data-line-number", String(point.lineNumber)).attr("data-quadrant", quadrant);
1955
- if (placed.connectorLine) {
1956
- pointG.append("line").attr("x1", placed.connectorLine.x1).attr("y1", placed.connectorLine.y1).attr("x2", placed.connectorLine.x2).attr("y2", placed.connectorLine.y2).attr("stroke", pointColor).attr("stroke-width", 1).attr("opacity", 0.5);
1781
+ for (const seg of segments) {
1782
+ ovLabelG.append("line").attr("x1", seg.x1).attr("y1", seg.y1).attr("x2", seg.x2).attr("y2", seg.y2).attr("stroke", overlapColor).attr("stroke-width", ctx.structural(1.25)).attr("opacity", 0.85);
1957
1783
  }
1958
- pointG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", POINT_RADIUS).attr("fill", bg).attr("stroke", pointColor).attr("stroke-width", ctx.structural(2));
1959
- const labelText = pointG.append("text").attr("x", placed.x).attr("y", placed.y).attr("text-anchor", placed.anchor).attr("dominant-baseline", "central").attr("fill", textColor).attr("font-size", POINT_LABEL_FONT_SIZE).attr("font-weight", "bold").style("text-shadow", `0 1px 2px ${shadowColor}`).style("transition", "y 120ms ease-out");
1960
- labelText.append("tspan").text(point.label);
1961
- const coordsTspan = labelText.append("tspan").attr("class", "point-coords").attr("x", placed.x).attr("dy", `${POINT_LABEL_FONT_SIZE}px`).attr("font-size", ctx.text(10)).attr("font-weight", "normal").attr("opacity", 0).text(`${point.x.toFixed(2)}, ${point.y.toFixed(2)}`);
1962
- const COORDS_LINE_H = ctx.structural(14);
1963
- const bumpDy = placed.y < cy ? -COORDS_LINE_H : COORDS_LINE_H;
1964
- pointG.style("cursor", onClickItem ? "pointer" : "default").on("mouseenter", () => {
1965
- pointG.select("circle").attr("r", ctx.structural(8));
1966
- labelText.attr("y", placed.y + bumpDy);
1967
- coordsTspan.attr("opacity", 1);
1968
- quadrantRects.attr(
1969
- "opacity",
1970
- (qd) => qd.position === quadrant ? 1 : 0.3
1971
- );
1972
- quadrantLabelTexts.attr(
1973
- "opacity",
1974
- (qd) => qd.position === quadrant ? 1 : 0.3
1975
- );
1976
- pointsG.selectAll("g.point-group").filter((_, j) => j !== i).attr("opacity", 0.3);
1784
+ const textEl = ovLabelG.append("text").attr("text-anchor", textAnchor).attr("dominant-baseline", baseline).attr("fill", overlapColor).attr("font-size", OVERLAP_FONT).attr("font-weight", "bold");
1785
+ lines.forEach((line2, i) => {
1786
+ const tspan = textEl.append("tspan").attr("x", textX);
1787
+ if (i === 0) tspan.attr("y", startY);
1788
+ else tspan.attr("dy", OVERLAP_LINE_H);
1789
+ tspan.text(line2);
1790
+ });
1791
+ }
1792
+ const hoverGroup = svg.append("g");
1793
+ circles.forEach((c, i) => {
1794
+ hoverGroup.append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", c.r).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", String(vennSets[i].lineNumber)).style("cursor", onClickItem ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
1795
+ showRegionOverlay([i]);
1977
1796
  }).on("mouseleave", () => {
1978
- pointG.select("circle").attr("r", POINT_RADIUS);
1979
- labelText.attr("y", placed.y);
1980
- coordsTspan.attr("opacity", 0);
1981
- quadrantRects.attr("opacity", 1);
1982
- quadrantLabelTexts.attr("opacity", 1);
1983
- pointsG.selectAll("g.point-group").attr("opacity", 1);
1984
- }).on("click", () => {
1985
- if (onClickItem && point.lineNumber) onClickItem(point.lineNumber);
1797
+ hideAllOverlays();
1798
+ }).on("click", function() {
1799
+ this.blur?.();
1800
+ if (onClickItem && vennSets[i].lineNumber)
1801
+ onClickItem(vennSets[i].lineNumber);
1986
1802
  });
1987
1803
  });
1988
- quadrantRects.style("cursor", onClickItem ? "pointer" : "default").on("mouseenter", function(_, d) {
1989
- quadrantRects.attr(
1990
- "opacity",
1991
- (qd) => qd.position === d.position ? 1 : 0.3
1992
- );
1993
- quadrantLabelTexts.attr(
1994
- "opacity",
1995
- (qd) => qd.position === d.position ? 1 : 0.3
1996
- );
1997
- pointsG.selectAll("g.point-group").each(function(_2, i) {
1998
- const pt = quadrantPoints[i];
1999
- const ptQuad = getPointQuadrant(pt.x, pt.y);
2000
- d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
1804
+ const overlayR = scaledR * 0.35;
1805
+ const subsets = [];
1806
+ if (n === 2) {
1807
+ subsets.push({
1808
+ idxs: [0, 1],
1809
+ sets: [vennSets[0].name, vennSets[1].name].sort()
2001
1810
  });
2002
- }).on("mouseleave", () => {
2003
- quadrantRects.attr("opacity", 1);
2004
- quadrantLabelTexts.attr("opacity", 1);
2005
- pointsG.selectAll("g.point-group").attr("opacity", 1);
2006
- }).on("click", (_, d) => {
2007
- if (onClickItem && d.label?.lineNumber) {
2008
- onClickItem(d.label.lineNumber);
1811
+ } else {
1812
+ for (let a = 0; a < n; a++) {
1813
+ for (let b = a + 1; b < n; b++) {
1814
+ subsets.push({
1815
+ idxs: [a, b],
1816
+ sets: [vennSets[a].name, vennSets[b].name].sort()
1817
+ });
1818
+ }
2009
1819
  }
2010
- });
1820
+ subsets.push({
1821
+ idxs: [0, 1, 2],
1822
+ sets: [vennSets[0].name, vennSets[1].name, vennSets[2].name].sort()
1823
+ });
1824
+ }
1825
+ for (const subset of subsets) {
1826
+ const { idxs, sets } = subset;
1827
+ const inside = circles.map((_, j) => idxs.includes(j));
1828
+ const centroid = regionCentroid(circles, inside);
1829
+ const declaredOv = vennOverlaps.find(
1830
+ (ov) => ov.sets.length === sets.length && ov.sets.every((s, k) => s === sets[k])
1831
+ );
1832
+ hoverGroup.append("circle").attr("cx", centroid.x).attr("cy", centroid.y).attr("r", overlayR).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", declaredOv ? String(declaredOv.lineNumber) : "").style("cursor", onClickItem && declaredOv ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
1833
+ showRegionOverlay(idxs);
1834
+ }).on("mouseleave", () => {
1835
+ hideAllOverlays();
1836
+ }).on("click", function() {
1837
+ this.blur?.();
1838
+ if (onClickItem && declaredOv) onClickItem(declaredOv.lineNumber);
1839
+ });
1840
+ }
2011
1841
  }
2012
1842
 
2013
- // src/d3.ts
2014
- async function resolveMapDataSource(source) {
2015
- if (source === void 0) return {};
2016
- if (typeof source !== "function") return { data: source };
1843
+ // src/wordcloud/renderer.ts
1844
+ import * as d3Selection3 from "d3-selection";
1845
+ import cloud from "d3-cloud";
1846
+ function getRotateFn(mode) {
1847
+ if (mode === "mixed") return () => Math.random() > 0.5 ? 0 : 90;
1848
+ if (mode === "angled") return () => Math.round(Math.random() * 30 - 15);
1849
+ return () => 0;
1850
+ }
1851
+ function hasCanvas2d() {
2017
1852
  try {
2018
- return { data: await source() };
2019
- } catch (e) {
2020
- return { reason: e instanceof Error ? e.message : String(e) };
1853
+ if (typeof document === "undefined") return false;
1854
+ const canvas = document.createElement("canvas");
1855
+ return typeof canvas.getContext === "function" && !!canvas.getContext("2d");
1856
+ } catch {
1857
+ return false;
2021
1858
  }
2022
1859
  }
2023
- var DIAGRAM_EXPORT_HANDLERS = {
2024
- org: exportOrg,
2025
- sitemap: exportSitemap,
2026
- kanban: exportKanban,
2027
- class: exportClass,
2028
- er: exportEr,
2029
- "boxes-and-lines": exportBoxesAndLines,
2030
- sketch: exportSketch,
2031
- whiteboard: exportWhiteboard,
2032
- swimlane: exportSwimlane,
2033
- family: exportFamily,
2034
- "version-control": exportVersionControl,
2035
- mindmap: exportMindmap,
2036
- wireframe: exportWireframe,
2037
- c4: exportC4,
2038
- flowchart: exportFlowchart,
2039
- infra: exportInfra,
2040
- pert: exportPert,
2041
- gantt: exportGantt,
2042
- state: exportState,
2043
- "tech-radar": exportTechRadar,
2044
- "journey-map": exportJourneyMap,
2045
- cycle: exportCycle,
2046
- map: exportMap,
2047
- pyramid: exportPyramid,
2048
- ring: exportRing,
2049
- treemap: exportTreemap,
2050
- block: exportBlock,
2051
- goal: exportGoal,
2052
- countdown: exportCountdown,
2053
- clock: exportClock,
2054
- bracket: exportBracket,
2055
- raci: exportRaci,
2056
- body: exportBody,
2057
- "live-link": exportLiveLink,
2058
- // D3 visualizations — own handler per type (Story 109.2). Only `sequence`
2059
- // still falls through to exportVisualization (no chart-type of its own).
2060
- slope: exportSlope,
2061
- arc: exportArc,
2062
- timeline: exportTimeline,
2063
- "event-line": exportEventLine,
2064
- wordcloud: exportWordcloud,
2065
- venn: exportVenn,
2066
- quadrant: exportQuadrant
2067
- };
2068
- function resolveArcChordOverride(content, detectedType, palette) {
2069
- if (detectedType === "arc") {
2070
- const p = parseArc(content, palette);
2071
- if (!p.error && p.layout === "chord" && p.links.length) {
2072
- const emitted = [
2073
- `chord ${p.title ?? ""}`.trimEnd(),
2074
- // §1.9 fill family survives the layout re-emit so the circular
2075
- // renderer honors fill-solid / fill-outline authored on the arc.
2076
- ...p.fillMode ? [`fill-${p.fillMode}`] : [],
2077
- ...p.links.map((l) => `${l.source} -> ${l.target} ${l.value}`)
2078
- ].join("\n");
2079
- return { content: emitted, type: "chord" };
2080
- }
1860
+ function estimateWordWidth(text, size) {
1861
+ return measureText(text, size, { bold: true });
1862
+ }
1863
+ var WORD_ASCENT = 0.78;
1864
+ var WORD_DESCENT = 0.22;
1865
+ var CLOUD_SHRINK = 0.9;
1866
+ var CLOUD_SHRINK_TRIES = 6;
1867
+ function layoutWordsNoCanvas(words, width, height, padding, rotateFn) {
1868
+ let placed = [];
1869
+ for (let attempt = 0, scale = 1; attempt <= CLOUD_SHRINK_TRIES; attempt++) {
1870
+ placed = packWords(
1871
+ words.map((w) => ({ ...w, size: w.size * scale })),
1872
+ width,
1873
+ height,
1874
+ padding,
1875
+ rotateFn
1876
+ );
1877
+ if (placed.length === words.length) break;
1878
+ scale *= CLOUD_SHRINK;
2081
1879
  }
2082
- return null;
1880
+ return placed;
2083
1881
  }
2084
- async function renderForExport(content, theme, palette, viewState, options) {
2085
- const exportMode = options?.exportMode ?? false;
2086
- const { parseDgmoChartType } = await import("./dgmo-router-4UAPPIKN.js");
2087
- const detectedTypeRaw = parseDgmoChartType(content);
2088
- const override = resolveArcChordOverride(content, detectedTypeRaw, palette);
2089
- const renderContent = override?.content ?? content;
2090
- const detectedType = override?.type ?? detectedTypeRaw;
2091
- if (detectedType !== null) {
2092
- const { supportsD3DataChart, renderDataChartD3: renderDataChartD32 } = await import("./charts-d3-QUUDVETP.js");
2093
- if (supportsD3DataChart(detectedType)) {
2094
- return renderDataChartD32(renderContent, theme, palette, {
2095
- ...options?.width !== void 0 && { width: options.width },
2096
- ...options?.height !== void 0 && { height: options.height }
2097
- });
1882
+ function packWords(words, width, height, padding, rotateFn) {
1883
+ const sorted = [...words].sort((a, b) => b.size - a.size);
1884
+ const placed = [];
1885
+ const maxR = Math.sqrt(width * width + height * height) / 2;
1886
+ const aspect = width > 0 ? height / width : 1;
1887
+ for (const w of sorted) {
1888
+ const rotate = rotateFn();
1889
+ const rawW = estimateWordWidth(w.text, w.size) + padding * 2;
1890
+ const rawH = (WORD_ASCENT + WORD_DESCENT) * w.size + padding * 2;
1891
+ const rad = rotate * Math.PI / 180;
1892
+ const cos = Math.abs(Math.cos(rad));
1893
+ const sin = Math.abs(Math.sin(rad));
1894
+ const halfW = (rawW * cos + rawH * sin) / 2;
1895
+ const halfH = (rawW * sin + rawH * cos) / 2;
1896
+ const off = (WORD_DESCENT - WORD_ASCENT) / 2 * w.size;
1897
+ const offX = -off * Math.sin(rad);
1898
+ const offY = off * Math.cos(rad);
1899
+ let spot = null;
1900
+ for (let t = 0; t < 4e3; t++) {
1901
+ const a = t * 0.25;
1902
+ const r = a * 1.4;
1903
+ if (r > maxR) break;
1904
+ const x = Math.cos(a) * r;
1905
+ const y = Math.sin(a) * r * aspect;
1906
+ const cx = x + offX;
1907
+ const cy = y + offY;
1908
+ if (cx - halfW < -width / 2 || cx + halfW > width / 2 || cy - halfH < -height / 2 || cy + halfH > height / 2) {
1909
+ continue;
1910
+ }
1911
+ let collides = false;
1912
+ for (const p of placed) {
1913
+ if (Math.abs(cx - p.cx) < halfW + p.halfW && Math.abs(cy - p.cy) < halfH + p.halfH) {
1914
+ collides = true;
1915
+ break;
1916
+ }
1917
+ }
1918
+ if (!collides) {
1919
+ spot = { x, y };
1920
+ break;
1921
+ }
2098
1922
  }
1923
+ if (!spot) continue;
1924
+ placed.push({
1925
+ ...w,
1926
+ rotate,
1927
+ x: spot.x,
1928
+ y: spot.y,
1929
+ cx: spot.x + offX,
1930
+ cy: spot.y + offY,
1931
+ halfW,
1932
+ halfH
1933
+ });
2099
1934
  }
2100
- const ctx = {
2101
- content: renderContent,
2102
- theme,
2103
- palette,
2104
- viewState,
2105
- options,
2106
- exportMode,
2107
- isDark: theme === "dark",
2108
- // The resolved canvas, and the raw request beside it. A chart whose own
2109
- // natural size is not EXPORT_WIDTH (tech-radar) needs to tell "the caller
2110
- // asked for 1200" apart from "nobody asked", which the resolved value
2111
- // cannot express.
2112
- requestedWidth: options?.width && options.width > 0 ? Math.round(options.width) : void 0,
2113
- requestedHeight: options?.height && options.height > 0 ? Math.round(options.height) : void 0,
2114
- width: options?.width && options.width > 0 ? Math.round(options.width) : EXPORT_WIDTH,
2115
- height: options?.height && options.height > 0 ? Math.round(options.height) : EXPORT_HEIGHT
1935
+ return placed;
1936
+ }
1937
+ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
1938
+ const { words, cloudOptions } = parsed;
1939
+ const title = parsed.noTitle ? null : parsed.title;
1940
+ if (words.length === 0) return;
1941
+ const init = initD3Chart(container, palette, exportDims);
1942
+ if (!init) return;
1943
+ const { svg, width, height, textColor, colors } = init;
1944
+ const idealWidth = Math.max(400, words.length * 30);
1945
+ const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
1946
+ const sTitleHeight = title ? ctx.aesthetic(40) : 0;
1947
+ const cloudHeight = height - sTitleHeight;
1948
+ const sPadding = ctx.structural(2);
1949
+ svg.attr("preserveAspectRatio", "xMidYMid meet");
1950
+ if (ctx.isBelowFloor) {
1951
+ svg.attr("width", "100%");
1952
+ }
1953
+ const { minSize, maxSize } = cloudOptions;
1954
+ const sMinSize = ctx.text(minSize);
1955
+ const sMaxSize = ctx.text(maxSize);
1956
+ const weights = words.map((w) => w.weight);
1957
+ const minWeight = Math.min(...weights);
1958
+ const maxWeight = Math.max(...weights);
1959
+ const range = maxWeight - minWeight || 1;
1960
+ const fontSize = (weight) => {
1961
+ const t = (weight - minWeight) / range;
1962
+ return sMinSize + Math.sqrt(t) * (sMaxSize - sMinSize);
2116
1963
  };
2117
- const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
2118
- return (handler ?? exportVisualization)(ctx);
1964
+ const rotateFn = getRotateFn(cloudOptions.rotate);
1965
+ renderChartTitle(
1966
+ svg,
1967
+ title,
1968
+ parsed.titleLineNumber,
1969
+ width,
1970
+ textColor,
1971
+ onClickItem
1972
+ );
1973
+ const g = svg.append("g").attr(
1974
+ "transform",
1975
+ `translate(${width / 2},${sTitleHeight + cloudHeight / 2})`
1976
+ );
1977
+ const sized = words.map((w) => ({ ...w, size: fontSize(w.weight) }));
1978
+ const draw = (layoutWords) => {
1979
+ g.selectAll("text").data(layoutWords).join("text").style("font-size", (d) => `${d.size}px`).style("font-family", FONT_FAMILY).style("font-weight", "bold").style("fill", (_d, i) => colors[i % colors.length]).style(
1980
+ "cursor",
1981
+ (d) => onClickItem && d.lineNumber ? "pointer" : "default"
1982
+ ).attr("text-anchor", "middle").attr("transform", (d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`).attr("data-line-number", (d) => {
1983
+ const ln = d.lineNumber;
1984
+ return ln ? String(ln) : null;
1985
+ }).text((d) => d.text).on("click", (_event, d) => {
1986
+ const ln = d.lineNumber;
1987
+ if (onClickItem && ln) onClickItem(ln);
1988
+ });
1989
+ };
1990
+ if (!hasCanvas2d()) {
1991
+ draw(layoutWordsNoCanvas(sized, width, cloudHeight, sPadding, rotateFn));
1992
+ return;
1993
+ }
1994
+ cloud().size([width, cloudHeight]).words(sized).padding(sPadding).rotate(rotateFn).fontSize((d) => d.size).font(FONT_FAMILY).on("end", draw).start();
1995
+ }
1996
+ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
1997
+ return new Promise((resolve) => {
1998
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
1999
+ const { words, cloudOptions } = parsed;
2000
+ const title = parsed.noTitle ? null : parsed.title;
2001
+ if (words.length === 0) {
2002
+ resolve();
2003
+ return;
2004
+ }
2005
+ const width = exportDims?.width ?? container.clientWidth;
2006
+ const height = exportDims?.height ?? container.clientHeight;
2007
+ if (width <= 0 || height <= 0) {
2008
+ resolve();
2009
+ return;
2010
+ }
2011
+ const titleHeight = title ? 40 : 0;
2012
+ const cloudHeight = height - titleHeight;
2013
+ const textColor = palette.text;
2014
+ const bgColor = palette.bg;
2015
+ const colors = getSeriesColors(palette);
2016
+ const { minSize, maxSize } = cloudOptions;
2017
+ const weights = words.map((w) => w.weight);
2018
+ const minWeight = Math.min(...weights);
2019
+ const maxWeight = Math.max(...weights);
2020
+ const range = maxWeight - minWeight || 1;
2021
+ const fontSize = (weight) => {
2022
+ const t = (weight - minWeight) / range;
2023
+ return minSize + Math.sqrt(t) * (maxSize - minSize);
2024
+ };
2025
+ const rotateFn = getRotateFn(cloudOptions.rotate);
2026
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMid meet").style("background", bgColor);
2027
+ renderChartTitle(svg, title, parsed.titleLineNumber, width, textColor);
2028
+ const g = svg.append("g").attr(
2029
+ "transform",
2030
+ `translate(${width / 2},${titleHeight + cloudHeight / 2})`
2031
+ );
2032
+ const sized = words.map((w) => ({ ...w, size: fontSize(w.weight) }));
2033
+ const draw = (layoutWords) => {
2034
+ g.selectAll("text").data(layoutWords).join("text").style("font-size", (d) => `${d.size}px`).style("font-family", FONT_FAMILY).style("font-weight", "bold").style("fill", (_d, i) => colors[i % colors.length]).attr("text-anchor", "middle").attr(
2035
+ "transform",
2036
+ (d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`
2037
+ ).text((d) => d.text);
2038
+ resolve();
2039
+ };
2040
+ if (!hasCanvas2d()) {
2041
+ draw(layoutWordsNoCanvas(sized, width, cloudHeight, 2, rotateFn));
2042
+ return;
2043
+ }
2044
+ cloud().size([width, cloudHeight]).words(sized).padding(2).rotate(rotateFn).fontSize((d) => d.size).font(FONT_FAMILY).on("end", draw).start();
2045
+ });
2119
2046
  }
2047
+
2048
+ // src/utils/export-context.ts
2120
2049
  function ctxTagOverride(ctx) {
2121
2050
  return ctx.viewState?.tag ?? ctx.options?.tagGroup;
2122
2051
  }
2123
- async function exportEventLine(ctx) {
2124
- const { content, theme, palette } = ctx;
2125
- const { parseEventLine: parseEventLine2 } = await import("./parser-YG625J5H.js");
2126
- const { renderEventLineForExport } = await import("./renderer-DVNMCYHZ.js");
2127
- const effectivePalette = await resolveExportPalette(theme, palette);
2128
- const parsed = parseEventLine2(content, effectivePalette);
2129
- if (parsed.error || parsed.events.length === 0) return "";
2130
- const container = createExportContainer(ctx.width, ctx.height);
2131
- renderEventLineForExport(
2132
- container,
2133
- parsed,
2134
- effectivePalette,
2135
- ctx.isDark,
2136
- { width: ctx.width, height: ctx.height },
2137
- ctxTagOverride(ctx),
2138
- /* @__PURE__ */ new Date()
2139
- );
2140
- return finalizeSvgExport(container, theme, effectivePalette);
2141
- }
2142
- async function exportBody(ctx) {
2143
- const { content, theme, palette } = ctx;
2144
- const { parseBody: parseBody2 } = await import("./parser-PEZ7RN43.js");
2145
- const { renderBodyForExport } = await import("./renderer-2FVUPBGV.js");
2146
- const effectivePalette = await resolveExportPalette(theme, palette);
2147
- const parsed = parseBody2(content, effectivePalette);
2148
- if (parsed.error) return "";
2149
- const container = createExportContainer(ctx.width, ctx.height);
2150
- renderBodyForExport(
2151
- container,
2152
- parsed,
2153
- effectivePalette,
2154
- ctx.isDark,
2155
- { width: ctx.width, height: ctx.height },
2156
- ctxTagOverride(ctx)
2157
- );
2158
- return finalizeSvgExport(container, theme, effectivePalette);
2159
- }
2160
- async function exportVersionControl(ctx) {
2161
- const { content, theme, palette } = ctx;
2162
- const { parseVersionControl: parseVersionControl2 } = await import("./parser-5UZV32ZI.js");
2163
- const { renderVersionControlForExport } = await import("./renderer-DC7C5Q3G.js");
2164
- const effectivePalette = await resolveExportPalette(theme, palette);
2165
- const parsed = parseVersionControl2(content, effectivePalette);
2166
- if (parsed.error || parsed.nodes.length === 0) return "";
2167
- const container = createExportContainer(ctx.width, ctx.height);
2168
- renderVersionControlForExport(
2169
- container,
2170
- parsed,
2171
- effectivePalette,
2172
- ctx.isDark,
2173
- {
2174
- width: ctx.width,
2175
- height: ctx.height
2176
- }
2177
- );
2178
- return finalizeSvgExport(container, theme, effectivePalette);
2179
- }
2180
2052
  function unionHiddenAttributes(hideOption, ha) {
2181
2053
  const sourceHidden = hideOption ? hideOption.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean) : [];
2182
2054
  const union = /* @__PURE__ */ new Set([...sourceHidden, ...ha ?? []]);
2183
2055
  return union.size > 0 ? union : void 0;
2184
2056
  }
2185
- async function exportOrg(ctx) {
2186
- const { content, theme, palette, viewState, exportMode } = ctx;
2187
- const { parseOrg: parseOrg2, findOrgNodeIdByName } = await import("./parser-BJ7RJNUM.js");
2188
- const { layoutOrg } = await import("./layout-MXHR3P35.js");
2189
- const { collapseOrgTree, focusOrgTree, unionCollapsedOrgNodes } = await import("./collapse-VNYYXMUI.js");
2190
- const { renderOrg, ancestorTrailReserve } = await import("./renderer-ALWVK3EJ.js");
2191
- const isDark = ctx.isDark;
2192
- const effectivePalette = await resolveExportPalette(theme, palette);
2193
- const orgParsed = parseOrg2(content, effectivePalette);
2194
- if (orgParsed.error) return "";
2195
- const collapsedNodes = unionCollapsedOrgNodes(orgParsed, viewState?.cg);
2196
- const activeTagGroup = resolveActiveTagGroup(
2197
- orgParsed.tagGroups,
2198
- orgParsed.options["active-tag"],
2199
- ctxTagOverride(ctx)
2200
- );
2201
- const hiddenAttributes = unionHiddenAttributes(
2202
- orgParsed.options["hide"],
2203
- viewState?.ha
2204
- );
2205
- const collapseResult = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
2206
- const { hiddenCounts } = collapseResult;
2207
- let effectiveParsed = collapseResult.parsed;
2208
- let ancestorPath;
2209
- const focusName = orgParsed.options["focus"];
2210
- if (focusName) {
2211
- const focusId = findOrgNodeIdByName(effectiveParsed.roots, focusName);
2212
- const focused = focusId ? focusOrgTree(effectiveParsed, focusId) : null;
2213
- if (focused) {
2214
- effectiveParsed = focused.parsed;
2215
- ancestorPath = focused.ancestorPath;
2216
- }
2217
- }
2218
- const orgLayout = layoutOrg(
2219
- effectiveParsed,
2220
- hiddenCounts.size > 0 ? hiddenCounts : void 0,
2221
- activeTagGroup,
2222
- hiddenAttributes,
2223
- false
2224
- // expandAllLegend off — collapsed-by-default per §1.3
2225
- );
2226
- const PADDING = 20;
2227
- const titleOffset = effectiveParsed.title ? 30 : 0;
2228
- const rootIds = new Set(effectiveParsed.roots.map((r) => r.id));
2229
- const rootTop = orgLayout.nodes.find((n) => rootIds.has(n.id))?.y ?? orgLayout.containers.find((c) => rootIds.has(c.nodeId))?.y ?? 0;
2230
- const trailReserve = ancestorTrailReserve(ancestorPath?.length ?? 0, rootTop);
2231
- const exportWidth = orgLayout.width + PADDING * 2;
2232
- const exportHeight = orgLayout.height + PADDING * 2 + titleOffset + trailReserve;
2233
- const container = createExportContainer(exportWidth, exportHeight);
2234
- renderOrg(
2235
- container,
2236
- effectiveParsed,
2237
- orgLayout,
2238
- effectivePalette,
2239
- isDark,
2240
- void 0,
2241
- { width: exportWidth, height: exportHeight },
2242
- activeTagGroup,
2243
- hiddenAttributes,
2244
- ancestorPath,
2245
- exportMode
2246
- );
2247
- return finalizeSvgExport(container, theme, effectivePalette);
2248
- }
2249
- async function exportSitemap(ctx) {
2250
- const { content, theme, palette, viewState, exportMode } = ctx;
2251
- const { parseSitemap: parseSitemap2 } = await import("./parser-6TDWUKM5.js");
2252
- const { layoutSitemap } = await import("./layout-ND26WVX2.js");
2253
- const { collapseSitemapTree } = await import("./collapse-TCVK3RPO.js");
2254
- const { renderSitemap } = await import("./renderer-J666LZXJ.js");
2255
- const isDark = ctx.isDark;
2256
- const effectivePalette = await resolveExportPalette(theme, palette);
2257
- const sitemapParsed = parseSitemap2(content, effectivePalette);
2258
- if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
2259
- const collapsedNodes = viewState?.cg ? new Set(viewState.cg) : void 0;
2260
- const activeTagGroup = resolveActiveTagGroup(
2261
- sitemapParsed.tagGroups,
2262
- sitemapParsed.options["active-tag"],
2263
- ctxTagOverride(ctx)
2264
- );
2265
- const hiddenAttributes = unionHiddenAttributes(
2266
- sitemapParsed.options["hide"],
2267
- viewState?.ha
2268
- );
2269
- const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
2270
- const sitemapLayout = layoutSitemap(
2271
- effectiveParsed,
2272
- hiddenCounts.size > 0 ? hiddenCounts : void 0,
2273
- activeTagGroup,
2274
- hiddenAttributes,
2275
- true
2276
- );
2277
- const PADDING = 20;
2278
- const titleOffset = effectiveParsed.title ? 30 : 0;
2279
- const {
2280
- LEGEND_HEIGHT: SITEMAP_LEGEND_HEIGHT,
2281
- LEGEND_GROUP_GAP: SITEMAP_LEGEND_GROUP_GAP
2282
- } = await import("./legend-constants-55MFBXYF.js");
2283
- const suppressedLegendBand = sitemapLayout.legend.length > 0 && effectiveParsed.options["no-legend"] === "on" ? SITEMAP_LEGEND_HEIGHT + SITEMAP_LEGEND_GROUP_GAP : 0;
2284
- const exportWidth = sitemapLayout.width + PADDING * 2;
2285
- const exportHeight = sitemapLayout.height + PADDING * 2 + titleOffset - suppressedLegendBand;
2286
- const container = createExportContainer(exportWidth, exportHeight);
2287
- renderSitemap(
2288
- container,
2289
- effectiveParsed,
2290
- sitemapLayout,
2291
- effectivePalette,
2292
- isDark,
2293
- void 0,
2294
- { width: exportWidth, height: exportHeight },
2295
- activeTagGroup,
2296
- hiddenAttributes,
2297
- exportMode
2298
- );
2299
- return finalizeSvgExport(container, theme, effectivePalette);
2300
- }
2301
- async function exportKanban(ctx) {
2302
- const { content, theme, palette, viewState, exportMode } = ctx;
2303
- const { parseKanban: parseKanban2 } = await import("./parser-KLCSFQ3J.js");
2304
- const { renderKanban } = await import("./renderer-5TZVFQ7K.js");
2305
- const effectivePalette = await resolveExportPalette(theme, palette);
2306
- const kanbanParsed = parseKanban2(content, effectivePalette);
2307
- if (kanbanParsed.error || kanbanParsed.columns.length === 0) return "";
2308
- const container = document.createElement("div");
2309
- container.style.position = "absolute";
2310
- container.style.left = "-9999px";
2311
- document.body.appendChild(container);
2312
- const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
2313
- const sourceCollapsedColumns = kanbanParsed.columns.filter((c) => c.collapsed).map((c) => c.id);
2314
- const kanbanCollapsedColumns = viewState?.cc || sourceCollapsedColumns.length > 0 ? /* @__PURE__ */ new Set([...sourceCollapsedColumns, ...viewState?.cc ?? []]) : void 0;
2315
- renderKanban(container, kanbanParsed, effectivePalette, ctx.isDark, {
2316
- activeTagGroup: resolveActiveTagGroup(
2317
- kanbanParsed.tagGroups,
2318
- kanbanParsed.options["active-tag"],
2319
- ctxTagOverride(ctx)
2320
- ),
2321
- currentSwimlaneGroup: viewState?.swim ?? kanbanParsed.options["lane-by"] ?? null,
2322
- ...kanbanCollapsedLanes !== void 0 && {
2323
- collapsedLanes: kanbanCollapsedLanes
2324
- },
2325
- ...kanbanCollapsedColumns !== void 0 && {
2326
- collapsedColumns: kanbanCollapsedColumns
2327
- },
2328
- ...viewState?.cm !== void 0 && { compactMeta: viewState.cm },
2329
- exportMode
2330
- });
2331
- return finalizeSvgExport(container, theme, effectivePalette);
2332
- }
2333
- async function exportClass(ctx) {
2334
- const { content, theme, palette, exportMode } = ctx;
2335
- const { parseClassDiagram: parseClassDiagram2 } = await import("./parser-L6RWDXQN.js");
2336
- const { layoutClassDiagram } = await import("./layout-S3RLVPH4.js");
2337
- const { renderClassDiagram } = await import("./renderer-ZGY7P4BM.js");
2338
- const effectivePalette = await resolveExportPalette(theme, palette);
2339
- const classParsed = parseClassDiagram2(content, effectivePalette);
2340
- if (classParsed.error || classParsed.classes.length === 0) return "";
2341
- const classLayout = layoutClassDiagram(classParsed);
2342
- const PADDING = 20;
2343
- const titleOffset = classParsed.title ? 40 : 0;
2344
- const exportWidth = classLayout.width + PADDING * 2;
2345
- const exportHeight = classLayout.height + PADDING * 2 + titleOffset;
2346
- const container = createExportContainer(exportWidth, exportHeight);
2347
- renderClassDiagram(
2348
- container,
2349
- classParsed,
2350
- classLayout,
2351
- effectivePalette,
2352
- ctx.isDark,
2353
- void 0,
2354
- { width: exportWidth, height: exportHeight },
2355
- void 0,
2356
- exportMode
2357
- );
2358
- return finalizeSvgExport(container, theme, effectivePalette);
2057
+ function beginVizExport(ctx) {
2058
+ const container = createExportContainer(ctx.width, ctx.height);
2059
+ const dims = {
2060
+ width: ctx.width,
2061
+ height: ctx.height
2062
+ };
2063
+ return { container, dims };
2359
2064
  }
2360
- async function exportEr(ctx) {
2361
- const { content, theme, palette, viewState, exportMode } = ctx;
2362
- const { parseERDiagram: parseERDiagram2 } = await import("./parser-QYISOAGR.js");
2363
- const { layoutERDiagram } = await import("./layout-5USEVQBY.js");
2364
- const { renderERDiagram } = await import("./renderer-S3YCLJKL.js");
2065
+
2066
+ // src/arc/export.ts
2067
+ async function exportArc(ctx) {
2068
+ const { content, theme, palette } = ctx;
2069
+ const parsed = parseArc(content, palette);
2070
+ if (parsed.error || parsed.links.length === 0) return "";
2365
2071
  const effectivePalette = await resolveExportPalette(theme, palette);
2366
- const erParsed = parseERDiagram2(content, effectivePalette);
2367
- if (erParsed.error || erParsed.tables.length === 0) return "";
2368
- const erLayout = layoutERDiagram(erParsed);
2369
- const PADDING = 20;
2370
- const titleOffset = erParsed.title ? 40 : 0;
2371
- const exportWidth = erLayout.width + PADDING * 2;
2372
- const exportHeight = erLayout.height + PADDING * 2 + titleOffset;
2373
- const container = createExportContainer(exportWidth, exportHeight);
2374
- renderERDiagram(
2072
+ const { container, dims } = beginVizExport(ctx);
2073
+ renderArcDiagram(
2375
2074
  container,
2376
- erParsed,
2377
- erLayout,
2075
+ parsed,
2378
2076
  effectivePalette,
2379
2077
  ctx.isDark,
2380
2078
  void 0,
2381
- { width: exportWidth, height: exportHeight },
2382
- resolveActiveTagGroup(
2383
- erParsed.tagGroups,
2384
- erParsed.options["active-tag"],
2385
- ctxTagOverride(ctx)
2386
- ),
2387
- // Semantic colors are on by default; the source `no-semantic-colors` flag
2388
- // suppresses them. An interactive `viewState.sem` overrides source.
2389
- viewState?.sem ?? (erParsed.options["no-semantic-colors"] === "on" ? false : void 0),
2390
- exportMode
2079
+ dims
2391
2080
  );
2392
2081
  return finalizeSvgExport(container, theme, effectivePalette);
2393
2082
  }
2394
- async function exportSketch(ctx) {
2395
- const { content, theme, viewState, exportMode } = ctx;
2396
- const { parseSketch: parseSketch2 } = await import("./parser-YHDGKPZI.js");
2397
- const effectivePalette = await resolveExportPalette(theme, ctx.palette);
2398
- const parsed = parseSketch2(content, effectivePalette);
2399
- if (parsed.error || parsed.nodes.length === 0 && parsed.boxes.length === 0) {
2400
- return "";
2401
- }
2402
- const { layoutSketch } = await import("./layout-D4L44NRG.js");
2403
- const { renderSketchForExport } = await import("./renderer-XO57LTU4.js");
2404
- const collapsedBoxes = viewState?.cg ? new Set(viewState.cg) : void 0;
2405
- const skLayout = layoutSketch(parsed, {
2406
- ...collapsedBoxes !== void 0 && { collapsedBoxes }
2083
+
2084
+ // src/block/export.ts
2085
+ async function exportBlock(ctx) {
2086
+ const { content, theme, palette } = ctx;
2087
+ const { parseBlock: parseBlock2 } = await import("./parser-TUAEX6ML.js");
2088
+ const { renderBlockForExport } = await import("./renderer-ISQJ2ELA.js");
2089
+ const effectivePalette = await resolveExportPalette(theme, palette);
2090
+ const blockParsed = parseBlock2(content, effectivePalette);
2091
+ if (blockParsed.error || blockParsed.top.rows.length === 0) return "";
2092
+ const container = createExportContainer(ctx.width, ctx.height);
2093
+ renderBlockForExport(container, blockParsed, effectivePalette, ctx.isDark, {
2094
+ width: ctx.width,
2095
+ height: ctx.height
2407
2096
  });
2408
- const PADDING = 20;
2409
- const titleOffset = parsed.title ? 40 : 0;
2410
- const exportWidth = skLayout.width + PADDING * 2;
2411
- const exportHeight = skLayout.height + PADDING * 2 + titleOffset;
2412
- const container = createExportContainer(exportWidth, exportHeight);
2413
- const skTagOverride = ctxTagOverride(ctx);
2414
- renderSketchForExport(
2097
+ return finalizeSvgExport(container, theme, effectivePalette);
2098
+ }
2099
+
2100
+ // src/body/export.ts
2101
+ async function exportBody(ctx) {
2102
+ const { content, theme, palette } = ctx;
2103
+ const { parseBody: parseBody2 } = await import("./parser-PEZ7RN43.js");
2104
+ const { renderBodyForExport } = await import("./renderer-AM7P4LN7.js");
2105
+ const effectivePalette = await resolveExportPalette(theme, palette);
2106
+ const parsed = parseBody2(content, effectivePalette);
2107
+ if (parsed.error) return "";
2108
+ const container = createExportContainer(ctx.width, ctx.height);
2109
+ renderBodyForExport(
2415
2110
  container,
2416
2111
  parsed,
2417
- skLayout,
2418
2112
  effectivePalette,
2419
2113
  ctx.isDark,
2420
- {
2421
- ...skTagOverride !== void 0 && { activeTagGroup: skTagOverride },
2422
- ...exportMode !== void 0 && { exportMode },
2423
- ...viewState?.hd !== void 0 && { hideDescriptions: viewState.hd }
2424
- }
2114
+ { width: ctx.width, height: ctx.height },
2115
+ ctxTagOverride(ctx)
2425
2116
  );
2426
2117
  return finalizeSvgExport(container, theme, effectivePalette);
2427
2118
  }
2428
- async function exportWhiteboard(ctx) {
2429
- const { content, theme } = ctx;
2430
- const { parseWhiteboard: parseWhiteboard2 } = await import("./parser-XZUKNZPB.js");
2431
- const effectivePalette = await resolveExportPalette(theme, ctx.palette);
2432
- const parsed = parseWhiteboard2(content, effectivePalette);
2433
- if (parsed.error || parsed.elements.length === 0) return "";
2434
- const { renderWhiteboard } = await import("./renderer-ZHGHTSNG.js");
2435
- const container = createExportContainer(1, 1);
2436
- const resolveImage = ctx.options?.resolveImage;
2437
- const showNotes = ctx.options?.showNotes;
2438
- renderWhiteboard(container, parsed, effectivePalette, ctx.isDark, {
2439
- ...resolveImage !== void 0 && { resolveImage },
2440
- ...showNotes !== void 0 && { showNotes }
2441
- });
2442
- return finalizeSvgExport(container, theme, effectivePalette);
2443
- }
2119
+
2120
+ // src/boxes-and-lines/export.ts
2444
2121
  async function exportBoxesAndLines(ctx) {
2445
2122
  const { content, theme, palette, viewState, exportMode } = ctx;
2446
- const { parseBoxesAndLines: parseBoxesAndLines2 } = await import("./parser-GX5YLRIP.js");
2123
+ const { parseBoxesAndLines: parseBoxesAndLines2 } = await import("./parser-7QKQ4P4P.js");
2447
2124
  const effectivePalette = await resolveExportPalette(theme, palette);
2448
2125
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
2449
2126
  if (blParsed.error || blParsed.nodes.length === 0) return "";
@@ -2454,8 +2131,8 @@ async function exportBoxesAndLines(ctx) {
2454
2131
  blHiddenTagValues.set(k, new Set(v));
2455
2132
  }
2456
2133
  }
2457
- const { renderBoxesAndLinesForExport } = await import("./renderer-RSK6QFWN.js");
2458
- const { layoutBoxesAndLines } = await import("./layout-BNLFQHSB.js");
2134
+ const { renderBoxesAndLinesForExport } = await import("./renderer-LNP23NSM.js");
2135
+ const { layoutBoxesAndLines } = await import("./layout-U3AH6M7J.js");
2459
2136
  const blLayout = await layoutBoxesAndLines(blParsed);
2460
2137
  const PADDING = 20;
2461
2138
  const titleOffset = blParsed.title ? 40 : 0;
@@ -2482,311 +2159,246 @@ async function exportBoxesAndLines(ctx) {
2482
2159
  );
2483
2160
  return finalizeSvgExport(container, theme, effectivePalette);
2484
2161
  }
2485
- async function exportSwimlane(ctx) {
2162
+
2163
+ // src/bracket/export.ts
2164
+ async function exportBracket(ctx) {
2486
2165
  const { content, theme, palette } = ctx;
2487
- const { parseSwimlane: parseSwimlane2 } = await import("./parser-H5OODY5N.js");
2488
- const { layoutSwimlane } = await import("./layout-J3AGFZZ7.js");
2489
- const { renderSwimlaneForExport } = await import("./renderer-OAR5VPH4.js");
2166
+ const { parseBracket: parseBracket2 } = await import("./parser-RTWPFKXG.js");
2167
+ const { renderBracketForExport, bracketExportDimensions } = await import("./renderer-2LYX6NHN.js");
2490
2168
  const effectivePalette = await resolveExportPalette(theme, palette);
2491
- const swimParsed = parseSwimlane2(content, effectivePalette);
2492
- if (swimParsed.error || swimParsed.nodes.length === 0) return "";
2493
- const swimLayout = layoutSwimlane(swimParsed);
2494
- const PADDING = 20;
2495
- const titleOffset = swimParsed.title ? 40 : 0;
2496
- const exportWidth = swimLayout.width + PADDING * 2;
2497
- const exportHeight = swimLayout.height + PADDING * 2 + titleOffset;
2498
- const container = createExportContainer(exportWidth, exportHeight);
2499
- renderSwimlaneForExport(
2500
- container,
2501
- swimParsed,
2502
- swimLayout,
2503
- effectivePalette,
2504
- ctx.isDark,
2505
- {
2506
- exportDims: { width: exportWidth, height: exportHeight },
2507
- activeTagGroup: resolveActiveTagGroup(
2508
- swimParsed.tagGroups,
2509
- swimParsed.options["active-tag"],
2510
- ctxTagOverride(ctx)
2511
- ),
2512
- exportMode: ctx.exportMode
2513
- }
2514
- );
2169
+ const parsed = parseBracket2(content, effectivePalette);
2170
+ if (parsed.error) return "";
2171
+ const { width, height } = bracketExportDimensions(parsed, effectivePalette);
2172
+ const container = createExportContainer(width, height);
2173
+ renderBracketForExport(container, parsed, effectivePalette, ctx.isDark, {
2174
+ width,
2175
+ height
2176
+ });
2515
2177
  return finalizeSvgExport(container, theme, effectivePalette);
2516
2178
  }
2517
- async function exportFamily(ctx) {
2518
- const { content, theme, palette } = ctx;
2519
- const { parseFamily: parseFamily2 } = await import("./parser-EAQPZX2B.js");
2520
- const { layoutFamily } = await import("./layout-MBVBAMPA.js");
2521
- const { renderFamilyForExport } = await import("./renderer-ZZ6MWVB7.js");
2179
+
2180
+ // src/c4/export.ts
2181
+ async function exportC4(ctx) {
2182
+ const { content, theme, palette, viewState, exportMode } = ctx;
2183
+ const { parseC4: parseC42 } = await import("./parser-4Y6PODW6.js");
2184
+ const {
2185
+ layoutC4Context,
2186
+ layoutC4Containers,
2187
+ layoutC4Components,
2188
+ layoutC4Deployment
2189
+ } = await import("./layout-JTIRCKAW.js");
2190
+ const { renderC4Context, renderC4Containers } = await import("./renderer-QTPWRRJ7.js");
2522
2191
  const effectivePalette = await resolveExportPalette(theme, palette);
2523
- const parsed = parseFamily2(content, effectivePalette);
2524
- if (parsed.error || parsed.persons.size === 0) return "";
2525
- const layout = layoutFamily(parsed);
2192
+ const c4Parsed = parseC42(content, effectivePalette);
2193
+ if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
2194
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
2195
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
2196
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
2197
+ const c4Layout = c4Level === "deployment" ? layoutC4Deployment(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers(c4Parsed, c4System) : layoutC4Context(c4Parsed);
2198
+ if (c4Layout.nodes.length === 0) return "";
2526
2199
  const PADDING = 20;
2527
- const titleOffset = parsed.title ? 40 : 0;
2528
- const exportWidth = layout.width + PADDING * 2;
2529
- const exportHeight = layout.height + PADDING * 2 + titleOffset;
2200
+ const titleOffset = c4Parsed.title ? 40 : 0;
2201
+ const exportWidth = c4Layout.width + PADDING * 2;
2202
+ const exportHeight = c4Layout.height + PADDING * 2 + titleOffset;
2530
2203
  const container = createExportContainer(exportWidth, exportHeight);
2531
- renderFamilyForExport(
2204
+ const renderFn = c4Level === "deployment" || c4Level === "components" && c4System && c4Container || c4Level === "containers" && c4System ? renderC4Containers : renderC4Context;
2205
+ renderFn(
2532
2206
  container,
2533
- parsed,
2534
- layout,
2207
+ c4Parsed,
2208
+ c4Layout,
2535
2209
  effectivePalette,
2536
2210
  ctx.isDark,
2537
- {
2538
- exportDims: { width: exportWidth, height: exportHeight },
2539
- activeTagGroup: resolveActiveTagGroup(
2540
- parsed.tagGroups,
2541
- parsed.options["active-tag"],
2542
- ctxTagOverride(ctx)
2543
- ),
2544
- exportMode: ctx.exportMode
2545
- }
2211
+ void 0,
2212
+ { width: exportWidth, height: exportHeight },
2213
+ resolveActiveTagGroup(
2214
+ c4Parsed.tagGroups,
2215
+ c4Parsed.options["active-tag"],
2216
+ ctxTagOverride(ctx)
2217
+ ),
2218
+ exportMode
2546
2219
  );
2547
2220
  return finalizeSvgExport(container, theme, effectivePalette);
2548
2221
  }
2549
- async function exportMindmap(ctx) {
2550
- const { content, theme, palette, viewState, exportMode } = ctx;
2551
- const { parseMindmap: parseMindmap2 } = await import("./parser-KKF6Y5MM.js");
2552
- const { layoutMindmap } = await import("./layout-RH3N46HP.js");
2553
- const { collapseMindmapTree } = await import("./collapse-BSPH3BFM.js");
2554
- const { renderMindmap } = await import("./renderer-QGIHLZOJ.js");
2555
- const isDark = ctx.isDark;
2222
+
2223
+ // src/class/export.ts
2224
+ async function exportClass(ctx) {
2225
+ const { content, theme, palette, exportMode } = ctx;
2226
+ const { parseClassDiagram: parseClassDiagram2 } = await import("./parser-TAYRTCPR.js");
2227
+ const { layoutClassDiagram } = await import("./layout-IT4ZTV6C.js");
2228
+ const { renderClassDiagram } = await import("./renderer-OUDHH5MV.js");
2556
2229
  const effectivePalette = await resolveExportPalette(theme, palette);
2557
- const mmParsed = parseMindmap2(content, effectivePalette);
2558
- if (mmParsed.error) return "";
2559
- const collapsedNodes = new Set(viewState?.cg ?? []);
2560
- const collectCollapsed = (nodes) => {
2561
- for (const n of nodes) {
2562
- if (n.collapsed) collapsedNodes.add(n.id);
2563
- if (n.children.length) collectCollapsed(n.children);
2564
- }
2565
- };
2566
- collectCollapsed(mmParsed.roots);
2567
- const activeTagGroup = resolveActiveTagGroup(
2568
- mmParsed.tagGroups,
2569
- mmParsed.options["active-tag"],
2570
- ctxTagOverride(ctx)
2571
- );
2572
- const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
2573
- const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
2574
- const effectiveParsed = { ...mmParsed, roots: effectiveRoots };
2575
- const mmLayout = layoutMindmap(effectiveParsed, effectivePalette, {
2576
- interactive: false,
2577
- ...hiddenCounts.size > 0 && { hiddenCounts },
2578
- activeTagGroup,
2579
- hideDescriptions
2580
- });
2230
+ const classParsed = parseClassDiagram2(content, effectivePalette);
2231
+ if (classParsed.error || classParsed.classes.length === 0) return "";
2232
+ const classLayout = layoutClassDiagram(classParsed);
2581
2233
  const PADDING = 20;
2582
- const titleOffset = effectiveParsed.title ? 30 : 0;
2583
- const exportWidth = mmLayout.width + PADDING * 2;
2584
- const exportHeight = mmLayout.height + PADDING * 2 + titleOffset;
2234
+ const titleOffset = classParsed.title ? 40 : 0;
2235
+ const exportWidth = classLayout.width + PADDING * 2;
2236
+ const exportHeight = classLayout.height + PADDING * 2 + titleOffset;
2585
2237
  const container = createExportContainer(exportWidth, exportHeight);
2586
- const colorByDepth = viewState?.cbd ?? mmParsed.options["color-by-depth"] === "on";
2587
- renderMindmap(
2238
+ renderClassDiagram(
2588
2239
  container,
2589
- effectiveParsed,
2590
- mmLayout,
2240
+ classParsed,
2241
+ classLayout,
2591
2242
  effectivePalette,
2592
- isDark,
2243
+ ctx.isDark,
2593
2244
  void 0,
2594
2245
  { width: exportWidth, height: exportHeight },
2595
2246
  void 0,
2596
- hideDescriptions,
2597
- colorByDepth ? null : activeTagGroup,
2598
- colorByDepth ? { colorByDepth: true, exportMode } : { exportMode }
2247
+ exportMode
2599
2248
  );
2600
2249
  return finalizeSvgExport(container, theme, effectivePalette);
2601
2250
  }
2602
- async function exportWireframe(ctx) {
2251
+
2252
+ // src/clock/export.ts
2253
+ async function exportClock(ctx) {
2603
2254
  const { content, theme, palette } = ctx;
2604
- const { parseWireframe: parseWireframe2 } = await import("./parser-4TBSWULQ.js");
2605
- const { layoutWireframe } = await import("./layout-HF3PMI4H.js");
2606
- const { renderWireframe } = await import("./renderer-36446HSH.js");
2255
+ const { parseClock: parseClock2 } = await import("./parser-K2DI26SC.js");
2256
+ const { renderClockForExport } = await import("./renderer-DDWDCM2K.js");
2607
2257
  const effectivePalette = await resolveExportPalette(theme, palette);
2608
- const wireframeParsed = parseWireframe2(content);
2609
- if (wireframeParsed.error || wireframeParsed.roots.length === 0 && wireframeParsed.modals.length === 0)
2610
- return "";
2611
- const wireframeLayout = layoutWireframe(wireframeParsed);
2612
- const exportWidth = wireframeLayout.width;
2613
- const exportHeight = wireframeLayout.height;
2614
- const container = createExportContainer(exportWidth, exportHeight);
2615
- renderWireframe(
2258
+ const parsed = parseClock2(content, effectivePalette);
2259
+ if (parsed.error) return "";
2260
+ const container = createExportContainer(ctx.width, ctx.height);
2261
+ renderClockForExport(container, parsed, effectivePalette, ctx.isDark, {
2262
+ width: ctx.width,
2263
+ height: ctx.height
2264
+ });
2265
+ return finalizeSvgExport(container, theme, effectivePalette);
2266
+ }
2267
+
2268
+ // src/countdown/export.ts
2269
+ async function exportCountdown(ctx) {
2270
+ const { content, theme, palette } = ctx;
2271
+ const { parseCountdown: parseCountdown2 } = await import("./parser-KJPPUN4D.js");
2272
+ const { renderCountdownForExport } = await import("./renderer-3NLDX7DQ.js");
2273
+ const effectivePalette = await resolveExportPalette(theme, palette);
2274
+ const parsed = parseCountdown2(content, effectivePalette);
2275
+ if (parsed.error) return "";
2276
+ const container = createExportContainer(ctx.width, ctx.height);
2277
+ renderCountdownForExport(container, parsed, effectivePalette, ctx.isDark, {
2278
+ width: ctx.width,
2279
+ height: ctx.height
2280
+ });
2281
+ return finalizeSvgExport(container, theme, effectivePalette);
2282
+ }
2283
+
2284
+ // src/cycle/export.ts
2285
+ async function exportCycle(ctx) {
2286
+ const { content, theme, palette, viewState, exportMode } = ctx;
2287
+ const { parseCycle: parseCycle2 } = await import("./parser-RCIEKN7Y.js");
2288
+ const { renderCycleForExport } = await import("./renderer-7OGGV327.js");
2289
+ const effectivePalette = await resolveExportPalette(theme, palette);
2290
+ const cycleParsed = parseCycle2(content);
2291
+ if (cycleParsed.error || cycleParsed.nodes.length === 0) return "";
2292
+ const container = createExportContainer(ctx.width, ctx.height);
2293
+ renderCycleForExport(
2616
2294
  container,
2617
- wireframeParsed,
2618
- wireframeLayout,
2295
+ cycleParsed,
2619
2296
  effectivePalette,
2620
2297
  ctx.isDark,
2621
- void 0,
2622
- { width: exportWidth, height: exportHeight },
2623
- theme
2298
+ { width: ctx.width, height: ctx.height },
2299
+ viewState,
2300
+ exportMode
2624
2301
  );
2625
2302
  return finalizeSvgExport(container, theme, effectivePalette);
2626
2303
  }
2627
- async function exportC4(ctx) {
2304
+
2305
+ // src/er/export.ts
2306
+ async function exportEr(ctx) {
2628
2307
  const { content, theme, palette, viewState, exportMode } = ctx;
2629
- const { parseC4: parseC42 } = await import("./parser-3MK56JZC.js");
2630
- const {
2631
- layoutC4Context,
2632
- layoutC4Containers,
2633
- layoutC4Components,
2634
- layoutC4Deployment
2635
- } = await import("./layout-JTIRCKAW.js");
2636
- const { renderC4Context, renderC4Containers } = await import("./renderer-VPIELEE5.js");
2308
+ const { parseERDiagram: parseERDiagram2 } = await import("./parser-4RFY7D52.js");
2309
+ const { layoutERDiagram } = await import("./layout-ISSBWBH7.js");
2310
+ const { renderERDiagram } = await import("./renderer-J3GPDK73.js");
2637
2311
  const effectivePalette = await resolveExportPalette(theme, palette);
2638
- const c4Parsed = parseC42(content, effectivePalette);
2639
- if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
2640
- const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
2641
- const c4System = ctx.options?.c4System ?? viewState?.c4s;
2642
- const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
2643
- const c4Layout = c4Level === "deployment" ? layoutC4Deployment(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers(c4Parsed, c4System) : layoutC4Context(c4Parsed);
2644
- if (c4Layout.nodes.length === 0) return "";
2312
+ const erParsed = parseERDiagram2(content, effectivePalette);
2313
+ if (erParsed.error || erParsed.tables.length === 0) return "";
2314
+ const erLayout = layoutERDiagram(erParsed);
2645
2315
  const PADDING = 20;
2646
- const titleOffset = c4Parsed.title ? 40 : 0;
2647
- const exportWidth = c4Layout.width + PADDING * 2;
2648
- const exportHeight = c4Layout.height + PADDING * 2 + titleOffset;
2316
+ const titleOffset = erParsed.title ? 40 : 0;
2317
+ const exportWidth = erLayout.width + PADDING * 2;
2318
+ const exportHeight = erLayout.height + PADDING * 2 + titleOffset;
2649
2319
  const container = createExportContainer(exportWidth, exportHeight);
2650
- const renderFn = c4Level === "deployment" || c4Level === "components" && c4System && c4Container || c4Level === "containers" && c4System ? renderC4Containers : renderC4Context;
2651
- renderFn(
2320
+ renderERDiagram(
2652
2321
  container,
2653
- c4Parsed,
2654
- c4Layout,
2322
+ erParsed,
2323
+ erLayout,
2655
2324
  effectivePalette,
2656
2325
  ctx.isDark,
2657
2326
  void 0,
2658
2327
  { width: exportWidth, height: exportHeight },
2659
2328
  resolveActiveTagGroup(
2660
- c4Parsed.tagGroups,
2661
- c4Parsed.options["active-tag"],
2329
+ erParsed.tagGroups,
2330
+ erParsed.options["active-tag"],
2662
2331
  ctxTagOverride(ctx)
2663
2332
  ),
2333
+ // Semantic colors are on by default; the source `no-semantic-colors` flag
2334
+ // suppresses them. An interactive `viewState.sem` overrides source.
2335
+ viewState?.sem ?? (erParsed.options["no-semantic-colors"] === "on" ? false : void 0),
2664
2336
  exportMode
2665
2337
  );
2666
2338
  return finalizeSvgExport(container, theme, effectivePalette);
2667
2339
  }
2668
- async function exportFlowchart(ctx) {
2340
+
2341
+ // src/event-line/export.ts
2342
+ async function exportEventLine(ctx) {
2669
2343
  const { content, theme, palette } = ctx;
2670
- const { parseFlowchart: parseFlowchart2 } = await import("./flowchart-parser-NA75YQ77.js");
2671
- const { layoutGraph } = await import("./layout-WRG4APSJ.js");
2672
- const { renderFlowchart } = await import("./flowchart-renderer-6MGGX4RC.js");
2344
+ const { parseEventLine: parseEventLine2 } = await import("./parser-YG625J5H.js");
2345
+ const { renderEventLineForExport } = await import("./renderer-J7ZSTJUE.js");
2673
2346
  const effectivePalette = await resolveExportPalette(theme, palette);
2674
- const fcParsed = parseFlowchart2(content, effectivePalette);
2675
- if (fcParsed.error || fcParsed.nodes.length === 0) return "";
2676
- const layout = layoutGraph(fcParsed);
2347
+ const parsed = parseEventLine2(content, effectivePalette);
2348
+ if (parsed.error || parsed.events.length === 0) return "";
2677
2349
  const container = createExportContainer(ctx.width, ctx.height);
2678
- renderFlowchart(
2350
+ renderEventLineForExport(
2679
2351
  container,
2680
- fcParsed,
2681
- layout,
2352
+ parsed,
2682
2353
  effectivePalette,
2683
2354
  ctx.isDark,
2684
- void 0,
2685
- { width: ctx.width, height: ctx.height }
2355
+ { width: ctx.width, height: ctx.height },
2356
+ ctxTagOverride(ctx),
2357
+ /* @__PURE__ */ new Date()
2686
2358
  );
2687
2359
  return finalizeSvgExport(container, theme, effectivePalette);
2688
2360
  }
2689
- async function exportInfra(ctx) {
2690
- const { content, theme, palette, viewState } = ctx;
2691
- const { parseInfra: parseInfra2 } = await import("./parser-FXDS4JDK.js");
2692
- const { computeInfra } = await import("./compute-II4G44IK.js");
2693
- const { layoutInfra } = await import("./layout-2WESJYRE.js");
2694
- const { renderInfra, computeInfraLegendGroups } = await import("./renderer-7ZZG5HR3.js");
2361
+
2362
+ // src/family/export.ts
2363
+ async function exportFamily(ctx) {
2364
+ const { content, theme, palette } = ctx;
2365
+ const { parseFamily: parseFamily2 } = await import("./parser-EAQPZX2B.js");
2366
+ const { layoutFamily } = await import("./layout-3TSVAGWH.js");
2367
+ const { renderFamilyForExport } = await import("./renderer-XCLARXCK.js");
2695
2368
  const effectivePalette = await resolveExportPalette(theme, palette);
2696
- const infraParsed = parseInfra2(content);
2697
- if (infraParsed.error || infraParsed.nodes.length === 0) return "";
2698
- const infraComputed = computeInfra(infraParsed);
2699
- const infraLayout = layoutInfra(infraComputed);
2700
- const activeTagGroup = resolveActiveTagGroup(
2701
- infraParsed.tagGroups,
2702
- infraParsed.options["active-tag"],
2703
- ctxTagOverride(ctx)
2704
- );
2705
- const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
2706
- const titleOffset = showInfraTitle ? 40 : 0;
2707
- const infraTagGroups = [...infraParsed.tagGroups];
2708
- const legendGroups = computeInfraLegendGroups(
2709
- infraLayout.nodes,
2710
- infraTagGroups,
2711
- effectivePalette
2712
- );
2713
- const legendOffset = legendGroups.length > 0 && infraParsed.options["no-legend"] !== "on" ? 28 : 0;
2714
- const exportWidth = infraLayout.width;
2715
- const exportHeight = infraLayout.height + titleOffset + legendOffset;
2369
+ const parsed = parseFamily2(content, effectivePalette);
2370
+ if (parsed.error || parsed.persons.size === 0) return "";
2371
+ const layout = layoutFamily(parsed);
2372
+ const PADDING = 20;
2373
+ const titleOffset = parsed.title ? 40 : 0;
2374
+ const exportWidth = layout.width + PADDING * 2;
2375
+ const exportHeight = layout.height + PADDING * 2 + titleOffset;
2716
2376
  const container = createExportContainer(exportWidth, exportHeight);
2717
- renderInfra(
2718
- container,
2719
- infraLayout,
2720
- effectivePalette,
2721
- ctx.isDark,
2722
- showInfraTitle ? infraParsed.title : null,
2723
- showInfraTitle ? infraParsed.titleLineNumber : null,
2724
- infraTagGroups,
2725
- activeTagGroup,
2726
- false,
2727
- null,
2728
- null,
2729
- true,
2730
- viewState?.cg ? new Set(viewState.cg) : null
2731
- );
2732
- const infraSvg = container.querySelector("svg");
2733
- if (infraSvg) {
2734
- infraSvg.setAttribute("width", String(exportWidth));
2735
- infraSvg.setAttribute("height", String(exportHeight));
2736
- }
2737
- return finalizeSvgExport(container, theme, effectivePalette);
2738
- }
2739
- async function exportPert(ctx) {
2740
- const { content, theme, palette, viewState } = ctx;
2741
- const { parsePert: parsePert2 } = await import("./parser-2IKPQVKX.js");
2742
- const { analyzePert } = await import("./analyzer-G7PGOT3V.js");
2743
- const { layoutPert } = await import("./layout-KV27UW25.js");
2744
- const { renderPert, measurePertAnalysisBlock } = await import("./renderer-O6DY46AV.js");
2745
- const effectivePalette = await resolveExportPalette(theme, palette);
2746
- const pertParsed = parsePert2(content, { palette: effectivePalette });
2747
- if (pertParsed.error || pertParsed.activities.length === 0) return "";
2748
- const pertResolved = analyzePert(pertParsed);
2749
- const pertLayout = layoutPert(pertResolved);
2750
- const hasTitle = !!pertParsed.title && !pertParsed.options.noTitle;
2751
- const hasSubtitle = pertResolved.projectSubtitle !== null;
2752
- const titleHeight = hasTitle ? 80 : hasSubtitle ? 50 : 0;
2753
- const PERT_PADDING = 20;
2754
- const analysisOn = viewState?.an ?? !pertParsed.options.noAnalysis;
2755
- const fieldLabelsOn = viewState?.fl === true;
2756
- const exportW = pertLayout.width + PERT_PADDING * 2;
2757
- const analysisMeasured = measurePertAnalysisBlock(
2758
- pertResolved,
2759
- exportW - 2 * PERT_PADDING,
2760
- {
2761
- showTornado: analysisOn,
2762
- showScurve: analysisOn,
2763
- showFieldLegend: fieldLabelsOn
2764
- }
2765
- );
2766
- const exportH = pertLayout.height + PERT_PADDING * 2 + titleHeight + analysisMeasured.height;
2767
- const container = createExportContainer(exportW, exportH);
2768
- renderPert(
2377
+ renderFamilyForExport(
2769
2378
  container,
2770
- pertResolved,
2771
- pertLayout,
2379
+ parsed,
2380
+ layout,
2772
2381
  effectivePalette,
2773
2382
  ctx.isDark,
2774
2383
  {
2775
- title: hasTitle ? pertParsed.title : null,
2776
- subtitle: pertResolved.projectSubtitle,
2777
- exportDims: { width: exportW, height: exportH },
2778
- showTornado: analysisOn,
2779
- showScurve: analysisOn,
2780
- showFieldLegend: fieldLabelsOn
2384
+ exportDims: { width: exportWidth, height: exportHeight },
2385
+ activeTagGroup: resolveActiveTagGroup(
2386
+ parsed.tagGroups,
2387
+ parsed.options["active-tag"],
2388
+ ctxTagOverride(ctx)
2389
+ ),
2390
+ exportMode: ctx.exportMode
2781
2391
  }
2782
2392
  );
2783
2393
  return finalizeSvgExport(container, theme, effectivePalette);
2784
2394
  }
2395
+
2396
+ // src/gantt/export.ts
2785
2397
  async function exportGantt(ctx) {
2786
2398
  const { content, theme, palette, viewState, exportMode } = ctx;
2787
- const { parseGantt: parseGantt2 } = await import("./parser-2NWH5JUS.js");
2399
+ const { parseGantt: parseGantt2 } = await import("./parser-A3AN76RQ.js");
2788
2400
  const { calculateSchedule } = await import("./calculator-BNH3GSJE.js");
2789
- const { renderGantt } = await import("./renderer-RUSFL7WU.js");
2401
+ const { renderGantt } = await import("./renderer-JYHA6R2D.js");
2790
2402
  const effectivePalette = await resolveExportPalette(theme, palette);
2791
2403
  const ganttParsed = parseGantt2(content, effectivePalette);
2792
2404
  const resolved = calculateSchedule(ganttParsed);
@@ -2825,11 +2437,50 @@ async function exportGantt(ctx) {
2825
2437
  );
2826
2438
  return finalizeSvgExport(container, theme, effectivePalette);
2827
2439
  }
2440
+
2441
+ // src/goal/export.ts
2442
+ async function exportGoal(ctx) {
2443
+ const { content, theme, palette } = ctx;
2444
+ const { parseGoal: parseGoal2 } = await import("./parser-3QIFY5IN.js");
2445
+ const { renderGoalForExport } = await import("./renderer-HX6ICPDX.js");
2446
+ const effectivePalette = await resolveExportPalette(theme, palette);
2447
+ const parsed = parseGoal2(content, effectivePalette);
2448
+ if (parsed.error) return "";
2449
+ const container = createExportContainer(ctx.width, ctx.height);
2450
+ renderGoalForExport(container, parsed, effectivePalette, ctx.isDark, {
2451
+ width: ctx.width,
2452
+ height: ctx.height
2453
+ });
2454
+ return finalizeSvgExport(container, theme, effectivePalette);
2455
+ }
2456
+
2457
+ // src/graph/export.ts
2458
+ async function exportFlowchart(ctx) {
2459
+ const { content, theme, palette } = ctx;
2460
+ const { parseFlowchart: parseFlowchart2 } = await import("./flowchart-parser-NZXASOZG.js");
2461
+ const { layoutGraph } = await import("./layout-VC3MXRB4.js");
2462
+ const { renderFlowchart } = await import("./flowchart-renderer-PTCRIANF.js");
2463
+ const effectivePalette = await resolveExportPalette(theme, palette);
2464
+ const fcParsed = parseFlowchart2(content, effectivePalette);
2465
+ if (fcParsed.error || fcParsed.nodes.length === 0) return "";
2466
+ const layout = layoutGraph(fcParsed);
2467
+ const container = createExportContainer(ctx.width, ctx.height);
2468
+ renderFlowchart(
2469
+ container,
2470
+ fcParsed,
2471
+ layout,
2472
+ effectivePalette,
2473
+ ctx.isDark,
2474
+ void 0,
2475
+ { width: ctx.width, height: ctx.height }
2476
+ );
2477
+ return finalizeSvgExport(container, theme, effectivePalette);
2478
+ }
2828
2479
  async function exportState(ctx) {
2829
2480
  const { content, theme, palette, viewState } = ctx;
2830
- const { parseState: parseState2 } = await import("./state-parser-TIR2ORJY.js");
2831
- const { layoutGraph } = await import("./layout-WRG4APSJ.js");
2832
- const { renderState } = await import("./state-renderer-G6SUXU4J.js");
2481
+ const { parseState: parseState2 } = await import("./state-parser-KPVW5CRI.js");
2482
+ const { layoutGraph } = await import("./layout-VC3MXRB4.js");
2483
+ const { renderState } = await import("./state-renderer-ZVEI265C.js");
2833
2484
  const { collapseStateGroups } = await import("./state-collapse-SE2G4K4I.js");
2834
2485
  const effectivePalette = await resolveExportPalette(theme, palette);
2835
2486
  const stateParsed = parseState2(content, effectivePalette);
@@ -2864,31 +2515,64 @@ async function exportState(ctx) {
2864
2515
  );
2865
2516
  return finalizeSvgExport(container, theme, effectivePalette);
2866
2517
  }
2867
- async function exportTechRadar(ctx) {
2868
- const { content, theme, palette, viewState, exportMode } = ctx;
2869
- const { parseTechRadar: parseTechRadar2 } = await import("./parser-3LT3ZUUC.js");
2870
- const { renderTechRadarForExport } = await import("./renderer-MM27BSBO.js");
2518
+
2519
+ // src/infra/export.ts
2520
+ async function exportInfra(ctx) {
2521
+ const { content, theme, palette, viewState } = ctx;
2522
+ const { parseInfra: parseInfra2 } = await import("./parser-F7NDRPMA.js");
2523
+ const { computeInfra } = await import("./compute-II4G44IK.js");
2524
+ const { layoutInfra } = await import("./layout-2WESJYRE.js");
2525
+ const { renderInfra, computeInfraLegendGroups } = await import("./renderer-UTEEJDGB.js");
2871
2526
  const effectivePalette = await resolveExportPalette(theme, palette);
2872
- const radarParsed = parseTechRadar2(content);
2873
- if (radarParsed.error || radarParsed.quadrants.length === 0) return "";
2874
- const RADAR_EXPORT_W = ctx.requestedWidth ?? 1300;
2875
- const RADAR_EXPORT_H = ctx.requestedHeight ?? 1500;
2876
- const container = createExportContainer(RADAR_EXPORT_W, RADAR_EXPORT_H);
2877
- renderTechRadarForExport(
2527
+ const infraParsed = parseInfra2(content);
2528
+ if (infraParsed.error || infraParsed.nodes.length === 0) return "";
2529
+ const infraComputed = computeInfra(infraParsed);
2530
+ const infraLayout = layoutInfra(infraComputed);
2531
+ const activeTagGroup = resolveActiveTagGroup(
2532
+ infraParsed.tagGroups,
2533
+ infraParsed.options["active-tag"],
2534
+ ctxTagOverride(ctx)
2535
+ );
2536
+ const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
2537
+ const titleOffset = showInfraTitle ? 40 : 0;
2538
+ const infraTagGroups = [...infraParsed.tagGroups];
2539
+ const legendGroups = computeInfraLegendGroups(
2540
+ infraLayout.nodes,
2541
+ infraTagGroups,
2542
+ effectivePalette
2543
+ );
2544
+ const legendOffset = legendGroups.length > 0 && infraParsed.options["no-legend"] !== "on" ? 28 : 0;
2545
+ const exportWidth = infraLayout.width;
2546
+ const exportHeight = infraLayout.height + titleOffset + legendOffset;
2547
+ const container = createExportContainer(exportWidth, exportHeight);
2548
+ renderInfra(
2878
2549
  container,
2879
- radarParsed,
2550
+ infraLayout,
2880
2551
  effectivePalette,
2881
- ctx.isDark,
2882
- { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
2883
- viewState,
2884
- exportMode
2552
+ ctx.isDark,
2553
+ showInfraTitle ? infraParsed.title : null,
2554
+ showInfraTitle ? infraParsed.titleLineNumber : null,
2555
+ infraTagGroups,
2556
+ activeTagGroup,
2557
+ false,
2558
+ null,
2559
+ null,
2560
+ true,
2561
+ viewState?.cg ? new Set(viewState.cg) : null
2885
2562
  );
2563
+ const infraSvg = container.querySelector("svg");
2564
+ if (infraSvg) {
2565
+ infraSvg.setAttribute("width", String(exportWidth));
2566
+ infraSvg.setAttribute("height", String(exportHeight));
2567
+ }
2886
2568
  return finalizeSvgExport(container, theme, effectivePalette);
2887
2569
  }
2570
+
2571
+ // src/journey-map/export.ts
2888
2572
  async function exportJourneyMap(ctx) {
2889
2573
  const { content, theme, palette, exportMode } = ctx;
2890
- const { parseJourneyMap: parseJourneyMap2 } = await import("./parser-5LW5JDYG.js");
2891
- const { renderJourneyMap } = await import("./renderer-3IHQI4A4.js");
2574
+ const { parseJourneyMap: parseJourneyMap2 } = await import("./parser-NL73FSFI.js");
2575
+ const { renderJourneyMap } = await import("./renderer-53B523PM.js");
2892
2576
  const { layoutJourneyMap } = await import("./layout-HFBK5UEG.js");
2893
2577
  const effectivePalette = await resolveExportPalette(theme, palette);
2894
2578
  const jmParsed = parseJourneyMap2(content, effectivePalette);
@@ -2907,31 +2591,66 @@ async function exportJourneyMap(ctx) {
2907
2591
  });
2908
2592
  return finalizeSvgExport(container, theme, effectivePalette);
2909
2593
  }
2910
- async function exportCycle(ctx) {
2594
+
2595
+ // src/kanban/export.ts
2596
+ async function exportKanban(ctx) {
2911
2597
  const { content, theme, palette, viewState, exportMode } = ctx;
2912
- const { parseCycle: parseCycle2 } = await import("./parser-X23GRTQJ.js");
2913
- const { renderCycleForExport } = await import("./renderer-3JY6JAQJ.js");
2598
+ const { parseKanban: parseKanban2 } = await import("./parser-KLCSFQ3J.js");
2599
+ const { renderKanban } = await import("./renderer-KJ3V5BYP.js");
2914
2600
  const effectivePalette = await resolveExportPalette(theme, palette);
2915
- const cycleParsed = parseCycle2(content);
2916
- if (cycleParsed.error || cycleParsed.nodes.length === 0) return "";
2917
- const container = createExportContainer(ctx.width, ctx.height);
2918
- renderCycleForExport(
2919
- container,
2920
- cycleParsed,
2921
- effectivePalette,
2922
- ctx.isDark,
2923
- { width: ctx.width, height: ctx.height },
2924
- viewState,
2601
+ const kanbanParsed = parseKanban2(content, effectivePalette);
2602
+ if (kanbanParsed.error || kanbanParsed.columns.length === 0) return "";
2603
+ const container = document.createElement("div");
2604
+ container.style.position = "absolute";
2605
+ container.style.left = "-9999px";
2606
+ document.body.appendChild(container);
2607
+ const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
2608
+ const sourceCollapsedColumns = kanbanParsed.columns.filter((c) => c.collapsed).map((c) => c.id);
2609
+ const kanbanCollapsedColumns = viewState?.cc || sourceCollapsedColumns.length > 0 ? /* @__PURE__ */ new Set([...sourceCollapsedColumns, ...viewState?.cc ?? []]) : void 0;
2610
+ renderKanban(container, kanbanParsed, effectivePalette, ctx.isDark, {
2611
+ activeTagGroup: resolveActiveTagGroup(
2612
+ kanbanParsed.tagGroups,
2613
+ kanbanParsed.options["active-tag"],
2614
+ ctxTagOverride(ctx)
2615
+ ),
2616
+ currentSwimlaneGroup: viewState?.swim ?? kanbanParsed.options["lane-by"] ?? null,
2617
+ ...kanbanCollapsedLanes !== void 0 && {
2618
+ collapsedLanes: kanbanCollapsedLanes
2619
+ },
2620
+ ...kanbanCollapsedColumns !== void 0 && {
2621
+ collapsedColumns: kanbanCollapsedColumns
2622
+ },
2623
+ ...viewState?.cm !== void 0 && { compactMeta: viewState.cm },
2925
2624
  exportMode
2926
- );
2625
+ });
2927
2626
  return finalizeSvgExport(container, theme, effectivePalette);
2928
2627
  }
2628
+
2629
+ // src/live-link/export.ts
2630
+ async function exportLiveLink(ctx) {
2631
+ const { content, theme, palette } = ctx;
2632
+ const { parseLiveLink: parseLiveLink2 } = await import("./parser-GMQJISZ6.js");
2633
+ const { renderLiveLinkCard } = await import("./renderer-4SE7R22T.js");
2634
+ const effectivePalette = await resolveExportPalette(theme, palette);
2635
+ return renderLiveLinkCard(parseLiveLink2(content), effectivePalette, theme);
2636
+ }
2637
+
2638
+ // src/map/export.ts
2639
+ async function resolveMapDataSource(source) {
2640
+ if (source === void 0) return {};
2641
+ if (typeof source !== "function") return { data: source };
2642
+ try {
2643
+ return { data: await source() };
2644
+ } catch (e) {
2645
+ return { reason: e instanceof Error ? e.message : String(e) };
2646
+ }
2647
+ }
2929
2648
  async function exportMap(ctx) {
2930
2649
  const { content, theme, palette, options } = ctx;
2931
2650
  const { parseMap: parseMap2 } = await import("./parser-EFF22GYO.js");
2932
2651
  const { resolveMap } = await import("./resolver-33K6MBCT.js");
2933
- const { renderMapForExport } = await import("./renderer-5VKPB4LL.js");
2934
- const { mapExportDimensions } = await import("./dimensions-SCNVYLRF.js");
2652
+ const { renderMapForExport } = await import("./renderer-BXD5KVSR.js");
2653
+ const { mapExportDimensions } = await import("./dimensions-DS6FWLRY.js");
2935
2654
  const effectivePalette = await resolveExportPalette(theme, palette);
2936
2655
  const mapParsed = parseMap2(content, effectivePalette);
2937
2656
  const { data: mapData, reason } = await resolveMapDataSource(
@@ -2962,166 +2681,376 @@ async function exportMap(ctx) {
2962
2681
  );
2963
2682
  return finalizeSvgExport(container, theme, effectivePalette);
2964
2683
  }
2965
- async function exportPyramid(ctx) {
2966
- const { content, theme, palette } = ctx;
2967
- const { parsePyramid: parsePyramid2 } = await import("./parser-VA7ZQQTA.js");
2968
- const { renderPyramidForExport } = await import("./renderer-XGG2UNXT.js");
2684
+
2685
+ // src/mindmap/export.ts
2686
+ async function exportMindmap(ctx) {
2687
+ const { content, theme, palette, viewState, exportMode } = ctx;
2688
+ const { parseMindmap: parseMindmap2 } = await import("./parser-KKF6Y5MM.js");
2689
+ const { layoutMindmap } = await import("./layout-RH3N46HP.js");
2690
+ const { collapseMindmapTree } = await import("./collapse-BSPH3BFM.js");
2691
+ const { renderMindmap } = await import("./renderer-PHLCYVSN.js");
2692
+ const isDark = ctx.isDark;
2969
2693
  const effectivePalette = await resolveExportPalette(theme, palette);
2970
- const pyramidParsed = parsePyramid2(content);
2971
- if (pyramidParsed.error || pyramidParsed.layers.length === 0) return "";
2972
- const container = createExportContainer(ctx.width, ctx.height);
2973
- renderPyramidForExport(
2694
+ const mmParsed = parseMindmap2(content, effectivePalette);
2695
+ if (mmParsed.error) return "";
2696
+ const collapsedNodes = new Set(viewState?.cg ?? []);
2697
+ const collectCollapsed = (nodes) => {
2698
+ for (const n of nodes) {
2699
+ if (n.collapsed) collapsedNodes.add(n.id);
2700
+ if (n.children.length) collectCollapsed(n.children);
2701
+ }
2702
+ };
2703
+ collectCollapsed(mmParsed.roots);
2704
+ const activeTagGroup = resolveActiveTagGroup(
2705
+ mmParsed.tagGroups,
2706
+ mmParsed.options["active-tag"],
2707
+ ctxTagOverride(ctx)
2708
+ );
2709
+ const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
2710
+ const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
2711
+ const effectiveParsed = { ...mmParsed, roots: effectiveRoots };
2712
+ const mmLayout = layoutMindmap(effectiveParsed, effectivePalette, {
2713
+ interactive: false,
2714
+ ...hiddenCounts.size > 0 && { hiddenCounts },
2715
+ activeTagGroup,
2716
+ hideDescriptions
2717
+ });
2718
+ const PADDING = 20;
2719
+ const titleOffset = effectiveParsed.title ? 30 : 0;
2720
+ const exportWidth = mmLayout.width + PADDING * 2;
2721
+ const exportHeight = mmLayout.height + PADDING * 2 + titleOffset;
2722
+ const container = createExportContainer(exportWidth, exportHeight);
2723
+ const colorByDepth = viewState?.cbd ?? mmParsed.options["color-by-depth"] === "on";
2724
+ renderMindmap(
2974
2725
  container,
2975
- pyramidParsed,
2726
+ effectiveParsed,
2727
+ mmLayout,
2976
2728
  effectivePalette,
2977
- ctx.isDark,
2978
- { width: ctx.width, height: ctx.height }
2729
+ isDark,
2730
+ void 0,
2731
+ { width: exportWidth, height: exportHeight },
2732
+ void 0,
2733
+ hideDescriptions,
2734
+ colorByDepth ? null : activeTagGroup,
2735
+ colorByDepth ? { colorByDepth: true, exportMode } : { exportMode }
2979
2736
  );
2980
2737
  return finalizeSvgExport(container, theme, effectivePalette);
2981
2738
  }
2982
- async function exportRing(ctx) {
2983
- const { content, theme, palette } = ctx;
2984
- const { parseRing: parseRing2 } = await import("./parser-SIKL4PLF.js");
2985
- const { renderRingForExport } = await import("./renderer-LQP3AGEJ.js");
2739
+
2740
+ // src/org/export.ts
2741
+ async function exportOrg(ctx) {
2742
+ const { content, theme, palette, viewState, exportMode } = ctx;
2743
+ const { parseOrg: parseOrg2, findOrgNodeIdByName } = await import("./parser-BJ7RJNUM.js");
2744
+ const { layoutOrg } = await import("./layout-MXHR3P35.js");
2745
+ const { collapseOrgTree, focusOrgTree, unionCollapsedOrgNodes } = await import("./collapse-VNYYXMUI.js");
2746
+ const { renderOrg, ancestorTrailReserve } = await import("./renderer-JJ4QW3SK.js");
2747
+ const isDark = ctx.isDark;
2986
2748
  const effectivePalette = await resolveExportPalette(theme, palette);
2987
- const ringParsed = parseRing2(content);
2988
- if (ringParsed.error || ringParsed.layers.length === 0) return "";
2989
- const container = createExportContainer(ctx.width, ctx.height);
2990
- renderRingForExport(container, ringParsed, effectivePalette, ctx.isDark, {
2991
- width: ctx.width,
2992
- height: ctx.height
2993
- });
2749
+ const orgParsed = parseOrg2(content, effectivePalette);
2750
+ if (orgParsed.error) return "";
2751
+ const collapsedNodes = unionCollapsedOrgNodes(orgParsed, viewState?.cg);
2752
+ const activeTagGroup = resolveActiveTagGroup(
2753
+ orgParsed.tagGroups,
2754
+ orgParsed.options["active-tag"],
2755
+ ctxTagOverride(ctx)
2756
+ );
2757
+ const hiddenAttributes = unionHiddenAttributes(
2758
+ orgParsed.options["hide"],
2759
+ viewState?.ha
2760
+ );
2761
+ const collapseResult = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
2762
+ const { hiddenCounts } = collapseResult;
2763
+ let effectiveParsed = collapseResult.parsed;
2764
+ let ancestorPath;
2765
+ const focusName = orgParsed.options["focus"];
2766
+ if (focusName) {
2767
+ const focusId = findOrgNodeIdByName(effectiveParsed.roots, focusName);
2768
+ const focused = focusId ? focusOrgTree(effectiveParsed, focusId) : null;
2769
+ if (focused) {
2770
+ effectiveParsed = focused.parsed;
2771
+ ancestorPath = focused.ancestorPath;
2772
+ }
2773
+ }
2774
+ const orgLayout = layoutOrg(
2775
+ effectiveParsed,
2776
+ hiddenCounts.size > 0 ? hiddenCounts : void 0,
2777
+ activeTagGroup,
2778
+ hiddenAttributes,
2779
+ false
2780
+ // expandAllLegend off — collapsed-by-default per §1.3
2781
+ );
2782
+ const PADDING = 20;
2783
+ const titleOffset = effectiveParsed.title ? 30 : 0;
2784
+ const rootIds = new Set(effectiveParsed.roots.map((r) => r.id));
2785
+ const rootTop = orgLayout.nodes.find((n) => rootIds.has(n.id))?.y ?? orgLayout.containers.find((c) => rootIds.has(c.nodeId))?.y ?? 0;
2786
+ const trailReserve = ancestorTrailReserve(ancestorPath?.length ?? 0, rootTop);
2787
+ const exportWidth = orgLayout.width + PADDING * 2;
2788
+ const exportHeight = orgLayout.height + PADDING * 2 + titleOffset + trailReserve;
2789
+ const container = createExportContainer(exportWidth, exportHeight);
2790
+ renderOrg(
2791
+ container,
2792
+ effectiveParsed,
2793
+ orgLayout,
2794
+ effectivePalette,
2795
+ isDark,
2796
+ void 0,
2797
+ { width: exportWidth, height: exportHeight },
2798
+ activeTagGroup,
2799
+ hiddenAttributes,
2800
+ ancestorPath,
2801
+ exportMode
2802
+ );
2994
2803
  return finalizeSvgExport(container, theme, effectivePalette);
2995
2804
  }
2996
- async function exportTreemap(ctx) {
2997
- const { content, theme, palette } = ctx;
2998
- const { parseTreemap: parseTreemap2 } = await import("./parser-LTRQASGB.js");
2805
+
2806
+ // src/pert/export.ts
2807
+ async function exportPert(ctx) {
2808
+ const { content, theme, palette, viewState } = ctx;
2809
+ const { parsePert: parsePert2 } = await import("./parser-2IKPQVKX.js");
2810
+ const { analyzePert } = await import("./analyzer-G7PGOT3V.js");
2811
+ const { layoutPert } = await import("./layout-KV27UW25.js");
2812
+ const { renderPert, measurePertAnalysisBlock } = await import("./renderer-MWZC7TMD.js");
2999
2813
  const effectivePalette = await resolveExportPalette(theme, palette);
3000
- const treemapParsed = parseTreemap2(content, effectivePalette);
3001
- if (treemapParsed.error || treemapParsed.roots.length === 0) return "";
3002
- const container = createExportContainer(ctx.width, ctx.height);
3003
- const dims = { width: ctx.width, height: ctx.height };
3004
- if (treemapParsed.options.radial) {
3005
- const { renderTreemapRadialForExport } = await import("./renderer-radial-WFUX3Z3C.js");
3006
- renderTreemapRadialForExport(
3007
- container,
3008
- treemapParsed,
3009
- effectivePalette,
3010
- ctx.isDark,
3011
- dims
3012
- );
3013
- } else {
3014
- const { renderTreemapForExport } = await import("./renderer-ISSDGK54.js");
3015
- renderTreemapForExport(
3016
- container,
3017
- treemapParsed,
3018
- effectivePalette,
3019
- ctx.isDark,
3020
- dims
3021
- );
3022
- }
2814
+ const pertParsed = parsePert2(content, { palette: effectivePalette });
2815
+ if (pertParsed.error || pertParsed.activities.length === 0) return "";
2816
+ const pertResolved = analyzePert(pertParsed);
2817
+ const pertLayout = layoutPert(pertResolved);
2818
+ const hasTitle = !!pertParsed.title && !pertParsed.options.noTitle;
2819
+ const hasSubtitle = pertResolved.projectSubtitle !== null;
2820
+ const titleHeight = hasTitle ? 80 : hasSubtitle ? 50 : 0;
2821
+ const PERT_PADDING = 20;
2822
+ const analysisOn = viewState?.an ?? !pertParsed.options.noAnalysis;
2823
+ const fieldLabelsOn = viewState?.fl === true;
2824
+ const exportW = pertLayout.width + PERT_PADDING * 2;
2825
+ const analysisMeasured = measurePertAnalysisBlock(
2826
+ pertResolved,
2827
+ exportW - 2 * PERT_PADDING,
2828
+ {
2829
+ showTornado: analysisOn,
2830
+ showScurve: analysisOn,
2831
+ showFieldLegend: fieldLabelsOn
2832
+ }
2833
+ );
2834
+ const exportH = pertLayout.height + PERT_PADDING * 2 + titleHeight + analysisMeasured.height;
2835
+ const container = createExportContainer(exportW, exportH);
2836
+ renderPert(
2837
+ container,
2838
+ pertResolved,
2839
+ pertLayout,
2840
+ effectivePalette,
2841
+ ctx.isDark,
2842
+ {
2843
+ title: hasTitle ? pertParsed.title : null,
2844
+ subtitle: pertResolved.projectSubtitle,
2845
+ exportDims: { width: exportW, height: exportH },
2846
+ showTornado: analysisOn,
2847
+ showScurve: analysisOn,
2848
+ showFieldLegend: fieldLabelsOn
2849
+ }
2850
+ );
3023
2851
  return finalizeSvgExport(container, theme, effectivePalette);
3024
2852
  }
3025
- async function exportLiveLink(ctx) {
2853
+
2854
+ // src/pyramid/export.ts
2855
+ async function exportPyramid(ctx) {
3026
2856
  const { content, theme, palette } = ctx;
3027
- const { parseLiveLink: parseLiveLink2 } = await import("./parser-GMQJISZ6.js");
3028
- const { renderLiveLinkCard } = await import("./renderer-TO2NVJP3.js");
2857
+ const { parsePyramid: parsePyramid2 } = await import("./parser-NUU3DVW4.js");
2858
+ const { renderPyramidForExport } = await import("./renderer-IXBOT6DU.js");
3029
2859
  const effectivePalette = await resolveExportPalette(theme, palette);
3030
- return renderLiveLinkCard(parseLiveLink2(content), effectivePalette, theme);
2860
+ const pyramidParsed = parsePyramid2(content);
2861
+ if (pyramidParsed.error || pyramidParsed.layers.length === 0) return "";
2862
+ const container = createExportContainer(ctx.width, ctx.height);
2863
+ renderPyramidForExport(
2864
+ container,
2865
+ pyramidParsed,
2866
+ effectivePalette,
2867
+ ctx.isDark,
2868
+ { width: ctx.width, height: ctx.height }
2869
+ );
2870
+ return finalizeSvgExport(container, theme, effectivePalette);
3031
2871
  }
3032
- async function exportBlock(ctx) {
2872
+
2873
+ // src/quadrant/export.ts
2874
+ async function exportQuadrant(ctx) {
3033
2875
  const { content, theme, palette } = ctx;
3034
- const { parseBlock: parseBlock2 } = await import("./parser-TUAEX6ML.js");
3035
- const { renderBlockForExport } = await import("./renderer-NDA2FFYB.js");
2876
+ const parsed = parseQuadrant(content, palette);
2877
+ if (parsed.error || parsed.quadrantPoints.length === 0) return "";
3036
2878
  const effectivePalette = await resolveExportPalette(theme, palette);
3037
- const blockParsed = parseBlock2(content, effectivePalette);
3038
- if (blockParsed.error || blockParsed.top.rows.length === 0) return "";
3039
- const container = createExportContainer(ctx.width, ctx.height);
3040
- renderBlockForExport(container, blockParsed, effectivePalette, ctx.isDark, {
3041
- width: ctx.width,
3042
- height: ctx.height
3043
- });
2879
+ const { container, dims } = beginVizExport(ctx);
2880
+ renderQuadrant(
2881
+ container,
2882
+ parsed,
2883
+ effectivePalette,
2884
+ ctx.isDark,
2885
+ void 0,
2886
+ dims
2887
+ );
3044
2888
  return finalizeSvgExport(container, theme, effectivePalette);
3045
2889
  }
3046
- async function exportGoal(ctx) {
2890
+
2891
+ // src/raci/export.ts
2892
+ async function exportRaci(ctx) {
3047
2893
  const { content, theme, palette } = ctx;
3048
- const { parseGoal: parseGoal2 } = await import("./parser-3QIFY5IN.js");
3049
- const { renderGoalForExport } = await import("./renderer-JEJT3YET.js");
2894
+ const { parseRaci: parseRaci2 } = await import("./parser-GDLKUBTO.js");
2895
+ const { renderRaciForExport } = await import("./renderer-W4MUMXJE.js");
3050
2896
  const effectivePalette = await resolveExportPalette(theme, palette);
3051
- const parsed = parseGoal2(content, effectivePalette);
3052
- if (parsed.error) return "";
2897
+ const raciParsed = parseRaci2(content, effectivePalette);
2898
+ if (raciParsed.error) return "";
3053
2899
  const container = createExportContainer(ctx.width, ctx.height);
3054
- renderGoalForExport(container, parsed, effectivePalette, ctx.isDark, {
2900
+ renderRaciForExport(container, raciParsed, effectivePalette, ctx.isDark, {
3055
2901
  width: ctx.width,
3056
2902
  height: ctx.height
3057
2903
  });
3058
2904
  return finalizeSvgExport(container, theme, effectivePalette);
3059
2905
  }
3060
- async function exportCountdown(ctx) {
2906
+
2907
+ // src/ring/export.ts
2908
+ async function exportRing(ctx) {
3061
2909
  const { content, theme, palette } = ctx;
3062
- const { parseCountdown: parseCountdown2 } = await import("./parser-SEUTXN6U.js");
3063
- const { renderCountdownForExport } = await import("./renderer-NHTR7IRR.js");
2910
+ const { parseRing: parseRing2 } = await import("./parser-KMKVFET2.js");
2911
+ const { renderRingForExport } = await import("./renderer-RUSQJFFC.js");
3064
2912
  const effectivePalette = await resolveExportPalette(theme, palette);
3065
- const parsed = parseCountdown2(content, effectivePalette);
3066
- if (parsed.error) return "";
2913
+ const ringParsed = parseRing2(content);
2914
+ if (ringParsed.error || ringParsed.layers.length === 0) return "";
3067
2915
  const container = createExportContainer(ctx.width, ctx.height);
3068
- renderCountdownForExport(container, parsed, effectivePalette, ctx.isDark, {
2916
+ renderRingForExport(container, ringParsed, effectivePalette, ctx.isDark, {
3069
2917
  width: ctx.width,
3070
2918
  height: ctx.height
3071
2919
  });
3072
2920
  return finalizeSvgExport(container, theme, effectivePalette);
3073
2921
  }
3074
- async function exportClock(ctx) {
3075
- const { content, theme, palette } = ctx;
3076
- const { parseClock: parseClock2 } = await import("./parser-K2DI26SC.js");
3077
- const { renderClockForExport } = await import("./renderer-WIU5BJUK.js");
2922
+
2923
+ // src/sequence/export.ts
2924
+ async function exportVisualization(ctx) {
2925
+ const { content, theme, palette, viewState } = ctx;
2926
+ const parsed = parseVisualization(content, palette);
2927
+ if (parsed.type !== "sequence") {
2928
+ if (parsed.error) {
2929
+ const looksLikeSequence = /->|~>|<-/.test(content);
2930
+ if (!looksLikeSequence) return "";
2931
+ } else {
2932
+ return "";
2933
+ }
2934
+ }
3078
2935
  const effectivePalette = await resolveExportPalette(theme, palette);
3079
- const parsed = parseClock2(content, effectivePalette);
3080
- if (parsed.error) return "";
2936
+ const isDark = ctx.isDark;
3081
2937
  const container = createExportContainer(ctx.width, ctx.height);
3082
- renderClockForExport(container, parsed, effectivePalette, ctx.isDark, {
3083
- width: ctx.width,
3084
- height: ctx.height
3085
- });
2938
+ const { parseSequenceDgmo: parseSequenceDgmo2 } = await import("./parser-BT4NI6JY.js");
2939
+ const { renderSequenceDiagram } = await import("./renderer-FMTLSOCX.js");
2940
+ const seqParsed = parseSequenceDgmo2(content, effectivePalette);
2941
+ if (seqParsed.error || seqParsed.participants.length === 0) return "";
2942
+ const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
2943
+ const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
2944
+ const seqActiveTagGroup = ctxTagOverride(ctx);
2945
+ renderSequenceDiagram(
2946
+ container,
2947
+ seqParsed,
2948
+ effectivePalette,
2949
+ isDark,
2950
+ void 0,
2951
+ {
2952
+ exportWidth: ctx.width,
2953
+ ...seqActiveTagGroup !== void 0 && {
2954
+ activeTagGroup: seqActiveTagGroup
2955
+ },
2956
+ ...collapsedSections !== void 0 && { collapsedSections },
2957
+ ...collapsedGroups !== void 0 && { collapsedGroups }
2958
+ }
2959
+ );
3086
2960
  return finalizeSvgExport(container, theme, effectivePalette);
3087
2961
  }
3088
- async function exportBracket(ctx) {
3089
- const { content, theme, palette } = ctx;
3090
- const { parseBracket: parseBracket2 } = await import("./parser-RTWPFKXG.js");
3091
- const { renderBracketForExport, bracketExportDimensions } = await import("./renderer-LM4ICK6I.js");
2962
+
2963
+ // src/sitemap/export.ts
2964
+ async function exportSitemap(ctx) {
2965
+ const { content, theme, palette, viewState, exportMode } = ctx;
2966
+ const { parseSitemap: parseSitemap2 } = await import("./parser-6TDWUKM5.js");
2967
+ const { layoutSitemap } = await import("./layout-ND26WVX2.js");
2968
+ const { collapseSitemapTree } = await import("./collapse-TCVK3RPO.js");
2969
+ const { renderSitemap } = await import("./renderer-XOWDERU2.js");
2970
+ const isDark = ctx.isDark;
3092
2971
  const effectivePalette = await resolveExportPalette(theme, palette);
3093
- const parsed = parseBracket2(content, effectivePalette);
3094
- if (parsed.error) return "";
3095
- const { width, height } = bracketExportDimensions(parsed, effectivePalette);
3096
- const container = createExportContainer(width, height);
3097
- renderBracketForExport(container, parsed, effectivePalette, ctx.isDark, {
3098
- width,
3099
- height
3100
- });
2972
+ const sitemapParsed = parseSitemap2(content, effectivePalette);
2973
+ if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
2974
+ const collapsedNodes = viewState?.cg ? new Set(viewState.cg) : void 0;
2975
+ const activeTagGroup = resolveActiveTagGroup(
2976
+ sitemapParsed.tagGroups,
2977
+ sitemapParsed.options["active-tag"],
2978
+ ctxTagOverride(ctx)
2979
+ );
2980
+ const hiddenAttributes = unionHiddenAttributes(
2981
+ sitemapParsed.options["hide"],
2982
+ viewState?.ha
2983
+ );
2984
+ const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
2985
+ const sitemapLayout = layoutSitemap(
2986
+ effectiveParsed,
2987
+ hiddenCounts.size > 0 ? hiddenCounts : void 0,
2988
+ activeTagGroup,
2989
+ hiddenAttributes,
2990
+ true
2991
+ );
2992
+ const PADDING = 20;
2993
+ const titleOffset = effectiveParsed.title ? 30 : 0;
2994
+ const {
2995
+ LEGEND_HEIGHT: SITEMAP_LEGEND_HEIGHT,
2996
+ LEGEND_GROUP_GAP: SITEMAP_LEGEND_GROUP_GAP
2997
+ } = await import("./legend-constants-55MFBXYF.js");
2998
+ const suppressedLegendBand = sitemapLayout.legend.length > 0 && effectiveParsed.options["no-legend"] === "on" ? SITEMAP_LEGEND_HEIGHT + SITEMAP_LEGEND_GROUP_GAP : 0;
2999
+ const exportWidth = sitemapLayout.width + PADDING * 2;
3000
+ const exportHeight = sitemapLayout.height + PADDING * 2 + titleOffset - suppressedLegendBand;
3001
+ const container = createExportContainer(exportWidth, exportHeight);
3002
+ renderSitemap(
3003
+ container,
3004
+ effectiveParsed,
3005
+ sitemapLayout,
3006
+ effectivePalette,
3007
+ isDark,
3008
+ void 0,
3009
+ { width: exportWidth, height: exportHeight },
3010
+ activeTagGroup,
3011
+ hiddenAttributes,
3012
+ exportMode
3013
+ );
3101
3014
  return finalizeSvgExport(container, theme, effectivePalette);
3102
3015
  }
3103
- async function exportRaci(ctx) {
3104
- const { content, theme, palette } = ctx;
3105
- const { parseRaci: parseRaci2 } = await import("./parser-BES6LV3G.js");
3106
- const { renderRaciForExport } = await import("./renderer-J5FKF423.js");
3107
- const effectivePalette = await resolveExportPalette(theme, palette);
3108
- const raciParsed = parseRaci2(content, effectivePalette);
3109
- if (raciParsed.error) return "";
3110
- const container = createExportContainer(ctx.width, ctx.height);
3111
- renderRaciForExport(container, raciParsed, effectivePalette, ctx.isDark, {
3112
- width: ctx.width,
3113
- height: ctx.height
3016
+
3017
+ // src/sketch/export.ts
3018
+ async function exportSketch(ctx) {
3019
+ const { content, theme, viewState, exportMode } = ctx;
3020
+ const { parseSketch: parseSketch2 } = await import("./parser-YHDGKPZI.js");
3021
+ const effectivePalette = await resolveExportPalette(theme, ctx.palette);
3022
+ const parsed = parseSketch2(content, effectivePalette);
3023
+ if (parsed.error || parsed.nodes.length === 0 && parsed.boxes.length === 0) {
3024
+ return "";
3025
+ }
3026
+ const { layoutSketch } = await import("./layout-D4L44NRG.js");
3027
+ const { renderSketchForExport } = await import("./renderer-WPN2SXGT.js");
3028
+ const collapsedBoxes = viewState?.cg ? new Set(viewState.cg) : void 0;
3029
+ const skLayout = layoutSketch(parsed, {
3030
+ ...collapsedBoxes !== void 0 && { collapsedBoxes }
3114
3031
  });
3032
+ const PADDING = 20;
3033
+ const titleOffset = parsed.title ? 40 : 0;
3034
+ const exportWidth = skLayout.width + PADDING * 2;
3035
+ const exportHeight = skLayout.height + PADDING * 2 + titleOffset;
3036
+ const container = createExportContainer(exportWidth, exportHeight);
3037
+ const skTagOverride = ctxTagOverride(ctx);
3038
+ renderSketchForExport(
3039
+ container,
3040
+ parsed,
3041
+ skLayout,
3042
+ effectivePalette,
3043
+ ctx.isDark,
3044
+ {
3045
+ ...skTagOverride !== void 0 && { activeTagGroup: skTagOverride },
3046
+ ...exportMode !== void 0 && { exportMode },
3047
+ ...viewState?.hd !== void 0 && { hideDescriptions: viewState.hd }
3048
+ }
3049
+ );
3115
3050
  return finalizeSvgExport(container, theme, effectivePalette);
3116
3051
  }
3117
- function beginVizExport(ctx) {
3118
- const container = createExportContainer(ctx.width, ctx.height);
3119
- const dims = {
3120
- width: ctx.width,
3121
- height: ctx.height
3122
- };
3123
- return { container, dims };
3124
- }
3052
+
3053
+ // src/slope/export.ts
3125
3054
  async function exportSlope(ctx) {
3126
3055
  const { content, theme, palette } = ctx;
3127
3056
  const parsed = parseSlope(content, palette);
@@ -3138,22 +3067,65 @@ async function exportSlope(ctx) {
3138
3067
  );
3139
3068
  return finalizeSvgExport(container, theme, effectivePalette);
3140
3069
  }
3141
- async function exportArc(ctx) {
3070
+
3071
+ // src/swimlane/export.ts
3072
+ async function exportSwimlane(ctx) {
3142
3073
  const { content, theme, palette } = ctx;
3143
- const parsed = parseArc(content, palette);
3144
- if (parsed.error || parsed.links.length === 0) return "";
3074
+ const { parseSwimlane: parseSwimlane2 } = await import("./parser-H5OODY5N.js");
3075
+ const { layoutSwimlane } = await import("./layout-VDCJOZSH.js");
3076
+ const { renderSwimlaneForExport } = await import("./renderer-DAZWHNLI.js");
3145
3077
  const effectivePalette = await resolveExportPalette(theme, palette);
3146
- const { container, dims } = beginVizExport(ctx);
3147
- renderArcDiagram(
3078
+ const swimParsed = parseSwimlane2(content, effectivePalette);
3079
+ if (swimParsed.error || swimParsed.nodes.length === 0) return "";
3080
+ const swimLayout = layoutSwimlane(swimParsed);
3081
+ const PADDING = 20;
3082
+ const titleOffset = swimParsed.title ? 40 : 0;
3083
+ const exportWidth = swimLayout.width + PADDING * 2;
3084
+ const exportHeight = swimLayout.height + PADDING * 2 + titleOffset;
3085
+ const container = createExportContainer(exportWidth, exportHeight);
3086
+ renderSwimlaneForExport(
3087
+ container,
3088
+ swimParsed,
3089
+ swimLayout,
3090
+ effectivePalette,
3091
+ ctx.isDark,
3092
+ {
3093
+ exportDims: { width: exportWidth, height: exportHeight },
3094
+ activeTagGroup: resolveActiveTagGroup(
3095
+ swimParsed.tagGroups,
3096
+ swimParsed.options["active-tag"],
3097
+ ctxTagOverride(ctx)
3098
+ ),
3099
+ exportMode: ctx.exportMode
3100
+ }
3101
+ );
3102
+ return finalizeSvgExport(container, theme, effectivePalette);
3103
+ }
3104
+
3105
+ // src/tech-radar/export.ts
3106
+ async function exportTechRadar(ctx) {
3107
+ const { content, theme, palette, viewState, exportMode } = ctx;
3108
+ const { parseTechRadar: parseTechRadar2 } = await import("./parser-3LT3ZUUC.js");
3109
+ const { renderTechRadarForExport } = await import("./renderer-TINHO2IO.js");
3110
+ const effectivePalette = await resolveExportPalette(theme, palette);
3111
+ const radarParsed = parseTechRadar2(content);
3112
+ if (radarParsed.error || radarParsed.quadrants.length === 0) return "";
3113
+ const RADAR_EXPORT_W = ctx.requestedWidth ?? 1300;
3114
+ const RADAR_EXPORT_H = ctx.requestedHeight ?? 1500;
3115
+ const container = createExportContainer(RADAR_EXPORT_W, RADAR_EXPORT_H);
3116
+ renderTechRadarForExport(
3148
3117
  container,
3149
- parsed,
3118
+ radarParsed,
3150
3119
  effectivePalette,
3151
3120
  ctx.isDark,
3152
- void 0,
3153
- dims
3121
+ { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
3122
+ viewState,
3123
+ exportMode
3154
3124
  );
3155
3125
  return finalizeSvgExport(container, theme, effectivePalette);
3156
3126
  }
3127
+
3128
+ // src/timeline/export.ts
3157
3129
  async function exportTimeline(ctx) {
3158
3130
  const { content, theme, palette, viewState, exportMode } = ctx;
3159
3131
  const parsed = parseTimeline(content, palette);
@@ -3181,21 +3153,39 @@ async function exportTimeline(ctx) {
3181
3153
  );
3182
3154
  return finalizeSvgExport(container, theme, effectivePalette);
3183
3155
  }
3184
- async function exportWordcloud(ctx) {
3156
+
3157
+ // src/treemap/export.ts
3158
+ async function exportTreemap(ctx) {
3185
3159
  const { content, theme, palette } = ctx;
3186
- const parsed = parseWordcloud(content, palette);
3187
- if (parsed.error || parsed.words.length === 0) return "";
3160
+ const { parseTreemap: parseTreemap2 } = await import("./parser-LTRQASGB.js");
3188
3161
  const effectivePalette = await resolveExportPalette(theme, palette);
3189
- const { container, dims } = beginVizExport(ctx);
3190
- await renderWordCloudAsync(
3191
- container,
3192
- parsed,
3193
- effectivePalette,
3194
- ctx.isDark,
3195
- dims
3196
- );
3162
+ const treemapParsed = parseTreemap2(content, effectivePalette);
3163
+ if (treemapParsed.error || treemapParsed.roots.length === 0) return "";
3164
+ const container = createExportContainer(ctx.width, ctx.height);
3165
+ const dims = { width: ctx.width, height: ctx.height };
3166
+ if (treemapParsed.options.radial) {
3167
+ const { renderTreemapRadialForExport } = await import("./renderer-radial-WFUX3Z3C.js");
3168
+ renderTreemapRadialForExport(
3169
+ container,
3170
+ treemapParsed,
3171
+ effectivePalette,
3172
+ ctx.isDark,
3173
+ dims
3174
+ );
3175
+ } else {
3176
+ const { renderTreemapForExport } = await import("./renderer-O6RP52CQ.js");
3177
+ renderTreemapForExport(
3178
+ container,
3179
+ treemapParsed,
3180
+ effectivePalette,
3181
+ ctx.isDark,
3182
+ dims
3183
+ );
3184
+ }
3197
3185
  return finalizeSvgExport(container, theme, effectivePalette);
3198
3186
  }
3187
+
3188
+ // src/venn/export.ts
3199
3189
  async function exportVenn(ctx) {
3200
3190
  const { content, theme, palette } = ctx;
3201
3191
  const parsed = parseVenn(content, palette);
@@ -3205,61 +3195,190 @@ async function exportVenn(ctx) {
3205
3195
  renderVenn(container, parsed, effectivePalette, ctx.isDark, void 0, dims);
3206
3196
  return finalizeSvgExport(container, theme, effectivePalette);
3207
3197
  }
3208
- async function exportQuadrant(ctx) {
3198
+
3199
+ // src/version-control/export.ts
3200
+ async function exportVersionControl(ctx) {
3209
3201
  const { content, theme, palette } = ctx;
3210
- const parsed = parseQuadrant(content, palette);
3211
- if (parsed.error || parsed.quadrantPoints.length === 0) return "";
3202
+ const { parseVersionControl: parseVersionControl2 } = await import("./parser-5UZV32ZI.js");
3203
+ const { renderVersionControlForExport } = await import("./renderer-NOYLNVTI.js");
3212
3204
  const effectivePalette = await resolveExportPalette(theme, palette);
3213
- const { container, dims } = beginVizExport(ctx);
3214
- renderQuadrant(
3205
+ const parsed = parseVersionControl2(content, effectivePalette);
3206
+ if (parsed.error || parsed.nodes.length === 0) return "";
3207
+ const container = createExportContainer(ctx.width, ctx.height);
3208
+ renderVersionControlForExport(
3215
3209
  container,
3216
3210
  parsed,
3217
3211
  effectivePalette,
3218
3212
  ctx.isDark,
3219
- void 0,
3220
- dims
3213
+ {
3214
+ width: ctx.width,
3215
+ height: ctx.height
3216
+ }
3221
3217
  );
3222
3218
  return finalizeSvgExport(container, theme, effectivePalette);
3223
3219
  }
3224
- async function exportVisualization(ctx) {
3225
- const { content, theme, palette, viewState } = ctx;
3226
- const parsed = parseVisualization(content, palette);
3227
- if (parsed.type !== "sequence") {
3228
- if (parsed.error) {
3229
- const looksLikeSequence = /->|~>|<-/.test(content);
3230
- if (!looksLikeSequence) return "";
3231
- } else {
3232
- return "";
3233
- }
3234
- }
3220
+
3221
+ // src/whiteboard/export.ts
3222
+ async function exportWhiteboard(ctx) {
3223
+ const { content, theme } = ctx;
3224
+ const { parseWhiteboard: parseWhiteboard2 } = await import("./parser-R3LPEQIT.js");
3225
+ const effectivePalette = await resolveExportPalette(theme, ctx.palette);
3226
+ const parsed = parseWhiteboard2(content, effectivePalette);
3227
+ if (parsed.error || parsed.elements.length === 0) return "";
3228
+ const { renderWhiteboard } = await import("./renderer-M5DUMITP.js");
3229
+ const container = createExportContainer(1, 1);
3230
+ const resolveImage = ctx.options?.resolveImage;
3231
+ const showNotes = ctx.options?.showNotes;
3232
+ renderWhiteboard(container, parsed, effectivePalette, ctx.isDark, {
3233
+ ...resolveImage !== void 0 && { resolveImage },
3234
+ ...showNotes !== void 0 && { showNotes }
3235
+ });
3236
+ return finalizeSvgExport(container, theme, effectivePalette);
3237
+ }
3238
+
3239
+ // src/wireframe/export.ts
3240
+ async function exportWireframe(ctx) {
3241
+ const { content, theme, palette } = ctx;
3242
+ const { parseWireframe: parseWireframe2 } = await import("./parser-4TBSWULQ.js");
3243
+ const { layoutWireframe } = await import("./layout-HF3PMI4H.js");
3244
+ const { renderWireframe } = await import("./renderer-36446HSH.js");
3235
3245
  const effectivePalette = await resolveExportPalette(theme, palette);
3236
- const isDark = ctx.isDark;
3237
- const container = createExportContainer(ctx.width, ctx.height);
3238
- const { parseSequenceDgmo: parseSequenceDgmo2 } = await import("./parser-LCJ76UXS.js");
3239
- const { renderSequenceDiagram } = await import("./renderer-3QAFDXEY.js");
3240
- const seqParsed = parseSequenceDgmo2(content, effectivePalette);
3241
- if (seqParsed.error || seqParsed.participants.length === 0) return "";
3242
- const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
3243
- const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
3244
- const seqActiveTagGroup = ctxTagOverride(ctx);
3245
- renderSequenceDiagram(
3246
+ const wireframeParsed = parseWireframe2(content);
3247
+ if (wireframeParsed.error || wireframeParsed.roots.length === 0 && wireframeParsed.modals.length === 0)
3248
+ return "";
3249
+ const wireframeLayout = layoutWireframe(wireframeParsed);
3250
+ const exportWidth = wireframeLayout.width;
3251
+ const exportHeight = wireframeLayout.height;
3252
+ const container = createExportContainer(exportWidth, exportHeight);
3253
+ renderWireframe(
3246
3254
  container,
3247
- seqParsed,
3255
+ wireframeParsed,
3256
+ wireframeLayout,
3248
3257
  effectivePalette,
3249
- isDark,
3258
+ ctx.isDark,
3250
3259
  void 0,
3251
- {
3252
- exportWidth: ctx.width,
3253
- ...seqActiveTagGroup !== void 0 && {
3254
- activeTagGroup: seqActiveTagGroup
3255
- },
3256
- ...collapsedSections !== void 0 && { collapsedSections },
3257
- ...collapsedGroups !== void 0 && { collapsedGroups }
3258
- }
3260
+ { width: exportWidth, height: exportHeight },
3261
+ theme
3262
+ );
3263
+ return finalizeSvgExport(container, theme, effectivePalette);
3264
+ }
3265
+
3266
+ // src/wordcloud/export.ts
3267
+ async function exportWordcloud(ctx) {
3268
+ const { content, theme, palette } = ctx;
3269
+ const parsed = parseWordcloud(content, palette);
3270
+ if (parsed.error || parsed.words.length === 0) return "";
3271
+ const effectivePalette = await resolveExportPalette(theme, palette);
3272
+ const { container, dims } = beginVizExport(ctx);
3273
+ await renderWordCloudAsync(
3274
+ container,
3275
+ parsed,
3276
+ effectivePalette,
3277
+ ctx.isDark,
3278
+ dims
3259
3279
  );
3260
3280
  return finalizeSvgExport(container, theme, effectivePalette);
3261
3281
  }
3262
3282
 
3283
+ // src/d3.ts
3284
+ var DIAGRAM_EXPORT_HANDLERS = {
3285
+ org: exportOrg,
3286
+ sitemap: exportSitemap,
3287
+ kanban: exportKanban,
3288
+ class: exportClass,
3289
+ er: exportEr,
3290
+ "boxes-and-lines": exportBoxesAndLines,
3291
+ sketch: exportSketch,
3292
+ whiteboard: exportWhiteboard,
3293
+ swimlane: exportSwimlane,
3294
+ family: exportFamily,
3295
+ "version-control": exportVersionControl,
3296
+ mindmap: exportMindmap,
3297
+ wireframe: exportWireframe,
3298
+ c4: exportC4,
3299
+ flowchart: exportFlowchart,
3300
+ infra: exportInfra,
3301
+ pert: exportPert,
3302
+ gantt: exportGantt,
3303
+ state: exportState,
3304
+ "tech-radar": exportTechRadar,
3305
+ "journey-map": exportJourneyMap,
3306
+ cycle: exportCycle,
3307
+ map: exportMap,
3308
+ pyramid: exportPyramid,
3309
+ ring: exportRing,
3310
+ treemap: exportTreemap,
3311
+ block: exportBlock,
3312
+ goal: exportGoal,
3313
+ countdown: exportCountdown,
3314
+ clock: exportClock,
3315
+ bracket: exportBracket,
3316
+ raci: exportRaci,
3317
+ body: exportBody,
3318
+ "live-link": exportLiveLink,
3319
+ // D3 visualizations — own handler per type (Story 109.2). Only `sequence`
3320
+ // still falls through to exportVisualization (no chart-type of its own).
3321
+ slope: exportSlope,
3322
+ arc: exportArc,
3323
+ timeline: exportTimeline,
3324
+ "event-line": exportEventLine,
3325
+ wordcloud: exportWordcloud,
3326
+ venn: exportVenn,
3327
+ quadrant: exportQuadrant
3328
+ };
3329
+ function resolveArcChordOverride(content, detectedType, palette) {
3330
+ if (detectedType === "arc") {
3331
+ const p = parseArc(content, palette);
3332
+ if (!p.error && p.layout === "chord" && p.links.length) {
3333
+ const emitted = [
3334
+ `chord ${p.title ?? ""}`.trimEnd(),
3335
+ // §1.9 fill family survives the layout re-emit so the circular
3336
+ // renderer honors fill-solid / fill-outline authored on the arc.
3337
+ ...p.fillMode ? [`fill-${p.fillMode}`] : [],
3338
+ ...p.links.map((l) => `${l.source} -> ${l.target} ${l.value}`)
3339
+ ].join("\n");
3340
+ return { content: emitted, type: "chord" };
3341
+ }
3342
+ }
3343
+ return null;
3344
+ }
3345
+ async function renderForExport(content, theme, palette, viewState, options) {
3346
+ const exportMode = options?.exportMode ?? false;
3347
+ const { parseDgmoChartType } = await import("./dgmo-router-E5O7RJSW.js");
3348
+ const detectedTypeRaw = parseDgmoChartType(content);
3349
+ const override = resolveArcChordOverride(content, detectedTypeRaw, palette);
3350
+ const renderContent = override?.content ?? content;
3351
+ const detectedType = override?.type ?? detectedTypeRaw;
3352
+ if (detectedType !== null) {
3353
+ const { supportsD3DataChart, renderDataChartD3: renderDataChartD32 } = await import("./charts-d3-SZV2SFTM.js");
3354
+ if (supportsD3DataChart(detectedType)) {
3355
+ return renderDataChartD32(renderContent, theme, palette, {
3356
+ ...options?.width !== void 0 && { width: options.width },
3357
+ ...options?.height !== void 0 && { height: options.height }
3358
+ });
3359
+ }
3360
+ }
3361
+ const ctx = {
3362
+ content: renderContent,
3363
+ theme,
3364
+ palette,
3365
+ viewState,
3366
+ options,
3367
+ exportMode,
3368
+ isDark: theme === "dark",
3369
+ // The resolved canvas, and the raw request beside it. A chart whose own
3370
+ // natural size is not EXPORT_WIDTH (tech-radar) needs to tell "the caller
3371
+ // asked for 1200" apart from "nobody asked", which the resolved value
3372
+ // cannot express.
3373
+ requestedWidth: options?.width && options.width > 0 ? Math.round(options.width) : void 0,
3374
+ requestedHeight: options?.height && options.height > 0 ? Math.round(options.height) : void 0,
3375
+ width: options?.width && options.width > 0 ? Math.round(options.width) : EXPORT_WIDTH,
3376
+ height: options?.height && options.height > 0 ? Math.round(options.height) : EXPORT_HEIGHT
3377
+ };
3378
+ const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
3379
+ return (handler ?? exportVisualization)(ctx);
3380
+ }
3381
+
3263
3382
  // src/utils/hover-styles.ts
3264
3383
  var MAX_HOVER_GROUPS = 40;
3265
3384
  var DEFAULT_DIM_OPACITY = 0.4;
@@ -3592,6 +3711,15 @@ var HOVER_SPECS = {
3592
3711
  legend: true,
3593
3712
  emphasis: "dim"
3594
3713
  },
3714
+ // A whiteboard's legend names its colours (#1344); each coloured element
3715
+ // carries its colour's entry.
3716
+ whiteboard: {
3717
+ markSelector: "[data-wb-meaning]",
3718
+ strategy: "enumerated",
3719
+ groupAttr: "data-wb-meaning",
3720
+ emphasis: "dim",
3721
+ legend: true
3722
+ },
3595
3723
  sketch: {
3596
3724
  markSelector: ".sk-node",
3597
3725
  strategy: "connection",
@@ -4869,12 +4997,12 @@ async function render(source, options) {
4869
4997
  }
4870
4998
 
4871
4999
  export {
4872
- renderSlopeChart,
4873
5000
  orderArcNodes,
4874
5001
  renderArcDiagram,
4875
- renderWordCloud,
4876
- renderVenn,
4877
5002
  renderQuadrant,
5003
+ renderSlopeChart,
5004
+ renderVenn,
5005
+ renderWordCloud,
4878
5006
  renderForExport,
4879
5007
  DEFAULT_DIM_OPACITY,
4880
5008
  render