@diagrammo/dgmo 0.54.0 → 0.55.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 (196) hide show
  1. package/dist/advanced.d.ts +76 -9
  2. package/dist/advanced.js +118 -95
  3. package/dist/auto.js +125 -124
  4. package/dist/auto.mjs +1579 -282
  5. package/dist/block.js +49 -48
  6. package/dist/chart-meta.js +43 -43
  7. package/dist/{charts-d3-25E4VWXO.js → charts-d3-KZ6V4OP2.js} +7 -6
  8. package/dist/{chunk-MTZOXISJ.js → chunk-2HX6ZGY7.js} +3 -3
  9. package/dist/{chunk-ICKXIMBA.js → chunk-2R6HG6D5.js} +5 -5
  10. package/dist/{chunk-NUTWTV6G.js → chunk-2ZJXKJSI.js} +102 -56
  11. package/dist/{chunk-LPJ6H5X4.js → chunk-32HUNA33.js} +53 -11
  12. package/dist/{chunk-UIT7E6UF.js → chunk-3BFXELBL.js} +51 -4
  13. package/dist/{chunk-C2UORMIA.js → chunk-3BZJ5KD5.js} +7 -3
  14. package/dist/{chunk-A3BDLDYG.js → chunk-3FUTFYSP.js} +57 -18
  15. package/dist/{chunk-DPGQ5UMO.js → chunk-4MZCJV4M.js} +6 -6
  16. package/dist/{chunk-WWHECSYZ.js → chunk-4R5KIPNX.js} +2 -2
  17. package/dist/{chunk-47JNEKJA.js → chunk-5D5VHDJM.js} +43 -8
  18. package/dist/{chunk-KQYQECWQ.js → chunk-5H5GWWA4.js} +49 -13
  19. package/dist/{chunk-RRGKMJYL.js → chunk-5K5XBEXT.js} +8 -3
  20. package/dist/{chunk-HAA25PLC.js → chunk-6NJWBMJV.js} +44 -9
  21. package/dist/{chunk-3FRO35WW.js → chunk-A2ARDILY.js} +17 -3
  22. package/dist/{chunk-TLNVYWPA.js → chunk-A2PTRSAO.js} +2 -2
  23. package/dist/{chunk-APKUAJ2L.js → chunk-A2RUBBVT.js} +14 -1
  24. package/dist/{chunk-KS5ZSM5T.js → chunk-A4UJP4IS.js} +61 -13
  25. package/dist/{chunk-LRSW4JLE.js → chunk-AKE6C2D7.js} +3 -3
  26. package/dist/{chunk-2NZOHEP4.js → chunk-ASPS5O6F.js} +3 -3
  27. package/dist/{chunk-KV4HLIJE.js → chunk-AU75JHSR.js} +6 -3
  28. package/dist/{chunk-R5BGGBFF.js → chunk-DS4MDEQ7.js} +2 -2
  29. package/dist/{chunk-7JDTNFAK.js → chunk-EFUQENO5.js} +76 -32
  30. package/dist/{chunk-BULHGPHK.js → chunk-EMSOFHEN.js} +2 -2
  31. package/dist/{chunk-IYNKLCAZ.js → chunk-ETTDF3S2.js} +45 -7
  32. package/dist/{chunk-KDK5EKUR.js → chunk-ETYF5QZR.js} +2 -2
  33. package/dist/{chunk-COJW2NEY.js → chunk-F5ZNUMSY.js} +50 -11
  34. package/dist/{chunk-EAI6XJ7D.js → chunk-FBP33UM6.js} +3 -3
  35. package/dist/{chunk-KINOX74I.js → chunk-FGAMFJNJ.js} +48 -10
  36. package/dist/{chunk-J7HQPVYH.js → chunk-GRXPFBYC.js} +23 -2
  37. package/dist/{chunk-GQ5ECRP4.js → chunk-GTO5VCYG.js} +3 -3
  38. package/dist/{chunk-RJ5LBZ5U.js → chunk-HES4K6DV.js} +13 -13
  39. package/dist/{chunk-ADCHLNWT.js → chunk-HGKOVKAX.js} +3 -3
  40. package/dist/{chunk-EPRF3ZRV.js → chunk-HLGOG44K.js} +2 -2
  41. package/dist/{chunk-KQ3FNRGR.js → chunk-HUTP35XT.js} +5 -4
  42. package/dist/{chunk-OJ435DQZ.js → chunk-HX7QNHC6.js} +3 -3
  43. package/dist/{chunk-6KL7TFLS.js → chunk-IALX52TR.js} +2 -2
  44. package/dist/{chunk-7SP4C6EV.js → chunk-IF4KYSJZ.js} +7 -3
  45. package/dist/{chunk-5XSNLXU5.js → chunk-IG6J7JWH.js} +51 -12
  46. package/dist/{chunk-M24VXUFG.js → chunk-IIUYKQ5J.js} +3 -3
  47. package/dist/{chunk-5VXU6MND.js → chunk-INTAQV2Y.js} +2 -2
  48. package/dist/{chunk-3KHJJP3J.js → chunk-ITXL2KU5.js} +2 -2
  49. package/dist/{chunk-I47VHSPS.js → chunk-IWG2ECAO.js} +2 -2
  50. package/dist/{chunk-CSHFU6LT.js → chunk-JGCEFKZP.js} +7 -7
  51. package/dist/{chunk-O576GYAP.js → chunk-JQLSFZZP.js} +2 -2
  52. package/dist/{chunk-AHTTKERF.js → chunk-JR7Q6GHE.js} +2 -2
  53. package/dist/{chunk-4T6OCSJ2.js → chunk-KC3WBBI7.js} +3 -3
  54. package/dist/{chunk-BM7MG6HT.js → chunk-KK3L5NVK.js} +3 -3
  55. package/dist/{chunk-FR6UFVSV.js → chunk-L2DIXZUT.js} +91 -41
  56. package/dist/{chunk-WW4BQ744.js → chunk-LCRPGGGS.js} +6 -6
  57. package/dist/{chunk-GKYHWJXI.js → chunk-LKGQ46P4.js} +3 -3
  58. package/dist/{chunk-IOIMQHQ6.js → chunk-LW7STMYZ.js} +2 -2
  59. package/dist/{chunk-OR3R27CU.js → chunk-M53MDBIW.js} +2 -2
  60. package/dist/{chunk-T7Y554OK.js → chunk-MEH4TJEX.js} +4 -4
  61. package/dist/{chunk-T3FAJAJT.js → chunk-MNS2ENXF.js} +3 -3
  62. package/dist/{chunk-CCGWX6AO.js → chunk-MSWIBF6J.js} +2 -2
  63. package/dist/{chunk-B7N5KYXQ.js → chunk-NL7FUIVD.js} +4 -4
  64. package/dist/{chunk-EQOMY4PY.js → chunk-NN5TF4OV.js} +2 -2
  65. package/dist/{chunk-BZJIITYM.js → chunk-NPPUZ4SK.js} +4 -4
  66. package/dist/{chunk-XWODFBHJ.js → chunk-NYOOPQFD.js} +7 -3
  67. package/dist/{chunk-VQFDBNYF.js → chunk-O4CZDTQA.js} +3 -3
  68. package/dist/{chunk-FVMKCZHF.js → chunk-P5XXIVEU.js} +2 -2
  69. package/dist/{chunk-F7XM366V.js → chunk-PC4YLEKA.js} +3 -3
  70. package/dist/{chunk-AQEYUVGJ.js → chunk-PCVXXPYU.js} +12 -5
  71. package/dist/{chunk-TJN2OIVG.js → chunk-PKUWYPI5.js} +180 -12
  72. package/dist/{chunk-W6PCFRPL.js → chunk-PSIDVCBA.js} +2 -2
  73. package/dist/{chunk-RIEAN4D5.js → chunk-PV7ND5WL.js} +48 -8
  74. package/dist/{chunk-7K6NDSXH.js → chunk-Q5CMQVWF.js} +3 -3
  75. package/dist/{chunk-FFVFZLRT.js → chunk-QQXFYTLC.js} +346 -46
  76. package/dist/{chunk-N7BSBAVP.js → chunk-QZYGRDMM.js} +108 -89
  77. package/dist/{chunk-U4IN6AHE.js → chunk-RKBCKCSF.js} +2 -2
  78. package/dist/{chunk-GK32ZFZY.js → chunk-RSBH2C7O.js} +3 -3
  79. package/dist/{chunk-OSXQKC3S.js → chunk-RYNCWA5X.js} +6 -3
  80. package/dist/{chunk-S7YFT3PO.js → chunk-RZWIPDZ2.js} +3 -3
  81. package/dist/{chunk-7UPFTBOW.js → chunk-S52F62GW.js} +4 -4
  82. package/dist/{chunk-WKNAJ4BR.js → chunk-SCOHNGPG.js} +51 -10
  83. package/dist/{chunk-BWYFIVZF.js → chunk-UBQU2BY6.js} +3 -3
  84. package/dist/{chunk-VMSBMIHW.js → chunk-UES4P7FN.js} +2 -2
  85. package/dist/chunk-US3P2DQO.js +87 -0
  86. package/dist/{chunk-AJFHNS67.js → chunk-UXXLE6AH.js} +5 -5
  87. package/dist/{chunk-M3H7ZE6C.js → chunk-VMWSZCAK.js} +5 -4
  88. package/dist/{chunk-S7FOIURR.js → chunk-VVIRAGID.js} +6 -6
  89. package/dist/{chunk-3SBHODJ4.js → chunk-VXTUWXFB.js} +48 -9
  90. package/dist/{chunk-HFEOOQ46.js → chunk-W4TSD3IP.js} +2 -2
  91. package/dist/{chunk-7U4OR7CD.js → chunk-WB3NA3JJ.js} +2 -2
  92. package/dist/{chunk-3SEEP2JM.js → chunk-WITOJZGL.js} +8 -4
  93. package/dist/{chunk-6OYDOQ2Q.js → chunk-WVGNXDEE.js} +2 -2
  94. package/dist/{chunk-UEU6IYKY.js → chunk-X3X4LZDC.js} +2 -2
  95. package/dist/{chunk-SBOAZ7YK.js → chunk-XKKH2DPG.js} +39 -39
  96. package/dist/{chunk-NR5HCAUD.js → chunk-XSVWUOUZ.js} +1 -1
  97. package/dist/{chunk-62Y27T4C.js → chunk-Y4BBGFKY.js} +2 -2
  98. package/dist/{chunk-YENNFS5F.js → chunk-YP2T5YUP.js} +8 -3
  99. package/dist/{chunk-2NYXI35Z.js → chunk-YXKNXIWA.js} +5 -4
  100. package/dist/{chunk-KBVOVDYP.js → chunk-ZGIBV36U.js} +3 -3
  101. package/dist/{chunk-4ZIKWVSQ.js → chunk-ZP265ICS.js} +4 -4
  102. package/dist/{chunk-DLU643TF.js → chunk-ZWVKTWK6.js} +3 -3
  103. package/dist/{chunk-QBYP2FBZ.js → chunk-ZXNHHO6D.js} +2 -2
  104. package/dist/cli.cjs +150 -147
  105. package/dist/clock.js +34 -1
  106. package/dist/completion-registry.js +12 -4
  107. package/dist/dgmo-router-T4HGLIGA.js +88 -0
  108. package/dist/{dimensions-L6BXQXXL.js → dimensions-3JINHSYK.js} +6 -6
  109. package/dist/editor.js +10 -1
  110. package/dist/element.js +124 -123
  111. package/dist/element.mjs +1579 -282
  112. package/dist/{flowchart-parser-UKYOW3VM.js → flowchart-parser-M7KKTLX7.js} +4 -4
  113. package/dist/{flowchart-renderer-DCFKUDDR.js → flowchart-renderer-FFEQFQEU.js} +5 -5
  114. package/dist/highlight.js +10 -1
  115. package/dist/index.js +58 -48
  116. package/dist/{layout-G2ABQJQZ.js → layout-3ZZBKUQC.js} +3 -3
  117. package/dist/{layout-CH4HFFRD.js → layout-AUKTWGYL.js} +4 -4
  118. package/dist/{layout-QBNAMEOA.js → layout-CRLTSFYQ.js} +3 -3
  119. package/dist/{layout-25Y3VDYF.js → layout-FNZDJGKF.js} +4 -4
  120. package/dist/{layout-NY7MHJCL.js → layout-GHWRJKZQ.js} +4 -4
  121. package/dist/{layout-ITLPDG6M.js → layout-LGXOKUCD.js} +4 -4
  122. package/dist/{layout-A6OIGZN2.js → layout-UR7CYAHL.js} +2 -2
  123. package/dist/{layout-4SPLI4HG.js → layout-UTMEVFHN.js} +4 -4
  124. package/dist/{layout-search-LUI475P3.js → layout-search-U2H4ORFJ.js} +8 -6
  125. package/dist/{parser-XRDUX2AR.js → parser-2AXPSKWB.js} +5 -5
  126. package/dist/{parser-B2A7XOAK.js → parser-2AZ2KQTO.js} +4 -4
  127. package/dist/{parser-VVE4THG3.js → parser-2LF33IS2.js} +4 -4
  128. package/dist/{parser-MHKYQPC2.js → parser-3QJ2Z4H4.js} +3 -3
  129. package/dist/parser-5GCQFSTQ.js +13 -0
  130. package/dist/{parser-XCPMEB2Z.js → parser-62OHBRU5.js} +3 -3
  131. package/dist/{parser-GML6V2KZ.js → parser-7LYJFZCP.js} +4 -4
  132. package/dist/{parser-LTA2BOZO.js → parser-BMOTF72G.js} +3 -3
  133. package/dist/{parser-FHRMFUNX.js → parser-CIFLYZYP.js} +3 -3
  134. package/dist/{parser-KTYF6WO5.js → parser-D2UL2G5U.js} +3 -3
  135. package/dist/{parser-5C3PEZK3.js → parser-DM2APTKB.js} +3 -3
  136. package/dist/{parser-X5XLFY4Z.js → parser-DYYTPLBL.js} +4 -4
  137. package/dist/{parser-WPPJGB4R.js → parser-GOUGUTPU.js} +4 -4
  138. package/dist/{parser-T3D7BV6O.js → parser-HGVUVD6J.js} +3 -3
  139. package/dist/{parser-F4FEU57T.js → parser-IEJD3CZB.js} +3 -3
  140. package/dist/{parser-FY3YFFBS.js → parser-JBEWNLAV.js} +5 -5
  141. package/dist/{parser-2UWWNTFL.js → parser-JMXPHNZD.js} +4 -4
  142. package/dist/{parser-YULS5MLD.js → parser-JMXSQY3K.js} +4 -4
  143. package/dist/{parser-YPWYT46P.js → parser-LUXS7J32.js} +4 -4
  144. package/dist/{parser-O7YQYFEN.js → parser-M5BUHULF.js} +4 -4
  145. package/dist/{parser-MVSYFK63.js → parser-MESRHUS4.js} +4 -4
  146. package/dist/{parser-HA3PF5FL.js → parser-OOYNI2UT.js} +4 -4
  147. package/dist/{parser-OPIXXUY4.js → parser-PVBUAVRR.js} +4 -4
  148. package/dist/{parser-3RXYQSA7.js → parser-QKRBSQLJ.js} +5 -5
  149. package/dist/{parser-CCNJKDDA.js → parser-S6NWSIJO.js} +4 -4
  150. package/dist/{parser-S4EJPNVN.js → parser-SFEK6Q7Z.js} +4 -4
  151. package/dist/{parser-7UI6UQ7B.js → parser-SUV52NQW.js} +4 -4
  152. package/dist/{parser-ESHNUFME.js → parser-SZHGLOOZ.js} +4 -4
  153. package/dist/{parser-66RYGJRB.js → parser-TO7IENWL.js} +4 -4
  154. package/dist/{parser-FPCWDYQV.js → parser-TVAMC7CQ.js} +4 -4
  155. package/dist/{parser-LU7UAKNB.js → parser-VAW46R57.js} +4 -4
  156. package/dist/{parser-6MQM7WT3.js → parser-XDSPOXSE.js} +4 -4
  157. package/dist/pert.d.ts +2 -0
  158. package/dist/{renderer-YVMETH6V.js → renderer-3PWLOJPD.js} +8 -8
  159. package/dist/{renderer-7HMZ5OCJ.js → renderer-5EMHIQL5.js} +3 -3
  160. package/dist/{renderer-2DME63VZ.js → renderer-5PODNLYP.js} +4 -4
  161. package/dist/{renderer-4A32DX5L.js → renderer-AQJIQ2XU.js} +7 -6
  162. package/dist/{renderer-RMAM2SOJ.js → renderer-BEW3AYFT.js} +8 -8
  163. package/dist/{renderer-YP3P6UVT.js → renderer-C3UYAXA6.js} +8 -6
  164. package/dist/{renderer-X2LYSMLU.js → renderer-CENXCMWG.js} +3 -3
  165. package/dist/{renderer-6CFML5G3.js → renderer-CXM3ARYN.js} +8 -7
  166. package/dist/{renderer-LHVCKEWF.js → renderer-DW6TEBLA.js} +8 -7
  167. package/dist/{renderer-Y3NXZINM.js → renderer-FE5M6SEZ.js} +8 -7
  168. package/dist/{renderer-G4UOW3RH.js → renderer-FXK6ZL3P.js} +3 -3
  169. package/dist/{renderer-4K5YYX5R.js → renderer-G5ESLCJB.js} +9 -8
  170. package/dist/{renderer-P3YQES7B.js → renderer-HTDS4FED.js} +9 -8
  171. package/dist/{renderer-YEFOOULC.js → renderer-IO4KCFVL.js} +8 -8
  172. package/dist/{renderer-FXMGCSRP.js → renderer-IRAFJNTK.js} +7 -6
  173. package/dist/{renderer-SES7QZYL.js → renderer-MFTTFW32.js} +9 -8
  174. package/dist/{renderer-Z4GNKNXO.js → renderer-MPXI4FVL.js} +6 -6
  175. package/dist/{renderer-RA4PTAMF.js → renderer-NVBEMQ6L.js} +5 -5
  176. package/dist/{renderer-7ZNVOVFG.js → renderer-OOGDCR6K.js} +3 -3
  177. package/dist/{renderer-CJ56SUMF.js → renderer-Q5P72O4A.js} +7 -7
  178. package/dist/{renderer-WCWI4I6A.js → renderer-QZESC66H.js} +2 -2
  179. package/dist/{renderer-B7Y6I7JX.js → renderer-RDBUXW7P.js} +4 -4
  180. package/dist/{renderer-V5SNTPAM.js → renderer-S4XB3JR2.js} +9 -9
  181. package/dist/{renderer-COJZZ4F6.js → renderer-SIWEEWJ5.js} +7 -6
  182. package/dist/{renderer-C3RI4OPJ.js → renderer-TKTM2SL4.js} +8 -7
  183. package/dist/{renderer-WVD6G2TS.js → renderer-TXTTV4HA.js} +8 -7
  184. package/dist/{renderer-HQ4JFZOV.js → renderer-UZIU4AHK.js} +7 -6
  185. package/dist/{renderer-3VY3VXJE.js → renderer-V72VJIGG.js} +9 -8
  186. package/dist/{renderer-PLVIARM7.js → renderer-VEGOEZOI.js} +7 -6
  187. package/dist/{renderer-OZX5KGQM.js → renderer-W6I6T2TA.js} +6 -6
  188. package/dist/{renderer-TQ5QDZOE.js → renderer-WSHJRR2T.js} +6 -6
  189. package/dist/{renderer-radial-VYBZAXXE.js → renderer-radial-AG75PBYD.js} +7 -7
  190. package/dist/{resolver-WFKZYBJ2.js → resolver-ZSFQQRTH.js} +3 -3
  191. package/dist/{state-parser-VIW2WNA5.js → state-parser-KIPZGIB4.js} +5 -5
  192. package/dist/{state-renderer-H22AQD3U.js → state-renderer-U6OSJG52.js} +9 -8
  193. package/docs/language-reference.md +2 -1
  194. package/package.json +1 -1
  195. package/dist/dgmo-router-TGZWFIZO.js +0 -88
  196. package/dist/parser-6GN5YJBJ.js +0 -13
