@diagrammo/dgmo 0.89.0 → 0.91.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 (233) hide show
  1. package/README.md +1 -1
  2. package/dist/advanced.d.ts +589 -15
  3. package/dist/advanced.js +813 -211
  4. package/dist/block.d.ts +11 -2
  5. package/dist/block.js +94 -64
  6. package/dist/{calculator-35YVW4QO.js → calculator-3LZUWOBK.js} +2 -2
  7. package/dist/{chart-meta-Bev5Ebjr.d.ts → chart-meta-B_WTMP0y.d.ts} +1 -1
  8. package/dist/chart-meta.d.ts +2 -2
  9. package/dist/chart-meta.js +18 -17
  10. package/dist/{chart-types-Bf8C5QQW.d.ts → chart-types-V2NcgN5O.d.ts} +4 -0
  11. package/dist/{charts-d3-KEQSUWKP.js → charts-d3-YXD6VGZQ.js} +8 -7
  12. package/dist/{chunk-PMNITNPR.js → chunk-273GB7WK.js} +6 -5
  13. package/dist/{chunk-HAMF4F3P.js → chunk-2A4OFZ2Z.js} +5 -5
  14. package/dist/{chunk-K5XT7NHD.js → chunk-2AM6VA3A.js} +10 -10
  15. package/dist/{chunk-XJ2TBXHA.js → chunk-2UV627GG.js} +9 -9
  16. package/dist/{chunk-7WGCQE2E.js → chunk-2ZVLI6GQ.js} +1 -1
  17. package/dist/{chunk-JVZ7KFAX.js → chunk-37QQXKFV.js} +9 -10
  18. package/dist/{chunk-M2BCY7H3.js → chunk-3LINSNJT.js} +199 -146
  19. package/dist/{chunk-WPL5PFXE.js → chunk-3SLRZKLB.js} +6 -6
  20. package/dist/chunk-3UR4F7FE.js +14 -0
  21. package/dist/{chunk-ZUCXFXLD.js → chunk-3YTAOMMN.js} +2 -2
  22. package/dist/{chunk-NIUO2TNR.js → chunk-4HG4PYHM.js} +197 -203
  23. package/dist/{chunk-BOO24UMJ.js → chunk-4YO7VJTP.js} +1 -1
  24. package/dist/{chunk-HUNDJTTH.js → chunk-53BJIOLF.js} +1 -1
  25. package/dist/{chunk-LR6RRDWN.js → chunk-57L2JPP2.js} +10 -6
  26. package/dist/{chunk-IIOLFAGJ.js → chunk-5DIBB2QG.js} +51 -32
  27. package/dist/{chunk-S2WE7TVK.js → chunk-5MDQMB6A.js} +1 -1
  28. package/dist/{chunk-7Z2VFRP5.js → chunk-5U6DXIMV.js} +47 -24
  29. package/dist/{chunk-QIOYTQDZ.js → chunk-5VPWIC6T.js} +9 -9
  30. package/dist/{chunk-JRUEIRJB.js → chunk-634LDSN6.js} +3 -3
  31. package/dist/{chunk-VEPIAUSK.js → chunk-6BQSDF77.js} +1 -5
  32. package/dist/{chunk-LPYYZCSI.js → chunk-6NO3MNBC.js} +3 -3
  33. package/dist/{chunk-ITSBBQTH.js → chunk-6QEXWMBP.js} +13 -17
  34. package/dist/{chunk-DDPGEGHB.js → chunk-7BBRSOKS.js} +3 -3
  35. package/dist/{chunk-WQ6G6FFM.js → chunk-AR4QVMWG.js} +5 -2
  36. package/dist/{chunk-BB6QKWHZ.js → chunk-B6EUFLRP.js} +10 -10
  37. package/dist/{chunk-C4FRGZGX.js → chunk-CDT33RHG.js} +1 -1
  38. package/dist/{chunk-WP22MHH6.js → chunk-CPGKRI7H.js} +10 -13
  39. package/dist/{chunk-ZNNVI5SR.js → chunk-CU2PZLZP.js} +53 -31
  40. package/dist/{chunk-CUYHST3B.js → chunk-CVZBLHHW.js} +1 -1
  41. package/dist/{chunk-Y7PSAHAY.js → chunk-CXIPV2FY.js} +3 -3
  42. package/dist/{chunk-NRPELUB6.js → chunk-D7DXNQW5.js} +2 -2
  43. package/dist/{chunk-YMFQLWZ2.js → chunk-D7PP35BA.js} +1 -1
  44. package/dist/{chunk-RGWYZR3S.js → chunk-DLX7MEN4.js} +2 -2
  45. package/dist/chunk-EDPDAUBL.js +825 -0
  46. package/dist/{chunk-GSYZZFI5.js → chunk-EP7BT3HU.js} +2 -2
  47. package/dist/{chunk-ICZ7M6IZ.js → chunk-EQJ3U5EW.js} +1 -1
  48. package/dist/{chunk-3C2FWS2J.js → chunk-EWERNTOX.js} +1 -1
  49. package/dist/{chunk-XUCQVKLQ.js → chunk-F2436VRR.js} +4 -4
  50. package/dist/{chunk-UG2XG57E.js → chunk-F26ZPBSF.js} +3 -3
  51. package/dist/{chunk-UWKRPQZO.js → chunk-FJCYK4K3.js} +25 -22
  52. package/dist/{chunk-FKLE6QA3.js → chunk-G22UV5AF.js} +12 -12
  53. package/dist/{chunk-GUTZCRBT.js → chunk-G3EUQMV4.js} +4 -4
  54. package/dist/{chunk-SOPM5PLO.js → chunk-GCDW3WAY.js} +14 -14
  55. package/dist/{chunk-7UDAIP6W.js → chunk-HFG7KQOY.js} +13 -15
  56. package/dist/{chunk-ID2K3IHI.js → chunk-HLE5QGZJ.js} +1 -1
  57. package/dist/{chunk-QFHWYWQL.js → chunk-HLVGZXM5.js} +2 -2
  58. package/dist/{chunk-7C2RAGPT.js → chunk-HSGWR2FL.js} +16 -16
  59. package/dist/{chunk-P3SKSKJI.js → chunk-HWXUQCDY.js} +3 -3
  60. package/dist/{chunk-HTWH2XQL.js → chunk-HYV6GSXM.js} +6 -3
  61. package/dist/{chunk-3RYBTEF5.js → chunk-I74AQRVD.js} +6 -12
  62. package/dist/chunk-IHK3Y2HG.js +781 -0
  63. package/dist/{chunk-7KPHRTND.js → chunk-II4XJETG.js} +1 -1
  64. package/dist/{chunk-RXIKESRI.js → chunk-IMHNZITR.js} +2 -2
  65. package/dist/{chunk-TAP2NZEU.js → chunk-IMKPFW67.js} +13 -13
  66. package/dist/{chunk-FKZ2Y3Q5.js → chunk-IS6WFBX7.js} +3 -3
  67. package/dist/{chunk-47E5UTXE.js → chunk-IUBCDNT2.js} +2 -2
  68. package/dist/{chunk-EXKCRXPO.js → chunk-IWTUXGHN.js} +12 -12
  69. package/dist/{chunk-C7QRRTAW.js → chunk-J2SQNZ6D.js} +2 -2
  70. package/dist/{chunk-DZPHN2WN.js → chunk-JA2S7PHR.js} +14 -14
  71. package/dist/{chunk-DPSMX4EY.js → chunk-JJQWCI35.js} +4 -4
  72. package/dist/chunk-JJUDOR4I.js +110 -0
  73. package/dist/{chunk-LYLNMN77.js → chunk-K3BYD3WU.js} +8 -3
  74. package/dist/{chunk-3AQJNY65.js → chunk-KFXY5IW2.js} +149 -43
  75. package/dist/{chunk-J64NZ6NF.js → chunk-KSZHIAI2.js} +1 -1
  76. package/dist/{chunk-LCJURK7L.js → chunk-LKR4FACF.js} +2 -4
  77. package/dist/{chunk-PIXJL77H.js → chunk-M6J25PAZ.js} +1 -1
  78. package/dist/{chunk-IJ27L3R4.js → chunk-MA42PIGJ.js} +1 -1
  79. package/dist/{chunk-IQ6P3HDE.js → chunk-MI6SJJQU.js} +2 -2
  80. package/dist/{chunk-N4BBSDWI.js → chunk-MJKQ5ZYY.js} +140 -151
  81. package/dist/{chunk-6K7RODXG.js → chunk-MKFQ5E7J.js} +1 -1
  82. package/dist/{chunk-AZ24RI5L.js → chunk-MNL547RV.js} +6 -6
  83. package/dist/{chunk-44GP7PD5.js → chunk-MPJQMMBZ.js} +12 -0
  84. package/dist/{chunk-ECMA6XE2.js → chunk-N254IJM2.js} +13 -13
  85. package/dist/{layout-search-YKUL4VBV.js → chunk-NWPMLX2V.js} +27 -30
  86. package/dist/{chunk-DV2VEJCC.js → chunk-NWSPPGIE.js} +6 -6
  87. package/dist/{chunk-GDJW64XE.js → chunk-OYKNMFUY.js} +1 -1
  88. package/dist/{chunk-B3S76BZO.js → chunk-PSGSFRRE.js} +4 -4
  89. package/dist/{chunk-4AJKOUBO.js → chunk-PUZ63XZM.js} +13 -11
  90. package/dist/{chunk-UQLHKLF4.js → chunk-PZVTFQI2.js} +8 -8
  91. package/dist/{chunk-W7QN5BC5.js → chunk-Q3N7PI3B.js} +180 -7
  92. package/dist/{chunk-L5AFONQU.js → chunk-QKJ6MQXW.js} +2 -4
  93. package/dist/{chunk-7SX4IY6T.js → chunk-QVKTAU7P.js} +1 -1
  94. package/dist/{chunk-SLQT3JP3.js → chunk-R7BSF7Z7.js} +1 -1
  95. package/dist/{chunk-4R6OMKXB.js → chunk-RHXIUUWB.js} +5 -5
  96. package/dist/{chunk-MHAYUS65.js → chunk-RMTOZALT.js} +8 -5
  97. package/dist/{chunk-HSRQDWV7.js → chunk-ROCBGUJN.js} +8 -17
  98. package/dist/{chunk-44FTCIRI.js → chunk-RVTYIGWZ.js} +9 -9
  99. package/dist/{chunk-RU725VCJ.js → chunk-SKF7XLCM.js} +10 -35
  100. package/dist/{chunk-RPKBWJBB.js → chunk-SMTWQ66K.js} +6 -6
  101. package/dist/{chunk-3AX6MCI6.js → chunk-SPQVER4T.js} +2 -2
  102. package/dist/{chunk-CX6OH5WP.js → chunk-SWBHH2UW.js} +4 -4
  103. package/dist/{chunk-WP2RLGDI.js → chunk-TCX5PMEY.js} +1 -1
  104. package/dist/{chunk-7TF3QOOD.js → chunk-TIVMAITN.js} +14 -0
  105. package/dist/{chunk-NSR2CGV6.js → chunk-TLPY4ZBI.js} +611 -437
  106. package/dist/{chunk-WRCAH3UO.js → chunk-TPEYEOXA.js} +15 -15
  107. package/dist/{chunk-FJNXXGTR.js → chunk-UWJP4JSZ.js} +3 -3
  108. package/dist/{chunk-MY6GUH26.js → chunk-V3KHZQPK.js} +3 -3
  109. package/dist/{chunk-66YFDTT6.js → chunk-VGSSAMLA.js} +4 -4
  110. package/dist/{chunk-Q6SQISHT.js → chunk-VMD2CYVP.js} +311 -74
  111. package/dist/{chunk-MFW6P7WH.js → chunk-WNTPJZU7.js} +3 -3
  112. package/dist/{chunk-7W4XLIVH.js → chunk-WTE27AJT.js} +14 -14
  113. package/dist/{chunk-UKJLPA7R.js → chunk-X6IZP2BD.js} +1 -1
  114. package/dist/{chunk-2GM7N5TV.js → chunk-XLAREWBK.js} +9 -9
  115. package/dist/{chunk-AYOVY75O.js → chunk-XLPJJ35N.js} +4 -4
  116. package/dist/{chunk-NGC3IZRY.js → chunk-XQTRGKQ3.js} +4 -4
  117. package/dist/{chunk-WEF6MXFG.js → chunk-YZG37S7L.js} +9 -9
  118. package/dist/{chunk-LFKSHDVI.js → chunk-ZB2Q67NO.js} +14 -13
  119. package/dist/{chunk-JQ57AWDL.js → chunk-ZJ4PYTCK.js} +1 -1
  120. package/dist/completion-registry.d.ts +6 -0
  121. package/dist/completion-registry.js +62 -6
  122. package/dist/{compute-JAP5E2E4.js → compute-2XWFVO7K.js} +1 -1
  123. package/dist/{d3-B6524aaP.d.ts → d3-2S3PrXik.d.ts} +11 -0
  124. package/dist/dgmo-router-YHBMKTXY.js +94 -0
  125. package/dist/{dimensions-7MF7VSGS.js → dimensions-QMGWJQTR.js} +5 -5
  126. package/dist/editor.js +12 -0
  127. package/dist/{flowchart-parser-GG7XBLQH.js → flowchart-parser-BLJ6RA53.js} +4 -4
  128. package/dist/{flowchart-renderer-DZQZRL2F.js → flowchart-renderer-FXRQKXAK.js} +12 -8
  129. package/dist/highlight.js +12 -0
  130. package/dist/index.d.ts +26 -5
  131. package/dist/index.js +84 -57
  132. package/dist/{layout-5VSMZPMG.js → layout-5HCEF7VO.js} +1 -1
  133. package/dist/{layout-6L3BIFEZ.js → layout-6PLSBB5X.js} +3 -3
  134. package/dist/{layout-UIV56NUA.js → layout-B7PQRAMB.js} +1 -1
  135. package/dist/{layout-IGAVGDME.js → layout-CCVCLZ6S.js} +1 -1
  136. package/dist/{layout-PP36LUTA.js → layout-CRUAP7JG.js} +3 -3
  137. package/dist/{layout-Q3RYSODA.js → layout-G42FDCF3.js} +3 -3
  138. package/dist/{layout-EFEZ533S.js → layout-IV4H2FLB.js} +3 -3
  139. package/dist/{layout-4RRC7XU7.js → layout-JTNRSGOS.js} +6 -3
  140. package/dist/{layout-H4K54IW3.js → layout-RBLD5EGX.js} +6 -3
  141. package/dist/{layout-SW3XWYKC.js → layout-ROAVGBUC.js} +1 -1
  142. package/dist/{layout-XUW444O5.js → layout-T2DQ4M77.js} +8 -4
  143. package/dist/{layout-MSSHF45T.js → layout-UPQGIT3N.js} +2 -2
  144. package/dist/{layout-DXVAYF6W.js → layout-VRJ5YFUE.js} +2 -2
  145. package/dist/{layout-ZP7XYVXF.js → layout-YKGQRFDM.js} +2 -4
  146. package/dist/layout-search-L75YOBME.js +42 -0
  147. package/dist/{palettes-WZAIQTEG.js → palettes-WQHELGVQ.js} +2 -1
  148. package/dist/{parser-ZLKYDFI5.js → parser-2XCDVLSN.js} +3 -3
  149. package/dist/{parser-GZ3LVF72.js → parser-3MGA2MTM.js} +2 -2
  150. package/dist/{parser-NOANDFJ4.js → parser-4AR25MAS.js} +2 -2
  151. package/dist/{parser-GCCWMBTU.js → parser-4QNRCGU5.js} +5 -4
  152. package/dist/{parser-LANGKWRG.js → parser-6LMB7BQQ.js} +2 -2
  153. package/dist/{parser-WEVTMBJ5.js → parser-6NSQLPTS.js} +3 -3
  154. package/dist/{parser-KYPP2DX6.js → parser-6X4GGH2R.js} +4 -4
  155. package/dist/parser-AL6YL7KM.js +12 -0
  156. package/dist/{parser-5655XSOR.js → parser-D6YRVV75.js} +3 -3
  157. package/dist/{parser-NGHQQGD2.js → parser-EY3VJWBE.js} +3 -3
  158. package/dist/{parser-TL3JA4RI.js → parser-EYF4ZSZO.js} +3 -3
  159. package/dist/{parser-256EIWB2.js → parser-FDXUQ5TI.js} +2 -2
  160. package/dist/{parser-JOOAVBJI.js → parser-H2DJEAPG.js} +3 -3
  161. package/dist/{parser-AKBVJHMK.js → parser-ILWAA3QJ.js} +3 -3
  162. package/dist/{parser-AN3MGHRL.js → parser-KMFMNZYI.js} +3 -3
  163. package/dist/{parser-IMM2JJX3.js → parser-MDSVVCJP.js} +4 -4
  164. package/dist/{parser-OVQTTTUJ.js → parser-OFTMA36M.js} +2 -2
  165. package/dist/{parser-LGS65QPN.js → parser-Q3UAOLEZ.js} +2 -2
  166. package/dist/{parser-DGF3ZEO6.js → parser-QL4DEPVA.js} +4 -4
  167. package/dist/{parser-XYY5NHP4.js → parser-R6OETHWM.js} +2 -2
  168. package/dist/{parser-GEKPWSSK.js → parser-UEDPLM46.js} +3 -3
  169. package/dist/{parser-KZMALSB2.js → parser-UL6WN6W5.js} +4 -4
  170. package/dist/{parser-J4DVNL4W.js → parser-ULSUK36C.js} +4 -4
  171. package/dist/{parser-KZMJNKDW.js → parser-UY3K4ROB.js} +2 -2
  172. package/dist/{parser-KFYRTGTC.js → parser-UYQNNQKT.js} +4 -4
  173. package/dist/{parser-DHSFIUPU.js → parser-VG747VR6.js} +3 -3
  174. package/dist/{parser-UTXFJG4X.js → parser-VK4PNDX4.js} +3 -3
  175. package/dist/{parser-SA3RAJFS.js → parser-WSONWDFN.js} +2 -2
  176. package/dist/{parser-NPXPJHWN.js → parser-X5FQ36KJ.js} +3 -3
  177. package/dist/{parser-OKB64MMN.js → parser-XO5IAXMT.js} +3 -3
  178. package/dist/{parser-HCGXNHHL.js → parser-YVHO2NRM.js} +3 -3
  179. package/dist/{parser-P4TOQYNN.js → parser-YWZGIU3C.js} +3 -3
  180. package/dist/{parser-337TACHB.js → parser-ZFLSULFC.js} +3 -3
  181. package/dist/{parser-7OEE7QT6.js → parser-ZR7JAKSW.js} +2 -2
  182. package/dist/{renderer-CHQZES6G.js → renderer-3N64GVNM.js} +2 -2
  183. package/dist/{renderer-JR2XNZAH.js → renderer-43IVPARP.js} +5 -5
  184. package/dist/{renderer-J4OS77PK.js → renderer-4MEZYGU6.js} +3 -3
  185. package/dist/{renderer-IR5GGAPB.js → renderer-54GXA3UO.js} +11 -7
  186. package/dist/{renderer-7VQOKAII.js → renderer-7EAWN7MC.js} +6 -6
  187. package/dist/{renderer-QQHQY3GT.js → renderer-7SEXPBGM.js} +5 -5
  188. package/dist/{renderer-P4XCRNCY.js → renderer-B7B7BQM3.js} +9 -9
  189. package/dist/{renderer-7HOSI6JJ.js → renderer-I2LYSQBV.js} +4 -4
  190. package/dist/{renderer-6OQM32X3.js → renderer-IPDCAACY.js} +3 -3
  191. package/dist/{renderer-JRVY7HAD.js → renderer-J2LOMTBC.js} +4 -4
  192. package/dist/renderer-JUH6M3XF.js +46 -0
  193. package/dist/renderer-KY6O5JTX.js +80 -0
  194. package/dist/{renderer-FCXRDYSJ.js → renderer-LHTDI5RV.js} +7 -7
  195. package/dist/{renderer-XHEMXBQM.js → renderer-LWL3IJOF.js} +5 -5
  196. package/dist/{renderer-BLHN3RCY.js → renderer-MRMFYYZT.js} +2 -2
  197. package/dist/{renderer-DBNDLKPE.js → renderer-NCIDGJ2R.js} +7 -7
  198. package/dist/{renderer-3AD4NDF5.js → renderer-OPA4JJKX.js} +6 -6
  199. package/dist/{renderer-XFEZ5UBE.js → renderer-QMNC7TGN.js} +6 -6
  200. package/dist/{renderer-5GZNGZU5.js → renderer-QQNPJGAP.js} +9 -7
  201. package/dist/{renderer-CEIX5JPA.js → renderer-R7OYCJEA.js} +2 -2
  202. package/dist/{renderer-WQ654YT5.js → renderer-R7RK7FKI.js} +1 -1
  203. package/dist/{renderer-4JE5ORRO.js → renderer-RFP55AW7.js} +13 -10
  204. package/dist/{renderer-NAA7D33G.js → renderer-S3NFBJCY.js} +15 -11
  205. package/dist/{renderer-43ESBXVY.js → renderer-SD4ZHHAR.js} +8 -6
  206. package/dist/{renderer-WB75JME5.js → renderer-SE7Z5AOL.js} +6 -6
  207. package/dist/{renderer-YTEZK3GC.js → renderer-T2WUA7MC.js} +7 -7
  208. package/dist/{renderer-VYWCMRWA.js → renderer-TWKEZ3DD.js} +5 -5
  209. package/dist/{renderer-DCJ4AQ3N.js → renderer-U5PD72G7.js} +5 -5
  210. package/dist/{renderer-EAORWLVK.js → renderer-VCTKBUTR.js} +7 -7
  211. package/dist/{renderer-7L7J4DHO.js → renderer-VNZTWATB.js} +7 -6
  212. package/dist/{renderer-2ZQ6DPVD.js → renderer-YDNWK6RI.js} +2 -2
  213. package/dist/{renderer-VGQMJC5K.js → renderer-YDOWBIUB.js} +11 -10
  214. package/dist/{renderer-7MCC6GKH.js → renderer-YQPZ7O2C.js} +2 -2
  215. package/dist/{renderer-ZXQXRNDR.js → renderer-ZQ77CM6Y.js} +6 -6
  216. package/dist/{renderer-radial-NA5DVBG4.js → renderer-radial-TNNEL3YN.js} +5 -5
  217. package/dist/{resolver-XSXE2TRX.js → resolver-6DJLZF6Y.js} +1 -1
  218. package/dist/{state-parser-FE3RF6XY.js → state-parser-NIIVOU2Y.js} +4 -4
  219. package/dist/{state-renderer-4S2C2CHB.js → state-renderer-TVLEG32M.js} +14 -10
  220. package/dist/{themes-CzuBdx1T.d.ts → themes-Cm2YBcT8.d.ts} +38 -2
  221. package/docs/ai-integration.md +4 -3
  222. package/docs/language-reference.md +162 -12
  223. package/gallery/fixtures/sketch.dgmo +2 -2
  224. package/gallery/fixtures/whiteboard.dgmo +18 -0
  225. package/package.json +2 -1
  226. package/dist/chunk-6EK55ART.js +0 -116
  227. package/dist/chunk-HTNLLT34.js +0 -72
  228. package/dist/dgmo-router-7WANZOBD.js +0 -92
  229. package/dist/renderer-TJJGFQGT.js +0 -78
  230. package/dist/{chunk-3FCSOYN4.js → chunk-5P2L5QXE.js} +0 -0
  231. package/dist/{chunk-HJRP6PXL.js → chunk-BQVTV4LB.js} +0 -0
  232. package/dist/{chunk-LYRDIVSY.js → chunk-GLCMUTO5.js} +0 -0
  233. package/dist/{chunk-5LE2CTLS.js → chunk-NTJ5HVV6.js} +3 -3
