@diagrammo/dgmo 0.48.2 → 0.50.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 (244) hide show
  1. package/.cursorrules +12 -3
  2. package/.github/copilot-instructions.md +12 -3
  3. package/.windsurfrules +12 -3
  4. package/SKILL.md +12 -3
  5. package/dist/advanced.d.ts +851 -1
  6. package/dist/advanced.js +601 -177
  7. package/dist/auto.css +63 -0
  8. package/dist/auto.js +220 -110
  9. package/dist/auto.mjs +45920 -33167
  10. package/dist/block.css +63 -0
  11. package/dist/block.d.ts +13 -5
  12. package/dist/block.js +239 -62
  13. package/dist/{charts-d3-QOZHKTCT.js → charts-d3-DKUD7MQE.js} +9 -8
  14. package/dist/{chunk-3MQQNXPD.js → chunk-2C56LEML.js} +3 -3
  15. package/dist/{chunk-L66GEFB6.js → chunk-2JJ2KNPI.js} +4 -4
  16. package/dist/{chunk-6SM5NJQH.js → chunk-2QYTMIRR.js} +4 -4
  17. package/dist/{chunk-RJE5JTVK.js → chunk-3G6TMNVF.js} +7 -7
  18. package/dist/{chunk-MO5YHHMQ.js → chunk-3LKM6FMW.js} +2 -2
  19. package/dist/{chunk-76OWZ3BA.js → chunk-3P2DKJCG.js} +2 -2
  20. package/dist/chunk-3TDRHT4J.js +1043 -0
  21. package/dist/chunk-3XPUDB7B.js +420 -0
  22. package/dist/{chunk-2LACINWN.js → chunk-3ZPC476N.js} +7 -7
  23. package/dist/{chunk-G2H74XJZ.js → chunk-4BMJQFNK.js} +8 -8
  24. package/dist/{chunk-THXCFRTU.js → chunk-4S723I74.js} +9 -9
  25. package/dist/{chunk-6NYWYNBI.js → chunk-4XK3FLFB.js} +3 -3
  26. package/dist/chunk-5JSWBPZZ.js +607 -0
  27. package/dist/{chunk-WCT3NYCV.js → chunk-5LWQH6QV.js} +1 -1
  28. package/dist/{chunk-7DTWEFUE.js → chunk-5Z736BSU.js} +8 -8
  29. package/dist/{chunk-ERLBPDGU.js → chunk-6BFZKBGD.js} +2 -2
  30. package/dist/{chunk-6HO5ZTXK.js → chunk-6IAA2QQ2.js} +2 -2
  31. package/dist/{chunk-H6PI3KJT.js → chunk-6IUAK7RZ.js} +2 -2
  32. package/dist/{chunk-RTLBSQ3G.js → chunk-6M5PJFQG.js} +3 -3
  33. package/dist/{chunk-Z5CGSFJB.js → chunk-6X4AKWLF.js} +4 -4
  34. package/dist/{chunk-OJR3MQYV.js → chunk-6ZT4U37Q.js} +291 -52
  35. package/dist/{chunk-5QXL4NYS.js → chunk-7G2ER4CY.js} +10 -5
  36. package/dist/chunk-7PQYTZMA.js +681 -0
  37. package/dist/{chunk-OURHMODR.js → chunk-7RB5Q6MB.js} +11 -11
  38. package/dist/{chunk-YAA6IID3.js → chunk-7UEMZREK.js} +2 -2
  39. package/dist/{chunk-AN2MOO5T.js → chunk-A3WQVFBS.js} +3 -3
  40. package/dist/{chunk-VOKGH627.js → chunk-AHGF2YYA.js} +13 -3
  41. package/dist/{chunk-CI2G3OEB.js → chunk-ASS5YYIZ.js} +12 -12
  42. package/dist/{chunk-TYS6CSS5.js → chunk-BCYVE3N5.js} +2 -2
  43. package/dist/chunk-BDXC7MDK.js +2395 -0
  44. package/dist/chunk-BTUTZMPT.js +482 -0
  45. package/dist/{chunk-YGJTDXVR.js → chunk-C4GQDB5L.js} +2 -2
  46. package/dist/{chunk-Y2ZE3MSG.js → chunk-C63UWAKM.js} +2 -2
  47. package/dist/{chunk-725LDASD.js → chunk-CLD2OKV7.js} +2 -2
  48. package/dist/{chunk-WBRZJOEC.js → chunk-CMCR5WBW.js} +3 -3
  49. package/dist/{chunk-IZMRON6C.js → chunk-CVV36CWI.js} +2 -2
  50. package/dist/chunk-DMJJ3CEQ.js +254 -0
  51. package/dist/{chunk-MZSBFAPJ.js → chunk-EJTKV7CY.js} +9 -9
  52. package/dist/{chunk-FITOLXSX.js → chunk-EOFPYPAU.js} +26 -2
  53. package/dist/{chunk-X5LCYCL5.js → chunk-ER5SCQWQ.js} +5 -5
  54. package/dist/{chunk-4OHACBGX.js → chunk-F2L5GCQ4.js} +2 -2
  55. package/dist/{chunk-FCIIHT24.js → chunk-F5UDBSJY.js} +7 -7
  56. package/dist/{chunk-3JLKYGBQ.js → chunk-F7BFXWNQ.js} +2 -2
  57. package/dist/{chunk-FANX6UJR.js → chunk-FAL7NOK3.js} +11 -11
  58. package/dist/{chunk-BUPDIHML.js → chunk-FB2WHOEQ.js} +4 -4
  59. package/dist/chunk-FL4IOQ37.js +379 -0
  60. package/dist/{chunk-LIOHW36Z.js → chunk-GBE54KY6.js} +6 -5
  61. package/dist/{chunk-RUNNI6VE.js → chunk-GL5UZ6NY.js} +3 -3
  62. package/dist/{chunk-4GRI7MEB.js → chunk-GPWHQ5FJ.js} +6 -6
  63. package/dist/chunk-GROQGHCT.js +960 -0
  64. package/dist/{chunk-SGZYXELT.js → chunk-H73FP42Q.js} +9 -6
  65. package/dist/{chunk-PBAHEPLQ.js → chunk-HGN652MU.js} +7 -6
  66. package/dist/{chunk-3SMGIJDD.js → chunk-HHXOONLX.js} +13 -1
  67. package/dist/{chunk-QOOUC2J5.js → chunk-HQCHYB5F.js} +2 -2
  68. package/dist/{chunk-AELOBFW5.js → chunk-I4IFRCUB.js} +3 -3
  69. package/dist/{chunk-GMR5MFHJ.js → chunk-I7F2A35O.js} +2 -2
  70. package/dist/{chunk-T2OE3YQB.js → chunk-IA3FJYWR.js} +3 -3
  71. package/dist/chunk-IAEC4KLA.js +492 -0
  72. package/dist/chunk-IVKC4GID.js +32 -0
  73. package/dist/{chunk-HSH73DP6.js → chunk-JFYIP7XU.js} +139 -39
  74. package/dist/{chunk-CKNVO24D.js → chunk-JZNMXEIV.js} +2 -2
  75. package/dist/chunk-KJXIHOVL.js +940 -0
  76. package/dist/chunk-L2ZTFHAY.js +331 -0
  77. package/dist/{chunk-B7JUCQUA.js → chunk-M5RXSRO2.js} +4 -4
  78. package/dist/{chunk-ULOEATBZ.js → chunk-NFFIKVNO.js} +106 -29
  79. package/dist/{chunk-PZMETGLY.js → chunk-NISQC5KW.js} +358 -122
  80. package/dist/chunk-O4VUFXIH.js +574 -0
  81. package/dist/{chunk-RVMRZAPB.js → chunk-O76UD6VJ.js} +3 -3
  82. package/dist/{chunk-JM4LEPY3.js → chunk-O7NUCTFU.js} +2 -2
  83. package/dist/{chunk-JED6ZDEE.js → chunk-O7WP4ZD4.js} +2 -2
  84. package/dist/{chunk-EVCVDTXE.js → chunk-OHQEMBLI.js} +3 -3
  85. package/dist/{chunk-ZBWMSD5N.js → chunk-ONA5KPV3.js} +4 -4
  86. package/dist/{chunk-Y3J7VR52.js → chunk-OTAL43EH.js} +34 -2
  87. package/dist/{chunk-WCSOJFU3.js → chunk-OTP5KVLO.js} +3 -3
  88. package/dist/{chunk-RYEGFMUO.js → chunk-PGEGPSCZ.js} +2 -2
  89. package/dist/{chunk-ZO27FGJS.js → chunk-PH5X4GIP.js} +2 -2
  90. package/dist/{chunk-ZJEL4H2R.js → chunk-PJUGRJHJ.js} +9 -9
  91. package/dist/{chunk-ZGR7ULFJ.js → chunk-PLZI6KPM.js} +26 -14
  92. package/dist/{chunk-3R5POCTZ.js → chunk-PMN3Q7RJ.js} +6 -6
  93. package/dist/{chunk-YGJZWZTY.js → chunk-PZFVK3IH.js} +2 -2
  94. package/dist/chunk-Q4V4XQWV.js +158 -0
  95. package/dist/chunk-QDFNCATJ.js +325 -0
  96. package/dist/{chunk-QEBUY5NY.js → chunk-QFQ35BTO.js} +4 -4
  97. package/dist/chunk-QQJUTGQH.js +135 -0
  98. package/dist/{chunk-PWIHZ6NJ.js → chunk-QUJGFNFN.js} +4 -4
  99. package/dist/{chunk-J5FIXQRT.js → chunk-R56LASIN.js} +4 -4
  100. package/dist/{chunk-SRTU6HMQ.js → chunk-RV26LTUU.js} +8 -8
  101. package/dist/chunk-RV6A2B5S.js +354 -0
  102. package/dist/{chunk-EB6FPQUP.js → chunk-S5GRONXL.js} +3 -3
  103. package/dist/{chunk-5MLYVTYM.js → chunk-SCHXJTZL.js} +2 -2
  104. package/dist/{chunk-T3UBLACL.js → chunk-SCQ5VPUF.js} +19 -5
  105. package/dist/{chunk-PBHE5UG6.js → chunk-SGFZZAUO.js} +46 -12
  106. package/dist/{chunk-BUVJFRPY.js → chunk-SU2VBK4L.js} +4 -4
  107. package/dist/{chunk-SML67VPV.js → chunk-T4BRO3Z6.js} +3 -3
  108. package/dist/{chunk-NURGB5Z4.js → chunk-TSA237IM.js} +2 -2
  109. package/dist/chunk-TWCLK2GA.js +273 -0
  110. package/dist/{chunk-IJZFN3HM.js → chunk-TWQUYVP3.js} +4 -4
  111. package/dist/{chunk-DKEPS5LH.js → chunk-U2HGPLZS.js} +36 -2
  112. package/dist/{chunk-B65G5DNL.js → chunk-URSQLCTR.js} +3 -3
  113. package/dist/{chunk-ILKORZ3H.js → chunk-UUS2GIJZ.js} +7 -7
  114. package/dist/{chunk-XAKEUMEJ.js → chunk-UXOSVCLJ.js} +4 -4
  115. package/dist/{chunk-QLCCJEJI.js → chunk-WCUHAQKB.js} +2 -2
  116. package/dist/{chunk-WVAJFAIA.js → chunk-WWN566I7.js} +257 -101
  117. package/dist/{chunk-IPWPWM6R.js → chunk-X2NMFTG3.js} +111 -10
  118. package/dist/{chunk-UWN33VNX.js → chunk-XCLHOZAM.js} +10 -10
  119. package/dist/{chunk-P235SVAH.js → chunk-XDXNDSKO.js} +2 -2
  120. package/dist/chunk-XHVV4NVW.js +51 -0
  121. package/dist/{chunk-33LCUH4H.js → chunk-XMVQ5F4M.js} +4 -4
  122. package/dist/{chunk-MITI6NOL.js → chunk-YA7D2N2O.js} +11 -11
  123. package/dist/{chunk-7JEGTRLD.js → chunk-YB3FW24O.js} +3 -3
  124. package/dist/chunk-YNRDWYZP.js +295 -0
  125. package/dist/{chunk-44CM72SQ.js → chunk-YZLMKW4T.js} +3 -3
  126. package/dist/chunk-Z6XJQ6KO.js +452 -0
  127. package/dist/cli.cjs +179 -133
  128. package/dist/clock.d.ts +10 -0
  129. package/dist/clock.js +235 -0
  130. package/dist/countdown.d.ts +12 -0
  131. package/dist/countdown.js +450 -0
  132. package/dist/dgmo-router-QOMFCOOV.js +84 -0
  133. package/dist/dimensions-P5FSX2QN.js +22 -0
  134. package/dist/editor.js +43 -2
  135. package/dist/element.js +220 -110
  136. package/dist/element.mjs +45920 -33167
  137. package/dist/{flowchart-parser-ZNSKIBVR.js → flowchart-parser-OE4WHLTF.js} +4 -4
  138. package/dist/{flowchart-renderer-SNDSII4D.js → flowchart-renderer-GPCSMTOR.js} +8 -8
  139. package/dist/highlight.d.ts +17 -1
  140. package/dist/highlight.js +163 -6
  141. package/dist/index.d.ts +5 -1
  142. package/dist/index.js +199 -233
  143. package/dist/{layout-HRBZPFCN.js → layout-34CLNICW.js} +5 -5
  144. package/dist/layout-46366KBT.js +10 -0
  145. package/dist/{layout-HWDSRX5E.js → layout-5BG6DFCU.js} +6 -6
  146. package/dist/layout-6RDJHS6E.js +9 -0
  147. package/dist/{layout-7QXCNOVS.js → layout-7H4H46CS.js} +3 -3
  148. package/dist/{layout-46N4JJYK.js → layout-7SKMFYHL.js} +5 -5
  149. package/dist/{layout-EE6GE46Y.js → layout-DC4TVXER.js} +5 -5
  150. package/dist/layout-FGQWRYYW.js +14 -0
  151. package/dist/{layout-S2QBYSK2.js → layout-NXG63LWH.js} +3 -3
  152. package/dist/{layout-N76NR4ZO.js → layout-PUQCB6AN.js} +7 -7
  153. package/dist/{layout-HYZPWAK5.js → layout-QNUQLEHU.js} +3 -3
  154. package/dist/{layout-UM5F4DJB.js → layout-T3OZU5QW.js} +5 -5
  155. package/dist/layout-TJIWF7EM.js +14 -0
  156. package/dist/layout-VYELEAW7.js +14 -0
  157. package/dist/{layout-5RA6QWR6.js → layout-WZHNDT2O.js} +6 -6
  158. package/dist/{layout-search-OYV77HHS.js → layout-search-QS44PI5R.js} +6 -6
  159. package/dist/{parser-6BTRLKRZ.js → parser-45DENHFZ.js} +4 -4
  160. package/dist/{parser-TFZFBVWN.js → parser-4AI5ZN5F.js} +3 -3
  161. package/dist/{parser-XDVELHRK.js → parser-4P36TUVD.js} +4 -4
  162. package/dist/{parser-ZSDPZYQP.js → parser-5AURY4A6.js} +4 -4
  163. package/dist/{parser-N5O4IHUN.js → parser-A2BEFMGK.js} +4 -4
  164. package/dist/parser-B5FVS7O6.js +11 -0
  165. package/dist/{parser-NGU3ETO6.js → parser-C2HF2C2E.js} +4 -4
  166. package/dist/parser-CSFUN3CT.js +14 -0
  167. package/dist/{parser-TRTHDFPP.js → parser-D3QT6GWN.js} +5 -5
  168. package/dist/parser-D5637H5H.js +15 -0
  169. package/dist/{parser-VQP2UJ6W.js → parser-DFFEDKHH.js} +3 -3
  170. package/dist/parser-DOKMZXVZ.js +13 -0
  171. package/dist/parser-FMPCVFRK.js +15 -0
  172. package/dist/{parser-Y6WTBU76.js → parser-HYRTF3DO.js} +3 -3
  173. package/dist/{parser-I6KXEYFK.js → parser-I2UIJKVQ.js} +4 -4
  174. package/dist/{parser-O5J4VBII.js → parser-IBWNAPY4.js} +3 -3
  175. package/dist/{parser-PQ3POX45.js → parser-KUQWRPIT.js} +4 -4
  176. package/dist/{parser-2MFP2H2E.js → parser-LWKKBNSA.js} +3 -3
  177. package/dist/{parser-OV7NOBW6.js → parser-O4QFCUW7.js} +5 -5
  178. package/dist/{parser-IXSZZQHY.js → parser-PGMQDD4G.js} +4 -4
  179. package/dist/{parser-3STE62GB.js → parser-Q4QKCIWQ.js} +4 -4
  180. package/dist/parser-QTOB4KFL.js +14 -0
  181. package/dist/{parser-2EWECDSA.js → parser-QYYM4QVW.js} +4 -4
  182. package/dist/{parser-4OGOBJJ4.js → parser-RZMQP7MM.js} +4 -4
  183. package/dist/parser-RZPVAQVX.js +11 -0
  184. package/dist/parser-SOUJOEYI.js +13 -0
  185. package/dist/{parser-4FL2CMDG.js → parser-UQE5OWZI.js} +5 -5
  186. package/dist/{parser-NRAI6O25.js → parser-WAV5JRPT.js} +4 -4
  187. package/dist/{parser-KNSQSKQO.js → parser-YLKYBRIT.js} +5 -5
  188. package/dist/{parser-XKRP7QKP.js → parser-YS7TOS2K.js} +3 -3
  189. package/dist/{parser-AQETEVRW.js → parser-ZSOWUBML.js} +5 -5
  190. package/dist/{parser-CFZD5IBL.js → parser-ZTBATMLX.js} +4 -4
  191. package/dist/{renderer-VACVQYML.js → renderer-35HSIDMZ.js} +8 -8
  192. package/dist/{renderer-5MYBMBYO.js → renderer-4ITCHBT7.js} +5 -5
  193. package/dist/{renderer-CLJHQPBJ.js → renderer-67NPCBIS.js} +11 -11
  194. package/dist/{renderer-5NDVNDAZ.js → renderer-6GHYK7DX.js} +9 -9
  195. package/dist/{renderer-Z7I3W2PP.js → renderer-6RNGHC5Z.js} +8 -8
  196. package/dist/renderer-72LGR2YF.js +14 -0
  197. package/dist/{renderer-KQB7UGDS.js → renderer-7RH2SI3B.js} +3 -3
  198. package/dist/renderer-7SWMIFMM.js +21 -0
  199. package/dist/renderer-BBCEFILK.js +24 -0
  200. package/dist/{renderer-JGBKUCZD.js → renderer-BSW7SRIZ.js} +4 -4
  201. package/dist/{renderer-BYCNMBF7.js → renderer-BY533UWU.js} +11 -11
  202. package/dist/{renderer-6W5462RB.js → renderer-BZSBPTZJ.js} +9 -9
  203. package/dist/{renderer-7MVLWWQD.js → renderer-CXUC4KAD.js} +9 -9
  204. package/dist/{renderer-QIWVHWZO.js → renderer-DAXMXIKD.js} +3 -3
  205. package/dist/renderer-FZUKFUNZ.js +23 -0
  206. package/dist/renderer-G2NLGNKF.js +17 -0
  207. package/dist/renderer-GO3FE3RA.js +24 -0
  208. package/dist/{renderer-ZGNTUWC4.js → renderer-HPGR6USK.js} +8 -8
  209. package/dist/{renderer-UV6MI64M.js → renderer-HVCU2C3Z.js} +3 -3
  210. package/dist/renderer-ICJN2SA2.js +24 -0
  211. package/dist/{renderer-AWWAF6XD.js → renderer-INEXO7B6.js} +10 -10
  212. package/dist/{renderer-YY2OSF6E.js → renderer-KDYCFRBX.js} +8 -8
  213. package/dist/renderer-KY4KQBTF.js +19 -0
  214. package/dist/{renderer-B2OMQIQG.js → renderer-LBN65V45.js} +13 -13
  215. package/dist/{renderer-N43WR5GV.js → renderer-MGBWOCXG.js} +10 -10
  216. package/dist/{renderer-PGX2TKVS.js → renderer-O4JKUGKO.js} +8 -8
  217. package/dist/{renderer-4JHRUBE6.js → renderer-OOGFODWP.js} +10 -10
  218. package/dist/{renderer-6EEZHMH4.js → renderer-Q6ZKPLS6.js} +9 -9
  219. package/dist/{renderer-RPOA5YPZ.js → renderer-SZQ326M5.js} +12 -12
  220. package/dist/{renderer-ZRNG54CW.js → renderer-TKG6LS4U.js} +11 -11
  221. package/dist/{renderer-W4M6QLIJ.js → renderer-TQMZGDAX.js} +13 -13
  222. package/dist/renderer-radial-JELMHQ24.js +21 -0
  223. package/dist/{resolver-NOAA7ID7.js → resolver-FHBCO3PL.js} +3 -3
  224. package/dist/{state-parser-Z2V23ATR.js → state-parser-3REXRK3R.js} +4 -4
  225. package/dist/{state-renderer-B4DQBM6M.js → state-renderer-XYLNRWXR.js} +8 -8
  226. package/docs/ai-integration.md +12 -3
  227. package/docs/language-reference.md +429 -24
  228. package/gallery/fixtures/body.dgmo +17 -0
  229. package/gallery/fixtures/bracket.dgmo +30 -0
  230. package/gallery/fixtures/clock.dgmo +8 -0
  231. package/gallery/fixtures/countdown.dgmo +3 -0
  232. package/gallery/fixtures/family.dgmo +23 -0
  233. package/gallery/fixtures/goal.dgmo +4 -0
  234. package/gallery/fixtures/sketch.dgmo +22 -0
  235. package/package.json +12 -4
  236. package/dist/chunk-ODZVHU3R.js +0 -153
  237. package/dist/dgmo-router-X35VLZLM.js +0 -73
  238. package/dist/dimensions-J3FHIMJ6.js +0 -22
  239. package/dist/layout-5T6RH5M6.js +0 -14
  240. package/dist/layout-G23F7LDH.js +0 -9
  241. package/dist/layout-OB3RI63S.js +0 -14
  242. package/dist/parser-JAL2HD4I.js +0 -13
  243. package/dist/renderer-SMIBCRVB.js +0 -24
  244. package/dist/renderer-radial-C32GPJQ7.js +0 -21
