@diagrammo/dgmo 0.52.0 → 0.53.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 (329) hide show
  1. package/.cursorrules +59 -58
  2. package/.github/copilot-instructions.md +59 -58
  3. package/.windsurfrules +59 -58
  4. package/SKILL.md +59 -58
  5. package/dist/advanced.d.ts +65 -230
  6. package/dist/advanced.js +278 -611
  7. package/dist/analyzer-TDN5KJFP.js +15 -0
  8. package/dist/auto.css +14 -6
  9. package/dist/auto.js +142 -132
  10. package/dist/auto.mjs +1117 -452
  11. package/dist/block.css +14 -6
  12. package/dist/block.js +85 -72
  13. package/dist/calculator-VBIT6GQF.js +11 -0
  14. package/dist/chart-meta-CPNVUZ5J.d.ts +232 -0
  15. package/dist/chart-meta.d.ts +2 -0
  16. package/dist/chart-meta.js +105 -0
  17. package/dist/charts-d3-DH2XA45I.js +26 -0
  18. package/dist/{chunk-QJMKPUYK.js → chunk-22OQWBWD.js} +14 -7
  19. package/dist/{chunk-5NGAO4QK.js → chunk-27STWBOK.js} +10 -8
  20. package/dist/{chunk-QGATPIPX.js → chunk-2QC4HXCY.js} +31 -17
  21. package/dist/{chunk-HFMYN6N2.js → chunk-3B2GXZCX.js} +4 -3
  22. package/dist/{chunk-6HQH6JZE.js → chunk-3BEKKRH5.js} +10 -10
  23. package/dist/{chunk-AL672HNE.js → chunk-3CZLWOCI.js} +17 -7
  24. package/dist/{chunk-J7TLCB2Q.js → chunk-3IUHKMJI.js} +2 -2
  25. package/dist/{chunk-7L4UJOY6.js → chunk-3TYUXC7D.js} +2 -2
  26. package/dist/{chunk-Y2ZE3MSG.js → chunk-43E26CDA.js} +2 -2
  27. package/dist/{chunk-YTMTPUH4.js → chunk-4ASVFYB2.js} +6 -6
  28. package/dist/{chunk-WCT3NYCV.js → chunk-4ASYQ347.js} +33 -8
  29. package/dist/{chunk-ZNWAYU6N.js → chunk-4K42RUGC.js} +33 -9
  30. package/dist/{chunk-I5IGB56C.js → chunk-4QV26TEG.js} +41 -1
  31. package/dist/{chunk-QOOUC2J5.js → chunk-4RGZFA7H.js} +2 -2
  32. package/dist/{chunk-CXK4DZH3.js → chunk-4W2PE66Q.js} +12 -2
  33. package/dist/{chunk-CIRFIL4M.js → chunk-4WGMGKCZ.js} +13 -13
  34. package/dist/{chunk-JED6ZDEE.js → chunk-5HWPEYWB.js} +2 -2
  35. package/dist/{chunk-VQOCMUWI.js → chunk-65V6IRFN.js} +16 -14
  36. package/dist/{chunk-R3YSD62R.js → chunk-6AUZPPAV.js} +3 -3
  37. package/dist/{chunk-FQ7HTV52.js → chunk-6DCOIN4W.js} +33 -5
  38. package/dist/{chunk-MUE65OTD.js → chunk-6HTRCTAS.js} +4 -4
  39. package/dist/{chunk-TJVLKNGR.js → chunk-6KY7OCZC.js} +5 -5
  40. package/dist/{chunk-YAA6IID3.js → chunk-774C2FF3.js} +2 -2
  41. package/dist/{chunk-UBLHQTLV.js → chunk-7GUF6SDR.js} +15 -14
  42. package/dist/{chunk-5HILBT7X.js → chunk-7JDTNFAK.js} +3 -3
  43. package/dist/{chunk-QBZYACOG.js → chunk-ADYKAKE3.js} +3 -3
  44. package/dist/{chunk-H7KENAZO.js → chunk-AHXIMHDQ.js} +74 -14
  45. package/dist/chunk-AIMLCDAO.js +404 -0
  46. package/dist/{chunk-4IHAV6O7.js → chunk-ALYZ3MAA.js} +4 -4
  47. package/dist/{chunk-ROOE4R4Y.js → chunk-AN2DQB3G.js} +146 -18
  48. package/dist/{chunk-GCYTI3DE.js → chunk-AZNXGWDC.js} +56 -10
  49. package/dist/{chunk-3LK5DNED.js → chunk-B4XS2AF2.js} +3 -3
  50. package/dist/{chunk-3HF5YEFJ.js → chunk-BD3WGY4T.js} +4 -4
  51. package/dist/{chunk-4AQRFDXL.js → chunk-BFDVEO5R.js} +2 -2
  52. package/dist/{chunk-REJAIQ5S.js → chunk-BR7AYN76.js} +8 -8
  53. package/dist/{chunk-P3NRSSUU.js → chunk-BULHGPHK.js} +3 -3
  54. package/dist/{chunk-VILVLS7P.js → chunk-CK2GMDXD.js} +8 -4
  55. package/dist/{chunk-HHKO6SKY.js → chunk-CMVZWP32.js} +3 -3
  56. package/dist/{chunk-GJFAMOGK.js → chunk-CQFNIIQN.js} +7 -7
  57. package/dist/{chunk-FT3ZYWET.js → chunk-DWUMPMV2.js} +11 -10
  58. package/dist/{chunk-USZW2HHU.js → chunk-EQW6HOP4.js} +4 -3
  59. package/dist/{chunk-Z5QVYKR7.js → chunk-FFXCKZOU.js} +4 -4
  60. package/dist/{chunk-WRNFDFPU.js → chunk-FGDSTT3F.js} +2 -2
  61. package/dist/{chunk-FY5MSZAP.js → chunk-FLDC7ZAK.js} +21 -6
  62. package/dist/{chunk-AAXZDUOV.js → chunk-FT7TPPZ5.js} +17 -16
  63. package/dist/{chunk-3QD7VP3U.js → chunk-FUKNRLIJ.js} +14 -7
  64. package/dist/{chunk-3YRPZ7ZZ.js → chunk-FXCB7H5O.js} +4 -4
  65. package/dist/{chunk-SMRFPVLQ.js → chunk-GABSL6TH.js} +4 -4
  66. package/dist/{chunk-BKVOYV6I.js → chunk-GAWKIIPO.js} +17 -17
  67. package/dist/{chunk-HWX6ZHF3.js → chunk-GMAH572Z.js} +6 -6
  68. package/dist/{chunk-MUVJ57EP.js → chunk-GMIJ35PW.js} +23 -20
  69. package/dist/{chunk-7QE3XMRK.js → chunk-GQ644SPD.js} +18 -18
  70. package/dist/{chunk-5QKQSSPO.js → chunk-GRM2A4BG.js} +4 -4
  71. package/dist/{chunk-J7KJR3R6.js → chunk-GWQJKTFS.js} +133 -696
  72. package/dist/{chunk-OTOHI4JO.js → chunk-H3HFC3SQ.js} +2 -2
  73. package/dist/{chunk-SWSIKFEB.js → chunk-HE5J53SZ.js} +4 -4
  74. package/dist/{chunk-7BIZCTAX.js → chunk-HNBY6CNV.js} +3 -3
  75. package/dist/{chunk-J76PIKAT.js → chunk-HO3RCNF6.js} +3 -3
  76. package/dist/{chunk-44VCIPHF.js → chunk-HORZ5H2L.js} +26 -23
  77. package/dist/{chunk-MX2EY5Y7.js → chunk-HOXKNRON.js} +2 -2
  78. package/dist/{chunk-LTXOLDUZ.js → chunk-I64ECJX3.js} +12 -5
  79. package/dist/{chunk-4YFKQ5WT.js → chunk-IQJCVREL.js} +8 -8
  80. package/dist/{chunk-YIBAWQGJ.js → chunk-J7HI6G3G.js} +7 -7
  81. package/dist/{chunk-ICPJPQ5C.js → chunk-JHOAH4TI.js} +3 -3
  82. package/dist/{chunk-6EJITS44.js → chunk-JTMSWEIW.js} +6 -6
  83. package/dist/{chunk-WBM7ZNDI.js → chunk-JYDMBWRP.js} +35 -18
  84. package/dist/{chunk-7DF4J4RL.js → chunk-KKHCNSKK.js} +17 -11
  85. package/dist/{chunk-4UNQ6X24.js → chunk-KUSAVOWZ.js} +4 -4
  86. package/dist/{chunk-77FGAVZC.js → chunk-LC3FFD43.js} +21 -19
  87. package/dist/{chunk-INE5YJBZ.js → chunk-LHILKFDP.js} +10 -10
  88. package/dist/{chunk-ONU3U4GL.js → chunk-LQHVDZYC.js} +52 -16
  89. package/dist/{chunk-75G3Q4DK.js → chunk-MA3HJZHB.js} +5 -5
  90. package/dist/{chunk-QVENJMNX.js → chunk-MEQYZZFR.js} +2 -2
  91. package/dist/{chunk-IM6PDDCE.js → chunk-NNUMX6WX.js} +42 -12
  92. package/dist/{chunk-FQMUHRVC.js → chunk-NNYFQOTK.js} +14 -13
  93. package/dist/{chunk-H6PI3KJT.js → chunk-ODUT5WMC.js} +2 -2
  94. package/dist/{chunk-K4EOKJA6.js → chunk-P3H7QVDH.js} +42 -15
  95. package/dist/{chunk-FUPF2UYQ.js → chunk-PIXZTCKH.js} +51 -108
  96. package/dist/{chunk-BPY242XU.js → chunk-PKNNHU7H.js} +19 -8
  97. package/dist/{chunk-BPIZNB5O.js → chunk-PT4SW76W.js} +11 -11
  98. package/dist/{chunk-UNURMIQO.js → chunk-PV2QO7QL.js} +4 -4
  99. package/dist/{chunk-LGT3PZCJ.js → chunk-PYO4TJJ3.js} +17 -17
  100. package/dist/{chunk-EMLPOL77.js → chunk-Q7AH665U.js} +15 -8
  101. package/dist/{chunk-LBMS4AW6.js → chunk-QAALSCKI.js} +14 -14
  102. package/dist/{chunk-JRCOYRRH.js → chunk-QI3BOLQB.js} +8 -8
  103. package/dist/{chunk-CCNDLW7Z.js → chunk-QWIBT6DS.js} +13 -13
  104. package/dist/{chunk-GU46PPT3.js → chunk-QXGACPS2.js} +5 -5
  105. package/dist/{chunk-FMDSF3OV.js → chunk-QZAW5SFH.js} +2 -2
  106. package/dist/{chunk-QHXCAEMX.js → chunk-R5NH5BZG.js} +8 -8
  107. package/dist/{chunk-5223KXFD.js → chunk-RJ5LBZ5U.js} +7 -7
  108. package/dist/{chunk-PRI2OD4E.js → chunk-RW5PEJ7D.js} +11 -6
  109. package/dist/{chunk-DYWQFQJP.js → chunk-SG4PRYO3.js} +22 -20
  110. package/dist/{chunk-NNS4E7CW.js → chunk-SGPFVDUL.js} +15 -6
  111. package/dist/{chunk-ZEF5M5MB.js → chunk-SQCBSFT5.js} +72 -74
  112. package/dist/{chunk-3HGU63UY.js → chunk-U7NKYLX2.js} +14 -13
  113. package/dist/chunk-U7VO6JAA.js +566 -0
  114. package/dist/{chunk-ZJV7GYR4.js → chunk-UFQJMOVZ.js} +10 -10
  115. package/dist/{chunk-Z72XRXD4.js → chunk-UJRTPOHH.js} +3 -3
  116. package/dist/{chunk-J6QROYDA.js → chunk-UK6DKJLD.js} +26 -10
  117. package/dist/{chunk-QZX5S6R3.js → chunk-UKP5HWB5.js} +149 -89
  118. package/dist/{chunk-T4A4TSTJ.js → chunk-UQRIKAMH.js} +21 -19
  119. package/dist/{chunk-TYS6CSS5.js → chunk-UU6LUYUH.js} +2 -2
  120. package/dist/{chunk-5BHJZH62.js → chunk-UVDCHR4E.js} +18 -6
  121. package/dist/{chunk-HQQNCY7W.js → chunk-VAO6ANGC.js} +5 -5
  122. package/dist/{chunk-6NYWYNBI.js → chunk-VGNIDAPQ.js} +4 -4
  123. package/dist/{chunk-VL2O2F3W.js → chunk-VHO3YBTB.js} +2 -2
  124. package/dist/{chunk-FHGVORFO.js → chunk-VIRANCNY.js} +14 -14
  125. package/dist/{chunk-XD6ZYURY.js → chunk-VJHBSPGF.js} +7 -14
  126. package/dist/{chunk-5RP7JRVP.js → chunk-VKNCC2GX.js} +12 -8
  127. package/dist/{chunk-TPFKKXCB.js → chunk-VRRDUYOK.js} +7 -7
  128. package/dist/{chunk-K2I2XOZD.js → chunk-W2DINCHL.js} +11 -11
  129. package/dist/{chunk-AUIM2NVH.js → chunk-W5KMNC5U.js} +8 -8
  130. package/dist/{chunk-FFUN5D66.js → chunk-WF64MW5G.js} +2 -2
  131. package/dist/{chunk-6LBQ7ELG.js → chunk-X3RXGSCZ.js} +40 -21
  132. package/dist/{chunk-HBPDFCQY.js → chunk-XEAQDIMF.js} +7 -6
  133. package/dist/{chunk-TTIM6YTT.js → chunk-XG4NWFPY.js} +17 -16
  134. package/dist/{chunk-7KINELPI.js → chunk-XQ4KN6JQ.js} +23 -23
  135. package/dist/{chunk-WBRZJOEC.js → chunk-XY3UDKKH.js} +4 -4
  136. package/dist/{chunk-5SEVICBT.js → chunk-YG2BEIQU.js} +14 -13
  137. package/dist/{chunk-BRENJ2BQ.js → chunk-YJVA3QUU.js} +24 -4
  138. package/dist/{chunk-KGVZ5VGH.js → chunk-YR43LJT5.js} +16 -15
  139. package/dist/{chunk-TLVIHFRO.js → chunk-ZKPPI6BF.js} +2 -2
  140. package/dist/{chunk-ARGMQ3H4.js → chunk-ZXUAPTCK.js} +3 -3
  141. package/dist/cli.cjs +156 -154
  142. package/dist/clock.js +9 -1
  143. package/dist/countdown.js +4 -3
  144. package/dist/dgmo-router-B4S23EBH.js +86 -0
  145. package/dist/dimensions-K6PGYYGT.js +22 -0
  146. package/dist/editor.js +33 -5
  147. package/dist/element.js +142 -132
  148. package/dist/element.mjs +1117 -452
  149. package/dist/flowchart-parser-LALR6J6F.js +21 -0
  150. package/dist/{flowchart-renderer-MNCDS7MV.js → flowchart-renderer-DXYB5YRF.js} +14 -14
  151. package/dist/highlight.js +37 -6
  152. package/dist/index.d.ts +4 -2
  153. package/dist/index.js +78 -73
  154. package/dist/layout-3BVDMKXI.js +15 -0
  155. package/dist/layout-3M5UYJHY.js +14 -0
  156. package/dist/{layout-2L3WMRYU.js → layout-3V2A4MUE.js} +8 -8
  157. package/dist/layout-4IHCMWZN.js +19 -0
  158. package/dist/layout-5XGYNYVT.js +9 -0
  159. package/dist/layout-AZUPXWSK.js +15 -0
  160. package/dist/{layout-F4WO357A.js → layout-CAAKQX4W.js} +3 -3
  161. package/dist/{layout-A5UHRWVH.js → layout-CB3XWCOI.js} +3 -3
  162. package/dist/{layout-OAC4PEAC.js → layout-E6HR6PXH.js} +7 -7
  163. package/dist/{layout-7QXCNOVS.js → layout-HBWT2G23.js} +3 -3
  164. package/dist/layout-KPQWJNAG.js +14 -0
  165. package/dist/layout-OL2XFJXA.js +17 -0
  166. package/dist/layout-WCAIKGZG.js +19 -0
  167. package/dist/layout-WSMPBQ4Q.js +17 -0
  168. package/dist/layout-ZE5DRJWX.js +20 -0
  169. package/dist/{layout-search-55FU6HD3.js → layout-search-34DPZD43.js} +51 -35
  170. package/dist/legend-constants-P5EQWRQQ.js +49 -0
  171. package/dist/{palettes-UDLZ2SA4.js → palettes-FGTNFARW.js} +4 -4
  172. package/dist/{parser-EYCKNKQA.js → parser-275KP7JO.js} +8 -8
  173. package/dist/parser-27Z76SN3.js +22 -0
  174. package/dist/parser-3CODK6RG.js +15 -0
  175. package/dist/{parser-YKYMQG3B.js → parser-3KIGEEJM.js} +6 -6
  176. package/dist/parser-3VSWEKBF.js +15 -0
  177. package/dist/parser-4E4I3GNT.js +13 -0
  178. package/dist/parser-5FGJSQNS.js +12 -0
  179. package/dist/parser-5HQXCNOS.js +12 -0
  180. package/dist/parser-5QPFOB5F.js +12 -0
  181. package/dist/parser-6B6SSZT7.js +11 -0
  182. package/dist/parser-6PPHAXXH.js +14 -0
  183. package/dist/parser-7JHWEWXM.js +13 -0
  184. package/dist/{parser-DQAFLUJB.js → parser-7JVIDLLR.js} +7 -7
  185. package/dist/parser-CKA3QMAG.js +18 -0
  186. package/dist/parser-CRKLXGW7.js +13 -0
  187. package/dist/parser-EXNBDNGY.js +11 -0
  188. package/dist/parser-FF4BMRSB.js +16 -0
  189. package/dist/parser-FOOFEJQU.js +13 -0
  190. package/dist/parser-G26MTBFS.js +11 -0
  191. package/dist/parser-GKP2MHHL.js +11 -0
  192. package/dist/parser-GN2MM5CC.js +11 -0
  193. package/dist/parser-IKWK2ZU3.js +18 -0
  194. package/dist/parser-IVGHXLZP.js +14 -0
  195. package/dist/parser-NNIL3HXU.js +11 -0
  196. package/dist/parser-RNLTBIJ6.js +19 -0
  197. package/dist/parser-UWRX52PC.js +16 -0
  198. package/dist/parser-VOERD6XR.js +14 -0
  199. package/dist/parser-XNKJCMMA.js +13 -0
  200. package/dist/parser-XVPTRWZ4.js +15 -0
  201. package/dist/parser-YSLNDFLV.js +22 -0
  202. package/dist/{parser-ZL5QUFVH.js → parser-ZGNXYFPS.js} +5 -5
  203. package/dist/parser-ZQDQTB4X.js +20 -0
  204. package/dist/pert.d.ts +2 -0
  205. package/dist/registry-CaSw0PSH.d.ts +350 -0
  206. package/dist/renderer-2C6DJRYN.js +24 -0
  207. package/dist/renderer-5MPN7CXZ.js +19 -0
  208. package/dist/renderer-6EN5XXJZ.js +27 -0
  209. package/dist/renderer-6WRYVM5R.js +24 -0
  210. package/dist/renderer-7NQSTQMQ.js +16 -0
  211. package/dist/renderer-7ZNVOVFG.js +16 -0
  212. package/dist/{renderer-DT4XRFEP.js → renderer-AK2WO4EI.js} +7 -7
  213. package/dist/renderer-COLQFSFV.js +26 -0
  214. package/dist/{renderer-3F7OS2AX.js → renderer-CWEFGEIC.js} +15 -15
  215. package/dist/renderer-D3BRNOMV.js +29 -0
  216. package/dist/renderer-HIFQVEJA.js +22 -0
  217. package/dist/renderer-JCZG6AYU.js +28 -0
  218. package/dist/{renderer-ZWIH4WDW.js → renderer-JTCX6PCG.js} +13 -13
  219. package/dist/renderer-JWGQSTMR.js +37 -0
  220. package/dist/{renderer-NHO46CT2.js → renderer-K7XLVPH5.js} +6 -6
  221. package/dist/renderer-KARQF3F3.js +22 -0
  222. package/dist/renderer-LVZAHRP6.js +34 -0
  223. package/dist/{renderer-CQY6ATOS.js → renderer-NE5W7CQP.js} +8 -8
  224. package/dist/renderer-QD2SJF2I.js +34 -0
  225. package/dist/renderer-RLMOTXZ6.js +37 -0
  226. package/dist/renderer-SQ54MDSA.js +26 -0
  227. package/dist/{renderer-HSHHVYWF.js → renderer-TYHGXAVT.js} +7 -7
  228. package/dist/renderer-UKXW2QPE.js +23 -0
  229. package/dist/renderer-UPF7NRTI.js +28 -0
  230. package/dist/renderer-VMH6L6CP.js +24 -0
  231. package/dist/{renderer-5DAKMIU2.js → renderer-WCWI4I6A.js} +5 -5
  232. package/dist/renderer-WFSUBKUL.js +25 -0
  233. package/dist/renderer-WNFXYYGL.js +21 -0
  234. package/dist/{renderer-KNFTTOBL.js → renderer-XTXSOK3L.js} +14 -14
  235. package/dist/renderer-YUKPS27O.js +24 -0
  236. package/dist/renderer-YUXGXGSW.js +27 -0
  237. package/dist/renderer-ZBTEX2JK.js +16 -0
  238. package/dist/renderer-radial-WF6VOUOD.js +21 -0
  239. package/dist/{resolver-EMTGNR3D.js → resolver-WFKZYBJ2.js} +4 -4
  240. package/dist/state-parser-L7DHZIQO.js +19 -0
  241. package/dist/state-renderer-TFEUOP4H.js +38 -0
  242. package/dist/{themes-32xj5ZTW.d.ts → themes-tbBkIVsn.d.ts} +2 -349
  243. package/docs/ai-integration.md +59 -58
  244. package/docs/language-reference.md +323 -286
  245. package/fonts/Inter-Bold.woff2 +0 -0
  246. package/fonts/Inter-Regular.woff2 +0 -0
  247. package/gallery/fixtures/boxes-and-lines-diverging.dgmo +1 -2
  248. package/gallery/fixtures/boxes-and-lines.dgmo +1 -1
  249. package/gallery/fixtures/clock.dgmo +1 -1
  250. package/gallery/fixtures/event-line.dgmo +1 -1
  251. package/gallery/fixtures/gantt-full.dgmo +1 -1
  252. package/gallery/fixtures/gantt-sprints.dgmo +9 -9
  253. package/gallery/fixtures/gantt.dgmo +1 -1
  254. package/gallery/fixtures/legacy-syntax-backcompat.dgmo +16 -0
  255. package/gallery/fixtures/state-tags.dgmo +40 -0
  256. package/gallery/fixtures/swimlane.dgmo +1 -1
  257. package/gallery/fixtures/timeline-intraday.dgmo +1 -1
  258. package/package.json +5 -1
  259. package/dist/analyzer-BJ54GIFB.js +0 -15
  260. package/dist/calculator-LDYQCDDY.js +0 -11
  261. package/dist/charts-d3-MXEP6BNP.js +0 -26
  262. package/dist/dgmo-router-X7ZGAPIE.js +0 -86
  263. package/dist/dimensions-45H6JQEY.js +0 -22
  264. package/dist/flowchart-parser-UKELHBSB.js +0 -21
  265. package/dist/layout-46N4JJYK.js +0 -17
  266. package/dist/layout-A4XWNVID.js +0 -14
  267. package/dist/layout-AM2JWW3X.js +0 -15
  268. package/dist/layout-EE6GE46Y.js +0 -17
  269. package/dist/layout-G23F7LDH.js +0 -9
  270. package/dist/layout-GM4UGR7B.js +0 -19
  271. package/dist/layout-GQQJXNYH.js +0 -15
  272. package/dist/layout-IIE2S5FK.js +0 -20
  273. package/dist/layout-VMCRX66I.js +0 -14
  274. package/dist/layout-ZDOSXL5G.js +0 -19
  275. package/dist/parser-2ECN6JA2.js +0 -16
  276. package/dist/parser-4WTIHM5J.js +0 -11
  277. package/dist/parser-5H2BTSJQ.js +0 -11
  278. package/dist/parser-75KVXUNS.js +0 -16
  279. package/dist/parser-774AC53T.js +0 -13
  280. package/dist/parser-BE4YCSRU.js +0 -11
  281. package/dist/parser-CNGUQUQI.js +0 -11
  282. package/dist/parser-DEUUSQIN.js +0 -22
  283. package/dist/parser-FXISIU6A.js +0 -14
  284. package/dist/parser-GTB4XC7P.js +0 -18
  285. package/dist/parser-GVEPAUSD.js +0 -12
  286. package/dist/parser-HSQGTY4G.js +0 -19
  287. package/dist/parser-HVKRINUS.js +0 -11
  288. package/dist/parser-J5MIEYO4.js +0 -15
  289. package/dist/parser-KMZIXLDZ.js +0 -13
  290. package/dist/parser-LJHXWSXU.js +0 -14
  291. package/dist/parser-PT3W72G5.js +0 -15
  292. package/dist/parser-SBJQDPHL.js +0 -15
  293. package/dist/parser-SUWWWCMX.js +0 -12
  294. package/dist/parser-SXDRNZOA.js +0 -13
  295. package/dist/parser-UMILOW3M.js +0 -14
  296. package/dist/parser-UP3AZ367.js +0 -13
  297. package/dist/parser-WHPRV3X5.js +0 -20
  298. package/dist/parser-WX3UODSJ.js +0 -12
  299. package/dist/parser-XCIEN3KN.js +0 -13
  300. package/dist/parser-XIRTUMVN.js +0 -18
  301. package/dist/parser-YKCGIG45.js +0 -11
  302. package/dist/parser-ZGM2OJ6Y.js +0 -22
  303. package/dist/renderer-4QB4K7Q3.js +0 -24
  304. package/dist/renderer-5H4SY7YY.js +0 -22
  305. package/dist/renderer-5PIQPFOX.js +0 -16
  306. package/dist/renderer-6MU37QF5.js +0 -19
  307. package/dist/renderer-AQVMWJ4M.js +0 -27
  308. package/dist/renderer-BDDW6K7N.js +0 -24
  309. package/dist/renderer-BNB3VIDY.js +0 -26
  310. package/dist/renderer-CVW75CXL.js +0 -37
  311. package/dist/renderer-ERHBSTQT.js +0 -25
  312. package/dist/renderer-IDPVUFNB.js +0 -28
  313. package/dist/renderer-ILX6BGKR.js +0 -21
  314. package/dist/renderer-JP663HKG.js +0 -22
  315. package/dist/renderer-MVUZBESR.js +0 -16
  316. package/dist/renderer-N4EBR2FV.js +0 -28
  317. package/dist/renderer-NO2QJ5LZ.js +0 -27
  318. package/dist/renderer-OGFV5RXX.js +0 -34
  319. package/dist/renderer-PDLESJMS.js +0 -37
  320. package/dist/renderer-T4MTNINC.js +0 -24
  321. package/dist/renderer-TOUGFGTM.js +0 -24
  322. package/dist/renderer-U3KPD43Y.js +0 -34
  323. package/dist/renderer-X334IJU2.js +0 -26
  324. package/dist/renderer-Y5OAMOCR.js +0 -23
  325. package/dist/renderer-YONAKAJU.js +0 -16
  326. package/dist/renderer-YWMI6Y2U.js +0 -29
  327. package/dist/renderer-radial-7CGZMVMS.js +0 -21
  328. package/dist/state-parser-BATWAID2.js +0 -18
  329. package/dist/state-renderer-3FKZJ3LP.js +0 -34