@@ -7,6 +7,9 @@ import {
7
7
  import {
8
8
  ScaleContext
9
9
  } from "./chunk-24YPR5EH.js";
10
+ import {
11
+ renderIntegratedLegend
12
+ } from "./chunk-QVKTAU7P.js";
10
13
  import {
11
14
  ARROWHEAD_HEIGHT,
12
15
  ARROWHEAD_WIDTH,
@@ -17,18 +20,15 @@ import {
17
20
  import {
18
21
  layoutInlineHeader
19
22
  } from "./chunk-CNUN5GO6.js";
23
+ import {
24
+ getLegendExtent,
25
+ getMaxLegendReservedHeight
26
+ } from "./chunk-NS7L7YKH.js";
20
27
  import {
21
28
  TITLE_FONT_SIZE,
22
29
  TITLE_FONT_WEIGHT,
23
30
  TITLE_Y
24
31
  } from "./chunk-VLC6WNRD.js";
25
- import {
26
- renderIntegratedLegend
27
- } from "./chunk-7SX4IY6T.js";
28
- import {
29
- getLegendExtent,
30
- getMaxLegendReservedHeight
31
- } from "./chunk-NS7L7YKH.js";
32
32
  import {
33
33
  FONT_FAMILY
34
34
  } from "./chunk-ICLB27PZ.js";
@@ -36,16 +36,16 @@ import {
36
36
  isSequenceBlock,
37
37
  isSequenceNote,
38
38
  isSequenceSection
39
- } from "./chunk-LFKSHDVI.js";
39
+ } from "./chunk-ZB2Q67NO.js";
40
40
  import {
41
41
  resolveActiveTagGroup,
42
42
  tagAttrKey
43
- } from "./chunk-L5AFONQU.js";
43
+ } from "./chunk-QKJ6MQXW.js";
44
44
  import {
45
45
  fillModeFromOptions,
46
46
  legendInlineRequested,
47
47
  legendSuppressed
48
- } from "./chunk-44GP7PD5.js";
48
+ } from "./chunk-MPJQMMBZ.js";
49
49
  import {
50
50
  legendChromeColors
51
51
  } from "./chunk-I6GGMCTV.js";