@@ -0,0 +1,940 @@
1
+ import {
2
+ renderInlineText
3
+ } from "./chunk-3HDBAXF5.js";
4
+ import {
5
+ renderIntegratedLegend
6
+ } from "./chunk-QUJGFNFN.js";
7
+ import {
8
+ getMaxLegendReservedHeight
9
+ } from "./chunk-7UEMZREK.js";
10
+ import {
11
+ TITLE_FONT_SIZE,
12
+ TITLE_FONT_WEIGHT,
13
+ TITLE_Y
14
+ } from "./chunk-A4GOY3PE.js";
15
+ import {
16
+ FONT_FAMILY
17
+ } from "./chunk-6KFWC4CO.js";
18
+ import {
19
+ LEGEND_GROUP_GAP
20
+ } from "./chunk-HQCHYB5F.js";
21
+ import {
22
+ measureText
23
+ } from "./chunk-5LWQH6QV.js";
24
+ import {
25
+ getSeriesColors,
26
+ mix,
27
+ shapeFill,
28
+ themeBaseBg
29
+ } from "./chunk-P6T66CCZ.js";
30
+ import {
31
+ resolveTagColor,
32
+ tagAttrKey
33
+ } from "./chunk-WCUHAQKB.js";
34
+ import {
35
+ resolveColor
36
+ } from "./chunk-TLVIHFRO.js";
37
+ import {
38
+ makeDgmoError
39
+ } from "./chunk-CXK4DZH3.js";
40
+
41
+ // src/bracket/renderer.ts
42
+ import * as d3Selection from "d3-selection";
43
+
44
+ // src/bracket/layout.ts
45
+ var BOX_W = 176;
46
+ var BOX_H = 30;
47
+ var PAIR_GAP = 8;
48
+ var ROW = 82;
49
+ var COL_W = 220;
50
+ var COMMENT_W = BOX_W - 14;
51
+ var HALF_SPAN = (BOX_H + PAIR_GAP) / 2;
52
+ function stripInline(s) {
53
+ return s.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*|__|\*|_|`/g, "");
54
+ }
55
+ function wrapText(text, maxW, fs) {
56
+ const words = text.split(/\s+/).filter(Boolean);
57
+ const lines = [];
58
+ let cur = "";
59
+ for (const w of words) {
60
+ const attempt = cur ? `${cur} ${w}` : w;
61
+ if (!cur || measureText(stripInline(attempt), fs) * 1.06 <= maxW)
62
+ cur = attempt;
63
+ else {
64
+ lines.push(cur);
65
+ cur = w;
66
+ }
67
+ }
68
+ if (cur) lines.push(cur);
69
+ return lines.length ? lines : [""];
70
+ }
71
+ function commentaryLineCount(commentary) {
72
+ return commentary.reduce((s, l) => s + wrapText(l, COMMENT_W, 11).length, 0);
73
+ }
74
+ function mean(xs) {
75
+ return xs.reduce((a, b) => a + b, 0) / xs.length;
76
+ }
77
+ function nextPow2(n) {
78
+ let p = 1;
79
+ while (p < n) p *= 2;
80
+ return Math.max(2, p);
81
+ }
82
+ function seedOrder(b) {
83
+ let order = [1];
84
+ while (order.length < b) {
85
+ const n = order.length * 2;
86
+ const next = [];
87
+ for (const s of order) {
88
+ next.push(s);
89
+ next.push(n + 1 - s);
90
+ }
91
+ order = next;
92
+ }
93
+ return order;
94
+ }
95
+ function winnerNameOf(n) {
96
+ if (n.winner === "top") return n.topName;
97
+ if (n.winner === "bot") return n.botName;
98
+ return null;
99
+ }
100
+ function resolveNames(nodes) {
101
+ const byId = new Map(nodes.map((n) => [n.id, n]));
102
+ const ordered = [...nodes].sort((a, b) => a.round - b.round);
103
+ for (const n of ordered) {
104
+ const tc = n.topChildId !== null ? byId.get(n.topChildId) : void 0;
105
+ const bc = n.botChildId !== null ? byId.get(n.botChildId) : void 0;
106
+ n.topName = tc ? winnerNameOf(tc) : n.topSeedName;
107
+ n.botName = bc ? winnerNameOf(bc) : n.botSeedName;
108
+ }
109
+ }
110
+ function applyResults(nodes, results, diagnostics) {
111
+ const pending = results.filter((r) => r.decided);
112
+ const done = /* @__PURE__ */ new Set();
113
+ for (let guard = 0; guard < pending.length + 2; guard++) {
114
+ resolveNames(nodes);
115
+ let progressed = false;
116
+ for (const r of pending) {
117
+ if (done.has(r)) continue;
118
+ const node = [...nodes].sort((a, b) => a.round - b.round).find(
119
+ (n) => n.winner === null && (n.topName === r.p1 && n.botName === r.p2 || n.topName === r.p2 && n.botName === r.p1)
120
+ );
121
+ if (node) {
122
+ node.winner = node.topName === r.p1 ? "top" : "bot";
123
+ node.score = r.score;
124
+ node.commentary = [...r.commentary];
125
+ node.homeName = r.home;
126
+ done.add(r);
127
+ progressed = true;
128
+ }
129
+ }
130
+ if (!progressed) break;
131
+ }
132
+ resolveNames(nodes);
133
+ for (const r of pending) {
134
+ if (!done.has(r)) {
135
+ diagnostics.push(
136
+ makeDgmoError(
137
+ r.lineNumber,
138
+ `No open matchup between "${r.p1}" and "${r.p2}" \u2014 check the seeding or earlier results.`,
139
+ "warning"
140
+ )
141
+ );
142
+ }
143
+ }
144
+ for (const r of results) {
145
+ if (r.decided) continue;
146
+ const has = nodes.some(
147
+ (n) => n.topName === r.p1 && n.botName === r.p2 || n.topName === r.p2 && n.botName === r.p1
148
+ );
149
+ if (!has) {
150
+ diagnostics.push(
151
+ makeDgmoError(
152
+ r.lineNumber,
153
+ `Pending matchup "${r.p1} vs ${r.p2}" doesn't line up with the seeded bracket.`,
154
+ "warning"
155
+ )
156
+ );
157
+ }
158
+ }
159
+ }
160
+ function buildSeededSide(seeds, results, side, nextId, diagnostics) {
161
+ const seedByNum = /* @__PURE__ */ new Map();
162
+ for (const s of seeds)
163
+ if (!seedByNum.has(s.seed)) seedByNum.set(s.seed, s.name);
164
+ const n = seeds.length;
165
+ const nodes = [];
166
+ if (n < 2) {
167
+ return buildCasualSide(results, side, nextId, diagnostics);
168
+ }
169
+ const b = nextPow2(n);
170
+ const order = seedOrder(b);
171
+ let level = order.map((seedNo) => ({
172
+ kind: "seed",
173
+ seedNo,
174
+ name: seedNo <= n ? seedByNum.get(seedNo) ?? null : null,
175
+ phantom: seedNo > n
176
+ }));
177
+ const carrierName = (c) => c.kind === "seed" ? c.name : null;
178
+ const carrierChild = (c) => c.kind === "match" ? c.id : null;
179
+ let round = 1;
180
+ while (level.length > 1) {
181
+ const next = [];
182
+ for (let i = 0; i < level.length; i += 2) {
183
+ const a = level[i];
184
+ const bb = level[i + 1];
185
+ const aPhantom = a.kind === "seed" && a.phantom;
186
+ const bPhantom = bb.kind === "seed" && bb.phantom;
187
+ if (aPhantom && !bPhantom) {
188
+ next.push(bb);
189
+ } else if (bPhantom && !aPhantom) {
190
+ next.push(a);
191
+ } else if (aPhantom && bPhantom) {
192
+ next.push(a);
193
+ } else {
194
+ const id = nextId();
195
+ nodes[id] = {
196
+ id,
197
+ round,
198
+ side,
199
+ topSeedName: carrierName(a),
200
+ botSeedName: carrierName(bb),
201
+ topChildId: carrierChild(a),
202
+ botChildId: carrierChild(bb),
203
+ topName: null,
204
+ botName: null,
205
+ winner: null,
206
+ score: null,
207
+ commentary: [],
208
+ homeName: null,
209
+ lineNumber: null,
210
+ isChampionship: false,
211
+ y: 0,
212
+ x: 0
213
+ };
214
+ next.push({ kind: "match", id });
215
+ }
216
+ }
217
+ level = next;
218
+ round++;
219
+ }
220
+ const rootId = level[0]?.kind === "match" ? level[0].id : null;
221
+ const compact = nodes.filter(Boolean);
222
+ applyResults(compact, results, diagnostics);
223
+ const rounds = compact.reduce((m, x) => Math.max(m, x.round), 0);
224
+ return { nodes: compact, rootId, rounds };
225
+ }
226
+ function buildCasualSide(matches, side, nextId, diagnostics) {
227
+ const nodes = matches.map((m) => {
228
+ const id = nextId();
229
+ return {
230
+ id,
231
+ round: 1,
232
+ side,
233
+ topSeedName: m.p1,
234
+ botSeedName: m.p2,
235
+ topChildId: null,
236
+ botChildId: null,
237
+ topName: m.p1,
238
+ botName: m.p2,
239
+ winner: m.decided ? "top" : null,
240
+ score: m.score,
241
+ commentary: [...m.commentary],
242
+ homeName: m.home,
243
+ lineNumber: m.lineNumber,
244
+ isChampionship: false,
245
+ y: 0,
246
+ x: 0
247
+ };
248
+ });
249
+ const winnerOf = (idx) => matches[idx].decided ? matches[idx].p1 : null;
250
+ const feeder = (p, idx) => {
251
+ for (let j = idx - 1; j >= 0; j--) {
252
+ if (winnerOf(j) === p) return j;
253
+ }
254
+ return null;
255
+ };
256
+ const baseId = nodes.length ? nodes[0].id : 0;
257
+ for (let idx = 0; idx < matches.length; idx++) {
258
+ const m = matches[idx];
259
+ const tf = feeder(m.p1, idx);
260
+ const bf = feeder(m.p2, idx);
261
+ nodes[idx].topChildId = tf !== null ? baseId + tf : null;
262
+ nodes[idx].botChildId = bf !== null ? baseId + bf : null;
263
+ }
264
+ const roundMemo = new Array(matches.length).fill(0);
265
+ const inProgress = /* @__PURE__ */ new Set();
266
+ const roundOf = (idx) => {
267
+ if (roundMemo[idx]) return roundMemo[idx];
268
+ if (inProgress.has(idx)) return 1;
269
+ inProgress.add(idx);
270
+ const m = matches[idx];
271
+ const rs = [];
272
+ const tf = feeder(m.p1, idx);
273
+ const bf = feeder(m.p2, idx);
274
+ if (tf !== null) rs.push(roundOf(tf));
275
+ if (bf !== null) rs.push(roundOf(bf));
276
+ inProgress.delete(idx);
277
+ const r = rs.length ? 1 + Math.max(...rs) : 1;
278
+ roundMemo[idx] = r;
279
+ return r;
280
+ };
281
+ for (let idx = 0; idx < matches.length; idx++)
282
+ nodes[idx].round = roundOf(idx);
283
+ const winnerFutureCount = /* @__PURE__ */ new Map();
284
+ for (let idx = 0; idx < matches.length; idx++) {
285
+ const w = winnerOf(idx);
286
+ if (!w) continue;
287
+ let count = 0;
288
+ for (let j = 0; j < matches.length; j++) {
289
+ if (j === idx) continue;
290
+ if (matches[j].p1 === w || matches[j].p2 === w) count++;
291
+ }
292
+ if (count > 1) winnerFutureCount.set(w, count);
293
+ }
294
+ for (const [w] of winnerFutureCount) {
295
+ const first = matches.findIndex((_, i) => winnerOf(i) === w);
296
+ diagnostics.push(
297
+ makeDgmoError(
298
+ matches[first]?.lineNumber ?? 1,
299
+ `"${w}" advances into more than one later match \u2014 using the first in source order.`,
300
+ "warning"
301
+ )
302
+ );
303
+ }
304
+ const childIds = /* @__PURE__ */ new Set();
305
+ for (const nd of nodes) {
306
+ if (nd.topChildId !== null) childIds.add(nd.topChildId);
307
+ if (nd.botChildId !== null) childIds.add(nd.botChildId);
308
+ }
309
+ const roots = nodes.filter((nd) => !childIds.has(nd.id));
310
+ roots.sort((a, b) => b.round - a.round || a.id - b.id);
311
+ const rootId = roots.length ? roots[0].id : null;
312
+ const rounds = nodes.reduce((m, x) => Math.max(m, x.round), 0);
313
+ return { nodes, rootId, rounds };
314
+ }
315
+ function positionSide(built, rowScale) {
316
+ const byId = new Map(built.nodes.map((n) => [n.id, n]));
317
+ let slot = 0;
318
+ const R = HALF_SPAN / rowScale;
319
+ const place = (id) => {
320
+ const n = byId.get(id);
321
+ const tHas = n.topChildId !== null;
322
+ const bHas = n.botChildId !== null;
323
+ let ty;
324
+ let by;
325
+ const winBoxY = (m) => m.y + (m.winner === "bot" ? R : m.winner === "top" ? -R : 0);
326
+ if (tHas && bHas) {
327
+ ty = place(n.topChildId);
328
+ by = place(n.botChildId);
329
+ const tc = byId.get(n.topChildId);
330
+ const bc = byId.get(n.botChildId);
331
+ if (tc.round === bc.round) n.y = mean([ty, by]);
332
+ else if (tc.round < bc.round) n.y = winBoxY(tc) + R;
333
+ else n.y = winBoxY(bc) - R;
334
+ } else if (tHas) {
335
+ const f = byId.get(n.topChildId);
336
+ place(n.topChildId);
337
+ n.y = winBoxY(f) + R;
338
+ } else if (bHas) {
339
+ const f = byId.get(n.botChildId);
340
+ place(n.botChildId);
341
+ n.y = winBoxY(f) - R;
342
+ } else {
343
+ ty = slot++;
344
+ by = slot++;
345
+ n.y = mean([ty, by]);
346
+ }
347
+ return n.y;
348
+ };
349
+ const childIds = /* @__PURE__ */ new Set();
350
+ for (const n of built.nodes) {
351
+ if (n.topChildId !== null) childIds.add(n.topChildId);
352
+ if (n.botChildId !== null) childIds.add(n.botChildId);
353
+ }
354
+ const roots = built.nodes.filter((n) => !childIds.has(n.id)).sort((a, b) => a.id - b.id);
355
+ for (const r of roots) place(r.id);
356
+ }
357
+ function roundLabel(r, rounds, defs, twoSide) {
358
+ const d = defs[r - 1];
359
+ if (d)
360
+ return d.color !== void 0 ? { label: d.name, color: d.color } : { label: d.name };
361
+ if (!twoSide && r === rounds) return { label: "Final" };
362
+ return { label: `Round ${r}` };
363
+ }
364
+ function layoutBracket(parsed) {
365
+ const diagnostics = [...parsed.diagnostics];
366
+ let idCounter = 0;
367
+ const nextId = () => idCounter++;
368
+ const groups = [];
369
+ const buildFor = (label, color) => {
370
+ const sideSeeds = parsed.seeds.filter((s) => s.side === label);
371
+ const sideMatches = parsed.matches.filter((m) => m.side === label);
372
+ const built = parsed.seeded ? buildSeededSide(sideSeeds, sideMatches, label, nextId, diagnostics) : buildCasualSide(sideMatches, label, nextId, diagnostics);
373
+ groups.push(
374
+ color !== void 0 ? { label, color, built } : { label, built }
375
+ );
376
+ };
377
+ if (parsed.sides.length === 0) {
378
+ buildFor(null, void 0);
379
+ } else {
380
+ for (const s of parsed.sides) buildFor(s.label, s.color);
381
+ }
382
+ const maxComment = Math.max(
383
+ 0,
384
+ ...groups.flatMap(
385
+ (g) => g.built.nodes.map((n) => commentaryLineCount(n.commentary))
386
+ )
387
+ );
388
+ const COMMENT_LH2 = 15;
389
+ const rowScale = maxComment > 0 ? Math.max(ROW, 2 * BOX_H + PAIR_GAP + maxComment * COMMENT_LH2 + 16) : ROW;
390
+ for (const g of groups) positionSide(g.built, rowScale);
391
+ const champResults = parsed.matches.filter(
392
+ (m) => m.side === null && parsed.sides.length >= 1
393
+ );
394
+ const twoSide = groups.length === 2;
395
+ const allNodes = [];
396
+ const nodeById = /* @__PURE__ */ new Map();
397
+ const columnLabels = [];
398
+ const sideLabelOut = [];
399
+ const pushSide = (x0, x1, label, color) => {
400
+ sideLabelOut.push(
401
+ color !== void 0 ? { x0, x1, label, color } : { x0, x1, label }
402
+ );
403
+ };
404
+ const seenColX = /* @__PURE__ */ new Set();
405
+ const addColLabel = (x, rl) => {
406
+ if (parsed.noRounds) return;
407
+ if (seenColX.has(x)) return;
408
+ seenColX.add(x);
409
+ columnLabels.push(
410
+ rl.color !== void 0 ? { x, label: rl.label, color: rl.color } : { x, label: rl.label }
411
+ );
412
+ };
413
+ let champion = null;
414
+ if (!twoSide) {
415
+ let yOffset = 0;
416
+ for (const g of groups) {
417
+ const rounds = g.built.rounds;
418
+ for (const n of g.built.nodes) {
419
+ n.x = (n.round - 1) * COL_W + BOX_W / 2;
420
+ n.y = n.y * rowScale + yOffset;
421
+ allNodes.push(n);
422
+ nodeById.set(n.id, n);
423
+ addColLabel(n.x, roundLabel(n.round, rounds, parsed.roundNames, false));
424
+ }
425
+ if (g.label) {
426
+ pushSide(0, (rounds - 1) * COL_W + BOX_W, g.label, g.color);
427
+ }
428
+ const slotSpan = Math.max(0, ...g.built.nodes.map((n) => n.y)) + rowScale;
429
+ yOffset = slotSpan + rowScale;
430
+ }
431
+ const rootWinners = groups.map(
432
+ (g) => g.built.rootId !== null ? nodeById.get(g.built.rootId) : null
433
+ ).filter(Boolean);
434
+ if (rootWinners.length) {
435
+ const finalNode = rootWinners.sort((a, b) => b.round - a.round)[0];
436
+ champion = winnerNameOf(finalNode);
437
+ }
438
+ } else {
439
+ const left = groups[0];
440
+ const right = groups[1];
441
+ const L = left.built.rounds;
442
+ const R = right.built.rounds;
443
+ const centerX = L * COL_W + BOX_W / 2;
444
+ for (const n of left.built.nodes) {
445
+ n.x = (n.round - 1) * COL_W + BOX_W / 2;
446
+ n.y = n.y * rowScale;
447
+ allNodes.push(n);
448
+ nodeById.set(n.id, n);
449
+ addColLabel(n.x, roundLabel(n.round, L, parsed.roundNames, true));
450
+ }
451
+ for (const n of right.built.nodes) {
452
+ n.x = centerX + (R - n.round + 1) * COL_W;
453
+ n.y = n.y * rowScale;
454
+ allNodes.push(n);
455
+ nodeById.set(n.id, n);
456
+ addColLabel(n.x, roundLabel(n.round, R, parsed.roundNames, true));
457
+ }
458
+ if (left.label)
459
+ pushSide(0, (L - 1) * COL_W + BOX_W, left.label, left.color);
460
+ if (right.label)
461
+ pushSide(
462
+ centerX + COL_W - BOX_W / 2,
463
+ centerX + R * COL_W + BOX_W / 2,
464
+ right.label,
465
+ right.color
466
+ );
467
+ const leftRoot = left.built.rootId !== null ? nodeById.get(left.built.rootId) : null;
468
+ const rightRoot = right.built.rootId !== null ? nodeById.get(right.built.rootId) : null;
469
+ const cid = nextId();
470
+ const champNode = {
471
+ id: cid,
472
+ round: 0,
473
+ side: null,
474
+ topSeedName: null,
475
+ botSeedName: null,
476
+ topChildId: leftRoot ? leftRoot.id : null,
477
+ botChildId: rightRoot ? rightRoot.id : null,
478
+ topName: leftRoot ? winnerNameOf(leftRoot) : null,
479
+ botName: rightRoot ? winnerNameOf(rightRoot) : null,
480
+ winner: null,
481
+ score: null,
482
+ commentary: [],
483
+ homeName: null,
484
+ lineNumber: null,
485
+ isChampionship: true,
486
+ x: centerX,
487
+ y: mean([leftRoot?.y ?? 0, rightRoot?.y ?? 0])
488
+ };
489
+ for (const r of champResults) {
490
+ if (!r.decided) continue;
491
+ if (champNode.topName === r.p1 && champNode.botName === r.p2) {
492
+ champNode.winner = "top";
493
+ champNode.score = r.score;
494
+ champNode.commentary = [...r.commentary];
495
+ champNode.homeName = r.home;
496
+ } else if (champNode.topName === r.p2 && champNode.botName === r.p1) {
497
+ champNode.winner = "bot";
498
+ champNode.score = r.score;
499
+ champNode.commentary = [...r.commentary];
500
+ champNode.homeName = r.home;
501
+ }
502
+ }
503
+ allNodes.push(champNode);
504
+ nodeById.set(cid, champNode);
505
+ const centerDef = parsed.roundNames[L];
506
+ addColLabel(
507
+ centerX,
508
+ centerDef ? centerDef.color !== void 0 ? { label: centerDef.name, color: centerDef.color } : { label: centerDef.name } : { label: parsed.title ?? "Final" }
509
+ );
510
+ champion = winnerNameOf(champNode);
511
+ }
512
+ const half = HALF_SPAN;
513
+ const winnerBoxCy = (n) => {
514
+ if (n.winner === "bot") return n.y + half;
515
+ if (n.winner === "top") return n.y - half;
516
+ return n.y;
517
+ };
518
+ const fromPoint = (c) => {
519
+ const y = winnerBoxCy(c);
520
+ return c.winner === null ? { x: c.x, y, yA: c.y - half, yB: c.y + half } : { x: c.x, y, yA: y, yB: y };
521
+ };
522
+ const matches = allNodes.map((n) => {
523
+ const topChild = n.topChildId !== null ? nodeById.get(n.topChildId) : null;
524
+ const botChild = n.botChildId !== null ? nodeById.get(n.botChildId) : null;
525
+ return {
526
+ round: n.round,
527
+ side: n.side,
528
+ x: n.x,
529
+ y: n.y,
530
+ top: n.topName,
531
+ bot: n.botName,
532
+ winner: n.winner,
533
+ score: n.score,
534
+ pending: n.winner === null && n.topName !== null && n.botName !== null,
535
+ isChampionship: n.isChampionship,
536
+ topFrom: topChild ? fromPoint(topChild) : null,
537
+ botFrom: botChild ? fromPoint(botChild) : null,
538
+ commentary: n.commentary,
539
+ home: n.homeName,
540
+ lineNumber: n.lineNumber
541
+ };
542
+ });
543
+ let minX = Infinity;
544
+ let maxX = -Infinity;
545
+ let minY = Infinity;
546
+ let maxY = -Infinity;
547
+ for (const m of matches) {
548
+ minX = Math.min(minX, m.x - BOX_W / 2);
549
+ maxX = Math.max(maxX, m.x + BOX_W / 2);
550
+ minY = Math.min(minY, m.y - half - BOX_H / 2);
551
+ maxY = Math.max(maxY, m.y + half + BOX_H / 2);
552
+ if (m.commentary.length) {
553
+ const commentX = m.x - BOX_W / 2 + 2;
554
+ maxX = Math.max(maxX, commentX + COMMENT_W);
555
+ const wl = commentaryLineCount(m.commentary);
556
+ maxY = Math.max(
557
+ maxY,
558
+ m.y + half + BOX_H / 2 + 13 + (wl - 1) * COMMENT_LH2 + 4
559
+ );
560
+ }
561
+ }
562
+ for (const s of sideLabelOut) {
563
+ minX = Math.min(minX, s.x0 - 8);
564
+ maxX = Math.max(maxX, s.x1 + 8);
565
+ }
566
+ if (!Number.isFinite(minX)) {
567
+ minX = 0;
568
+ maxX = BOX_W;
569
+ minY = 0;
570
+ maxY = BOX_H;
571
+ }
572
+ const SIDE_BAR = sideLabelOut.length ? 30 : 0;
573
+ const LABEL_BAND = columnLabels.length ? 48 : 0;
574
+ const topBand = SIDE_BAR + LABEL_BAND;
575
+ const shiftX = -minX;
576
+ const shiftY = -minY + topBand;
577
+ const shifted = matches.map((m) => ({
578
+ ...m,
579
+ x: m.x + shiftX,
580
+ y: m.y + shiftY,
581
+ topFrom: m.topFrom ? {
582
+ x: m.topFrom.x + shiftX,
583
+ y: m.topFrom.y + shiftY,
584
+ yA: m.topFrom.yA + shiftY,
585
+ yB: m.topFrom.yB + shiftY
586
+ } : null,
587
+ botFrom: m.botFrom ? {
588
+ x: m.botFrom.x + shiftX,
589
+ y: m.botFrom.y + shiftY,
590
+ yA: m.botFrom.yA + shiftY,
591
+ yB: m.botFrom.yB + shiftY
592
+ } : null
593
+ }));
594
+ const shiftedCols = columnLabels.map((c) => ({ ...c, x: c.x + shiftX }));
595
+ const shiftedSides = sideLabelOut.map((s) => ({
596
+ ...s,
597
+ x0: s.x0 + shiftX,
598
+ x1: s.x1 + shiftX
599
+ }));
600
+ return {
601
+ matches: shifted,
602
+ columns: shiftedCols,
603
+ sideLabels: shiftedSides,
604
+ width: maxX - minX,
605
+ height: maxY - minY + topBand,
606
+ champion,
607
+ diagnostics
608
+ };
609
+ }
610
+
611
+ // src/bracket/renderer.ts
612
+ var PADDING = 16;
613
+ var TITLE_BAND = 40;
614
+ var HALF_SPAN2 = (BOX_H + PAIR_GAP) / 2;
615
+ var COMMENT_LH = 15;
616
+ var LEGEND_POSITION = {
617
+ placement: "top-center",
618
+ titleRelation: "below-title"
619
+ };
620
+ function clip(s, maxW, fs) {
621
+ if (measureText(s, fs) <= maxW) return s;
622
+ let lo = 0;
623
+ let hi = s.length;
624
+ while (lo < hi) {
625
+ const mid = Math.ceil((lo + hi) / 2);
626
+ if (measureText(s.slice(0, mid) + "\u2026", fs) <= maxW) lo = mid;
627
+ else hi = mid - 1;
628
+ }
629
+ return lo <= 0 ? "" : s.slice(0, lo) + "\u2026";
630
+ }
631
+ function paintForAccent(palette, isDark, accent) {
632
+ const base = themeBaseBg(palette, isDark);
633
+ return {
634
+ accent,
635
+ ink: palette.text,
636
+ muted: mix(palette.text, base, 52),
637
+ // Loser / TBD outlines + connectors are ink-based (not the pale border) so
638
+ // they stay legible on top of a colored, shaded column band.
639
+ hair: mix(palette.text, base, 34),
640
+ wire: mix(palette.text, base, 30),
641
+ track: mix(palette.border, base, 55),
642
+ tint: shapeFill(palette, accent, isDark)
643
+ };
644
+ }
645
+ function paintFor(palette, isDark, sideColor) {
646
+ const accent = sideColor && resolveColor(sideColor, palette) || sideColor || getSeriesColors(palette)[0];
647
+ return paintForAccent(palette, isDark, accent);
648
+ }
649
+ function resolveBracketLegend(parsed, palette) {
650
+ const activeTag = parsed.activeTag;
651
+ const activeGroup = activeTag ? parsed.tagGroups.find((g) => tagAttrKey(g.name) === tagAttrKey(activeTag)) : void 0;
652
+ const legendGroups = activeGroup && !parsed.noLegend ? [
653
+ {
654
+ name: activeGroup.name,
655
+ entries: activeGroup.entries.map((e) => ({
656
+ value: e.value,
657
+ color: resolveColor(e.color, palette) ?? e.color
658
+ }))
659
+ }
660
+ ] : [];
661
+ return { activeGroup, legendGroups };
662
+ }
663
+ function bracketExportDimensions(parsed, palette) {
664
+ const layout = layoutBracket(parsed);
665
+ const width = layout.width + PADDING * 2;
666
+ const titleH = parsed.title ? TITLE_BAND : 0;
667
+ const { legendGroups } = resolveBracketLegend(parsed, palette);
668
+ const legendReserve = legendGroups.length > 0 ? getMaxLegendReservedHeight(
669
+ { groups: legendGroups, position: LEGEND_POSITION, mode: "export" },
670
+ width
671
+ ) + LEGEND_GROUP_GAP : 0;
672
+ const height = titleH + legendReserve + PADDING + layout.height + PADDING;
673
+ return { width, height };
674
+ }
675
+ function renderBracket(container, parsed, palette, isDark, exportDims) {
676
+ d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
677
+ if (parsed.error) return;
678
+ const layout = layoutBracket(parsed);
679
+ const width = exportDims?.width ?? container.clientWidth;
680
+ const height = exportDims?.height ?? container.clientHeight;
681
+ if (width <= 0 || height <= 0) return;
682
+ const showTitle = !!parsed.title;
683
+ const titleH = showTitle ? TITLE_BAND : 0;
684
+ const exportMode = exportDims !== void 0;
685
+ const seedByName = /* @__PURE__ */ new Map();
686
+ for (const s of parsed.seeds)
687
+ if (!seedByName.has(s.name)) seedByName.set(s.name, s.seed);
688
+ const activeTag = parsed.activeTag;
689
+ const activeKey = activeTag ? tagAttrKey(activeTag) : null;
690
+ const tagValueOf = (name) => {
691
+ if (!name || !activeKey) return null;
692
+ const meta = parsed.competitorMeta.get(name);
693
+ return meta?.[activeKey] ?? null;
694
+ };
695
+ const tagColorOf = (name) => {
696
+ if (!name || !activeTag) return null;
697
+ const meta = parsed.competitorMeta.get(name);
698
+ if (!meta || !activeKey || !meta[activeKey]) return null;
699
+ const c = resolveTagColor(meta, [...parsed.tagGroups], activeTag);
700
+ return c ? resolveColor(c, palette) ?? c : null;
701
+ };
702
+ const { activeGroup, legendGroups } = resolveBracketLegend(parsed, palette);
703
+ const legendReserve = legendGroups.length > 0 ? getMaxLegendReservedHeight(
704
+ { groups: legendGroups, position: LEGEND_POSITION, mode: "preview" },
705
+ width
706
+ ) + LEGEND_GROUP_GAP : 0;
707
+ const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").attr("class", "dgmo-bracket").attr("role", "img").attr(
708
+ "aria-label",
709
+ `Tournament bracket${parsed.title ? ` \u2014 ${parsed.title}` : ""}${layout.champion ? `, champion ${layout.champion}` : ""}`
710
+ ).style("font-family", FONT_FAMILY);
711
+ if (showTitle) {
712
+ const t = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
713
+ if (parsed.titleLineNumber !== null)
714
+ t.attr("data-line-number", parsed.titleLineNumber);
715
+ }
716
+ const areaY = titleH + legendReserve + PADDING;
717
+ const areaX = PADDING;
718
+ const areaW = Math.max(1, width - PADDING * 2);
719
+ const areaH = Math.max(1, height - areaY - PADDING);
720
+ const scale = Math.min(1, areaW / layout.width, areaH / layout.height);
721
+ const offsetX = areaX + (areaW - layout.width * scale) / 2;
722
+ const offsetY = areaY;
723
+ const root = svg.append("g").attr("class", "dgmo-bracket-body").attr("transform", `translate(${offsetX},${offsetY}) scale(${scale})`);
724
+ if (legendGroups.length > 0) {
725
+ const legendG = svg.append("g").attr("class", "dgmo-bracket-legend").attr("transform", `translate(0, ${titleH})`);
726
+ renderIntegratedLegend(legendG, {
727
+ groups: legendGroups,
728
+ palette: {
729
+ bg: palette.bg,
730
+ surface: palette.surface,
731
+ text: palette.text,
732
+ textMuted: palette.textMuted,
733
+ primary: palette.primary
734
+ },
735
+ isDark,
736
+ width,
737
+ mode: exportMode ? "export" : "preview",
738
+ position: LEGEND_POSITION,
739
+ activeGroup: activeGroup?.name ?? null
740
+ });
741
+ }
742
+ const baseAccent = parsed.accentColor ?? resolveColor("blue", palette) ?? getSeriesColors(palette)[0];
743
+ const basePaint = paintForAccent(palette, isDark, baseAccent);
744
+ const sideColorByLabel = /* @__PURE__ */ new Map();
745
+ for (const s of parsed.sides) sideColorByLabel.set(s.label, s.color);
746
+ const sideBarH = layout.sideLabels.length ? 30 : 0;
747
+ const bandTop = sideBarH + 2;
748
+ const roundY = sideBarH + 24;
749
+ for (const col of layout.columns) {
750
+ if (!col.color) continue;
751
+ const covered = layout.sideLabels.some(
752
+ (s) => col.x >= s.x0 && col.x <= s.x1
753
+ );
754
+ const top = covered ? bandTop : 2;
755
+ const c = resolveColor(col.color, palette) ?? col.color;
756
+ root.append("rect").attr("x", col.x - BOX_W / 2 - 10).attr("y", top).attr("width", BOX_W + 20).attr("height", Math.max(0, layout.height - top)).attr("rx", 10).attr("fill", mix(c, palette.bg, isDark ? 12 : 8));
757
+ }
758
+ for (const s of layout.sideLabels) {
759
+ const c = s.color ? resolveColor(s.color, palette) ?? s.color : basePaint.accent;
760
+ root.append("rect").attr("x", s.x0 - 8).attr("y", 2).attr("width", s.x1 - s.x0 + 16).attr("height", 24).attr("rx", 8).attr("fill", mix(c, palette.bg, isDark ? 20 : 14)).attr("stroke", mix(c, palette.bg, 55)).attr("stroke-width", 1);
761
+ root.append("text").attr("x", (s.x0 + s.x1) / 2).attr("y", 18).attr("text-anchor", "middle").attr("font-size", 13).attr("font-weight", 800).attr("letter-spacing", 0.3).attr("fill", c).text(clip(s.label, s.x1 - s.x0, 13));
762
+ }
763
+ for (const col of layout.columns) {
764
+ const c = col.color ? resolveColor(col.color, palette) ?? col.color : basePaint.muted;
765
+ const covered = layout.sideLabels.some(
766
+ (s) => col.x >= s.x0 && col.x <= s.x1
767
+ );
768
+ root.append("text").attr("x", col.x).attr("y", covered ? roundY : 22).attr("text-anchor", "middle").attr("font-size", covered ? 14 : 15).attr("font-weight", covered ? 700 : 800).attr("fill", c).attr("letter-spacing", 0.4).text(clip(col.label, BOX_W + 20, covered ? 14 : 15));
769
+ }
770
+ for (const m of layout.matches) {
771
+ drawConnector(root, m, m.topFrom, m.y - HALF_SPAN2, basePaint);
772
+ drawConnector(root, m, m.botFrom, m.y + HALF_SPAN2, basePaint);
773
+ }
774
+ for (const m of layout.matches) {
775
+ const sidePaint = m.side && sideColorByLabel.get(m.side) ? paintFor(palette, isDark, sideColorByLabel.get(m.side)) : basePaint;
776
+ const topSeed = seedByName.get(m.top ?? "") ?? null;
777
+ const botSeed = seedByName.get(m.bot ?? "") ?? null;
778
+ let homeName = m.home;
779
+ if (!homeName && topSeed !== null && botSeed !== null) {
780
+ homeName = topSeed <= botSeed ? m.top : m.bot;
781
+ }
782
+ const [wSeed, lSeed] = m.winner === "top" ? [topSeed, botSeed] : m.winner === "bot" ? [botSeed, topSeed] : [null, null];
783
+ const isUpset = wSeed !== null && lSeed !== null && wSeed > lSeed && !m.isChampionship;
784
+ const scoreParts = m.score ? m.score.split(/[-–]/).map((p) => p.trim()) : null;
785
+ const winScore = scoreParts?.length === 2 ? scoreParts[0] : "";
786
+ const loseScore = scoreParts?.length === 2 ? scoreParts[1] : "";
787
+ const champWinner = m.isChampionship ? layout.champion : null;
788
+ const mkInfo = (side, name) => {
789
+ const isWinner = m.winner === side;
790
+ const isLoser = m.winner !== null && m.winner !== side;
791
+ return {
792
+ isWinner,
793
+ isLoser,
794
+ pending: m.pending,
795
+ isChampion: champWinner !== null && isWinner,
796
+ score: isWinner ? winScore : isLoser ? loseScore : "",
797
+ seed: seedByName.get(name ?? "") ?? null,
798
+ tagColor: tagColorOf(name),
799
+ tagValue: tagValueOf(name),
800
+ isHome: homeName !== null && homeName === name
801
+ };
802
+ };
803
+ drawBox(
804
+ root,
805
+ m.x,
806
+ m.y - HALF_SPAN2,
807
+ m.top,
808
+ mkInfo("top", m.top),
809
+ sidePaint,
810
+ palette,
811
+ isDark,
812
+ m,
813
+ activeKey
814
+ );
815
+ drawBox(
816
+ root,
817
+ m.x,
818
+ m.y + HALF_SPAN2,
819
+ m.bot,
820
+ mkInfo("bot", m.bot),
821
+ sidePaint,
822
+ palette,
823
+ isDark,
824
+ m,
825
+ activeKey
826
+ );
827
+ if (isUpset) drawUpset(root, m, sidePaint);
828
+ if (m.commentary.length) drawCommentary(root, m, palette);
829
+ }
830
+ }
831
+ function drawConnector(g, m, from, toCy, paint) {
832
+ if (!from) return;
833
+ const parentLeft = m.x - BOX_W / 2;
834
+ const parentRight = m.x + BOX_W / 2;
835
+ const childIsLeft = from.x < m.x;
836
+ const childEdge = childIsLeft ? from.x + BOX_W / 2 : from.x - BOX_W / 2;
837
+ const parentEdge = childIsLeft ? parentLeft : parentRight;
838
+ const midX = (childEdge + parentEdge) / 2;
839
+ const vLo = Math.min(from.yA, from.yB, toCy);
840
+ const vHi = Math.max(from.yA, from.yB, toCy);
841
+ const d = from.yA !== from.yB ? `M ${childEdge} ${from.yA} H ${midX} M ${childEdge} ${from.yB} H ${midX} M ${midX} ${vLo} V ${vHi} M ${midX} ${toCy} H ${parentEdge}` : `M ${childEdge} ${from.y} H ${midX} V ${toCy} H ${parentEdge}`;
842
+ g.append("path").attr("d", d).attr("fill", "none").attr("stroke", paint.wire).attr("stroke-width", 1.5);
843
+ }
844
+ function drawBox(g, cx, cy, name, info, sidePaint, palette, isDark, m, activeKey) {
845
+ const x = cx - BOX_W / 2;
846
+ const y = cy - BOX_H / 2;
847
+ const paint = info.tagColor ? paintForAccent(palette, isDark, info.tagColor) : sidePaint;
848
+ const cell = g.append("g").attr("class", "dgmo-bracket-box");
849
+ if (m.lineNumber !== null) cell.attr("data-line-number", m.lineNumber);
850
+ if (activeKey && info.tagValue)
851
+ cell.attr(`data-tag-${activeKey}`, info.tagValue.toLowerCase());
852
+ const isTBD = name === null;
853
+ let fill = "none";
854
+ let stroke = paint.hair;
855
+ let sw = 1.25;
856
+ let dash = null;
857
+ let textColor = paint.ink;
858
+ let weight = 600;
859
+ if (info.isChampion) {
860
+ fill = paint.tint;
861
+ stroke = paint.accent;
862
+ sw = 3;
863
+ weight = 800;
864
+ } else if (isTBD) {
865
+ fill = paint.track;
866
+ dash = "4 4";
867
+ textColor = paint.muted;
868
+ weight = 500;
869
+ } else if (info.isWinner) {
870
+ fill = paint.tint;
871
+ stroke = paint.accent;
872
+ sw = 2;
873
+ weight = 700;
874
+ } else if (info.isLoser) {
875
+ stroke = paint.hair;
876
+ textColor = paint.muted;
877
+ weight = 500;
878
+ } else if (info.pending) {
879
+ stroke = mix(paint.accent, paint.hair, 50);
880
+ }
881
+ const rect = cell.append("rect").attr("x", x).attr("y", y).attr("width", BOX_W).attr("height", BOX_H).attr("rx", 6).attr("fill", fill).attr("stroke", stroke).attr("stroke-width", sw);
882
+ if (dash) rect.attr("stroke-dasharray", dash);
883
+ let leftPad = 11;
884
+ if (info.seed !== null) {
885
+ cell.append("text").attr("x", x + 9).attr("y", cy + 4).attr("text-anchor", "start").attr("font-size", 10).attr("font-weight", 700).attr("fill", paint.muted).text(info.seed);
886
+ leftPad = 9 + Math.max(12, measureText(String(info.seed), 10)) + 6;
887
+ }
888
+ let rightPad = 11;
889
+ if (info.score) rightPad += 20;
890
+ if (info.isHome) {
891
+ drawHome(cell, x + BOX_W - rightPad - 6, cy, paint.muted);
892
+ rightPad += 16;
893
+ }
894
+ cell.append("text").attr("x", x + leftPad).attr("y", cy + 4).attr("text-anchor", "start").attr("font-size", 12.5).attr("font-weight", weight).attr("fill", textColor).text(clip(isTBD ? "TBD" : name, BOX_W - leftPad - rightPad, 12.5));
895
+ if (info.score) {
896
+ cell.append("text").attr("x", x + BOX_W - 11).attr("y", cy + 4).attr("text-anchor", "end").attr("font-size", 12).attr("font-weight", info.isWinner ? 700 : 500).attr("fill", info.isWinner ? textColor : paint.muted).text(info.score);
897
+ }
898
+ }
899
+ function drawHome(cell, hx, cy, color) {
900
+ const s = 5;
901
+ const yTop = cy - s - 1;
902
+ const d = `M ${hx - s} ${cy - 1} L ${hx} ${yTop} L ${hx + s} ${cy - 1} L ${hx + s} ${cy + s} L ${hx - s} ${cy + s} Z`;
903
+ cell.append("path").attr("d", d).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1.2).attr("stroke-linejoin", "round");
904
+ }
905
+ function drawUpset(g, m, paint) {
906
+ g.append("text").attr("x", m.x + BOX_W / 2).attr("y", m.y - HALF_SPAN2 - BOX_H / 2 - 4).attr("text-anchor", "end").attr("font-size", 9).attr("font-weight", 800).attr("letter-spacing", 0.6).attr("fill", paint.accent).text("UPSET");
907
+ }
908
+ function drawCommentary(g, m, palette) {
909
+ const x = m.x - BOX_W / 2 + 2;
910
+ let y = m.y + HALF_SPAN2 + BOX_H / 2 + 13;
911
+ const muted = mix(palette.text, palette.bg, 45);
912
+ const bulletW = measureText("\u2022 ", 11);
913
+ const emit = (line, lx) => {
914
+ const t = g.append("text").attr("x", lx).attr("y", y).attr("text-anchor", "start").attr("font-size", 11).attr("font-style", "italic").attr("fill", muted);
915
+ renderInlineText(t, line, palette, 11);
916
+ y += COMMENT_LH;
917
+ };
918
+ for (const raw of m.commentary) {
919
+ const isBullet = raw.startsWith("\u2022 ");
920
+ if (isBullet) {
921
+ const body = raw.slice(2);
922
+ wrapText(body, COMMENT_W - bulletW, 11).forEach(
923
+ (line, i) => i === 0 ? emit(`\u2022 ${line}`, x) : emit(line, x + bulletW)
924
+ );
925
+ } else {
926
+ for (const line of wrapText(raw, COMMENT_W, 11)) emit(line, x);
927
+ }
928
+ }
929
+ }
930
+ function renderBracketForExport(container, parsed, palette, isDark, exportDims) {
931
+ renderBracket(container, parsed, palette, isDark, exportDims);
932
+ }
933
+
934
+ export {
935
+ layoutBracket,
936
+ bracketExportDimensions,
937
+ renderBracket,
938
+ renderBracketForExport
939
+ };
940
+ //# sourceMappingURL=chunk-KJXIHOVL.js.map