package/dist/auto.mjs CHANGED
@@ -104,7 +104,7 @@ function nameMergedMessage(args) {
104
104
  function emptyMetadataValueMessage(key) {
105
105
  return `Metadata key '${key}:' has no value \u2014 the pair is dropped. Provide a value or remove the key.`;
106
106
  }
107
- var NAME_DIAGNOSTIC_CODES, METADATA_DIAGNOSTIC_CODES;
107
+ var NAME_DIAGNOSTIC_CODES, METADATA_DIAGNOSTIC_CODES, TITLE_DIRECTIVE_DX;
108
108
  var init_diagnostics = __esm({
109
109
  "src/diagnostics.ts"() {
110
110
  "use strict";
@@ -135,6 +135,15 @@ var init_diagnostics = __esm({
135
135
  */
136
136
  EMPTY_METADATA_VALUE: "W_EMPTY_METADATA_VALUE"
137
137
  };
138
+ TITLE_DIRECTIVE_DX = {
139
+ code: "E_TITLE_DIRECTIVE",
140
+ severity: "error",
141
+ chartType: null,
142
+ title: "title directive on a data chart",
143
+ message: "The `title` directive is not supported \u2014 the chart title is line 1 \u2014 remove the title directive.",
144
+ hint: "Put the title on the declaration line (`bar My Chart`) and delete the `title` line.",
145
+ example: "bar Revenue\ntitle Revenue\nQ1 100"
146
+ };
138
147
  }
139
148
  });
140
149
 
@@ -430,7 +439,7 @@ function withTagAliases(base, aliases) {
430
439
  function isReservedKey(registry, key) {
431
440
  return registry.keys.has(key) || registry.tagAliases.has(key);
432
441
  }
433
- var SEQUENCE_REGISTRY, INFRA_REGISTRY, MAP_REGISTRY, ORG_REGISTRY, C4_REGISTRY, ER_REGISTRY, EVENT_LINE_REGISTRY, KANBAN_REGISTRY, SITEMAP_REGISTRY, GANTT_REGISTRY, PERT_REGISTRY, BOXES_AND_LINES_REGISTRY, SKETCH_REGISTRY, TIMELINE_REGISTRY, MINDMAP_REGISTRY, TECH_RADAR_REGISTRY, CYCLE_REGISTRY, JOURNEY_MAP_REGISTRY, PYRAMID_REGISTRY, RING_REGISTRY, TREEMAP_REGISTRY, RACI_REGISTRY, FAMILY_PERSON_REGISTRY, FAMILY_UNION_REGISTRY;
442
+ var SEQUENCE_REGISTRY, INFRA_REGISTRY, MAP_REGISTRY, ORG_REGISTRY, C4_REGISTRY, ER_REGISTRY, EVENT_LINE_REGISTRY, KANBAN_REGISTRY, SITEMAP_REGISTRY, GANTT_REGISTRY, PERT_REGISTRY, BOXES_AND_LINES_REGISTRY, STATE_REGISTRY, SKETCH_REGISTRY, TIMELINE_REGISTRY, MINDMAP_REGISTRY, TECH_RADAR_REGISTRY, CYCLE_REGISTRY, JOURNEY_MAP_REGISTRY, PYRAMID_REGISTRY, RING_REGISTRY, TREEMAP_REGISTRY, RACI_REGISTRY, FAMILY_PERSON_REGISTRY, FAMILY_UNION_REGISTRY;
434
443
  var init_reserved_key_registry = __esm({
435
444
  "src/utils/reserved-key-registry.ts"() {
436
445
  "use strict";
@@ -522,6 +531,7 @@ var init_reserved_key_registry = __esm({
522
531
  "description",
523
532
  "heat"
524
533
  ]);
534
+ STATE_REGISTRY = staticRegistry([]);
525
535
  SKETCH_REGISTRY = staticRegistry([
526
536
  "shape",
527
537
  "at",
@@ -623,6 +633,9 @@ function extractColor(label, palette, diagnostics, line11) {
623
633
  ...color !== void 0 && { color }
624
634
  };
625
635
  }
636
+ function legendSuppressed(options) {
637
+ return options["no-legend"] === "on";
638
+ }
626
639
  function fillModeFromToken(token) {
627
640
  switch (token.trim().toLowerCase()) {
628
641
  case "fill-solid":
@@ -831,6 +844,17 @@ function peelTrailingColorName(label) {
831
844
  colorName: trailing
832
845
  };
833
846
  }
847
+ function peelTrailingCollapsedFlag(text) {
848
+ const t = text.trimEnd();
849
+ if (t === "collapsed") return { rest: "", collapsed: true };
850
+ if (/[ \t]collapsed$/.test(t)) {
851
+ return {
852
+ rest: t.slice(0, t.length - "collapsed".length).trimEnd().replace(/,\s*$/, ""),
853
+ collapsed: true
854
+ };
855
+ }
856
+ return { rest: text, collapsed: false };
857
+ }
834
858
  function peelRampColors(label) {
835
859
  const peelOne = (s) => {
836
860
  const idx = Math.max(s.lastIndexOf(" "), s.lastIndexOf(" "));
@@ -1075,7 +1099,8 @@ var init_parsing = __esm({
1075
1099
  "fill-solid",
1076
1100
  "fill-outline",
1077
1101
  "no-title",
1078
- "no-notes"
1102
+ "no-notes",
1103
+ "no-legend"
1079
1104
  ]);
1080
1105
  FILL_FAMILY_TOKENS = /* @__PURE__ */ new Set([
1081
1106
  "fill-tint",
@@ -2434,7 +2459,7 @@ function parseVisualizationFull(content, palette) {
2434
2459
  }
2435
2460
  continue;
2436
2461
  }
2437
- if (!/^(era|marker|tag|sort|active-tag|swimlanes|no-scale|lane-by|fill-tint|fill-solid|fill-outline)\b/i.test(
2462
+ if (!/^(era|marker|tag|sort|active-tag|swimlanes|no-scale|no-legend|lane-by|fill-tint|fill-solid|fill-outline)\b/i.test(
2438
2463
  line11
2439
2464
  ) && !line11.startsWith("[")) {
2440
2465
  warn2(lineNumber, `Expected a date at the start \u2014 e.g., '1718 ${line11}'`);
@@ -2590,11 +2615,7 @@ function parseVisualizationFull(content, palette) {
2590
2615
  continue;
2591
2616
  }
2592
2617
  if (firstToken === "title") {
2593
- result.title = restValue;
2594
- result.titleLineNumber = lineNumber;
2595
- if (result.type === "quadrant") {
2596
- result.quadrantTitleLineNumber = lineNumber;
2597
- }
2618
+ result.diagnostics.push(emit(TITLE_DIRECTIVE_DX, lineNumber));
2598
2619
  continue;
2599
2620
  }
2600
2621
  if (firstToken === "order") {
@@ -2657,6 +2678,10 @@ function parseVisualizationFull(content, palette) {
2657
2678
  result.noTitle = true;
2658
2679
  continue;
2659
2680
  }
2681
+ if (bareToken === "no-legend") {
2682
+ result.noLegend = true;
2683
+ continue;
2684
+ }
2660
2685
  if (bareToken === "orientation-vertical") {
2661
2686
  if (result.type === "arc") result.orientation = "vertical";
2662
2687
  continue;
@@ -2792,11 +2817,7 @@ function parseVisualizationFull(content, palette) {
2792
2817
  const key = rawKey.toLowerCase();
2793
2818
  const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
2794
2819
  if (key === "title") {
2795
- result.title = line11.substring(colonIndex + 1).trim();
2796
- result.titleLineNumber = lineNumber;
2797
- if (result.type === "quadrant") {
2798
- result.quadrantTitleLineNumber = lineNumber;
2799
- }
2820
+ result.diagnostics.push(emit(TITLE_DIRECTIVE_DX, lineNumber));
2800
2821
  continue;
2801
2822
  }
2802
2823
  if (key === "order") {
@@ -3273,6 +3294,8 @@ var init_registry = __esm({
3273
3294
 
3274
3295
  // src/palettes/color-utils.ts
3275
3296
  function hexToHSL(hex) {
3297
+ const cached = HEX_TO_HSL_CACHE.get(hex);
3298
+ if (cached) return { ...cached };
3276
3299
  const raw = hex.replace("#", "");
3277
3300
  const full = raw.length === 3 ? [...raw].map((c) => c + c).join("") : raw;
3278
3301
  const r = parseInt(full.substring(0, 2), 16) / 255;
@@ -3281,24 +3304,29 @@ function hexToHSL(hex) {
3281
3304
  const max = Math.max(r, g, b);
3282
3305
  const min = Math.min(r, g, b);
3283
3306
  const l = (max + min) / 2;
3307
+ let result;
3284
3308
  if (max === min) {
3285
- return { h: 0, s: 0, l: Math.round(l * 100) };
3286
- }
3287
- const d = max - min;
3288
- const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
3289
- let h;
3290
- if (max === r) {
3291
- h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
3292
- } else if (max === g) {
3293
- h = ((b - r) / d + 2) / 6;
3309
+ result = { h: 0, s: 0, l: Math.round(l * 100) };
3294
3310
  } else {
3295
- h = ((r - g) / d + 4) / 6;
3311
+ const d = max - min;
3312
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
3313
+ let h;
3314
+ if (max === r) {
3315
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
3316
+ } else if (max === g) {
3317
+ h = ((b - r) / d + 2) / 6;
3318
+ } else {
3319
+ h = ((r - g) / d + 4) / 6;
3320
+ }
3321
+ result = {
3322
+ h: Math.round(h * 360),
3323
+ s: Math.round(s * 100),
3324
+ l: Math.round(l * 100)
3325
+ };
3296
3326
  }
3297
- return {
3298
- h: Math.round(h * 360),
3299
- s: Math.round(s * 100),
3300
- l: Math.round(l * 100)
3301
- };
3327
+ if (HEX_TO_HSL_CACHE.size >= HEX_TO_HSL_CACHE_MAX) HEX_TO_HSL_CACHE.clear();
3328
+ HEX_TO_HSL_CACHE.set(hex, result);
3329
+ return { ...result };
3302
3330
  }
3303
3331
  function hslToHex(h, s, l) {
3304
3332
  const sNorm = s / 100;
@@ -3357,6 +3385,9 @@ function shade(hex, base, amount) {
3357
3385
  return `#${sr.toString(16).padStart(2, "0")}${sg.toString(16).padStart(2, "0")}${sb.toString(16).padStart(2, "0")}`;
3358
3386
  }
3359
3387
  function mix(a, b, pct) {
3388
+ const key = `${a}|${b}|${pct}`;
3389
+ const cached = MIX_CACHE.get(key);
3390
+ if (cached !== void 0) return cached;
3360
3391
  const parse = (h) => {
3361
3392
  const r = h.replace("#", "");
3362
3393
  const f = r.length === 3 ? [...r].map((c2) => c2 + c2).join("") : r;
@@ -3368,7 +3399,10 @@ function mix(a, b, pct) {
3368
3399
  };
3369
3400
  const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
3370
3401
  const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
3371
- return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
3402
+ const out = `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
3403
+ if (MIX_CACHE.size >= MIX_CACHE_MAX) MIX_CACHE.clear();
3404
+ MIX_CACHE.set(key, out);
3405
+ return out;
3372
3406
  }
3373
3407
  function valueRampColor(low, high, t, _opts) {
3374
3408
  const tc = Math.max(0, Math.min(1, t));
@@ -3470,11 +3504,15 @@ function politicalTints(palette, count, isDark) {
3470
3504
  }
3471
3505
  return out.slice(0, count);
3472
3506
  }
3473
- var POLITICAL_TINT_BANDS;
3507
+ var HEX_TO_HSL_CACHE, HEX_TO_HSL_CACHE_MAX, MIX_CACHE, MIX_CACHE_MAX, POLITICAL_TINT_BANDS;
3474
3508
  var init_color_utils = __esm({
3475
3509
  "src/palettes/color-utils.ts"() {
3476
3510
  "use strict";
3477
3511
  init_colors();
3512
+ HEX_TO_HSL_CACHE = /* @__PURE__ */ new Map();
3513
+ HEX_TO_HSL_CACHE_MAX = 5e3;
3514
+ MIX_CACHE = /* @__PURE__ */ new Map();
3515
+ MIX_CACHE_MAX = 5e3;
3478
3516
  POLITICAL_TINT_BANDS = {
3479
3517
  light: [32, 48, 64, 80],
3480
3518
  dark: [44, 58, 72, 86]
@@ -4428,23 +4466,36 @@ var init_d3_helpers = __esm({
4428
4466
  });
4429
4467
 
4430
4468
  // src/utils/text-measure.ts
4431
- function measureText(text, fontSize) {
4432
- let w = 0;
4469
+ function extendWidth(acc, text, fontSize) {
4470
+ let w = acc;
4433
4471
  for (let i = 0; i < text.length; i++) {
4434
4472
  w += (CHAR_W[text.charAt(i)] ?? DEFAULT_W) * fontSize;
4435
4473
  }
4436
4474
  return w;
4437
4475
  }
4476
+ function measureText(text, fontSize) {
4477
+ const key = `${fontSize}|${text}`;
4478
+ const cached = MEASURE_CACHE.get(key);
4479
+ if (cached !== void 0) return cached;
4480
+ const w = extendWidth(0, text, fontSize);
4481
+ if (MEASURE_CACHE.size >= MEASURE_CACHE_MAX) MEASURE_CACHE.clear();
4482
+ MEASURE_CACHE.set(key, w);
4483
+ return w;
4484
+ }
4438
4485
  function truncateText(text, fontSize, maxWidth) {
4439
4486
  if (measureText(text, fontSize) <= maxWidth) return text;
4440
4487
  const ellipsis = "\u2026";
4441
4488
  const ellipsisW = measureText(ellipsis, fontSize);
4442
4489
  if (ellipsisW > maxWidth) return "";
4490
+ const prefix = [0];
4491
+ for (let i = 0; i < text.length; i++) {
4492
+ prefix.push(extendWidth(prefix[i], text.charAt(i), fontSize));
4493
+ }
4443
4494
  let lo = 0;
4444
4495
  let hi = text.length;
4445
4496
  while (lo < hi) {
4446
4497
  const mid = Math.ceil((lo + hi) / 2);
4447
- if (measureText(text.slice(0, mid), fontSize) + ellipsisW <= maxWidth) {
4498
+ if (prefix[mid] + ellipsisW <= maxWidth) {
4448
4499
  lo = mid;
4449
4500
  } else {
4450
4501
  hi = mid - 1;
@@ -4456,15 +4507,19 @@ function wrapTextToWidth(text, fontSize, maxWidth, opts) {
4456
4507
  const words = text.split(/\s+/).filter((w) => w.length > 0);
4457
4508
  if (words.length === 0) return [""];
4458
4509
  const hardBreak = opts?.hardBreak ?? false;
4510
+ const spaceW = (CHAR_W[" "] ?? DEFAULT_W) * fontSize;
4459
4511
  const lines = [];
4460
4512
  let current = "";
4513
+ let currentW = 0;
4461
4514
  const pushWord = (word) => {
4462
- const test = current ? `${current} ${word}` : word;
4463
- if (measureText(test, fontSize) <= maxWidth || !current) {
4464
- current = test;
4515
+ const testW = extendWidth(current ? currentW + spaceW : 0, word, fontSize);
4516
+ if (testW <= maxWidth || !current) {
4517
+ current = current ? `${current} ${word}` : word;
4518
+ currentW = testW;
4465
4519
  } else {
4466
4520
  lines.push(current);
4467
4521
  current = word;
4522
+ currentW = extendWidth(0, word, fontSize);
4468
4523
  }
4469
4524
  };
4470
4525
  for (const word of words) {
@@ -4472,17 +4527,23 @@ function wrapTextToWidth(text, fontSize, maxWidth, opts) {
4472
4527
  if (current) {
4473
4528
  lines.push(current);
4474
4529
  current = "";
4530
+ currentW = 0;
4475
4531
  }
4476
4532
  let chunk = "";
4533
+ let chunkW = 0;
4477
4534
  for (const ch of word) {
4478
- if (chunk && measureText(chunk + ch, fontSize) > maxWidth) {
4535
+ const candW = extendWidth(chunkW, ch, fontSize);
4536
+ if (chunk && candW > maxWidth) {
4479
4537
  lines.push(chunk);
4480
4538
  chunk = ch;
4539
+ chunkW = extendWidth(0, ch, fontSize);
4481
4540
  } else {
4482
4541
  chunk += ch;
4542
+ chunkW = candW;
4483
4543
  }
4484
4544
  }
4485
4545
  current = chunk;
4546
+ currentW = chunkW;
4486
4547
  continue;
4487
4548
  }
4488
4549
  pushWord(word);
@@ -4490,7 +4551,7 @@ function wrapTextToWidth(text, fontSize, maxWidth, opts) {
4490
4551
  if (current) lines.push(current);
4491
4552
  return lines.length > 0 ? lines : [""];
4492
4553
  }
4493
- var CHAR_W, DEFAULT_W, CHAR_WIDTH_RATIO;
4554
+ var CHAR_W, DEFAULT_W, CHAR_WIDTH_RATIO, MEASURE_CACHE, MEASURE_CACHE_MAX;
4494
4555
  var init_text_measure = __esm({
4495
4556
  "src/utils/text-measure.ts"() {
4496
4557
  "use strict";
@@ -4583,6 +4644,8 @@ var init_text_measure = __esm({
4583
4644
  };
4584
4645
  DEFAULT_W = 0.56;
4585
4646
  CHAR_WIDTH_RATIO = DEFAULT_W;
4647
+ MEASURE_CACHE = /* @__PURE__ */ new Map();
4648
+ MEASURE_CACHE_MAX = 1e4;
4586
4649
  }
4587
4650
  });
4588
4651
 
@@ -4859,6 +4922,30 @@ var init_time_ticks = __esm({
4859
4922
  });
4860
4923
 
4861
4924
  // src/utils/legend-constants.ts
4925
+ var legend_constants_exports = {};
4926
+ __export(legend_constants_exports, {
4927
+ CONTROLS_ICON_PATH: () => CONTROLS_ICON_PATH,
4928
+ EYE_CLOSED_PATH: () => EYE_CLOSED_PATH,
4929
+ EYE_OPEN_PATH: () => EYE_OPEN_PATH,
4930
+ LEGEND_CAPSULE_PAD: () => LEGEND_CAPSULE_PAD,
4931
+ LEGEND_DOT_R: () => LEGEND_DOT_R,
4932
+ LEGEND_ENTRY_DOT_GAP: () => LEGEND_ENTRY_DOT_GAP,
4933
+ LEGEND_ENTRY_FONT_SIZE: () => LEGEND_ENTRY_FONT_SIZE,
4934
+ LEGEND_ENTRY_TRAIL: () => LEGEND_ENTRY_TRAIL,
4935
+ LEGEND_EYE_GAP: () => LEGEND_EYE_GAP,
4936
+ LEGEND_EYE_SIZE: () => LEGEND_EYE_SIZE,
4937
+ LEGEND_GEAR_PILL_W: () => LEGEND_GEAR_PILL_W,
4938
+ LEGEND_GROUP_GAP: () => LEGEND_GROUP_GAP,
4939
+ LEGEND_HEIGHT: () => LEGEND_HEIGHT,
4940
+ LEGEND_ICON_W: () => LEGEND_ICON_W,
4941
+ LEGEND_MAX_ENTRY_ROWS: () => LEGEND_MAX_ENTRY_ROWS,
4942
+ LEGEND_PILL_FONT_SIZE: () => LEGEND_PILL_FONT_SIZE,
4943
+ LEGEND_PILL_PAD: () => LEGEND_PILL_PAD,
4944
+ LEGEND_TOGGLE_DOT_R: () => LEGEND_TOGGLE_DOT_R,
4945
+ LEGEND_TOGGLE_OFF_OPACITY: () => LEGEND_TOGGLE_OFF_OPACITY,
4946
+ measureLegendText: () => measureLegendText,
4947
+ truncateLegendText: () => truncateLegendText
4948
+ });
4862
4949
  var LEGEND_HEIGHT, LEGEND_PILL_PAD, LEGEND_PILL_FONT_SIZE, LEGEND_CAPSULE_PAD, LEGEND_DOT_R, LEGEND_ENTRY_FONT_SIZE, LEGEND_ENTRY_DOT_GAP, LEGEND_ENTRY_TRAIL, LEGEND_GROUP_GAP, LEGEND_EYE_SIZE, LEGEND_EYE_GAP, LEGEND_ICON_W, LEGEND_MAX_ENTRY_ROWS, measureLegendText, truncateLegendText, EYE_OPEN_PATH, EYE_CLOSED_PATH, CONTROLS_ICON_PATH, LEGEND_TOGGLE_DOT_R, LEGEND_TOGGLE_OFF_OPACITY, LEGEND_GEAR_PILL_W;
4863
4950
  var init_legend_constants = __esm({
4864
4951
  "src/utils/legend-constants.ts"() {
@@ -6077,7 +6164,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
6077
6164
  }
6078
6165
  }
6079
6166
  const datePadding = (maxDate - minDate) * 0.05 || 0.5;
6080
- const tagLegendReserve = parsed.timelineTagGroups.length > 0 ? 36 : 0;
6167
+ const tagLegendReserve = parsed.timelineTagGroups.length > 0 && !parsed.noLegend ? 36 : 0;
6081
6168
  const idealWidth = isVertical ? 500 : Math.max(600, timelineEvents.length * 40 + 200);
6082
6169
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
6083
6170
  return {
@@ -6214,7 +6301,7 @@ function makeTimelineHoverHelpers() {
6214
6301
  };
6215
6302
  }
6216
6303
  function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, swimlaneTagGroup, activeTagGroup, onTagStateChange, viewMode, exportMode) {
6217
- if (parsed.timelineTagGroups.length === 0) return;
6304
+ if (parsed.timelineTagGroups.length === 0 || parsed.noLegend) return;
6218
6305
  const { width, textColor, groupColorMap, fillMode } = setup;
6219
6306
  const { FADE_OPACITY: FADE_OPACITY2, fadeReset, fadeToTagValue } = hovers;
6220
6307
  const title = parsed.noTitle ? null : parsed.title;
@@ -6514,7 +6601,7 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
6514
6601
  formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
6515
6602
  );
6516
6603
  }
6517
- if (timelineGroups.length > 0) {
6604
+ if (timelineGroups.length > 0 && !parsed.noLegend) {
6518
6605
  const legendY = timelineScale ? -ctx.aesthetic(75) : -ctx.aesthetic(55);
6519
6606
  renderTimelineGroupLegend(
6520
6607
  g,
@@ -7144,7 +7231,7 @@ function renderTimelineVertical(container, parsed, palette, isDark, setup, hover
7144
7231
  formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
7145
7232
  );
7146
7233
  }
7147
- if (timelineGroups.length > 0) {
7234
+ if (timelineGroups.length > 0 && !parsed.noLegend) {
7148
7235
  renderTimelineGroupLegend(
7149
7236
  g,
7150
7237
  timelineGroups,
@@ -8133,8 +8220,13 @@ function parseSequenceDgmo(content, palette) {
8133
8220
  const groupName = groupMatch[1].trim();
8134
8221
  const groupColor = groupMatch[2]?.trim();
8135
8222
  let groupMeta;
8136
- const afterBracket = groupMatch[3]?.trim() || "";
8223
+ let afterBracket = groupMatch[3]?.trim() || "";
8137
8224
  let isCollapsed = false;
8225
+ const barePeel = peelTrailingCollapsedFlag(afterBracket);
8226
+ if (barePeel.collapsed) {
8227
+ isCollapsed = true;
8228
+ afterBracket = barePeel.rest;
8229
+ }
8138
8230
  if (afterBracket) {
8139
8231
  const groupRegistry = withTagAliases(
8140
8232
  SEQUENCE_REGISTRY,
@@ -8815,7 +8907,8 @@ var init_parser7 = __esm({
8815
8907
  "fill-tint",
8816
8908
  "fill-solid",
8817
8909
  "fill-outline",
8818
- "no-title"
8910
+ "no-title",
8911
+ "no-legend"
8819
8912
  ]);
8820
8913
  NO_PREFIX_ONLY_BOOLEANS = /* @__PURE__ */ new Set(["activations"]);
8821
8914
  VALID_PARTICIPANT_TYPES = /* @__PURE__ */ new Set([
@@ -10119,6 +10212,7 @@ function parseState(content, palette) {
10119
10212
  direction: "LR",
10120
10213
  nodes: [],
10121
10214
  edges: [],
10215
+ tagGroups: [],
10122
10216
  options,
10123
10217
  diagnostics: [],
10124
10218
  error: null
@@ -10137,6 +10231,22 @@ function parseState(content, palette) {
10137
10231
  const groups = [];
10138
10232
  let contentStarted = false;
10139
10233
  let firstLineParsed = false;
10234
+ const tagGroups = result.tagGroups;
10235
+ let currentTagGroup = null;
10236
+ const metaAliasMap = /* @__PURE__ */ new Map();
10237
+ let registry = STATE_REGISTRY;
10238
+ const refreshRegistry = () => {
10239
+ registry = withTagAliases(STATE_REGISTRY, new Set(metaAliasMap.keys()));
10240
+ };
10241
+ function peelStateMeta(text) {
10242
+ if (metaAliasMap.size === 0) return { text, meta: {} };
10243
+ const cut = cutUnionMetadata(text, registry);
10244
+ if (cut === -1) return { text, meta: {} };
10245
+ return {
10246
+ text: text.substring(0, cut).trimEnd(),
10247
+ meta: parseMetadataRegion(text.substring(cut), metaAliasMap)
10248
+ };
10249
+ }
10140
10250
  const nameAliasMap = /* @__PURE__ */ new Map();
10141
10251
  function peelAlias2(seg) {
10142
10252
  const trimmed = seg.trim();
@@ -10144,10 +10254,13 @@ function parseState(content, palette) {
10144
10254
  if (!m) return { seg: trimmed };
10145
10255
  return { seg: m[1].trim(), alias: m[2] };
10146
10256
  }
10147
- function getOrCreateNode(ref, lineNumber) {
10257
+ function getOrCreateNode(ref, lineNumber, meta) {
10148
10258
  const key = ref.id;
10149
10259
  const existing = nodeMap.get(key);
10150
10260
  if (existing) {
10261
+ if (meta && Object.keys(meta).length > 0) {
10262
+ existing.metadata = { ...existing.metadata ?? {}, ...meta };
10263
+ }
10151
10264
  const incomingDisplay = displayName(ref.label);
10152
10265
  const existingDisplay = displayName(existing.label);
10153
10266
  if (incomingDisplay !== existingDisplay) {
@@ -10173,7 +10286,8 @@ function parseState(content, palette) {
10173
10286
  shape: ref.shape,
10174
10287
  lineNumber,
10175
10288
  ...ref.color && { color: ref.color },
10176
- ...currentGroup && { group: currentGroup.id }
10289
+ ...currentGroup && { group: currentGroup.id },
10290
+ ...meta && Object.keys(meta).length > 0 && { metadata: { ...meta } }
10177
10291
  };
10178
10292
  nodeMap.set(key, node);
10179
10293
  result.nodes.push(node);
@@ -10195,7 +10309,10 @@ function parseState(content, palette) {
10195
10309
  const trimmed = raw.trim();
10196
10310
  const lineNumber = i + 1;
10197
10311
  const indent = measureIndent(raw);
10198
- if (!trimmed) continue;
10312
+ if (!trimmed) {
10313
+ currentTagGroup = null;
10314
+ continue;
10315
+ }
10199
10316
  if (trimmed.startsWith("//")) continue;
10200
10317
  if (!firstLineParsed && !contentStarted) {
10201
10318
  const firstLineResult = parseFirstLine(trimmed);
@@ -10215,6 +10332,53 @@ function parseState(content, palette) {
10215
10332
  continue;
10216
10333
  }
10217
10334
  }
10335
+ if (!contentStarted) {
10336
+ const tagBlockMatch = matchTagBlockHeading(trimmed);
10337
+ if (tagBlockMatch) {
10338
+ currentTagGroup = {
10339
+ name: tagBlockMatch.name,
10340
+ ...tagBlockMatch.alias !== void 0 && {
10341
+ alias: tagBlockMatch.alias
10342
+ },
10343
+ entries: [],
10344
+ lineNumber
10345
+ };
10346
+ if (tagBlockMatch.alias) {
10347
+ metaAliasMap.set(
10348
+ normalizeName(tagBlockMatch.alias),
10349
+ tagAttrKey(tagBlockMatch.name)
10350
+ );
10351
+ }
10352
+ metaAliasMap.set(
10353
+ normalizeName(tagBlockMatch.name),
10354
+ tagAttrKey(tagBlockMatch.name)
10355
+ );
10356
+ refreshRegistry();
10357
+ tagGroups.push(currentTagGroup);
10358
+ continue;
10359
+ }
10360
+ if (currentTagGroup && indent > 0) {
10361
+ const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
10362
+ const { label, color } = extractColor(
10363
+ cleanEntry,
10364
+ palette,
10365
+ result.diagnostics,
10366
+ lineNumber
10367
+ );
10368
+ if (isDefault || currentTagGroup.entries.length === 0) {
10369
+ currentTagGroup.defaultValue = label;
10370
+ }
10371
+ currentTagGroup.entries.push({
10372
+ value: label,
10373
+ // Bare value → sentinel; finalizeAutoTagColors assigns a
10374
+ // deterministic palette color after the parse.
10375
+ color: color ?? AUTO_TAG_COLOR_SENTINEL,
10376
+ lineNumber
10377
+ });
10378
+ continue;
10379
+ }
10380
+ currentTagGroup = null;
10381
+ }
10218
10382
  const noteResult = tryCollectNote(
10219
10383
  lines,
10220
10384
  i,
@@ -10237,7 +10401,7 @@ function parseState(content, palette) {
10237
10401
  result.diagnostics,
10238
10402
  palette
10239
10403
  ) : void 0;
10240
- const groupCollapsed = groupMatch[3]?.toLowerCase() === "true";
10404
+ const groupCollapsed = groupMatch[3] !== void 0 && (groupMatch[4] === void 0 || groupMatch[4].toLowerCase() === "true");
10241
10405
  currentGroup = {
10242
10406
  id: `group:${groupLabel.toLowerCase()}`,
10243
10407
  label: groupLabel,
@@ -10251,8 +10415,9 @@ function parseState(content, palette) {
10251
10415
  continue;
10252
10416
  }
10253
10417
  if (!contentStarted) {
10254
- if (/^direction-tb$/i.test(trimmed)) {
10255
- result.direction = "TB";
10418
+ const dirBool = trimmed.match(/^direction-(lr|tb)$/i);
10419
+ if (dirBool) {
10420
+ result.direction = dirBool[1].toUpperCase();
10256
10421
  continue;
10257
10422
  }
10258
10423
  if (FILL_FAMILY_TOKENS.has(trimmed.toLowerCase())) {
@@ -10271,7 +10436,8 @@ function parseState(content, palette) {
10271
10436
  continue;
10272
10437
  }
10273
10438
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
10274
- if (optMatch && !trimmed.includes("->")) {
10439
+ const carriesTagMeta = metaAliasMap.size > 0 && cutUnionMetadata(trimmed, registry) !== -1;
10440
+ if (optMatch && !trimmed.includes("->") && !carriesTagMeta) {
10275
10441
  const key = optMatch[1].toLowerCase();
10276
10442
  const value = optMatch[2].trim();
10277
10443
  if (key === "no-color") {
@@ -10307,10 +10473,11 @@ function parseState(content, palette) {
10307
10473
  ) : null;
10308
10474
  const segments = splitArrows2(trimmed);
10309
10475
  if (segments.length === 1) {
10310
- const peeled = peelAlias2(segments[0]);
10476
+ const withMeta = peelStateMeta(segments[0]);
10477
+ const peeled = peelAlias2(withMeta.text);
10311
10478
  const ref = parseStateNodeRef(peeled.seg);
10312
10479
  if (ref) {
10313
- const node = getOrCreateNode(ref, lineNumber);
10480
+ const node = getOrCreateNode(ref, lineNumber, withMeta.meta);
10314
10481
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
10315
10482
  node.lineNumber = lineNumber;
10316
10483
  indentStack.push({ nodeId: node.id, indent });
@@ -10367,6 +10534,46 @@ function parseState(content, palette) {
10367
10534
  indentStack.push({ nodeId: lastNodeId, indent });
10368
10535
  }
10369
10536
  }
10537
+ if (tagGroups.length > 0) {
10538
+ finalizeAutoTagColors(tagGroups, palette);
10539
+ const defaults = [];
10540
+ for (const group of tagGroups) {
10541
+ if (group.defaultValue) {
10542
+ defaults.push({
10543
+ key: tagAttrKey(group.name),
10544
+ value: group.defaultValue
10545
+ });
10546
+ }
10547
+ }
10548
+ const taggable = result.nodes.filter(
10549
+ (n) => n.shape !== "pseudostate"
10550
+ );
10551
+ for (const node of taggable) {
10552
+ const meta = { ...node.metadata ?? {} };
10553
+ for (const { key, value } of defaults) {
10554
+ if (!(key in meta)) meta[key] = value;
10555
+ }
10556
+ node.metadata = meta;
10557
+ }
10558
+ validateTagValues(
10559
+ taggable.map((n) => ({
10560
+ metadata: n.metadata ?? {},
10561
+ lineNumber: n.lineNumber
10562
+ })),
10563
+ tagGroups,
10564
+ (line11, message) => result.diagnostics.push(makeDgmoError(line11, message, "warning")),
10565
+ suggest
10566
+ );
10567
+ validateTagGroupNames(
10568
+ tagGroups,
10569
+ (line11, message) => result.diagnostics.push(makeDgmoError(line11, message, "warning")),
10570
+ (line11, message) => {
10571
+ const diag = makeDgmoError(line11, message);
10572
+ result.diagnostics.push(diag);
10573
+ if (!result.error) result.error = formatDgmoError(diag);
10574
+ }
10575
+ );
10576
+ }
10370
10577
  if (groups.length > 0) result.groups = groups;
10371
10578
  if (notes.length > 0) {
10372
10579
  result.notes = notes;
@@ -10411,11 +10618,13 @@ var init_state_parser = __esm({
10411
10618
  init_diagnostics();
10412
10619
  init_arrows();
10413
10620
  init_parsing();
10621
+ init_reserved_key_registry();
10622
+ init_tag_groups();
10414
10623
  init_name_normalize();
10415
10624
  init_notes2();
10416
10625
  PSEUDOSTATE_ID = "pseudostate:[*]";
10417
10626
  PSEUDOSTATE_LABEL = "[*]";
10418
- GROUP_BRACKET_RE = /^\[([^\]]+)\](?:\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white))?(?:,?\s+collapsed:\s*(\S+))?\s*$/;
10627
+ GROUP_BRACKET_RE = /^\[([^\]]+)\](?:\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white))?(?:,?\s+(collapsed)(?::\s*(\S+))?)?\s*$/;
10419
10628
  }
10420
10629
  });
10421
10630
 
@@ -11364,6 +11573,7 @@ function parseOrg(content, palette) {
11364
11573
  roots: [],
11365
11574
  tagGroups: [],
11366
11575
  options,
11576
+ direction: "TB",
11367
11577
  diagnostics: [],
11368
11578
  error: null
11369
11579
  };
@@ -11467,7 +11677,12 @@ function parseOrg(content, palette) {
11467
11677
  }
11468
11678
  }
11469
11679
  if (KNOWN_BOOLEANS.has(trimmed.toLowerCase())) {
11470
- options[trimmed.toLowerCase()] = "on";
11680
+ const boolKey = trimmed.toLowerCase();
11681
+ if (boolKey === "direction-lr" || boolKey === "direction-tb") {
11682
+ delete options["direction-lr"];
11683
+ delete options["direction-tb"];
11684
+ }
11685
+ options[boolKey] = "on";
11471
11686
  continue;
11472
11687
  }
11473
11688
  }
@@ -11590,6 +11805,7 @@ function parseOrg(content, palette) {
11590
11805
  result.diagnostics.push(diag);
11591
11806
  result.error = formatDgmoError(diag);
11592
11807
  }
11808
+ result.direction = options["direction-lr"] ? "LR" : "TB";
11593
11809
  return result;
11594
11810
  }
11595
11811
  function parseNodeLabel(trimmed, _indent, lineNumber, _palette, counter, metaAliasMap = /* @__PURE__ */ new Map(), diagnostics, nameAliasMap) {
@@ -11671,10 +11887,12 @@ var init_parser10 = __esm({
11671
11887
  KNOWN_BOOLEANS = /* @__PURE__ */ new Set([
11672
11888
  "show-sub-node-count",
11673
11889
  "direction-tb",
11890
+ "direction-lr",
11674
11891
  "fill-tint",
11675
11892
  "fill-solid",
11676
11893
  "fill-outline",
11677
- "no-title"
11894
+ "no-title",
11895
+ "no-legend"
11678
11896
  ]);
11679
11897
  }
11680
11898
  });
@@ -11846,8 +12064,9 @@ function parseSitemap(content, palette) {
11846
12064
  continue;
11847
12065
  }
11848
12066
  if (!contentStarted && !currentTagGroup && measureIndent(line11) === 0 && !trimmed.includes("|") && !trimmed.includes("->") && !trimmed.includes(":")) {
11849
- if (/^direction-tb$/i.test(trimmed)) {
11850
- result.direction = "TB";
12067
+ const dirBool = trimmed.match(/^direction-(lr|tb)$/i);
12068
+ if (dirBool) {
12069
+ result.direction = dirBool[1].toUpperCase();
11851
12070
  continue;
11852
12071
  }
11853
12072
  if (tryParseSharedOption(trimmed, options)) {
@@ -12418,9 +12637,10 @@ function addGanttDuration(startDate, duration, holidays, holidaySet, direction =
12418
12637
  }
12419
12638
  }
12420
12639
  function parseDuration(s) {
12421
- const match = s.trim().match(/^(\d+(?:\.\d+)?)(min|bd|d|w|m|q|y|h|s)$/);
12640
+ const match = s.trim().match(/^(\d+(?:\.\d+)?)(min|bd|sp|d|w|m|q|y|h|s)$/);
12422
12641
  if (!match) return null;
12423
- return { amount: parseFloat(match[1]), unit: match[2] };
12642
+ const unit = match[2] === "sp" ? "s" : match[2];
12643
+ return { amount: parseFloat(match[1]), unit };
12424
12644
  }
12425
12645
  function parseOffset(value) {
12426
12646
  const trimmed = value.trim();
@@ -12603,7 +12823,8 @@ function parseEdgeLabel(label, defaultUnit) {
12603
12823
  if (numRaw) {
12604
12824
  const amount = parseFloat(numRaw);
12605
12825
  if (amount !== 0) {
12606
- const unit = unitRaw ? unitRaw.toLowerCase() : defaultUnit;
12826
+ const unitLow = unitRaw?.toLowerCase();
12827
+ const unit = unitLow ? unitLow === "sp" ? "s" : unitLow : defaultUnit;
12607
12828
  if (!isValidDurationUnit(unit)) return null;
12608
12829
  lag = { amount, unit };
12609
12830
  }
@@ -12702,7 +12923,7 @@ function parseEstimateToken(token, defaultUnit) {
12702
12923
  if (!m) return null;
12703
12924
  const amount = parseFloat(m[1]);
12704
12925
  if (!Number.isFinite(amount)) return null;
12705
- const unit = m[2] ?? defaultUnit;
12926
+ const unit = (m[2] === "sp" ? "s" : m[2]) ?? defaultUnit;
12706
12927
  if (unit === "s") {
12707
12928
  return { amount, unit: defaultUnit };
12708
12929
  }
@@ -12918,7 +13139,13 @@ function parsePert(content, parseOpts = {}) {
12918
13139
  contentStarted = true;
12919
13140
  currentTagGroup = null;
12920
13141
  const name = groupMatch[1].trim();
12921
- const tail = (groupMatch[2] ?? "").trim();
13142
+ let tail = (groupMatch[2] ?? "").trim();
13143
+ let bareCollapsed = false;
13144
+ const barePeel = peelTrailingCollapsedFlag(tail);
13145
+ if (barePeel.collapsed) {
13146
+ bareCollapsed = true;
13147
+ tail = barePeel.rest;
13148
+ }
12922
13149
  const meta = tail ? parsePipeMetadata(tail, metaAliasMap) : {};
12923
13150
  const id = `[${normalizeName(name)}]`;
12924
13151
  const tags = {};
@@ -12929,7 +13156,7 @@ function parsePert(content, parseOpts = {}) {
12929
13156
  id,
12930
13157
  name,
12931
13158
  activityIds: [],
12932
- collapsed: meta["collapsed"] === "true",
13159
+ collapsed: bareCollapsed || meta["collapsed"] === "true",
12933
13160
  lineNumber,
12934
13161
  ...Object.keys(tags).length > 0 && { tags }
12935
13162
  });
@@ -13333,6 +13560,7 @@ function applyDirective(key, value, lineNumber, options, error, warn2, dateCfg)
13333
13560
  case "no-current-year":
13334
13561
  return;
13335
13562
  case "time-unit": {
13563
+ const normalized = value === "sp" ? "s" : value;
13336
13564
  const valid = [
13337
13565
  "min",
13338
13566
  "h",
@@ -13344,14 +13572,15 @@ function applyDirective(key, value, lineNumber, options, error, warn2, dateCfg)
13344
13572
  "y",
13345
13573
  "s"
13346
13574
  ];
13347
- if (!valid.includes(value)) {
13575
+ if (!valid.includes(normalized)) {
13576
+ const shown = valid.map((u) => u === "s" ? "sp" : u);
13348
13577
  error(
13349
13578
  lineNumber,
13350
- `Unknown time-unit '${value}'. Expected one of ${valid.join(", ")}.`
13579
+ `Unknown time-unit '${value}'. Expected one of ${shown.join(", ")}.`
13351
13580
  );
13352
13581
  return;
13353
13582
  }
13354
- options.timeUnit = value;
13583
+ options.timeUnit = normalized;
13355
13584
  return;
13356
13585
  }
13357
13586
  case "default-confidence": {
@@ -13362,6 +13591,10 @@ function applyDirective(key, value, lineNumber, options, error, warn2, dateCfg)
13362
13591
  options.noTitle = true;
13363
13592
  return;
13364
13593
  }
13594
+ case "no-legend": {
13595
+ options.noLegend = true;
13596
+ return;
13597
+ }
13365
13598
  case "fill-solid": {
13366
13599
  options.fillMode = "solid";
13367
13600
  return;
@@ -13439,6 +13672,14 @@ function applyDirective(key, value, lineNumber, options, error, warn2, dateCfg)
13439
13672
  options.direction = upper;
13440
13673
  return;
13441
13674
  }
13675
+ case "direction-lr": {
13676
+ options.direction = "LR";
13677
+ return;
13678
+ }
13679
+ case "direction-tb": {
13680
+ options.direction = "TB";
13681
+ return;
13682
+ }
13442
13683
  case "node-detail": {
13443
13684
  if (value !== "compact" && value !== "full") {
13444
13685
  error(
@@ -13610,7 +13851,11 @@ var init_parser12 = __esm({
13610
13851
  DIRECTIVE_KEYS = /* @__PURE__ */ new Set([
13611
13852
  "time-unit",
13612
13853
  "default-confidence",
13854
+ // Canonical booleans (§1.9, last one wins); key+value `direction LR|TB`
13855
+ // accepted legacy.
13613
13856
  "direction",
13857
+ "direction-lr",
13858
+ "direction-tb",
13614
13859
  "node-detail",
13615
13860
  "no-analysis",
13616
13861
  "trials",
@@ -13619,6 +13864,7 @@ var init_parser12 = __esm({
13619
13864
  "start-date",
13620
13865
  "end-date",
13621
13866
  "no-title",
13867
+ "no-legend",
13622
13868
  "fill-tint",
13623
13869
  "fill-solid",
13624
13870
  "fill-outline",
@@ -13656,7 +13902,7 @@ var init_parser12 = __esm({
13656
13902
  {
13657
13903
  stem: "time",
13658
13904
  canonical: "time-unit",
13659
- matches: /^(d|w|m|q|y|h|min|bd|s)$/i
13905
+ matches: /^(d|w|m|q|y|h|min|bd|sp|s)$/i
13660
13906
  }
13661
13907
  ];
13662
13908
  GROUP_HEADER_RE = /^\[([^\]]+)\]\s*(.*)$/;
@@ -13675,7 +13921,7 @@ var init_parser12 = __esm({
13675
13921
  "s"
13676
13922
  ]);
13677
13923
  ALIAS_SUFFIX_RE = /\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*$/;
13678
- ESTIMATE_TOKEN_RE = /^(\d+(?:\.\d+)?)(min|bd|d|w|m|q|y|h|s)?$/;
13924
+ ESTIMATE_TOKEN_RE = /^(\d+(?:\.\d+)?)(min|bd|sp|d|w|m|q|y|h|s)?$/;
13679
13925
  DEFAULT_OPTIONS = {
13680
13926
  timeUnit: "d",
13681
13927
  direction: "LR",
@@ -14045,6 +14291,8 @@ function parseChart(content, palette) {
14045
14291
  else result.fillMode = fm;
14046
14292
  } else if (firstToken === "no-title") {
14047
14293
  result.noTitle = true;
14294
+ } else if (firstToken === "no-legend") {
14295
+ result.noLegend = true;
14048
14296
  } else if (firstToken === "no-auto-y") {
14049
14297
  result.noAutoY = true;
14050
14298
  } else if (firstToken === "fill") {
@@ -14070,8 +14318,7 @@ function parseChart(content, palette) {
14070
14318
  continue;
14071
14319
  }
14072
14320
  if (firstToken === "title") {
14073
- result.title = value;
14074
- result.titleLineNumber = lineNumber;
14321
+ result.diagnostics.push(emit(TITLE_DIRECTIVE_DX, lineNumber));
14075
14322
  continue;
14076
14323
  }
14077
14324
  if (firstToken === "x-label") {
@@ -14343,6 +14590,7 @@ var init_chart = __esm({
14343
14590
  "fill-solid",
14344
14591
  "fill-outline",
14345
14592
  "no-title",
14593
+ "no-legend",
14346
14594
  "no-auto-y",
14347
14595
  "fill",
14348
14596
  "hole"
@@ -14632,8 +14880,7 @@ function parseExtendedChartFull(content, palette) {
14632
14880
  continue;
14633
14881
  }
14634
14882
  if (firstToken === "title") {
14635
- result.title = value;
14636
- result.titleLineNumber = lineNumber;
14883
+ result.diagnostics.push(emit(TITLE_DIRECTIVE_DX, lineNumber));
14637
14884
  continue;
14638
14885
  }
14639
14886
  if (firstToken === "series") {
@@ -14729,6 +14976,10 @@ function parseExtendedChartFull(content, palette) {
14729
14976
  result.noTitle = true;
14730
14977
  continue;
14731
14978
  }
14979
+ if (firstToken === "no-legend" && spaceIdx < 0) {
14980
+ result.noLegend = true;
14981
+ continue;
14982
+ }
14732
14983
  if (firstToken.startsWith("no-") && spaceIdx < 0) {
14733
14984
  continue;
14734
14985
  }
@@ -15000,8 +15251,9 @@ var init_diagnostics4 = __esm({
15000
15251
  },
15001
15252
  // parser.ts emits three wordings under this one code (each passes the full
15002
15253
  // wording as `reason`):
15003
- // directive handler: `event-line is horizontal-only in v1; \`direction ${dir}\`
15004
- // (vertical orientation) is a fast-follow.`
15254
+ // directive handler: `event-line is horizontal-only in v1; \`direction-tb\`
15255
+ // (vertical orientation) is a fast-follow.` (the
15256
+ // legacy key+value form words it as \`direction ${dir}\`)
15005
15257
  // `section` seam: 'Group events with `[Name]` era brackets (§28.6a), not `section`.'
15006
15258
  // parseEventHeader: 'event-line events are points; a date range (`->`) is not
15007
15259
  // supported — using the start date.'
@@ -15011,10 +15263,10 @@ var init_diagnostics4 = __esm({
15011
15263
  severity: "warning",
15012
15264
  chartType: "event-line",
15013
15265
  title: "Unsupported construct",
15014
- message: (p) => typeof p.reason === "string" && p.reason ? p.reason : "event-line is horizontal-only in v1; `direction TB` (vertical orientation) is a fast-follow.",
15015
- hint: "Only horizontal (`direction LR`) is supported; use `[Name]` era brackets (not `section`); events are single points, not date ranges (`->`).",
15266
+ message: (p) => typeof p.reason === "string" && p.reason ? p.reason : "event-line is horizontal-only in v1; `direction-tb` (vertical orientation) is a fast-follow.",
15267
+ hint: "Only horizontal (`direction-lr`) is supported; use `[Name]` era brackets (not `section`); events are single points, not date ranges (`->`).",
15016
15268
  example: `event-line X
15017
- direction TB
15269
+ direction-tb
15018
15270
 
15019
15271
  2020 A
15020
15272
  one`
@@ -15231,6 +15483,12 @@ function parseEventLine(content, palette) {
15231
15483
  if (cm) {
15232
15484
  collapsed = cm[1].toLowerCase() === "true";
15233
15485
  rest = (rest.slice(0, cm.index) + rest.slice(cm.index + cm[0].length)).trim();
15486
+ } else {
15487
+ const bare = rest.match(/(?:^|\s)collapsed(?=\s|$)/);
15488
+ if (bare) {
15489
+ collapsed = true;
15490
+ rest = (rest.slice(0, bare.index) + " " + rest.slice(bare.index + bare[0].length)).trim();
15491
+ }
15234
15492
  }
15235
15493
  let color = null;
15236
15494
  if (rest) {
@@ -15278,6 +15536,15 @@ function parseEventLine(content, palette) {
15278
15536
  continue;
15279
15537
  }
15280
15538
  }
15539
+ if (/^direction-lr$/i.test(trimmed)) continue;
15540
+ if (/^direction-tb$/i.test(trimmed)) {
15541
+ result.diagnostics.push(
15542
+ emit(EVENT_LINE_DX.UNSUPPORTED, lineNumber, {
15543
+ reason: "event-line is horizontal-only in v1; `direction-tb` (vertical orientation) is a fast-follow."
15544
+ })
15545
+ );
15546
+ continue;
15547
+ }
15281
15548
  const dirMatch = trimmed.match(DIRECTION_RE);
15282
15549
  if (dirMatch) {
15283
15550
  const dir = dirMatch[1].toUpperCase();
@@ -15773,7 +16040,7 @@ function parseVersionControl(content, palette) {
15773
16040
  if (hint) msg2 += ` ${hint}`;
15774
16041
  return fail(lineNumber, msg2);
15775
16042
  }
15776
- const dir = /^direction\s+(LR|TB|BT)$/i.exec(trimmed);
16043
+ const dir = /^direction[-\s]+(LR|TB)$/i.exec(trimmed);
15777
16044
  if (dir) {
15778
16045
  options.direction = dir[1].toUpperCase();
15779
16046
  continue;
@@ -16215,6 +16482,12 @@ function parseKanban(content, palette) {
16215
16482
  columnCounter++;
16216
16483
  const colName = columnMatch[1].trim();
16217
16484
  let tail = (columnMatch[2] ?? "").trim();
16485
+ let colCollapsed = false;
16486
+ const barePeel = peelTrailingCollapsedFlag(tail);
16487
+ if (barePeel.collapsed) {
16488
+ colCollapsed = true;
16489
+ tail = barePeel.rest;
16490
+ }
16218
16491
  let colColor;
16219
16492
  const colorMatch = tail.match(PALETTE_COLOR_WORD_RE);
16220
16493
  if (colorMatch) {
@@ -16259,7 +16532,6 @@ function parseKanban(content, palette) {
16259
16532
  const wipVal = parseInt(columnMetadata["wip"], 10);
16260
16533
  if (!isNaN(wipVal)) wipLimit = wipVal;
16261
16534
  }
16262
- let colCollapsed = false;
16263
16535
  if (columnMetadata["collapsed"]?.toLowerCase() === "true") {
16264
16536
  colCollapsed = true;
16265
16537
  delete columnMetadata["collapsed"];
@@ -16416,7 +16688,8 @@ var init_parser15 = __esm({
16416
16688
  "fill-tint",
16417
16689
  "fill-solid",
16418
16690
  "fill-outline",
16419
- "no-title"
16691
+ "no-title",
16692
+ "no-legend"
16420
16693
  ]);
16421
16694
  REMOVED_BOOLEANS = {
16422
16695
  "no-auto-color": '"no-auto-color" was removed \u2014 colors are always auto-assigned from the palette; delete this line.'
@@ -16493,6 +16766,7 @@ function parseC4(content, palette) {
16493
16766
  title: null,
16494
16767
  titleLineNumber: null,
16495
16768
  options,
16769
+ direction: "TB",
16496
16770
  tagGroups: [],
16497
16771
  elements: [],
16498
16772
  relationships: [],
@@ -16580,7 +16854,12 @@ function parseC4(content, palette) {
16580
16854
  }
16581
16855
  if (!contentStarted && !currentTagGroup && measureIndent(line11) === 0) {
16582
16856
  if (KNOWN_C4_BOOLEANS.has(trimmed.toLowerCase())) {
16583
- options[trimmed.toLowerCase()] = "on";
16857
+ const boolKey = trimmed.toLowerCase();
16858
+ if (boolKey === "direction-lr" || boolKey === "direction-tb") {
16859
+ delete options["direction-lr"];
16860
+ delete options["direction-tb"];
16861
+ }
16862
+ options[boolKey] = "on";
16584
16863
  continue;
16585
16864
  }
16586
16865
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
@@ -16710,11 +16989,13 @@ function parseC4(content, palette) {
16710
16989
  const containerMatch = trimmed.match(CONTAINER_RE3);
16711
16990
  if (containerMatch) {
16712
16991
  const groupName = containerMatch[1].trim();
16992
+ const groupCollapsed = containerMatch[2] !== void 0 || containerMatch[3]?.toLowerCase() === "true";
16713
16993
  const parentEntry = findParentElement(indent, stack);
16714
16994
  if (parentEntry) {
16715
16995
  const group = {
16716
16996
  name: groupName,
16717
16997
  children: [],
16998
+ ...groupCollapsed && { collapsed: true },
16718
16999
  lineNumber
16719
17000
  };
16720
17001
  parentEntry.element.groups.push(group);
@@ -17052,6 +17333,7 @@ function parseC4(content, palette) {
17052
17333
  result.tagGroups,
17053
17334
  (line11, msg) => pushError(line11, msg, "warning")
17054
17335
  );
17336
+ result.direction = options["direction-lr"] ? "LR" : "TB";
17055
17337
  return result;
17056
17338
  }
17057
17339
  function findParentElement(indent, stack) {
@@ -17155,7 +17437,7 @@ var init_parser16 = __esm({
17155
17437
  init_parsing();
17156
17438
  init_reserved_key_registry();
17157
17439
  init_description_helpers();
17158
- CONTAINER_RE3 = /^\[([^\]]+)\]$/;
17440
+ CONTAINER_RE3 = /^\[([^\]]+)\](?:\s+(collapsed)|\s+collapsed:\s*(\S+))?$/;
17159
17441
  ELEMENT_RE = /^(person|system|container|component)\s+(.+)$/i;
17160
17442
  IS_A_RE = /\s+is\s+a(?:n)?\s+(\w+)\s*$/i;
17161
17443
  C4_IS_A_RE = /^([^:]+?)\s+is\s+an?\s+(person|system|container|component|external|database)\b(.*)$/i;
@@ -17184,10 +17466,12 @@ var init_parser16 = __esm({
17184
17466
  KNOWN_C4_OPTIONS = /* @__PURE__ */ new Set(["layout", "active-tag"]);
17185
17467
  KNOWN_C4_BOOLEANS = /* @__PURE__ */ new Set([
17186
17468
  "direction-tb",
17469
+ "direction-lr",
17187
17470
  "fill-tint",
17188
17471
  "fill-solid",
17189
17472
  "fill-outline",
17190
- "no-title"
17473
+ "no-title",
17474
+ "no-legend"
17191
17475
  ]);
17192
17476
  ALL_CHART_TYPES2 = [
17193
17477
  "c4",
@@ -17306,7 +17590,6 @@ var init_directives_registry = __esm({
17306
17590
  { token: "flow-width", category: "directive", map: true },
17307
17591
  { token: "locale", category: "directive", map: true },
17308
17592
  { token: "caption", category: "directive", map: true },
17309
- { token: "no-legend", category: "directive", map: true },
17310
17593
  { token: "no-coastline", category: "directive", map: true },
17311
17594
  { token: "no-relief", category: "directive", map: true },
17312
17595
  { token: "no-context-labels", category: "directive", map: true },
@@ -17324,6 +17607,9 @@ var init_directives_registry = __esm({
17324
17607
  { token: "days", category: "directive", map: true },
17325
17608
  // ── Shared across types ──────────────────────────────────
17326
17609
  // `active-tag`: infra top-level option + gantt option + map directive.
17610
+ // Treemap also accepts it (§24C.6, decision #48) — validated inline in its
17611
+ // parser, so no chart-scoped Set is derived for it; highlighting comes from
17612
+ // the global directive category here.
17327
17613
  {
17328
17614
  token: "active-tag",
17329
17615
  category: "directive",
@@ -17331,7 +17617,9 @@ var init_directives_registry = __esm({
17331
17617
  gantt: ["option"],
17332
17618
  map: true
17333
17619
  },
17334
- // `title`: gantt option (also a general data-chart directive, hand-listed).
17620
+ // `title`: gantt option. (The data-chart `title` directive was removed in
17621
+ // decision #48 — it now raises E_TITLE_DIRECTIVE; the token stays here for
17622
+ // gantt.)
17335
17623
  { token: "title", category: "directive", gantt: ["option"] },
17336
17624
  // `sort`: gantt option (also general, hand-listed).
17337
17625
  { token: "sort", category: "directive", gantt: ["option"] },
@@ -17340,6 +17628,11 @@ var init_directives_registry = __esm({
17340
17628
  { token: "lane-by", category: "directive", gantt: ["option"] },
17341
17629
  // `no-title`: gantt boolean + map directive (also general, hand-listed).
17342
17630
  { token: "no-title", category: "directive", gantt: ["boolean"], map: true },
17631
+ // `no-legend`: universal (decision #48) — every chart that renders a legend
17632
+ // accepts it, and charts without one take it as a harmless no-op. Parsed for
17633
+ // most types via GLOBAL_BOOLEANS in utils/parsing; gantt derives its boolean
17634
+ // Set from here, and map from `map: true`.
17635
+ { token: "no-legend", category: "directive", gantt: ["boolean"], map: true },
17343
17636
  // ══════════════════════════════════════════════════════════
17344
17637
  // Highlight-only vocab (no extractable parser Set).
17345
17638
  // ══════════════════════════════════════════════════════════
@@ -17351,10 +17644,15 @@ var init_directives_registry = __esm({
17351
17644
  { token: "era", category: "directive" },
17352
17645
  { token: "marker", category: "directive" },
17353
17646
  { token: "holiday", category: "directive" },
17354
- { token: "workweek", category: "directive" },
17647
+ // `workweek` is also the canonical weekday-window directive on clock and map
17648
+ // clock-cards (decision #48; `days` stays as the legacy alias).
17649
+ { token: "workweek", category: "directive", map: true },
17355
17650
  { token: "no-dependencies", category: "directive" },
17356
17651
  // ── Tech-radar ───────────────────────────────────────────
17357
17652
  { token: "rings", category: "directive" },
17653
+ // Blip listing is default-on everywhere (decision #48): `no-blip-legend`
17654
+ // suppresses it; `show-blip-legend` stays as the parse-accepted legacy no-op.
17655
+ { token: "no-blip-legend", category: "directive" },
17358
17656
  { token: "show-blip-legend", category: "directive" },
17359
17657
  { token: "trend", category: "directive" },
17360
17658
  // ── Tags / shared directives ─────────────────────────────
@@ -17380,7 +17678,13 @@ var init_directives_registry = __esm({
17380
17678
  // ATTRIBUTE_KEYS colon-gate from reclassifying `heat:` as a propertyName. So
17381
17679
  // `heat` lives only in the reserved-key registry + ATTRIBUTE_KEYS and highlights
17382
17680
  // as a property in both positions — exactly the treemap `heat` precedent.
17681
+ // Box values render by default since decision #48; `show-values` stays as
17682
+ // the parse-accepted legacy no-op (suppress with the shared `no-value`).
17383
17683
  { token: "show-values", category: "directive" },
17684
+ // Treemap's legacy plural spelling of the same suppression (decision #48
17685
+ // made the shared singular `no-value` canonical). Registered so a legacy
17686
+ // document still highlights it as a directive rather than plain text.
17687
+ { token: "no-values", category: "directive" },
17384
17688
  // ── Swimlane ─────────────────────────────────────────────
17385
17689
  { token: "lane", category: "directive" },
17386
17690
  // ── ER ───────────────────────────────────────────────────
@@ -17463,7 +17767,9 @@ var init_directives_registry = __esm({
17463
17767
  { token: "trials", category: "directive" },
17464
17768
  { token: "seed", category: "directive" },
17465
17769
  { token: "scrubber-trials", category: "directive" },
17466
- { token: "start-date", category: "directive" },
17770
+ // `start-date` is also gantt's canonical project-start option (decision #48;
17771
+ // bare `start` above stays as the legacy alias).
17772
+ { token: "start-date", category: "directive", gantt: ["option"] },
17467
17773
  { token: "end-date", category: "directive" },
17468
17774
  // ── Goal ─────────────────────────────────────────────────
17469
17775
  // Mode flags, value-key leaders, and opt-outs. `now`/`target` collide with
@@ -17501,6 +17807,12 @@ var init_directives_registry = __esm({
17501
17807
  { token: "backlog", category: "status" },
17502
17808
  { token: "ready", category: "status" },
17503
17809
  // ── Modifiers + ER column types/modifiers (MODIFIER) ─────
17810
+ // `collapsed` is the bare trailing group-line flag made canonical by
17811
+ // decision #48 (`[Fulfillment] collapsed`). It is deliberately a bare
17812
+ // MODIFIER rather than a colon key: the legacy `collapsed: true` form still
17813
+ // parses and still highlights (as `modifier` instead of `propertyName`),
17814
+ // whereas leaving it colon-only left the canonical bare form as plain text.
17815
+ { token: "collapsed", category: "modifier" },
17504
17816
  { token: "as", category: "modifier" },
17505
17817
  { token: "alias", category: "modifier" },
17506
17818
  { token: "aka", category: "modifier" },
@@ -17788,8 +18100,9 @@ function parseInfra(content) {
17788
18100
  continue;
17789
18101
  }
17790
18102
  }
17791
- if (/^direction-tb$/i.test(trimmed)) {
17792
- result.direction = "TB";
18103
+ const dirBool = trimmed.match(/^direction-(lr|tb)$/i);
18104
+ if (dirBool) {
18105
+ result.direction = dirBool[1].toUpperCase();
17793
18106
  continue;
17794
18107
  }
17795
18108
  if (trimmed === "animate" || trimmed === "no-animate") {
@@ -17828,8 +18141,15 @@ function parseInfra(content) {
17828
18141
  const groupAlias = groupMatch[2];
17829
18142
  const gId = groupId(gLabel);
17830
18143
  if (groupAlias) nameAliasMap.set(groupAlias, gId);
17831
- const groupMeta = groupMatch[3] ? extractPipeMetadata(groupMatch[3]).tags : void 0;
18144
+ let groupCollapsed = groupMatch[3] !== void 0;
18145
+ const groupMeta = groupMatch[4] ? extractPipeMetadata(groupMatch[4]).tags : void 0;
17832
18146
  if (groupMeta) {
18147
+ if (groupMeta["collapsed"] !== void 0) {
18148
+ if (groupMeta["collapsed"].toLowerCase() === "true") {
18149
+ groupCollapsed = true;
18150
+ }
18151
+ delete groupMeta["collapsed"];
18152
+ }
17833
18153
  warnUnknownMetaKeys(
17834
18154
  groupMeta,
17835
18155
  withTagAliases(INFRA_REGISTRY, tagAliasSet),
@@ -17841,6 +18161,7 @@ function parseInfra(content) {
17841
18161
  id: gId,
17842
18162
  label: gLabel,
17843
18163
  ...hasMeta && { metadata: groupMeta },
18164
+ ...groupCollapsed && { collapsed: true },
17844
18165
  lineNumber
17845
18166
  };
17846
18167
  currentGroup = newGroup;
@@ -17932,6 +18253,25 @@ function parseInfra(content) {
17932
18253
  continue;
17933
18254
  }
17934
18255
  }
18256
+ const spacePropMatch = trimmed.match(
18257
+ /^(instances|collapsed)\s+(\S+)\s*$/
18258
+ );
18259
+ if (spacePropMatch) {
18260
+ const key = spacePropMatch[1];
18261
+ const val = spacePropMatch[2];
18262
+ if (key === "instances" && RANGE_RE.test(val)) {
18263
+ currentGroup.instances = val;
18264
+ continue;
18265
+ }
18266
+ if (key === "instances" && /^\d+$/.test(val)) {
18267
+ currentGroup.instances = parseInt(val, 10);
18268
+ continue;
18269
+ }
18270
+ if (key === "collapsed" && /^(true|false)$/i.test(val)) {
18271
+ currentGroup.collapsed = val.toLowerCase() === "true";
18272
+ continue;
18273
+ }
18274
+ }
17935
18275
  const compMatch = trimmed.match(COMPONENT_RE);
17936
18276
  if (compMatch) {
17937
18277
  finishCurrentTagGroup();
@@ -18359,7 +18699,9 @@ function extractSymbols4(docText) {
18359
18699
  if (!indented) {
18360
18700
  const firstLine = parseFirstLine(line11);
18361
18701
  if (firstLine) continue;
18362
- if (/^(?:direction-tb|animate|no-animate|slo-|default-)/i.test(line11))
18702
+ if (/^(?:direction-tb|direction-lr|animate|no-animate|slo-|default-)/i.test(
18703
+ line11
18704
+ ))
18363
18705
  continue;
18364
18706
  if (/^[a-z-]+\s*:/i.test(line11)) continue;
18365
18707
  inMetadata = false;
@@ -18423,7 +18765,7 @@ var init_parser17 = __esm({
18423
18765
  ASYNC_CONNECTION_RE = /^~(?:([^~].*?))?~>\s*(.+?)\s*$/;
18424
18766
  ASYNC_SIMPLE_CONNECTION_RE = /^~>\s*(.+?)\s*$/;
18425
18767
  DEPRECATED_FANOUT_RE = /\bx(\d+)\s*$/;
18426
- GROUP_RE = /^\[([^\]]+)\]\s*(?:as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*)?((?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
18768
+ GROUP_RE = /^\[([^\]]+)\]\s*(?:as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*)?(?:(collapsed)(?!\s*:)\s*)?((?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
18427
18769
  TAG_VALUE_RE = /^(\w[\w\s]+?)\s*$/;
18428
18770
  COMPONENT_RE = /^(?:"(?<qname>[^"]+)"|(?<uname>[a-zA-Z_][\w -]*?))(?:\s+as\s+(?<alias>[A-Za-z][A-Za-z0-9_]{0,11}))?\s*(?<meta>(?:\|.*)|(?:[a-zA-Z_]\w*\s*:.*))?$/;
18429
18771
  PIPE_META_RE = /[|,]\s*(\w+)\s*:\s*([^|,]+)/g;
@@ -18532,7 +18874,8 @@ function parseGantt(content, palette) {
18532
18874
  sprintStart: null,
18533
18875
  sprintMode: null,
18534
18876
  fillMode: void 0,
18535
- noTitle: false
18877
+ noTitle: false,
18878
+ noLegend: false
18536
18879
  },
18537
18880
  diagnostics,
18538
18881
  error: null,
@@ -19186,6 +19529,9 @@ function parseGantt(content, palette) {
19186
19529
  case "no-title":
19187
19530
  result.options.noTitle = true;
19188
19531
  break;
19532
+ case "no-legend":
19533
+ result.options.noLegend = true;
19534
+ break;
19189
19535
  }
19190
19536
  continue;
19191
19537
  }
@@ -19212,6 +19558,9 @@ function parseGantt(content, palette) {
19212
19558
  const value = optNoColonMatch[2].trim();
19213
19559
  result.options.optionLineNumbers[key] = lineNumber;
19214
19560
  switch (key) {
19561
+ // `start-date` is canonical (decision #48, pert parity); bare `start`
19562
+ // stays as an undocumented legacy alias.
19563
+ case "start-date":
19215
19564
  case "start":
19216
19565
  result.options.start = wholeDate(value)?.iso ?? value;
19217
19566
  break;
@@ -19331,7 +19680,13 @@ function parseGantt(content, palette) {
19331
19680
  );
19332
19681
  continue;
19333
19682
  }
19334
- const afterBrackets = gm[2].trim();
19683
+ let afterBrackets = gm[2].trim();
19684
+ let collapsed = false;
19685
+ const barePeel = peelTrailingCollapsedFlag(afterBrackets);
19686
+ if (barePeel.collapsed) {
19687
+ collapsed = true;
19688
+ afterBrackets = barePeel.rest;
19689
+ }
19335
19690
  let metadata = {};
19336
19691
  if (afterBrackets) {
19337
19692
  const groupRegistry = withTagAliases(
@@ -19354,7 +19709,6 @@ function parseGantt(content, palette) {
19354
19709
  );
19355
19710
  metadata = split.meta;
19356
19711
  }
19357
- let collapsed = false;
19358
19712
  if (metadata["collapsed"]?.toLowerCase() === "true") {
19359
19713
  collapsed = true;
19360
19714
  delete metadata["collapsed"];
@@ -19531,7 +19885,8 @@ function parseGantt(content, palette) {
19531
19885
  for (let j = tokens.length - 1; j >= 0; j--) {
19532
19886
  const m = tokens[j].match(DURATION_TOKEN_RE);
19533
19887
  if (m) {
19534
- duration = { amount: parseFloat(m[1]), unit: m[2] };
19888
+ const unit = m[2] === "sp" ? "s" : m[2];
19889
+ duration = { amount: parseFloat(m[1]), unit };
19535
19890
  uncertain = !!m[3];
19536
19891
  name = tokens.slice(0, j).join(" ");
19537
19892
  break;
@@ -19719,13 +20074,13 @@ var init_parser18 = __esm({
19719
20074
  DEPENDENCY_RE = /^(?:-(.+?))?->\s*(.+)$/;
19720
20075
  COMMENT_RE = /^\/\//;
19721
20076
  WORKWEEK_RE = /^workweek\s+(.+)$/i;
19722
- OFFSET_PREFIX_RE = /^\+(\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h|s))\s+(.*)/;
19723
- DURATION_TOKEN_RE = /^(\d+(?:\.\d+)?)(min|bd|d|w|m|q|y|h|s)(\?)?$/;
19724
- LAG_ARROW_RE = /^-(\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h|s))->\s*(.*)/;
19725
- LEAD_ARROW_RE = /^--(\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h|s))->\s*(.*)/;
20077
+ OFFSET_PREFIX_RE = /^\+(\d+(?:\.\d+)?(?:min|bd|sp|d|w|m|q|y|h|s))\s+(.*)/;
20078
+ DURATION_TOKEN_RE = /^(\d+(?:\.\d+)?)(min|bd|sp|d|w|m|q|y|h|s)(\?)?$/;
20079
+ LAG_ARROW_RE = /^-(\d+(?:\.\d+)?(?:min|bd|sp|d|w|m|q|y|h|s))->\s*(.*)/;
20080
+ LEAD_ARROW_RE = /^--(\d+(?:\.\d+)?(?:min|bd|sp|d|w|m|q|y|h|s))->\s*(.*)/;
19726
20081
  BASIC_ARROW_RE = /^->\s*(.*)/;
19727
- ERA_OFFSET_RE = /^era\s+\+(\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h|s))\s*(?:->|–>)\s*\+(\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h|s))\s+(.+)$/i;
19728
- MARKER_OFFSET_RE = /^marker\s+\+(\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h|s))\s+(.+)$/i;
20082
+ ERA_OFFSET_RE = /^era\s+\+(\d+(?:\.\d+)?(?:min|bd|sp|d|w|m|q|y|h|s))\s*(?:->|–>)\s*\+(\d+(?:\.\d+)?(?:min|bd|sp|d|w|m|q|y|h|s))\s+(.+)$/i;
20083
+ MARKER_OFFSET_RE = /^marker\s+\+(\d+(?:\.\d+)?(?:min|bd|sp|d|w|m|q|y|h|s))\s+(.+)$/i;
19729
20084
  WEEKDAY_MAP = {
19730
20085
  mon: "mon",
19731
20086
  tue: "tue",
@@ -20010,12 +20365,14 @@ function parseMap(content, palette) {
20010
20365
  d.noClusterPois = true;
20011
20366
  break;
20012
20367
  // ── Clock channel (BL-122). Activation is the per-POI `clock` flag (see
20013
- // handlePoi); `hours`/`days` are the global availability window, stored
20014
- // raw and parsed at resolve. ──
20368
+ // handlePoi); `hours`/`workweek` are the global availability window,
20369
+ // stored raw and parsed at resolve. `days` is the legacy alias for
20370
+ // `workweek` (decision #48). ──
20015
20371
  case "hours":
20016
20372
  dup(d.clockHours);
20017
20373
  d.clockHours = value;
20018
20374
  break;
20375
+ case "workweek":
20019
20376
  case "days":
20020
20377
  dup(d.clockDays);
20021
20378
  d.clockDays = value;
@@ -20510,7 +20867,7 @@ function parseBoxesAndLines(content, palette) {
20510
20867
  continue;
20511
20868
  }
20512
20869
  if (indent === 0) {
20513
- const dirMatch = trimmed.match(/^direction\s+(TB|LR)$/i);
20870
+ const dirMatch = trimmed.match(/^direction[-\s]+(TB|LR)$/i);
20514
20871
  if (dirMatch) {
20515
20872
  result.direction = dirMatch[1].toUpperCase();
20516
20873
  continue;
@@ -20542,6 +20899,10 @@ function parseBoxesAndLines(content, palette) {
20542
20899
  if (low !== void 0) result.boxMetricLowColor = low;
20543
20900
  continue;
20544
20901
  }
20902
+ if (/^no-value$/i.test(trimmed)) {
20903
+ result.showValues = false;
20904
+ continue;
20905
+ }
20545
20906
  if (/^show-values$/i.test(trimmed)) {
20546
20907
  result.showValues = true;
20547
20908
  continue;
@@ -22145,6 +22506,12 @@ function parseSwimlane(content, palette) {
22145
22506
  options[trimmed.toLowerCase()] = "on";
22146
22507
  continue;
22147
22508
  }
22509
+ const dirBool = indent === 0 ? trimmed.match(/^direction-(lr|tb)$/i) : null;
22510
+ if (dirBool) {
22511
+ direction = dirBool[1].toUpperCase();
22512
+ options["direction"] = direction;
22513
+ continue;
22514
+ }
22148
22515
  if (indent === 0 && /^direction\s+/i.test(trimmed)) {
22149
22516
  const val = trimmed.replace(/^direction\s+/i, "").trim().toUpperCase();
22150
22517
  if (val === "LR" || val === "TB") {
@@ -22367,7 +22734,8 @@ function parseSwimlane(content, palette) {
22367
22734
  if (cIdx > 0) trimmed = trimmed.slice(0, cIdx).trim();
22368
22735
  if (!trimmed) continue;
22369
22736
  const indent = measureIndent(raw);
22370
- if (indent === 0 && /^direction\s+/i.test(trimmed)) continue;
22737
+ if (indent === 0 && /^direction(?:\s+|-(?:lr|tb)$)/i.test(trimmed))
22738
+ continue;
22371
22739
  if (indent === 0 && FILL_FAMILY_TOKENS.has(trimmed.toLowerCase())) continue;
22372
22740
  if (matchTagBlockHeading(trimmed) && indent === 0) {
22373
22741
  inTagBlock = true;
@@ -23440,9 +23808,18 @@ function parseMindmap(content, palette) {
23440
23808
  return result;
23441
23809
  }
23442
23810
  function parseNodeLine2(trimmed, lineNumber, _palette, counter, aliasMap, diagnostics) {
23811
+ let bareCollapsed = false;
23812
+ let nodeText = trimmed;
23813
+ {
23814
+ const barePeel = peelTrailingCollapsedFlag(nodeText);
23815
+ if (barePeel.collapsed && barePeel.rest) {
23816
+ bareCollapsed = true;
23817
+ nodeText = barePeel.rest;
23818
+ }
23819
+ }
23443
23820
  const registry = withTagAliases(MINDMAP_REGISTRY, new Set(aliasMap.keys()));
23444
23821
  const split = splitNameAndMeta(
23445
- trimmed,
23822
+ nodeText,
23446
23823
  registry,
23447
23824
  aliasMap,
23448
23825
  void 0,
@@ -23464,9 +23841,9 @@ function parseNodeLine2(trimmed, lineNumber, _palette, counter, aliasMap, diagno
23464
23841
  if (descVal) description = [descVal];
23465
23842
  delete metadata["description"];
23466
23843
  }
23467
- let collapsed;
23844
+ let collapsed = bareCollapsed ? true : void 0;
23468
23845
  if ("collapsed" in metadata) {
23469
- collapsed = metadata["collapsed"].toLowerCase() === "true";
23846
+ collapsed = collapsed || metadata["collapsed"].toLowerCase() === "true";
23470
23847
  delete metadata["collapsed"];
23471
23848
  }
23472
23849
  return {
@@ -24565,11 +24942,15 @@ var init_parser26 = __esm({
24565
24942
  ];
24566
24943
  KNOWN_OPTIONS8 = /* @__PURE__ */ new Set([]);
24567
24944
  KNOWN_BOOLEANS5 = /* @__PURE__ */ new Set([
24945
+ // Blip listing is default-on (decision #48): `no-blip-legend` suppresses it;
24946
+ // `show-blip-legend` is the parse-accepted legacy no-op.
24947
+ "no-blip-legend",
24568
24948
  "show-blip-legend",
24569
24949
  "fill-tint",
24570
24950
  "fill-solid",
24571
24951
  "fill-outline",
24572
- "no-title"
24952
+ "no-title",
24953
+ "no-legend"
24573
24954
  ]);
24574
24955
  }
24575
24956
  });
@@ -25022,11 +25403,6 @@ function parseJourneyMap(content, palette) {
25022
25403
  continue;
25023
25404
  }
25024
25405
  }
25025
- const removedMsg = REMOVED_BOOLEANS2[trimmed.toLowerCase()];
25026
- if (removedMsg && !PHASE_RE.test(trimmed)) {
25027
- warn2(lineNumber, removedMsg);
25028
- continue;
25029
- }
25030
25406
  if (KNOWN_BOOLEANS6.has(trimmed.toLowerCase()) && !PHASE_RE.test(trimmed)) {
25031
25407
  options[trimmed.toLowerCase()] = "on";
25032
25408
  continue;
@@ -25216,7 +25592,7 @@ function parseStepLine(trimmed, lineNumber, counter, aliasMap, warn2, diagnostic
25216
25592
  endLineNumber: lineNumber
25217
25593
  };
25218
25594
  }
25219
- var PHASE_RE, ANNOTATION_RE, KNOWN_OPTIONS9, KNOWN_BOOLEANS6, REMOVED_BOOLEANS2;
25595
+ var PHASE_RE, ANNOTATION_RE, KNOWN_OPTIONS9, KNOWN_BOOLEANS6;
25220
25596
  var init_parser28 = __esm({
25221
25597
  "src/journey-map/parser.ts"() {
25222
25598
  "use strict";
@@ -25234,11 +25610,9 @@ var init_parser28 = __esm({
25234
25610
  "fill-tint",
25235
25611
  "fill-solid",
25236
25612
  "fill-outline",
25237
- "no-title"
25613
+ "no-title",
25614
+ "no-legend"
25238
25615
  ]);
25239
- REMOVED_BOOLEANS2 = {
25240
- "no-legend": '"no-legend" has been removed \u2014 remove tag groups to hide the legend.'
25241
- };
25242
25616
  }
25243
25617
  });
25244
25618
 
@@ -25755,7 +26129,7 @@ function parseTreemap(content, palette) {
25755
26129
  validateTagGroupNames(result.tagGroups, pushWarning);
25756
26130
  cascadeTagMetadata(result.roots, result.tagGroups);
25757
26131
  }
25758
- result.defaultColorMode = resolveDefaultColorMode(result);
26132
+ result.defaultColorMode = resolveDefaultColorMode(result, pushWarning);
25759
26133
  if (result.roots.length === 0 && !result.error) {
25760
26134
  const diag = makeDgmoError(1, "No nodes found in treemap");
25761
26135
  result.diagnostics.push(diag);
@@ -25789,10 +26163,16 @@ function handleDirective(trimmed, options, result, pushWarning, lineNumber) {
25789
26163
  result.hasHeat = true;
25790
26164
  return true;
25791
26165
  }
25792
- const noMatch = trimmed.match(/^no-(values|percent|headers|legend)\s*$/i);
26166
+ const activeTagMatch = trimmed.match(/^active-tag\s+(.+)$/i);
26167
+ if (activeTagMatch) {
26168
+ result.activeTag = activeTagMatch[1].trim();
26169
+ result.activeTagLineNumber = lineNumber;
26170
+ return true;
26171
+ }
26172
+ const noMatch = trimmed.match(/^no-(values?|percent|headers|legend)\s*$/i);
25793
26173
  if (noMatch) {
25794
26174
  const key = noMatch[1].toLowerCase();
25795
- if (key === "values") options.noValues = true;
26175
+ if (key === "value" || key === "values") options.noValues = true;
25796
26176
  else if (key === "percent") options.noPercent = true;
25797
26177
  else if (key === "headers") options.noHeaders = true;
25798
26178
  else if (key === "legend") options.noLegend = true;
@@ -25811,9 +26191,27 @@ function handleDirective(trimmed, options, result, pushWarning, lineNumber) {
25811
26191
  }
25812
26192
  return false;
25813
26193
  }
25814
- function resolveDefaultColorMode(result) {
25815
- if (result.tagGroups.length > 0) return "tag";
26194
+ function resolveDefaultColorMode(result, pushWarning) {
26195
+ const at = result.activeTag?.trim();
26196
+ if (at) {
26197
+ const lv = at.toLowerCase();
26198
+ if (lv === "none") return "branch";
26199
+ const heatName = result.options.heatLabel;
26200
+ const heatHit = heatName !== void 0 ? lv === heatName.toLowerCase() : lv === "value" || lv === "heat";
26201
+ if (result.hasHeat && heatHit) return "heat";
26202
+ if (result.tagGroups.some((g) => g.name.toLowerCase() === lv)) return "tag";
26203
+ const dims = [
26204
+ ...result.tagGroups.map((g) => g.name),
26205
+ ...result.hasHeat ? [heatName ?? "Value"] : []
26206
+ ];
26207
+ const hint = dims.length ? ` Available: ${dims.join(", ")}, none.` : " No tag groups or heat are declared.";
26208
+ pushWarning(
26209
+ result.activeTagLineNumber ?? 0,
26210
+ `active-tag "${at}" does not match a declared tag group or the heat label.${hint}`
26211
+ );
26212
+ }
25816
26213
  if (result.hasHeat) return "heat";
26214
+ if (result.tagGroups.length > 0) return "tag";
25817
26215
  return "branch";
25818
26216
  }
25819
26217
  function parseNodeLine3(trimmed, lineNumber, counter, aliasMap, diagnostics) {
@@ -26336,7 +26734,7 @@ function parseGoal(content, palette) {
26336
26734
  options.noAutoColor = true;
26337
26735
  continue;
26338
26736
  }
26339
- if (lower === "no-note") {
26737
+ if (lower === "no-notes" || lower === "no-note") {
26340
26738
  options.noNote = true;
26341
26739
  continue;
26342
26740
  }
@@ -26824,19 +27222,20 @@ function parse24h(tok) {
26824
27222
  return { hour, minute };
26825
27223
  }
26826
27224
  function parseAt(rest, lineNum, softError) {
26827
- const t = parse24h(rest.trim());
27225
+ const tok = rest.trim();
27226
+ const t = parse24h(tok);
26828
27227
  if (t) return t;
26829
- const fixed = ampmTo24h(rest.trim());
26830
- softError(
26831
- lineNum,
26832
- fixed ? `"at ${rest}": use 24h time (${fixed}).` : `"at" needs a 24h time like 18:00 (got "${rest}").`
26833
- );
27228
+ const fixed = ampmTo24h(tok);
27229
+ if (fixed) return parse24h(fixed);
27230
+ softError(lineNum, `"at" needs a time like 18:00 or 6:30pm (got "${rest}").`);
26834
27231
  return null;
26835
27232
  }
26836
27233
  function ampmTo24h(tok) {
26837
27234
  const m = tok.match(AMPM_RE);
26838
27235
  if (!m) return null;
26839
- let hour = Number(m[1]) % 12;
27236
+ const h = Number(m[1]);
27237
+ if (h < 1 || h > 12) return null;
27238
+ let hour = h % 12;
26840
27239
  if (/pm/i.test(m[3])) hour += 12;
26841
27240
  return `${String(hour).padStart(2, "0")}:${m[2] ?? "00"}`;
26842
27241
  }
@@ -27975,12 +28374,14 @@ function parseClock(content, palette) {
27975
28374
  }
27976
28375
  break;
27977
28376
  }
28377
+ // `workweek` is canonical; `days` is the legacy alias (decision #48).
28378
+ case "workweek":
27978
28379
  case "days": {
27979
28380
  const parsed = parseDays(rest);
27980
28381
  if (parsed === null) {
27981
28382
  softError(
27982
28383
  lineNum,
27983
- `"days" needs weekdays like mon-fri or mon,wed,fri (got "${rest}").`
28384
+ `"${key}" needs weekdays like mon-fri or mon,wed,fri (got "${rest}").`
27984
28385
  );
27985
28386
  } else {
27986
28387
  workDays = parsed;
@@ -28017,9 +28418,15 @@ function parseClock(content, palette) {
28017
28418
  }
28018
28419
  break;
28019
28420
  }
28421
+ case "direction-lr":
28422
+ result.columns = true;
28423
+ break;
28424
+ case "direction-tb":
28425
+ result.columns = false;
28426
+ break;
28020
28427
  case "direction": {
28021
28428
  const v = rest.trim().toLowerCase();
28022
- result.columns = v.startsWith("lr") || v === "columns";
28429
+ result.columns = v === "lr" || v === "columns";
28023
28430
  break;
28024
28431
  }
28025
28432
  }
@@ -28135,7 +28542,7 @@ function parseClock(content, palette) {
28135
28542
  } else if (workDays !== null) {
28136
28543
  warn2(
28137
28544
  result.titleLineNumber ?? 1,
28138
- "`days` has no effect without `hours` \u2014 the working-hours status needs a window."
28545
+ "`workweek` has no effect without `hours` \u2014 the working-hours status needs a window."
28139
28546
  );
28140
28547
  }
28141
28548
  result.entries = entries;
@@ -28159,10 +28566,17 @@ var init_parser35 = __esm({
28159
28566
  DIRECTIVES = /* @__PURE__ */ new Set([
28160
28567
  "analog",
28161
28568
  "hours",
28569
+ "workweek",
28570
+ // Legacy alias for `workweek` (decision #48).
28162
28571
  "days",
28163
28572
  "no-sun",
28164
28573
  "time-24",
28165
28574
  "no-title",
28575
+ // Canonical direction booleans (§1.9); key+value `direction <lr|tb>` (and
28576
+ // its `columns` value alias) stays parse-accepted legacy. These MUST be in
28577
+ // this set — otherwise a bare `direction-lr` line parses as a zone entry.
28578
+ "direction-lr",
28579
+ "direction-tb",
28166
28580
  "direction",
28167
28581
  "color-by",
28168
28582
  "fill-tint",
@@ -28251,6 +28665,7 @@ function parseBracket(content, palette) {
28251
28665
  };
28252
28666
  const lines = content.split("\n");
28253
28667
  let headerParsed = false;
28668
+ let accentFromTitle = false;
28254
28669
  let currentSide = null;
28255
28670
  let currentTagGroup = null;
28256
28671
  let commentaryTarget = null;
@@ -28273,13 +28688,17 @@ function parseBracket(content, palette) {
28273
28688
  return fail(lineNum, 'Expected "bracket [Title]" as the first line.');
28274
28689
  }
28275
28690
  if (first.title) {
28276
- const { label } = extractColor(
28691
+ const { label, color } = extractColor(
28277
28692
  first.title,
28278
28693
  palette,
28279
28694
  result.diagnostics,
28280
28695
  lineNum
28281
28696
  );
28282
28697
  result.title = label || null;
28698
+ if (color !== void 0) {
28699
+ result.accentColor = color;
28700
+ accentFromTitle = true;
28701
+ }
28283
28702
  }
28284
28703
  result.titleLineNumber = lineNum;
28285
28704
  headerParsed = true;
@@ -28381,7 +28800,7 @@ function parseBracket(content, palette) {
28381
28800
  result.diagnostics,
28382
28801
  lineNum
28383
28802
  );
28384
- if (color !== void 0) result.accentColor = color;
28803
+ if (color !== void 0 && !accentFromTitle) result.accentColor = color;
28385
28804
  continue;
28386
28805
  }
28387
28806
  const sideM = trimmed.match(/^\[(.+?)\](.*)$/);
@@ -29217,7 +29636,7 @@ function parseRaci(content, palette) {
29217
29636
  continue;
29218
29637
  }
29219
29638
  const lower = trimmed.toLowerCase();
29220
- const removedMsg = REMOVED_BOOLEANS3[lower];
29639
+ const removedMsg = REMOVED_BOOLEANS2[lower];
29221
29640
  if (removedMsg) {
29222
29641
  warn2(lineNumber, removedMsg);
29223
29642
  continue;
@@ -29458,7 +29877,7 @@ function* allTasks(parsed) {
29458
29877
  for (const t of phase.tasks) yield t;
29459
29878
  }
29460
29879
  }
29461
- var KNOWN_OPTIONS10, KNOWN_BOOLEANS7, REMOVED_BOOLEANS3, PHASE_RE2, PHASE_PALETTE_COLOR_RE, ROLE_ASSIGNMENT_RE;
29880
+ var KNOWN_OPTIONS10, KNOWN_BOOLEANS7, REMOVED_BOOLEANS2, PHASE_RE2, PHASE_PALETTE_COLOR_RE, ROLE_ASSIGNMENT_RE;
29462
29881
  var init_parser37 = __esm({
29463
29882
  "src/raci/parser.ts"() {
29464
29883
  "use strict";
@@ -29472,7 +29891,7 @@ var init_parser37 = __esm({
29472
29891
  init_variants();
29473
29892
  KNOWN_OPTIONS10 = /* @__PURE__ */ new Set(["roles", "palette", "theme", "active-tag"]);
29474
29893
  KNOWN_BOOLEANS7 = /* @__PURE__ */ new Set(["no-title"]);
29475
- REMOVED_BOOLEANS3 = {
29894
+ REMOVED_BOOLEANS2 = {
29476
29895
  "no-rule-enforcement": '"no-rule-enforcement" has been removed \u2014 RACI validation is always active.'
29477
29896
  };
29478
29897
  PHASE_RE2 = /^\[(.+?)\]\s*(.*)$/;
@@ -32768,7 +33187,7 @@ var init_dgmo_router = __esm({
32768
33187
  init_diagnostics();
32769
33188
  init_chart_types();
32770
33189
  init_chart_type_registry();
32771
- GANTT_DURATION_RE = /^\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h)(?:\?)?\s+/;
33190
+ GANTT_DURATION_RE = /^\d+(?:\.\d+)?(?:min|bd|sp|d|w|m|q|y|h)(?:\?)?\s+/;
32772
33191
  GANTT_DATE_RE = /^\d{4}-\d{2}-\d{2}(?:\s\d{2}:\d{2})?\s+/;
32773
33192
  C4_TYPE_RE = /\bis\s+an?\s+(person|system|container|component)\b/i;
32774
33193
  CHART_TYPE_DESCRIPTIONS = Object.fromEntries(chartTypes.map((c) => [c.id, c.description]));
@@ -32873,7 +33292,9 @@ function fmtNum(n) {
32873
33292
  function reserveHeader(svg, chart, colors, palette, isDark, hasTitle, width) {
32874
33293
  return injectLegendGroups(
32875
33294
  svg,
32876
- getSimpleChartLegendGroups(chart, colors),
33295
+ // `no-legend` (#48) → pass no groups; injectLegendGroups then returns the
33296
+ // title-only top inset, so the legend band collapses rather than blanking.
33297
+ chart.noLegend ? [] : getSimpleChartLegendGroups(chart, colors),
32877
33298
  palette,
32878
33299
  isDark,
32879
33300
  hasTitle,
@@ -34398,7 +34819,7 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
34398
34819
  );
34399
34820
  return true;
34400
34821
  case "radar": {
34401
- const groups = getSimpleChartLegendGroups(std, colors);
34822
+ const groups = std.noLegend ? [] : getSimpleChartLegendGroups(std, colors);
34402
34823
  const top = injectLegendGroups(
34403
34824
  s,
34404
34825
  groups,
@@ -34468,7 +34889,7 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
34468
34889
  );
34469
34890
  return true;
34470
34891
  case "scatter": {
34471
- const groups = getExtendedChartLegendGroups(ext, seriesColors2);
34892
+ const groups = ext.noLegend ? [] : getExtendedChartLegendGroups(ext, seriesColors2);
34472
34893
  const top = injectLegendGroups(
34473
34894
  s,
34474
34895
  groups,
@@ -34517,7 +34938,7 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
34517
34938
  );
34518
34939
  return true;
34519
34940
  case "function": {
34520
- const groups = getExtendedChartLegendGroups(ext, seriesColors2);
34941
+ const groups = ext.noLegend ? [] : getExtendedChartLegendGroups(ext, seriesColors2);
34521
34942
  const top = injectLegendGroups(
34522
34943
  s,
34523
34944
  groups,
@@ -36468,7 +36889,10 @@ function injectDefaultMetadata(roots, tagGroups) {
36468
36889
  function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expandAllLegend) {
36469
36890
  if (parsed.roots.length === 0) {
36470
36891
  const showEyeIcons2 = hiddenAttributes !== void 0;
36471
- const legendGroups2 = computeLegendGroups(parsed.tagGroups, showEyeIcons2);
36892
+ const legendGroups2 = computeLegendGroups(
36893
+ legendSuppressed(parsed.options) ? [] : parsed.tagGroups,
36894
+ showEyeIcons2
36895
+ );
36472
36896
  if (legendGroups2.length === 0) {
36473
36897
  return {
36474
36898
  nodes: [],
@@ -36496,6 +36920,11 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36496
36920
  height: cy - LEGEND_GROUP_GAP + MARGIN2
36497
36921
  };
36498
36922
  }
36923
+ const isLR = parsed.direction === "LR";
36924
+ const depthExtent = (tn) => isLR ? tn.width : tn.height;
36925
+ const crossExtent = (tn) => isLR ? tn.height : tn.width;
36926
+ const ptToScreen = (ax, ay) => isLR ? { x: ay, y: ax } : { x: ax, y: ay };
36927
+ const screenTopLeft = (ax, ay, w, h2) => isLR ? { left: ay, top: ax - h2 / 2 } : { left: ax - w / 2, top: ay };
36499
36928
  injectDefaultMetadata(parsed.roots, parsed.tagGroups);
36500
36929
  const subNodeLabel = parsed.options["sub-node-label"] ?? void 0;
36501
36930
  const showSubNodeCount = ["yes", "on"].includes(
@@ -36542,6 +36971,10 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36542
36971
  for (const tn of allTreeNodes) {
36543
36972
  tn.width = maxWidth;
36544
36973
  }
36974
+ const maxDepthExtent = isLR ? maxWidth : maxHeight;
36975
+ const maxCrossExtent = isLR ? maxHeight : maxWidth;
36976
+ const depthGap = V_GAP;
36977
+ const crossGap = H_GAP;
36545
36978
  const leafStacks = /* @__PURE__ */ new Map();
36546
36979
  const collapseLeafContainers = (tn) => {
36547
36980
  for (const child of tn.children) collapseLeafContainers(child);
@@ -36555,7 +36988,8 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36555
36988
  for (const child of tn.children) {
36556
36989
  child.width = maxW;
36557
36990
  }
36558
- const totalH = tn.children.reduce((s, c) => s + c.height, 0) + (tn.children.length - 1) * STACK_V_GAP;
36991
+ const stackDepth = tn.children.reduce((s, c) => s + depthExtent(c), 0) + (tn.children.length - 1) * STACK_V_GAP;
36992
+ const stackCross = Math.max(...tn.children.map((c) => crossExtent(c)));
36559
36993
  tn.children = [
36560
36994
  {
36561
36995
  orgNode: {
@@ -36568,8 +37002,8 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36568
37002
  lineNumber: 0
36569
37003
  },
36570
37004
  children: [],
36571
- width: maxW,
36572
- height: totalH
37005
+ width: isLR ? stackDepth : stackCross,
37006
+ height: isLR ? stackCross : stackDepth
36573
37007
  }
36574
37008
  ];
36575
37009
  }
@@ -36578,8 +37012,8 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36578
37012
  centerHeavyChildren(root);
36579
37013
  const h = hierarchy(root, (d) => d.children);
36580
37014
  const treeLayout = tree().nodeSize([
36581
- maxWidth + H_GAP,
36582
- maxHeight + V_GAP
37015
+ maxCrossExtent + crossGap,
37016
+ maxDepthExtent + depthGap
36583
37017
  ]);
36584
37018
  treeLayout(h);
36585
37019
  {
@@ -36588,16 +37022,17 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36588
37022
  for (const d of descendants) {
36589
37023
  if (d.data.orgNode.id.startsWith("__stack_")) continue;
36590
37024
  const cur = levelMaxHeight.get(d.depth) ?? 0;
36591
- if (d.data.height > cur) levelMaxHeight.set(d.depth, d.data.height);
37025
+ const ext = depthExtent(d.data);
37026
+ if (ext > cur) levelMaxHeight.set(d.depth, ext);
36592
37027
  }
36593
37028
  const maxDepth = Math.max(...levelMaxHeight.keys(), 0);
36594
37029
  const compactedY = /* @__PURE__ */ new Map();
36595
37030
  const rootDepth = treeNodes.length === 1 ? 0 : 1;
36596
37031
  compactedY.set(rootDepth, 0);
36597
37032
  for (let d = rootDepth + 1; d <= maxDepth; d++) {
36598
- const parentH = levelMaxHeight.get(d - 1) ?? maxHeight;
37033
+ const parentH = levelMaxHeight.get(d - 1) ?? maxDepthExtent;
36599
37034
  const prevY = compactedY.get(d - 1) ?? 0;
36600
- compactedY.set(d, prevY + parentH + V_GAP);
37035
+ compactedY.set(d, prevY + parentH + depthGap);
36601
37036
  }
36602
37037
  for (const d of h.descendants()) {
36603
37038
  if (d.data.orgNode.id === "__virtual_root__") continue;
@@ -36612,7 +37047,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36612
37047
  const actualLevelGap = childY - d.y;
36613
37048
  const metaCount = Object.keys(d.data.orgNode.metadata).length;
36614
37049
  const headerHeight = CONTAINER_LABEL_HEIGHT + metaCount * CONTAINER_META_LINE_HEIGHT2;
36615
- const desiredGap = headerHeight + 15;
37050
+ const desiredGap = isLR ? CONTAINER_PAD_X : headerHeight + 15;
36616
37051
  const shiftUp = actualLevelGap - desiredGap;
36617
37052
  if (shiftUp <= 0) continue;
36618
37053
  const shift = (node) => {
@@ -36630,10 +37065,10 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36630
37065
  if (d.data.orgNode.isContainer) continue;
36631
37066
  if (!d.children || d.children.length === 0) continue;
36632
37067
  if (!d.children.every((c) => c.data.orgNode.isContainer)) continue;
36633
- const parentBottomY = d.y + d.data.height;
37068
+ const parentBottomY = d.y + depthExtent(d.data);
36634
37069
  const firstChildY = Math.min(...d.children.map((c) => c.y));
36635
37070
  const currentGap = firstChildY - parentBottomY;
36636
- const desiredGap = V_GAP * 0.6;
37071
+ const desiredGap = depthGap * 0.6;
36637
37072
  const shiftUp = currentGap - desiredGap;
36638
37073
  if (shiftUp <= 0) continue;
36639
37074
  const shiftDown = (node) => {
@@ -36650,8 +37085,8 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36650
37085
  }
36651
37086
  {
36652
37087
  const subtreeExtent = (node) => {
36653
- let min = node.x - node.data.width / 2;
36654
- let max = node.x + node.data.width / 2;
37088
+ let min = node.x - crossExtent(node.data) / 2;
37089
+ let max = node.x + crossExtent(node.data) / 2;
36655
37090
  if (node.children) {
36656
37091
  for (const child of node.children) {
36657
37092
  const childExt = subtreeExtent(child);
@@ -36662,6 +37097,10 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36662
37097
  if (node.data.orgNode.isContainer) {
36663
37098
  min -= CONTAINER_PAD_X;
36664
37099
  max += CONTAINER_PAD_X;
37100
+ if (isLR) {
37101
+ const metaCount = Object.keys(node.data.orgNode.metadata).length;
37102
+ min -= CONTAINER_LABEL_HEIGHT + metaCount * CONTAINER_META_LINE_HEIGHT2;
37103
+ }
36665
37104
  }
36666
37105
  return { minX: min, maxX: max };
36667
37106
  };
@@ -36683,7 +37122,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36683
37122
  const positions = [0];
36684
37123
  for (let i = 1; i < children.length; i++) {
36685
37124
  const prevRight = positions[i - 1] + extents[i - 1].relRight;
36686
- positions[i] = prevRight + H_GAP - extents[i].relLeft;
37125
+ positions[i] = prevRight + crossGap - extents[i].relLeft;
36687
37126
  }
36688
37127
  const newCenter = (positions[0] + positions[positions.length - 1]) / 2;
36689
37128
  const centerShift = currentCenter - newCenter;
@@ -36728,29 +37167,33 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36728
37167
  cx: d.x,
36729
37168
  cy: currentY
36730
37169
  });
36731
- currentY += child.height + STACK_V_GAP;
37170
+ currentY += depthExtent(child) + STACK_V_GAP;
36732
37171
  }
36733
37172
  }
37173
+ const screenRect = (ax, ay, w, h2) => {
37174
+ const { left, top } = screenTopLeft(ax, ay, w, h2);
37175
+ return { left, top, right: left + w, bottom: top + h2 };
37176
+ };
37177
+ const growBBox = (r) => {
37178
+ if (r.left < minX) minX = r.left;
37179
+ if (r.right > maxX) maxX = r.right;
37180
+ if (r.top < minY) minY = r.top;
37181
+ if (r.bottom > maxY) maxY = r.bottom;
37182
+ };
36734
37183
  for (const d of h.descendants()) {
36735
37184
  if (d.data.orgNode.id === "__virtual_root__") continue;
36736
37185
  if (d.data.orgNode.id.startsWith("__stack_")) continue;
36737
- const w = d.data.width;
36738
- const ht = d.data.height;
36739
- const cx = d.x;
36740
- const cy = d.y;
36741
- if (cx - w / 2 < minX) minX = cx - w / 2;
36742
- if (cx + w / 2 > maxX) maxX = cx + w / 2;
36743
- if (cy < minY) minY = cy;
36744
- if (cy + ht > maxY) maxY = cy + ht;
37186
+ growBBox(screenRect(d.x, d.y, d.data.width, d.data.height));
36745
37187
  }
36746
37188
  for (const ec of expandedChildren) {
36747
- if (ec.cx - ec.width / 2 < minX) minX = ec.cx - ec.width / 2;
36748
- if (ec.cx + ec.width / 2 > maxX) maxX = ec.cx + ec.width / 2;
36749
- if (ec.cy < minY) minY = ec.cy;
36750
- if (ec.cy + ec.height > maxY) maxY = ec.cy + ec.height;
37189
+ growBBox(screenRect(ec.cx, ec.cy, ec.width, ec.height));
36751
37190
  }
36752
37191
  const offsetX = -minX + MARGIN2;
36753
37192
  const offsetY = -minY + MARGIN2;
37193
+ const placeCard = (ax, ay, w, h2) => {
37194
+ const r = screenRect(ax, ay, w, h2);
37195
+ return { x: r.left + offsetX + w / 2, y: r.top + offsetY };
37196
+ };
36754
37197
  const subNodeKey = subNodeLabel ?? "Sub-node Count";
36755
37198
  for (const ec of expandedChildren) {
36756
37199
  const hc = hiddenCounts?.get(ec.orgNode.id);
@@ -36773,14 +37216,17 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36773
37216
  isContainer: ec.orgNode.isContainer,
36774
37217
  lineNumber: ec.orgNode.lineNumber,
36775
37218
  ...ecColor !== void 0 && { color: ecColor },
36776
- x: ec.cx + offsetX,
36777
- y: ec.cy + offsetY,
37219
+ ...placeCard(ec.cx, ec.cy, ec.width, ec.height),
36778
37220
  width: ec.width,
36779
37221
  height: ec.height,
36780
37222
  ...hc !== void 0 && { hiddenCount: hc },
36781
37223
  ...ecHasChildren !== void 0 && { hasChildren: ecHasChildren }
36782
37224
  });
36783
37225
  }
37226
+ const placePoint = (ax, ay) => {
37227
+ const p = ptToScreen(ax, ay);
37228
+ return { x: p.x + offsetX, y: p.y + offsetY };
37229
+ };
36784
37230
  const busGroups = /* @__PURE__ */ new Map();
36785
37231
  for (const d of h.descendants()) {
36786
37232
  if (d.data.orgNode.id === "__virtual_root__") continue;
@@ -36788,8 +37234,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36788
37234
  const orgNode = d.data.orgNode;
36789
37235
  const w = d.data.width;
36790
37236
  const ht = d.data.height;
36791
- const x = d.x + offsetX;
36792
- const y = d.y + offsetY;
37237
+ const { x, y } = placeCard(d.x, d.y, w, ht);
36793
37238
  const hc = hiddenCounts?.get(orgNode.id);
36794
37239
  const nodeMeta = filterMetadata(orgNode.metadata, hiddenAttributes);
36795
37240
  if (!orgNode.isContainer && showSubNodeCount && !(hc != null && hc > 0)) {
@@ -36821,19 +37266,16 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36821
37266
  if (d.parent && d.parent.data.orgNode.id !== "__virtual_root__" && !parentIsContainerBox) {
36822
37267
  const parentId = d.parent.data.orgNode.id;
36823
37268
  if (!busGroups.has(parentId)) {
36824
- const px = d.parent.x + offsetX;
36825
- const py = d.parent.y + offsetY;
36826
- const parentH = d.parent.data.height;
36827
37269
  busGroups.set(parentId, {
36828
- parentX: px,
36829
- parentBottomY: py + parentH,
37270
+ parentX: d.parent.x,
37271
+ parentBottomY: d.parent.y + depthExtent(d.parent.data),
36830
37272
  children: []
36831
37273
  });
36832
37274
  }
36833
37275
  busGroups.get(parentId).children.push({
36834
37276
  id: orgNode.id,
36835
- x,
36836
- topY: y
37277
+ x: d.x,
37278
+ topY: d.y
36837
37279
  });
36838
37280
  }
36839
37281
  }
@@ -36846,10 +37288,10 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36846
37288
  sourceId: parentId,
36847
37289
  targetId: child.id,
36848
37290
  points: [
36849
- { x: parentX, y: parentBottomY },
36850
- { x: parentX, y: midY },
36851
- { x: child.x, y: midY },
36852
- { x: child.x, y: child.topY }
37291
+ placePoint(parentX, parentBottomY),
37292
+ placePoint(parentX, midY),
37293
+ placePoint(child.x, midY),
37294
+ placePoint(child.x, child.topY)
36853
37295
  ]
36854
37296
  });
36855
37297
  } else {
@@ -36860,27 +37302,18 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36860
37302
  layoutEdges.push({
36861
37303
  sourceId: parentId,
36862
37304
  targetId: parentId,
36863
- points: [
36864
- { x: parentX, y: parentBottomY },
36865
- { x: parentX, y: midY }
36866
- ]
37305
+ points: [placePoint(parentX, parentBottomY), placePoint(parentX, midY)]
36867
37306
  });
36868
37307
  layoutEdges.push({
36869
37308
  sourceId: parentId,
36870
37309
  targetId: parentId,
36871
- points: [
36872
- { x: leftX, y: midY },
36873
- { x: rightX, y: midY }
36874
- ]
37310
+ points: [placePoint(leftX, midY), placePoint(rightX, midY)]
36875
37311
  });
36876
37312
  for (const child of children) {
36877
37313
  layoutEdges.push({
36878
37314
  sourceId: parentId,
36879
37315
  targetId: child.id,
36880
- points: [
36881
- { x: child.x, y: midY },
36882
- { x: child.x, y: child.topY }
36883
- ]
37316
+ points: [placePoint(child.x, midY), placePoint(child.x, child.topY)]
36884
37317
  });
36885
37318
  }
36886
37319
  }
@@ -36893,8 +37326,6 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36893
37326
  const EMPTY_CONTAINER_MIN_HEIGHT = 60;
36894
37327
  for (const d of allContainerNodes) {
36895
37328
  if (d.children && d.children.length > 0) continue;
36896
- const cx = d.x + offsetX;
36897
- const cy = d.y + offsetY;
36898
37329
  const metaCount = Object.keys(d.data.orgNode.metadata).length;
36899
37330
  const labelHeight = CONTAINER_LABEL_HEIGHT + metaCount * CONTAINER_META_LINE_HEIGHT2;
36900
37331
  const boxWidth = d.data.width;
@@ -36902,8 +37333,9 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36902
37333
  labelHeight + CONTAINER_PAD_BOTTOM,
36903
37334
  EMPTY_CONTAINER_MIN_HEIGHT
36904
37335
  );
36905
- const boxX = cx - boxWidth / 2;
36906
- const boxY = cy;
37336
+ const emptyTL = screenTopLeft(d.x, d.y, boxWidth, boxHeight);
37337
+ const boxX = emptyTL.left + offsetX;
37338
+ const boxY = emptyTL.top + offsetY;
36907
37339
  containerBoundsMap.set(d.data.orgNode.id, {
36908
37340
  minX: boxX,
36909
37341
  maxX: boxX + boxWidth,
@@ -36952,46 +37384,79 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
36952
37384
  if (allDesc.length === 0) continue;
36953
37385
  let descMinX = Infinity;
36954
37386
  let descMaxX = -Infinity;
37387
+ let descMinY = Infinity;
36955
37388
  let descMaxY = -Infinity;
37389
+ const growDesc = (left, top, right, bottom) => {
37390
+ if (left < descMinX) descMinX = left;
37391
+ if (right > descMaxX) descMaxX = right;
37392
+ if (top < descMinY) descMinY = top;
37393
+ if (bottom > descMaxY) descMaxY = bottom;
37394
+ };
36956
37395
  for (const desc of allDesc) {
36957
37396
  const innerBounds = containerBoundsMap.get(desc.data.orgNode.id);
36958
37397
  if (innerBounds) {
36959
- if (innerBounds.minX < descMinX) descMinX = innerBounds.minX;
36960
- if (innerBounds.maxX > descMaxX) descMaxX = innerBounds.maxX;
36961
- if (innerBounds.maxY > descMaxY) descMaxY = innerBounds.maxY;
37398
+ growDesc(
37399
+ innerBounds.minX,
37400
+ innerBounds.minY,
37401
+ innerBounds.maxX,
37402
+ innerBounds.maxY
37403
+ );
36962
37404
  } else if (desc.data.orgNode.id.startsWith("__stack_")) {
36963
37405
  const cid = desc.data.orgNode.id.replace("__stack_", "");
36964
37406
  const stack = leafStacks.get(cid);
36965
37407
  if (stack) {
36966
37408
  for (const ec of expandedChildren) {
36967
37409
  if (ec.orgNode.parentId !== cid) continue;
36968
- const ex = ec.cx + offsetX;
36969
- const ey = ec.cy + offsetY;
36970
- if (ex - ec.width / 2 < descMinX) descMinX = ex - ec.width / 2;
36971
- if (ex + ec.width / 2 > descMaxX) descMaxX = ex + ec.width / 2;
36972
- if (ey + ec.height > descMaxY) descMaxY = ey + ec.height;
37410
+ const r = screenRect(ec.cx, ec.cy, ec.width, ec.height);
37411
+ growDesc(
37412
+ r.left + offsetX,
37413
+ r.top + offsetY,
37414
+ r.right + offsetX,
37415
+ r.bottom + offsetY
37416
+ );
36973
37417
  }
36974
37418
  }
36975
37419
  } else {
36976
- const dw = desc.data.width;
36977
- const dh = desc.data.height;
36978
- const dx = desc.x + offsetX;
36979
- const dy = desc.y + offsetY;
36980
- if (dx - dw / 2 < descMinX) descMinX = dx - dw / 2;
36981
- if (dx + dw / 2 > descMaxX) descMaxX = dx + dw / 2;
36982
- if (dy + dh > descMaxY) descMaxY = dy + dh;
37420
+ const r = screenRect(
37421
+ desc.x,
37422
+ desc.y,
37423
+ desc.data.width,
37424
+ desc.data.height
37425
+ );
37426
+ growDesc(
37427
+ r.left + offsetX,
37428
+ r.top + offsetY,
37429
+ r.right + offsetX,
37430
+ r.bottom + offsetY
37431
+ );
36983
37432
  }
36984
37433
  }
36985
- const containerX = d.x + offsetX;
36986
- const containerY = d.y + offsetY;
37434
+ const ownTL = screenTopLeft(d.x, d.y, d.data.width, d.data.height);
37435
+ const containerCenterX = ownTL.left + d.data.width / 2 + offsetX;
37436
+ const containerLeftX = ownTL.left + offsetX;
37437
+ const containerY = ownTL.top + offsetY;
36987
37438
  const metaCount = Object.keys(d.data.orgNode.metadata).length;
36988
37439
  const labelHeight = CONTAINER_LABEL_HEIGHT + metaCount * CONTAINER_META_LINE_HEIGHT2;
36989
- const boxY = containerY;
36990
- const boxHeight = descMaxY - containerY + CONTAINER_PAD_BOTTOM;
36991
- const boxX = descMinX - CONTAINER_PAD_X;
36992
- const contentWidth = descMaxX - descMinX + CONTAINER_PAD_X * 2;
36993
- const finalBoxWidth = Math.max(contentWidth, d.data.width);
36994
- const centeredBoxX = finalBoxWidth > contentWidth ? containerX - finalBoxWidth / 2 : boxX;
37440
+ let boxY;
37441
+ let boxHeight;
37442
+ let centeredBoxX;
37443
+ let finalBoxWidth;
37444
+ if (isLR) {
37445
+ centeredBoxX = containerLeftX;
37446
+ finalBoxWidth = Math.max(
37447
+ descMaxX - containerLeftX + CONTAINER_PAD_X,
37448
+ d.data.width
37449
+ );
37450
+ boxY = descMinY - labelHeight - CONTAINER_PAD_X;
37451
+ boxHeight = descMaxY - boxY + CONTAINER_PAD_BOTTOM;
37452
+ } else {
37453
+ boxY = containerY;
37454
+ boxHeight = descMaxY - containerY + CONTAINER_PAD_BOTTOM;
37455
+ const boxX = descMinX - CONTAINER_PAD_X;
37456
+ const contentWidth = descMaxX - descMinX + CONTAINER_PAD_X * 2;
37457
+ finalBoxWidth = Math.max(contentWidth, d.data.width);
37458
+ centeredBoxX = finalBoxWidth > contentWidth ? containerCenterX - finalBoxWidth / 2 : boxX;
37459
+ }
36995
37460
  containerBoundsMap.set(d.data.orgNode.id, {
36996
37461
  minX: centeredBoxX,
36997
37462
  maxX: centeredBoxX + finalBoxWidth,
@@ -37024,17 +37489,30 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
37024
37489
  containers.reverse();
37025
37490
  let finalMinX = minX;
37026
37491
  let finalMaxX = maxX;
37492
+ let finalMinY = minY;
37027
37493
  let finalMaxY = maxY;
37028
37494
  for (const c of containers) {
37029
37495
  const cLeft = c.x - offsetX;
37030
37496
  const cRight = cLeft + c.width;
37031
- const cBottom = c.y - offsetY + c.height;
37497
+ const cTop = c.y - offsetY;
37498
+ const cBottom = cTop + c.height;
37032
37499
  if (cLeft < finalMinX) finalMinX = cLeft;
37033
37500
  if (cRight > finalMaxX) finalMaxX = cRight;
37501
+ if (cTop < finalMinY) finalMinY = cTop;
37034
37502
  if (cBottom > finalMaxY) finalMaxY = cBottom;
37035
37503
  }
37504
+ const topOvershoot = minY - finalMinY;
37505
+ if (topOvershoot > 0) {
37506
+ for (const n of layoutNodes) n.y += topOvershoot;
37507
+ for (const c of containers) c.y += topOvershoot;
37508
+ for (const e of layoutEdges) {
37509
+ for (const p of e.points) {
37510
+ p.y += topOvershoot;
37511
+ }
37512
+ }
37513
+ }
37036
37514
  const totalWidth = finalMaxX - finalMinX + MARGIN2 * 2;
37037
- const totalHeight = finalMaxY - minY + MARGIN2 * 2;
37515
+ const totalHeight = finalMaxY - finalMinY + MARGIN2 * 2;
37038
37516
  const usedValuesByGroup = /* @__PURE__ */ new Map();
37039
37517
  for (const group of parsed.tagGroups) {
37040
37518
  const key = tagAttrKey(group.name);
@@ -37050,7 +37528,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
37050
37528
  }
37051
37529
  const showEyeIcons = hiddenAttributes !== void 0;
37052
37530
  const legendGroups = computeLegendGroups(
37053
- parsed.tagGroups,
37531
+ legendSuppressed(parsed.options) ? [] : parsed.tagGroups,
37054
37532
  showEyeIcons,
37055
37533
  usedValuesByGroup
37056
37534
  );
@@ -37097,6 +37575,7 @@ var init_layout = __esm({
37097
37575
  "use strict";
37098
37576
  init_tag_groups();
37099
37577
  init_tag_groups();
37578
+ init_parsing();
37100
37579
  init_legend_constants();
37101
37580
  init_text_measure();
37102
37581
  LABEL_FONT_SIZE2 = 13;
@@ -38520,7 +38999,9 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
38520
38999
  const sTitleHeight = ctx.structural(TITLE_HEIGHT2);
38521
39000
  const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
38522
39001
  const sLegendFixedGap = ctx.aesthetic(LEGEND_FIXED_GAP2);
38523
- const hasLegend = layout.legend.length > 0;
39002
+ const noLegend = legendSuppressed(parsed.options);
39003
+ const hasLegend = layout.legend.length > 0 && !noLegend;
39004
+ const suppressedLegendBand = layout.legend.length > 0 && noLegend;
38524
39005
  const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
38525
39006
  const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
38526
39007
  const fixedLegend = !exportDims && hasLegend;
@@ -38546,7 +39027,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
38546
39027
  const titleOffset = !fixedTitle && showTitle ? sTitleHeight : 0;
38547
39028
  const diagramW = layout.width;
38548
39029
  let diagramH = layout.height + titleOffset;
38549
- if (fixedLegend) {
39030
+ if (fixedLegend || suppressedLegendBand) {
38550
39031
  diagramH -= layoutLegendShift;
38551
39032
  }
38552
39033
  const availH = height - sDiagramPadding * 2 - fixedReserveTop - fixedReserveBottom;
@@ -39098,7 +39579,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
39098
39579
  if (showTopTitle) {
39099
39580
  svg.append("text").attr("class", "chart-title").attr("data-line-number", parsed.titleLineNumber ?? 0).attr("x", sDiagramPadding).attr("y", sDiagramPadding + sTitleFontSize).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(parsed.title);
39100
39581
  }
39101
- if (parsed.tagGroups.length > 0) {
39582
+ if (parsed.tagGroups.length > 0 && !legendSuppressed(parsed.options)) {
39102
39583
  const titleTextWidth = showTopTitle ? measureLegendText(parsed.title, sTitleFontSize) + 16 : 0;
39103
39584
  const legendX = sDiagramPadding + titleTextWidth;
39104
39585
  const legendConfig = {
@@ -39636,6 +40117,7 @@ var init_renderer7 = __esm({
39636
40117
  init_mutations();
39637
40118
  init_legend_constants();
39638
40119
  init_legend_layout();
40120
+ init_parsing();
39639
40121
  init_legend_integration();
39640
40122
  init_scaling();
39641
40123
  init_text_measure();
@@ -39959,7 +40441,7 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
39959
40441
  const sTitleY = ctx.structural(TITLE_Y);
39960
40442
  const sLegendHeight = ctx.structural(LEGEND_HEIGHT);
39961
40443
  const legendEntries = collectClassTypes(parsed);
39962
- const hasLegend = legendEntries.length > 1;
40444
+ const hasLegend = legendEntries.length > 1 && !legendSuppressed(parsed.options);
39963
40445
  const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
39964
40446
  const titleHeight = showTitle ? 40 : 0;
39965
40447
  const LEGEND_FIXED_GAP4 = 8;
@@ -40187,7 +40669,7 @@ function renderClassDiagramForExport(content, theme, palette) {
40187
40669
  const isDark = theme === "dark";
40188
40670
  const legendEntries = collectClassTypes(parsed);
40189
40671
  const EXPORT_LEGEND_GAP = 8;
40190
- const legendReserve = legendEntries.length > 1 ? LEGEND_HEIGHT + EXPORT_LEGEND_GAP : 0;
40672
+ const legendReserve = legendEntries.length > 1 && !legendSuppressed(parsed.options) ? LEGEND_HEIGHT + EXPORT_LEGEND_GAP : 0;
40191
40673
  const exportWidth = layout.width + DIAGRAM_PADDING4 * 2;
40192
40674
  const exportHeight = layout.height + DIAGRAM_PADDING4 * 2 + (parsed.title ? 40 : 0) + legendReserve;
40193
40675
  return runInExportContainer(exportWidth, exportHeight, (container) => {
@@ -40776,8 +41258,9 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
40776
41258
  d3Selection13.select(container).selectAll(":not([data-d3-tooltip])").remove();
40777
41259
  const useSemanticColors = parsed.tagGroups.length === 0 && layout.nodes.every((n) => !n.color);
40778
41260
  const LEGEND_FIXED_GAP4 = 8;
40779
- const hasTagLegend = parsed.tagGroups.length > 0;
40780
- const legendReserveH = useSemanticColors ? LEGEND_HEIGHT + LEGEND_FIXED_GAP4 : hasTagLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP4 : 0;
41261
+ const noLegend = legendSuppressed(parsed.options);
41262
+ const hasTagLegend = parsed.tagGroups.length > 0 && !noLegend;
41263
+ const legendReserveH = !noLegend && (useSemanticColors || hasTagLegend) ? LEGEND_HEIGHT + LEGEND_FIXED_GAP4 : 0;
40781
41264
  const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
40782
41265
  const titleHeight = showTitle ? 40 : 0;
40783
41266
  const diagramW = layout.width;
@@ -40981,7 +41464,7 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
40981
41464
  }
40982
41465
  }
40983
41466
  }
40984
- if (parsed.tagGroups.length > 0) {
41467
+ if (hasTagLegend) {
40985
41468
  const legendY = sDiagramPadding + titleHeight;
40986
41469
  const legendG = svg.append("g").attr("class", "er-tag-legend").attr("transform", `translate(0,${legendY})`);
40987
41470
  renderIntegratedLegend(legendG, {
@@ -40994,7 +41477,7 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
40994
41477
  });
40995
41478
  legendG.selectAll("[data-legend-group]").classed("er-legend-group", true);
40996
41479
  }
40997
- if (semanticRoles) {
41480
+ if (semanticRoles && !noLegend) {
40998
41481
  const presentRoles = ROLE_ORDER.filter((role) => {
40999
41482
  for (const r of semanticRoles.values()) {
41000
41483
  if (r === role) return true;
@@ -42820,7 +43303,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
42820
43303
  const reserveHasDescriptions = parsed.nodes.some(
42821
43304
  (n) => n.description && n.description.length > 0
42822
43305
  );
42823
- const willRenderLegend = legendGroups.length > 0 || reserveHasDescriptions && controlsHost !== "app";
43306
+ const noLegend = legendSuppressed(parsed.options);
43307
+ const willRenderLegend = (legendGroups.length > 0 || reserveHasDescriptions && controlsHost !== "app") && !noLegend;
42824
43308
  const sLegendHeight = willRenderLegend ? sctx.structural(
42825
43309
  getMaxLegendReservedHeight(
42826
43310
  {
@@ -42841,7 +43325,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
42841
43325
  const hasAnyDescriptions = parsed.nodes.some(
42842
43326
  (n) => n.description && n.description.length > 0
42843
43327
  );
42844
- const needsLegend = legendGroups.length > 0 || hasAnyDescriptions && onToggleDescriptions;
43328
+ const needsLegend = (legendGroups.length > 0 || hasAnyDescriptions && !!onToggleDescriptions) && !noLegend;
42845
43329
  const legendH = needsLegend ? sLegendHeight + 8 : 0;
42846
43330
  const groupLabelsSet = new Set(layout.groups.map((g) => g.label));
42847
43331
  let labelZoneExtension = 0;
@@ -43104,11 +43588,11 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
43104
43588
  const tooltipText = fullText.length > 200 ? fullText.slice(0, 199) + "\u2026" : fullText;
43105
43589
  nodeG.append("title").text(tooltipText);
43106
43590
  }
43107
- if (parsed.showValues && node.value !== void 0) {
43591
+ if (parsed.showValues !== false && node.value !== void 0) {
43108
43592
  const valueLabel = parsed.boxMetric ? `${parsed.boxMetric}: ${node.value}` : String(node.value);
43109
43593
  nodeG.append("text").attr("class", "bl-node-value").attr("x", 0).attr("y", descStartY + visibleLines.length * descLineH).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", colors.text).text(valueLabel);
43110
43594
  }
43111
- } else if (parsed.showValues && node.value !== void 0) {
43595
+ } else if (parsed.showValues !== false && node.value !== void 0) {
43112
43596
  const valueLabel = parsed.boxMetric ? `${parsed.boxMetric}: ${node.value}` : String(node.value);
43113
43597
  const headerH = ln.height / 2;
43114
43598
  const sepY = -ln.height / 2 + headerH;
@@ -43211,7 +43695,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
43211
43695
  const hasDescriptions = parsed.nodes.some(
43212
43696
  (n) => n.description && n.description.length > 0
43213
43697
  );
43214
- const hasLegend = legendGroups.length > 0 || hasDescriptions && controlsHost !== "app";
43698
+ const hasLegend = (legendGroups.length > 0 || hasDescriptions && controlsHost !== "app") && !noLegend;
43215
43699
  if (hasLegend) {
43216
43700
  let controlsGroup;
43217
43701
  if (hasDescriptions && (onToggleDescriptions || controlsHost === "app")) {
@@ -45349,7 +45833,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
45349
45833
  const sizes = /* @__PURE__ */ new Map();
45350
45834
  let maxDescH = 0;
45351
45835
  for (const node of parsed.nodes) {
45352
- const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
45836
+ const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues !== false);
45353
45837
  sizes.set(node.label, s);
45354
45838
  if (!hideDescriptions && node.description && node.description.length > 0)
45355
45839
  maxDescH = Math.max(maxDescH, s.height);
@@ -45585,22 +46069,26 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
45585
46069
  const REFINE_K = opts?.refineK ?? 6;
45586
46070
  const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
45587
46071
  const prev = opts?.previousPositions;
45588
- const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
45589
- const SPACINGS = [
45590
- { nodesep: 50, ranksep: 60 },
45591
- { nodesep: 34, ranksep: 46 },
45592
- { nodesep: 66, ranksep: 82 }
45593
- ];
45594
46072
  const configs = [];
45595
- for (const ranker of RANKERS)
45596
- for (const sp of SPACINGS) configs.push({ ranker, ...sp });
45597
- for (let s = 0; s < seedCount; s++)
45598
- configs.push({
45599
- ranker: "network-simplex",
45600
- nodesep: 50,
45601
- ranksep: 60,
45602
- seed: s
45603
- });
46073
+ if (opts?.configs) {
46074
+ configs.push(...opts.configs);
46075
+ } else {
46076
+ const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
46077
+ const SPACINGS = [
46078
+ { nodesep: 50, ranksep: 60 },
46079
+ { nodesep: 34, ranksep: 46 },
46080
+ { nodesep: 66, ranksep: 82 }
46081
+ ];
46082
+ for (const ranker of RANKERS)
46083
+ for (const sp of SPACINGS) configs.push({ ranker, ...sp });
46084
+ for (let s = 0; s < seedCount; s++)
46085
+ configs.push({
46086
+ ranker: "network-simplex",
46087
+ nodesep: 50,
46088
+ ranksep: 60,
46089
+ seed: s
46090
+ });
46091
+ }
45604
46092
  const badness = (lay, floor) => {
45605
46093
  const x = countSplineCrossings(lay, floor);
45606
46094
  if (x > floor) return Infinity;
@@ -45619,9 +46107,12 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
45619
46107
  }
45620
46108
  };
45621
46109
  const pool = [];
46110
+ const cfgOf = /* @__PURE__ */ new Map();
45622
46111
  for (const cfg of configs) {
45623
46112
  try {
45624
- pool.push(place(cfg));
46113
+ const lay = place(cfg);
46114
+ pool.push(lay);
46115
+ cfgOf.set(lay, cfg);
45625
46116
  } catch {
45626
46117
  }
45627
46118
  await step("Optimizing layout");
@@ -45657,22 +46148,26 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
45657
46148
  best = lay;
45658
46149
  }
45659
46150
  };
46151
+ const topConfigs = [];
45660
46152
  for (const lay of pool.slice(0, refineK)) {
45661
46153
  consider(lay);
46154
+ const cfg = cfgOf.get(lay);
46155
+ if (cfg) topConfigs.push(cfg);
45662
46156
  await step("Refining layout");
45663
46157
  }
45664
- if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
46158
+ if (!opts?.configs && bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
45665
46159
  const extra = [];
45666
46160
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
46161
+ const cfg = {
46162
+ ranker: "network-simplex",
46163
+ nodesep: 50,
46164
+ ranksep: 60,
46165
+ seed: s
46166
+ };
45667
46167
  try {
45668
- extra.push(
45669
- place({
45670
- ranker: "network-simplex",
45671
- nodesep: 50,
45672
- ranksep: 60,
45673
- seed: s
45674
- })
45675
- );
46168
+ const lay = place(cfg);
46169
+ extra.push(lay);
46170
+ cfgOf.set(lay, cfg);
45676
46171
  } catch {
45677
46172
  }
45678
46173
  }
@@ -45680,7 +46175,11 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
45680
46175
  for (const lay of extra)
45681
46176
  extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
45682
46177
  extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
45683
- for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
46178
+ for (const lay of extra.slice(0, ESCALATE_REFINE)) {
46179
+ consider(lay);
46180
+ const cfg = cfgOf.get(lay);
46181
+ if (cfg) topConfigs.push(cfg);
46182
+ }
45684
46183
  }
45685
46184
  for (const lay of layered) {
45686
46185
  const variants = [lay];
@@ -45704,6 +46203,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
45704
46203
  if (separated !== best && badness(separated, bestBad - 1) < bestBad)
45705
46204
  best = separated;
45706
46205
  }
46206
+ opts?.onTopConfigs?.(topConfigs);
45707
46207
  return best;
45708
46208
  }
45709
46209
  var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE3;
@@ -45817,16 +46317,22 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
45817
46317
  onProgress: layoutOptions.onProgress
45818
46318
  }
45819
46319
  };
45820
- const searched = await layoutBoxesAndLinesSearch2(
45821
- parsed,
45822
- collapseInfo,
45823
- searchOpts
45824
- );
46320
+ let topConfigs;
46321
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
46322
+ ...searchOpts,
46323
+ onTopConfigs: (cfgs) => {
46324
+ topConfigs = cfgs;
46325
+ }
46326
+ });
45825
46327
  let placed = placeEdgeLabels(applyParallelEdgeOffsets(searched));
45826
46328
  if (placed.unresolved.length > 0) {
45827
46329
  const relaid = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
45828
46330
  ...searchOpts,
45829
- reserveEdgeLabels: true
46331
+ reserveEdgeLabels: true,
46332
+ // Only the label reservation changed — re-laying-out the top candidates
46333
+ // from the first search is enough. Falls back to the full pool when the
46334
+ // first search surfaced no dagre candidates.
46335
+ ...topConfigs !== void 0 && topConfigs.length > 0 && { configs: topConfigs }
45830
46336
  });
45831
46337
  const relaidPlaced = placeEdgeLabels(applyParallelEdgeOffsets(relaid));
45832
46338
  if (relaidPlaced.unresolved.length < placed.unresolved.length)
@@ -47365,7 +47871,7 @@ function renderFamilyForExport(container, parsed, layout, palette, isDark, opts
47365
47871
  name: g.name,
47366
47872
  entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
47367
47873
  }));
47368
- const hasLegend = legendGroups.length > 0;
47874
+ const hasLegend = legendGroups.length > 0 && !legendSuppressed(parsed.options);
47369
47875
  const legendConfig = {
47370
47876
  groups: legendGroups,
47371
47877
  position: { placement: "top-center", titleRelation: "below-title" },
@@ -48186,7 +48692,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
48186
48692
  const svg = d3Selection17.select(container).append("svg").attr("width", containerWidth).attr("height", containerHeight).attr("viewBox", `0 0 ${containerWidth} ${containerHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY);
48187
48693
  const appHosted = options?.controlsHost === "app";
48188
48694
  const hasControls = !!options?.onToggleColorByDepth || !!options?.onToggleDescriptions;
48189
- const hasLegend = parsed.tagGroups.length > 0 || hasControls && !appHosted;
48695
+ const hasLegend = (parsed.tagGroups.length > 0 || hasControls && !appHosted) && !legendSuppressed(parsed.options);
48190
48696
  const fixedLegend = !isExport && hasLegend;
48191
48697
  const legendReserve = fixedLegend ? getMaxLegendReservedHeight(
48192
48698
  {
@@ -49386,6 +49892,23 @@ __export(layout_exports11, {
49386
49892
  rollUpContextRelationships: () => rollUpContextRelationships
49387
49893
  });
49388
49894
  import dagre5 from "@dagrejs/dagre";
49895
+ function transposeGraph(g) {
49896
+ for (const name of g.nodes()) {
49897
+ const pos = gNode(g, name);
49898
+ if (!pos) continue;
49899
+ [pos.x, pos.y] = [pos.y, pos.x];
49900
+ [pos.width, pos.height] = [pos.height, pos.width];
49901
+ }
49902
+ }
49903
+ function reduceCrossingsForDirection(g, direction, edgeList, nodeGroupMap) {
49904
+ if (direction === "LR") {
49905
+ transposeGraph(g);
49906
+ reduceCrossings(g, edgeList, nodeGroupMap);
49907
+ transposeGraph(g);
49908
+ return;
49909
+ }
49910
+ reduceCrossings(g, edgeList, nodeGroupMap);
49911
+ }
49389
49912
  function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
49390
49913
  let penalty = 0;
49391
49914
  for (const edge of edgeList) {
@@ -49810,9 +50333,10 @@ function layoutC4Context(parsed, activeTagGroup) {
49810
50333
  }
49811
50334
  const contextRels = rollUpContextRelationships(parsed);
49812
50335
  const spacing = computeAdaptiveSpacing(contextRels);
50336
+ const direction = parsed.direction;
49813
50337
  const g = new dagre5.graphlib.Graph();
49814
50338
  g.setGraph({
49815
- rankdir: "TB",
50339
+ rankdir: direction,
49816
50340
  nodesep: spacing.nodesep,
49817
50341
  ranksep: spacing.ranksep,
49818
50342
  edgesep: spacing.edgesep
@@ -49832,8 +50356,9 @@ function layoutC4Context(parsed, activeTagGroup) {
49832
50356
  }
49833
50357
  }
49834
50358
  dagre5.layout(g);
49835
- reduceCrossings(
50359
+ reduceCrossingsForDirection(
49836
50360
  g,
50361
+ direction,
49837
50362
  validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
49838
50363
  );
49839
50364
  const nodes = contextElements.map(
@@ -49913,7 +50438,9 @@ function layoutC4Context(parsed, activeTagGroup) {
49913
50438
  }
49914
50439
  let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN10 * 2 : 0;
49915
50440
  let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN10 * 2 : 0;
49916
- const legendGroups = computeLegendGroups3(parsed.tagGroups);
50441
+ const legendGroups = computeLegendGroups3(
50442
+ legendSuppressed(parsed.options) ? [] : parsed.tagGroups
50443
+ );
49917
50444
  if (legendGroups.length > 0) {
49918
50445
  const legendY = totalHeight + MARGIN10;
49919
50446
  let legendX = MARGIN10;
@@ -50009,6 +50536,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
50009
50536
  }
50010
50537
  }
50011
50538
  const hasGroups = elementToGroup.size > 0;
50539
+ const direction = parsed.direction;
50012
50540
  const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
50013
50541
  g.setDefaultEdgeLabel(() => ({}));
50014
50542
  if (hasGroups) {
@@ -50077,7 +50605,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
50077
50605
  }
50078
50606
  const spacing = computeAdaptiveSpacing(containerRels);
50079
50607
  g.setGraph({
50080
- rankdir: "TB",
50608
+ rankdir: direction,
50081
50609
  nodesep: spacing.nodesep,
50082
50610
  ranksep: spacing.ranksep,
50083
50611
  edgesep: spacing.edgesep
@@ -50089,8 +50617,9 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
50089
50617
  }
50090
50618
  dagre5.layout(g);
50091
50619
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
50092
- reduceCrossings(
50620
+ reduceCrossingsForDirection(
50093
50621
  g,
50622
+ direction,
50094
50623
  containerRels.filter(
50095
50624
  (r) => nameToElement.has(r.sourceName) && nameToElement.has(r.targetName)
50096
50625
  ).map((r) => ({ source: r.sourceName, target: r.targetName })),
@@ -50268,7 +50797,9 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
50268
50797
  }
50269
50798
  let totalWidth = maxX - minX + MARGIN10 * 2;
50270
50799
  let totalHeight = maxY - minY + MARGIN10 * 2;
50271
- const legendGroups = computeLegendGroups3(parsed.tagGroups);
50800
+ const legendGroups = computeLegendGroups3(
50801
+ legendSuppressed(parsed.options) ? [] : parsed.tagGroups
50802
+ );
50272
50803
  if (legendGroups.length > 0) {
50273
50804
  const legendY = totalHeight + MARGIN10;
50274
50805
  let legendX = MARGIN10;
@@ -50416,6 +50947,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
50416
50947
  }
50417
50948
  }
50418
50949
  const hasGroups = elementToGroup.size > 0;
50950
+ const direction = parsed.direction;
50419
50951
  const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
50420
50952
  g.setDefaultEdgeLabel(() => ({}));
50421
50953
  if (hasGroups) {
@@ -50490,7 +51022,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
50490
51022
  }
50491
51023
  const spacing = computeAdaptiveSpacing(componentRels);
50492
51024
  g.setGraph({
50493
- rankdir: "TB",
51025
+ rankdir: direction,
50494
51026
  nodesep: spacing.nodesep,
50495
51027
  ranksep: spacing.ranksep,
50496
51028
  edgesep: spacing.edgesep
@@ -50502,8 +51034,9 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
50502
51034
  }
50503
51035
  dagre5.layout(g);
50504
51036
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
50505
- reduceCrossings(
51037
+ reduceCrossingsForDirection(
50506
51038
  g,
51039
+ direction,
50507
51040
  componentRels.filter(
50508
51041
  (r) => nameToElement.has(r.sourceName) && nameToElement.has(r.targetName)
50509
51042
  ).map((r) => ({ source: r.sourceName, target: r.targetName })),
@@ -50683,7 +51216,9 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
50683
51216
  }
50684
51217
  let totalWidth = maxX - minX + MARGIN10 * 2;
50685
51218
  let totalHeight = maxY - minY + MARGIN10 * 2;
50686
- const legendGroups = computeLegendGroups3(parsed.tagGroups);
51219
+ const legendGroups = computeLegendGroups3(
51220
+ legendSuppressed(parsed.options) ? [] : parsed.tagGroups
51221
+ );
50687
51222
  if (legendGroups.length > 0) {
50688
51223
  const legendY = totalHeight + MARGIN10;
50689
51224
  let legendX = MARGIN10;
@@ -50792,6 +51327,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
50792
51327
  for (const r of refEntries) {
50793
51328
  nameToElement.set(r.element.name, r.element);
50794
51329
  }
51330
+ const direction = parsed.direction;
50795
51331
  const g = new dagre5.graphlib.Graph({ compound: true });
50796
51332
  g.setDefaultEdgeLabel(() => ({}));
50797
51333
  for (const [infraId] of infraIds) {
@@ -50826,7 +51362,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
50826
51362
  }
50827
51363
  const spacing = computeAdaptiveSpacing(deployRels);
50828
51364
  g.setGraph({
50829
- rankdir: "TB",
51365
+ rankdir: direction,
50830
51366
  nodesep: spacing.nodesep,
50831
51367
  ranksep: spacing.ranksep,
50832
51368
  edgesep: spacing.edgesep
@@ -50839,8 +51375,9 @@ function layoutC4Deployment(parsed, activeTagGroup) {
50839
51375
  dagre5.layout(g);
50840
51376
  const nodeInfraMap = /* @__PURE__ */ new Map();
50841
51377
  for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
50842
- reduceCrossings(
51378
+ reduceCrossingsForDirection(
50843
51379
  g,
51380
+ direction,
50844
51381
  deployRels.filter(
50845
51382
  (r) => nameToElement.has(r.sourceName) && nameToElement.has(r.targetName)
50846
51383
  ).map((r) => ({ source: r.sourceName, target: r.targetName })),
@@ -50992,7 +51529,9 @@ function layoutC4Deployment(parsed, activeTagGroup) {
50992
51529
  }
50993
51530
  let totalWidth = maxX - minX + MARGIN10 * 2;
50994
51531
  let totalHeight = maxY - minY + MARGIN10 * 2;
50995
- const legendGroups = computeLegendGroups3(parsed.tagGroups);
51532
+ const legendGroups = computeLegendGroups3(
51533
+ legendSuppressed(parsed.options) ? [] : parsed.tagGroups
51534
+ );
50996
51535
  if (legendGroups.length > 0) {
50997
51536
  const legendY = totalHeight + MARGIN10;
50998
51537
  let legendX = MARGIN10;
@@ -51020,6 +51559,7 @@ var init_layout11 = __esm({
51020
51559
  "src/c4/layout.ts"() {
51021
51560
  "use strict";
51022
51561
  init_tag_groups();
51562
+ init_parsing();
51023
51563
  init_legend_constants();
51024
51564
  init_text_measure();
51025
51565
  gNode = (g, name) => g.node(name);
@@ -52195,6 +52735,7 @@ function layoutGraph(graph, options) {
52195
52735
  shape: node.shape,
52196
52736
  ...node.color !== void 0 && { color: node.color },
52197
52737
  ...node.group !== void 0 && { group: node.group },
52738
+ ...node.metadata !== void 0 && { metadata: node.metadata },
52198
52739
  lineNumber: node.lineNumber,
52199
52740
  x: p.x,
52200
52741
  y: p.y,
@@ -52414,6 +52955,14 @@ __export(flowchart_renderer_exports, {
52414
52955
  renderFlowchartForExport: () => renderFlowchartForExport
52415
52956
  });
52416
52957
  import * as d3Selection20 from "d3-selection";
52958
+ function clipPointToDiamond(cx, cy, halfW, halfH, towardX, towardY) {
52959
+ const dx = towardX - cx;
52960
+ const dy = towardY - cy;
52961
+ const denom = Math.abs(dx) / halfW + Math.abs(dy) / halfH;
52962
+ if (denom === 0) return { x: cx, y: cy };
52963
+ const s = 1 / denom;
52964
+ return { x: cx + dx * s, y: cy + dy * s };
52965
+ }
52417
52966
  function shapeDefaultColor(shape, palette, isEndTerminal, colorOff) {
52418
52967
  if (colorOff) return palette.textMuted;
52419
52968
  switch (shape) {
@@ -52751,13 +53300,51 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
52751
53300
  }
52752
53301
  const labelPosMap = /* @__PURE__ */ new Map();
52753
53302
  for (const lp of labelPositions) labelPosMap.set(lp.edgeIdx, lp);
53303
+ const nodeGeom = /* @__PURE__ */ new Map();
53304
+ for (const n of layout.nodes) {
53305
+ nodeGeom.set(n.id, {
53306
+ x: n.x,
53307
+ y: n.y,
53308
+ halfW: n.width / 2,
53309
+ halfH: n.height / 2,
53310
+ shape: n.shape
53311
+ });
53312
+ }
52754
53313
  for (let ei = 0; ei < layout.edges.length; ei++) {
52755
53314
  const edge = layout.edges[ei];
52756
53315
  if (edge.points.length < 2) continue;
52757
53316
  const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber)).attr("data-source", edge.source).attr("data-target", edge.target);
52758
53317
  const edgeColor3 = palette.textMuted;
52759
53318
  const markerId = "fc-arrow";
52760
- const pathD = edgeSplinePath(edge.points);
53319
+ let drawPoints = edge.points;
53320
+ const src = nodeGeom.get(edge.source);
53321
+ const tgt = nodeGeom.get(edge.target);
53322
+ if (src?.shape === "decision" || tgt?.shape === "decision") {
53323
+ const pts = edge.points.map((p) => ({ x: p.x, y: p.y }));
53324
+ const last = pts.length - 1;
53325
+ if (tgt?.shape === "decision") {
53326
+ pts[last] = clipPointToDiamond(
53327
+ tgt.x,
53328
+ tgt.y,
53329
+ tgt.halfW,
53330
+ tgt.halfH,
53331
+ pts[last - 1].x,
53332
+ pts[last - 1].y
53333
+ );
53334
+ }
53335
+ if (src?.shape === "decision") {
53336
+ pts[0] = clipPointToDiamond(
53337
+ src.x,
53338
+ src.y,
53339
+ src.halfW,
53340
+ src.halfH,
53341
+ pts[1].x,
53342
+ pts[1].y
53343
+ );
53344
+ }
53345
+ drawPoints = pts;
53346
+ }
53347
+ const pathD = edgeSplinePath(drawPoints);
52761
53348
  if (pathD) {
52762
53349
  edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", sEdgeStrokeWidth).attr("marker-end", `url(#${markerId})`).attr("class", "fc-edge");
52763
53350
  }
@@ -55682,7 +56269,7 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
55682
56269
  palette,
55683
56270
  layout.edges
55684
56271
  );
55685
- const hasLegend = legendGroups.length > 0 || !!playback && !appHostedPlayback;
56272
+ const hasLegend = (legendGroups.length > 0 || !!playback && !appHostedPlayback) && !legendSuppressed(layout.options);
55686
56273
  const fixedLegend = !exportMode && hasLegend;
55687
56274
  const legendDynamicH = hasLegend ? getMaxLegendReservedHeight(
55688
56275
  {
@@ -55873,6 +56460,7 @@ var init_renderer17 = __esm({
55873
56460
  init_legend_layout();
55874
56461
  init_title_constants();
55875
56462
  init_scaling();
56463
+ init_parsing();
55876
56464
  init_visual_conventions();
55877
56465
  NODE_FONT_SIZE5 = 13;
55878
56466
  META_FONT_SIZE6 = 10;
@@ -57702,7 +58290,7 @@ function renderPert(container, resolved, layout, palette, isDark, options = {})
57702
58290
  resolved.options.activeTag,
57703
58291
  options.activeTagOverride
57704
58292
  );
57705
- const showTagLegend = showLegend && resolved.tagGroups.length > 0;
58293
+ const showTagLegend = showLegend && !resolved.options.noLegend && resolved.tagGroups.length > 0;
57706
58294
  const legendBlockHeight = showTagLegend ? LEGEND_TOP_GAP + LEGEND_PILL_HEIGHT + LEGEND_BOTTOM_GAP : 0;
57707
58295
  const naturalChartWidth = layout.width + DIAGRAM_PADDING12 * 2;
57708
58296
  const minAnalysisRowW = analysisLayer.analysisHasContent ? analysisLayer.minContentWidth + 2 * DIAGRAM_PADDING12 : 0;
@@ -60303,7 +60891,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
60303
60891
  const showTitle = !!title && !resolved.options.noTitle;
60304
60892
  const titleHeight = showTitle ? 50 : 20;
60305
60893
  const containerWidth = exportDims?.width ?? (container.clientWidth || 800);
60306
- const hasTagLegend = resolved.tagGroups.length > 0 || hasCriticalPath || hasDependencies;
60894
+ const hasTagLegend = !resolved.options.noLegend && (resolved.tagGroups.length > 0 || hasCriticalPath || hasDependencies);
60307
60895
  const tagLegendReserve = hasTagLegend ? getMaxLegendReservedHeight(
60308
60896
  {
60309
60897
  groups: resolved.tagGroups.map((tg) => ({
@@ -60364,7 +60952,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
60364
60952
  }
60365
60953
  function drawLegend() {
60366
60954
  svg.selectAll(".gantt-tag-legend-container").remove();
60367
- if (resolved.tagGroups.length > 0 || hasCriticalPath || hasDependencies) {
60955
+ if (hasTagLegend) {
60368
60956
  const legendY = sTitleHeight;
60369
60957
  renderTagLegend(
60370
60958
  svg,
@@ -62307,6 +62895,23 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
62307
62895
  const sGroupExtraPadding = ctx.aesthetic(GROUP_EXTRA_PADDING);
62308
62896
  const showTitle = !!graph.title && graph.options["no-title"] !== "on";
62309
62897
  const titleHeight = showTitle ? 40 : 0;
62898
+ const tagGroups = graph.tagGroups ?? [];
62899
+ const activeTagGroup = resolveActiveTagGroup(
62900
+ tagGroups,
62901
+ graph.options["active-tag"]
62902
+ );
62903
+ const legendGroups = tagGroups;
62904
+ const hasLegend = legendGroups.length > 0 && !legendSuppressed(graph.options);
62905
+ const legendH = hasLegend ? ctx.structural(
62906
+ getMaxLegendReservedHeight(
62907
+ {
62908
+ groups: legendGroups,
62909
+ position: { placement: "top-center", titleRelation: "below-title" },
62910
+ mode: exportDims ? "export" : "preview"
62911
+ },
62912
+ width
62913
+ )
62914
+ ) + 8 : 0;
62310
62915
  const diagramW = layout.width;
62311
62916
  const diagramH = layout.height;
62312
62917
  const { scale, offsetX, offsetY, canvasHeight } = fitDiagramToCanvas({
@@ -62315,7 +62920,9 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
62315
62920
  diagramW,
62316
62921
  diagramH,
62317
62922
  padding: sDiagramPadding,
62318
- titleHeight,
62923
+ // The legend band sits directly under the title, so it reserves the same
62924
+ // top strip fitDiagramToCanvas already offsets the diagram by.
62925
+ titleHeight: titleHeight + legendH,
62319
62926
  maxScale: MAX_SCALE7,
62320
62927
  exportMode: !!exportDims
62321
62928
  });
@@ -62348,6 +62955,21 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
62348
62955
  }
62349
62956
  }
62350
62957
  }
62958
+ if (hasLegend) {
62959
+ const legendG = svg.append("g").attr("transform", `translate(0, ${titleHeight + 4})`);
62960
+ renderIntegratedLegend(legendG, {
62961
+ groups: legendGroups,
62962
+ activeGroup: activeTagGroup,
62963
+ mode: exportDims ? "export" : "preview",
62964
+ // Inactive sibling groups stay visible as collapsed pills so the user
62965
+ // can click one to flip the active colouring dimension (as in b&l).
62966
+ showInactivePills: true,
62967
+ palette,
62968
+ isDark,
62969
+ width
62970
+ });
62971
+ legendG.selectAll("[data-legend-group]").classed("st-legend-group", true);
62972
+ }
62351
62973
  const contentG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
62352
62974
  for (const group of layout.groups) {
62353
62975
  if (group.collapsed) continue;
@@ -62453,9 +63075,22 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
62453
63075
  const colorOff = graph.options?.["color"] === "off";
62454
63076
  const fillMode = fillModeFromOptions(graph.options ?? {});
62455
63077
  const noNotes = graph.options?.["no-notes"] === "on";
63078
+ const mutableTagGroups = [...tagGroups];
63079
+ const nodeIntentColor = (node) => node.color ?? (activeTagGroup ? resolveTagColor(
63080
+ node.metadata ?? {},
63081
+ mutableTagGroups,
63082
+ activeTagGroup
63083
+ ) : void 0);
62456
63084
  for (const node of layout.nodes) {
62457
63085
  const isCollapsedGroup = collapsedGroupIds.has(node.id);
62458
63086
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", isCollapsedGroup ? "st-group-wrapper st-node" : "st-node").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id).style("cursor", "pointer");
63087
+ if (activeTagGroup && node.shape !== "pseudostate") {
63088
+ const tagKey = tagAttrKey(activeTagGroup);
63089
+ const metaValue = node.metadata?.[tagKey];
63090
+ if (metaValue) {
63091
+ nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
63092
+ }
63093
+ }
62459
63094
  if (isCollapsedGroup) {
62460
63095
  nodeG.attr("data-group-toggle", node.id).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "false").attr("aria-label", `Expand group ${node.label}`);
62461
63096
  }
@@ -62470,7 +63105,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
62470
63105
  } else if (isCollapsedGroup) {
62471
63106
  const w = node.width;
62472
63107
  const h = node.height;
62473
- const groupColor = node.color ?? stateDefaultColor(palette, colorOff);
63108
+ const groupColor = nodeIntentColor(node) ?? stateDefaultColor(palette, colorOff);
62474
63109
  const fillColor = shapeFill(palette, groupColor, isDark, {
62475
63110
  mode: fillMode
62476
63111
  });
@@ -62491,14 +63126,15 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
62491
63126
  } else {
62492
63127
  const w = node.width;
62493
63128
  const h = node.height;
63129
+ const intentColor = nodeIntentColor(node);
62494
63130
  const resolvedFill = stateFill(
62495
63131
  palette,
62496
63132
  isDark,
62497
- node.color,
63133
+ intentColor,
62498
63134
  colorOff,
62499
63135
  fillMode
62500
63136
  );
62501
- nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", sStateCornerRadius).attr("ry", sStateCornerRadius).attr("fill", resolvedFill).attr("stroke", stateStroke(palette, node.color, colorOff)).attr("stroke-width", sNodeStrokeWidth);
63137
+ nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", sStateCornerRadius).attr("ry", sStateCornerRadius).attr("fill", resolvedFill).attr("stroke", stateStroke(palette, intentColor, colorOff)).attr("stroke-width", sNodeStrokeWidth);
62502
63138
  nodeG.append("text").attr("x", 0).attr("y", 0).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr(
62503
63139
  "fill",
62504
63140
  contrastText(
@@ -62590,6 +63226,9 @@ var init_state_renderer = __esm({
62590
63226
  init_fit_canvas();
62591
63227
  init_fonts();
62592
63228
  init_color_utils();
63229
+ init_tag_groups();
63230
+ init_legend_integration();
63231
+ init_legend_layout();
62593
63232
  init_state_parser();
62594
63233
  init_layout12();
62595
63234
  init_edge_spline();
@@ -63367,8 +64006,8 @@ function renderTechRadar(container, parsed, palette, isDark, onClickItem, export
63367
64006
  );
63368
64007
  return;
63369
64008
  }
63370
- const directiveOn = parsed.options["show-blip-legend"] === "on";
63371
- const showListing = exportDims ? true : options?.showListing ?? directiveOn;
64009
+ const directiveOff = parsed.options["no-blip-legend"] === "on";
64010
+ const showListing = exportDims ? !directiveOff : options?.showListing ?? !directiveOff;
63372
64011
  const init = initRadarSvg(container, palette, exportDims);
63373
64012
  if (!init) return;
63374
64013
  const { svg, width, height, textColor, mutedColor } = init;
@@ -63400,7 +64039,7 @@ function renderTechRadar(container, parsed, palette, isDark, onClickItem, export
63400
64039
  svg.append("text").attr("x", radarWidth / 2).attr("y", titleY).attr("text-anchor", "middle").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", sTitleFontSize).attr("font-weight", "bold").text(parsed.title);
63401
64040
  }
63402
64041
  let legendReservedHeight = 0;
63403
- if (!exportDims && options?.onToggleListing) {
64042
+ if (!exportDims && options?.onToggleListing && !legendSuppressed(parsed.options)) {
63404
64043
  const legendY = showTitle ? titleY + 8 : 4;
63405
64044
  const legendG = svg.append("g").attr("transform", `translate(0, ${legendY})`);
63406
64045
  const legendConfig = {
@@ -64039,6 +64678,7 @@ var init_renderer20 = __esm({
64039
64678
  init_interactive();
64040
64679
  init_legend_integration();
64041
64680
  init_legend_layout();
64681
+ init_parsing();
64042
64682
  BLIP_RADIUS2 = 12;
64043
64683
  BLIP_FONT_SIZE2 = 9;
64044
64684
  RING_LABEL_FONT_SIZE = 13;
@@ -66409,11 +67049,49 @@ function pointInGeometry(geometry, lon, lat) {
66409
67049
  }
66410
67050
  return false;
66411
67051
  }
67052
+ function planarGeometryBbox(geometry) {
67053
+ const g = geometry;
67054
+ if (!g) return null;
67055
+ const polys = g.type === "Polygon" ? [g.coordinates] : g.type === "MultiPolygon" ? g.coordinates : [];
67056
+ let minLon = Infinity;
67057
+ let minLat = Infinity;
67058
+ let maxLon = -Infinity;
67059
+ let maxLat = -Infinity;
67060
+ let seen = false;
67061
+ for (const rings of polys) {
67062
+ const outer = rings[0];
67063
+ if (!outer) continue;
67064
+ for (const pt of outer) {
67065
+ const x = pt[0];
67066
+ const y = pt[1];
67067
+ if (x < minLon) minLon = x;
67068
+ if (x > maxLon) maxLon = x;
67069
+ if (y < minLat) minLat = y;
67070
+ if (y > maxLat) maxLat = y;
67071
+ seen = true;
67072
+ }
67073
+ }
67074
+ return seen ? [minLon, minLat, maxLon, maxLat] : null;
67075
+ }
67076
+ function featurePlanarBbox(f) {
67077
+ let bbox = planarBboxCache.get(f);
67078
+ if (bbox === void 0) {
67079
+ bbox = planarGeometryBbox(f.geometry);
67080
+ planarBboxCache.set(f, bbox);
67081
+ }
67082
+ return bbox;
67083
+ }
67084
+ function bboxRejects(f, lon, lat) {
67085
+ const b = featurePlanarBbox(f);
67086
+ if (!b) return false;
67087
+ return lon < b[0] - EDGE_EPS || lon > b[2] + EDGE_EPS || lat < b[1] - EDGE_EPS || lat > b[3] + EDGE_EPS;
67088
+ }
66412
67089
  function regionAt(lonLat, countries, states) {
66413
67090
  const lon = lonLat[0];
66414
67091
  const lat = lonLat[1];
66415
67092
  let country = null;
66416
67093
  for (const f of countries) {
67094
+ if (bboxRejects(f, lon, lat)) continue;
66417
67095
  if (pointInGeometry(f.geometry, lon, lat)) {
66418
67096
  country = { iso: f.id, name: f.properties.name };
66419
67097
  break;
@@ -66422,6 +67100,7 @@ function regionAt(lonLat, countries, states) {
66422
67100
  let state = null;
66423
67101
  if (country?.iso === "US" && states) {
66424
67102
  for (const f of states) {
67103
+ if (bboxRejects(f, lon, lat)) continue;
66425
67104
  if (pointInGeometry(f.geometry, lon, lat)) {
66426
67105
  state = { iso: f.id, name: f.properties.name };
66427
67106
  break;
@@ -66547,13 +67226,14 @@ function unionLongitudes(lons) {
66547
67226
  }
66548
67227
  return { west: pts[gapIdx], east: pts[gapIdx - 1] + 360 };
66549
67228
  }
66550
- var fold, adjacencyCache, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
67229
+ var fold, adjacencyCache, EDGE_EPS, planarBboxCache, DETACH_GAP_DEG, DETACH_AREA_FRAC;
66551
67230
  var init_geo = __esm({
66552
67231
  "src/map/geo.ts"() {
66553
67232
  "use strict";
66554
67233
  fold = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
66555
67234
  adjacencyCache = /* @__PURE__ */ new WeakMap();
66556
67235
  EDGE_EPS = 1e-9;
67236
+ planarBboxCache = /* @__PURE__ */ new WeakMap();
66557
67237
  DETACH_GAP_DEG = 10;
66558
67238
  DETACH_AREA_FRAC = 0.25;
66559
67239
  }
@@ -72298,12 +72978,23 @@ function depthTint(hue, depth, bg) {
72298
72978
  }
72299
72979
  function resolveColorMode(parsed, override) {
72300
72980
  let mode = override ?? parsed.defaultColorMode;
72981
+ if (mode === "heat" && !parsed.hasHeat) {
72982
+ mode = parsed.tagGroups.length > 0 ? "tag" : "branch";
72983
+ }
72301
72984
  if (mode === "tag" && parsed.tagGroups.length === 0) {
72302
72985
  mode = parsed.hasHeat ? "heat" : "branch";
72303
72986
  }
72304
- if (mode === "heat" && !parsed.hasHeat) mode = "branch";
72305
72987
  return mode;
72306
72988
  }
72989
+ function activeTagGroupOf(parsed) {
72990
+ if (parsed.tagGroups.length === 0) return null;
72991
+ const at = parsed.activeTag?.trim().toLowerCase();
72992
+ if (at) {
72993
+ const g = parsed.tagGroups.find((x) => x.name.toLowerCase() === at);
72994
+ if (g) return g;
72995
+ }
72996
+ return parsed.tagGroups[0];
72997
+ }
72307
72998
  function resolveCellColor(cell, ctx) {
72308
72999
  if (ctx.mode === "heat") {
72309
73000
  return cell.heat !== void 0 && ctx.heat ? ctx.heat.scale(cell.heat) : MUTED_FILL;
@@ -72391,7 +73082,7 @@ function buildLegend(activeMode, parsed, heat, seriesColors2, colorOffset) {
72391
73082
  const modeByName = /* @__PURE__ */ new Map();
72392
73083
  let activeGroup = null;
72393
73084
  if (parsed.tagGroups.length > 0) {
72394
- const tg = parsed.tagGroups[0];
73085
+ const tg = activeTagGroupOf(parsed);
72395
73086
  const used = /* @__PURE__ */ new Set();
72396
73087
  const collect = (nodes) => {
72397
73088
  for (const n of nodes) {
@@ -72490,7 +73181,7 @@ function renderTreemapRadial(container, parsed, palette, isDark, exportDims, opt
72490
73181
  const defs = svg.append("defs");
72491
73182
  svg.append("rect").attr("width", width).attr("height", height).attr("fill", palette.bg);
72492
73183
  const plot = svg.append("g").attr("class", "dgmo-treemap-cells").attr("transform", `translate(${cx},${cy})`);
72493
- const activeGroup = parsed.tagGroups.length > 0 ? parsed.tagGroups[0].name : null;
73184
+ const activeGroup = activeTagGroupOf(parsed)?.name ?? null;
72494
73185
  const rootIndexByLabel = new Map(parsed.roots.map((r, i) => [r.label, i]));
72495
73186
  const colorCtx = {
72496
73187
  mode,
@@ -72762,14 +73453,31 @@ function toDatum2(node) {
72762
73453
  children: node.children.map((c) => toDatum2(c))
72763
73454
  };
72764
73455
  }
72765
- function datumHeat2(node) {
73456
+ function collectHeatAggs(root) {
73457
+ const aggs = /* @__PURE__ */ new Map();
73458
+ root.eachBefore((node) => {
73459
+ if (node.children) return;
73460
+ const heat = node.data.heat;
73461
+ if (heat === void 0) return;
73462
+ let p = node;
73463
+ while (p) {
73464
+ const agg = aggs.get(p);
73465
+ if (agg) {
73466
+ agg.sum += heat;
73467
+ agg.count += 1;
73468
+ } else {
73469
+ aggs.set(p, { sum: heat, count: 1 });
73470
+ }
73471
+ p = p.parent;
73472
+ }
73473
+ });
73474
+ return aggs;
73475
+ }
73476
+ function datumHeat2(node, heatAggs) {
72766
73477
  if (node.data.heat !== void 0) return node.data.heat;
72767
- const xs = [];
72768
- for (const l of node.leaves()) {
72769
- if (l.data.heat !== void 0) xs.push(l.data.heat);
72770
- }
72771
- if (xs.length === 0) return void 0;
72772
- return xs.reduce((a, b) => a + b, 0) / xs.length;
73478
+ const agg = heatAggs.get(node);
73479
+ if (agg === void 0) return void 0;
73480
+ return agg.sum / agg.count;
72773
73481
  }
72774
73482
  function layoutTreemap(roots, opts) {
72775
73483
  const maxDepth = opts.maxDepth ?? Infinity;
@@ -72785,6 +73493,7 @@ function layoutTreemap(roots, opts) {
72785
73493
  d._topIndex = i;
72786
73494
  });
72787
73495
  });
73496
+ const heatAggs = collectHeatAggs(hroot);
72788
73497
  const cells = [];
72789
73498
  for (const d of hroot.descendants()) {
72790
73499
  if (d.depth === 0) continue;
@@ -72798,7 +73507,7 @@ function layoutTreemap(roots, opts) {
72798
73507
  path.unshift(p.data.label);
72799
73508
  p = p.parent;
72800
73509
  }
72801
- const heatVal = datumHeat2(d);
73510
+ const heatVal = datumHeat2(d, heatAggs);
72802
73511
  cells.push({
72803
73512
  node: d.data.ref ?? null,
72804
73513
  label: d.data.label,
@@ -72883,7 +73592,7 @@ function renderTreemap(container, parsed, palette, isDark, exportDims, options =
72883
73592
  const root = svg.append("g").attr("class", "dgmo-treemap-cells").attr("transform", `translate(${areaX},${areaY})`);
72884
73593
  const seriesColors2 = seriesColorsTop;
72885
73594
  const colorOffset = options.colorOffset ?? 0;
72886
- const activeGroup = parsed.tagGroups.length > 0 ? parsed.tagGroups[0].name : null;
73595
+ const activeGroup = activeTagGroupOf(parsed)?.name ?? null;
72887
73596
  const rootIndexByLabel = new Map(parsed.roots.map((r, i) => [r.label, i]));
72888
73597
  const colorCtx = {
72889
73598
  mode,
@@ -73824,8 +74533,10 @@ function bakedHero(parsed, now) {
73824
74533
  if (parsed.expired !== null) return parsed.expired;
73825
74534
  const elapsed = -remaining;
73826
74535
  if (elapsed <= 0) return "Now!";
73827
- if (parsed.units === "compound" || parsed.units === "human")
73828
- return `${formatCompound(resolved, now, 2, tz)} ago`;
74536
+ if (parsed.units === "compound" || parsed.units === "human") {
74537
+ const [a, b] = parsed.hasTime ? [resolved, now] : [dayStart(resolved, tz), dayStart(now, tz)];
74538
+ return `${formatCompound(a, b, 2, tz)} ago`;
74539
+ }
73829
74540
  const bu = parsed.units === "full" || parsed.units === "clock" ? "days" : parsed.units;
73830
74541
  return `${formatCount(elapsed, { units: bu, round: parsed.round, fields: parsed.fields })} ago`;
73831
74542
  }
@@ -73939,18 +74650,20 @@ function haloRect(svg, x, y, w, h, rx, C) {
73939
74650
  function pickBand(nowMs, resolvedMs, hasTime) {
73940
74651
  const days = dayDelta2(nowMs, resolvedMs);
73941
74652
  if (hasTime && days <= 0) return "clock";
73942
- if (days > 3 * 365) return "years";
73943
- if (days > 365) return "year";
73944
- if (days > 92) return "months";
73945
- if (days > 18) return "monthgrid";
73946
- if (days >= 1) return "weekstrip";
73947
74653
  if (days === 0) return "today";
73948
- return "after";
74654
+ const mag = Math.abs(days);
74655
+ if (mag > 3 * 365) return "years";
74656
+ if (mag > 365) return "year";
74657
+ if (mag > 92) return "months";
74658
+ if (mag > 7) return "monthgrid";
74659
+ return "weekstrip";
73949
74660
  }
73950
74661
  function monthSpan(nowMs, resolvedMs) {
73951
74662
  const n = new Date(nowMs);
73952
74663
  const t = new Date(resolvedMs);
73953
- return (t.getFullYear() - n.getFullYear()) * 12 + (t.getMonth() - n.getMonth()) + 1;
74664
+ return Math.abs(
74665
+ (t.getFullYear() - n.getFullYear()) * 12 + (t.getMonth() - n.getMonth())
74666
+ ) + 1;
73954
74667
  }
73955
74668
  function monthGridLayout(nowMs, resolvedMs, contentW) {
73956
74669
  const now = new Date(nowMs);
@@ -73960,21 +74673,24 @@ function monthGridLayout(nowMs, resolvedMs, contentW) {
73960
74673
  const padBefore = Math.floor((shown - realSpan) / 2);
73961
74674
  const nowMk = now.getFullYear() * 12 + now.getMonth();
73962
74675
  const evMk = ev.getFullYear() * 12 + ev.getMonth();
74676
+ const loMk = Math.min(nowMk, evMk);
74677
+ const hiMk = Math.max(nowMk, evMk);
74678
+ const startMk = loMk - padBefore;
73963
74679
  const months = [];
73964
74680
  const dim = [];
73965
74681
  for (let i = 0; i < shown; i++) {
73966
- const d = new Date(now.getFullYear(), now.getMonth() - padBefore + i, 1);
74682
+ const d = new Date(Math.floor(startMk / 12), startMk % 12 + i, 1);
73967
74683
  const mk = d.getFullYear() * 12 + d.getMonth();
73968
74684
  months.push(d);
73969
- dim.push(mk < nowMk || mk > evMk);
74685
+ dim.push(mk < loMk || mk > hiMk);
73970
74686
  }
73971
74687
  return { months, dim };
73972
74688
  }
73973
74689
  function yearsLayout(nowMs, resolvedMs) {
73974
74690
  const y0 = new Date(nowMs).getFullYear();
73975
74691
  const y1 = new Date(resolvedMs).getFullYear();
73976
- const d0 = Math.floor(y0 / 10) * 10;
73977
- const d1 = Math.floor(y1 / 10) * 10;
74692
+ const d0 = Math.floor(Math.min(y0, y1) / 10) * 10;
74693
+ const d1 = Math.floor(Math.max(y0, y1) / 10) * 10;
73978
74694
  return { d0, d1, rows: (d1 - d0) / 10 + 1 };
73979
74695
  }
73980
74696
  function bandHeightFor(kind, nowMs, resolvedMs, contentW) {
@@ -73996,7 +74712,9 @@ function bandHeightFor(kind, nowMs, resolvedMs, contentW) {
73996
74712
  return total;
73997
74713
  }
73998
74714
  case "year": {
73999
- const R = new Date(resolvedMs).getFullYear() - new Date(nowMs).getFullYear() + 1;
74715
+ const R = Math.abs(
74716
+ new Date(resolvedMs).getFullYear() - new Date(nowMs).getFullYear()
74717
+ ) + 1;
74000
74718
  const yearW = 66;
74001
74719
  const gap = 8;
74002
74720
  const cellW = (contentW - yearW - 11 * gap) / 12;
@@ -74022,14 +74740,13 @@ function bandHeightFor(kind, nowMs, resolvedMs, contentW) {
74022
74740
  return 26 + 6 * cellH + 8;
74023
74741
  }
74024
74742
  case "weekstrip": {
74025
- const n = Math.max(7, dayDelta2(nowMs, resolvedMs) + 1);
74743
+ const n = Math.max(7, Math.abs(dayDelta2(nowMs, resolvedMs)) + 1);
74026
74744
  const cw = (contentW - 10 * (n - 1)) / n;
74027
74745
  return clamp6(cw * 1.5, 116, 170);
74028
74746
  }
74029
74747
  case "clock":
74030
74748
  return clamp6(contentW * 0.26, 150, 205);
74031
74749
  case "today":
74032
- case "after":
74033
74750
  return clamp6(contentW * 0.22, 125, 170);
74034
74751
  }
74035
74752
  }
@@ -74067,7 +74784,7 @@ function vizYears(svg, g, nowMs, resolvedMs, C) {
74067
74784
  const before = yr < nowY;
74068
74785
  const isNow = yr === nowY;
74069
74786
  const isEv = yr === evY;
74070
- const between = yr > nowY && yr < evY;
74787
+ const between = (yr - nowY) * (yr - evY) < 0;
74071
74788
  const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : between ? gradStyle(C, (yr - nowY) / (evY - nowY)) : roleStyle(C, before ? "past" : "future");
74072
74789
  const x = g.left + labW + i * (cellW + gap);
74073
74790
  aRect(svg, x, ry, cellW, cellH, rx, st.fill, st.stroke, 1);
@@ -74091,8 +74808,8 @@ function vizYears(svg, g, nowMs, resolvedMs, C) {
74091
74808
  function vizYear(svg, g, nowMs, resolvedMs, C) {
74092
74809
  const now = new Date(nowMs);
74093
74810
  const ev = new Date(resolvedMs);
74094
- const y0 = now.getFullYear();
74095
- const y1 = ev.getFullYear();
74811
+ const y0 = Math.min(now.getFullYear(), ev.getFullYear());
74812
+ const y1 = Math.max(now.getFullYear(), ev.getFullYear());
74096
74813
  const years = [];
74097
74814
  for (let y = y0; y <= y1; y++) years.push(y);
74098
74815
  const R = years.length;
@@ -74122,10 +74839,11 @@ function vizYear(svg, g, nowMs, resolvedMs, C) {
74122
74839
  { "dominant-baseline": "central" }
74123
74840
  );
74124
74841
  for (let m = 0; m < 12; m++) {
74125
- const before = yr < nowY || yr === nowY && m < nowM;
74842
+ const mOrd = yr * 12 + m;
74843
+ const before = mOrd < nowOrd;
74126
74844
  const isNow = yr === nowY && m === nowM;
74127
74845
  const isEv = yr === evY && m === evM;
74128
- const between = (yr > nowY || yr === nowY && m > nowM) && (yr < evY || yr === evY && m < evM);
74846
+ const between = (mOrd - nowOrd) * (mOrd - evOrd) < 0;
74129
74847
  const anchor = isEv || isNow;
74130
74848
  const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : between ? gradStyle(C, (yr * 12 + m - nowOrd) / (evOrd - nowOrd)) : roleStyle(C, before ? "past" : "future");
74131
74849
  const x = g.left + yearW + m * (cellW + gap);
@@ -74189,19 +74907,22 @@ function vizMonths(svg, g, nowMs, resolvedMs, C) {
74189
74907
  const cellH = (g.height - rowGap * (rows - 1)) / rows;
74190
74908
  const nowMk = now.getFullYear() * 12 + now.getMonth();
74191
74909
  const evMk = ev.getFullYear() * 12 + ev.getMonth();
74910
+ const loMk = Math.min(nowMk, evMk);
74911
+ const hiMk = Math.max(nowMk, evMk);
74912
+ const startMk = loMk - padBefore;
74192
74913
  for (let i = 0; i < shown; i++) {
74193
- const d = new Date(now.getFullYear(), now.getMonth() - padBefore + i, 1);
74914
+ const d = new Date(Math.floor(startMk / 12), startMk % 12 + i, 1);
74194
74915
  const mk = d.getFullYear() * 12 + d.getMonth();
74195
74916
  const isNow = mk === nowMk;
74196
74917
  const isEv = mk === evMk;
74197
- const isContext = mk < nowMk || mk > evMk;
74918
+ const isContext = mk < loMk || mk > hiMk;
74198
74919
  const anchor = isNow || isEv;
74199
74920
  const col = i % perRow;
74200
74921
  const row = Math.floor(i / perRow);
74201
74922
  const x = g.left + col * (cellW + gap);
74202
74923
  const y = g.top + row * (cellH + rowGap);
74203
74924
  const mt = isContext ? svg.append("g").attr("opacity", 0.42) : svg;
74204
- const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : mk > nowMk && mk < evMk ? gradStyle(C, (mk - nowMk) / (evMk - nowMk)) : roleStyle(C, mk < nowMk ? "past" : "future");
74925
+ const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : (mk - nowMk) * (mk - evMk) < 0 ? gradStyle(C, (mk - nowMk) / (evMk - nowMk)) : roleStyle(C, mk < nowMk ? "past" : "future");
74205
74926
  const rxM = Math.min(12, cellH * 0.14);
74206
74927
  aRect(mt, x, y, cellW, cellH, rxM, st.fill, st.stroke, 1.25);
74207
74928
  if (isEv) haloRect(mt, x, y, cellW, cellH, rxM, C);
@@ -74300,7 +75021,7 @@ function vizMonthGrid(svg, g, nowMs, resolvedMs, C) {
74300
75021
  const cy = gy + wk * cellH;
74301
75022
  const isEvent = dk === evKey;
74302
75023
  const isToday = dk === todayKey;
74303
- const between = dk > todayKey && dk < evKey;
75024
+ const between = (dk - todayKey) * (dk - evKey) < 0;
74304
75025
  const anchor = isEvent || isToday;
74305
75026
  const dtMs = new Date(m.getFullYear(), m.getMonth(), day).getTime();
74306
75027
  const st = isEvent ? roleStyle(C, "event") : isToday ? roleStyle(C, "today") : between ? gradStyle(C, dayDelta2(nowMs, dtMs) / dayDelta2(nowMs, resolvedMs)) : roleStyle(C, dk < todayKey ? "past" : "future");
@@ -74339,8 +75060,7 @@ function vizMonthGrid(svg, g, nowMs, resolvedMs, C) {
74339
75060
  });
74340
75061
  }
74341
75062
  function vizWeekStrip(svg, g, nowMs, resolvedMs, C) {
74342
- const now = new Date(nowMs);
74343
- const nReal = dayDelta2(nowMs, resolvedMs) + 1;
75063
+ const nReal = Math.abs(dayDelta2(nowMs, resolvedMs)) + 1;
74344
75064
  const n = Math.max(7, nReal);
74345
75065
  const padBefore = Math.floor((n - nReal) / 2);
74346
75066
  const gap = n <= 7 ? 14 : n <= 12 ? 10 : 7;
@@ -74351,17 +75071,20 @@ function vizWeekStrip(svg, g, nowMs, resolvedMs, C) {
74351
75071
  const showTag = cw >= 44;
74352
75072
  const nowOrd = DAY_START(nowMs);
74353
75073
  const evOrd = DAY_START(resolvedMs);
75074
+ const loOrd = Math.min(nowOrd, evOrd);
75075
+ const hiOrd = Math.max(nowOrd, evOrd);
75076
+ const first = new Date(Math.min(nowMs, resolvedMs));
74354
75077
  for (let i = 0; i < n; i++) {
74355
75078
  const dt = new Date(
74356
- now.getFullYear(),
74357
- now.getMonth(),
74358
- now.getDate() + i - padBefore
75079
+ first.getFullYear(),
75080
+ first.getMonth(),
75081
+ first.getDate() + i - padBefore
74359
75082
  );
74360
75083
  const dtOrd = DAY_START(dt.getTime());
74361
75084
  const x = g.left + i * (cw + gap);
74362
75085
  const isToday = dtOrd === nowOrd;
74363
75086
  const isEvent = dtOrd === evOrd;
74364
- const isContext = dtOrd < nowOrd || dtOrd > evOrd;
75087
+ const isContext = dtOrd < loOrd || dtOrd > hiOrd;
74365
75088
  const st = isEvent ? roleStyle(C, "event") : isToday ? roleStyle(C, "today") : isContext ? roleStyle(C, dtOrd < nowOrd ? "past" : "future") : gradStyle(C, (dtOrd - nowOrd) / (evOrd - nowOrd));
74366
75089
  const ct = isContext ? svg.append("g").attr("opacity", 0.5) : svg;
74367
75090
  const sw = isToday || isEvent ? 2 : 1.25;
@@ -74464,75 +75187,6 @@ function vizToday(svg, g, resolvedMs, C) {
74464
75187
  { "letter-spacing": "0.12em" }
74465
75188
  );
74466
75189
  }
74467
- function vizAfter(svg, g, nowMs, resolvedMs, C) {
74468
- const ev = new Date(resolvedMs);
74469
- const elapsed = dayDelta2(resolvedMs, nowMs);
74470
- const n = elapsed + 1;
74471
- const gap = 10;
74472
- const cw = (g.contentW - gap * (n - 1)) / n;
74473
- const ch = Math.min(cw * 1.1, g.height * 0.62);
74474
- const top = g.top + (g.height - ch) / 2 - 8;
74475
- for (let i = 0; i < n; i++) {
74476
- const dt = new Date(ev);
74477
- dt.setDate(ev.getDate() + i);
74478
- const x = g.left + i * (cw + gap);
74479
- const isEvent = i === 0;
74480
- const isToday = i === n - 1;
74481
- const st = roleStyle(C, isEvent ? "event" : "past");
74482
- const fill2 = st.fill;
74483
- const stroke2 = st.stroke;
74484
- const tcol = st.text;
74485
- const sw = isEvent ? 2.5 : 1;
74486
- aRect(svg, x, top, cw, ch, 12, fill2, stroke2, sw);
74487
- if (isEvent) haloRect(svg, x, top, cw, ch, 12, C);
74488
- aText(
74489
- svg,
74490
- x + cw / 2,
74491
- top + ch * 0.46,
74492
- Math.min(34, cw * 0.34),
74493
- tcol,
74494
- isEvent ? 750 : 500,
74495
- "middle",
74496
- String(dt.getDate())
74497
- );
74498
- aText(
74499
- svg,
74500
- x + cw / 2,
74501
- top + ch * 0.72,
74502
- 11,
74503
- tcol,
74504
- 600,
74505
- "middle",
74506
- MON_ABBR[dt.getMonth()]
74507
- );
74508
- if (isEvent)
74509
- aText(
74510
- svg,
74511
- x + cw / 2,
74512
- top - 8,
74513
- 11,
74514
- C.event,
74515
- 700,
74516
- "middle",
74517
- "IT HAPPENED",
74518
- { "letter-spacing": "0.05em" }
74519
- );
74520
- if (isToday)
74521
- aText(svg, x + cw / 2, top - 8, 11, C.now, 700, "middle", "TODAY", {
74522
- "letter-spacing": "0.05em"
74523
- });
74524
- }
74525
- aText(
74526
- svg,
74527
- g.left,
74528
- top + ch + 28,
74529
- 14,
74530
- C.muted,
74531
- 400,
74532
- "start",
74533
- `${elapsed} day${elapsed === 1 ? "" : "s"} ago \u2014 the day has passed`
74534
- );
74535
- }
74536
75190
  function polar(cx, cy, r, deg) {
74537
75191
  const a = (deg - 90) * Math.PI / 180;
74538
75192
  return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
@@ -74619,8 +75273,6 @@ function drawBand2(svg, kind, g, nowMs, resolvedMs, C) {
74619
75273
  return vizWeekStrip(svg, g, nowMs, resolvedMs, C);
74620
75274
  case "today":
74621
75275
  return vizToday(svg, g, resolvedMs, C);
74622
- case "after":
74623
- return vizAfter(svg, g, nowMs, resolvedMs, C);
74624
75276
  case "clock":
74625
75277
  return vizClock(svg, g, nowMs, resolvedMs, C);
74626
75278
  }
@@ -76443,7 +77095,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
76443
77095
  const opts = typeof handlers === "function" ? { onClickLine: handlers } : handlers ?? {};
76444
77096
  const onClickLine = opts.onClickLine;
76445
77097
  const onMarkerDragStart = opts.onMarkerDragStart;
76446
- const hideLegend = opts.hideLegend ?? false;
77098
+ const hideLegend = (opts.hideLegend ?? false) || legendSuppressed(parsed.options);
76447
77099
  const collapsedPhases = opts.collapsedPhases ?? /* @__PURE__ */ new Set();
76448
77100
  const hideTitle = (opts.hideTitle ?? false) || parsed.options["no-title"] === "on";
76449
77101
  d3Selection38.select(container).selectAll(":not([data-d3-tooltip])").remove();
@@ -78092,7 +78744,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
78092
78744
  position: { placement: "top-center", titleRelation: "below-title" },
78093
78745
  mode: "preview"
78094
78746
  };
78095
- const legendTopSpace = parsed.tagGroups.length > 0 ? getMaxLegendReservedHeight(legendConfig, containerWidth) + LEGEND_FIXED_GAP4 : 0;
78747
+ const noLegend = legendSuppressed(parsedOptions);
78748
+ const legendTopSpace = parsed.tagGroups.length > 0 && !noLegend ? getMaxLegendReservedHeight(legendConfig, containerWidth) + LEGEND_FIXED_GAP4 : 0;
78096
78749
  const groupOffset = parsed.groups.length > 0 ? GROUP_PADDING_TOP2 + GROUP_LABEL_SIZE : 0;
78097
78750
  const participantStartY = sTopMargin + titleOffset + legendTopSpace + sParticipantYOffset + groupOffset;
78098
78751
  const lifelineStartY0 = participantStartY + sBoxH;
@@ -78364,7 +79017,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
78364
79017
  titleEl.attr("data-line-number", parsed.titleLineNumber);
78365
79018
  }
78366
79019
  }
78367
- const hasTagGroups = parsed.tagGroups.length > 0;
79020
+ const hasTagGroups = parsed.tagGroups.length > 0 && !noLegend;
78368
79021
  const collapsedGroupNames = /* @__PURE__ */ new Set();
78369
79022
  const collapsedGroupMeta = /* @__PURE__ */ new Map();
78370
79023
  for (const group of parsed.groups) {
@@ -78954,6 +79607,7 @@ var init_renderer33 = __esm({
78954
79607
  init_tag_resolution();
78955
79608
  init_tag_groups();
78956
79609
  init_legend_layout();
79610
+ init_parsing();
78957
79611
  init_legend_integration();
78958
79612
  init_title_constants();
78959
79613
  init_scaling();
@@ -81459,8 +82113,13 @@ async function exportSitemap(ctx) {
81459
82113
  );
81460
82114
  const PADDING7 = 20;
81461
82115
  const titleOffset = effectiveParsed.title ? 30 : 0;
82116
+ const {
82117
+ LEGEND_HEIGHT: SITEMAP_LEGEND_HEIGHT,
82118
+ LEGEND_GROUP_GAP: SITEMAP_LEGEND_GROUP_GAP
82119
+ } = await Promise.resolve().then(() => (init_legend_constants(), legend_constants_exports));
82120
+ const suppressedLegendBand = sitemapLayout.legend.length > 0 && effectiveParsed.options["no-legend"] === "on" ? SITEMAP_LEGEND_HEIGHT + SITEMAP_LEGEND_GROUP_GAP : 0;
81462
82121
  const exportWidth = sitemapLayout.width + PADDING7 * 2;
81463
- const exportHeight = sitemapLayout.height + PADDING7 * 2 + titleOffset;
82122
+ const exportHeight = sitemapLayout.height + PADDING7 * 2 + titleOffset - suppressedLegendBand;
81464
82123
  const container = createExportContainer(exportWidth, exportHeight);
81465
82124
  renderSitemap2(
81466
82125
  container,
@@ -81872,7 +82531,7 @@ async function exportInfra(ctx) {
81872
82531
  infraTagGroups,
81873
82532
  effectivePalette
81874
82533
  );
81875
- const legendOffset = legendGroups.length > 0 ? 28 : 0;
82534
+ const legendOffset = legendGroups.length > 0 && infraParsed.options["no-legend"] !== "on" ? 28 : 0;
81876
82535
  const exportWidth = infraLayout.width;
81877
82536
  const exportHeight = infraLayout.height + titleOffset + legendOffset;
81878
82537
  const container = createExportContainer(exportWidth, exportHeight);
@@ -82101,6 +82760,7 @@ async function exportMap(ctx) {
82101
82760
  }
82102
82761
  }
82103
82762
  const mapResolved = resolveMap2(mapParsed, mapData);
82763
+ options?.onMapResolverDiagnostics?.(mapResolved.diagnostics);
82104
82764
  const dims = mapExportDimensions2(
82105
82765
  mapResolved,
82106
82766
  mapData,
@@ -82987,6 +83647,7 @@ async function render(content, options) {
82987
83647
  releaseDom();
82988
83648
  }
82989
83649
  }
83650
+ const mapDiag = { current: null };
82990
83651
  await acquireDom();
82991
83652
  let svg;
82992
83653
  try {
@@ -83002,26 +83663,18 @@ async function render(content, options) {
83002
83663
  c4Container: options.c4Container
83003
83664
  },
83004
83665
  ...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup },
83005
- ...options?.mapData !== void 0 && { mapData: options.mapData }
83666
+ ...options?.mapData !== void 0 && { mapData: options.mapData },
83667
+ onMapResolverDiagnostics: (d) => {
83668
+ mapDiag.current = d;
83669
+ }
83006
83670
  }
83007
83671
  );
83008
83672
  svg = injectHoverStyles(svg, chartType, { bakeHover });
83009
83673
  } finally {
83010
83674
  releaseDom();
83011
83675
  }
83012
- if (chartType === "map") {
83013
- try {
83014
- const [{ parseMap: parseMap2 }, { resolveMap: resolveMap2 }, { loadMapData: loadMapData2 }] = await Promise.all(
83015
- [
83016
- Promise.resolve().then(() => (init_parser19(), parser_exports13)),
83017
- Promise.resolve().then(() => (init_resolver2(), resolver_exports)),
83018
- Promise.resolve().then(() => (init_load_data(), load_data_exports))
83019
- ]
83020
- );
83021
- const data = options?.mapData ?? await loadMapData2();
83022
- diagnostics = [...resolveMap2(parseMap2(content), data).diagnostics];
83023
- } catch {
83024
- }
83676
+ if (chartType === "map" && mapDiag.current) {
83677
+ diagnostics = [...mapDiag.current];
83025
83678
  }
83026
83679
  return { svg, diagnostics, chartType: chartType ?? void 0 };
83027
83680
  }
@@ -83167,9 +83820,10 @@ function updateNode(node, now) {
83167
83820
  });
83168
83821
  text = remaining < 0 ? `${clock} ago` : clock;
83169
83822
  } else if (expiredNow) {
83170
- if (units === "compound" || units === "human")
83171
- text = `${formatCompound(resolvedMs, now, 2, tz)} ago`;
83172
- else {
83823
+ if (units === "compound" || units === "human") {
83824
+ const [a, b] = hasTime ? [resolvedMs, now] : [dayStart(resolvedMs, tz), dayStart(now, tz)];
83825
+ text = `${formatCompound(a, b, 2, tz)} ago`;
83826
+ } else {
83173
83827
  const elapsed = -remaining;
83174
83828
  text = elapsed <= 0 ? "Now!" : `${formatCount(elapsed, { units, round, fields })} ago`;
83175
83829
  }
@@ -83482,7 +84136,7 @@ var parser = LRParser.deserialize({
83482
84136
  maxTerm: 40,
83483
84137
  skippedNodes: [0],
83484
84138
  repeatNodeCount: 2,
83485
- tokenData: ">l~RzOX#uXY#zYZ$VZp#upq#zqr#urs$[st#uux#uxy%eyz%lz{%s{|%z|}&R}!O&Y!O!P#u!P!Q&i!Q!['Y![!]1q!]!^#u!^!_1x!_!`2P!`!a2^!a!b2e!b!c#u!c!}2l!}#O5r#O#P#u#P#Q5w#Q#R#u#R#S2l#S#T#u#T#[2l#[#]6O#]#o2l#o#p#u#p#q>O#q#r#u#r#s>V#s;'S#u;'S;=`>f<%lO#u~#zOq~~$PQv~XY#zpq#z~$[Ow~~$aUq~OY$sZr$srs%Ys;'S$s;'S;=`%_<%lO$s~$vUOY$sZr$srs%Ys;'S$s;'S;=`%_<%lO$s~%_Oo~~%bP;=`<%l$s~%lOc~q~~%sOd~q~~%zOm~q~~&ROj~q~~&YOi~q~~&aPk~q~!`!a&d~&iOX~~&nPq~!P!Q&q~&vSV~OY&qZ;'S&q;'S;=`'S<%lO&q~'VP;=`<%l&q~'a_^~q~uv(`|}(e}!O)b!O!P*l!P!Q,]!Q![,v#R#S1]#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h+m#k#l+m#m#n+m~(eO]~~(hP!Q![(k~(nP!Q![(q~(tP!Q![(w~(|Q^~|}(e!O!P)S~)VP!Q![)Y~)_P^~!Q![)Y~)eP!Q![)h~)mQ[~}!O)s!Q![*d~)vP!Q![)y~)|P!Q![*P~*UP[~!Q![*X~*[P!Q![*_~*dO[~~*iP[~}!O)s~*oP!Q![*r~*wY^~uv(`!Q![*r#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h+m#k#l+m#m#n+m~+jP#W#X+m~+rPZ~!a!b+u~+zOZ~~,PQZ~!a!b+u#]#^,V~,YP#b#c+m~,`P!Q![,c~,hQ[~!P!Q)s!Q![,n~,sP[~!P!Q)s~,{_^~uv(`|}(e}!O)b!O!P*l!P!Q,]!Q![-z#R#S1]#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h+m#k#l+m#m#n+m~.P]^~uv(`|}(e!O!P*l!Q![.x#R#S1]#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h+m#k#l+m#m#n+m~.}]^~uv(`}!O/v!O!P*l!Q![0b#R#S1]#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h+m#k#l+m#m#n+m~/yP!Q![/|~0PP!Q![0S~0XP[~}!O0[~0_P!Q![*X~0g[^~uv(`!O!P*l!Q![0b#R#S1]#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h+m#k#l+m#m#n+m~1`P!Q![1c~1hR^~!O!P)S!Q![1c#R#S1]~1xOh~q~~2POe~q~~2UPq~!_!`2X~2^O_~~2eOf~q~~2lOn~q~~2s_p~q~qr3rst3rvw3rwx3r{|3r}!O4v!O!P3r!P!Q3r!Q![3r!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#o3r~3w_p~qr3rst3rvw3rwx3r{|3r}!O4v!O!P3r!P!Q3r!Q![3r!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#o3r~4y]qr3rst3rvw3rwx3r{|3r!O!P3r!P!Q3r!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#o3r~5wOa~~6OOb~q~~6Vap~q~qr3rst3rvw3rwx3r{|3r}!O4v!O!P3r!P!Q3r!Q![3r!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#h3r#h#i7[#i#o3r~7aap~qr3rst3rvw3rwx3r{|3r}!O4v!O!P3r!P!Q3r!Q![3r!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#h3r#h#i8f#i#o3r~8kap~qr3rst3rvw3rwx3r{|3r}!O4v!O!P3r!P!Q3r!Q![3r!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#d3r#d#e9p#e#o3r~9ubp~qr3rst3rvw3rwx3r{|3r}!O4v!O!P3r!P!Q3r!Q![3r![!]:}!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#g3r#g#h<w#h#o3r~;QP!P!Q;T~;WP!P!Q;Z~;^YOX;|Zp;|qy;|z|;|}!`;|!a#P;|#Q#p;|#q;'S;|;'S;=`<q<%lO;|~<RY`~OX;|Zp;|qy;|z|;|}!`;|!a#P;|#Q#p;|#q;'S;|;'S;=`<q<%lO;|~<tP;=`<%l;|~<|`p~qr3rst3rvw3rwx3r{|3r}!O4v!O!P3r!P!Q3r!Q![3r![!]:}!_!`3r!a!b3r!b!c3r!c!}3r#R#S3r#T#o3r~>VOg~q~~>^Pl~q~!`!a>a~>fOY~~>iP;=`<%l#u",
84139
+ tokenData: ">w~RzOX#uXY#zYZ$VZp#upq#zqr#urs$[st#uux#uxy%eyz%lz{%s{|%z|}&R}!O&Y!O!P#u!P!Q&i!Q!['Y![!]1|!]!^#u!^!_2T!_!`2[!`!a2i!a!b2p!b!c#u!c!}2w!}#O5}#O#P#u#P#Q6S#Q#R#u#R#S2w#S#T#u#T#[2w#[#]6Z#]#o2w#o#p#u#p#q>Z#q#r#u#r#s>b#s;'S#u;'S;=`>q<%lO#u~#zOq~~$PQv~XY#zpq#z~$[Ow~~$aUq~OY$sZr$srs%Ys;'S$s;'S;=`%_<%lO$s~$vUOY$sZr$srs%Ys;'S$s;'S;=`%_<%lO$s~%_Oo~~%bP;=`<%l$s~%lOc~q~~%sOd~q~~%zOm~q~~&ROj~q~~&YOi~q~~&aPk~q~!`!a&d~&iOX~~&nPq~!P!Q&q~&vSV~OY&qZ;'S&q;'S;=`'S<%lO&q~'VP;=`<%l&q~'a_^~q~uv(`|}(e}!O)b!O!P*l!P!Q,h!Q![-R#R#S1h#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h,]#k#l+m#m#n+m~(eO]~~(hP!Q![(k~(nP!Q![(q~(tP!Q![(w~(|Q^~|}(e!O!P)S~)VP!Q![)Y~)_P^~!Q![)Y~)eP!Q![)h~)mQ[~}!O)s!Q![*d~)vP!Q![)y~)|P!Q![*P~*UP[~!Q![*X~*[P!Q![*_~*dO[~~*iP[~}!O)s~*oP!Q![*r~*wY^~uv(`!Q![*r#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h,]#k#l+m#m#n+m~+jP#W#X+m~+rPZ~!a!b+u~+zOZ~~,PQZ~!a!b+u#]#^,V~,YP#b#c+m~,bQZ~!a!b+u#d#e+m~,kP!Q![,n~,sQ[~!P!Q)s!Q![,y~-OP[~!P!Q)s~-W_^~uv(`|}(e}!O)b!O!P*l!P!Q,h!Q![.V#R#S1h#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h,]#k#l+m#m#n+m~.[]^~uv(`|}(e!O!P*l!Q![/T#R#S1h#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h,]#k#l+m#m#n+m~/Y]^~uv(`}!O0R!O!P*l!Q![0m#R#S1h#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h,]#k#l+m#m#n+m~0UP!Q![0X~0[P!Q![0_~0dP[~}!O0g~0jP!Q![*X~0r[^~uv(`!O!P*l!Q![0m#R#S1h#U#V+g#W#X+m#[#]+m#a#b+z#e#f+m#g#h,]#k#l+m#m#n+m~1kP!Q![1n~1sR^~!O!P)S!Q![1n#R#S1h~2TOh~q~~2[Oe~q~~2aPq~!_!`2d~2iO_~~2pOf~q~~2wOn~q~~3O_p~q~qr3}st3}vw3}wx3}{|3}}!O5R!O!P3}!P!Q3}!Q![3}!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#o3}~4S_p~qr3}st3}vw3}wx3}{|3}}!O5R!O!P3}!P!Q3}!Q![3}!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#o3}~5U]qr3}st3}vw3}wx3}{|3}!O!P3}!P!Q3}!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#o3}~6SOa~~6ZOb~q~~6bap~q~qr3}st3}vw3}wx3}{|3}}!O5R!O!P3}!P!Q3}!Q![3}!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#h3}#h#i7g#i#o3}~7lap~qr3}st3}vw3}wx3}{|3}}!O5R!O!P3}!P!Q3}!Q![3}!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#h3}#h#i8q#i#o3}~8vap~qr3}st3}vw3}wx3}{|3}}!O5R!O!P3}!P!Q3}!Q![3}!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#d3}#d#e9{#e#o3}~:Qbp~qr3}st3}vw3}wx3}{|3}}!O5R!O!P3}!P!Q3}!Q![3}![!];Y!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#g3}#g#h=S#h#o3}~;]P!P!Q;`~;cP!P!Q;f~;iYOX<XZp<Xqy<Xz|<X}!`<X!a#P<X#Q#p<X#q;'S<X;'S;=`<|<%lO<X~<^Y`~OX<XZp<Xqy<Xz|<X}!`<X!a#P<X#Q#p<X#q;'S<X;'S;=`<|<%lO<X~=PP;=`<%l<X~=X`p~qr3}st3}vw3}wx3}{|3}}!O5R!O!P3}!P!Q3}!Q![3}![!];Y!_!`3}!a!b3}!b!c3}!c!}3}#R#S3}#T#o3}~>bOg~q~~>iPl~q~!`!a>l~>qOY~~>tP;=`<%l#u",
83486
84140
  tokenizers: [0],
83487
84141
  topRules: { "Document": [0, 6] },
83488
84142
  specialized: [{ term: 32, get: (value, stack) => specializeKeyword(value, stack) << 1, external: specializeKeyword }],
@@ -83621,7 +84275,10 @@ var ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
83621
84275
  "pain",
83622
84276
  "opportunity",
83623
84277
  "thought",
83624
- "collapsed",
84278
+ // `collapsed` moved to the registry as a bare MODIFIER keyword (decision
84279
+ // #48 made the bare flag canonical). It must NOT be listed here too: the
84280
+ // ATTRIBUTE_KEYS colon gate only fires on `default` tokens, so a bare
84281
+ // keyword classification wins and a duplicate entry would be dead weight.
83625
84282
  "tech",
83626
84283
  "span",
83627
84284
  "split",
@@ -83962,17 +84619,25 @@ var BLOCK_CSS = `
83962
84619
 
83963
84620
  /* === Toolbar ===
83964
84621
  * The diagram is the star: the toolbar is an overlay pinned to the diagram's
83965
- * top-right corner (position:absolute \u2014 it reserves no layout space, so the
83966
- * block is exactly as tall as the chart). It stays fully transparent until
83967
- * the block is hovered or focused, or the source panel is open. It is the
83968
- * <summary> of the source <details>, so toggling works without any JS.
83969
- * No text labels, no disclosure triangle \u2014 three wordless icon buttons. */
84622
+ * bottom-right corner (position:absolute \u2014 it reserves no layout space, so the
84623
+ * block is exactly as tall as the chart). Bottom-right (not top-right) so it
84624
+ * never collides with a host's own top-right chart chrome \u2014 e.g. Obsidian's
84625
+ * code-block copy button. It stays fully transparent until the block is
84626
+ * hovered or focused, or the source panel is open. It is the <summary> of the
84627
+ * source <details>, so toggling works without any JS. No text labels, no
84628
+ * disclosure triangle \u2014 three wordless icon buttons.
84629
+ *
84630
+ * Anchored to .dgmo-source-wrap (position:relative below), whose top edge is
84631
+ * always the diagram's bottom edge \u2014 bottom:calc(100% + gap) therefore keeps
84632
+ * the toolbar at the diagram's bottom-right whether the source panel is open or
84633
+ * closed (a figure-anchored bottom would drift down below the open code). */
83970
84634
  .dgmo-source-wrap {
83971
84635
  margin: 0;
84636
+ position: relative;
83972
84637
  }
83973
84638
  .dgmo-toolbar {
83974
84639
  position: absolute;
83975
- top: 0.4em;
84640
+ bottom: calc(100% + 0.4em);
83976
84641
  right: 0.4em;
83977
84642
  z-index: 2;
83978
84643
  display: flex;
@@ -84431,7 +85096,7 @@ var CSS = AUTO_BASE_CSS + "\n" + BLOCK_CSS + "\n" + darkScopedBlockCss(BLOCK_CSS
84431
85096
 
84432
85097
  // src/auto/shared.ts
84433
85098
  init_safe_href();
84434
- var VERSION = "0.52.0";
85099
+ var VERSION = "0.53.0";
84435
85100
  var ARIA_LABEL_MAX = 200;
84436
85101
  var COPIED_INTERACTION_MS = 1500;
84437
85102
  var LOG_PREFIX = "[dgmo]";