@diagrammo/dgmo 0.49.0 → 0.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.cursorrules +6 -2
  2. package/.github/copilot-instructions.md +6 -2
  3. package/.windsurfrules +6 -2
  4. package/SKILL.md +6 -2
  5. package/dist/advanced.js +557 -195
  6. package/dist/auto.js +129 -119
  7. package/dist/auto.mjs +19496 -12761
  8. package/dist/block.js +145 -48
  9. package/dist/{charts-d3-EW3YQTEA.js → charts-d3-LR6D3D7A.js} +5 -5
  10. package/dist/{chunk-5FGDJIJX.js → chunk-2C56LEML.js} +3 -3
  11. package/dist/{chunk-XJ7J6WOO.js → chunk-2KGXHFYK.js} +4 -4
  12. package/dist/{chunk-T7IKPCMI.js → chunk-3LH5CLP4.js} +3 -3
  13. package/dist/{chunk-ZDUQGL3N.js → chunk-3LKM6FMW.js} +2 -2
  14. package/dist/{chunk-4FIV4K4Z.js → chunk-3NODSS6A.js} +7 -7
  15. package/dist/{chunk-4BD7EVPK.js → chunk-3P2DKJCG.js} +2 -2
  16. package/dist/{chunk-TC3YWBFO.js → chunk-3UIKSPFB.js} +35 -9
  17. package/dist/{chunk-SJEGO2QL.js → chunk-3XGZF2QJ.js} +4 -4
  18. package/dist/chunk-3XPUDB7B.js +420 -0
  19. package/dist/{chunk-O5MQPK7E.js → chunk-3Y2DRD7S.js} +3 -3
  20. package/dist/chunk-3YPY3OXD.js +354 -0
  21. package/dist/{chunk-N4J5KGWG.js → chunk-4MJ7M6YL.js} +8 -8
  22. package/dist/{chunk-JFPY3UJ7.js → chunk-4S52SK6G.js} +8 -8
  23. package/dist/{chunk-ALRHIR6I.js → chunk-5GJYGP5Z.js} +4 -4
  24. package/dist/{chunk-SQDFK5ZQ.js → chunk-5OWVC2A6.js} +3 -3
  25. package/dist/{chunk-7VLUGEMJ.js → chunk-66NVRO4K.js} +4 -4
  26. package/dist/{chunk-ZREZOIGS.js → chunk-66RUL3XF.js} +6 -6
  27. package/dist/{chunk-MJCG5K2O.js → chunk-6BFZKBGD.js} +2 -2
  28. package/dist/{chunk-2WMBWZY2.js → chunk-6HJBFVLO.js} +3 -3
  29. package/dist/{chunk-XCC2RENT.js → chunk-6IAA2QQ2.js} +2 -2
  30. package/dist/{chunk-B57V3DWN.js → chunk-7L3R6I3U.js} +3 -3
  31. package/dist/chunk-7PQYTZMA.js +681 -0
  32. package/dist/{chunk-6SA7FJE4.js → chunk-7QKLIMRZ.js} +10 -10
  33. package/dist/{chunk-YTN572Q2.js → chunk-7TV5Y53Y.js} +2 -2
  34. package/dist/{chunk-6CLKNXVP.js → chunk-BBGDM4NQ.js} +457 -78
  35. package/dist/{chunk-3NFJWFNP.js → chunk-BDXC7MDK.js} +1045 -124
  36. package/dist/{chunk-D5AVQCDN.js → chunk-BH3UYK36.js} +3 -3
  37. package/dist/chunk-BTUTZMPT.js +482 -0
  38. package/dist/{chunk-LVHPTBO3.js → chunk-C2CVAM3U.js} +4 -4
  39. package/dist/{chunk-BCT5G2VN.js → chunk-DFTFPYUD.js} +5 -5
  40. package/dist/{chunk-KV6Y2HE6.js → chunk-DSWVNQVS.js} +4 -4
  41. package/dist/{chunk-4YNP6O47.js → chunk-DZXYF2F5.js} +13 -3
  42. package/dist/{chunk-FUCAB3KQ.js → chunk-EAMEADTV.js} +3 -3
  43. package/dist/chunk-FA6K3FWM.js +940 -0
  44. package/dist/{chunk-HBVD67XG.js → chunk-FJUNPCYI.js} +2 -2
  45. package/dist/{chunk-6VE3VLWM.js → chunk-GBE54KY6.js} +6 -5
  46. package/dist/{chunk-YRVLP7UU.js → chunk-GKC3HILQ.js} +4 -4
  47. package/dist/{chunk-ZXL6MB4R.js → chunk-GL5UZ6NY.js} +3 -3
  48. package/dist/{chunk-GB3YZTAM.js → chunk-H73FP42Q.js} +5 -5
  49. package/dist/{chunk-4UDKKGHH.js → chunk-HEVYAHLW.js} +2 -2
  50. package/dist/{chunk-DDTJ5N7Q.js → chunk-HLJVEAOF.js} +6 -6
  51. package/dist/{chunk-CLR4A2N2.js → chunk-HTMN2Z6U.js} +4 -4
  52. package/dist/{chunk-TLKMTQYL.js → chunk-I2WKW7C7.js} +3 -3
  53. package/dist/{chunk-IUK7AGD4.js → chunk-I7F2A35O.js} +2 -2
  54. package/dist/{chunk-IBC35BTK.js → chunk-IUMPEA64.js} +2 -2
  55. package/dist/{chunk-5XCM7UUW.js → chunk-J6K7RP6P.js} +100 -36
  56. package/dist/{chunk-AJ2PEY7J.js → chunk-JJUOHGX2.js} +2 -2
  57. package/dist/{chunk-CEHZOPGM.js → chunk-KFZYNWWO.js} +2 -2
  58. package/dist/{chunk-4NQ3IKUM.js → chunk-KHEYDLMT.js} +5 -5
  59. package/dist/{chunk-YM2BSPYW.js → chunk-LE4MYMLI.js} +2 -2
  60. package/dist/{chunk-PU3AUQPA.js → chunk-M5RXSRO2.js} +2 -2
  61. package/dist/{chunk-XKTN7CZF.js → chunk-MCZ5LBZD.js} +4 -4
  62. package/dist/{chunk-35BBZDFE.js → chunk-NU3XLTGP.js} +4 -4
  63. package/dist/{chunk-BPIJLRYE.js → chunk-NWED7CYO.js} +3 -3
  64. package/dist/{chunk-YYX3SU6H.js → chunk-OMNGQPEZ.js} +257 -101
  65. package/dist/{chunk-BGJV5ATD.js → chunk-ONA5KPV3.js} +2 -2
  66. package/dist/{chunk-ZNQX6QWH.js → chunk-OTAL43EH.js} +6 -2
  67. package/dist/{chunk-F743CM2N.js → chunk-OTQKUHRS.js} +3 -3
  68. package/dist/{chunk-KUBBJ2CD.js → chunk-P7CE7LXL.js} +6 -6
  69. package/dist/{chunk-UVCXZEGU.js → chunk-PFHVU7UA.js} +2 -2
  70. package/dist/{chunk-5ZZLLBFK.js → chunk-PGEGPSCZ.js} +2 -2
  71. package/dist/chunk-Q4V4XQWV.js +158 -0
  72. package/dist/{chunk-IHLSHCPZ.js → chunk-QJP6PG7F.js} +5 -5
  73. package/dist/{chunk-EYN7VFKL.js → chunk-QROYX7TT.js} +3 -3
  74. package/dist/chunk-QXKWYZNR.js +379 -0
  75. package/dist/{chunk-VC3TR4NL.js → chunk-RZ6YKEUJ.js} +3 -3
  76. package/dist/{chunk-K6HNAY5V.js → chunk-SCHXJTZL.js} +2 -2
  77. package/dist/{chunk-7OVSJI2V.js → chunk-SQLNBA64.js} +3 -3
  78. package/dist/{chunk-65RTDUZH.js → chunk-SXRTRMEE.js} +3 -3
  79. package/dist/{chunk-3BIAFYD2.js → chunk-T4VOVZT5.js} +2 -2
  80. package/dist/{chunk-UWDO3O52.js → chunk-TN35C5KM.js} +2 -2
  81. package/dist/{chunk-IFK3GIUW.js → chunk-TSA237IM.js} +2 -2
  82. package/dist/{chunk-DKEPS5LH.js → chunk-U2HGPLZS.js} +36 -2
  83. package/dist/{chunk-4HVNZ7WF.js → chunk-VBYSIOOY.js} +3 -3
  84. package/dist/{chunk-ZTLWVRNS.js → chunk-VDOOQZUD.js} +2 -2
  85. package/dist/{chunk-RTERS6S5.js → chunk-VUJGMJE6.js} +4 -4
  86. package/dist/{chunk-X2CH42BN.js → chunk-VVW3F3MD.js} +7 -7
  87. package/dist/{chunk-RI4NIMFP.js → chunk-VVZRV54S.js} +75 -35
  88. package/dist/{chunk-LG4TWW4U.js → chunk-WW32K72E.js} +8 -8
  89. package/dist/{chunk-NVT6C2VE.js → chunk-X6EZPSW6.js} +4 -4
  90. package/dist/{chunk-KQAEKMIX.js → chunk-XDXNDSKO.js} +2 -2
  91. package/dist/{chunk-FMKTYMP4.js → chunk-XJLKLU4D.js} +3 -3
  92. package/dist/chunk-Y4RNXEAO.js +952 -0
  93. package/dist/{chunk-26RVGM44.js → chunk-YIA4LEX4.js} +15 -7
  94. package/dist/chunk-YNRDWYZP.js +295 -0
  95. package/dist/{chunk-SGHSY3LU.js → chunk-Z6XJQ6KO.js} +81 -6
  96. package/dist/{chunk-SVDWQGX2.js → chunk-ZAD4HV2G.js} +139 -78
  97. package/dist/cli.cjs +153 -144
  98. package/dist/clock.d.ts +10 -0
  99. package/dist/clock.js +235 -0
  100. package/dist/countdown.d.ts +12 -0
  101. package/dist/countdown.js +450 -0
  102. package/dist/dgmo-router-YTU3ENAX.js +84 -0
  103. package/dist/{dimensions-WSIJXT5Q.js → dimensions-OTBKC5M6.js} +5 -5
  104. package/dist/editor.js +39 -1
  105. package/dist/element.js +131 -121
  106. package/dist/element.mjs +19496 -12761
  107. package/dist/{flowchart-parser-IVZN7CQN.js → flowchart-parser-OE4WHLTF.js} +4 -4
  108. package/dist/{flowchart-renderer-GMI2U3DD.js → flowchart-renderer-GPCSMTOR.js} +6 -6
  109. package/dist/highlight.d.ts +17 -1
  110. package/dist/highlight.js +156 -5
  111. package/dist/index.js +50 -45
  112. package/dist/{layout-HQMBPJVS.js → layout-46366KBT.js} +2 -2
  113. package/dist/{layout-D42XE4RE.js → layout-5BG6DFCU.js} +3 -3
  114. package/dist/{layout-K2ZN3Q42.js → layout-7MZ5NF2N.js} +4 -4
  115. package/dist/{layout-4Z5JM7PT.js → layout-FWODLIMP.js} +4 -4
  116. package/dist/{layout-GZ2CWONU.js → layout-J36454XU.js} +4 -4
  117. package/dist/{layout-S3EK4IYO.js → layout-LD6XTLD6.js} +4 -4
  118. package/dist/{parser-CLMFZWCC.js → parser-36HVKU5U.js} +5 -5
  119. package/dist/{parser-MCJWF5G3.js → parser-4AI5ZN5F.js} +3 -3
  120. package/dist/{parser-DERPY5KC.js → parser-4NO6VZ6S.js} +4 -4
  121. package/dist/parser-4WQQQXCG.js +13 -0
  122. package/dist/{parser-4ROBKVUR.js → parser-5OXGPNBF.js} +5 -5
  123. package/dist/{parser-XWUDFJ7P.js → parser-AGVYTQXV.js} +4 -4
  124. package/dist/{parser-TITK4YW6.js → parser-AXKBDGNO.js} +5 -5
  125. package/dist/parser-B5FVS7O6.js +11 -0
  126. package/dist/parser-CSFUN3CT.js +14 -0
  127. package/dist/parser-CVMEDULU.js +15 -0
  128. package/dist/{parser-KPMN5KKE.js → parser-D5VILGN2.js} +4 -4
  129. package/dist/{parser-IKZFTLOJ.js → parser-DFFEDKHH.js} +3 -3
  130. package/dist/{parser-JD7OQ2E6.js → parser-H3XEPFPV.js} +4 -4
  131. package/dist/{parser-UBGPTIZD.js → parser-HYRTF3DO.js} +3 -3
  132. package/dist/{parser-K647FRI4.js → parser-IBWNAPY4.js} +3 -3
  133. package/dist/{parser-4BKIFG2C.js → parser-IJBMRXMC.js} +5 -5
  134. package/dist/{parser-MBJSBTL5.js → parser-JJR52VAT.js} +5 -5
  135. package/dist/{parser-SMBJO3XN.js → parser-K3XXLMUW.js} +4 -4
  136. package/dist/{parser-OBQG655V.js → parser-KCR2WS3V.js} +4 -4
  137. package/dist/{parser-HVJFTOXL.js → parser-LWKKBNSA.js} +3 -3
  138. package/dist/{parser-ZYC3XGNS.js → parser-MPCE5JBG.js} +4 -4
  139. package/dist/{parser-QJYS7KQQ.js → parser-MTOMTWTD.js} +4 -4
  140. package/dist/{parser-GFSOPDB7.js → parser-O4HIUN6G.js} +4 -4
  141. package/dist/{parser-BL7Z72EY.js → parser-PSA4UBSP.js} +4 -4
  142. package/dist/{parser-TNYX5S7E.js → parser-RV4M6AS2.js} +4 -4
  143. package/dist/parser-RZPVAQVX.js +11 -0
  144. package/dist/{parser-UGA23PYK.js → parser-TGQZJ5OT.js} +4 -4
  145. package/dist/parser-TXND652E.js +13 -0
  146. package/dist/{parser-WISPH7LW.js → parser-VKOJM7KS.js} +4 -4
  147. package/dist/{parser-ZKF5K2H2.js → parser-W4PQUGX3.js} +4 -4
  148. package/dist/{parser-MRBPXXIV.js → parser-WAV5JRPT.js} +4 -4
  149. package/dist/{parser-D6ONJQFR.js → parser-YS7TOS2K.js} +3 -3
  150. package/dist/pert.d.ts +8 -0
  151. package/dist/{renderer-VFUVRNLK.js → renderer-2AZ5HJFK.js} +4 -4
  152. package/dist/{renderer-V3JEMWMM.js → renderer-2QPMJ2DA.js} +6 -6
  153. package/dist/renderer-45PSOL3E.js +19 -0
  154. package/dist/{renderer-BB5PREMY.js → renderer-4ITCHBT7.js} +4 -4
  155. package/dist/{renderer-XEKIRPI6.js → renderer-5DSEWF74.js} +5 -5
  156. package/dist/{renderer-QNHUXCRL.js → renderer-6OKF44KS.js} +9 -9
  157. package/dist/renderer-72LGR2YF.js +14 -0
  158. package/dist/{renderer-YHXPN3GF.js → renderer-7SD6MSBS.js} +7 -7
  159. package/dist/renderer-A5VE5BCI.js +24 -0
  160. package/dist/{renderer-A5VFZQQK.js → renderer-AUW2BE6G.js} +6 -6
  161. package/dist/{renderer-LWGJFT3D.js → renderer-B5TBNZJE.js} +5 -5
  162. package/dist/{renderer-4IUPINAE.js → renderer-BSR2O2H6.js} +7 -7
  163. package/dist/{renderer-DKESNMU5.js → renderer-CYB4LK3G.js} +6 -6
  164. package/dist/{renderer-MFJ7HY2H.js → renderer-FF24KZCI.js} +5 -5
  165. package/dist/{renderer-CGW7QPNA.js → renderer-FPE3J5VZ.js} +7 -7
  166. package/dist/{renderer-6T3KK4S3.js → renderer-FYSVXL7M.js} +7 -7
  167. package/dist/{renderer-THD6V5TL.js → renderer-HDCCZURL.js} +9 -7
  168. package/dist/{renderer-TPIBXNOS.js → renderer-JCCHNECZ.js} +6 -6
  169. package/dist/{renderer-QL5KO6SK.js → renderer-KS7YZJE2.js} +7 -7
  170. package/dist/{renderer-SQ3JUAI7.js → renderer-NGXZTRK6.js} +6 -6
  171. package/dist/{renderer-MJDDU27W.js → renderer-ODUUOPVT.js} +10 -10
  172. package/dist/renderer-PJM35LJN.js +17 -0
  173. package/dist/{renderer-TYXRHMRI.js → renderer-PPIHO5FI.js} +9 -9
  174. package/dist/{renderer-W5RMOURB.js → renderer-PSMMZCKH.js} +6 -6
  175. package/dist/{renderer-UZF3JROD.js → renderer-QAOBLRXV.js} +8 -8
  176. package/dist/{renderer-ROV756WT.js → renderer-R2WNIC2C.js} +5 -5
  177. package/dist/{renderer-VICQ4WNY.js → renderer-SBS3YWU5.js} +5 -5
  178. package/dist/{renderer-6L3NBEY5.js → renderer-UYMQQCJK.js} +6 -6
  179. package/dist/{renderer-radial-MHO2VDXN.js → renderer-radial-F7X5F2VU.js} +6 -6
  180. package/dist/{state-parser-RX3GXOIB.js → state-parser-3REXRK3R.js} +4 -4
  181. package/dist/{state-renderer-AJUKGNWU.js → state-renderer-XYLNRWXR.js} +6 -6
  182. package/docs/ai-integration.md +6 -2
  183. package/docs/language-reference.md +253 -22
  184. package/gallery/fixtures/body.dgmo +17 -0
  185. package/gallery/fixtures/bracket.dgmo +30 -0
  186. package/gallery/fixtures/clock.dgmo +8 -0
  187. package/gallery/fixtures/countdown.dgmo +3 -0
  188. package/gallery/fixtures/goal.dgmo +4 -0
  189. package/package.json +12 -4
  190. package/dist/advanced.d.ts +0 -6162
  191. package/dist/auto.d.ts +0 -54
  192. package/dist/block.d.ts +0 -123
  193. package/dist/dgmo-router-ODKPEJ3P.js +0 -79
  194. package/dist/element.d.ts +0 -62
  195. package/dist/index.d.ts +0 -166
  196. package/dist/parser-667RGSUW.js +0 -13
  197. package/dist/parser-J3P2D6IP.js +0 -13
  198. package/dist/themes-1-CuKpeH.d.ts +0 -908
@@ -0,0 +1,940 @@
1
+ import {
2
+ renderInlineText
3
+ } from "./chunk-3HDBAXF5.js";
4
+ import {
5
+ renderIntegratedLegend
6
+ } from "./chunk-HEVYAHLW.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-FJUNPCYI.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-FA6K3FWM.js.map