@@ -234,7 +234,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
234
234
  const groupKey = tagAttrKey(group.name);
235
235
  const participantMeta = /* @__PURE__ */ new Map();
236
236
  for (const p of parsed.participants) {
237
- participantMeta.set(p.id, { ...p.metadata || {} });
237
+ participantMeta.set(p.id, { ...p.metadata ?? {} });
238
238
  }
239
239
  propagateGroupTags(participantMeta, parsed.groups);
240
240
  const inherited = computeReceiverInheritance(
@@ -332,7 +332,7 @@ function wrapLabelWords(words, maxWidth, fontSize) {
332
332
  return lines;
333
333
  }
334
334
  var fill = (palette, isDark, color, fillMode) => color ? shapeFill(palette, color, isDark, { mode: fillMode }) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
335
- var stroke = (palette, color) => color || palette.border;
335
+ var stroke = (palette, color) => color ?? palette.border;
336
336
  var SW = 1.5;
337
337
  var W = PARTICIPANT_BOX_WIDTH;
338
338
  var H = PARTICIPANT_BOX_HEIGHT;
@@ -664,10 +664,29 @@ function applyGroupOrdering(participants, groups, messages = []) {
664
664
  }
665
665
  return result;
666
666
  }
667
- function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
668
- d3Selection.select(container).selectAll("*").remove();
667
+ var BLOCK_HEADER_SPACE = 30;
668
+ var BLOCK_AFTER_SPACE = 15;
669
+ var FRAME_PADDING_TOP = 42;
670
+ var FRAME_PADDING_X = 30;
671
+ var FRAME_PADDING_BOTTOM = 15;
672
+ var FRAME_LABEL_HEIGHT = 18;
673
+ var SELF_ARROW_FRAME_PAD = 10;
674
+ var GROUP_PADDING_X = 15;
675
+ var GROUP_NEST_INSET_X = 6;
676
+ var GROUP_NEST_MIN_PAD_X = 4;
677
+ var GROUP_NEST_INSET_Y = 6;
678
+ var GROUP_PADDING_TOP = 22;
679
+ var GROUP_PADDING_BOTTOM = 8;
680
+ var GROUP_LABEL_SIZE = 11;
681
+ var GROUP_LABEL_WEIGHT = "bold";
682
+ function groupBottomPad(depth, maxGroupDepth) {
683
+ return GROUP_PADDING_BOTTOM + (maxGroupDepth - depth) * GROUP_NEST_INSET_Y;
684
+ }
685
+ function isSectionCollapsed(sec, collapsedSections) {
686
+ return collapsedSections?.has(sec.lineNumber) ?? false;
687
+ }
688
+ function layoutSequence(parsed, containerWidth, options) {
669
689
  const { title, options: parsedOptions } = parsed;
670
- const fillMode = fillModeFromOptions(parsedOptions);
671
690
  const effectiveCollapsedGroups = options?.collapsedGroups ? new Set(options.collapsedGroups) : new Set(
672
691
  parsed.groups.filter((g) => g.collapsed).map((g) => g.lineNumber)
673
692
  );
@@ -694,7 +713,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
694
713
  const participants = applyPositionOverrides(
695
714
  applyGroupOrdering(sourceParticipants, groups, messages)
696
715
  );
697
- if (participants.length === 0) return;
716
+ if (participants.length === 0) return null;
698
717
  const groupBoundaryIds = /* @__PURE__ */ new Set();
699
718
  if (groups.length > 0) {
700
719
  const pToG = /* @__PURE__ */ new Map();
@@ -733,7 +752,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
733
752
  participants.length * effectiveGap,
734
753
  uniformBoxWidth + 40
735
754
  );
736
- const containerWidth = options?.exportWidth ?? container.getBoundingClientRect().width;
737
755
  const ctx = options?.exportWidth ? ScaleContext.identity() : ScaleContext.from(containerWidth, idealWidth);
738
756
  const sGap = ctx.structural(effectiveGap);
739
757
  const sBoxW = ctx.structural(uniformBoxWidth);
@@ -786,21 +804,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
786
804
  parsedOptions["active-tag"],
787
805
  options?.activeTagGroup
788
806
  ) ?? void 0;
789
- let tagMap;
790
- const tagValueToColor = /* @__PURE__ */ new Map();
791
- if (activeTagGroup) {
792
- tagMap = resolveSequenceTags(parsed, activeTagGroup);
793
- const tg = parsed.tagGroups.find(
794
- (g) => g.name.toLowerCase() === activeTagGroup.toLowerCase()
795
- );
796
- if (tg) {
797
- for (const entry of tg.entries) {
798
- tagValueToColor.set(entry.value.toLowerCase(), entry.color);
799
- }
800
- }
801
- }
802
- const getTagColor = (value) => value ? tagValueToColor.get(value.toLowerCase()) : void 0;
803
- const tagKey = activeTagGroup ? tagAttrKey(activeTagGroup) : void 0;
804
807
  const hiddenMsgIndices = /* @__PURE__ */ new Set();
805
808
  if (collapsedSections && collapsedSections.size > 0) {
806
809
  const sectionGroups = groupMessagesBySection(elements, messages);
@@ -836,9 +839,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
836
839
  };
837
840
  });
838
841
  const stepSpacing = 35;
839
- const BLOCK_HEADER_SPACE = 30;
840
- const BLOCK_AFTER_SPACE = 15;
841
- const FRAME_PADDING_TOP = 42;
842
842
  const msgToFirstStep = /* @__PURE__ */ new Map();
843
843
  const msgToLastStep = /* @__PURE__ */ new Map();
844
844
  renderSteps.forEach((step, si) => {
@@ -907,13 +907,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
907
907
  }
908
908
  return -1;
909
909
  };
910
- const isSectionCollapsed = (sec) => collapsedSections?.has(sec.lineNumber) ?? false;
911
- const collapsedBandPad = (sec) => isSectionCollapsed(sec) ? COLLAPSED_SECTION_BAND_EXTRA / 2 : 0;
910
+ const collapsedBandPad = (sec) => isSectionCollapsed(sec, collapsedSections) ? COLLAPSED_SECTION_BAND_EXTRA / 2 : 0;
912
911
  const SECTION_TOP_PAD = 35;
913
912
  const SECTION_BOTTOM_PAD = 45;
914
913
  const extraBeforeMsg = /* @__PURE__ */ new Map();
915
914
  const addExtra = (msgIdx, amount) => {
916
- extraBeforeMsg.set(msgIdx, (extraBeforeMsg.get(msgIdx) || 0) + amount);
915
+ extraBeforeMsg.set(msgIdx, (extraBeforeMsg.get(msgIdx) ?? 0) + amount);
917
916
  };
918
917
  const markBlockSpacing = (els) => {
919
918
  for (let i = 0; i < els.length; i++) {
@@ -1093,7 +1092,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1093
1092
  trailingSections.push(region.section);
1094
1093
  continue;
1095
1094
  }
1096
- const existing = sectionsBeforeStep.get(filteredStep) || [];
1095
+ const existing = sectionsBeforeStep.get(filteredStep) ?? [];
1097
1096
  existing.push(region.section);
1098
1097
  sectionsBeforeStep.set(filteredStep, existing);
1099
1098
  }
@@ -1102,15 +1101,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1102
1101
  sectionMsgCounts.set(region.section.lineNumber, region.msgIndices.length);
1103
1102
  }
1104
1103
  const sEdgePad = ctx.aesthetic(20);
1105
- const GROUP_PADDING_X = 15;
1106
- const GROUP_NEST_INSET_X = 6;
1107
- const GROUP_NEST_MIN_PAD_X = 4;
1108
- const GROUP_NEST_INSET_Y = 6;
1109
- const GROUP_PADDING_TOP = 22;
1110
- const GROUP_PADDING_BOTTOM = 8;
1111
- const GROUP_LABEL_SIZE = 11;
1112
1104
  const sGroupLabelSize = ctx.text(GROUP_LABEL_SIZE);
1113
- const GROUP_LABEL_WEIGHT = "bold";
1114
1105
  const showTitle = !!title && parsedOptions["no-title"] !== "on";
1115
1106
  const titleOffset = showTitle ? sTitleHeight : 0;
1116
1107
  const LEGEND_FIXED_GAP = 8;
@@ -1152,11 +1143,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1152
1143
  });
1153
1144
  const maxGroupDepth = parsed.groups.reduce((d, g) => Math.max(d, g.depth), 0);
1154
1145
  const groupOffset = parsed.groups.length > 0 ? (maxGroupDepth + 1) * GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
1155
- const groupBottomPad = (depth) => GROUP_PADDING_BOTTOM + (maxGroupDepth - depth) * GROUP_NEST_INSET_Y;
1156
1146
  const participantStartY = sTopMargin + titleOffset + (header.inline ? 0 : legendTopSpace) + sParticipantYOffset + groupOffset;
1157
1147
  const lifelineStartY0 = participantStartY + sBoxH;
1158
1148
  const hasActors = participants.some((p) => p.type === "actor");
1159
- const messageStartOffset = sMsgStartOffset + (hasActors ? 20 : 0) + (parsed.groups.length > 0 ? groupBottomPad(0) : 0);
1149
+ const messageStartOffset = sMsgStartOffset + (hasActors ? 20 : 0) + (parsed.groups.length > 0 ? groupBottomPad(0, maxGroupDepth) : 0);
1160
1150
  const totalGaps = participants.length > 1 ? participants.length - 1 : 0;
1161
1151
  const numWithinGaps = totalGaps - numGroupGaps;
1162
1152
  let sWithinGap = sGap;
@@ -1223,7 +1213,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1223
1213
  }
1224
1214
  const step = renderSteps[i];
1225
1215
  if (msgToFirstStep.get(step.messageIndex) === i) {
1226
- const extra = extraBeforeMsg.get(step.messageIndex) || 0;
1216
+ const extra = extraBeforeMsg.get(step.messageIndex) ?? 0;
1227
1217
  curY += extra;
1228
1218
  }
1229
1219
  curY += labelRoomBefore[i];
@@ -1303,6 +1293,20 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1303
1293
  noteTopY + noteH + NOTE_TRAILING_GAP
1304
1294
  );
1305
1295
  }
1296
+ const noteLayouts = /* @__PURE__ */ new Map();
1297
+ for (const [note, topY] of noteYMap) {
1298
+ const position = effectiveNotePosition(note);
1299
+ const afterSelfCall = isNoteAfterSelfCall(note);
1300
+ const maxW = noteEffectiveMaxW(note.participantId, position, afterSelfCall);
1301
+ noteLayouts.set(note, {
1302
+ topY,
1303
+ position,
1304
+ afterSelfCall,
1305
+ maxW,
1306
+ textWidth: noteTextWidth(maxW)
1307
+ });
1308
+ }
1309
+ const stepLabels = renderSteps.map((step) => displayMessageLabel(step));
1306
1310
  const messageAreaHeight = contentBottomY - lifelineStartY0;
1307
1311
  const lifelineLength = messageAreaHeight + sLifelineTail;
1308
1312
  const edgeProjection = (id) => {
@@ -1434,82 +1438,35 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1434
1438
  offsetX = leftMargin + Math.max(0, (svgWidth - totalWidth) / 2);
1435
1439
  buildParticipantX();
1436
1440
  }