package/dist/element.mjs CHANGED
@@ -568,6 +568,15 @@ var init_directives_registry = __esm({
568
568
  // most types via GLOBAL_BOOLEANS in utils/parsing; gantt derives its boolean
569
569
  // Set from here, and map from `map: true`.
570
570
  { token: "no-legend", category: "directive", gantt: ["boolean"], map: true },
571
+ // `legend-inline`: universal §1.9 boolean (decision #50) — the top-center-
572
+ // legend data charts (bar/line/radar/scatter/function) render the title and
573
+ // legend on one line; every other chart accepts it as a harmless no-op.
574
+ {
575
+ token: "legend-inline",
576
+ category: "directive",
577
+ gantt: ["boolean"],
578
+ map: true
579
+ },
571
580
  // ══════════════════════════════════════════════════════════
572
581
  // Highlight-only vocab (no extractable parser Set).
573
582
  // ══════════════════════════════════════════════════════════
@@ -706,7 +715,7 @@ var init_directives_registry = __esm({
706
715
  { token: "persona", category: "directive" },
707
716
  // ── Function ─────────────────────────────────────────────
708
717
  { token: "x", category: "directive" },
709
- { token: "shade", category: "directive" },
718
+ { token: "fill", category: "directive" },
710
719
  // ── Wordcloud ────────────────────────────────────────────
711
720
  { token: "max", category: "directive" },
712
721
  { token: "size", category: "directive" },
@@ -1038,6 +1047,9 @@ function extractColor(label, palette, diagnostics, line11) {
1038
1047
  function legendSuppressed(options) {
1039
1048
  return options["no-legend"] === "on";
1040
1049
  }
1050
+ function legendInlineRequested(options) {
1051
+ return options["legend-inline"] === "on";
1052
+ }
1041
1053
  function fillModeFromToken(token) {
1042
1054
  switch (token.trim().toLowerCase()) {
1043
1055
  case "fill-solid":
@@ -1516,7 +1528,8 @@ var init_parsing = __esm({
1516
1528
  "fill-outline",
1517
1529
  "no-title",
1518
1530
  "no-notes",
1519
- "no-legend"
1531
+ "no-legend",
1532
+ "legend-inline"
1520
1533
  ]);
1521
1534
  FILL_FAMILY_TOKENS = /* @__PURE__ */ new Set([
1522
1535
  "fill-tint",
@@ -5917,6 +5930,26 @@ function getMaxLegendReservedHeight(config, containerWidth) {
5917
5930
  }
5918
5931
  return max;
5919
5932
  }
5933
+ function getLegendExtent(config, state, containerWidth) {
5934
+ const layout = computeLegendLayout(config, state, containerWidth);
5935
+ let left = Infinity;
5936
+ let right = 0;
5937
+ let bottom = 0;
5938
+ const track = (x, y, w, h) => {
5939
+ left = Math.min(left, x);
5940
+ right = Math.max(right, x + w);
5941
+ bottom = Math.max(bottom, y + h);
5942
+ };
5943
+ if (layout.activeCapsule) {
5944
+ const c = layout.activeCapsule;
5945
+ track(c.x, c.y, c.width, c.height);
5946
+ }
5947
+ for (const pill of layout.pills) track(pill.x, pill.y, pill.width, pill.height);
5948
+ return {
5949
+ width: right - (left === Infinity ? 0 : left),
5950
+ height: bottom || LEGEND_HEIGHT
5951
+ };
5952
+ }
5920
5953
  var CONTROL_PILL_PAD, CONTROL_FONT_SIZE, CONTROL_ICON_GAP, CONTROL_GAP, RAMP_LEGEND_W, RAMP_LEGEND_H, RAMP_LABEL_GAP;
5921
5954
  var init_legend_layout = __esm({
5922
5955
  "src/utils/legend-layout.ts"() {
@@ -9333,7 +9366,8 @@ var init_parser7 = __esm({
9333
9366
  "fill-solid",
9334
9367
  "fill-outline",
9335
9368
  "no-title",
9336
- "no-legend"
9369
+ "no-legend",
9370
+ "legend-inline"
9337
9371
  ]);
9338
9372
  NO_PREFIX_ONLY_BOOLEANS = /* @__PURE__ */ new Set(["activations"]);
9339
9373
  VALID_PARTICIPANT_TYPES = /* @__PURE__ */ new Set([
@@ -14044,6 +14078,10 @@ function applyDirective(key, value, lineNumber, options, error, warn, dateCfg) {
14044
14078
  options.noLegend = true;
14045
14079
  return;
14046
14080
  }
14081
+ case "legend-inline": {
14082
+ options.legendInline = true;
14083
+ return;
14084
+ }
14047
14085
  case "fill-solid": {
14048
14086
  options.fillMode = "solid";
14049
14087
  return;
@@ -14314,6 +14352,7 @@ var init_parser12 = __esm({
14314
14352
  "end-date",
14315
14353
  "no-title",
14316
14354
  "no-legend",
14355
+ "legend-inline",
14317
14356
  "fill-tint",
14318
14357
  "fill-solid",
14319
14358
  "fill-outline",
@@ -14746,6 +14785,8 @@ function parseChart(content, palette) {
14746
14785
  result.noTitle = true;
14747
14786
  } else if (firstToken === "no-legend") {
14748
14787
  result.noLegend = true;
14788
+ } else if (firstToken === "legend-inline") {
14789
+ result.legendInline = true;
14749
14790
  } else if (firstToken === "no-auto-y") {
14750
14791
  result.noAutoY = true;
14751
14792
  } else if (firstToken === "fill") {
@@ -15092,6 +15133,7 @@ var init_chart = __esm({
15092
15133
  "fill-outline",
15093
15134
  "no-title",
15094
15135
  "no-legend",
15136
+ "legend-inline",
15095
15137
  "no-auto-y",
15096
15138
  "fill",
15097
15139
  "hole"
@@ -15399,7 +15441,7 @@ function parseExtendedChartFull(content, palette) {
15399
15441
  const spaceIdx2 = trimmed.indexOf(" ");
15400
15442
  const lastTok = trimmed.substring(trimmed.lastIndexOf(" ") + 1);
15401
15443
  const hasNumericSuffix = spaceIdx2 >= 0 && !isNaN(parseFloat(normalizeNumericToken(lastTok) ?? lastTok));
15402
- const isBareKeywordOption = spaceIdx2 < 0 && /^(fill-tint|fill-solid|fill-outline|no-name|no-value|no-percent|shade|no-title)$/i.test(
15444
+ const isBareKeywordOption = spaceIdx2 < 0 && /^(fill|fill-tint|fill-solid|fill-outline|no-name|no-value|no-percent|no-title)$/i.test(
15403
15445
  trimmed
15404
15446
  );
15405
15447
  const leadToken = spaceIdx2 >= 0 ? trimmed.substring(0, spaceIdx2).toLowerCase() : "";
@@ -15531,8 +15573,8 @@ function parseExtendedChartFull(content, palette) {
15531
15573
  result.noPercent = true;
15532
15574
  continue;
15533
15575
  }
15534
- if (firstToken === "shade") {
15535
- result.shade = true;
15576
+ if (firstToken === "fill" && spaceIdx < 0) {
15577
+ result.fill = true;
15536
15578
  continue;
15537
15579
  }
15538
15580
  const fillFamily = fillModeFromToken(firstToken);
@@ -15549,6 +15591,10 @@ function parseExtendedChartFull(content, palette) {
15549
15591
  result.noLegend = true;
15550
15592
  continue;
15551
15593
  }
15594
+ if (firstToken === "legend-inline" && spaceIdx < 0) {
15595
+ result.legendInline = true;
15596
+ continue;
15597
+ }
15552
15598
  if (firstToken.startsWith("no-") && spaceIdx < 0) {
15553
15599
  continue;
15554
15600
  }
@@ -15911,6 +15957,7 @@ function parseEventLine(content, palette) {
15911
15957
  titleLineNumber: null,
15912
15958
  events: [],
15913
15959
  eras: [],
15960
+ now: null,
15914
15961
  tagGroups: [],
15915
15962
  options,
15916
15963
  diagnostics: [],
@@ -16105,6 +16152,44 @@ function parseEventLine(content, palette) {
16105
16152
  options.noLegend = true;
16106
16153
  continue;
16107
16154
  }
16155
+ if (trimmed.toLowerCase() === "legend-inline") {
16156
+ options.legendInline = true;
16157
+ continue;
16158
+ }
16159
+ {
16160
+ const nowMatch = trimmed.match(NOW_RE);
16161
+ if (nowMatch) {
16162
+ const rest = nowMatch[1].trim();
16163
+ if (!rest) {
16164
+ result.now = {
16165
+ computed: true,
16166
+ date: null,
16167
+ dateValue: null,
16168
+ label: "now",
16169
+ lineNumber
16170
+ };
16171
+ } else {
16172
+ const prefix = extractDatePrefix(rest, dateCtx);
16173
+ if (prefix) {
16174
+ const label = prefix.remainder?.trim() || "now";
16175
+ result.now = {
16176
+ computed: false,
16177
+ date: prefix.startDate,
16178
+ dateValue: parseTimelineDate(prefix.startDate),
16179
+ label,
16180
+ lineNumber
16181
+ };
16182
+ } else {
16183
+ result.diagnostics.push(
16184
+ emit(EVENT_LINE_DX.BAD_DATE, lineNumber, {
16185
+ token: rest.split(/\s+/)[0]
16186
+ })
16187
+ );
16188
+ }
16189
+ }
16190
+ continue;
16191
+ }
16192
+ }
16108
16193
  {
16109
16194
  const fm = fillModeFromToken(trimmed);
16110
16195
  if (fm !== null) {
@@ -16180,6 +16265,13 @@ function parseEventLine(content, palette) {
16180
16265
  });
16181
16266
  }
16182
16267
  validateEventDateOrder(result.events, eraFlaggedLines, result.diagnostics);
16268
+ if (result.now && !options.scale) {
16269
+ result.diagnostics.push(
16270
+ emit(EVENT_LINE_DX.UNSUPPORTED, result.now.lineNumber, {
16271
+ reason: "the `now` marker needs a to-scale axis; it is ignored under `no-scale`."
16272
+ })
16273
+ );
16274
+ }
16183
16275
  return result;
16184
16276
  }
16185
16277
  function resolveFutureEvents(events) {
@@ -16374,7 +16466,7 @@ function parseEventHeader(trimmed, lineNumber, era, aliasMap, diagnostics, pushW
16374
16466
  era
16375
16467
  };
16376
16468
  }
16377
- var NON_ISO_DATE_RE, TBD_RE, SIDE_RE, ERA_RE2, ERA_COLLAPSED_RE, SECTION_SEAM_RE, DIRECTION_RE;
16469
+ var NON_ISO_DATE_RE, TBD_RE, SIDE_RE, ERA_RE2, ERA_COLLAPSED_RE, NOW_RE, SECTION_SEAM_RE, DIRECTION_RE;
16378
16470
  var init_parser13 = __esm({
16379
16471
  "src/event-line/parser.ts"() {
16380
16472
  "use strict";
@@ -16390,6 +16482,7 @@ var init_parser13 = __esm({
16390
16482
  SIDE_RE = /^side\s+(above|below|alternate)\b/i;
16391
16483
  ERA_RE2 = /^\[([^\]]+)\]\s*(.*)$/;
16392
16484
  ERA_COLLAPSED_RE = /\bcollapsed:\s*(true|false)\b/i;
16485
+ NOW_RE = /^now\b\s*(.*)$/i;
16393
16486
  SECTION_SEAM_RE = /^section\b/i;
16394
16487
  DIRECTION_RE = /^direction\s+(\w+)/i;
16395
16488
  }
@@ -18054,7 +18147,8 @@ var init_parser16 = __esm({
18054
18147
  "fill-solid",
18055
18148
  "fill-outline",
18056
18149
  "no-title",
18057
- "no-legend"
18150
+ "no-legend",
18151
+ "legend-inline"
18058
18152
  ]);
18059
18153
  ALL_CHART_TYPES2 = [
18060
18154
  "c4",
@@ -19758,6 +19852,9 @@ function parseGantt(content, palette) {
19758
19852
  case "no-legend":
19759
19853
  result.options.noLegend = true;
19760
19854
  break;
19855
+ case "legend-inline":
19856
+ result.options.legendInline = true;
19857
+ break;
19761
19858
  }
19762
19859
  continue;
19763
19860
  }
@@ -20563,6 +20660,10 @@ function parseMap(content, palette) {
20563
20660
  case "no-legend":
20564
20661
  d.noLegend = true;
20565
20662
  break;
20663
+ // §1.9 universal — accepted so it doesn't parse as a region; map renders no
20664
+ // one-line header, so render.ts emits the "not supported" warning.
20665
+ case "legend-inline":
20666
+ break;
20566
20667
  case "no-coastline":
20567
20668
  d.noCoastline = true;
20568
20669
  break;
@@ -21853,6 +21954,7 @@ __export(parser_exports15, {
21853
21954
  function parseSketch(content, palette) {
21854
21955
  const options = {
21855
21956
  noLegend: false,
21957
+ legendInline: false,
21856
21958
  fillMode: void 0,
21857
21959
  noDescriptions: false
21858
21960
  };
@@ -22233,6 +22335,10 @@ function parseSketch(content, palette) {
22233
22335
  continue;
22234
22336
  }
22235
22337
  currentTagGroup = null;
22338
+ if (indent === 0 && /^legend-inline\s*$/i.test(trimmed)) {
22339
+ options.legendInline = true;
22340
+ continue;
22341
+ }
22236
22342
  if (indent === 0 && /^no-legend\s*$/i.test(trimmed)) {
22237
22343
  options.noLegend = true;
22238
22344
  continue;
@@ -25837,7 +25943,8 @@ var init_parser28 = __esm({
25837
25943
  "fill-solid",
25838
25944
  "fill-outline",
25839
25945
  "no-title",
25840
- "no-legend"
25946
+ "no-legend",
25947
+ "legend-inline"
25841
25948
  ]);
25842
25949
  }
25843
25950
  });
@@ -26425,6 +26532,10 @@ function handleDirective(trimmed, options, result, pushWarning, lineNumber) {
26425
26532
  else if (key === "legend") options.noLegend = true;
26426
26533
  return true;
26427
26534
  }
26535
+ if (/^legend-inline\s*$/i.test(trimmed)) {
26536
+ options.legendInline = true;
26537
+ return true;
26538
+ }
26428
26539
  {
26429
26540
  const fm = fillModeFromToken(trimmed);
26430
26541
  if (fm !== null) {
@@ -26676,6 +26787,10 @@ function parseBlock(content, palette) {
26676
26787
  options.noLegend = true;
26677
26788
  continue;
26678
26789
  }
26790
+ if (/^legend-inline\s*$/i.test(trimmed)) {
26791
+ options.legendInline = true;
26792
+ continue;
26793
+ }
26679
26794
  const fm = fillModeFromToken(trimmed);
26680
26795
  if (fm !== null) {
26681
26796
  if (fm === "tint") delete options.fillMode;
@@ -28227,6 +28342,18 @@ function formatTime(h, m, s, hours12) {
28227
28342
  }
28228
28343
  return { main: `${pad22(h)}:${pad22(m)}`, sec, ap: "" };
28229
28344
  }
28345
+ function fixedTimeCells(s, x0, fs) {
28346
+ const digit = fs * 0.6;
28347
+ const colon = fs * 0.32;
28348
+ const cells = [];
28349
+ let x = x0;
28350
+ for (const ch of s) {
28351
+ const cw = ch === ":" ? colon : digit;
28352
+ cells.push({ ch, x: x + cw / 2 });
28353
+ x += cw;
28354
+ }
28355
+ return { cells, width: x - x0 };
28356
+ }
28230
28357
  function fmtDelta(ms) {
28231
28358
  const m = Math.max(0, Math.round(ms / 6e4));
28232
28359
  if (m < 90) return `${m}m`;
@@ -28654,6 +28781,8 @@ function parseClock(content, palette) {
28654
28781
  case "no-title":
28655
28782
  result.noTitle = true;
28656
28783
  break;
28784
+ case "legend-inline":
28785
+ break;
28657
28786
  case "fill-tint":
28658
28787
  case "fill-solid":
28659
28788
  case "fill-outline": {
@@ -28829,6 +28958,7 @@ var init_parser35 = __esm({
28829
28958
  "no-sun",
28830
28959
  "time-24",
28831
28960
  "no-title",
28961
+ "legend-inline",
28832
28962
  // Canonical direction booleans (§1.9); key+value `direction <lr|tb>` (and
28833
28963
  // its `columns` value alias) stays parse-accepted legacy. These MUST be in
28834
28964
  // this set — otherwise a bare `direction-lr` line parses as a zone entry.
@@ -29035,6 +29165,10 @@ function parseBracket(content, palette) {
29035
29165
  else result.mode = lower;
29036
29166
  continue;
29037
29167
  }
29168
+ if (lower === "legend-inline") {
29169
+ result.legendInline = true;
29170
+ continue;
29171
+ }
29038
29172
  if (lower === "no-legend") {
29039
29173
  result.noLegend = true;
29040
29174
  continue;
@@ -33492,7 +33626,10 @@ function renderLegendSvg(groups, options) {
33492
33626
  const wrapWidth = containerWidth > 0 ? containerWidth : 1200;
33493
33627
  const config = {
33494
33628
  groups,
33495
- position: { placement: "top-center", titleRelation: "below-title" },
33629
+ position: {
33630
+ placement: "top-center",
33631
+ titleRelation: options.align === "left" ? "inline-with-title" : "below-title"
33632
+ },
33496
33633
  mode: "preview"
33497
33634
  };
33498
33635
  const state = { activeGroup: activeGroup ?? null };
@@ -33538,13 +33675,132 @@ var init_legend_svg = __esm({
33538
33675
  }
33539
33676
  });
33540
33677
 
33678
+ // src/utils/inline-header.ts
33679
+ function legendInlineSupported(type) {
33680
+ return LEGEND_INLINE_SUPPORTED.has(type.toLowerCase());
33681
+ }
33682
+ function measureTitleWidth(title, fontSize = TITLE_FONT_SIZE) {
33683
+ return measureText(title, fontSize) * BOLD_WIDTH_FACTOR;
33684
+ }
33685
+ function layoutInlineHeader(input) {
33686
+ const pad3 = input.pad ?? INLINE_HEADER_PAD;
33687
+ const gap = input.gap ?? INLINE_HEADER_GAP;
33688
+ const fontSize = input.titleFontSize ?? TITLE_FONT_SIZE;
33689
+ const baselineY = input.titleBaselineY ?? TITLE_Y;
33690
+ const stacked = {
33691
+ inline: false,
33692
+ titleX: input.containerWidth / 2,
33693
+ titleAnchor: "middle",
33694
+ legendX: 0,
33695
+ legendY: input.titleBandHeight,
33696
+ headerHeight: input.titleBandHeight + (input.hasLegend ? input.legendReserve : 0)
33697
+ };
33698
+ if (!input.requested || !input.hasLegend) return stacked;
33699
+ const titleW = measureTitleWidth(input.title, fontSize);
33700
+ const titleRight = pad3 + titleW + gap;
33701
+ const avail = input.containerWidth - titleRight - pad3;
33702
+ const fits = avail >= MIN_INLINE_LEGEND_W && input.legendHeight <= LEGEND_HEIGHT + 1 && input.legendWidth <= avail + 0.5;
33703
+ if (!fits) return stacked;
33704
+ return {
33705
+ inline: true,
33706
+ titleX: pad3,
33707
+ titleAnchor: "start",
33708
+ // Flush to the right edge; the fit test guarantees this clears the title.
33709
+ legendX: input.containerWidth - pad3 - input.legendWidth,
33710
+ // Center the legend row on the title's visual middle (cap-center ≈ baseline
33711
+ // − 0.36·fontSize).
33712
+ legendY: baselineY - fontSize * 0.36 - input.legendHeight / 2,
33713
+ headerHeight: input.titleBandHeight
33714
+ };
33715
+ }
33716
+ var LEGEND_INLINE_SUPPORTED, INLINE_HEADER_PAD, INLINE_HEADER_GAP, BOLD_WIDTH_FACTOR, MIN_INLINE_LEGEND_W;
33717
+ var init_inline_header = __esm({
33718
+ "src/utils/inline-header.ts"() {
33719
+ "use strict";
33720
+ init_legend_constants();
33721
+ init_text_measure();
33722
+ init_title_constants();
33723
+ LEGEND_INLINE_SUPPORTED = /* @__PURE__ */ new Set([
33724
+ // data charts
33725
+ "bar",
33726
+ "line",
33727
+ "radar",
33728
+ "scatter",
33729
+ "function",
33730
+ // structured
33731
+ "state",
33732
+ "treemap",
33733
+ "block",
33734
+ "event-line",
33735
+ "boxes-and-lines",
33736
+ "er",
33737
+ "class",
33738
+ "family",
33739
+ "infra",
33740
+ "sequence",
33741
+ "sketch",
33742
+ "bracket",
33743
+ "gantt",
33744
+ "pert",
33745
+ "sitemap",
33746
+ "mindmap",
33747
+ "kanban",
33748
+ "journey-map"
33749
+ ]);
33750
+ INLINE_HEADER_PAD = 8;
33751
+ INLINE_HEADER_GAP = 16;
33752
+ BOLD_WIDTH_FACTOR = 1.06;
33753
+ MIN_INLINE_LEGEND_W = 48;
33754
+ }
33755
+ });
33756
+
33541
33757
  // src/charts-d3/shared.ts
33542
33758
  function fmtNum(n) {
33543
33759
  if (!isFinite(n)) return "";
33544
33760
  if (Math.abs(n) >= 1e3) return n.toLocaleString("en-US");
33545
33761
  return String(Math.round(n * 100) / 100);
33546
33762
  }
33547
- function reserveHeader(svg, chart, colors, palette, isDark, hasTitle, width) {
33763
+ function renderInlineHeader(svg, groups, palette, isDark, width, info) {
33764
+ const {
33765
+ svg: legendSvg,
33766
+ height: legendH,
33767
+ width: legendW
33768
+ } = renderLegendSvg(groups, {
33769
+ palette,
33770
+ isDark,
33771
+ containerWidth: width,
33772
+ activeGroup: groups[0].name,
33773
+ className: "chart-legend",
33774
+ align: "left"
33775
+ });
33776
+ if (!legendSvg) return null;
33777
+ const geom = layoutInlineHeader({
33778
+ requested: true,
33779
+ title: info.title,
33780
+ hasLegend: true,
33781
+ legendWidth: legendW,
33782
+ legendHeight: legendH,
33783
+ containerWidth: width,
33784
+ titleBandHeight: TITLE_OFFSET,
33785
+ legendReserve: 0,
33786
+ titleBaselineY: TITLE_Y,
33787
+ titleFontSize: TITLE_FONT_SIZE
33788
+ });
33789
+ if (!geom.inline) return null;
33790
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", geom.titleX).attr("y", TITLE_Y).attr("fill", info.textColor).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(info.title);
33791
+ if (info.titleLineNumber) {
33792
+ titleEl.attr("data-line-number", info.titleLineNumber);
33793
+ }
33794
+ const node = svg.node();
33795
+ if (node) {
33796
+ node.insertAdjacentHTML(
33797
+ "beforeend",
33798
+ `<g transform="translate(${geom.legendX},${geom.legendY})">${legendSvg}</g>`
33799
+ );
33800
+ }
33801
+ return TITLE_OFFSET + 12;
33802
+ }
33803
+ function reserveHeader(svg, chart, colors, palette, isDark, hasTitle, width, inlineTitle) {
33548
33804
  return injectLegendGroups(
33549
33805
  svg,
33550
33806
  // `no-legend` (#48) → pass no groups; injectLegendGroups then returns the
@@ -33553,10 +33809,31 @@ function reserveHeader(svg, chart, colors, palette, isDark, hasTitle, width) {
33553
33809
  palette,
33554
33810
  isDark,
33555
33811
  hasTitle,
33556
- width
33812
+ width,
33813
+ inlineTitle
33557
33814
  );
33558
33815
  }
33559
- function injectLegendGroups(svg, groups, palette, isDark, hasTitle, width) {
33816
+ function injectLegendGroups(svg, groups, palette, isDark, hasTitle, width, inlineTitle) {
33817
+ if (inlineTitle && groups.length > 0) {
33818
+ const inset = renderInlineHeader(
33819
+ svg,
33820
+ groups,
33821
+ palette,
33822
+ isDark,
33823
+ width,
33824
+ inlineTitle
33825
+ );
33826
+ if (inset !== null) return inset;
33827
+ }
33828
+ if (inlineTitle) {
33829
+ renderChartTitle(
33830
+ svg,
33831
+ inlineTitle.title,
33832
+ inlineTitle.titleLineNumber,
33833
+ width,
33834
+ inlineTitle.textColor
33835
+ );
33836
+ }
33560
33837
  const titleH = hasTitle ? 40 : 0;
33561
33838
  if (groups.length === 0) return hasTitle ? titleH + 12 : 24;
33562
33839
  const legendY = 8 + titleH;
@@ -33599,6 +33876,7 @@ function tagDatum(sel, o) {
33599
33876
  if (o.name != null) sel.attr("data-name", o.name);
33600
33877
  if (o.value != null) sel.attr("data-value", String(o.value));
33601
33878
  if (o.color != null) sel.attr("data-color", o.color);
33879
+ if (o.seriesName != null) sel.attr("data-series-name", o.seriesName);
33602
33880
  return sel;
33603
33881
  }
33604
33882
  function drawValueLabel(svg, text, x, y, color, anchor = "middle") {
@@ -33612,6 +33890,9 @@ var init_shared = __esm({
33612
33890
  init_text_measure();
33613
33891
  init_data_chart_parser();
33614
33892
  init_legend_svg();
33893
+ init_d3_helpers();
33894
+ init_inline_header();
33895
+ init_title_constants();
33615
33896
  AXIS_LABEL_FONT = 13;
33616
33897
  TICK_FONT = 12;
33617
33898
  VALUE_FONT = 12;
@@ -33626,7 +33907,7 @@ function seriesValues(pt, seriesCount) {
33626
33907
  for (let s = 1; s < seriesCount; s++) out.push(pt.extraValues?.[s - 1] ?? 0);
33627
33908
  return out;
33628
33909
  }
33629
- function renderBar(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, hasTitle) {
33910
+ function renderBar(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, hasTitle, inlineTitle) {
33630
33911
  const data = chart.data;
33631
33912
  const seriesNames = chart.seriesNames?.length ? chart.seriesNames : [chart.series ?? ""];
33632
33913
  const stacked = chart.barLayout === "stack";
@@ -33641,7 +33922,7 @@ function renderBar(svg, chart, width, height, colors, palette, isDark, textColor
33641
33922
  const fillMode = chart.fillMode;
33642
33923
  const colorFor = (seriesIdx, catIdx, override) => {
33643
33924
  if (override) return override;
33644
- return multiSeries ? colors[seriesIdx % colors.length] : colors[catIdx % colors.length];
33925
+ return multiSeries ? chart.seriesNameColors?.[seriesIdx] ?? colors[seriesIdx % colors.length] : colors[catIdx % colors.length];
33645
33926
  };
33646
33927
  const fillOf = (c) => shapeFill(palette, c, isDark, { mode: fillMode });
33647
33928
  const top = reserveHeader(
@@ -33651,7 +33932,8 @@ function renderBar(svg, chart, width, height, colors, palette, isDark, textColor
33651
33932
  palette,
33652
33933
  isDark,
33653
33934
  hasTitle,
33654
- width
33935
+ width,
33936
+ inlineTitle
33655
33937
  );
33656
33938
  const perCat = data.map((d) => seriesValues(d, seriesCount));
33657
33939
  const maxVal = stacked ? d3max(perCat, (vals) => vals.reduce((a, b) => a + b, 0)) ?? 0 : d3max(perCat, (vals) => d3max(vals) ?? 0) ?? 0;
@@ -33704,7 +33986,8 @@ function renderBar(svg, chart, width, height, colors, palette, isDark, textColor
33704
33986
  key: multiSeries ? sName : d.label,
33705
33987
  name: multiSeries ? `${d.label} \xB7 ${sName}` : d.label,
33706
33988
  value: v,
33707
- color: stroke2
33989
+ color: stroke2,
33990
+ seriesName: multiSeries ? sName : void 0
33708
33991
  };
33709
33992
  if (stacked) {
33710
33993
  if (horiz) {
@@ -33825,7 +34108,7 @@ import { line as d3line, area as d3area } from "d3-shape";
33825
34108
  function seriesValue(pt, s) {
33826
34109
  return s === 0 ? pt.value : pt.extraValues?.[s - 1] ?? 0;
33827
34110
  }
33828
- function renderLine(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, bgColor, hasTitle) {
34111
+ function renderLine(svg, chart, width, height, colors, palette, isDark, textColor, mutedColor, bgColor, hasTitle, inlineTitle) {
33829
34112
  const data = chart.data;
33830
34113
  const isArea = chart.fill === true;
33831
34114
  const solid = chart.fillMode === "solid";
@@ -33851,7 +34134,8 @@ function renderLine(svg, chart, width, height, colors, palette, isDark, textColo
33851
34134
  palette,
33852
34135
  isDark,
33853
34136
  hasTitle,
33854
- width
34137
+ width,
34138
+ inlineTitle
33855
34139
  );
33856
34140
  const extentFor = (side) => {
33857
34141
  let lo = Infinity;
@@ -34010,7 +34294,7 @@ function renderPie(svg, chart, width, height, palette, isDark, textColor, topIns
34010
34294
  const MARGIN11 = 6;
34011
34295
  const halfW = width / 2 - MARGIN11;
34012
34296
  const rH = availH / 2 - availH * 0.06;
34013
- const labelExtent = 1.268 + 0.0428 * maxLabelLen;
34297
+ const labelExtent = 1.27 + 0.06 * maxLabelLen;
34014
34298
  const rLabeled = Math.min(rH, halfW / labelExtent);
34015
34299
  const showLabels = maxLabelLen > 0 && rLabeled >= 45;
34016
34300
  const rPlain = Math.min(rH, halfW * 0.94);
@@ -34024,6 +34308,7 @@ function renderPie(svg, chart, width, height, palette, isDark, textColor, topIns
34024
34308
  const arcs = d3pie().sort(null).value((d) => d.value)(data.map((d) => ({ value: d.value })));
34025
34309
  const arcGen = d3arc().innerRadius(hasHole ? radius * holeRatio : 0).outerRadius(radius);
34026
34310
  const g = svg.append("g").attr("transform", `translate(${cx},${cy})`);
34311
+ const labelInfos = [];
34027
34312
  arcs.forEach((a, i) => {
34028
34313
  const stroke2 = strokeFor(i, data[i].color);
34029
34314
  const fill2 = shapeFill(palette, stroke2, isDark, { mode: fillMode });
@@ -34037,32 +34322,65 @@ function renderPie(svg, chart, width, height, palette, isDark, textColor, topIns
34037
34322
  color: stroke2
34038
34323
  });
34039
34324
  if (!showLabels) return;
34325
+ const label = labels[i];
34326
+ if (!label) return;
34040
34327
  const mid = (a.startAngle + a.endAngle) / 2 - Math.PI / 2;
34041
34328
  const rightSide = Math.cos(mid) >= 0;
34042
- const x0 = Math.cos(mid) * radius;
34043
- const y0 = Math.sin(mid) * radius;
34044
- const x1 = Math.cos(mid) * (radius + elbowOut);
34045
- const y1 = Math.sin(mid) * (radius + elbowOut);
34046
- const x2 = x1 + (rightSide ? elbowRun : -elbowRun);
34047
- const label = labels[i];
34048
- if (label) {
34049
- const tag = {
34329
+ labelInfos.push({
34330
+ rightSide,
34331
+ x0: Math.cos(mid) * radius,
34332
+ y0: Math.sin(mid) * radius,
34333
+ // Radial stub end — meets the arc at 90° to the circle edge.
34334
+ x1: Math.cos(mid) * (radius + elbowOut),
34335
+ y1: Math.sin(mid) * (radius + elbowOut),
34336
+ idealY: Math.sin(mid) * (radius + elbowOut),
34337
+ y: 0,
34338
+ label,
34339
+ stroke: stroke2,
34340
+ // Tag the leader-line + label with the same emph-key as the wedge so
34341
+ // hover (baked-CSS :has() and the app's JS dim) emphasizes all three
34342
+ // together; colour the label text to match its segment.
34343
+ tag: {
34050
34344
  line: data[i].lineNumber,
34051
34345
  key: data[i].label,
34052
34346
  name: data[i].label,
34053
34347
  value: `${fmtNum(data[i].value)} (${pct}%)`,
34054
34348
  color: stroke2
34055
- };
34056
- tagDatum(
34057
- g.append("polyline").attr("points", `${x0},${y0} ${x1},${y1} ${x2},${y1}`).attr("fill", "none").attr("stroke", stroke2).attr("stroke-width", 1),
34058
- tag
34059
- );
34060
- tagDatum(
34061
- g.append("text").attr("x", x2 + (rightSide ? font * 0.3 : -font * 0.3)).attr("y", y1 + font * 0.3).attr("text-anchor", rightSide ? "start" : "end").attr("fill", stroke2).attr("font-size", font).attr("font-family", FONT_FAMILY).text(label),
34062
- tag
34063
- );
34064
- }
34349
+ }
34350
+ });
34065
34351
  });
34352
+ if (showLabels && labelInfos.length > 0) {
34353
+ const labelX = radius + elbowOut + elbowRun;
34354
+ const minGap = font * 1.18;
34355
+ const bound = availH / 2 - font;
34356
+ for (const side of [true, false]) {
34357
+ const group = labelInfos.filter((l) => l.rightSide === side);
34358
+ if (group.length === 0) continue;
34359
+ const topHalf = group.filter((l) => l.idealY < 0).sort((a, b) => b.idealY - a.idealY);
34360
+ const botHalf = group.filter((l) => l.idealY >= 0).sort((a, b) => a.idealY - b.idealY);
34361
+ topHalf.forEach((l, i) => {
34362
+ l.y = i === 0 ? l.idealY : Math.min(l.idealY, topHalf[i - 1].y - minGap);
34363
+ l.y = Math.max(l.y, -bound);
34364
+ });
34365
+ botHalf.forEach((l, i) => {
34366
+ l.y = i === 0 ? l.idealY : Math.max(l.idealY, botHalf[i - 1].y + minGap);
34367
+ l.y = Math.min(l.y, bound);
34368
+ });
34369
+ const sx = side ? 1 : -1;
34370
+ const tx = sx * labelX;
34371
+ for (const l of group) {
34372
+ const xr = Math.abs(l.y0) < 1e-3 ? l.x1 : l.y * l.x0 / l.y0;
34373
+ tagDatum(
34374
+ g.append("polyline").attr("points", `${l.x0},${l.y0} ${xr},${l.y} ${tx},${l.y}`).attr("fill", "none").attr("stroke", l.stroke).attr("stroke-width", 1),
34375
+ l.tag
34376
+ );
34377
+ tagDatum(
34378
+ g.append("text").attr("x", tx + sx * font * 0.3).attr("y", l.y + font * 0.3).attr("text-anchor", side ? "start" : "end").attr("fill", l.stroke).attr("font-size", font).attr("font-family", FONT_FAMILY).text(l.label),
34379
+ l.tag
34380
+ );
34381
+ }
34382
+ }
34383
+ }
34066
34384
  if (hasHole && !chart.noCenterTotal) {
34067
34385
  g.append("text").attr("text-anchor", "middle").attr("y", Math.max(4, 8 * labelScale)).attr("fill", textColor).attr("font-size", Math.max(11, 26 * labelScale)).attr("font-weight", 700).attr("font-family", FONT_FAMILY).text(fmtNum(total));
34068
34386
  }
@@ -34908,25 +35226,179 @@ var init_chord = __esm({
34908
35226
  });
34909
35227
 
34910
35228
  // src/utils/expr-eval.ts
35229
+ function tokenize(expr) {
35230
+ const tokens = [];
35231
+ let i = 0;
35232
+ const n = expr.length;
35233
+ while (i < n) {
35234
+ const c = expr.charAt(i);
35235
+ if (c === " " || c === " ") {
35236
+ i++;
35237
+ continue;
35238
+ }
35239
+ if (c >= "0" && c <= "9" || c === ".") {
35240
+ let j = i + 1;
35241
+ while (/[0-9.]/.test(expr.charAt(j))) j++;
35242
+ const exp = expr.charAt(j);
35243
+ if (exp === "e" || exp === "E") {
35244
+ let k = j + 1;
35245
+ const sign = expr.charAt(k);
35246
+ if (sign === "+" || sign === "-") k++;
35247
+ if (/[0-9]/.test(expr.charAt(k))) {
35248
+ while (/[0-9]/.test(expr.charAt(k))) k++;
35249
+ j = k;
35250
+ }
35251
+ }
35252
+ const num = Number(expr.slice(i, j));
35253
+ if (!isFinite(num)) throw new Error("bad number");
35254
+ tokens.push({ t: "num", v: num });
35255
+ i = j;
35256
+ continue;
35257
+ }
35258
+ if (/[a-zA-Z]/.test(c)) {
35259
+ let j = i + 1;
35260
+ while (/[a-zA-Z0-9]/.test(expr.charAt(j))) j++;
35261
+ tokens.push({ t: "id", v: expr.slice(i, j).toLowerCase() });
35262
+ i = j;
35263
+ continue;
35264
+ }
35265
+ if ("+-*/^()".includes(c)) {
35266
+ tokens.push({ t: "op", v: c });
35267
+ i++;
35268
+ continue;
35269
+ }
35270
+ throw new Error(`unexpected char: ${c}`);
35271
+ }
35272
+ return tokens;
35273
+ }
34911
35274
  function evaluateExpression(expr, x) {
34912
35275
  try {
34913
- const processed = expr.replace(/\bpi\b/gi, String(Math.PI)).replace(/\be\b/g, String(Math.E)).replace(/\bsin\s*\(/gi, "Math.sin(").replace(/\bcos\s*\(/gi, "Math.cos(").replace(/\btan\s*\(/gi, "Math.tan(").replace(/\bln\s*\(/gi, "Math.log(").replace(/\blog\s*\(/gi, "Math.log10(").replace(/\bexp\s*\(/gi, "Math.exp(").replace(/\bsqrt\s*\(/gi, "Math.sqrt(").replace(/\babs\s*\(/gi, "Math.abs(").replace(/\bx\b/gi, `(${x})`).replace(/\^/g, "**");
34914
- const result = new Function(`return ${processed}`)();
35276
+ const result = new Parser(tokenize(expr), x).evaluate();
34915
35277
  return typeof result === "number" && isFinite(result) ? result : NaN;
34916
35278
  } catch {
34917
35279
  return NaN;
34918
35280
  }
34919
35281
  }
35282
+ var FUNCS, Parser;
34920
35283
  var init_expr_eval = __esm({
34921
35284
  "src/utils/expr-eval.ts"() {
34922
35285
  "use strict";
35286
+ FUNCS = {
35287
+ sin: Math.sin,
35288
+ cos: Math.cos,
35289
+ tan: Math.tan,
35290
+ ln: Math.log,
35291
+ log: Math.log10,
35292
+ exp: Math.exp,
35293
+ sqrt: Math.sqrt,
35294
+ abs: Math.abs
35295
+ };
35296
+ Parser = class {
35297
+ constructor(tokens, x) {
35298
+ this.tokens = tokens;
35299
+ this.x = x;
35300
+ }
35301
+ tokens;
35302
+ x;
35303
+ pos = 0;
35304
+ peek() {
35305
+ return this.tokens[this.pos];
35306
+ }
35307
+ eatOp(v) {
35308
+ const tk = this.peek();
35309
+ if (tk?.t !== "op" || tk.v !== v) throw new Error(`expected ${v}`);
35310
+ this.pos++;
35311
+ }
35312
+ evaluate() {
35313
+ const value = this.parseExpr();
35314
+ if (this.pos !== this.tokens.length) throw new Error("trailing input");
35315
+ return value;
35316
+ }
35317
+ // expr = term (('+' | '-') term)*
35318
+ parseExpr() {
35319
+ let value = this.parseTerm();
35320
+ for (; ; ) {
35321
+ const tk = this.peek();
35322
+ if (tk?.t === "op" && (tk.v === "+" || tk.v === "-")) {
35323
+ this.pos++;
35324
+ const rhs = this.parseTerm();
35325
+ value = tk.v === "+" ? value + rhs : value - rhs;
35326
+ } else break;
35327
+ }
35328
+ return value;
35329
+ }
35330
+ // term = factor (('*' | '/') factor)*
35331
+ parseTerm() {
35332
+ let value = this.parseFactor();
35333
+ for (; ; ) {
35334
+ const tk = this.peek();
35335
+ if (tk?.t === "op" && (tk.v === "*" || tk.v === "/")) {
35336
+ this.pos++;
35337
+ const rhs = this.parseFactor();
35338
+ value = tk.v === "*" ? value * rhs : value / rhs;
35339
+ } else break;
35340
+ }
35341
+ return value;
35342
+ }
35343
+ // factor = ('+' | '-') factor | power
35344
+ parseFactor() {
35345
+ const tk = this.peek();
35346
+ if (tk?.t === "op" && (tk.v === "+" || tk.v === "-")) {
35347
+ this.pos++;
35348
+ const operand = this.parseFactor();
35349
+ return tk.v === "-" ? -operand : operand;
35350
+ }
35351
+ return this.parsePower();
35352
+ }
35353
+ // power = primary ('^' factor)? — right-associative
35354
+ parsePower() {
35355
+ const base = this.parsePrimary();
35356
+ const tk = this.peek();
35357
+ if (tk?.t === "op" && tk.v === "^") {
35358
+ this.pos++;
35359
+ const exponent = this.parseFactor();
35360
+ return base ** exponent;
35361
+ }
35362
+ return base;
35363
+ }
35364
+ // primary = number | const | 'x' | func '(' expr ')' | '(' expr ')'
35365
+ parsePrimary() {
35366
+ const tk = this.peek();
35367
+ if (!tk) throw new Error("unexpected end");
35368
+ if (tk.t === "num") {
35369
+ this.pos++;
35370
+ return tk.v;
35371
+ }
35372
+ if (tk.t === "op" && tk.v === "(") {
35373
+ this.pos++;
35374
+ const value = this.parseExpr();
35375
+ this.eatOp(")");
35376
+ return value;
35377
+ }
35378
+ if (tk.t === "id") {
35379
+ this.pos++;
35380
+ if (tk.v === "x") return this.x;
35381
+ if (tk.v === "pi") return Math.PI;
35382
+ if (tk.v === "e") return Math.E;
35383
+ const fn = FUNCS[tk.v];
35384
+ if (fn) {
35385
+ this.eatOp("(");
35386
+ const arg = this.parseExpr();
35387
+ this.eatOp(")");
35388
+ return fn(arg);
35389
+ }
35390
+ throw new Error(`unknown identifier: ${tk.v}`);
35391
+ }
35392
+ throw new Error("unexpected token");
35393
+ }
35394
+ };
34923
35395
  }
34924
35396
  });
34925
35397
 
34926
35398
  // src/charts-d3/function.ts
34927
35399
  import { scaleLinear as scaleLinear8 } from "d3-scale";
34928
- import { line as d3line2 } from "d3-shape";
34929
- function renderFunction(svg, chart, width, height, colors, textColor, mutedColor, topInset) {
35400
+ import { line as d3line2, area as d3area2 } from "d3-shape";
35401
+ function renderFunction(svg, chart, width, height, colors, textColor, mutedColor, topInset, palette, isDark) {
34930
35402
  const fns = chart.functions ?? [];
34931
35403
  if (fns.length === 0) return;
34932
35404
  const xr = chart.xRange ?? { min: -10, max: 10 };
@@ -34974,7 +35446,15 @@ function renderFunction(svg, chart, width, height, colors, textColor, mutedColor
34974
35446
  svg.append("line").attr("x1", xx).attr("x2", xx).attr("y1", m.top).attr("y2", m.top + plotH).attr("stroke", mutedColor).attr("stroke-opacity", t === 0 ? 0.6 : 0.2);
34975
35447
  svg.append("text").attr("class", "dgmo-tick").attr("x", xx).attr("y", m.top + plotH + 18).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", TICK_FONT).attr("font-family", FONT_FAMILY).text(fmtNum(t));
34976
35448
  }
34977
- const gen = d3line2().defined((p) => isFinite(p[1]) && p[1] >= yLo - 1 && p[1] <= yHi + 1).x((p) => x(p[0])).y((p) => y(p[1]));
35449
+ const inRange = (p) => isFinite(p[1]) && p[1] >= yLo - 1 && p[1] <= yHi + 1;
35450
+ if (chart.fill === true) {
35451
+ const solid = chart.fillMode === "solid";
35452
+ const areaGen = d3area2().defined(inRange).x((p) => x(p[0])).y0(y(Math.max(y.domain()[0], 0))).y1((p) => y(p[1]));
35453
+ for (const c of curves) {
35454
+ svg.append("path").attr("class", "dgmo-series-area").attr("d", areaGen(c.pts) ?? "").attr("fill", solid ? c.color : shapeFill(palette, c.color, isDark)).attr("stroke", "none").attr("data-series-name", c.name);
35455
+ }
35456
+ }
35457
+ const gen = d3line2().defined(inRange).x((p) => x(p[0])).y((p) => y(p[1]));
34978
35458
  for (const c of curves) {
34979
35459
  const path = svg.append("path").attr("d", gen(c.pts) ?? "").attr("fill", "none").attr("stroke", c.color).attr("stroke-width", 1.75).attr("stroke-linejoin", "round").attr("data-series-name", c.name);
34980
35460
  tagDatum(path, { line: c.line, key: c.name, name: c.name, color: c.color });
@@ -34992,6 +35472,7 @@ var SAMPLES;
34992
35472
  var init_function = __esm({
34993
35473
  "src/charts-d3/function.ts"() {
34994
35474
  "use strict";
35475
+ init_color_utils();
34995
35476
  init_fonts();
34996
35477
  init_expr_eval();
34997
35478
  init_shared();
@@ -35009,6 +35490,18 @@ __export(charts_d3_exports, {
35009
35490
  function supportsD3DataChart(type) {
35010
35491
  return D3_DATA_CHART_TYPES.has(type);
35011
35492
  }
35493
+ function inlineTitleFor(chart, hasTitle, textColor) {
35494
+ if (!hasTitle || !chart.legendInline || chart.noLegend || !INLINE_LEGEND_TYPES.has(chart.type)) {
35495
+ return void 0;
35496
+ }
35497
+ return {
35498
+ title: chart.title,
35499
+ ...chart.titleLineNumber !== void 0 && {
35500
+ titleLineNumber: chart.titleLineNumber
35501
+ },
35502
+ textColor
35503
+ };
35504
+ }
35012
35505
  function firstNonCommentToken(content) {
35013
35506
  for (const raw of content.split("\n")) {
35014
35507
  const l = raw.trim();
@@ -35056,7 +35549,8 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35056
35549
  const std = forceExtended ? null : parseChart(content, palette);
35057
35550
  if (std && !std.error && std.data.length > 0 && STANDARD.has(std.type)) {
35058
35551
  const hasTitle2 = !std.noTitle && !!std.title;
35059
- if (hasTitle2)
35552
+ const inlineTitle2 = inlineTitleFor(std, hasTitle2, textColor);
35553
+ if (hasTitle2 && !inlineTitle2)
35060
35554
  renderChartTitle(s, std.title, std.titleLineNumber, width, textColor);
35061
35555
  switch (std.type) {
35062
35556
  case "bar":
@@ -35070,7 +35564,8 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35070
35564
  isDark,
35071
35565
  textColor,
35072
35566
  mutedColor,
35073
- hasTitle2
35567
+ hasTitle2,
35568
+ inlineTitle2
35074
35569
  );
35075
35570
  return true;
35076
35571
  case "line":
@@ -35085,7 +35580,8 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35085
35580
  textColor,
35086
35581
  mutedColor,
35087
35582
  bgColor,
35088
- hasTitle2
35583
+ hasTitle2,
35584
+ inlineTitle2
35089
35585
  );
35090
35586
  return true;
35091
35587
  case "pie":
@@ -35108,7 +35604,8 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35108
35604
  palette,
35109
35605
  isDark,
35110
35606
  hasTitle2,
35111
- width
35607
+ width,
35608
+ inlineTitle2
35112
35609
  );
35113
35610
  renderRadar(
35114
35611
  s,
@@ -35142,7 +35639,8 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35142
35639
  if (ext.error || !EXTENDED.has(ext.type)) return false;
35143
35640
  const seriesColors2 = getSeriesColors(palette);
35144
35641
  const hasTitle = !ext.noTitle && !!ext.title;
35145
- if (hasTitle)
35642
+ const inlineTitle = inlineTitleFor(ext, hasTitle, textColor);
35643
+ if (hasTitle && !inlineTitle)
35146
35644
  renderChartTitle(s, ext.title, ext.titleLineNumber, width, textColor);
35147
35645
  switch (ext.type) {
35148
35646
  case "funnel":
@@ -35178,7 +35676,8 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35178
35676
  palette,
35179
35677
  isDark,
35180
35678
  hasTitle,
35181
- width
35679
+ width,
35680
+ inlineTitle
35182
35681
  );
35183
35682
  renderScatter(
35184
35683
  s,
@@ -35227,7 +35726,8 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35227
35726
  palette,
35228
35727
  isDark,
35229
35728
  hasTitle,
35230
- width
35729
+ width,
35730
+ inlineTitle
35231
35731
  );
35232
35732
  renderFunction(
35233
35733
  s,
@@ -35237,7 +35737,9 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35237
35737
  seriesColors2,
35238
35738
  textColor,
35239
35739
  mutedColor,
35240
- top
35740
+ top,
35741
+ palette,
35742
+ isDark
35241
35743
  );
35242
35744
  return true;
35243
35745
  }
@@ -35245,7 +35747,7 @@ function renderInto(s, content, palette, isDark, width, height, textColor, muted
35245
35747
  return false;
35246
35748
  }
35247
35749
  }
35248
- var STANDARD, EXTENDED, D3_DATA_CHART_TYPES;
35750
+ var STANDARD, EXTENDED, D3_DATA_CHART_TYPES, INLINE_LEGEND_TYPES;
35249
35751
  var init_charts_d3 = __esm({
35250
35752
  "src/charts-d3/index.ts"() {
35251
35753
  "use strict";
@@ -35276,6 +35778,13 @@ var init_charts_d3 = __esm({
35276
35778
  "function"
35277
35779
  ]);
35278
35780
  D3_DATA_CHART_TYPES = /* @__PURE__ */ new Set([...STANDARD, ...EXTENDED]);
35781
+ INLINE_LEGEND_TYPES = /* @__PURE__ */ new Set([
35782
+ "bar",
35783
+ "line",
35784
+ "radar",
35785
+ "scatter",
35786
+ "function"
35787
+ ]);
35279
35788
  }
35280
35789
  });
35281
35790
 
@@ -35355,7 +35864,7 @@ __export(renderer_exports, {
35355
35864
  renderEventLineForExport: () => renderEventLineForExport
35356
35865
  });
35357
35866
  import * as d3Selection6 from "d3-selection";
35358
- function renderEventLine(container, parsed, palette, isDark, onClickItem, exportDims, tagOverride) {
35867
+ function renderEventLine(container, parsed, palette, isDark, onClickItem, exportDims, tagOverride, nowDate) {
35359
35868
  if (parsed.events.length === 0) return;
35360
35869
  d3Selection6.select(container).selectAll(":not([data-d3-tooltip])").remove();
35361
35870
  const width = exportDims?.width ?? container.clientWidth;
@@ -35374,7 +35883,32 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
35374
35883
  const hasLegend = parsed.tagGroups.length > 0 && activeGroup !== null && !parsed.options.noLegend;
35375
35884
  const titleH = showTitle ? TITLE_AREA : 0;
35376
35885
  const legendH = hasLegend ? LEGEND_BAND : 0;
35377
- const topUsed = titleH + legendH;
35886
+ const inlineRequested = parsed.options.legendInline === true;
35887
+ const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
35888
+ {
35889
+ groups: parsed.tagGroups,
35890
+ position: {
35891
+ placement: "top-center",
35892
+ titleRelation: "inline-with-title"
35893
+ },
35894
+ mode: exportMode ? "export" : "preview"
35895
+ },
35896
+ { activeGroup: activeGroup ?? null },
35897
+ width
35898
+ ) : { width: 0, height: 0 };
35899
+ const header = layoutInlineHeader({
35900
+ requested: inlineRequested,
35901
+ title: parsed.title ?? "",
35902
+ hasLegend,
35903
+ legendWidth: legendExtent.width,
35904
+ legendHeight: legendExtent.height,
35905
+ containerWidth: width,
35906
+ titleBandHeight: titleH,
35907
+ legendReserve: legendH,
35908
+ titleBaselineY: TITLE_Y,
35909
+ titleFontSize: TITLE_FONT_SIZE
35910
+ });
35911
+ const topUsed = header.inline ? titleH : titleH + legendH;
35378
35912
  const eraByName = new Map(parsed.eras.map((e) => [e.name, e]));
35379
35913
  const collapsedSet = new Set(
35380
35914
  parsed.eras.filter((e) => e.collapsed).map((e) => e.name)
@@ -35717,14 +36251,17 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
35717
36251
  }
35718
36252
  if (!exportMode) svg.append("style").text(HOVER_CSS);
35719
36253
  if (showTitle) {
35720
- const t = svg.append("text").attr("class", "chart-title").attr("x", contentW / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("data-line-number", parsed.titleLineNumber ?? 1).text(parsed.title);
36254
+ const t = svg.append("text").attr("class", "chart-title").attr("x", header.inline ? header.titleX : contentW / 2).attr("y", TITLE_Y).attr("text-anchor", header.inline ? header.titleAnchor : "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("data-line-number", parsed.titleLineNumber ?? 1).text(parsed.title);
35721
36255
  if (onClickItem && parsed.titleLineNumber) {
35722
36256
  const ln = parsed.titleLineNumber;
35723
36257
  t.style("cursor", "pointer").on("click", () => onClickItem(ln));
35724
36258
  }
35725
36259
  }
35726
36260
  if (hasLegend) {
35727
- const legendG = svg.append("g").attr("transform", `translate(0, ${titleH})`);
36261
+ const legendG = svg.append("g").attr(
36262
+ "transform",
36263
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${titleH})`
36264
+ );
35728
36265
  const groups = parsed.tagGroups.map((g) => ({
35729
36266
  name: g.name,
35730
36267
  entries: g.entries.map((e) => ({
@@ -35738,7 +36275,14 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
35738
36275
  isDark,
35739
36276
  width: contentW,
35740
36277
  mode: exportMode ? "export" : "preview",
35741
- activeGroup
36278
+ activeGroup,
36279
+ // Inline → left-origin so the wrapper's right-flush translate lands the
36280
+ // legend at the header's right edge; stacked → centered below the title
36281
+ // (== DEFAULT_POSITION, so the stacked output is byte-identical).
36282
+ position: {
36283
+ placement: "top-center",
36284
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
36285
+ }
35742
36286
  });
35743
36287
  if (!exportMode) addLegendEntryHitAreas(legendG);
35744
36288
  }
@@ -35964,6 +36508,111 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
35964
36508
  }
35965
36509
  }
35966
36510
  }
36511
+ if (parsed.now && scaled) {
36512
+ let nowValue = parsed.now.dateValue;
36513
+ if (parsed.now.computed) {
36514
+ const d = nowDate ?? /* @__PURE__ */ new Date();
36515
+ const iso = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
36516
+ const v = parseTimelineDate(iso);
36517
+ nowValue = Number.isFinite(v) ? v : null;
36518
+ }
36519
+ const dated = placed.filter((p) => p.kind !== "era" && !p.future && p.dateValue !== null).sort((a, b) => a.dateValue - b.dateValue);
36520
+ let nowX = nowValue === null ? null : valueToSpineX(nowValue, dated);
36521
+ const lastReal = dated[dated.length - 1];
36522
+ if (nowX !== null && nowValue !== null && lastReal && nowValue > lastReal.dateValue) {
36523
+ const trailing = placed.filter((p) => p.future && !p.futureSpan).map((p) => p.x);
36524
+ if (trailing.length) nowX = Math.min(...trailing);
36525
+ }
36526
+ if (nowX !== null) {
36527
+ const base = themeBaseBg(palette, isDark);
36528
+ const nowColor = palette.destructive;
36529
+ const fillMode = parsed.options.fillMode;
36530
+ const pillFill = shapeFill(palette, nowColor, isDark, { mode: fillMode });
36531
+ const pillStroke = fillMode === "solid" ? "none" : fillMode === "outline" ? nowColor : mix(nowColor, base, 55);
36532
+ const pillStrokeW = fillMode === "solid" ? 0 : fillMode === "outline" ? 1.3 : 1;
36533
+ const pillText = fillMode === "solid" ? contrastText(
36534
+ pillFill,
36535
+ palette.textOnFillLight,
36536
+ palette.textOnFillDark
36537
+ ) : nowColor;
36538
+ const label = parsed.now.label;
36539
+ const pillH = 18;
36540
+ const pillW = Math.max(28, label.length * 6.6 + 16);
36541
+ const PIN_GAP = 6;
36542
+ const xHalf = pillW / 2 + 4;
36543
+ const xLo = nowX - xHalf;
36544
+ const xHi = nowX + xHalf;
36545
+ const boxes = geo.map((g) => ({
36546
+ side: g.p.side,
36547
+ top: g.top,
36548
+ bot: g.top + g.p.cardH,
36549
+ left: g.p.left,
36550
+ right: g.p.left + CARD_W
36551
+ }));
36552
+ const overlapsX = (b) => b.left < xHi && b.right > xLo;
36553
+ const cands = [];
36554
+ const addAbove = (pillBottom) => {
36555
+ const y0 = pillBottom - pillH;
36556
+ if (y0 > topUsed + 1)
36557
+ cands.push({ y0, y1: pillBottom, stem: spineY - pillBottom });
36558
+ };
36559
+ const addBelow = (pillTop) => {
36560
+ const y1 = pillTop + pillH;
36561
+ if (y1 < totalH - 1)
36562
+ cands.push({ y0: pillTop, y1, stem: pillTop - spineY });
36563
+ };
36564
+ addAbove(spineY - LEADER_ABOVE / 2 + pillH / 2);
36565
+ addBelow(spineY + LEADER_BELOW / 2 - pillH / 2);
36566
+ for (const b of boxes) {
36567
+ if (!overlapsX(b)) continue;
36568
+ if (b.side === "above") addAbove(b.top - PIN_GAP);
36569
+ else addBelow(b.bot + PIN_GAP);
36570
+ }
36571
+ const isClear = (c) => !boxes.some((b) => overlapsX(b) && b.bot > c.y0 && b.top < c.y1);
36572
+ const byStem = (a, b) => a.stem - b.stem;
36573
+ const slot = cands.filter(isClear).sort(byStem)[0] ?? cands.sort(byStem)[0] ?? {
36574
+ y0: spineY - PIN_GAP - pillH,
36575
+ y1: spineY - PIN_GAP,
36576
+ stem: PIN_GAP + pillH
36577
+ };
36578
+ const above = slot.y1 <= spineY;
36579
+ const stemFrom = above ? spineY - DOT_R : spineY + DOT_R;
36580
+ const stemTo = above ? slot.y1 : slot.y0;
36581
+ const pillCX = Math.max(
36582
+ pillW / 2 + 2,
36583
+ Math.min(nowX, contentW - pillW / 2 - 2)
36584
+ );
36585
+ const nowG = svg.append("g").attr("class", "evt-now").attr("data-now-x", nowX.toFixed(2));
36586
+ const FADE_SPAN = Math.min(LEADER_ABOVE, LEADER_BELOW);
36587
+ const ruleTop = spineY - FADE_SPAN;
36588
+ const ruleBot = spineY + FADE_SPAN;
36589
+ const gradId = `evt-now-fade-${nowX.toFixed(0)}`;
36590
+ const grad = nowG.append("defs").append("linearGradient").attr("id", gradId).attr("gradientUnits", "userSpaceOnUse").attr("x1", 0).attr("y1", ruleTop).attr("x2", 0).attr("y2", ruleBot);
36591
+ grad.append("stop").attr("offset", "0%").attr("stop-color", nowColor).attr("stop-opacity", 0);
36592
+ grad.append("stop").attr("offset", "50%").attr("stop-color", nowColor).attr("stop-opacity", 0.9);
36593
+ grad.append("stop").attr("offset", "100%").attr("stop-color", nowColor).attr("stop-opacity", 0);
36594
+ nowG.append("line").attr("x1", nowX).attr("y1", ruleTop).attr("x2", nowX).attr("y2", ruleBot).attr("stroke", `url(#${gradId})`).attr("stroke-width", 1.6).attr("stroke-dasharray", "1 4").attr("stroke-linecap", "round");
36595
+ nowG.append("path").attr("d", `M${nowX} ${spineY - 5} l5 5 l-5 5 l-5 -5 z`).attr("fill", nowColor);
36596
+ nowG.append("line").attr("x1", nowX).attr("y1", stemFrom).attr("x2", nowX).attr("y2", stemTo).attr("stroke", nowColor).attr("stroke-width", 1.6);
36597
+ nowG.append("rect").attr("x", pillCX - pillW / 2).attr("y", slot.y0).attr("width", pillW).attr("height", pillH).attr("rx", pillH / 2).attr("fill", pillFill).attr("stroke", pillStroke).attr("stroke-width", pillStrokeW);
36598
+ nowG.append("text").attr("x", pillCX).attr("y", slot.y0 + pillH / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-family", FONT_FAMILY).attr("font-size", 10.5).attr("font-weight", 600).attr("fill", pillText).text(label);
36599
+ if (!exportMode) {
36600
+ const hoverRule = nowG.append("line").attr("class", "evt-now-rule-full").attr("x1", nowX).attr("y1", Math.max(topUsed, 2)).attr("x2", nowX).attr("y2", totalH - 2).attr("stroke", nowColor).attr("stroke-width", 1.4).attr("stroke-dasharray", "1 4").attr("stroke-linecap", "round").attr("opacity", 0).style("transition", "opacity 120ms ease").style("pointer-events", "none");
36601
+ hoverRule.lower();
36602
+ const nowNode = nowG.node();
36603
+ if (nowNode) {
36604
+ nowNode.addEventListener(
36605
+ "mouseenter",
36606
+ () => hoverRule.attr("opacity", 0.5)
36607
+ );
36608
+ nowNode.addEventListener(
36609
+ "mouseleave",
36610
+ () => hoverRule.attr("opacity", 0)
36611
+ );
36612
+ }
36613
+ }
36614
+ }
36615
+ }
35967
36616
  if (!exportMode) {
35968
36617
  const root = svg.node();
35969
36618
  wireEventLineHover(root);
@@ -35978,7 +36627,8 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
35978
36627
  isDark,
35979
36628
  onClickItem,
35980
36629
  exportDims,
35981
- tagOverride
36630
+ tagOverride,
36631
+ nowDate
35982
36632
  )
35983
36633
  );
35984
36634
  applyEvtHidden(container, root);
@@ -36186,7 +36836,7 @@ function wireEventLineHover(root) {
36186
36836
  if (!to || !root.contains(to)) applyEvtFocus(root, readPin(root));
36187
36837
  });
36188
36838
  }
36189
- function renderEventLineForExport(container, parsed, palette, isDark, exportDims, tagOverride) {
36839
+ function renderEventLineForExport(container, parsed, palette, isDark, exportDims, tagOverride, nowDate) {
36190
36840
  renderEventLine(
36191
36841
  container,
36192
36842
  parsed,
@@ -36194,9 +36844,27 @@ function renderEventLineForExport(container, parsed, palette, isDark, exportDims
36194
36844
  isDark,
36195
36845
  void 0,
36196
36846
  exportDims,
36197
- tagOverride
36847
+ tagOverride,
36848
+ nowDate
36198
36849
  );
36199
36850
  }
36851
+ function valueToSpineX(v, dated) {
36852
+ if (!dated.length) return null;
36853
+ const first = dated[0];
36854
+ const last = dated[dated.length - 1];
36855
+ if (v <= first.dateValue) return first.x;
36856
+ if (v >= last.dateValue) return last.x;
36857
+ for (let i = 0; i < dated.length - 1; i++) {
36858
+ const a = dated[i];
36859
+ const b = dated[i + 1];
36860
+ if (v >= a.dateValue && v <= b.dateValue) {
36861
+ const span = b.dateValue - a.dateValue;
36862
+ if (span <= 0) return a.x;
36863
+ return a.x + (v - a.dateValue) / span * (b.x - a.x);
36864
+ }
36865
+ }
36866
+ return last.x;
36867
+ }
36200
36868
  function axisBreakPaths(cx, cy) {
36201
36869
  const HUMPS = 2;
36202
36870
  const STEPS = 48;
@@ -36268,7 +36936,10 @@ var init_renderer2 = __esm({
36268
36936
  init_text_measure();
36269
36937
  init_wrapped_desc();
36270
36938
  init_legend_integration();
36939
+ init_legend_layout();
36940
+ init_inline_header();
36271
36941
  init_renderer();
36942
+ init_parser();
36272
36943
  init_tag_groups();
36273
36944
  CARD_W = 210;
36274
36945
  H_MARGIN = 58;
@@ -36718,12 +37389,9 @@ function renderVersionControl(container, parsed, palette, isDark, onClickItem, e
36718
37389
  const pos = /* @__PURE__ */ new Map();
36719
37390
  const placements = [];
36720
37391
  let contentW, contentH, gutter, cross0, captionY = 0;
36721
- const cos = Math.cos(ANG * Math.PI / 180);
36722
- const sin = Math.sin(ANG * Math.PI / 180);
36723
37392
  if (!TB) {
36724
- const maxName = Math.max(4, ...parsed.branches.map((b) => b.name.length));
36725
- gutter = maxName * 7.5 + 18;
36726
- const along0 = gutter + 34, step = 86, laneGap = 54;
37393
+ gutter = 14;
37394
+ const along0 = gutter + 20, step = 88, laneGap = 50;
36727
37395
  cross0 = 16 + maxStack * STACK + 14;
36728
37396
  for (const n of nodes)
36729
37397
  pos.set(n.key, {
@@ -36731,17 +37399,34 @@ function renderVersionControl(container, parsed, palette, isDark, onClickItem, e
36731
37399
  y: cross0 + n.lane * laneGap
36732
37400
  });
36733
37401
  const lanesBottom = cross0 + (laneCount - 1) * laneGap;
36734
- const bandY = lanesBottom + 26;
36735
- for (const n of nodes) {
37402
+ const bandTop = lanesBottom + LEADER_GAP;
37403
+ const shelfGap = 6;
37404
+ const rowRight = [];
37405
+ const sorted = [...nodes].sort(
37406
+ (a, b) => pos.get(a.key).x - pos.get(b.key).x
37407
+ );
37408
+ for (const n of sorted) {
36736
37409
  const p = pos.get(n.key);
37410
+ const w = label.get(n.key).lw + SHELF_PAD * 2;
37411
+ const left = p.x - w / 2;
37412
+ let row = rowRight.length;
37413
+ for (let l = 0; l < rowRight.length; l++)
37414
+ if (rowRight[l] + shelfGap <= left) {
37415
+ row = l;
37416
+ break;
37417
+ }
37418
+ const top = bandTop + row * (SHELF_H + ROW_GAP);
37419
+ rowRight[row] = left + w;
36737
37420
  placements.push({
36738
37421
  node: n,
36739
37422
  kind: "msg",
36740
- diag: true,
36741
37423
  x: p.x,
36742
- y: bandY,
37424
+ y: top,
36743
37425
  anchor: "start",
36744
- leader: { x1: p.x, y1: p.y + DOT, x2: p.x, y2: bandY - 3 }
37426
+ shelfLeft: Math.max(4, left),
37427
+ shelfTop: top,
37428
+ shelfW: w,
37429
+ leader: { x1: p.x, y1: p.y + DOT, x2: p.x, y2: top }
36745
37430
  });
36746
37431
  const stack = above.get(n.key) ?? [];
36747
37432
  stack.forEach(
@@ -36757,6 +37442,7 @@ function renderVersionControl(container, parsed, palette, isDark, onClickItem, e
36757
37442
  })
36758
37443
  );
36759
37444
  }
37445
+ const rowCount = rowRight.length;
36760
37446
  for (const b of parsed.branches) {
36761
37447
  if (!b.ab || b.ab.ahead + b.ab.behind === 0 || b.tip == null) continue;
36762
37448
  const tip = pos.get(b.tip);
@@ -36770,10 +37456,12 @@ function renderVersionControl(container, parsed, palette, isDark, onClickItem, e
36770
37456
  });
36771
37457
  }
36772
37458
  contentW = Math.max(
36773
- along0 + (K - 1) * step + DOT + 70,
36774
- ...[...pos.entries()].map(([k, p]) => p.x + label.get(k).lw * cos)
36775
- ) + 18;
36776
- contentH = bandY + maxLW * sin + 12 * cos + 16;
37459
+ along0 + (K - 1) * step + DOT + 40,
37460
+ ...[...pos.entries()].map(
37461
+ ([k, p]) => p.x + label.get(k).lw / 2 + SHELF_PAD + 8
37462
+ )
37463
+ ) + 8;
37464
+ contentH = bandTop + rowCount * (SHELF_H + ROW_GAP) + 8;
36777
37465
  if (parsed.notes.length) {
36778
37466
  captionY = contentH + 4;
36779
37467
  contentH += 8 + parsed.notes.length * 16 + 8;
@@ -36793,7 +37481,6 @@ function renderVersionControl(container, parsed, palette, isDark, onClickItem, e
36793
37481
  placements.push({
36794
37482
  node: n,
36795
37483
  kind: "msg",
36796
- diag: false,
36797
37484
  x: bandX,
36798
37485
  y: p.y,
36799
37486
  anchor: "start",
@@ -36818,10 +37505,16 @@ function renderVersionControl(container, parsed, palette, isDark, onClickItem, e
36818
37505
  }
36819
37506
  const showTitle = !!parsed.title;
36820
37507
  const titleH = showTitle ? TITLE_AREA2 : 0;
36821
- const W3 = Math.round(contentW);
36822
- const H2 = Math.round(contentH + titleH);
37508
+ const baseW = contentW;
37509
+ const baseH = contentH + titleH;
36823
37510
  const exportMode = !!exportDims;
36824
- const svg = d3Selection8.select(container).append("svg").attr("width", W3).attr("height", H2).attr("viewBox", `0 0 ${W3} ${H2}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY);
37511
+ const paneW = exportMode ? 0 : container.clientWidth || 0;
37512
+ const paneH = exportMode ? 0 : container.clientHeight || 0;
37513
+ const W3 = Math.round(Math.max(baseW, paneW));
37514
+ const H2 = Math.round(Math.max(baseH, paneH));
37515
+ const vw = exportMode ? W3 : paneW || W3;
37516
+ const vh = exportMode ? H2 : paneH || H2;
37517
+ const svg = d3Selection8.select(container).append("svg").attr("width", vw).attr("height", vh).attr("viewBox", `0 0 ${W3} ${H2}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY);
36825
37518
  svg.append("rect").attr("width", W3).attr("height", H2).attr("fill", palette.bg);
36826
37519
  if (showTitle) {
36827
37520
  const t = svg.append("text").attr("class", "chart-title").attr("x", W3 / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("data-line-number", parsed.titleLineNumber ?? 1).text(parsed.title);
@@ -36841,7 +37534,6 @@ function renderVersionControl(container, parsed, palette, isDark, onClickItem, e
36841
37534
  label,
36842
37535
  colorOf2,
36843
37536
  byKey,
36844
- gutter,
36845
37537
  cross0,
36846
37538
  captionY,
36847
37539
  onClickItem && !exportMode ? onClickItem : void 0
@@ -36853,7 +37545,7 @@ function abText(ab) {
36853
37545
  if (ab.behind) a.push("\u2193" + ab.behind);
36854
37546
  return a.join(" ");
36855
37547
  }
36856
- function drawGraph(g, parsed, palette, TB, pos, placements, label, colorOf2, byKey, gutter, cross0, captionY, onClickItem) {
37548
+ function drawGraph(g, parsed, palette, TB, pos, placements, label, colorOf2, byKey, cross0, captionY, onClickItem) {
36857
37549
  const nodes = parsed.nodes;
36858
37550
  const P = (k) => pos.get(k);
36859
37551
  const branchCorner = (par, n) => TB ? { x: P(n.key).x, y: P(par.key).y } : { x: P(par.key).x, y: P(n.key).y };
@@ -36877,17 +37569,17 @@ function drawGraph(g, parsed, palette, TB, pos, placements, label, colorOf2, byK
36877
37569
  const N = P(n.key);
36878
37570
  if (n.prev != null) {
36879
37571
  const a = P(n.prev);
36880
- const e = g.append("line").attr("x1", a.x).attr("y1", a.y).attr("x2", N.x).attr("y2", N.y).attr("stroke", c).attr("stroke-width", LINE).attr("stroke-linecap", "round").attr("opacity", o);
37572
+ const e = g.append("line").attr("x1", a.x).attr("y1", a.y).attr("x2", N.x).attr("y2", N.y).attr("stroke", c).attr("stroke-width", LINE).attr("stroke-linecap", "round").attr("data-branch", n.branch).attr("opacity", o);
36881
37573
  if (n.ghost) e.attr("stroke-dasharray", "5 5");
36882
37574
  }
36883
37575
  if (n.parent != null) {
36884
37576
  const par = byKey(n.parent), cc = branchCorner(par, n), pp = P(par.key);
36885
- const e = g.append("path").attr("d", roundedL(pp.x, pp.y, cc.x, cc.y, N.x, N.y, CORNER)).attr("fill", "none").attr("stroke", c).attr("stroke-width", LINE).attr("stroke-linecap", "round").attr("opacity", o);
37577
+ const e = g.append("path").attr("d", roundedL(pp.x, pp.y, cc.x, cc.y, N.x, N.y, CORNER)).attr("fill", "none").attr("stroke", c).attr("stroke-width", LINE).attr("stroke-linecap", "round").attr("data-branch", n.branch).attr("opacity", o);
36886
37578
  if (n.ghost) e.attr("stroke-dasharray", "5 5");
36887
37579
  }
36888
37580
  if (n.mergeFrom != null) {
36889
37581
  const s = byKey(n.mergeFrom), cc = mergeCorner(s, n), sp = P(s.key);
36890
- g.append("path").attr("d", roundedL(sp.x, sp.y, cc.x, cc.y, N.x, N.y, CORNER)).attr("fill", "none").attr("stroke", colorOf2(s)).attr("stroke-width", LINE).attr("stroke-linecap", "round").attr("opacity", 0.92 * o);
37582
+ g.append("path").attr("d", roundedL(sp.x, sp.y, cc.x, cc.y, N.x, N.y, CORNER)).attr("fill", "none").attr("stroke", colorOf2(s)).attr("stroke-width", LINE).attr("stroke-linecap", "round").attr("data-branch", s.branch).attr("opacity", 0.92 * o);
36891
37583
  }
36892
37584
  if (n.cherryFrom != null)
36893
37585
  dlink(byKey(n.cherryFrom), n, colorOf2(byKey(n.cherryFrom)), false);
@@ -36908,7 +37600,7 @@ function drawGraph(g, parsed, palette, TB, pos, placements, label, colorOf2, byK
36908
37600
  if (TB) {
36909
37601
  g.append("text").attr("x", fp.x).attr("y", 14).attr("text-anchor", "middle").attr("font-size", 12.5).attr("font-weight", 800).attr("fill", col).text(b.name);
36910
37602
  } else {
36911
- g.append("text").attr("x", gutter).attr("y", fp.y + 4).attr("text-anchor", "end").attr("font-size", 12.5).attr("font-weight", 800).attr("fill", col).text(b.name);
37603
+ g.append("text").attr("x", fp.x - DOT - 1).attr("y", fp.y - DOT - 7).attr("text-anchor", "start").attr("font-size", 12.5).attr("font-weight", 800).attr("fill", col).attr("data-branch", b.name).text(b.name);
36912
37604
  }
36913
37605
  }
36914
37606
  }
@@ -36962,11 +37654,12 @@ function drawGraph(g, parsed, palette, TB, pos, placements, label, colorOf2, byK
36962
37654
  }
36963
37655
  g.append("text").attr("x", cx).attr("y", cy + 4).attr("text-anchor", "middle").attr("font-size", 10.5).attr("font-weight", weight).attr("fill", textColor).text(text);
36964
37656
  };
37657
+ let shelfSeq = 0;
36965
37658
  for (const pl of placements) {
36966
37659
  const n = pl.node;
36967
37660
  const c = colorOf2(n);
36968
37661
  if (pl.leader) {
36969
- g.append("line").attr("x1", pl.leader.x1).attr("y1", pl.leader.y1).attr("x2", pl.leader.x2).attr("y2", pl.leader.y2).attr("stroke", c).attr("stroke-width", 1).attr("opacity", 0.34 * gop(n));
37662
+ g.append("line").attr("x1", pl.leader.x1).attr("y1", pl.leader.y1).attr("x2", pl.leader.x2).attr("y2", pl.leader.y2).attr("stroke", c).attr("stroke-width", 1.5).attr("stroke-linecap", "round").attr("data-branch", n.branch).attr("opacity", 0.55 * gop(n));
36970
37663
  }
36971
37664
  if (pl.kind === "tag") pill(pl.x, pl.y, pl.w, pl.label, c, palette.bg);
36972
37665
  else if (pl.kind === "head")
@@ -36992,7 +37685,22 @@ function drawGraph(g, parsed, palette, TB, pos, placements, label, colorOf2, byK
36992
37685
  const L = label.get(n.key);
36993
37686
  const gh = n.ghost;
36994
37687
  const mfill = gh ? palette.textMuted : c;
36995
- const t = pl.diag ? g.append("text").attr("transform", `translate(${pl.x},${pl.y}) rotate(${ANG})`).attr("font-size", 12).attr("opacity", gh ? 0.55 : 1) : g.append("text").attr("x", pl.x).attr("y", pl.y + 4).attr("text-anchor", pl.anchor).attr("font-size", 12).attr("opacity", gh ? 0.55 : 1);
37688
+ let tx = pl.x;
37689
+ let ty = pl.y + 4;
37690
+ let anchor = pl.anchor;
37691
+ if (pl.shelfW != null) {
37692
+ const left = pl.shelfLeft;
37693
+ const top = pl.shelfTop;
37694
+ const w = pl.shelfW;
37695
+ const clipId = `dgmo-vc-shelf-${shelfSeq++}`;
37696
+ g.append("clipPath").attr("id", clipId).append("rect").attr("x", left).attr("y", top).attr("width", w).attr("height", SHELF_H).attr("rx", SHELF_EDGE2);
37697
+ g.append("rect").attr("x", left).attr("y", top).attr("width", w).attr("height", SHELF_H).attr("rx", SHELF_EDGE2).attr("fill", gh ? palette.bg : mix(c, palette.bg, SHELF_TINT2)).attr("data-branch", n.branch).attr("opacity", gh ? 0.5 : 1);
37698
+ g.append("rect").attr("x", left).attr("y", top).attr("width", w).attr("height", SHELF_LIP2).attr("clip-path", `url(#${clipId})`).attr("fill", c).attr("data-branch", n.branch).attr("opacity", gh ? 0.4 : 1);
37699
+ tx = left + SHELF_PAD;
37700
+ ty = top + SHELF_H / 2 + 4;
37701
+ anchor = "start";
37702
+ }
37703
+ const t = g.append("text").attr("x", tx).attr("y", ty).attr("text-anchor", anchor).attr("font-size", 12).attr("data-branch", n.branch).attr("opacity", gh ? 0.6 : 1);
36996
37704
  if (L.sha)
36997
37705
  t.append("tspan").attr("font-family", "ui-monospace, monospace").attr("font-size", 11).attr("fill", palette.textMuted).text(L.sha + " ");
36998
37706
  t.append("tspan").attr("fill", mfill).attr("font-weight", n.kind === "cherry" ? 400 : 700).attr("font-style", n.kind === "cherry" ? "italic" : "normal").text(L.msg);
@@ -37018,7 +37726,7 @@ function renderVersionControlForExport(container, parsed, palette, isDark, expor
37018
37726
  exportDims
37019
37727
  );
37020
37728
  }
37021
- var LINE, DOT, CORNER, ANG, STACK, CIRCLED, TITLE_AREA2;
37729
+ var LINE, DOT, CORNER, STACK, CIRCLED, TITLE_AREA2, SHELF_TINT2, SHELF_EDGE2, SHELF_LIP2, SHELF_PAD, SHELF_H, LEADER_GAP, ROW_GAP;
37022
37730
  var init_renderer4 = __esm({
37023
37731
  "src/version-control/renderer.ts"() {
37024
37732
  "use strict";
@@ -37026,13 +37734,19 @@ var init_renderer4 = __esm({
37026
37734
  init_title_constants();
37027
37735
  init_color_utils();
37028
37736
  init_colors();
37029
- LINE = 5.5;
37030
- DOT = 8;
37031
- CORNER = 13;
37032
- ANG = 32;
37737
+ LINE = 3;
37738
+ DOT = 5.5;
37739
+ CORNER = 10;
37033
37740
  STACK = 19;
37034
37741
  CIRCLED = "\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468";
37035
37742
  TITLE_AREA2 = 40;
37743
+ SHELF_TINT2 = 13;
37744
+ SHELF_EDGE2 = 4;
37745
+ SHELF_LIP2 = 2;
37746
+ SHELF_PAD = 8;
37747
+ SHELF_H = 21;
37748
+ LEADER_GAP = 22;
37749
+ ROW_GAP = 7;
37036
37750
  }
37037
37751
  });
37038
37752
 
@@ -39319,7 +40033,33 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
39319
40033
  ) : LEGEND_HEIGHT
39320
40034
  );
39321
40035
  const legendReserveH = fixedLegend ? sLegendHeight + sLegendFixedGap : 0;
39322
- const fixedReserveTop = fixedTitleH + legendReserveH;
40036
+ const inlineRequested = legendInlineRequested(parsed.options);
40037
+ const legendExtent = inlineRequested && fixedTitle ? getLegendExtent(
40038
+ {
40039
+ groups: parsed.tagGroups,
40040
+ position: {
40041
+ placement: "top-center",
40042
+ titleRelation: "inline-with-title"
40043
+ },
40044
+ mode: exportMode ? "export" : "preview",
40045
+ capsulePillAddonWidth: LEGEND_EYE_SIZE + LEGEND_EYE_GAP
40046
+ },
40047
+ { activeGroup: activeTagGroup ?? null },
40048
+ width
40049
+ ) : { width: 0, height: 0 };
40050
+ const header = layoutInlineHeader({
40051
+ requested: inlineRequested,
40052
+ title: parsed.title ?? "",
40053
+ hasLegend: fixedTitle,
40054
+ legendWidth: legendExtent.width,
40055
+ legendHeight: legendExtent.height,
40056
+ containerWidth: width,
40057
+ titleBandHeight: fixedTitleH,
40058
+ legendReserve: legendReserveH,
40059
+ titleBaselineY: sDiagramPadding + sTitleFontSize,
40060
+ titleFontSize: sTitleFontSize
40061
+ });
40062
+ const fixedReserveTop = header.inline ? fixedTitleH : fixedTitleH + legendReserveH;
39323
40063
  const fixedReserveBottom = 0;
39324
40064
  const titleOffset = !fixedTitle && showTitle ? sTitleHeight : 0;
39325
40065
  const diagramW = layout.width;
@@ -39521,7 +40261,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
39521
40261
  );
39522
40262
  }
39523
40263
  if (fixedTitle) {
39524
- const titleEl = svg.append("text").attr("x", width / 2).attr("y", sDiagramPadding + sTitleFontSize).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("class", "sitemap-title chart-title").style("font-family", FONT_FAMILY);
40264
+ const titleEl = svg.append("text").attr("x", header.titleX).attr("y", sDiagramPadding + sTitleFontSize).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("class", "sitemap-title chart-title").style("font-family", FONT_FAMILY);
39525
40265
  if (parsed.titleLineNumber) {
39526
40266
  titleEl.attr("data-line-number", parsed.titleLineNumber);
39527
40267
  if (onClickItem) {
@@ -39531,7 +40271,10 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
39531
40271
  titleEl.text(parsed.title);
39532
40272
  }
39533
40273
  if (fixedLegend) {
39534
- const legendParent = svg.append("g").attr("class", "sitemap-legend-fixed").attr("transform", `translate(0, ${sDiagramPadding + fixedTitleH})`);
40274
+ const legendParent = svg.append("g").attr("class", "sitemap-legend-fixed").attr(
40275
+ "transform",
40276
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${sDiagramPadding + fixedTitleH})`
40277
+ );
39535
40278
  if (activeTagGroup) {
39536
40279
  legendParent.attr("data-legend-active", tagAttrKey(activeTagGroup));
39537
40280
  }
@@ -39543,11 +40286,12 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
39543
40286
  activeTagGroup,
39544
40287
  width,
39545
40288
  hiddenAttributes,
39546
- exportMode
40289
+ exportMode,
40290
+ header.inline
39547
40291
  );
39548
40292
  }
39549
40293
  }
39550
- function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fixedWidth, hiddenAttributes, exportMode) {
40294
+ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fixedWidth, hiddenAttributes, exportMode, inlineHeader) {
39551
40295
  if (legendGroups.length === 0) return;
39552
40296
  const groups = legendGroups.map((g) => ({
39553
40297
  name: g.name,
@@ -39563,6 +40307,10 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
39563
40307
  groups,
39564
40308
  activeGroup: activeTagGroup ?? null,
39565
40309
  mode: exportMode ? "export" : "preview",
40310
+ position: {
40311
+ placement: "top-center",
40312
+ titleRelation: inlineHeader ? "inline-with-title" : "below-title"
40313
+ },
39566
40314
  capsulePillAddonWidth: eyeAddonWidth,
39567
40315
  palette,
39568
40316
  isDark,
@@ -39640,6 +40388,7 @@ var init_renderer6 = __esm({
39640
40388
  "use strict";
39641
40389
  init_tag_groups();
39642
40390
  init_parsing();
40391
+ init_inline_header();
39643
40392
  init_fonts();
39644
40393
  init_color_utils();
39645
40394
  init_inline_markdown();
@@ -40744,15 +41493,50 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
40744
41493
  const LEGEND_FIXED_GAP4 = 8;
40745
41494
  const sLegendFixedGap = ctx.aesthetic(LEGEND_FIXED_GAP4);
40746
41495
  const legendReserve = hasLegend ? sLegendHeight + sLegendFixedGap : 0;
41496
+ const isLegendExpanded = legendActive !== false;
41497
+ const legendGroups = [
41498
+ {
41499
+ name: LEGEND_GROUP_NAME,
41500
+ entries: legendEntries.map((entry) => ({
41501
+ value: entry.label,
41502
+ color: palette.colors[entry.colorKey]
41503
+ }))
41504
+ }
41505
+ ];
41506
+ const inlineRequested = legendInlineRequested(parsed.options);
41507
+ const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
41508
+ {
41509
+ groups: legendGroups,
41510
+ position: {
41511
+ placement: "top-center",
41512
+ titleRelation: "inline-with-title"
41513
+ },
41514
+ mode: exportMode ? "export" : "preview"
41515
+ },
41516
+ { activeGroup: isLegendExpanded ? LEGEND_GROUP_NAME : null },
41517
+ width
41518
+ ) : { width: 0, height: 0 };
41519
+ const header = layoutInlineHeader({
41520
+ requested: inlineRequested,
41521
+ title: parsed.title ?? "",
41522
+ hasLegend,
41523
+ legendWidth: legendExtent.width,
41524
+ legendHeight: legendExtent.height,
41525
+ containerWidth: width,
41526
+ titleBandHeight: titleHeight,
41527
+ legendReserve,
41528
+ titleBaselineY: sTitleY,
41529
+ titleFontSize: sTitleFontSize
41530
+ });
40747
41531
  const diagramW = layout.width;
40748
41532
  const diagramH = layout.height;
40749
- const availH = height - titleHeight - legendReserve;
41533
+ const availH = height - titleHeight - (header.inline ? 0 : legendReserve);
40750
41534
  const scaleX = (width - sDiagramPadding * 2) / diagramW;
40751
41535
  const scaleY = (availH - sDiagramPadding * 2) / diagramH;
40752
41536
  const scale = Math.min(MAX_SCALE3, scaleX, scaleY);
40753
41537
  const scaledW = diagramW * scale;
40754
41538
  const offsetX = (width - scaledW) / 2;
40755
- const offsetY = titleHeight + legendReserve + sDiagramPadding;
41539
+ const offsetY = titleHeight + (header.inline ? 0 : legendReserve) + sDiagramPadding;
40756
41540
  const svg = d3Selection12.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY);
40757
41541
  if (ctx.isBelowFloor) {
40758
41542
  svg.attr("width", "100%");
@@ -40769,7 +41553,7 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
40769
41553
  defs.append("marker").attr("id", "cd-arrow-depend").attr("viewBox", `0 0 ${AW} ${AH}`).attr("refX", AW).attr("refY", AH / 2).attr("markerWidth", AW).attr("markerHeight", AH).attr("orient", "auto").append("polyline").attr("points", `0,0 ${AW},${AH / 2} 0,${AH}`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
40770
41554
  defs.append("marker").attr("id", "cd-arrow-assoc").attr("viewBox", `0 0 ${AW} ${AH}`).attr("refX", AW).attr("refY", AH / 2).attr("markerWidth", AW).attr("markerHeight", AH).attr("orient", "auto").append("polyline").attr("points", `0,0 ${AW},${AH / 2} 0,${AH}`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
40771
41555
  if (showTitle) {
40772
- const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", sTitleY).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).style(
41556
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", sTitleY).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).style(
40773
41557
  "cursor",
40774
41558
  onClickItem && parsed.titleLineNumber ? "pointer" : "default"
40775
41559
  ).text(parsed.title);
@@ -40784,22 +41568,19 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
40784
41568
  }
40785
41569
  }
40786
41570
  }
40787
- const isLegendExpanded = legendActive !== false;
40788
41571
  if (hasLegend) {
40789
- const legendGroups = [
40790
- {
40791
- name: LEGEND_GROUP_NAME,
40792
- entries: legendEntries.map((entry) => ({
40793
- value: entry.label,
40794
- color: palette.colors[entry.colorKey]
40795
- }))
40796
- }
40797
- ];
40798
- const legendG = svg.append("g").attr("class", "cd-legend").attr("transform", `translate(0,${titleHeight})`);
41572
+ const legendG = svg.append("g").attr("class", "cd-legend").attr(
41573
+ "transform",
41574
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0,${titleHeight})`
41575
+ );
40799
41576
  renderIntegratedLegend(legendG, {
40800
41577
  groups: legendGroups,
40801
41578
  activeGroup: isLegendExpanded ? LEGEND_GROUP_NAME : null,
40802
41579
  mode: exportMode ? "export" : "preview",
41580
+ position: {
41581
+ placement: "top-center",
41582
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
41583
+ },
40803
41584
  palette,
40804
41585
  isDark,
40805
41586
  width
@@ -40998,6 +41779,8 @@ var init_renderer8 = __esm({
40998
41779
  init_export_container();
40999
41780
  init_legend_constants();
41000
41781
  init_legend_integration();
41782
+ init_legend_layout();
41783
+ init_inline_header();
41001
41784
  init_title_constants();
41002
41785
  init_color_utils();
41003
41786
  init_parser8();
@@ -41575,7 +42358,55 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
41575
42358
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
41576
42359
  const sTitleY = ctx.structural(TITLE_Y);
41577
42360
  const naturalW = diagramW + sDiagramPadding * 2;
41578
- const naturalH = diagramH + titleHeight + legendReserveH + sDiagramPadding * 2;
42361
+ const semanticRoles = useSemanticColors ? classifyEREntities([...parsed.tables], [...parsed.relationships]) : null;
42362
+ const semanticActive = semanticRoles !== null && (semanticColorsActive ?? true);
42363
+ const presentRoles = semanticRoles ? ROLE_ORDER.filter((role) => {
42364
+ for (const r of semanticRoles.values()) {
42365
+ if (r === role) return true;
42366
+ }
42367
+ return false;
42368
+ }) : [];
42369
+ const semanticGroups = presentRoles.length > 0 ? [
42370
+ {
42371
+ name: "Role",
42372
+ entries: presentRoles.map((role) => ({
42373
+ value: ROLE_LABELS[role],
42374
+ color: palette.colors[ROLE_COLORS[role]]
42375
+ }))
42376
+ }
42377
+ ] : [];
42378
+ const showSemanticLegend = semanticRoles !== null && !noLegend && presentRoles.length > 0;
42379
+ const inlineRequested = legendInlineRequested(parsed.options);
42380
+ const hasLegend = hasTagLegend || showSemanticLegend;
42381
+ const inlineLegendGroups = hasTagLegend ? parsed.tagGroups : semanticGroups;
42382
+ const inlineActiveGroup = hasTagLegend ? activeTagGroup ?? null : semanticActive ? "Role" : null;
42383
+ const inlineContainerW = exportDims?.width ?? naturalW;
42384
+ const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
42385
+ {
42386
+ groups: inlineLegendGroups,
42387
+ position: {
42388
+ placement: "top-center",
42389
+ titleRelation: "inline-with-title"
42390
+ },
42391
+ mode: exportMode ? "export" : "preview"
42392
+ },
42393
+ { activeGroup: inlineActiveGroup },
42394
+ inlineContainerW
42395
+ ) : { width: 0, height: 0 };
42396
+ const header = layoutInlineHeader({
42397
+ requested: inlineRequested,
42398
+ title: parsed.title ?? "",
42399
+ hasLegend,
42400
+ legendWidth: legendExtent.width,
42401
+ legendHeight: legendExtent.height,
42402
+ containerWidth: inlineContainerW,
42403
+ titleBandHeight: titleHeight,
42404
+ legendReserve: legendReserveH,
42405
+ titleBaselineY: sTitleY,
42406
+ titleFontSize: sTitleFontSize
42407
+ });
42408
+ const effectiveReserveH = header.inline ? 0 : legendReserveH;
42409
+ const naturalH = diagramH + titleHeight + effectiveReserveH + sDiagramPadding * 2;
41579
42410
  let viewW;
41580
42411
  let viewH;
41581
42412
  let scale;
@@ -41584,24 +42415,24 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
41584
42415
  if (exportDims) {
41585
42416
  viewW = exportDims.width ?? naturalW;
41586
42417
  viewH = exportDims.height ?? naturalH;
41587
- const availH = viewH - titleHeight - legendReserveH;
42418
+ const availH = viewH - titleHeight - effectiveReserveH;
41588
42419
  const scaleX = (viewW - sDiagramPadding * 2) / diagramW;
41589
42420
  const scaleY = (availH - sDiagramPadding * 2) / diagramH;
41590
42421
  scale = Math.min(MAX_SCALE4, scaleX, scaleY);
41591
42422
  const scaledW = diagramW * scale;
41592
42423
  offsetX = (viewW - scaledW) / 2;
41593
- offsetY = titleHeight + legendReserveH + sDiagramPadding;
42424
+ offsetY = titleHeight + effectiveReserveH + sDiagramPadding;
41594
42425
  } else {
41595
42426
  viewW = naturalW;
41596
42427
  viewH = naturalH;
41597
42428
  scale = 1;
41598
42429
  offsetX = sDiagramPadding;
41599
- offsetY = titleHeight + legendReserveH + sDiagramPadding;
42430
+ offsetY = titleHeight + effectiveReserveH + sDiagramPadding;
41600
42431
  }
41601
42432
  if (viewW <= 0 || viewH <= 0) return;
41602
42433
  const svg = d3Selection13.select(container).append("svg").attr("width", exportDims ? viewW : "100%").attr("height", exportDims ? viewH : "100%").attr("viewBox", `0 0 ${viewW} ${viewH}`).attr("preserveAspectRatio", "xMidYMid meet").style("font-family", FONT_FAMILY);
41603
42434
  if (showTitle) {
41604
- const titleEl = svg.append("text").attr("class", "chart-title").attr("x", viewW / 2).attr("y", sTitleY).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).style(
42435
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", sTitleY).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).style(
41605
42436
  "cursor",
41606
42437
  onClickItem && parsed.titleLineNumber ? "pointer" : "default"
41607
42438
  ).text(parsed.title);
@@ -41618,8 +42449,6 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
41618
42449
  }
41619
42450
  const contentG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
41620
42451
  const seriesColors2 = getSeriesColors(palette);
41621
- const semanticRoles = useSemanticColors ? classifyEREntities([...parsed.tables], [...parsed.relationships]) : null;
41622
- const semanticActive = semanticRoles !== null && (semanticColorsActive ?? true);
41623
42452
  const useLabels = parsed.options["notation"] === "labels";
41624
42453
  for (const edge of layout.edges) {
41625
42454
  if (edge.points.length < 2) continue;
@@ -41763,46 +42592,43 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
41763
42592
  }
41764
42593
  if (hasTagLegend) {
41765
42594
  const legendY = sDiagramPadding + titleHeight;
41766
- const legendG = svg.append("g").attr("class", "er-tag-legend").attr("transform", `translate(0,${legendY})`);
42595
+ const legendG = svg.append("g").attr("class", "er-tag-legend").attr(
42596
+ "transform",
42597
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0,${legendY})`
42598
+ );
41767
42599
  renderIntegratedLegend(legendG, {
41768
42600
  groups: parsed.tagGroups,
41769
42601
  activeGroup: activeTagGroup ?? null,
41770
42602
  mode: exportMode ? "export" : "preview",
42603
+ position: {
42604
+ placement: "top-center",
42605
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
42606
+ },
41771
42607
  palette,
41772
42608
  isDark,
41773
42609
  width: viewW
41774
42610
  });
41775
42611
  legendG.selectAll("[data-legend-group]").classed("er-legend-group", true);
41776
42612
  }
41777
- if (semanticRoles && !noLegend) {
41778
- const presentRoles = ROLE_ORDER.filter((role) => {
41779
- for (const r of semanticRoles.values()) {
41780
- if (r === role) return true;
41781
- }
41782
- return false;
42613
+ if (showSemanticLegend) {
42614
+ const legendY = sDiagramPadding + titleHeight;
42615
+ const legendG = svg.append("g").attr("class", "er-semantic-legend").attr(
42616
+ "transform",
42617
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0,${legendY})`
42618
+ );
42619
+ renderIntegratedLegend(legendG, {
42620
+ groups: semanticGroups,
42621
+ activeGroup: semanticActive ? "Role" : null,
42622
+ mode: exportMode ? "export" : "preview",
42623
+ position: {
42624
+ placement: "top-center",
42625
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
42626
+ },
42627
+ palette,
42628
+ isDark,
42629
+ width: viewW
41783
42630
  });
41784
- if (presentRoles.length > 0) {
41785
- const legendY = sDiagramPadding + titleHeight;
41786
- const semanticGroups = [
41787
- {
41788
- name: "Role",
41789
- entries: presentRoles.map((role) => ({
41790
- value: ROLE_LABELS[role],
41791
- color: palette.colors[ROLE_COLORS[role]]
41792
- }))
41793
- }
41794
- ];
41795
- const legendG = svg.append("g").attr("class", "er-semantic-legend").attr("transform", `translate(0,${legendY})`);
41796
- renderIntegratedLegend(legendG, {
41797
- groups: semanticGroups,
41798
- activeGroup: semanticActive ? "Role" : null,
41799
- mode: exportMode ? "export" : "preview",
41800
- palette,
41801
- isDark,
41802
- width: viewW
41803
- });
41804
- legendG.selectAll("[data-legend-group]").classed("er-legend-group", true);
41805
- }
42631
+ legendG.selectAll("[data-legend-group]").classed("er-legend-group", true);
41806
42632
  }
41807
42633
  }
41808
42634
  function renderERDiagramForExport(content, theme, palette) {
@@ -41857,6 +42683,8 @@ var init_renderer9 = __esm({
41857
42683
  init_palettes();
41858
42684
  init_tag_groups();
41859
42685
  init_legend_constants();
42686
+ init_legend_layout();
42687
+ init_inline_header();
41860
42688
  init_legend_integration();
41861
42689
  init_title_constants();
41862
42690
  init_scaling();
@@ -42333,7 +43161,7 @@ var init_layout5 = __esm({
42333
43161
  // src/sketch/markdown-card.ts
42334
43162
  function segWidth(seg, fontSize) {
42335
43163
  const w = measureText(seg.text, fontSize);
42336
- return seg.bold ? w * BOLD_WIDTH_FACTOR : w;
43164
+ return seg.bold ? w * BOLD_WIDTH_FACTOR2 : w;
42337
43165
  }
42338
43166
  function parseInline(text) {
42339
43167
  const segments = [];
@@ -42516,12 +43344,12 @@ function drawMarkdownBlock(container, text, opts) {
42516
43344
  }
42517
43345
  return shown.length * lineHeight;
42518
43346
  }
42519
- var BOLD_WIDTH_FACTOR, INDENT_PX, BULLET_HANG_PX, ELLIPSIS, BULLET;
43347
+ var BOLD_WIDTH_FACTOR2, INDENT_PX, BULLET_HANG_PX, ELLIPSIS, BULLET;
42520
43348
  var init_markdown_card = __esm({
42521
43349
  "src/sketch/markdown-card.ts"() {
42522
43350
  "use strict";
42523
43351
  init_text_measure();
42524
- BOLD_WIDTH_FACTOR = 1.06;
43352
+ BOLD_WIDTH_FACTOR2 = 1.06;
42525
43353
  INDENT_PX = 12;
42526
43354
  BULLET_HANG_PX = 12;
42527
43355
  ELLIPSIS = "\u2026";
@@ -42666,7 +43494,34 @@ function renderSketch(container, parsed, layout, palette, isDark, options = {})
42666
43494
  },
42667
43495
  width
42668
43496
  ) : 0;
42669
- const contentTop = titleOffset + legendHeight + DIAGRAM_PADDING6 + extraTop;
43497
+ const hasLegend = legendGroups.length > 0;
43498
+ const inlineRequested = parsed.options.legendInline === true;
43499
+ const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
43500
+ {
43501
+ groups: legendGroups,
43502
+ position: {
43503
+ placement: "top-center",
43504
+ titleRelation: "inline-with-title"
43505
+ },
43506
+ mode: exportMode ? "export" : "preview",
43507
+ showInactivePills: true
43508
+ },
43509
+ { activeGroup: activeName },
43510
+ width
43511
+ ) : { width: 0, height: 0 };
43512
+ const header = layoutInlineHeader({
43513
+ requested: inlineRequested,
43514
+ title: parsed.title ?? "",
43515
+ hasLegend,
43516
+ legendWidth: legendExtent.width,
43517
+ legendHeight: legendExtent.height,
43518
+ containerWidth: width,
43519
+ titleBandHeight: titleOffset,
43520
+ legendReserve: legendHeight,
43521
+ titleBaselineY: TITLE_Y2,
43522
+ titleFontSize: TITLE_FONT_SIZE2
43523
+ });
43524
+ const contentTop = titleOffset + (header.inline ? 0 : legendHeight) + DIAGRAM_PADDING6 + extraTop;
42670
43525
  const height = Math.max(
42671
43526
  contentTop + maxY + DIAGRAM_PADDING6,
42672
43527
  options.exportDims?.height ?? 0
@@ -42692,15 +43547,22 @@ function renderSketch(container, parsed, layout, palette, isDark, options = {})
42692
43547
  ).attr("fill", color);
42693
43548
  }
42694
43549
  if (showTitle) {
42695
- svg.append("text").attr("x", width / 2).attr("y", TITLE_Y2).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE2).attr("font-weight", 700).attr("fill", palette.text).text(parsed.title);
43550
+ svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", TITLE_Y2).attr("text-anchor", header.titleAnchor).attr("font-size", TITLE_FONT_SIZE2).attr("font-weight", 700).attr("fill", palette.text).text(parsed.title);
42696
43551
  }
42697
43552
  if (legendGroups.length > 0) {
42698
- const legendG = svg.append("g").attr("transform", `translate(0,${titleOffset + 4})`);
43553
+ const legendG = svg.append("g").attr(
43554
+ "transform",
43555
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0,${titleOffset + 4})`
43556
+ );
42699
43557
  renderIntegratedLegend(legendG, {
42700
43558
  groups: legendGroups,
42701
43559
  ...activeName !== null && { activeGroup: activeName },
42702
43560
  mode: exportMode ? "export" : "preview",
42703
43561
  showInactivePills: true,
43562
+ position: {
43563
+ placement: "top-center",
43564
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
43565
+ },
42704
43566
  palette,
42705
43567
  isDark,
42706
43568
  width
@@ -43347,6 +44209,7 @@ var init_renderer10 = __esm({
43347
44209
  init_markdown_card();
43348
44210
  init_legend_layout();
43349
44211
  init_legend_integration();
44212
+ init_inline_header();
43350
44213
  init_tag_groups();
43351
44214
  init_text_measure();
43352
44215
  init_visual_conventions();
@@ -43624,6 +44487,32 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
43624
44487
  );
43625
44488
  const needsLegend = (legendGroups.length > 0 || hasAnyDescriptions && !!onToggleDescriptions) && !noLegend;
43626
44489
  const legendH = needsLegend ? sLegendHeight + 8 : 0;
44490
+ const inlineRequested = legendInlineRequested(parsed.options);
44491
+ const legendExtent = inlineRequested && needsLegend ? getLegendExtent(
44492
+ {
44493
+ groups: legendGroups,
44494
+ position: {
44495
+ placement: "top-center",
44496
+ titleRelation: "inline-with-title"
44497
+ },
44498
+ mode: exportMode ? "export" : "preview",
44499
+ showInactivePills: true
44500
+ },
44501
+ { activeGroup },
44502
+ width
44503
+ ) : { width: 0, height: 0 };
44504
+ const header = layoutInlineHeader({
44505
+ requested: inlineRequested,
44506
+ title: parsed.title ?? "",
44507
+ hasLegend: needsLegend,
44508
+ legendWidth: legendExtent.width,
44509
+ legendHeight: legendExtent.height,
44510
+ containerWidth: width,
44511
+ titleBandHeight: titleOffset,
44512
+ legendReserve: legendH,
44513
+ titleBaselineY: sTitleY,
44514
+ titleFontSize: sTitleFontSize
44515
+ });
43627
44516
  const groupLabelsSet = new Set(layout.groups.map((g) => g.label));
43628
44517
  let labelZoneExtension = 0;
43629
44518
  for (const group of parsed.groups) {
@@ -43632,19 +44521,20 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
43632
44521
  }
43633
44522
  }
43634
44523
  const contentW = layout.width;
43635
- const contentH = layout.height + titleOffset + legendH + labelZoneExtension;
44524
+ const vOverhead = titleOffset + (header.inline ? 0 : legendH) + labelZoneExtension;
44525
+ const availH = Math.max(1, height - vOverhead - sDiagramPadding * 2);
43636
44526
  const scaleX = width / (contentW + sDiagramPadding * 2);
43637
- const scaleY = height / (contentH + sDiagramPadding * 2);
44527
+ const scaleY = availH / layout.height;
43638
44528
  const scale = Math.min(scaleX, scaleY, 3);
43639
44529
  const offsetX = (width - contentW * scale) / 2;
43640
- const offsetY = sDiagramPadding + titleOffset + legendH;
44530
+ const offsetY = sDiagramPadding + titleOffset + (header.inline ? 0 : legendH);
43641
44531
  const svg = d3Selection14.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY).style("background", palette.bg);
43642
44532
  if (sctx.isBelowFloor) {
43643
44533
  svg.attr("width", "100%");
43644
44534
  }
43645
44535
  const defs = svg.append("defs");
43646
44536
  if (showTitle) {
43647
- svg.append("text").attr("x", width / 2).attr("y", sTitleY).attr("text-anchor", "middle").attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(parsed.title);
44537
+ svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", sTitleY).attr("text-anchor", header.titleAnchor).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(parsed.title);
43648
44538
  }
43649
44539
  const diagramG = svg.append("g").attr("transform", `translate(${offsetX},${offsetY}) scale(${scale})`);
43650
44540
  const arrowColors = /* @__PURE__ */ new Set();
@@ -44019,11 +44909,20 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
44019
44909
  }
44020
44910
  }
44021
44911
  };
44022
- const legendG = svg.append("g").attr("transform", `translate(0,${titleOffset + 4})`);
44912
+ const legendG = svg.append("g").attr(
44913
+ "transform",
44914
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0,${titleOffset + 4})`
44915
+ );
44023
44916
  renderIntegratedLegend(legendG, {
44024
44917
  groups: legendGroups,
44025
44918
  activeGroup,
44026
44919
  mode: exportMode ? "export" : "preview",
44920
+ // Inline → left-origin so the wrapper's right-flush translate lands the
44921
+ // legend at the chart's right edge; stacked → centered below the title.
44922
+ position: {
44923
+ placement: "top-center",
44924
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
44925
+ },
44027
44926
  // Keep inactive sibling tag groups visible as collapsed pills so the user
44028
44927
  // can click one to flip the active colouring dimension (preview only).
44029
44928
  showInactivePills: true,
@@ -44068,6 +44967,7 @@ var init_renderer11 = __esm({
44068
44967
  "src/boxes-and-lines/renderer.ts"() {
44069
44968
  "use strict";
44070
44969
  init_parsing();
44970
+ init_inline_header();
44071
44971
  init_note_box();
44072
44972
  init_fonts();
44073
44973
  init_legend_integration();
@@ -46420,9 +47320,11 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
46420
47320
  if (!pool.length)
46421
47321
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
46422
47322
  let layered = [];
46423
- try {
46424
- layered = layeredCandidates(parsed, sizes);
46425
- } catch {
47323
+ if (collapsedGroupLabels.size === 0) {
47324
+ try {
47325
+ layered = layeredCandidates(parsed, sizes);
47326
+ } catch {
47327
+ }
46426
47328
  }
46427
47329
  if (parsed.groups.length > 0 && collapsedGroupLabels.size === 0) {
46428
47330
  try {
@@ -47885,7 +48787,7 @@ function layoutFamily(parsed, focusId) {
47885
48787
  let acc = MARGIN8 + ANCESTOR_BAND;
47886
48788
  for (let r = 0; r <= maxRow; r++) {
47887
48789
  rowY.set(r, acc);
47888
- acc += (rowHeight.get(r) ?? 0) + ROW_GAP;
48790
+ acc += (rowHeight.get(r) ?? 0) + ROW_GAP2;
47889
48791
  }
47890
48792
  }
47891
48793
  const centerX = /* @__PURE__ */ new Map();
@@ -48108,7 +49010,7 @@ function layoutFamily(parsed, focusId) {
48108
49010
  height
48109
49011
  };
48110
49012
  }
48111
- var MARGIN8, MIN_CARD_W, CARD_H_PAD3, CARD_TEXT_PAD, H_GAP2, ROW_GAP, KEY_VALUE_GAP, KEY_X, GUTTER_WIDTH;
49013
+ var MARGIN8, MIN_CARD_W, CARD_H_PAD3, CARD_TEXT_PAD, H_GAP2, ROW_GAP2, KEY_VALUE_GAP, KEY_X, GUTTER_WIDTH;
48112
49014
  var init_layout8 = __esm({
48113
49015
  "src/family/layout.ts"() {
48114
49016
  "use strict";
@@ -48121,7 +49023,7 @@ var init_layout8 = __esm({
48121
49023
  CARD_H_PAD3 = 12;
48122
49024
  CARD_TEXT_PAD = 12;
48123
49025
  H_GAP2 = 60;
48124
- ROW_GAP = 74;
49026
+ ROW_GAP2 = 74;
48125
49027
  KEY_VALUE_GAP = 6;
48126
49028
  KEY_X = 10;
48127
49029
  GUTTER_WIDTH = 72;
@@ -48184,7 +49086,33 @@ function renderFamilyForExport(container, parsed, layout, palette, isDark, opts
48184
49086
  const contentH = layout.height;
48185
49087
  const W3 = opts.fit?.width ?? opts.exportDims?.width ?? contentW + PAD3 * 2;
48186
49088
  const legendReserve = hasLegend ? getMaxLegendReservedHeight(legendConfig, W3) + LEGEND_GROUP_GAP : 0;
48187
- const chrome = titleReserve + legendReserve;
49089
+ const inlineRequested = legendInlineRequested(parsed.options);
49090
+ const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
49091
+ {
49092
+ groups: legendGroups,
49093
+ position: {
49094
+ placement: "top-center",
49095
+ titleRelation: "inline-with-title"
49096
+ },
49097
+ mode: opts.exportMode ? "export" : "preview",
49098
+ showInactivePills: true
49099
+ },
49100
+ { activeGroup: activeTagGroup },
49101
+ W3
49102
+ ) : { width: 0, height: 0 };
49103
+ const header = layoutInlineHeader({
49104
+ requested: inlineRequested,
49105
+ title: parsed.title ?? "",
49106
+ hasLegend,
49107
+ legendWidth: legendExtent.width,
49108
+ legendHeight: legendExtent.height,
49109
+ containerWidth: W3,
49110
+ titleBandHeight: titleReserve,
49111
+ legendReserve,
49112
+ titleBaselineY: TITLE_Y,
49113
+ titleFontSize: TITLE_FONT_SIZE
49114
+ });
49115
+ const chrome = titleReserve + (header.inline ? 0 : legendReserve);
48188
49116
  let scale = 1;
48189
49117
  let H2;
48190
49118
  if (opts.fit) {
@@ -48201,12 +49129,21 @@ function renderFamilyForExport(container, parsed, layout, palette, isDark, opts
48201
49129
  ".family-focus-icon{opacity:0;transition:opacity .15s ease;pointer-events:none}.family-card:hover .family-focus-icon{opacity:.6;pointer-events:all}.family-focus-icon:hover{opacity:1}"
48202
49130
  );
48203
49131
  if (parsed.title) {
48204
- svg.append("text").attr("x", W3 / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(parsed.title);
49132
+ svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", TITLE_Y).attr("text-anchor", header.titleAnchor).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(parsed.title);
48205
49133
  }
48206
49134
  if (hasLegend) {
48207
- const legendParent = svg.append("g").attr("transform", `translate(0, ${titleReserve})`);
49135
+ const legendParent = svg.append("g").attr(
49136
+ "transform",
49137
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${titleReserve})`
49138
+ );
48208
49139
  renderIntegratedLegend(legendParent, {
48209
49140
  ...legendConfig,
49141
+ // Inline → left-origin so the wrapper's right-flush translate lands the
49142
+ // legend at the chart's right edge; stacked → centered below the title.
49143
+ position: {
49144
+ placement: "top-center",
49145
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
49146
+ },
48210
49147
  palette,
48211
49148
  isDark,
48212
49149
  width: W3,
@@ -48345,6 +49282,7 @@ var init_renderer13 = __esm({
48345
49282
  "src/family/renderer.ts"() {
48346
49283
  "use strict";
48347
49284
  init_parsing();
49285
+ init_inline_header();
48348
49286
  init_fonts();
48349
49287
  init_title_constants();
48350
49288
  init_color_utils();
@@ -48373,7 +49311,7 @@ var init_renderer13 = __esm({
48373
49311
  });
48374
49312
 
48375
49313
  // src/mindmap/text-wrap.ts
48376
- function tokenize(text) {
49314
+ function tokenize2(text) {
48377
49315
  const tokens = [];
48378
49316
  const parts = text.split(/(\s+)/);
48379
49317
  for (const part of parts) {
@@ -48419,7 +49357,7 @@ function truncateLastLine(lines, maxWidth, fontSize) {
48419
49357
  }
48420
49358
  function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABEL_LINES3) {
48421
49359
  if (!text) return { lines: [""], fontSize: baseFontSize };
48422
- const tokens = tokenize(text);
49360
+ const tokens = tokenize2(text);
48423
49361
  for (let fs = baseFontSize; fs >= minFontSize; fs--) {
48424
49362
  const lines = tryWrap(tokens, maxWidth, fs, maxLines);
48425
49363
  if (lines) {
@@ -49008,8 +49946,33 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
49008
49946
  const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
49009
49947
  const fixedTitle = !isExport && showTitle;
49010
49948
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
49949
+ const inlineRequested = legendInlineRequested(parsed.options);
49950
+ const legendExtent = inlineRequested && fixedLegend ? getLegendExtent(
49951
+ {
49952
+ groups: parsed.tagGroups,
49953
+ position: {
49954
+ placement: "top-center",
49955
+ titleRelation: "inline-with-title"
49956
+ },
49957
+ mode: isExport ? "export" : "preview"
49958
+ },
49959
+ { activeGroup: activeTagGroup ?? null },
49960
+ containerWidth
49961
+ ) : { width: 0, height: 0 };
49962
+ const header = layoutInlineHeader({
49963
+ requested: inlineRequested,
49964
+ title: parsed.title ?? "",
49965
+ hasLegend: fixedLegend,
49966
+ legendWidth: legendExtent.width,
49967
+ legendHeight: legendExtent.height,
49968
+ containerWidth,
49969
+ titleBandHeight: titleReserve,
49970
+ legendReserve,
49971
+ titleBaselineY: DIAGRAM_PADDING8 + TITLE_FONT_SIZE,
49972
+ titleFontSize: TITLE_FONT_SIZE
49973
+ });
49011
49974
  const availWidth = containerWidth;
49012
- const availHeight = containerHeight - DIAGRAM_PADDING8 * 2 - legendReserve - titleReserve;
49975
+ const availHeight = containerHeight - DIAGRAM_PADDING8 * 2 - (header.inline ? 0 : legendReserve) - titleReserve;
49013
49976
  let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
49014
49977
  availWidth,
49015
49978
  layout.width,
@@ -49050,7 +50013,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
49050
50013
  const scaledWidth = renderLayout.width * fitScale;
49051
50014
  const scaledHeight = renderLayout.height * fitScale;
49052
50015
  const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
49053
- const offsetY = DIAGRAM_PADDING8 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
50016
+ const offsetY = DIAGRAM_PADDING8 + (header.inline ? 0 : legendReserve) + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
49054
50017
  const mainG = svg.append("g").attr(
49055
50018
  "transform",
49056
50019
  `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
@@ -49060,9 +50023,9 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
49060
50023
  }
49061
50024
  if (showTitle) {
49062
50025
  const titleParent = fixedTitle ? svg : mainG;
49063
- const titleX = fixedTitle ? containerWidth / 2 : renderLayout.width / 2;
50026
+ const titleX = fixedTitle ? header.titleX : renderLayout.width / 2;
49064
50027
  const titleY = fixedTitle ? DIAGRAM_PADDING8 + TITLE_FONT_SIZE : TITLE_FONT_SIZE;
49065
- const titleText = titleParent.append("text").attr("x", titleX).attr("y", titleY).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).attr("class", "chart-title").text(parsed.title);
50028
+ const titleText = titleParent.append("text").attr("x", titleX).attr("y", titleY).attr("text-anchor", fixedTitle ? header.titleAnchor : "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).attr("class", "chart-title").text(parsed.title);
49066
50029
  if (parsed.titleLineNumber) {
49067
50030
  titleText.attr("data-line-number", parsed.titleLineNumber);
49068
50031
  }
@@ -49071,7 +50034,10 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
49071
50034
  }
49072
50035
  }
49073
50036
  if (fixedLegend) {
49074
- const legendG = svg.append("g").attr("class", "mindmap-legend-fixed").attr("transform", `translate(0, ${DIAGRAM_PADDING8 + titleReserve})`);
50037
+ const legendG = svg.append("g").attr("class", "mindmap-legend-fixed").attr(
50038
+ "transform",
50039
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${DIAGRAM_PADDING8 + titleReserve})`
50040
+ );
49075
50041
  const usedValues = /* @__PURE__ */ new Map();
49076
50042
  for (const node of renderLayout.nodes) {
49077
50043
  for (const tg of parsed.tagGroups) {
@@ -49112,7 +50078,10 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
49112
50078
  entries: tg.entries.filter((e) => used?.has(e.value.toLowerCase())).map((e) => ({ value: e.value, color: e.color }))
49113
50079
  };
49114
50080
  }),
49115
- position: { placement: "top-center", titleRelation: "below-title" },
50081
+ position: {
50082
+ placement: "top-center",
50083
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
50084
+ },
49116
50085
  mode: options?.exportMode ? "export" : "preview",
49117
50086
  ...controlsToggles !== void 0 && { controlsGroup: controlsToggles },
49118
50087
  ...options?.controlsHost !== void 0 && {
@@ -49305,6 +50274,7 @@ var init_renderer14 = __esm({
49305
50274
  init_legend_integration();
49306
50275
  init_legend_constants();
49307
50276
  init_legend_layout();
50277
+ init_inline_header();
49308
50278
  init_title_constants();
49309
50279
  init_scaling();
49310
50280
  init_visual_conventions();
@@ -56490,10 +57460,10 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
56490
57460
  }
56491
57461
  return groups;
56492
57462
  }
56493
- function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false, controlsHost) {
57463
+ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false, controlsHost, legendX = 0, inlineHeader = false) {
56494
57464
  if (legendGroups.length === 0 && !playback) return;
56495
57465
  const appHostedPlayback = controlsHost === "app" && !!playback;
56496
- const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
57466
+ const legendG = rootSvg.append("g").attr("transform", `translate(${legendX}, ${legendY})`);
56497
57467
  if (activeGroup) {
56498
57468
  legendG.attr("data-legend-active", tagAttrKey(activeGroup));
56499
57469
  }
@@ -56509,6 +57479,12 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
56509
57479
  activeGroup,
56510
57480
  mode: exportMode ? "export" : "preview",
56511
57481
  showEmptyGroups: true,
57482
+ ...inlineHeader && {
57483
+ position: {
57484
+ placement: "top-center",
57485
+ titleRelation: "inline-with-title"
57486
+ }
57487
+ },
56512
57488
  ...appHostedPlayback && {
56513
57489
  controlsHost: "app",
56514
57490
  controlsGroup: {
@@ -56592,7 +57568,39 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
56592
57568
  const legendOffset = hasLegend && !fixedLegend ? legendDynamicH : 0;
56593
57569
  const titleOffset = title ? TITLE_OFFSET : 0;
56594
57570
  const totalWidth = layout.width;
56595
- const totalHeight = layout.height + titleOffset + legendOffset;
57571
+ const inlineRequested = legendInlineRequested(layout.options) && !fixedLegend;
57572
+ const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
57573
+ {
57574
+ groups: legendGroups.map((g) => ({
57575
+ name: g.name,
57576
+ entries: g.entries.map((e) => ({
57577
+ value: e.value,
57578
+ color: e.color
57579
+ }))
57580
+ })),
57581
+ position: {
57582
+ placement: "top-center",
57583
+ titleRelation: "inline-with-title"
57584
+ },
57585
+ mode: exportMode ? "export" : "preview",
57586
+ showEmptyGroups: true
57587
+ },
57588
+ { activeGroup: activeGroup ?? null },
57589
+ totalWidth
57590
+ ) : { width: 0, height: 0 };
57591
+ const header = layoutInlineHeader({
57592
+ requested: inlineRequested,
57593
+ title: title ?? "",
57594
+ hasLegend,
57595
+ legendWidth: legendExtent.width,
57596
+ legendHeight: legendExtent.height,
57597
+ containerWidth: totalWidth,
57598
+ titleBandHeight: titleOffset,
57599
+ legendReserve: legendOffset,
57600
+ titleBaselineY: sc.sTitleY,
57601
+ titleFontSize: sc.sTitleFontSize
57602
+ });
57603
+ const totalHeight = layout.height + titleOffset + (header.inline ? 0 : legendOffset);
56596
57604
  const shouldAnimate = animate !== false;
56597
57605
  const fixedTitleH = fixedLegend && title ? TITLE_OFFSET : 0;
56598
57606
  const diagramViewHeight = fixedLegend ? layout.height + (title && !fixedTitleH ? titleOffset : 0) + legendOffset : totalHeight;
@@ -56652,9 +57660,12 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
56652
57660
  `);
56653
57661
  }
56654
57662
  const contentTitleOffset = fixedTitleH ? 0 : titleOffset;
56655
- const svg = rootSvg.append("g").attr("transform", `translate(0, ${contentTitleOffset + legendOffset})`);
57663
+ const svg = rootSvg.append("g").attr(
57664
+ "transform",
57665
+ `translate(0, ${contentTitleOffset + (header.inline ? 0 : legendOffset)})`
57666
+ );
56656
57667
  if (title && !fixedTitleH) {
56657
- rootSvg.append("text").attr("class", "chart-title").attr("x", totalWidth / 2).attr("y", sc.sTitleY).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).attr("data-line-number", titleLineNumber != null ? titleLineNumber : "").text(title);
57668
+ rootSvg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", sc.sTitleY).attr("text-anchor", header.titleAnchor).attr("font-family", FONT_FAMILY).attr("font-size", sc.sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).attr("data-line-number", titleLineNumber != null ? titleLineNumber : "").text(title);
56658
57669
  }
56659
57670
  renderGroups(svg, layout.groups, palette, isDark, sc);
56660
57671
  const speedMultiplier = playback?.speed ?? 1;
@@ -56731,13 +57742,15 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
56731
57742
  rootSvg,
56732
57743
  legendGroups,
56733
57744
  totalWidth,
56734
- titleOffset,
57745
+ header.inline ? header.legendY : titleOffset,
56735
57746
  palette,
56736
57747
  isDark,
56737
57748
  activeGroup ?? null,
56738
57749
  playback ?? void 0,
56739
57750
  exportMode,
56740
- controlsHost
57751
+ controlsHost,
57752
+ header.inline ? header.legendX : 0,
57753
+ header.inline
56741
57754
  );
56742
57755
  }
56743
57756
  }
@@ -56767,6 +57780,7 @@ var init_renderer17 = __esm({
56767
57780
  init_legend_constants();
56768
57781
  init_legend_integration();
56769
57782
  init_legend_layout();
57783
+ init_inline_header();
56770
57784
  init_title_constants();
56771
57785
  init_scaling();
56772
57786
  init_parsing();
@@ -58643,25 +59657,54 @@ function renderPert(container, resolved, layout, palette, isDark, options = {})
58643
59657
  const scaledHeight = layout.height + sDiagramPad * 2 + sTitleHeight + sLegendBlockHeight + analysisBlockHeight + fieldLegendBlockHeight;
58644
59658
  const svgW = ctx.isBelowFloor ? exportWidth : scaledWidth;
58645
59659
  const svgH = ctx.isBelowFloor ? exportHeight : scaledHeight;
59660
+ const inlineRequested = resolved.options.legendInline === true && showTagLegend;
59661
+ const legendExtent = inlineRequested ? getLegendExtent(
59662
+ {
59663
+ groups: resolved.tagGroups.map((g) => ({
59664
+ name: g.name,
59665
+ entries: g.entries
59666
+ })),
59667
+ position: {
59668
+ placement: "top-center",
59669
+ titleRelation: "inline-with-title"
59670
+ },
59671
+ mode: options.exportMode ? "export" : "preview"
59672
+ },
59673
+ { activeGroup: tagLegendActive },
59674
+ svgW
59675
+ ) : { width: 0, height: 0 };
59676
+ const header = layoutInlineHeader({
59677
+ requested: inlineRequested,
59678
+ title: effectiveTitle ?? "",
59679
+ hasLegend: showTagLegend,
59680
+ legendWidth: legendExtent.width,
59681
+ legendHeight: legendExtent.height,
59682
+ containerWidth: svgW,
59683
+ titleBandHeight: sTitleHeight,
59684
+ legendReserve: sLegendBlockHeight,
59685
+ titleBaselineY: sTitleY,
59686
+ titleFontSize: sTitleFontSize
59687
+ });
58646
59688
  const svg = d3Selection22.select(container).append("svg").attr("width", ctx.isBelowFloor ? "100%" : svgW).attr("height", svgH).attr("viewBox", `0 0 ${svgW} ${svgH}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY);
58647
59689
  const defs = svg.append("defs");
58648
59690
  buildArrowheads(defs, palette, sArrowheadW, sArrowheadH);
58649
59691
  if (effectiveTitle) {
58650
- svg.append("text").attr("class", "pert-title chart-title").attr("x", svgW / 2).attr("y", sTitleY).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).text(effectiveTitle);
59692
+ svg.append("text").attr("class", "pert-title chart-title").attr("x", header.titleX).attr("y", sTitleY).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).text(effectiveTitle);
58651
59693
  }
58652
59694
  if (effectiveSubtitle) {
58653
59695
  const subtitleY = effectiveTitle ? sTitleY + ctx.aesthetic(26) : sTitleY;
58654
59696
  svg.append("text").attr("class", "pert-subtitle").attr("x", svgW / 2).attr("y", subtitleY).attr("text-anchor", "middle").attr("fill", palette.textMuted).attr("font-size", sSubtitleFontSize).attr("font-weight", 400).text(effectiveSubtitle);
58655
59697
  }
58656
59698
  const offsetX = Math.max(sDiagramPad, (svgW - layout.width) / 2);
58657
- const offsetY = sDiagramPad + sTitleHeight + sLegendBlockHeight;
59699
+ const offsetY = sDiagramPad + sTitleHeight + (header.inline ? 0 : sLegendBlockHeight);
58658
59700
  if (showTagLegend) {
58659
59701
  const tagLegendY = sDiagramPad + sTitleHeight + sLegendTopGap;
58660
59702
  renderTagLegendRow(svg, resolved, palette, isDark, {
58661
- x: 0,
58662
- y: tagLegendY,
58663
- width: svgW,
59703
+ x: header.inline ? svgW - INLINE_HEADER_PAD - legendExtent.width : 0,
59704
+ y: header.inline ? header.legendY : tagLegendY,
59705
+ width: header.inline ? legendExtent.width : svgW,
58664
59706
  activeGroup: tagLegendActive,
59707
+ inline: header.inline,
58665
59708
  ...options.exportMode !== void 0 && {
58666
59709
  exportMode: options.exportMode
58667
59710
  }
@@ -59731,7 +60774,7 @@ function renderCaptionBlock(svg, bullets, args) {
59731
60774
  }
59732
60775
  function renderTagLegendRow(svg, resolved, palette, isDark, args) {
59733
60776
  if (resolved.tagGroups.length === 0) return;
59734
- const { x, y, width, activeGroup, exportMode } = args;
60777
+ const { x, y, width, activeGroup, exportMode, inline } = args;
59735
60778
  const groups = resolved.tagGroups.map((g) => ({
59736
60779
  name: g.name,
59737
60780
  entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
@@ -59743,7 +60786,13 @@ function renderTagLegendRow(svg, resolved, palette, isDark, args) {
59743
60786
  palette,
59744
60787
  isDark,
59745
60788
  width,
59746
- activeGroup
60789
+ activeGroup,
60790
+ ...inline && {
60791
+ position: {
60792
+ placement: "top-center",
60793
+ titleRelation: "inline-with-title"
60794
+ }
60795
+ }
59747
60796
  });
59748
60797
  }
59749
60798
  function renderFieldLegendBlock(svg, args) {
@@ -60186,6 +61235,8 @@ var init_renderer18 = __esm({
60186
61235
  init_legend_constants();
60187
61236
  init_tag_groups();
60188
61237
  init_legend_integration();
61238
+ init_legend_layout();
61239
+ init_inline_header();
60189
61240
  init_parser12();
60190
61241
  init_analyzer();
60191
61242
  init_layout14();
@@ -61223,7 +62274,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
61223
62274
  const idealWidth = leftMargin + totalRows * 20 + RIGHT_MARGIN + 200;
61224
62275
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(containerWidth, idealWidth);
61225
62276
  const sBarH = ctx.structural(BAR_H);
61226
- const sRowGap = ctx.structural(ROW_GAP2);
62277
+ const sRowGap = ctx.structural(ROW_GAP3);
61227
62278
  const sGroupGap = ctx.structural(GROUP_GAP3);
61228
62279
  const sMilestoneSize = ctx.structural(MILESTONE_SIZE);
61229
62280
  const sBottomMargin = ctx.aesthetic(BOTTOM_MARGIN);
@@ -61240,12 +62291,38 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
61240
62291
  const sSprintLabelReserve = ctx.structural(sprintLabelReserve);
61241
62292
  const sTitleHeight = ctx.aesthetic(titleHeight);
61242
62293
  const sTagLegendReserve = ctx.structural(tagLegendReserve);
62294
+ const inlineRequested = resolved.options.legendInline === true && resolved.tagGroups.length > 0;
62295
+ const legendExtent = inlineRequested ? getLegendExtent(
62296
+ {
62297
+ groups: resolved.tagGroups.map((tg) => ({
62298
+ name: tg.name,
62299
+ entries: tg.entries
62300
+ })),
62301
+ position: { placement: "top-center", titleRelation: "inline-with-title" },
62302
+ mode: "preview",
62303
+ capsulePillAddonWidth: LEGEND_ICON_W
62304
+ },
62305
+ { activeGroup: currentActiveGroup },
62306
+ containerWidth
62307
+ ) : { width: 0, height: 0 };
62308
+ const header = layoutInlineHeader({
62309
+ requested: inlineRequested,
62310
+ title: title ?? "",
62311
+ hasLegend: hasTagLegend,
62312
+ legendWidth: legendExtent.width,
62313
+ legendHeight: legendExtent.height,
62314
+ containerWidth,
62315
+ titleBandHeight: sTitleHeight,
62316
+ legendReserve: sTagLegendReserve,
62317
+ titleBaselineY: sTitleY,
62318
+ titleFontSize: sTitleFontSize
62319
+ });
61243
62320
  const sEraReserve = ctx.structural(eraReserve);
61244
62321
  const sMarkerReserve = ctx.structural(markerReserve);
61245
62322
  const sTodayReserve = ctx.structural(todayReserve);
61246
62323
  const sFont10 = ctx.text(10);
61247
62324
  const sFont11 = ctx.text(11);
61248
- const sMarginTop = sTitleHeight + sTagLegendReserve + sEraReserve + sMarkerReserve + sTodayReserve + sTopDateLabelReserve + sSprintLabelReserve;
62325
+ const sMarginTop = sTitleHeight + (header.inline ? 0 : sTagLegendReserve) + sEraReserve + sMarkerReserve + sTodayReserve + sTopDateLabelReserve + sSprintLabelReserve;
61249
62326
  const sTodayLabelY = sTodayReserve ? -(sTopDateLabelReserve + sHeaderRowH / 2) : 0;
61250
62327
  const sMarkerLabelY = sMarkerReserve ? -(sTopDateLabelReserve + sTodayReserve + sHeaderRowH / 2) : 0;
61251
62328
  const sEraLabelY = sEraReserve ? -(sTopDateLabelReserve + sTodayReserve + sMarkerReserve + sHeaderRowH / 2) : 0;
@@ -61257,12 +62334,12 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
61257
62334
  const svg = d3Selection23.select(container).append("svg").attr("viewBox", `0 0 ${containerWidth} ${sOuterHeight}`).attr("width", exportDims ? containerWidth : "100%").attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY).style("overflow", "visible");
61258
62335
  const g = svg.append("g").attr("transform", `translate(${leftMargin}, ${sMarginTop})`);
61259
62336
  if (showTitle) {
61260
- svg.append("text").attr("class", "chart-title").attr("x", containerWidth / 2).attr("y", sTitleY).attr("text-anchor", "middle").attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(title);
62337
+ svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", sTitleY).attr("text-anchor", header.titleAnchor).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(title);
61261
62338
  }
61262
62339
  function drawLegend() {
61263
62340
  svg.selectAll(".gantt-tag-legend-container").remove();
61264
62341
  if (hasTagLegend) {
61265
- const legendY = sTitleHeight;
62342
+ const legendY = header.inline ? header.legendY : sTitleHeight;
61266
62343
  renderTagLegend(
61267
62344
  svg,
61268
62345
  g,
@@ -61311,7 +62388,8 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
61311
62388
  drawLegend();
61312
62389
  },
61313
62390
  options?.exportMode ?? false,
61314
- controlsHost
62391
+ controlsHost,
62392
+ header.inline
61315
62393
  );
61316
62394
  }
61317
62395
  }
@@ -62130,7 +63208,7 @@ function buildControlsToggles(hasCriticalPath, criticalPathActive, hasDependenci
62130
63208
  }
62131
63209
  return toggles;
62132
63210
  }
62133
- function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargin, chartInnerWidth, legendY, palette, isDark, hasCriticalPath, criticalPathActive, _optionLineNumbers, onToggle, onToggleControlsExpand, currentSwimlaneGroup, onSwimlaneChange, legendViewMode, resolvedTasks, controlsExpanded = false, hasDependencies = false, dependenciesActive = false, onControlsToggle, exportMode = false, controlsHost = "inline") {
63211
+ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargin, chartInnerWidth, legendY, palette, isDark, hasCriticalPath, criticalPathActive, _optionLineNumbers, onToggle, onToggleControlsExpand, currentSwimlaneGroup, onSwimlaneChange, legendViewMode, resolvedTasks, controlsExpanded = false, hasDependencies = false, dependenciesActive = false, onControlsToggle, exportMode = false, controlsHost = "inline", inlineHeader = false) {
62134
63212
  let visibleGroups;
62135
63213
  if (activeGroupName) {
62136
63214
  const activeGroup = tagGroups.filter(
@@ -62200,7 +63278,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
62200
63278
  totalW += controlsExpanded ? controlsGroupCapsuleWidth(controlsToggleLabels) : LEGEND_GEAR_PILL_W;
62201
63279
  }
62202
63280
  const containerWidth = chartLeftMargin + chartInnerWidth + RIGHT_MARGIN;
62203
- const legendX = (containerWidth - totalW) / 2;
63281
+ const legendX = inlineHeader ? containerWidth - INLINE_HEADER_PAD - totalW : (containerWidth - totalW) / 2;
62204
63282
  const legendRow = svg.append("g").attr("class", "gantt-tag-legend-container").attr("transform", `translate(${legendX}, ${legendY})`);
62205
63283
  if (visibleGroups.length > 0) {
62206
63284
  const showIcon = !legendViewMode && tagGroups.length > 0;
@@ -62308,6 +63386,12 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
62308
63386
  width: tagGroupsW,
62309
63387
  mode: exportMode ? "export" : "preview",
62310
63388
  capsulePillAddonWidth: iconReserve,
63389
+ ...inlineHeader && {
63390
+ position: {
63391
+ placement: "top-center",
63392
+ titleRelation: "inline-with-title"
63393
+ }
63394
+ },
62311
63395
  ...controlsToggles.length > 0 && {
62312
63396
  controlsGroup: { toggles: controlsToggles }
62313
63397
  },
@@ -63109,7 +64193,7 @@ function renderTimeScaleHorizontal(g, scale, _innerWidth, innerHeight, textColor
63109
64193
  g.append("text").attr("class", "gantt-scale-tick").attr("x", tick.pos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", textColor).attr("opacity", opacity).text(tick.label);
63110
64194
  }
63111
64195
  }
63112
- var BAR_H, ROW_GAP2, GROUP_GAP3, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
64196
+ var BAR_H, ROW_GAP3, GROUP_GAP3, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
63113
64197
  var init_renderer19 = __esm({
63114
64198
  "src/gantt/renderer.ts"() {
63115
64199
  "use strict";
@@ -63125,9 +64209,10 @@ var init_renderer19 = __esm({
63125
64209
  init_legend_constants();
63126
64210
  init_legend_integration();
63127
64211
  init_legend_layout();
64212
+ init_inline_header();
63128
64213
  init_title_constants();
63129
64214
  BAR_H = 22;
63130
- ROW_GAP2 = 6;
64215
+ ROW_GAP3 = 6;
63131
64216
  GROUP_GAP3 = 14;
63132
64217
  MILESTONE_SIZE = 10;
63133
64218
  MIN_LEFT_MARGIN = 120;
@@ -63221,6 +64306,32 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
63221
64306
  width
63222
64307
  )
63223
64308
  ) + 8 : 0;
64309
+ const inlineRequested = legendInlineRequested(graph.options);
64310
+ const legendExtent = inlineRequested && hasLegend ? getLegendExtent(
64311
+ {
64312
+ groups: legendGroups,
64313
+ position: {
64314
+ placement: "top-center",
64315
+ titleRelation: "inline-with-title"
64316
+ },
64317
+ mode: exportDims ? "export" : "preview",
64318
+ showInactivePills: true
64319
+ },
64320
+ { activeGroup: activeTagGroup },
64321
+ width
64322
+ ) : { width: 0, height: 0 };
64323
+ const header = layoutInlineHeader({
64324
+ requested: inlineRequested,
64325
+ title: graph.title ?? "",
64326
+ hasLegend,
64327
+ legendWidth: legendExtent.width,
64328
+ legendHeight: legendExtent.height,
64329
+ containerWidth: width,
64330
+ titleBandHeight: titleHeight,
64331
+ legendReserve: legendH,
64332
+ titleBaselineY: sTitleY,
64333
+ titleFontSize: sTitleFontSize
64334
+ });
63224
64335
  const diagramW = layout.width;
63225
64336
  const diagramH = layout.height;
63226
64337
  const { scale, offsetX, offsetY, canvasHeight } = fitDiagramToCanvas({
@@ -63229,9 +64340,8 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
63229
64340
  diagramW,
63230
64341
  diagramH,
63231
64342
  padding: sDiagramPadding,
63232
- // The legend band sits directly under the title, so it reserves the same
63233
- // top strip fitDiagramToCanvas already offsets the diagram by.
63234
- titleHeight: titleHeight + legendH,
64343
+ // Inline → one band (title only); stacked → title + legend band.
64344
+ titleHeight: header.inline ? titleHeight : titleHeight + legendH,
63235
64345
  maxScale: MAX_SCALE7,
63236
64346
  exportMode: !!exportDims
63237
64347
  });
@@ -63249,7 +64359,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
63249
64359
  colors: edgeColors
63250
64360
  });
63251
64361
  if (showTitle) {
63252
- const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", sTitleY).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).style(
64362
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", sTitleY).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-size", sTitleFontSize).attr("font-weight", TITLE_FONT_WEIGHT).style(
63253
64363
  "cursor",
63254
64364
  onClickItem && graph.titleLineNumber ? "pointer" : "default"
63255
64365
  ).text(graph.title);
@@ -63265,7 +64375,10 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
63265
64375
  }
63266
64376
  }
63267
64377
  if (hasLegend) {
63268
- const legendG = svg.append("g").attr("transform", `translate(0, ${titleHeight + 4})`);
64378
+ const legendG = svg.append("g").attr(
64379
+ "transform",
64380
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${titleHeight + 4})`
64381
+ );
63269
64382
  renderIntegratedLegend(legendG, {
63270
64383
  groups: legendGroups,
63271
64384
  activeGroup: activeTagGroup,
@@ -63273,6 +64386,12 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
63273
64386
  // Inactive sibling groups stay visible as collapsed pills so the user
63274
64387
  // can click one to flip the active colouring dimension (as in b&l).
63275
64388
  showInactivePills: true,
64389
+ // Inline → left-origin so the wrapper's right-flush translate lands the
64390
+ // legend at the chart's right edge; stacked → centered below the title.
64391
+ position: {
64392
+ placement: "top-center",
64393
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
64394
+ },
63276
64395
  palette,
63277
64396
  isDark,
63278
64397
  width
@@ -63531,6 +64650,7 @@ var init_state_renderer = __esm({
63531
64650
  "src/graph/state-renderer.ts"() {
63532
64651
  "use strict";
63533
64652
  init_parsing();
64653
+ init_inline_header();
63534
64654
  init_arrow_markers();
63535
64655
  init_fit_canvas();
63536
64656
  init_fonts();
@@ -70958,8 +72078,8 @@ function layoutMap(resolvedIn, data, size, opts) {
70958
72078
  if (near) near.push(p);
70959
72079
  else groups2.push([p]);
70960
72080
  }
70961
- const ROW_GAP3 = 3;
70962
- const step = poiLabH + ROW_GAP3;
72081
+ const ROW_GAP4 = 3;
72082
+ const step = poiLabH + ROW_GAP4;
70963
72083
  const COL_GAP2 = 16;
70964
72084
  const makeItems = (group) => group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
70965
72085
  const columnRows = (items, side) => {
@@ -73876,9 +74996,36 @@ function renderTreemap(container, parsed, palette, isDark, exportDims, options =
73876
74996
  { groups: legend.groups, position: LEGEND_POSITION2, mode: "preview" },
73877
74997
  width
73878
74998
  ) + LEGEND_GROUP_GAP : 0;
74999
+ const inlineRequested = parsed.options.legendInline === true;
75000
+ const legendExtent = inlineRequested && legend ? getLegendExtent(
75001
+ {
75002
+ groups: legend.groups,
75003
+ position: {
75004
+ placement: "top-center",
75005
+ titleRelation: "inline-with-title"
75006
+ },
75007
+ mode: exportMode ? "export" : "preview",
75008
+ showInactivePills: !exportMode,
75009
+ showEmptyGroups: !exportMode
75010
+ },
75011
+ { activeGroup: legend.activeGroup },
75012
+ width
75013
+ ) : { width: 0, height: 0 };
75014
+ const header = layoutInlineHeader({
75015
+ requested: inlineRequested,
75016
+ title: parsed.title ?? "",
75017
+ hasLegend: !!legend,
75018
+ legendWidth: legendExtent.width,
75019
+ legendHeight: legendExtent.height,
75020
+ containerWidth: width,
75021
+ titleBandHeight: titleH,
75022
+ legendReserve,
75023
+ titleBaselineY: TITLE_Y,
75024
+ titleFontSize: TITLE_FONT_SIZE
75025
+ });
73879
75026
  const headerH = opts.noHeaders ? 0 : HEADER_H;
73880
75027
  const areaX = PADDING4;
73881
- const areaY = titleH + legendReserve + PADDING4;
75028
+ const areaY = (header.inline ? titleH : titleH + legendReserve) + PADDING4;
73882
75029
  const areaW = Math.max(1, width - PADDING4 * 2);
73883
75030
  const areaH = Math.max(1, height - areaY - PADDING4);
73884
75031
  const layout = layoutTreemap(parsed.roots, {
@@ -73893,7 +75040,7 @@ function renderTreemap(container, parsed, palette, isDark, exportDims, options =
73893
75040
  );
73894
75041
  svg.append("rect").attr("width", width).attr("height", height).attr("fill", palette.bg);
73895
75042
  if (showTitle) {
73896
- const title = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
75043
+ const title = svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", TITLE_Y).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
73897
75044
  if (parsed.titleLineNumber !== null) {
73898
75045
  title.attr("data-line-number", parsed.titleLineNumber);
73899
75046
  }
@@ -74023,7 +75170,10 @@ function renderTreemap(container, parsed, palette, isDark, exportDims, options =
74023
75170
  }
74024
75171
  }
74025
75172
  if (legend) {
74026
- const legendG = svg.append("g").attr("class", "dgmo-treemap-legend").attr("transform", `translate(0, ${titleH})`);
75173
+ const legendG = svg.append("g").attr("class", "dgmo-treemap-legend").attr(
75174
+ "transform",
75175
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${titleH})`
75176
+ );
74027
75177
  renderIntegratedLegend(legendG, {
74028
75178
  groups: legend.groups,
74029
75179
  palette: {
@@ -74036,7 +75186,12 @@ function renderTreemap(container, parsed, palette, isDark, exportDims, options =
74036
75186
  isDark,
74037
75187
  width,
74038
75188
  mode: exportMode ? "export" : "preview",
74039
- position: LEGEND_POSITION2,
75189
+ // Inline → left-origin so the wrapper's right-flush translate lands the
75190
+ // legend at the chart's right edge; stacked → centered below the title.
75191
+ position: {
75192
+ placement: "top-center",
75193
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
75194
+ },
74040
75195
  activeGroup: legend.activeGroup,
74041
75196
  // The color-mode switcher is the legend itself: each applicable mode is a
74042
75197
  // group pill; the active one is the open capsule, the rest are clickable
@@ -74104,6 +75259,7 @@ var init_renderer26 = __esm({
74104
75259
  init_text_measure();
74105
75260
  init_legend_integration();
74106
75261
  init_legend_layout();
75262
+ init_inline_header();
74107
75263
  init_legend_constants();
74108
75264
  init_title_constants();
74109
75265
  init_layout19();
@@ -74289,7 +75445,32 @@ function renderBlock(container, parsed, palette, isDark, exportDims, options = {
74289
75445
  { groups: legend.groups, position: LEGEND_POSITION3, mode: "preview" },
74290
75446
  width
74291
75447
  ) + LEGEND_GROUP_GAP : 0;
74292
- const areaY = titleH + legendReserve + PADDING5;
75448
+ const inlineRequested = opts.legendInline === true;
75449
+ const legendExtent = inlineRequested && legend ? getLegendExtent(
75450
+ {
75451
+ groups: legend.groups,
75452
+ position: {
75453
+ placement: "top-center",
75454
+ titleRelation: "inline-with-title"
75455
+ },
75456
+ mode: options.exportMode ? "export" : "preview"
75457
+ },
75458
+ { activeGroup: legend.activeGroup ?? null },
75459
+ width
75460
+ ) : { width: 0, height: 0 };
75461
+ const header = layoutInlineHeader({
75462
+ requested: inlineRequested,
75463
+ title: parsed.title ?? "",
75464
+ hasLegend: !!legend,
75465
+ legendWidth: legendExtent.width,
75466
+ legendHeight: legendExtent.height,
75467
+ containerWidth: width,
75468
+ titleBandHeight: titleH,
75469
+ legendReserve,
75470
+ titleBaselineY: TITLE_Y,
75471
+ titleFontSize: TITLE_FONT_SIZE
75472
+ });
75473
+ const areaY = (header.inline ? titleH : titleH + legendReserve) + PADDING5;
74293
75474
  if (options.exportMode) {
74294
75475
  height = areaY + layout.height + PADDING5;
74295
75476
  } else {
@@ -74304,7 +75485,7 @@ function renderBlock(container, parsed, palette, isDark, exportDims, options = {
74304
75485
  const offsetY = areaY;
74305
75486
  const svg = d3Selection33.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").attr("class", "dgmo-block").style("font-family", FONT_FAMILY);
74306
75487
  if (showTitle) {
74307
- const title = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
75488
+ const title = svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", TITLE_Y).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
74308
75489
  if (parsed.titleLineNumber !== null) {
74309
75490
  title.attr("data-line-number", parsed.titleLineNumber);
74310
75491
  }
@@ -74323,7 +75504,10 @@ function renderBlock(container, parsed, palette, isDark, exportDims, options = {
74323
75504
  onToggle: options.onToggle
74324
75505
  });
74325
75506
  if (legend) {
74326
- const legendG = svg.append("g").attr("class", "dgmo-block-legend").attr("transform", `translate(0, ${titleH})`);
75507
+ const legendG = svg.append("g").attr("class", "dgmo-block-legend").attr(
75508
+ "transform",
75509
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${titleH})`
75510
+ );
74327
75511
  renderIntegratedLegend(legendG, {
74328
75512
  groups: legend.groups,
74329
75513
  palette: {
@@ -74336,7 +75520,10 @@ function renderBlock(container, parsed, palette, isDark, exportDims, options = {
74336
75520
  isDark,
74337
75521
  width,
74338
75522
  mode: options.exportMode ? "export" : "preview",
74339
- position: LEGEND_POSITION3,
75523
+ position: {
75524
+ placement: "top-center",
75525
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
75526
+ },
74340
75527
  activeGroup: legend.activeGroup
74341
75528
  });
74342
75529
  }
@@ -74454,6 +75641,7 @@ var init_renderer27 = __esm({
74454
75641
  init_fonts();
74455
75642
  init_color_utils();
74456
75643
  init_tag_groups();
75644
+ init_inline_header();
74457
75645
  init_legend_integration();
74458
75646
  init_legend_layout();
74459
75647
  init_legend_constants();
@@ -76116,12 +77304,12 @@ function drawRow(svg, entry, index, parsed, now, rowTop, rowH, geom, col) {
76116
77304
  const baseY = center + 16;
76117
77305
  const capTop = baseY - mainFont * 0.7;
76118
77306
  const sf = 20;
76119
- const mainW = [...ts.main].reduce(
76120
- (w, c) => w + mainFont * (c === ":" ? 0.334 : 0.6),
76121
- 0
76122
- );
76123
- const stackX = contentLeft + mainW + 5;
76124
- g.append("text").attr("data-dgmo-clock-digital", "").attr("data-dgmo-clock-digital-part", "main").attr("x", contentLeft).attr("y", baseY).attr("fill", timeColor).attr("font-family", FONT_FAMILY).attr("font-size", mainFont).attr("font-weight", 600).attr("textLength", mainW).attr("lengthAdjust", "spacing").style("font-variant-numeric", "tabular-nums").text(ts.main);
77307
+ const gap = 5;
77308
+ const { cells, width: mainW } = fixedTimeCells(ts.main, contentLeft, mainFont);
77309
+ const stackX = contentLeft + mainW + gap;
77310
+ const mainText = g.append("text").attr("data-dgmo-clock-digital", "").attr("data-dgmo-clock-digital-part", "main").attr("y", baseY).attr("text-anchor", "middle").attr("fill", timeColor).attr("font-family", FONT_FAMILY).attr("font-size", mainFont).attr("font-weight", 600).attr("data-dgmo-clock-x0", contentLeft).attr("data-dgmo-clock-fs", mainFont).attr("data-dgmo-clock-gap", gap);
77311
+ for (const c of cells)
77312
+ mainText.append("tspan").attr("x", c.x).text(c.ch);
76125
77313
  g.append("text").attr("data-dgmo-clock-digital-part", "sec").attr("x", stackX).attr("y", capTop + sf * 0.72).attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", sf).attr("font-weight", 600).style("font-variant-numeric", "tabular-nums").text(`:${ts.sec}`);
76126
77314
  if (ts.ap)
76127
77315
  g.append("text").attr("data-dgmo-clock-digital-part", "ap").attr("x", stackX).attr("y", baseY).attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", sf).attr("font-weight", 600).text(ts.ap);
@@ -76280,14 +77468,13 @@ function drawColumns(body, parsed, now, top, geom, col) {
76280
77468
  const gap = 11;
76281
77469
  const cap = 0.7;
76282
77470
  const tf = fitFont(ts.main, 54, innerW - 30 - gap, 20);
76283
- const mainW = [...ts.main].reduce(
76284
- (w, c) => w + tf * (c === ":" ? 0.334 : 0.6),
76285
- 0
76286
- );
77471
+ const mainW = fixedTimeCells(ts.main, 0, tf).width;
76287
77472
  const stackW = Math.max(estWidth(`:${ts.sec}`, sf), estWidth(ts.ap, sf));
76288
77473
  const gLeft = cx - (mainW + gap + stackW) / 2;
76289
77474
  const baseY = cy + tf * 0.34;
76290
- g.append("text").attr("data-dgmo-clock-digital", "").attr("data-dgmo-clock-digital-part", "main").attr("x", gLeft).attr("y", baseY).attr("fill", timeColor).attr("font-family", FONT_FAMILY).attr("font-size", tf).attr("font-weight", 600).attr("textLength", mainW).attr("lengthAdjust", "spacing").style("font-variant-numeric", "tabular-nums").text(ts.main);
77475
+ const mainText = g.append("text").attr("data-dgmo-clock-digital", "").attr("data-dgmo-clock-digital-part", "main").attr("y", baseY).attr("text-anchor", "middle").attr("fill", timeColor).attr("font-family", FONT_FAMILY).attr("font-size", tf).attr("font-weight", 600).attr("data-dgmo-clock-x0", gLeft).attr("data-dgmo-clock-fs", tf).attr("data-dgmo-clock-gap", gap);
77476
+ for (const c of fixedTimeCells(ts.main, gLeft, tf).cells)
77477
+ mainText.append("tspan").attr("x", c.x).text(c.ch);
76291
77478
  const stackX = gLeft + mainW + gap;
76292
77479
  g.append("text").attr("data-dgmo-clock-digital-part", "sec").attr("x", stackX).attr("y", baseY - tf * cap + sf * cap).attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", sf).attr("font-weight", 500).text(`:${ts.sec}`);
76293
77480
  if (ts.ap)
@@ -77057,16 +78244,41 @@ function renderBracket(container, parsed, palette, isDark, exportDims) {
77057
78244
  { groups: legendGroups, position: LEGEND_POSITION4, mode: "preview" },
77058
78245
  width
77059
78246
  ) + LEGEND_GROUP_GAP : 0;
78247
+ const inlineRequested = parsed.legendInline === true;
78248
+ const legendExtent = inlineRequested && legendGroups.length > 0 ? getLegendExtent(
78249
+ {
78250
+ groups: legendGroups,
78251
+ position: {
78252
+ placement: "top-center",
78253
+ titleRelation: "inline-with-title"
78254
+ },
78255
+ mode: exportMode ? "export" : "preview"
78256
+ },
78257
+ { activeGroup: activeGroup?.name ?? null },
78258
+ width
78259
+ ) : { width: 0, height: 0 };
78260
+ const header = layoutInlineHeader({
78261
+ requested: inlineRequested,
78262
+ title: parsed.title ?? "",
78263
+ hasLegend: legendGroups.length > 0,
78264
+ legendWidth: legendExtent.width,
78265
+ legendHeight: legendExtent.height,
78266
+ containerWidth: width,
78267
+ titleBandHeight: titleH,
78268
+ legendReserve,
78269
+ titleBaselineY: TITLE_Y,
78270
+ titleFontSize: TITLE_FONT_SIZE
78271
+ });
77060
78272
  const svg = d3Selection37.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").attr("class", "dgmo-bracket").attr("role", "img").attr(
77061
78273
  "aria-label",
77062
78274
  `Tournament bracket${parsed.title ? ` \u2014 ${parsed.title}` : ""}${layout.champion ? `, champion ${layout.champion}` : ""}`
77063
78275
  ).style("font-family", FONT_FAMILY);
77064
78276
  if (showTitle) {
77065
- const t = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
78277
+ const t = svg.append("text").attr("class", "chart-title").attr("x", header.titleX).attr("y", TITLE_Y).attr("text-anchor", header.titleAnchor).attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
77066
78278
  if (parsed.titleLineNumber !== null)
77067
78279
  t.attr("data-line-number", parsed.titleLineNumber);
77068
78280
  }
77069
- const areaY = titleH + legendReserve + PADDING6;
78281
+ const areaY = (header.inline ? titleH : titleH + legendReserve) + PADDING6;
77070
78282
  const areaX = PADDING6;
77071
78283
  const areaW = Math.max(1, width - PADDING6 * 2);
77072
78284
  const areaH = Math.max(1, height - areaY - PADDING6);
@@ -77075,7 +78287,10 @@ function renderBracket(container, parsed, palette, isDark, exportDims) {
77075
78287
  const offsetY = areaY;
77076
78288
  const root = svg.append("g").attr("class", "dgmo-bracket-body").attr("transform", `translate(${offsetX},${offsetY}) scale(${scale})`);
77077
78289
  if (legendGroups.length > 0) {
77078
- const legendG = svg.append("g").attr("class", "dgmo-bracket-legend").attr("transform", `translate(0, ${titleH})`);
78290
+ const legendG = svg.append("g").attr("class", "dgmo-bracket-legend").attr(
78291
+ "transform",
78292
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0, ${titleH})`
78293
+ );
77079
78294
  renderIntegratedLegend(legendG, {
77080
78295
  groups: legendGroups,
77081
78296
  palette: {
@@ -77088,7 +78303,10 @@ function renderBracket(container, parsed, palette, isDark, exportDims) {
77088
78303
  isDark,
77089
78304
  width,
77090
78305
  mode: exportMode ? "export" : "preview",
77091
- position: LEGEND_POSITION4,
78306
+ position: {
78307
+ placement: "top-center",
78308
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
78309
+ },
77092
78310
  activeGroup: activeGroup?.name ?? null
77093
78311
  });
77094
78312
  }
@@ -77315,6 +78533,7 @@ var init_renderer31 = __esm({
77315
78533
  init_tag_groups();
77316
78534
  init_legend_integration();
77317
78535
  init_legend_layout();
78536
+ init_inline_header();
77318
78537
  init_legend_constants();
77319
78538
  init_layout21();
77320
78539
  PADDING6 = 16;
@@ -78587,7 +79806,6 @@ function applyPositionOverrides(participants) {
78587
79806
  return result;
78588
79807
  }
78589
79808
  function applyGroupOrdering(participants, groups, messages = []) {
78590
- if (groups.length === 0) return [...participants];
78591
79809
  const idToGroup = /* @__PURE__ */ new Map();
78592
79810
  for (const group of groups) {
78593
79811
  for (const id of group.participantIds) {
@@ -79055,8 +80273,33 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
79055
80273
  };
79056
80274
  const noLegend = legendSuppressed(parsedOptions);
79057
80275
  const legendTopSpace = parsed.tagGroups.length > 0 && !noLegend ? getMaxLegendReservedHeight(legendConfig, containerWidth) + LEGEND_FIXED_GAP4 : 0;
80276
+ const inlineRequested = legendInlineRequested(parsedOptions);
80277
+ const hasLegendForHeader = parsed.tagGroups.length > 0 && !noLegend;
80278
+ const legendExtent = inlineRequested && hasLegendForHeader ? getLegendExtent(
80279
+ {
80280
+ ...legendConfig,
80281
+ position: {
80282
+ placement: "top-center",
80283
+ titleRelation: "inline-with-title"
80284
+ }
80285
+ },
80286
+ { activeGroup: activeTagGroup ?? null },
80287
+ containerWidth
80288
+ ) : { width: 0, height: 0 };
80289
+ const header = layoutInlineHeader({
80290
+ requested: inlineRequested,
80291
+ title: title ?? "",
80292
+ hasLegend: hasLegendForHeader,
80293
+ legendWidth: legendExtent.width,
80294
+ legendHeight: legendExtent.height,
80295
+ containerWidth,
80296
+ titleBandHeight: titleOffset,
80297
+ legendReserve: legendTopSpace,
80298
+ titleBaselineY: ctx.structural(TITLE_Y),
80299
+ titleFontSize: ctx.text(TITLE_FONT_SIZE)
80300
+ });
79058
80301
  const groupOffset = parsed.groups.length > 0 ? GROUP_PADDING_TOP2 + GROUP_LABEL_SIZE : 0;
79059
- const participantStartY = sTopMargin + titleOffset + legendTopSpace + sParticipantYOffset + groupOffset;
80302
+ const participantStartY = sTopMargin + titleOffset + (header.inline ? 0 : legendTopSpace) + sParticipantYOffset + groupOffset;
79060
80303
  const lifelineStartY0 = participantStartY + sBoxH;
79061
80304
  const hasActors = participants.some((p) => p.type === "actor");
79062
80305
  const messageStartOffset = sMsgStartOffset + (hasActors ? 20 : 0) + (parsed.groups.length > 0 ? GROUP_PADDING_BOTTOM2 : 0);
@@ -79221,56 +80464,63 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
79221
80464
  });
79222
80465
  };
79223
80466
  buildParticipantX();
79224
- let contentLeft = 0;
79225
- let contentRight = svgWidth;
79226
- for (const step of renderSteps) {
79227
- if (!step.label) continue;
79228
- const labelW = measureText(step.label, 12);
79229
- if (step.from === step.to) {
79230
- const px = participantX.get(step.from);
79231
- if (px !== void 0) {
79232
- const loopRight = px + sActivationWidth / 2 + sSelfCallWidth + 5 + labelW;
79233
- contentRight = Math.max(contentRight, loopRight);
79234
- }
79235
- } else {
79236
- const fromX = participantX.get(step.from);
79237
- const toX = participantX.get(step.to);
79238
- if (fromX !== void 0 && toX !== void 0) {
79239
- const midX = (fromX + toX) / 2;
79240
- contentLeft = Math.min(contentLeft, midX - labelW / 2);
79241
- contentRight = Math.max(contentRight, midX + labelW / 2);
80467
+ const labelSafety = sActivationWidth / 2 + 2;
80468
+ const scanNotes = (els, acc) => {
80469
+ for (const el of els) {
80470
+ if (isSequenceNote(el)) {
80471
+ const note = el;
80472
+ const pos = effectiveNotePosition(note);
80473
+ if (pos === "right") {
80474
+ const px = participantX.get(note.participantId);
80475
+ if (px !== void 0) {
80476
+ const sc = isNoteAfterSelfCall(note);
80477
+ const rOff = sc ? sActivationWidth / 2 + sSelfCallWidth + sNoteGap : sActivationWidth + sNoteGap;
80478
+ const maxW = noteEffectiveMaxW(note.participantId, pos, sc);
80479
+ acc.right = Math.max(acc.right, px + rOff + maxW);
80480
+ }
80481
+ }
80482
+ } else if (isSequenceBlock(el)) {
80483
+ scanNotes(el.children, acc);
80484
+ scanNotes(el.elseChildren, acc);
80485
+ if (el.elseIfBranches) {
80486
+ for (const b of el.elseIfBranches) scanNotes(b.children, acc);
80487
+ }
79242
80488
  }
79243
80489
  }
79244
- }
79245
- if (elements) {
79246
- const scanNotes = (els) => {
79247
- for (const el of els) {
79248
- if (isSequenceNote(el)) {
79249
- const note = el;
79250
- const pos = effectiveNotePosition(note);
79251
- if (pos === "right") {
79252
- const px = participantX.get(note.participantId);
79253
- if (px !== void 0) {
79254
- const sc = isNoteAfterSelfCall(note);
79255
- const rOff = sc ? sActivationWidth / 2 + sSelfCallWidth + sNoteGap : sActivationWidth + sNoteGap;
79256
- const maxW = noteEffectiveMaxW(note.participantId, pos, sc);
79257
- contentRight = Math.max(contentRight, px + rOff + maxW);
79258
- }
79259
- }
79260
- } else if (isSequenceBlock(el)) {
79261
- scanNotes(el.children);
79262
- scanNotes(el.elseChildren);
79263
- if (el.elseIfBranches) {
79264
- for (const b of el.elseIfBranches) scanNotes(b.children);
79265
- }
80490
+ };
80491
+ for (let pass = 0; pass < 4; pass++) {
80492
+ let contentLeft = 0;
80493
+ let contentRight = svgWidth;
80494
+ for (const step of renderSteps) {
80495
+ if (!step.label) continue;
80496
+ const labelW = measureText(step.label, 12);
80497
+ if (step.from === step.to) {
80498
+ const px = participantX.get(step.from);
80499
+ if (px !== void 0) {
80500
+ const loopRight = px + sActivationWidth / 2 + sSelfCallWidth + 5 + labelW + labelSafety;
80501
+ contentRight = Math.max(contentRight, loopRight);
80502
+ }
80503
+ } else {
80504
+ const fromX = participantX.get(step.from);
80505
+ const toX = participantX.get(step.to);
80506
+ if (fromX !== void 0 && toX !== void 0) {
80507
+ const midX = (fromX + toX) / 2;
80508
+ contentLeft = Math.min(contentLeft, midX - labelW / 2 - labelSafety);
80509
+ contentRight = Math.max(
80510
+ contentRight,
80511
+ midX + labelW / 2 + labelSafety
80512
+ );
79266
80513
  }
79267
80514
  }
79268
- };
79269
- scanNotes(elements);
79270
- }
79271
- const neededLeftPad = Math.max(0, -contentLeft);
79272
- const neededRightPad = Math.max(0, contentRight - svgWidth);
79273
- if (neededLeftPad > 0 || neededRightPad > 0) {
80515
+ }
80516
+ if (elements) {
80517
+ const acc = { right: contentRight };
80518
+ scanNotes(elements, acc);
80519
+ contentRight = acc.right;
80520
+ }
80521
+ const neededLeftPad = Math.max(0, -contentLeft);
80522
+ const neededRightPad = Math.max(0, contentRight - svgWidth);
80523
+ if (neededLeftPad <= 0.5 && neededRightPad <= 0.5) break;
79274
80524
  leftMargin += neededLeftPad;
79275
80525
  rightMargin += neededRightPad;
79276
80526
  totalWidth = Math.max(leftMargin + diagramWidth + rightMargin, sBoxW + 40);
@@ -79321,7 +80571,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
79321
80571
  }
79322
80572
  };
79323
80573
  if (showTitle) {
79324
- const titleEl = svg.append("text").attr("class", "chart-title").attr("x", svgWidth / 2).attr("y", ctx.structural(TITLE_Y)).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", ctx.text(TITLE_FONT_SIZE)).attr("font-weight", TITLE_FONT_WEIGHT).text(title);
80574
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", header.inline ? header.titleX : svgWidth / 2).attr("y", ctx.structural(TITLE_Y)).attr("text-anchor", header.inline ? header.titleAnchor : "middle").attr("fill", palette.text).attr("font-size", ctx.text(TITLE_FONT_SIZE)).attr("font-weight", TITLE_FONT_WEIGHT).text(title);
79325
80575
  if (parsed.titleLineNumber) {
79326
80576
  titleEl.attr("data-line-number", parsed.titleLineNumber);
79327
80577
  }
@@ -79807,9 +81057,19 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
79807
81057
  if (hasTagGroups) {
79808
81058
  const legendY = sTopMargin + titleOffset;
79809
81059
  const legendCallbacks = {};
79810
- const legendG = svg.append("g").attr("class", "sequence-legend").attr("transform", `translate(0,${legendY})`);
81060
+ const legendG = svg.append("g").attr("class", "sequence-legend").attr(
81061
+ "transform",
81062
+ header.inline ? `translate(${header.legendX}, ${header.legendY})` : `translate(0,${legendY})`
81063
+ );
79811
81064
  renderIntegratedLegend(legendG, {
79812
81065
  ...legendConfig,
81066
+ // Inline → left-origin so the wrapper's right-flush translate lands the
81067
+ // legend at the header's right edge; stacked → centered below the title
81068
+ // (identical to legendConfig.position, so the stacked path is unchanged).
81069
+ position: {
81070
+ placement: "top-center",
81071
+ titleRelation: header.inline ? "inline-with-title" : "below-title"
81072
+ },
79813
81073
  palette,
79814
81074
  isDark,
79815
81075
  width: svgWidth,
@@ -79917,6 +81177,7 @@ var init_renderer33 = __esm({
79917
81177
  init_tag_groups();
79918
81178
  init_legend_layout();
79919
81179
  init_parsing();
81180
+ init_inline_header();
79920
81181
  init_legend_integration();
79921
81182
  init_title_constants();
79922
81183
  init_scaling();
@@ -82296,7 +83557,8 @@ async function exportEventLine(ctx) {
82296
83557
  effectivePalette,
82297
83558
  ctx.isDark,
82298
83559
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
82299
- ctxTagOverride(ctx)
83560
+ ctxTagOverride(ctx),
83561
+ /* @__PURE__ */ new Date()
82300
83562
  );
82301
83563
  return finalizeSvgExport(container, theme, effectivePalette);
82302
83564
  }
@@ -83859,6 +85121,8 @@ function injectHoverStyles(svg, chartType, opts) {
83859
85121
 
83860
85122
  // src/render.ts
83861
85123
  init_dgmo_router();
85124
+ init_diagnostics();
85125
+ init_inline_header();
83862
85126
  init_registry();
83863
85127
  var DOM_GLOBALS = [
83864
85128
  "document",
@@ -83938,6 +85202,18 @@ async function render(content, options) {
83938
85202
  const renderContent = acOverride?.content ?? content;
83939
85203
  const chartType = acOverride?.type ?? parsed.chartType;
83940
85204
  const category = acOverride ? "data-chart" : chartType ? getRenderCategory(chartType) : null;
85205
+ const legendInlineWarning = (() => {
85206
+ if (!chartType || legendInlineSupported(chartType)) return null;
85207
+ const idx = renderContent.split("\n").findIndex((l) => l.trim().toLowerCase() === "legend-inline");
85208
+ if (idx < 0) return null;
85209
+ return makeDgmoError(
85210
+ idx + 1,
85211
+ `'legend-inline' isn't supported for ${chartType} charts \u2014 the title and legend render stacked.`,
85212
+ "warning",
85213
+ "W_LEGEND_INLINE_UNSUPPORTED"
85214
+ );
85215
+ })();
85216
+ const withLegendInlineWarning = (ds) => legendInlineWarning ? [...ds, legendInlineWarning] : ds;
83941
85217
  const viewState = options?.viewState ?? (options?.legendState ? {
83942
85218
  ...options.legendState.activeGroup !== void 0 && {
83943
85219
  tag: options.legendState.activeGroup
@@ -83951,7 +85227,7 @@ async function render(content, options) {
83951
85227
  try {
83952
85228
  const raw = await renderDataChartD3(renderContent, theme, paletteColors);
83953
85229
  const svg2 = injectHoverStyles(raw, chartType, { bakeHover });
83954
- return { svg: svg2, diagnostics, chartType: chartType ?? void 0 };
85230
+ return { svg: svg2, diagnostics: withLegendInlineWarning(diagnostics), chartType: chartType ?? void 0 };
83955
85231
  } finally {
83956
85232
  releaseDom();
83957
85233
  }
@@ -83985,7 +85261,7 @@ async function render(content, options) {
83985
85261
  if (chartType === "map" && mapDiag.current) {
83986
85262
  diagnostics = [...mapDiag.current];
83987
85263
  }
83988
- return { svg, diagnostics, chartType: chartType ?? void 0 };
85264
+ return { svg, diagnostics: withLegendInlineWarning(diagnostics), chartType: chartType ?? void 0 };
83989
85265
  }
83990
85266
 
83991
85267
  // src/countdown/ticker.ts
@@ -84215,6 +85491,27 @@ function setPart(group, sel, textContent) {
84215
85491
  const el = group.querySelector(sel);
84216
85492
  if (el && el.textContent !== textContent) el.textContent = textContent;
84217
85493
  }
85494
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
85495
+ function setDigits(group, main) {
85496
+ const el = group.querySelector('[data-dgmo-clock-digital-part="main"]');
85497
+ if (!el || el.textContent === main) return;
85498
+ const x0 = parseFloat(el.getAttribute("data-dgmo-clock-x0") ?? "0");
85499
+ const fs = parseFloat(el.getAttribute("data-dgmo-clock-fs") ?? "0");
85500
+ const gap = parseFloat(el.getAttribute("data-dgmo-clock-gap") ?? "0");
85501
+ const { cells, width } = fixedTimeCells(main, x0, fs);
85502
+ while (el.firstChild) el.removeChild(el.firstChild);
85503
+ for (const c of cells) {
85504
+ const t = document.createElementNS(SVG_NS2, "tspan");
85505
+ t.setAttribute("x", String(c.x));
85506
+ t.textContent = c.ch;
85507
+ el.appendChild(t);
85508
+ }
85509
+ const stackX = String(x0 + width + gap);
85510
+ for (const part of ["sec", "ap"]) {
85511
+ const s = group.querySelector(`[data-dgmo-clock-digital-part="${part}"]`);
85512
+ if (s) s.setAttribute("x", stackX);
85513
+ }
85514
+ }
84218
85515
  function updateRow(group, now) {
84219
85516
  const zone = group.getAttribute("data-dgmo-clock-zone");
84220
85517
  if (!zone) return;
@@ -84254,7 +85551,7 @@ function updateRow(group, now) {
84254
85551
  const secondHand = group.querySelector('[data-dgmo-clock-hand="s"]');
84255
85552
  if (secondHand) secondHand.setAttribute("stroke", handColor);
84256
85553
  }
84257
- setPart(group, '[data-dgmo-clock-digital-part="main"]', ts.main);
85554
+ setDigits(group, ts.main);
84258
85555
  setPart(group, '[data-dgmo-clock-digital-part="sec"]', `:${ts.sec}`);
84259
85556
  setPart(group, '[data-dgmo-clock-digital-part="ap"]', ts.ap);
84260
85557
  setPart(group, "[data-dgmo-clock-ampm]", ts.ap.toUpperCase());
@@ -85405,7 +86702,7 @@ var CSS = AUTO_BASE_CSS + "\n" + BLOCK_CSS + "\n" + darkScopedBlockCss(BLOCK_CSS
85405
86702
 
85406
86703
  // src/auto/shared.ts
85407
86704
  init_safe_href();
85408
- var VERSION = "0.54.0";
86705
+ var VERSION = "0.55.0";
85409
86706
  var ARIA_LABEL_MAX = 200;
85410
86707
  var COPIED_INTERACTION_MS = 1500;
85411
86708
  var LOG_PREFIX = "[dgmo]";