1437
- const svg = d3Selection.select(container).append("svg").attr("width", svgWidth).attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
1438
- if (ctx.isBelowFloor) {
1439
- svg.attr("width", "100%");
1440
- }
1441
- const defs = svg.append("defs");
1442
- defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polygon").attr("points", `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`).attr("fill", palette.text);
1443
- defs.append("marker").attr("id", "seq-arrowhead-open").attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.2);
1444
- defs.append("marker").attr("id", "seq-arrowhead-async").attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`).attr("fill", "none").attr("stroke", palette.text).attr("stroke-width", 1.2);
1445
- const arrowPoints = `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`;
1446
- for (const [, color] of tagValueToColor) {
1447
- const hex = color.replace("#", "");
1448
- defs.append("marker").attr("id", `seq-arrowhead-c${hex}`).attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polygon").attr("points", arrowPoints).attr("fill", color);
1449
- defs.append("marker").attr("id", `seq-arrowhead-async-c${hex}`).attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", arrowPoints).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1.2);
1450
- defs.append("marker").attr("id", `seq-arrowhead-open-c${hex}`).attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", arrowPoints).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1.2);
1451
- }
1452
- const coloredMarker = (type, tagColor) => {
1453
- if (tagColor) {
1454
- const hex = tagColor.replace("#", "");
1455
- switch (type) {
1456
- case "call":
1457
- return `url(#seq-arrowhead-c${hex})`;
1458
- case "async":
1459
- return `url(#seq-arrowhead-async-c${hex})`;
1460
- case "return":
1461
- return `url(#seq-arrowhead-open-c${hex})`;
1462
- }
1463
- }
1464
- switch (type) {
1465
- case "call":
1466
- return "url(#seq-arrowhead)";
1467
- case "async":
1468
- return "url(#seq-arrowhead-async)";
1469
- case "return":
1470
- return "url(#seq-arrowhead-open)";
1471
- }
1472
- };
1473
- if (showTitle) {
1474
- 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);
1475
- if (parsed.titleLineNumber) {
1476
- titleEl.attr("data-line-number", parsed.titleLineNumber);
1477
- }
1478
- }
1479
- const hasTagGroups = parsed.tagGroups.length > 0 && !noLegend;
1480
- const collapsedGroupNames = /* @__PURE__ */ new Set();
1481
- const collapsedGroupMeta = /* @__PURE__ */ new Map();
1482
- for (const group of parsed.groups) {
1483
- if (effectiveCollapsedGroups.has(group.lineNumber)) {
1484
- collapsedGroupNames.add(group.name);
1485
- collapsedGroupMeta.set(group.name, {
1486
- lineNumber: group.lineNumber,
1487
- depth: group.depth,
1488
- participantIds: group.participantIds,
1489
- ...group.metadata !== void 0 && { metadata: group.metadata }
1490
- });
1491
- }
1492
- }
1493
- const participantLabels = new Map(
1494
- parsed.participants.map((p) => [String(p.id), p.label])
1495
- );
1496
- const groupToggleName = (groupName, memberIds) => {
1497
- const labels = memberIds.map((id) => participantLabels.get(id) ?? id);
1498
- return labels.length > 0 ? `${groupName}, ${labels.join(", ")}` : groupName;
1499
- };
1500
- const participantTagColor = (id) => {
1501
- const direct = getTagColor(tagMap?.participants.get(id));
1502
- if (direct) return direct;
1503
- if (!collapsedGroupNames.has(id) || !tagKey) return void 0;
1504
- const meta = collapsedGroupMeta.get(id);
1505
- return meta?.metadata ? getTagColor(meta.metadata[tagKey]) : void 0;
1506
- };
1441
+ const collapsedGroupBoxes = /* @__PURE__ */ new Map();
1507
1442
  const hasExpandedGroup = groups.some(
1508
1443
  (g) => g.participantIds.some((id) => participantX.has(id))
1509
1444
  );
1445
+ for (const group of parsed.groups) {
1446
+ if (!effectiveCollapsedGroups.has(group.lineNumber)) continue;
1447
+ const padTop = hasExpandedGroup ? (maxGroupDepth + 1 - group.depth) * GROUP_PADDING_TOP : 0;
1448
+ const padBottom = hasExpandedGroup ? groupBottomPad(group.depth, maxGroupDepth) : 0;
1449
+ const height = sBoxH + padTop + padBottom;
1450
+ const barHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
1451
+ collapsedGroupBoxes.set(group.name, {
1452
+ lineNumber: group.lineNumber,
1453
+ depth: group.depth,
1454
+ participantIds: group.participantIds,
1455
+ ...group.metadata !== void 0 && { metadata: group.metadata },
1456
+ padTop,
1457
+ padBottom,
1458
+ height,
1459
+ // Centred in the box ABOVE the bar, so the bar does not push the name
1460
+ // off-centre the way it would if the whole box were used.
1461
+ nameY: -padTop + (height - barHeight) / 2,
1462
+ barY: -padTop + height - barHeight,
1463
+ barHeight
1464
+ });
1465
+ }
1510
1466
  const framedGroups = [...groups].sort((a, b) => a.depth - b.depth);
1511
1467
  const allColumnXs = [...participantX.values()].sort((a, b) => a - b);
1512
1468
  const frameExtents = /* @__PURE__ */ new Map();
1469
+ const groupFrames = [];
1513
1470
  for (const group of framedGroups) {
1514
1471
  if (group.participantIds.length === 0) continue;
1515
1472
  const memberXs = group.participantIds.map((id) => participantX.get(id)).filter((x) => x !== void 0);
@@ -1535,118 +1492,18 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1535
1492
  }
1536
1493
  frameExtents.set(group.name, { minX, maxX });
1537
1494
  const boxY = participantStartY - (maxGroupDepth + 1 - group.depth) * GROUP_PADDING_TOP;
1538
- const boxH = sBoxH + (participantStartY - boxY) + groupBottomPad(group.depth);
1539
- const groupTagValue = tagKey && group.metadata?.[tagKey];
1540
- const groupTagColor = getTagColor(groupTagValue || void 0);
1541
- const fillColor = groupTagColor ? mix(groupTagColor, themeBaseBg(palette, isDark), isDark ? 15 : 20) : isDark ? palette.surface : palette.bg;
1542
- const strokeColor = groupTagColor || palette.textMuted;
1543
- const groupG = svg.append("g").attr("class", "group-box-wrapper").attr("data-group-toggle", "").attr("data-group-line", String(group.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("aria-label", groupToggleName(group.name, group.participantIds)).attr("cursor", "pointer");
1544
- groupG.append("rect").attr("x", minX).attr("y", boxY).attr("width", maxX - minX).attr("height", boxH).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("pointer-events", "none").attr("class", "group-box");
1545
- groupG.append("rect").attr("x", minX).attr("y", boxY).attr("width", maxX - minX).attr("height", participantStartY - boxY).attr("fill", "transparent").attr("class", "group-label-hit");
1546
- groupG.append("text").attr("x", (minX + maxX) / 2).attr("y", boxY + sGroupLabelSize + 4).attr("text-anchor", "middle").attr("fill", strokeColor).attr("font-size", sGroupLabelSize).attr("font-weight", GROUP_LABEL_WEIGHT).attr("opacity", 0.7).attr("pointer-events", "none").attr("class", "group-label").text(group.name);
1547
- }
1548
- const bandLeftmostX = Math.min(...Array.from(participantX.values()));
1549
- const bandRightmostX = Math.max(...Array.from(participantX.values()));
1550
- const sectionLineX1 = bandLeftmostX - sBoxW / 2 - 10;
1551
- const sectionLineX2 = bandRightmostX + sBoxW / 2 + 10;
1552
- const sectionLabelX = (sectionLineX1 + sectionLineX2) / 2;
1553
- const sectionLabelTextFor = (sec) => {
1554
- if (!isSectionCollapsed(sec)) return sec.label;
1555
- const count = sectionMsgCounts.get(sec.lineNumber) ?? 0;
1556
- return `${sec.label} (${count} ${count === 1 ? "message" : "messages"})`;
1557
- };
1558
- const sectionLabelBaselineFor = (sec, secY) => isSectionCollapsed(sec) ? secY + COLLAPSED_SECTION_LABEL_BASELINE : secY + 4;
1559
- const sectionLabelBoxes = sectionRegions.map((region) => {
1560
- const secY = sectionYPositions.get(region.section.lineNumber);
1561
- if (secY === void 0) return null;
1562
- const width = measureText(sectionLabelTextFor(region.section), 11, {
1563
- bold: true
1495
+ groupFrames.push({
1496
+ group,
1497
+ x: minX,
1498
+ y: boxY,
1499
+ width: maxX - minX,
1500
+ height: sBoxH + (participantStartY - boxY) + groupBottomPad(group.depth, maxGroupDepth),
1501
+ headerHeight: participantStartY - boxY,
1502
+ // Centred across the header strip.
1503
+ labelX: (minX + maxX) / 2,
1504
+ labelY: boxY + sGroupLabelSize + 4
1564
1505
  });
1565
- const baseline = sectionLabelBaselineFor(region.section, secY);
1566
- return {
1567
- x1: sectionLabelX - width / 2 - SECTION_LABEL_CLEARANCE_X,
1568
- x2: sectionLabelX + width / 2 + SECTION_LABEL_CLEARANCE_X,
1569
- y1: baseline - SECTION_LABEL_CLEARANCE_ABOVE,
1570
- y2: baseline + SECTION_LABEL_CLEARANCE_BELOW
1571
- };
1572
- }).filter((box) => box !== null);
1573
- const lifelineSegments = (cx, yTop, yBottom) => {
1574
- const cuts = sectionLabelBoxes.filter((b) => cx >= b.x1 && cx <= b.x2 && b.y2 > yTop && b.y1 < yBottom).sort((a, b) => a.y1 - b.y1);
1575
- const segments = [];
1576
- let y = yTop;
1577
- for (const cut of cuts) {
1578
- if (cut.y1 > y) segments.push([y, cut.y1]);
1579
- y = Math.max(y, cut.y2);
1580
- }
1581
- if (y < yBottom) segments.push([y, yBottom]);
1582
- return segments;
1583
- };
1584
- const lifelineStartY = lifelineStartY0;
1585
- participants.forEach((participant) => {
1586
- const cx = participantX.get(participant.id);
1587
- const cy = participantStartY;
1588
- const pTagValue = tagMap?.participants.get(participant.id);
1589
- const pTagAttr = tagKey && pTagValue ? { key: tagKey, value: pTagValue.toLowerCase() } : void 0;
1590
- const isCollapsedGroup = collapsedGroupNames.has(participant.id);
1591
- const effectiveTagColor = participantTagColor(participant.id);
1592
- renderParticipant(
1593
- svg,
1594
- participant,
1595
- cx,
1596
- cy,
1597
- palette,
1598
- isDark,
1599
- effectiveTagColor,
1600
- pTagAttr,
1601
- fillMode,
1602
- sBoxW,
1603
- sBoxH,
1604
- sLabelTextWidth,
1605
- sLabelFontSize,
1606
- isCollapsedGroup
1607
- );
1608
- if (isCollapsedGroup) {
1609
- const meta = collapsedGroupMeta.get(participant.id);
1610
- const drillColor = effectiveTagColor ?? mix(palette.textMuted, themeBaseBg(palette, isDark), 55);
1611
- const drillBarH = ctx.structural(COLLAPSE_BAR_HEIGHT);
1612
- const boxW = sBoxW;
1613
- const padTop = hasExpandedGroup ? (maxGroupDepth + 1 - meta.depth) * GROUP_PADDING_TOP : 0;
1614
- const padBottom = hasExpandedGroup ? groupBottomPad(meta.depth) : 0;
1615
- const fullH = sBoxH + padTop + padBottom;
1616
- const clipId = `clip-drill-group-${participant.id.replace(/[^a-zA-Z0-9-]/g, "-")}`;
1617
- const participantG = svg.select(
1618
- `.participant[data-participant-id="${participant.id}"]`
1619
- );
1620
- participantG.attr("data-group-toggle", "").attr("data-group-line", String(meta.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "false").attr(
1621
- "aria-label",
1622
- groupToggleName(participant.id, meta.participantIds)
1623
- ).attr("cursor", "pointer");
1624
- const pFill = effectiveTagColor ? mix(effectiveTagColor, themeBaseBg(palette, isDark), isDark ? 30 : 40) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
1625
- const pStroke = effectiveTagColor || palette.border;
1626
- participantG.append("rect").attr("x", -boxW / 2).attr("y", -padTop).attr("width", boxW).attr("height", fullH).attr("rx", 6).attr("fill", pFill).attr("stroke", pStroke).attr("stroke-width", 1.5);
1627
- const nameY = -padTop + (fullH - drillBarH) / 2;
1628
- participantG.append("text").attr("x", 0).attr("y", nameY).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", sLabelFontSize).attr("font-weight", "normal").attr("class", "collapsed-group-label").text(truncateText(participant.label, sLabelFontSize, sLabelTextWidth));
1629
- participantG.append("clipPath").attr("id", clipId).append("rect").attr("x", -boxW / 2).attr("y", -padTop).attr("width", boxW).attr("height", fullH).attr("rx", 6);
1630
- participantG.append("rect").attr("class", "sequence-drill-bar").attr("x", -boxW / 2).attr("y", -padTop + fullH - drillBarH).attr("width", boxW).attr("height", drillBarH).attr("fill", drillColor).attr("clip-path", `url(#${clipId})`);
1631
- }
1632
- const llY = isCollapsedGroup ? lifelineStartY + (hasExpandedGroup ? groupBottomPad(collapsedGroupMeta.get(participant.id)?.depth ?? 0) : 0) : participant.type === "actor" ? lifelineStartY + ACTOR_LABEL_CLEARANCE : lifelineStartY;
1633
- const llColor = effectiveTagColor || palette.textMuted;
1634
- for (const [segTop, segBottom] of lifelineSegments(
1635
- cx,
1636
- llY,
1637
- lifelineStartY + lifelineLength
1638
- )) {
1639
- const lifelineEl = svg.append("line").attr("x1", cx).attr("y1", segTop).attr("x2", cx).attr("y2", segBottom).attr("stroke", llColor).attr("stroke-width", 1).attr("stroke-dasharray", EDGE_DASH).attr("class", "lifeline").attr("data-participant-id", participant.id);
1640
- if (tagKey && pTagValue) {
1641
- lifelineEl.attr(`data-tag-${tagKey}`, pTagValue.toLowerCase());
1642
- }
1643
- }
1644
- });
1645
- const FRAME_PADDING_X = 30;
1646
- const FRAME_PADDING_BOTTOM = 15;
1647
- const FRAME_LABEL_HEIGHT = 18;
1648
- const SELF_ARROW_PROJECTION = sActivationWidth / 2 + sSelfCallWidth;
1649
- const SELF_ARROW_FRAME_PAD = 10;
1506
+ }
1650
1507
  const collectMsgIndices = (els) => {
1651
1508
  const indices = [];
1652
1509
  for (const el of els) {
@@ -1667,22 +1524,25 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1667
1524
  }
1668
1525
  return indices;
1669
1526
  };
1670
- const deferredLabels = [];
1671
- const deferredLines = [];
1672
- const renderBlockFrames = (els, depth) => {
1527
+ const firstStepOf = (indices) => {
1528
+ let first = Infinity;
1529
+ for (const mi of indices) {
1530
+ const step = msgToFirstStep.get(mi);
1531
+ if (step !== void 0) first = Math.min(first, step);
1532
+ }
1533
+ return first;
1534
+ };
1535
+ const SELF_ARROW_PROJECTION = sActivationWidth / 2 + sSelfCallWidth;
1536
+ const blockFrames = [];
1537
+ const layoutBlockFrames = (els) => {
1673
1538
  for (const el of els) {
1674
1539
  if (!isSequenceBlock(el)) continue;
1675
1540
  const ifIndices = collectMsgIndices(el.children);
1676
- const elseIfBranchData = [];
1677
- if (el.elseIfBranches) {
1678
- for (const branch of el.elseIfBranches) {
1679
- elseIfBranchData.push({
1680
- label: branch.label,
1681
- indices: collectMsgIndices(branch.children),
1682
- lineNumber: branch.lineNumber
1683
- });
1684
- }
1685
- }
1541
+ const elseIfBranchData = (el.elseIfBranches ?? []).map((branch) => ({
1542
+ label: branch.label,
1543
+ indices: collectMsgIndices(branch.children),
1544
+ lineNumber: branch.lineNumber
1545
+ }));
1686
1546
  const elseIndices = collectMsgIndices(el.elseChildren);
1687
1547
  const allIndices = [
1688
1548
  ...ifIndices,
@@ -1734,93 +1594,467 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1734
1594
  const frameY = stepY(minStep) - FRAME_PADDING_TOP;
1735
1595
  const frameW = maxPX - minPX + FRAME_PADDING_X * 2 + extraRight;
1736
1596
  const frameH = stepY(maxStep) - stepY(minStep) + FRAME_PADDING_TOP + FRAME_PADDING_BOTTOM + (maxStepIsSelfCall ? sSelfCallHeight : 0);
1737
- svg.append("rect").attr("x", frameX).attr("y", frameY).attr("width", frameW).attr("height", frameH).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1).attr("stroke-dasharray", HAIRLINE_DASH).attr("rx", 3).attr("ry", 3).attr("class", "block-frame").attr("data-block-line", String(el.lineNumber));
1738
- deferredLabels.push({
1739
- x: frameX + 6,
1740
- y: frameY + FRAME_LABEL_HEIGHT - 4,
1741
- text: `${el.type} ${el.label}`,
1742
- bold: false,
1743
- italic: false,
1744
- blockLine: el.lineNumber
1745
- });
1597
+ const divider = (firstStep, text, lineNumber) => {
1598
+ const dividerY = stepY(firstStep) - BLOCK_HEADER_SPACE;
1599
+ return {
1600
+ x1: frameX,
1601
+ x2: frameX + frameW,
1602
+ y: dividerY,
1603
+ label: { x: frameX + 6, y: dividerY + 14, text },
1604
+ ...lineNumber !== void 0 && { lineNumber }
1605
+ };
1606
+ };
1607
+ const dividers = [];
1746
1608
  for (const branchData of elseIfBranchData) {
1747
- if (branchData.indices.length > 0) {
1748
- let firstBranchStep = Infinity;
1749
- for (const mi of branchData.indices) {
1750
- const first = msgToFirstStep.get(mi);
1751
- if (first !== void 0)
1752
- firstBranchStep = Math.min(firstBranchStep, first);
1753
- }
1754
- if (firstBranchStep < Infinity) {
1755
- const dividerY = stepY(firstBranchStep) - BLOCK_HEADER_SPACE;
1756
- deferredLines.push({
1757
- x1: frameX,
1758
- y1: dividerY,
1759
- x2: frameX + frameW,
1760
- y2: dividerY,
1761
- blockLine: branchData.lineNumber
1762
- });
1763
- deferredLabels.push({
1764
- x: frameX + 6,
1765
- y: dividerY + 14,
1766
- text: `else if ${branchData.label}`,
1767
- bold: false,
1768
- italic: false,
1769
- blockLine: branchData.lineNumber
1770
- });
1771
- }
1609
+ const firstBranchStep = firstStepOf(branchData.indices);
1610
+ if (firstBranchStep < Infinity) {
1611
+ dividers.push(
1612
+ divider(
1613
+ firstBranchStep,
1614
+ `else if ${branchData.label}`,
1615
+ branchData.lineNumber
1616
+ )
1617
+ );
1772
1618
  }
1773
1619
  }
1774
- if (elseIndices.length > 0) {
1775
- let firstElseStep = Infinity;
1776
- for (const mi of elseIndices) {
1777
- const first = msgToFirstStep.get(mi);
1778
- if (first !== void 0)
1779
- firstElseStep = Math.min(firstElseStep, first);
1780
- }
1781
- if (firstElseStep < Infinity) {
1782
- const dividerY = stepY(firstElseStep) - BLOCK_HEADER_SPACE;
1783
- deferredLines.push({
1784
- x1: frameX,
1785
- y1: dividerY,
1786
- x2: frameX + frameW,
1787
- y2: dividerY,
1788
- ...el.elseLineNumber !== void 0 && {
1789
- blockLine: el.elseLineNumber
1790
- }
1791
- });
1792
- deferredLabels.push({
1793
- x: frameX + 6,
1794
- y: dividerY + 14,
1795
- text: "else",
1796
- bold: false,
1797
- italic: false,
1798
- ...el.elseLineNumber !== void 0 && {
1799
- blockLine: el.elseLineNumber
1800
- }
1801
- });
1802
- }
1620
+ const firstElseStep = firstStepOf(elseIndices);
1621
+ if (firstElseStep < Infinity) {
1622
+ dividers.push(divider(firstElseStep, "else", el.elseLineNumber));
1803
1623
  }
1804
- renderBlockFrames(el.children, depth + 1);
1805
- if (el.elseIfBranches) {
1806
- for (const branch of el.elseIfBranches) {
1807
- renderBlockFrames(branch.children, depth + 1);
1808
- }
1624
+ blockFrames.push({
1625
+ lineNumber: el.lineNumber,
1626
+ x: frameX,
1627
+ y: frameY,
1628
+ width: frameW,
1629
+ height: frameH,
1630
+ label: {
1631
+ x: frameX + 6,
1632
+ y: frameY + FRAME_LABEL_HEIGHT - 4,
1633
+ text: `${el.type} ${el.label}`
1634
+ },
1635
+ dividers
1636
+ });
1637
+ layoutBlockFrames(el.children);
1638
+ for (const branch of el.elseIfBranches ?? []) {
1639
+ layoutBlockFrames(branch.children);
1809
1640
  }
1810
- renderBlockFrames(el.elseChildren, depth + 1);
1641
+ layoutBlockFrames(el.elseChildren);
1811
1642
  }
1812
1643
  };
1813
- if (elements && elements.length > 0) {
1814
- renderBlockFrames(elements, 0);
1644
+ if (elements && elements.length > 0) layoutBlockFrames(elements);
1645
+ const sectionLineX1 = Math.min(...Array.from(participantX.values())) - sBoxW / 2 - 10;
1646
+ const sectionLineX2 = Math.max(...Array.from(participantX.values())) + sBoxW / 2 + 10;
1647
+ const sectionLabelX = (sectionLineX1 + sectionLineX2) / 2;
1648
+ const sectionBands = [];
1649
+ for (const region of sectionRegions) {
1650
+ const sec = region.section;
1651
+ const secY = sectionYPositions.get(sec.lineNumber);
1652
+ if (secY === void 0) continue;
1653
+ const isCollapsed = isSectionCollapsed(sec, collapsedSections);
1654
+ const count = sectionMsgCounts.get(sec.lineNumber) ?? 0;
1655
+ const labelText = isCollapsed ? `${sec.label} (${count} ${count === 1 ? "message" : "messages"})` : sec.label;
1656
+ const height = isCollapsed ? COLLAPSED_SECTION_BAND_HEIGHT : SECTION_BAND_HEIGHT;
1657
+ const marks = [];
1658
+ let reach = null;
1659
+ if (isCollapsed) {
1660
+ const participation = summarizeSectionParticipation(
1661
+ messages,
1662
+ region.msgIndices
1663
+ );
1664
+ const involvedXs = participants.filter((p) => participation.has(p.id)).map((p) => participantX.get(p.id)).filter((x) => x !== void 0);
1665
+ if (involvedXs.length > 1) {
1666
+ reach = { x1: Math.min(...involvedXs), x2: Math.max(...involvedXs) };
1667
+ }
1668
+ for (const participant of participants) {
1669
+ const markX = participantX.get(participant.id);
1670
+ if (markX === void 0) continue;
1671
+ const part = participation.get(participant.id);
1672
+ marks.push({
1673
+ participantId: participant.id,
1674
+ x: markX,
1675
+ ...part !== void 0 && { part },
1676
+ radius: part ? sectionMarkRadius(part.sends + part.receives) : 2.5
1677
+ });
1678
+ }
1679
+ }
1680
+ sectionBands.push({
1681
+ region,
1682
+ collapsed: isCollapsed,
1683
+ y: secY,
1684
+ x: sectionLineX1 - 10,
1685
+ width: sectionLineX2 - sectionLineX1 + 20,
1686
+ height,
1687
+ labelText,
1688
+ labelX: sectionLabelX,
1689
+ labelBaseline: isCollapsed ? secY + COLLAPSED_SECTION_LABEL_BASELINE : secY + 4,
1690
+ // The section label is set at a fixed 11px, bold.
1691
+ labelWidth: measureText(labelText, 11, { bold: true }),
1692
+ markY: secY + COLLAPSED_SECTION_MARK_OFFSET,
1693
+ marks,
1694
+ reach
1695
+ });
1696
+ }
1697
+ const sectionLabelBoxes = sectionBands.map((band) => ({
1698
+ x1: band.labelX - band.labelWidth / 2 - SECTION_LABEL_CLEARANCE_X,
1699
+ x2: band.labelX + band.labelWidth / 2 + SECTION_LABEL_CLEARANCE_X,
1700
+ y1: band.labelBaseline - SECTION_LABEL_CLEARANCE_ABOVE,
1701
+ y2: band.labelBaseline + SECTION_LABEL_CLEARANCE_BELOW
1702
+ }));
1703
+ const lifelines = /* @__PURE__ */ new Map();
1704
+ for (const participant of participants) {
1705
+ const cx = participantX.get(participant.id);
1706
+ const collapsedBox = collapsedGroupBoxes.get(participant.id);
1707
+ const yTop = collapsedBox ? lifelineStartY0 + collapsedBox.padBottom : participant.type === "actor" ? lifelineStartY0 + ACTOR_LABEL_CLEARANCE : lifelineStartY0;
1708
+ const yBottom = lifelineStartY0 + lifelineLength;
1709
+ const cuts = sectionLabelBoxes.filter((b) => cx >= b.x1 && cx <= b.x2 && b.y2 > yTop && b.y1 < yBottom).sort((a, b) => a.y1 - b.y1);
1710
+ const segments = [];
1711
+ let y = yTop;
1712
+ for (const cut of cuts) {
1713
+ if (cut.y1 > y) segments.push([y, cut.y1]);
1714
+ y = Math.max(y, cut.y2);
1715
+ }
1716
+ if (y < yBottom) segments.push([y, yBottom]);
1717
+ lifelines.set(participant.id, segments);
1815
1718
  }
1816
- activations.forEach((act) => {
1719
+ const activationBars = activations.map((act) => {
1817
1720
  const px = participantX.get(act.participantId);
1818
- if (px === void 0) return;
1721
+ if (px === void 0) return null;
1819
1722
  const x = px - sActivationWidth / 2 + act.depth * ACTIVATION_NEST_OFFSET;
1820
1723
  const y1 = stepY(act.startStep);
1821
1724
  const rawY2 = stepY(act.endStep);
1822
1725
  const implicitY2 = rawY2 - labelRoomBefore[act.endStep] - ACTIVATION_IMPLICIT_END_CLEARANCE;
1823
1726
  const y2 = act.implicitEnd && implicitY2 > y1 + sActivationWidth ? implicitY2 : rawY2;
1727
+ return { x, y: y1, width: sActivationWidth, height: y2 - y1 };
1728
+ });
1729
+ const activeDepthAt = (pid, stepIdx) => {
1730
+ let maxDepth = -1;
1731
+ for (const act of activations) {
1732
+ if (act.participantId === pid && act.startStep <= stepIdx && stepIdx <= act.endStep && act.depth > maxDepth) {
1733
+ maxDepth = act.depth;
1734
+ }
1735
+ }
1736
+ return maxDepth;
1737
+ };
1738
+ const arrowEdgeX = (pid, stepIdx, side) => {
1739
+ const px = participantX.get(pid);
1740
+ const depth = activeDepthAt(pid, stepIdx);
1741
+ if (depth < 0) return px;
1742
+ const offset = depth * ACTIVATION_NEST_OFFSET;
1743
+ return side === "right" ? px + sActivationWidth / 2 + offset : px - sActivationWidth / 2 + offset;
1744
+ };
1745
+ const messageArrows = renderSteps.map((step, i) => {
1746
+ const fromX = participantX.get(step.from);
1747
+ const toX = participantX.get(step.to);
1748
+ if (fromX === void 0 || toX === void 0) return null;
1749
+ const y = stepY(i);
1750
+ if (step.from === step.to) {
1751
+ if (step.type !== "call") return null;
1752
+ const x = arrowEdgeX(step.from, i, "right");
1753
+ const loopX = x + sSelfCallWidth;
1754
+ return {
1755
+ kind: "self",
1756
+ x,
1757
+ loopX,
1758
+ y,
1759
+ returnY: y + sSelfCallHeight,
1760
+ label: { x: loopX + 5, y: y + sSelfCallHeight / 2 + 4 }
1761
+ };
1762
+ }
1763
+ const goingRight = fromX < toX;
1764
+ const x1 = arrowEdgeX(step.from, i, goingRight ? "right" : "left");
1765
+ const x2 = arrowEdgeX(step.to, i, goingRight ? "left" : "right");
1766
+ return {
1767
+ kind: step.type,
1768
+ x1,
1769
+ x2,
1770
+ y,
1771
+ label: {
1772
+ x: (x1 + x2) / 2,
1773
+ y: y - (step.type === "call" ? MSG_LABEL_OFFSET : RETURN_LABEL_OFFSET)
1774
+ }
1775
+ };
1776
+ });
1777
+ const noteBoxes = /* @__PURE__ */ new Map();
1778
+ const BULLET_BODY_INDENT = 10;
1779
+ for (const [note, noteLayout] of noteLayouts) {
1780
+ const px = participantX.get(note.participantId);
1781
+ if (px === void 0) continue;
1782
+ const { topY, position, afterSelfCall, maxW, textWidth } = noteLayout;
1783
+ const isRight = position === "right";
1784
+ const wrappedLines = wrapTextLines(note.text, textWidth, sNoteFontSize);
1785
+ const noteH = wrappedLines.length * sNoteLineH + sNotePadV * 2;
1786
+ const maxLineW = Math.max(
1787
+ ...wrappedLines.map((l) => measureText(l.text, sNoteFontSize))
1788
+ );
1789
+ const noteW = Math.min(
1790
+ maxW,
1791
+ Math.max(80, maxLineW + sNotePadH * 2 + sNoteFold)
1792
+ );
1793
+ const rightOffset = afterSelfCall && isRight ? sActivationWidth / 2 + sSelfCallWidth + sNoteGap : sActivationWidth + sNoteGap;
1794
+ const noteX = isRight ? px + rightOffset : px - sActivationWidth - sNoteGap - noteW;
1795
+ noteBoxes.set(note, {
1796
+ x: noteX,
1797
+ y: topY,
1798
+ width: noteW,
1799
+ height: noteH,
1800
+ lines: wrappedLines.map((line, li) => ({
1801
+ kind: line.kind,
1802
+ text: line.text,
1803
+ x: noteX + sNotePadH + (line.kind === "plain" ? 0 : BULLET_BODY_INDENT),
1804
+ y: topY + sNotePadV + (li + 1) * sNoteLineH - 3,
1805
+ ...line.kind === "bullet-first" && { bulletX: noteX + sNotePadH }
1806
+ }))
1807
+ });
1808
+ }
1809
+ return {
1810
+ ctx,
1811
+ participants,
1812
+ messages,
1813
+ elements,
1814
+ groups,
1815
+ effectiveCollapsedGroups,
1816
+ activeTagGroup,
1817
+ renderSteps,
1818
+ stepLabels,
1819
+ activations,
1820
+ activationBars,
1821
+ messageArrows,
1822
+ labelRoomBefore,
1823
+ msgToFirstStep,
1824
+ msgToLastStep,
1825
+ stepYPositions,
1826
+ sectionYPositions,
1827
+ sectionRegions,
1828
+ sectionMsgCounts,
1829
+ noteLayouts,
1830
+ noteBoxes,
1831
+ participantX,
1832
+ participantStartY,
1833
+ lifelineStartY0,
1834
+ lifelineLength,
1835
+ lifelines,
1836
+ groupFrames,
1837
+ collapsedGroupBoxes,
1838
+ blockFrames,
1839
+ sectionBands,
1840
+ maxGroupDepth,
1841
+ svgWidth,
1842
+ totalHeight,
1843
+ showTitle,
1844
+ titleOffset,
1845
+ noLegend,
1846
+ legendConfig,
1847
+ header,
1848
+ sTopMargin,
1849
+ sBoxW,
1850
+ sBoxH,
1851
+ sLabelFontSize,
1852
+ sLabelTextWidth,
1853
+ sGroupLabelSize,
1854
+ sActivationWidth,
1855
+ ACTIVATION_NEST_OFFSET,
1856
+ sArrowheadW,
1857
+ sArrowheadH,
1858
+ sSelfCallWidth,
1859
+ sSelfCallHeight,
1860
+ sNoteFold,
1861
+ sNoteFontSize,
1862
+ sNoteGap,
1863
+ sNoteLineH,
1864
+ sNotePadH,
1865
+ sNotePadV
1866
+ };
1867
+ }
1868
+ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
1869
+ d3Selection.select(container).selectAll("*").remove();
1870
+ const layout = layoutSequence(
1871
+ parsed,
1872
+ options?.exportWidth ?? container.getBoundingClientRect().width,
1873
+ options
1874
+ );
1875
+ if (!layout) return;
1876
+ const {
1877
+ ctx,
1878
+ participants,
1879
+ messages,
1880
+ elements,
1881
+ activeTagGroup,
1882
+ renderSteps,
1883
+ stepLabels,
1884
+ activations,
1885
+ activationBars,
1886
+ messageArrows,
1887
+ noteBoxes,
1888
+ participantX,
1889
+ participantStartY,
1890
+ lifelines,
1891
+ groupFrames,
1892
+ collapsedGroupBoxes,
1893
+ blockFrames,
1894
+ sectionBands,
1895
+ svgWidth,
1896
+ totalHeight,
1897
+ showTitle,
1898
+ titleOffset,
1899
+ noLegend,
1900
+ legendConfig,
1901
+ header,
1902
+ sTopMargin,
1903
+ sBoxW,
1904
+ sBoxH,
1905
+ sLabelFontSize,
1906
+ sLabelTextWidth,
1907
+ sGroupLabelSize,
1908
+ sArrowheadW,
1909
+ sArrowheadH,
1910
+ sSelfCallWidth,
1911
+ sSelfCallHeight,
1912
+ sNoteFold,
1913
+ sNoteFontSize
1914
+ } = layout;
1915
+ const { title, options: parsedOptions } = parsed;
1916
+ const fillMode = fillModeFromOptions(parsedOptions);
1917
+ let tagMap;
1918
+ const tagValueToColor = /* @__PURE__ */ new Map();
1919
+ if (activeTagGroup) {
1920
+ tagMap = resolveSequenceTags(parsed, activeTagGroup);
1921
+ const tg = parsed.tagGroups.find(
1922
+ (g) => g.name.toLowerCase() === activeTagGroup.toLowerCase()
1923
+ );
1924
+ if (tg) {
1925
+ for (const entry of tg.entries) {
1926
+ tagValueToColor.set(entry.value.toLowerCase(), entry.color);
1927
+ }
1928
+ }
1929
+ }
1930
+ const getTagColor = (value) => value ? tagValueToColor.get(value.toLowerCase()) : void 0;
1931
+ const tagKey = activeTagGroup ? tagAttrKey(activeTagGroup) : void 0;
1932
+ const svg = d3Selection.select(container).append("svg").attr("width", svgWidth).attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
1933
+ if (ctx.isBelowFloor) {
1934
+ svg.attr("width", "100%");
1935
+ }
1936
+ const defs = svg.append("defs");
1937
+ defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polygon").attr("points", `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`).attr("fill", palette.text);
1938
+ defs.append("marker").attr("id", "seq-arrowhead-open").attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.2);
1939
+ defs.append("marker").attr("id", "seq-arrowhead-async").attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`).attr("fill", "none").attr("stroke", palette.text).attr("stroke-width", 1.2);
1940
+ const arrowPoints = `0,0 ${sArrowheadW},${sArrowheadH / 2} 0,${sArrowheadH}`;
1941
+ for (const [, color] of tagValueToColor) {
1942
+ const hex = color.replace("#", "");
1943
+ defs.append("marker").attr("id", `seq-arrowhead-c${hex}`).attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polygon").attr("points", arrowPoints).attr("fill", color);
1944
+ defs.append("marker").attr("id", `seq-arrowhead-async-c${hex}`).attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", arrowPoints).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1.2);
1945
+ defs.append("marker").attr("id", `seq-arrowhead-open-c${hex}`).attr("viewBox", `0 0 ${sArrowheadW} ${sArrowheadH}`).attr("refX", sArrowheadW).attr("refY", sArrowheadH / 2).attr("markerWidth", sArrowheadW).attr("markerHeight", sArrowheadH).attr("orient", "auto").append("polyline").attr("points", arrowPoints).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1.2);
1946
+ }
1947
+ const coloredMarker = (type, tagColor) => {
1948
+ if (tagColor) {
1949
+ const hex = tagColor.replace("#", "");
1950
+ switch (type) {
1951
+ case "call":
1952
+ return `url(#seq-arrowhead-c${hex})`;
1953
+ case "async":
1954
+ return `url(#seq-arrowhead-async-c${hex})`;
1955
+ case "return":
1956
+ return `url(#seq-arrowhead-open-c${hex})`;
1957
+ }
1958
+ }
1959
+ switch (type) {
1960
+ case "call":
1961
+ return "url(#seq-arrowhead)";
1962
+ case "async":
1963
+ return "url(#seq-arrowhead-async)";
1964
+ case "return":
1965
+ return "url(#seq-arrowhead-open)";
1966
+ }
1967
+ };
1968
+ if (showTitle) {
1969
+ 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);
1970
+ if (parsed.titleLineNumber) {
1971
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
1972
+ }
1973
+ }
1974
+ const hasTagGroups = parsed.tagGroups.length > 0 && !noLegend;
1975
+ const participantLabels = new Map(
1976
+ parsed.participants.map((p) => [String(p.id), p.label])
1977
+ );
1978
+ const groupToggleName = (groupName, memberIds) => {
1979
+ const labels = memberIds.map((id) => participantLabels.get(id) ?? id);
1980
+ return labels.length > 0 ? `${groupName}, ${labels.join(", ")}` : groupName;
1981
+ };
1982
+ const participantTagColor = (id) => {
1983
+ const direct = getTagColor(tagMap?.participants.get(id));
1984
+ if (direct) return direct;
1985
+ if (!collapsedGroupBoxes.has(id) || !tagKey) return void 0;
1986
+ const meta = collapsedGroupBoxes.get(id);
1987
+ return meta?.metadata ? getTagColor(meta.metadata[tagKey]) : void 0;
1988
+ };
1989
+ for (const frame of groupFrames) {
1990
+ const { group } = frame;
1991
+ const groupTagValue = tagKey && group.metadata?.[tagKey];
1992
+ const groupTagColor = getTagColor(groupTagValue);
1993
+ const fillColor = groupTagColor ? mix(groupTagColor, themeBaseBg(palette, isDark), isDark ? 15 : 20) : isDark ? palette.surface : palette.bg;
1994
+ const strokeColor = groupTagColor ?? palette.textMuted;
1995
+ const groupG = svg.append("g").attr("class", "group-box-wrapper").attr("data-group-toggle", "").attr("data-group-line", String(group.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("aria-label", groupToggleName(group.name, group.participantIds)).attr("cursor", "pointer");
1996
+ groupG.append("rect").attr("x", frame.x).attr("y", frame.y).attr("width", frame.width).attr("height", frame.height).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("pointer-events", "none").attr("class", "group-box");
1997
+ groupG.append("rect").attr("x", frame.x).attr("y", frame.y).attr("width", frame.width).attr("height", frame.headerHeight).attr("fill", "transparent").attr("class", "group-label-hit");
1998
+ groupG.append("text").attr("x", frame.labelX).attr("y", frame.labelY).attr("text-anchor", "middle").attr("fill", strokeColor).attr("font-size", sGroupLabelSize).attr("font-weight", GROUP_LABEL_WEIGHT).attr("opacity", 0.7).attr("pointer-events", "none").attr("class", "group-label").text(group.name);
1999
+ }
2000
+ participants.forEach((participant) => {
2001
+ const cx = participantX.get(participant.id);
2002
+ const cy = participantStartY;
2003
+ const pTagValue = tagMap?.participants.get(participant.id);
2004
+ const pTagAttr = tagKey && pTagValue ? { key: tagKey, value: pTagValue.toLowerCase() } : void 0;
2005
+ const isCollapsedGroup = collapsedGroupBoxes.has(participant.id);
2006
+ const effectiveTagColor = participantTagColor(participant.id);
2007
+ renderParticipant(
2008
+ svg,
2009
+ participant,
2010
+ cx,
2011
+ cy,
2012
+ palette,
2013
+ isDark,
2014
+ effectiveTagColor,
2015
+ pTagAttr,
2016
+ fillMode,
2017
+ sBoxW,
2018
+ sBoxH,
2019
+ sLabelTextWidth,
2020
+ sLabelFontSize,
2021
+ isCollapsedGroup
2022
+ );
2023
+ if (isCollapsedGroup) {
2024
+ const meta = collapsedGroupBoxes.get(participant.id);
2025
+ const drillColor = effectiveTagColor ?? mix(palette.textMuted, themeBaseBg(palette, isDark), 55);
2026
+ const boxW = sBoxW;
2027
+ const { padTop, height: fullH, nameY, barY, barHeight } = meta;
2028
+ const clipId = `clip-drill-group-${participant.id.replace(/[^a-zA-Z0-9-]/g, "-")}`;
2029
+ const participantG = svg.select(
2030
+ `.participant[data-participant-id="${participant.id}"]`
2031
+ );
2032
+ participantG.attr("data-group-toggle", "").attr("data-group-line", String(meta.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "false").attr(
2033
+ "aria-label",
2034
+ groupToggleName(participant.id, meta.participantIds)
2035
+ ).attr("cursor", "pointer");
2036
+ const pFill = effectiveTagColor ? mix(effectiveTagColor, themeBaseBg(palette, isDark), isDark ? 30 : 40) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
2037
+ const pStroke = effectiveTagColor ?? palette.border;
2038
+ participantG.append("rect").attr("x", -boxW / 2).attr("y", -padTop).attr("width", boxW).attr("height", fullH).attr("rx", 6).attr("fill", pFill).attr("stroke", pStroke).attr("stroke-width", 1.5);
2039
+ participantG.append("text").attr("x", 0).attr("y", nameY).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", sLabelFontSize).attr("font-weight", "normal").attr("class", "collapsed-group-label").text(truncateText(participant.label, sLabelFontSize, sLabelTextWidth));
2040
+ participantG.append("clipPath").attr("id", clipId).append("rect").attr("x", -boxW / 2).attr("y", -padTop).attr("width", boxW).attr("height", fullH).attr("rx", 6);
2041
+ participantG.append("rect").attr("class", "sequence-drill-bar").attr("x", -boxW / 2).attr("y", barY).attr("width", boxW).attr("height", barHeight).attr("fill", drillColor).attr("clip-path", `url(#${clipId})`);
2042
+ }
2043
+ const llColor = effectiveTagColor ?? palette.textMuted;
2044
+ for (const [segTop, segBottom] of lifelines.get(participant.id) ?? []) {
2045
+ const lifelineEl = svg.append("line").attr("x1", cx).attr("y1", segTop).attr("x2", cx).attr("y2", segBottom).attr("stroke", llColor).attr("stroke-width", 1).attr("stroke-dasharray", EDGE_DASH).attr("class", "lifeline").attr("data-participant-id", participant.id);
2046
+ if (tagKey && pTagValue) {
2047
+ lifelineEl.attr(`data-tag-${tagKey}`, pTagValue.toLowerCase());
2048
+ }
2049
+ }
2050
+ });
2051
+ for (const frame of blockFrames) {
2052
+ svg.append("rect").attr("x", frame.x).attr("y", frame.y).attr("width", frame.width).attr("height", frame.height).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1).attr("stroke-dasharray", HAIRLINE_DASH).attr("rx", 3).attr("ry", 3).attr("class", "block-frame").attr("data-block-line", String(frame.lineNumber));
2053
+ }
2054
+ activations.forEach((act, ai) => {
2055
+ const bar = activationBars[ai];
2056
+ if (!bar) return;
2057
+ const { x, y: y1, height } = bar;
1824
2058
  const coveredLines = [];
1825
2059
  for (let si = act.startStep; si <= act.endStep; si++) {
1826
2060
  const step = renderSteps[si];
@@ -1832,162 +2066,125 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1832
2066
  const triggerMsg = triggerStep !== void 0 ? messages[triggerStep.messageIndex] : void 0;
1833
2067
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
1834
2068
  const actTagColor = getTagColor(actTagValue);
1835
- const actBaseColor = actTagColor || palette.primary;
1836
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
2069
+ const actBaseColor = actTagColor ?? palette.primary;
2070
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", bar.width).attr("height", height).attr("fill", themeBaseBg(palette, isDark));
1837
2071
  const actFill = shapeFill(palette, actBaseColor, isDark, {
1838
2072
  mode: fillMode
1839
2073
  });
1840
- const actRect = svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", actBaseColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("data-line-number", coveredLines[0] ?? "").attr("class", "activation");
2074
+ const actRect = svg.append("rect").attr("x", x).attr("y", y1).attr("width", bar.width).attr("height", height).attr("fill", actFill).attr("stroke", actBaseColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("data-line-number", coveredLines[0] ?? "").attr("class", "activation");
1841
2075
  if (tagKey && actTagValue) {
1842
2076
  actRect.attr(`data-tag-${tagKey}`, actTagValue.toLowerCase());
1843
2077
  }
1844
2078
  });
1845
- for (const ln of deferredLines) {
1846
- const line = svg.append("line").attr("x1", ln.x1).attr("y1", ln.y1).attr("x2", ln.x2).attr("y2", ln.y2).attr("stroke", palette.textMuted).attr("stroke-width", 1).attr("stroke-dasharray", HAIRLINE_DASH).attr("class", "block-divider");
1847
- if (ln.blockLine !== void 0)
1848
- line.attr("data-block-line", String(ln.blockLine));
2079
+ for (const divider of blockFrames.flatMap((frame) => frame.dividers)) {
2080
+ const line = svg.append("line").attr("x1", divider.x1).attr("y1", divider.y).attr("x2", divider.x2).attr("y2", divider.y).attr("stroke", palette.textMuted).attr("stroke-width", 1).attr("stroke-dasharray", HAIRLINE_DASH).attr("class", "block-divider");
2081
+ if (divider.lineNumber !== void 0)
2082
+ line.attr("data-block-line", String(divider.lineNumber));
1849
2083
  }
1850
- for (const lbl of deferredLabels) {
2084
+ const blockLabels = blockFrames.flatMap((frame) => [
2085
+ { ...frame.label, lineNumber: frame.lineNumber },
2086
+ ...frame.dividers.map((d) => ({ ...d.label, lineNumber: d.lineNumber }))
2087
+ ]);
2088
+ for (const lbl of blockLabels) {
1851
2089
  const t = svg.append("text").attr("x", lbl.x).attr("y", lbl.y).attr("fill", palette.text).attr("font-size", 11).attr("class", "block-label").text(lbl.text);
1852
- if (lbl.bold) t.attr("font-weight", "bold");
1853
- if (lbl.italic) t.attr("font-style", "italic");
1854
- if (lbl.blockLine !== void 0)
1855
- t.attr("data-block-line", String(lbl.blockLine));
2090
+ if (lbl.lineNumber !== void 0)
2091
+ t.attr("data-block-line", String(lbl.lineNumber));
1856
2092
  }
1857
- const activeDepthAt = (pid, stepIdx) => {
1858
- let maxDepth = -1;
1859
- for (const act of activations) {
1860
- if (act.participantId === pid && act.startStep <= stepIdx && stepIdx <= act.endStep && act.depth > maxDepth) {
1861
- maxDepth = act.depth;
1862
- }
1863
- }
1864
- return maxDepth;
1865
- };
1866
- const arrowEdgeX = (pid, stepIdx, side) => {
1867
- const px = participantX.get(pid);
1868
- const depth = activeDepthAt(pid, stepIdx);
1869
- if (depth < 0) return px;
1870
- const offset = depth * ACTIVATION_NEST_OFFSET;
1871
- return side === "right" ? px + sActivationWidth / 2 + offset : px - sActivationWidth / 2 + offset;
1872
- };
1873
- for (const region of sectionRegions) {
1874
- const sec = region.section;
1875
- const secY = sectionYPositions.get(sec.lineNumber);
1876
- if (secY === void 0) continue;
1877
- const isCollapsed = isSectionCollapsed(sec);
2093
+ for (const band of sectionBands) {
2094
+ const sec = band.region.section;
2095
+ const secY = band.y;
2096
+ const isCollapsed = band.collapsed;
1878
2097
  const lineColor = palette.textMuted;
1879
2098
  const sectionG = svg.append("g").attr("data-section-toggle", "").attr("data-line-number", String(sec.lineNumber)).attr("data-section", "").attr("tabindex", "0").attr("role", "button").attr("aria-expanded", String(!isCollapsed));
1880
- const BAND_HEIGHT = isCollapsed ? COLLAPSED_SECTION_BAND_HEIGHT : SECTION_BAND_HEIGHT;
1881
- const bandX = sectionLineX1 - 10;
1882
- const bandWidth = sectionLineX2 - sectionLineX1 + 20;
2099
+ const BAND_HEIGHT = band.height;
1883
2100
  const bandFill = isCollapsed ? legendChromeColors(palette, isDark).groupBg : lineColor;
1884
2101
  const bandOpacity = isCollapsed ? 1 : isDark ? 0.1 : 0.08;
1885
- sectionG.append("rect").attr("x", bandX).attr("y", secY - BAND_HEIGHT / 2).attr("width", bandWidth).attr("height", BAND_HEIGHT).attr("fill", bandFill).attr("opacity", bandOpacity).attr("rx", 2).attr("pointer-events", "none").attr("class", "section-divider");
1886
- const labelText = sectionLabelTextFor(sec);
1887
- const labelWidth = measureText(labelText, 11, { bold: true });
1888
- const labelX = sectionLabelX;
1889
- const labelBaseline = sectionLabelBaselineFor(sec, secY);
2102
+ sectionG.append("rect").attr("x", band.x).attr("y", secY - BAND_HEIGHT / 2).attr("width", band.width).attr("height", BAND_HEIGHT).attr("fill", bandFill).attr("opacity", bandOpacity).attr("rx", 2).attr("pointer-events", "none").attr("class", "section-divider");
2103
+ const { labelText, labelWidth, labelX, labelBaseline } = band;
1890
2104
  const labelHitW = Math.max(80, labelWidth + 24);
1891
2105
  sectionG.append("rect").attr("x", labelX - labelHitW / 2).attr("y", secY - BAND_HEIGHT / 2).attr("width", labelHitW).attr("height", BAND_HEIGHT).attr("fill", "transparent").attr("class", "section-label-hit");
1892
2106
  sectionG.append("text").attr("x", labelX).attr("y", labelBaseline).attr("text-anchor", "middle").attr("fill", lineColor).attr("font-size", 11).attr("font-weight", "bold").attr("class", "section-label").text(labelText);
1893
2107
  if (!isCollapsed) continue;
1894
- const participation = summarizeSectionParticipation(
1895
- messages,
1896
- region.msgIndices
1897
- );
1898
- const markY = secY + COLLAPSED_SECTION_MARK_OFFSET;
1899
- const involvedXs = participants.filter((p) => participation.has(p.id)).map((p) => participantX.get(p.id)).filter((x) => x !== void 0);
1900
- if (involvedXs.length > 1) {
1901
- sectionG.append("line").attr("x1", Math.min(...involvedXs)).attr("x2", Math.max(...involvedXs)).attr("y1", markY).attr("y2", markY).attr("stroke", lineColor).attr("stroke-width", 1).attr("stroke-dasharray", HAIRLINE_DASH).attr("opacity", 0.5).attr("pointer-events", "none").attr("class", "section-reach");
1902
- }
1903
- for (const participant of participants) {
1904
- const markX = participantX.get(participant.id);
1905
- if (markX === void 0) continue;
1906
- const part = participation.get(participant.id);
1907
- const mark = sectionG.append("circle").attr("cx", markX).attr("cy", markY).attr("pointer-events", "none").attr("data-participant-id", participant.id);
2108
+ const markY = band.markY;
2109
+ if (band.reach) {
2110
+ sectionG.append("line").attr("x1", band.reach.x1).attr("x2", band.reach.x2).attr("y1", markY).attr("y2", markY).attr("stroke", lineColor).attr("stroke-width", 1).attr("stroke-dasharray", HAIRLINE_DASH).attr("opacity", 0.5).attr("pointer-events", "none").attr("class", "section-reach");
2111
+ }
2112
+ for (const { participantId, x: markX, part, radius } of band.marks) {
2113
+ const mark = sectionG.append("circle").attr("cx", markX).attr("cy", markY).attr("pointer-events", "none").attr("data-participant-id", participantId);
1908
2114
  if (!part) {
1909
- mark.attr("r", 2.5).attr("fill", "none").attr("stroke", lineColor).attr("stroke-width", 1).attr("opacity", 0.5).attr("class", "section-mark section-mark-absent");
2115
+ mark.attr("r", radius).attr("fill", "none").attr("stroke", lineColor).attr("stroke-width", 1).attr("opacity", 0.5).attr("class", "section-mark section-mark-absent");
1910
2116
  continue;
1911
2117
  }
1912
- const markColor = participantTagColor(participant.id) ?? palette.text;
1913
- const radius = sectionMarkRadius(part.sends + part.receives);
1914
- sectionG.append("circle").attr("cx", markX).attr("cy", markY).attr("r", Math.max(radius, SECTION_MARK_HIT_RADIUS)).attr("fill", "transparent").attr("class", "section-mark-hit").attr("data-participant-id", participant.id).attr("data-section-mark-line", String(part.firstLine)).attr("cursor", "pointer");
2118
+ const markColor = participantTagColor(participantId) ?? palette.text;
2119
+ sectionG.append("circle").attr("cx", markX).attr("cy", markY).attr("r", Math.max(radius, SECTION_MARK_HIT_RADIUS)).attr("fill", "transparent").attr("class", "section-mark-hit").attr("data-participant-id", participantId).attr("data-section-mark-line", String(part.firstLine)).attr("cursor", "pointer");
1915
2120
  if (part.sends === 0) {
1916
2121
  mark.attr("r", radius).attr("fill", bandFill).attr("stroke", markColor).attr("stroke-width", 1.6).attr("class", "section-mark section-mark-receives");
1917
2122
  } else {
1918
2123
  mark.attr("r", radius).attr("fill", markColor).attr("class", "section-mark section-mark-sends");
1919
2124
  }
1920
2125
  }
1921
- const involvedLabels = participants.filter((p) => participation.has(p.id)).map((p) => p.label);
2126
+ const involved = new Set(
2127
+ band.marks.filter((m) => m.part).map((m) => m.participantId)
2128
+ );
2129
+ const involvedLabels = participants.filter((p) => involved.has(p.id)).map((p) => p.label);
1922
2130
  if (involvedLabels.length > 0) {
1923
2131
  sectionG.attr("aria-label", `${labelText}, ${involvedLabels.join(", ")}`);
1924
2132
  }
1925
2133
  }
1926
2134
  renderSteps.forEach((step, i) => {
1927
- const fromX = participantX.get(step.from);
1928
- const toX = participantX.get(step.to);
1929
- if (fromX === void 0 || toX === void 0) return;
1930
- const y = stepY(i);
2135
+ const arrow = messageArrows[i];
2136
+ if (!arrow) return;
2137
+ const { y, label } = arrow;
1931
2138
  const HIT_H = 20;
1932
2139
  const msg = messages[step.messageIndex];
1933
2140
  const msgTagValue = msg ? tagMap?.messages.get(msg.lineNumber) : void 0;
1934
2141
  const msgTagColor = getTagColor(msgTagValue);
1935
2142
  if (step.type === "call") {
1936
- const arrowColor = msgTagColor || palette.text;
1937
- if (step.from === step.to) {
1938
- const x = arrowEdgeX(step.from, i, "right");
1939
- const loopX = x + sSelfCallWidth;
2143
+ const arrowColor = msgTagColor ?? palette.text;
2144
+ if (arrow.kind === "self") {
2145
+ const { x, loopX, returnY } = arrow;
1940
2146
  const hitX = x;
1941
2147
  svg.append("rect").attr("x", hitX).attr("y", y - 5).attr("width", sSelfCallWidth).attr("height", sSelfCallHeight + 10).attr("fill", "transparent").attr("class", "message-hit-area").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
1942
- const selfCallEl = svg.append("path").attr("d", `M ${x} ${y} H ${loopX} V ${y + sSelfCallHeight} H ${x}`).attr("fill", "none").attr("stroke", arrowColor).attr("stroke-width", 1.2).attr("marker-end", coloredMarker("call", msgTagColor)).attr("class", "message-arrow self-call").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i)).attr("data-from", step.from).attr("data-to", step.to);
2148
+ const selfCallEl = svg.append("path").attr("d", `M ${x} ${y} H ${loopX} V ${returnY} H ${x}`).attr("fill", "none").attr("stroke", arrowColor).attr("stroke-width", 1.2).attr("marker-end", coloredMarker("call", msgTagColor)).attr("class", "message-arrow self-call").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i)).attr("data-from", step.from).attr("data-to", step.to);
1943
2149
  if (tagKey && msgTagValue) {
1944
2150
  selfCallEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
1945
2151
  }
1946
- const displayLabel = displayMessageLabel(step);
2152
+ const displayLabel = stepLabels[i];
1947
2153
  if (displayLabel) {
1948
- const labelEl = svg.append("text").attr("x", loopX + 5).attr("y", y + sSelfCallHeight / 2 + 4).attr("text-anchor", "start").attr("fill", arrowColor).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("font-size", 12).attr("class", "message-label").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
2154
+ const labelEl = svg.append("text").attr("x", label.x).attr("y", label.y).attr("text-anchor", "start").attr("fill", arrowColor).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("font-size", 12).attr("class", "message-label").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
1949
2155
  if (tagKey && msgTagValue) {
1950
2156
  labelEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
1951
2157
  }
1952
2158
  labelEl.text(displayLabel);
1953
2159
  }
1954
2160
  } else {
1955
- const goingRight = fromX < toX;
1956
- const x1 = arrowEdgeX(step.from, i, goingRight ? "right" : "left");
1957
- const x2 = arrowEdgeX(step.to, i, goingRight ? "left" : "right");
2161
+ const { x1, x2 } = arrow;
1958
2162
  svg.append("rect").attr("x", Math.min(x1, x2)).attr("y", y - HIT_H / 2).attr("width", Math.abs(x2 - x1)).attr("height", HIT_H).attr("fill", "transparent").attr("class", "message-hit-area").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
1959
2163
  const markerRef = step.async ? coloredMarker("async", msgTagColor) : coloredMarker("call", msgTagColor);
1960
2164
  const arrowEl = svg.append("line").attr("x1", x1).attr("y1", y).attr("x2", x2).attr("y2", y).attr("stroke", arrowColor).attr("stroke-width", 1.2).attr("marker-end", markerRef).attr("class", "message-arrow").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i)).attr("data-from", step.from).attr("data-to", step.to);
1961
2165
  if (tagKey && msgTagValue) {
1962
2166
  arrowEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
1963
2167
  }
1964
- const displayLabel = displayMessageLabel(step);
2168
+ const displayLabel = stepLabels[i];
1965
2169
  if (displayLabel) {
1966
- const midX = (x1 + x2) / 2;
1967
- const labelEl = svg.append("text").attr("x", midX).attr("y", y - MSG_LABEL_OFFSET).attr("text-anchor", "middle").attr("fill", arrowColor).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("font-size", MSG_LABEL_FONT_SIZE).attr("class", "message-label").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
2170
+ const labelEl = svg.append("text").attr("x", label.x).attr("y", label.y).attr("text-anchor", "middle").attr("fill", arrowColor).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("font-size", MSG_LABEL_FONT_SIZE).attr("class", "message-label").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
1968
2171
  if (tagKey && msgTagValue) {
1969
2172
  labelEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
1970
2173
  }
1971
2174
  labelEl.text(displayLabel);
1972
2175
  }
1973
2176
  }
1974
- } else {
1975
- if (step.from === step.to) {
1976
- return;
1977
- }
1978
- const goingRight = fromX < toX;
1979
- const x1 = arrowEdgeX(step.from, i, goingRight ? "right" : "left");
1980
- const x2 = arrowEdgeX(step.to, i, goingRight ? "left" : "right");
1981
- const returnColor = msgTagColor || palette.textMuted;
2177
+ } else if (arrow.kind === "return") {
2178
+ const { x1, x2 } = arrow;
2179
+ const returnColor = msgTagColor ?? palette.textMuted;
1982
2180
  svg.append("rect").attr("x", Math.min(x1, x2)).attr("y", y - HIT_H / 2).attr("width", Math.abs(x2 - x1)).attr("height", HIT_H).attr("fill", "transparent").attr("class", "message-hit-area").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
1983
2181
  const returnEl = svg.append("line").attr("x1", x1).attr("y1", y).attr("x2", x2).attr("y2", y).attr("stroke", returnColor).attr("stroke-width", 1).attr("stroke-dasharray", EDGE_DASH).attr("marker-end", coloredMarker("return", msgTagColor)).attr("class", "return-arrow").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i)).attr("data-from", step.from).attr("data-to", step.to);
1984
2182
  if (tagKey && msgTagValue) {
1985
2183
  returnEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
1986
2184
  }
1987
- const displayLabel = displayMessageLabel(step);
2185
+ const displayLabel = stepLabels[i];
1988
2186
  if (displayLabel) {
1989
- const midX = (x1 + x2) / 2;
1990
- const labelEl = svg.append("text").attr("x", midX).attr("y", y - RETURN_LABEL_OFFSET).attr("text-anchor", "middle").attr("fill", returnColor).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("font-size", RETURN_LABEL_FONT_SIZE).attr("class", "message-label").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
2187
+ const labelEl = svg.append("text").attr("x", label.x).attr("y", label.y).attr("text-anchor", "middle").attr("fill", returnColor).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("font-size", RETURN_LABEL_FONT_SIZE).attr("class", "message-label").attr("data-line-number", String(msg.lineNumber)).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
1991
2188
  if (tagKey && msgTagValue) {
1992
2189
  labelEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
1993
2190
  }
@@ -1999,30 +2196,9 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1999
2196
  const renderNoteElements = (els) => {
2000
2197
  for (const el of els) {
2001
2198
  if (isSequenceNote(el)) {
2002
- const px = participantX.get(el.participantId);
2003
- if (px === void 0) continue;
2004
- const noteTopY = noteYMap.get(el);
2005
- if (noteTopY === void 0) continue;
2006
- const position = effectiveNotePosition(el);
2007
- const isRight = position === "right";
2008
- const afterSelfCall = isNoteAfterSelfCall(el);
2009
- const maxW = noteEffectiveMaxW(
2010
- el.participantId,
2011
- position,
2012
- afterSelfCall
2013
- );
2014
- const textWidth = noteTextWidth(maxW);
2015
- const wrappedLines = wrapTextLines(el.text, textWidth, sNoteFontSize);
2016
- const noteH = wrappedLines.length * sNoteLineH + sNotePadV * 2;
2017
- const maxLineW = Math.max(
2018
- ...wrappedLines.map((l) => measureText(l.text, sNoteFontSize))
2019
- );
2020
- const noteW = Math.min(
2021
- maxW,
2022
- Math.max(80, maxLineW + sNotePadH * 2 + sNoteFold)
2023
- );
2024
- const rightOffset = afterSelfCall && isRight ? sActivationWidth / 2 + sSelfCallWidth + sNoteGap : sActivationWidth + sNoteGap;
2025
- const noteX = isRight ? px + rightOffset : px - sActivationWidth - sNoteGap - noteW;
2199
+ const box = noteBoxes.get(el);
2200
+ if (box === void 0) continue;
2201
+ const { x: noteX, y: noteTopY, width: noteW, height: noteH } = box;
2026
2202
  const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber)).attr("data-line-end", String(el.endLineNumber));
2027
2203
  noteG.append("path").attr(
2028
2204
  "d",
@@ -2043,16 +2219,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2043
2219
  `L ${noteX + noteW} ${noteTopY + sNoteFold}`
2044
2220
  ].join(" ")
2045
2221
  ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
2046
- const BULLET_BODY_INDENT = 10;
2047
- wrappedLines.forEach((line, li) => {
2048
- const textY = noteTopY + sNotePadV + (li + 1) * sNoteLineH - 3;
2049
- const indent = line.kind === "plain" ? 0 : BULLET_BODY_INDENT;
2050
- if (line.kind === "bullet-first") {
2051
- noteG.append("text").attr("x", noteX + sNotePadH).attr("y", textY).attr("fill", palette.text).attr("font-size", sNoteFontSize).text("\u2022");
2222
+ for (const line of box.lines) {
2223
+ if (line.bulletX !== void 0) {
2224
+ noteG.append("text").attr("x", line.bulletX).attr("y", line.y).attr("fill", palette.text).attr("font-size", sNoteFontSize).text("\u2022");
2052
2225
  }
2053
- const textEl = noteG.append("text").attr("x", noteX + sNotePadH + indent).attr("y", textY).attr("fill", palette.text).attr("font-size", sNoteFontSize).attr("class", "note-text");
2226
+ const textEl = noteG.append("text").attr("x", line.x).attr("y", line.y).attr("fill", palette.text).attr("font-size", sNoteFontSize).attr("class", "note-text");
2054
2227
  renderInlineText(textEl, line.text, palette, sNoteFontSize);
2055
- });
2228
+ }
2056
2229
  } else if (isSequenceBlock(el)) {
2057
2230
  renderNoteElements(el.children);
2058
2231
  if (el.elseIfBranches) {
@@ -2183,6 +2356,7 @@ export {
2183
2356
  computeActivations,
2184
2357
  applyPositionOverrides,
2185
2358
  applyGroupOrdering,
2359
+ layoutSequence,
2186
2360
  renderSequenceDiagram,
2187
2361
  buildNoteMessageMap
2188
2362
  };