collabmd 0.1.54 → 0.1.55

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 (447) hide show
  1. package/bin/collabmd.js +19 -38
  2. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-cMeDxAEL.js +1 -0
  3. package/dist/client/assets/{abnfDiagram-VCTEODGH-DxNxad6-.js → abnfDiagram-VCTEODGH-D2UdmYvj.js} +1 -1
  4. package/dist/client/assets/{abnfDiagram-O67JEVCF-Dwt6a0Ds.js → abnfDiagram-YUKMZFIV-DbmU4Q-X.js} +1 -1
  5. package/dist/client/assets/api-client-utils-C5ESDKMG.js +1 -0
  6. package/dist/client/assets/architecture-7GRP2DOG-BBwRXFv3.js +1 -1
  7. package/dist/client/assets/architecture-WOLXFQ4H-C0NxYiG7.js +1 -0
  8. package/dist/client/assets/{architectureDiagram-NJMV4G6O-D5CGiX3G.js → architectureDiagram-47P4ROYG-C7OjXK9S.js} +3 -3
  9. package/dist/client/assets/{architectureDiagram-5GKGNRK7-C1uvg5id.js → architectureDiagram-5GKGNRK7-CSiUzzeN.js} +1 -1
  10. package/dist/client/assets/base-CaIEJUF9.css +1 -0
  11. package/dist/client/assets/{blockDiagram-BEXU5L5S-BgTM0var.js → blockDiagram-E7TT5TSB-DMG-YlZw.js} +3 -3
  12. package/dist/client/assets/{blockDiagram-I7D4REHJ-B2YonsoG.js → blockDiagram-I7D4REHJ-sXfAgK2w.js} +1 -1
  13. package/dist/client/assets/{browser-BKLM0ThC-BCLQGPD6-K2seJgRL.js → browser-BKLM0ThC-BCLQGPD6-CcMAHZXi.js} +1 -1
  14. package/dist/client/assets/{c4Diagram-7LVT6UL2-r4IwomZm.js → c4Diagram-7LVT6UL2-C88V_K-8.js} +1 -1
  15. package/dist/client/assets/{c4Diagram-YGBWAQC7-_xZPrNkB.js → c4Diagram-CRA5TL53-D3F9ElJv.js} +2 -2
  16. package/dist/client/assets/canvasEditor-BJkwGzO1.css +1 -0
  17. package/dist/client/assets/canvasEditor-sBK7KfrD.js +1 -0
  18. package/dist/client/assets/{chunk-6AZGARVD--pTGpDnx.js → chunk-24IY7LWP-flnn6SWX.js} +1 -1
  19. package/dist/client/assets/chunk-2BW5OAIV-CNFO1U_e.js +10 -0
  20. package/dist/client/assets/{chunk-2E4U76K2-CIQUuV9n.js → chunk-2E4U76K2-DEnS5IbF.js} +1 -1
  21. package/dist/client/assets/{chunk-5V3GS4D5-B5uC2oxu.js → chunk-3M4EKCLJ-D-sZGCtO.js} +1 -1
  22. package/dist/client/assets/chunk-3YJQHVM4-Ctp__8EK.js +2 -0
  23. package/dist/client/assets/{chunk-7PRAP22T-9uTvtc_-.js → chunk-4EA7E6EY-bU4wRJot.js} +1 -1
  24. package/dist/client/assets/{chunk-4HAMMTFA-q0ldNVvr.js → chunk-4HAMMTFA-CQy6Skzw.js} +1 -1
  25. package/dist/client/assets/{chunk-6K3QC6MW-JnuvEGV4.js → chunk-4S7OKTRW-BpUW13a3.js} +1 -1
  26. package/dist/client/assets/{chunk-ICYGCRZG-odvlDZyA.js → chunk-53FOQ5SW-5lPdGYP4.js} +1 -1
  27. package/dist/client/assets/{chunk-75Z2AOVW-DitkSoSG.js → chunk-75Z2AOVW-BRdhvVTZ.js} +1 -1
  28. package/dist/client/assets/{chunk-GWA4HPMP-CGmv2yIq.js → chunk-7TKQ45FW-rA2u-WUl.js} +1 -1
  29. package/dist/client/assets/{chunk-GTNCS2PH-ChgkzEIi.js → chunk-AW2ZBBNX-DL0KGAdi.js} +3 -3
  30. package/dist/client/assets/{chunk-747NJXEK-BjqqAsS9.js → chunk-BP2KR52E-DAQ1pzDV.js} +1 -1
  31. package/dist/client/assets/{chunk-CLGD4ZFX-BfYJtJm1.js → chunk-CLGD4ZFX-DJiSE0gD.js} +1 -1
  32. package/dist/client/assets/{chunk-5DYCD2WN-BKWCKnK-.js → chunk-DBDB3WZW-CXleNlok.js} +1 -1
  33. package/dist/client/assets/{chunk-DU6HZSFF-CWlP1Dk_.js → chunk-DU6HZSFF-C-18UZkS.js} +1 -1
  34. package/dist/client/assets/{chunk-MBY4JIJT-DTJ4UcAm.js → chunk-E2ZNV5FY-D_07zfWd.js} +3 -3
  35. package/dist/client/assets/chunk-EU5HNXII-CETVNUXF.js +213 -0
  36. package/dist/client/assets/{chunk-F27PBJKO-DPcsPcEs.js → chunk-F27PBJKO-_beIJos4.js} +1 -1
  37. package/dist/client/assets/{chunk-2ZTRR5NV-DcPRfelM.js → chunk-F6S3BTY2-DVWKCAHp.js} +1 -1
  38. package/dist/client/assets/{chunk-ISPTBLVO-BMsDq9Ha.js → chunk-FLFP27SU-CydzbSsY.js} +2 -2
  39. package/dist/client/assets/chunk-FVRAUYC3-C7b9SJ81.js +1 -0
  40. package/dist/client/assets/{chunk-GMAD6QVW-Bgk8qnty.js → chunk-GMAD6QVW-DnZH7DxV.js} +1 -1
  41. package/dist/client/assets/{chunk-GVQU2GXP-CQ8cl-Uu.js → chunk-GVQU2GXP-B0Vm415C.js} +1 -1
  42. package/dist/client/assets/chunk-H5A3YXKD-ChP7mNJq.js +41 -0
  43. package/dist/client/assets/{chunk-742MDFTN-D1nh6vNv.js → chunk-HJ2JQQFS-DQX7RePR.js} +1 -1
  44. package/dist/client/assets/{chunk-NETBCI7D-CmUwDWUZ.js → chunk-HTAEGDNF-C0PA_N-c.js} +1 -1
  45. package/dist/client/assets/{chunk-IMKFNOWR-CBqxfpcP.js → chunk-IMKFNOWR-DoO8IHzF.js} +1 -1
  46. package/dist/client/assets/{chunk-IH6LHLGP-BNevEHZK.js → chunk-KPI5JJXK-B2ld5-w4.js} +1 -1
  47. package/dist/client/assets/{chunk-WEXAMYUT-DDlVNp8J.js → chunk-KQW6MTUR-OQ4MyMr_.js} +1 -1
  48. package/dist/client/assets/{chunk-L3NEJ4N5-BflzkdTI.js → chunk-L3NEJ4N5-CvHossEU.js} +1 -1
  49. package/dist/client/assets/chunk-NGNAAXSQ-DqoPQk1a.js +136 -0
  50. package/dist/client/assets/{chunk-UA2S7LBM-DicYDjDM.js → chunk-NTY3LDVX-CVWLK_fh.js} +1 -1
  51. package/dist/client/assets/{chunk-UY3FDG6J-QT-rkt0y.js → chunk-OD3NTWWA-Dz9nCUGX.js} +1 -1
  52. package/dist/client/assets/{chunk-OSK3NFVY-DqeKV_pg.js → chunk-OSK3NFVY-C3JOcBao.js} +1 -1
  53. package/dist/client/assets/{chunk-P2QGCYS3-BA--DhpW.js → chunk-P2QGCYS3-Bfo8qS8r.js} +1 -1
  54. package/dist/client/assets/{chunk-POPQ4Y6H-V_NEc45n.js → chunk-POPQ4Y6H-sv6rCftH.js} +1 -1
  55. package/dist/client/assets/{chunk-PWAF6VOD-D_kLse-N.js → chunk-PWAF6VOD-B-AT-L2H.js} +1 -1
  56. package/dist/client/assets/{chunk-3Z5EZCMW-BAnA6eQ7.js → chunk-QNH66VMT-Cb9xxTwQ.js} +1 -1
  57. package/dist/client/assets/{chunk-SHT3W25Y-YM3S-A_M.js → chunk-SHT3W25Y-C1lu518T.js} +1 -1
  58. package/dist/client/assets/{chunk-GNY47TPC-CTrH5O5R.js → chunk-SVEVUXB4-BnM-LXop.js} +1 -1
  59. package/dist/client/assets/{chunk-SVP7TREG-DdNhDOmW.js → chunk-SVP7TREG-CKqt0syj.js} +1 -1
  60. package/dist/client/assets/{chunk-TICWLB2K-BGGgCr4x.js → chunk-TICWLB2K-D2ZnWAfF.js} +1 -1
  61. package/dist/client/assets/{chunk-TLUHSLCS-DCZhIDYA.js → chunk-TLUHSLCS-BMIhqv1X.js} +1 -1
  62. package/dist/client/assets/{chunk-6EIED4P4-DBVWq7Tz.js → chunk-TPMEZKFX-DIPHIqGb.js} +1 -1
  63. package/dist/client/assets/{chunk-6TQVIW2G-BSj0l_pu.js → chunk-VPELOWWC-DXayM28-.js} +1 -1
  64. package/dist/client/assets/chunk-VPRB5NB3-BqaJfj_v.js +127 -0
  65. package/dist/client/assets/{chunk-I5DQTOEV-aBZooNp1.js → chunk-VTWWFHGB-SZnJiyWY.js} +1 -1
  66. package/dist/client/assets/{chunk-KI3K4JFJ-FTtDwx3-.js → chunk-VZTHESGH-eHyXmK8W.js} +1 -1
  67. package/dist/client/assets/{chunk-7INBJB4K-CDmiHbaX.js → chunk-XC4XBNZT-D9oehSn8.js} +20 -20
  68. package/dist/client/assets/{chunk-XHIXRSVI-DN_fEwws.js → chunk-YK26KJH5-DVy3MGMd.js} +1 -1
  69. package/dist/client/assets/{chunk-6AEJRKK7-DTVZj5bY.js → chunk-Z6ETV63D-DOcrnz-P.js} +1 -1
  70. package/dist/client/assets/{chunk-OUJLGHUK-75UXlPjl.js → chunk-ZO67DCNQ-BAOMIkLF.js} +1 -1
  71. package/dist/client/assets/classDiagram-ZZMXUADV-BmETdFmC.js +1 -0
  72. package/dist/client/assets/{classDiagram-v2-NBCMYWYE-3TQORq33.js → classDiagram-v2-K4WV4PDN-eaadfJDB.js} +3 -3
  73. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BmETdFmC.js +1 -0
  74. package/dist/client/assets/{comment-markdown-renderer-Bxi_JRSR.js → comment-markdown-renderer-CpBKaj7H.js} +1 -1
  75. package/dist/client/assets/comment-thread-store-DUQsWtnu.js +1 -0
  76. package/dist/client/assets/comment-threads-DnfkLVJA.js +4 -0
  77. package/dist/client/assets/comment-ui-shared-D_pgPUti.js +7 -0
  78. package/dist/client/assets/cynefin-EF2NZ3EQ-DVkJeZ8i.js +1 -0
  79. package/dist/client/assets/cynefin-OW5HDTMX-CF3N9j1_.js +1 -1
  80. package/dist/client/assets/{cynefinDiagram-VND7K2PF-DwfMZ3vk.js → cynefinDiagram-3GCD6N5R-DjpAsBHC.js} +1 -1
  81. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-DTnLZaO7.js → cynefinDiagram-5FMLGOSQ-D2rFQG67.js} +1 -1
  82. package/dist/client/assets/dagre-C-rnJpZF.js +1 -0
  83. package/dist/client/assets/{dagre-GXQ25YYZ-Vtyx08Qh.js → dagre-GXQ25YYZ-Cku9eia9.js} +2 -2
  84. package/dist/client/assets/{dagre-6A5THRUB-DewyAGK6.js → dagre-W4DXFKR2-DOMOIsPd.js} +2 -2
  85. package/dist/client/assets/deferred-collab-C9aa9cvT.css +1 -0
  86. package/dist/client/assets/deferred-git-BPQ-UgrN.css +1 -0
  87. package/dist/client/assets/deferred-preview-B-RebLry.css +1 -0
  88. package/dist/client/assets/{diagram-22UHCM2B-DbZirFfl.js → diagram-2UJZ2QOL-D8WvnEyx.js} +6 -6
  89. package/dist/client/assets/{diagram-3UASUU5V-DmhdYv6_.js → diagram-OVF4WLC6-D8_escYE.js} +2 -2
  90. package/dist/client/assets/{diagram-CDSNMT55-D_qyS11R.js → diagram-PFPMY2P6-B1izZfrN.js} +3 -3
  91. package/dist/client/assets/{diagram-S7CK7UJ4-CGTzfo2f.js → diagram-S7CK7UJ4-BzicH2t-.js} +1 -1
  92. package/dist/client/assets/{diagram-MLGK6HIB-DIBG6Ivu.js → diagram-UMYRVEAY-Djf7WDjZ.js} +1 -1
  93. package/dist/client/assets/{diagram-UQ7AKVKN-CWZS8QtB.js → diagram-UQ7AKVKN-DqX4zTtr.js} +1 -1
  94. package/dist/client/assets/{diagram-VSXAHHWV-DaO1nOZF.js → diagram-VSXAHHWV-DnVbfI4Y.js} +1 -1
  95. package/dist/client/assets/{diagram-VX7I27RA-B54P6Cyf.js → diagram-VX7I27RA-D-Dj0TQC.js} +1 -1
  96. package/dist/client/assets/{diagram-MPIPVDR6-D9XsALDV.js → diagram-YEKJPTXX-B40bP3Yr.js} +2 -2
  97. package/dist/client/assets/{diagram-Z3DM3KII-Cl2EXiXp.js → diagram-Z3DM3KII-_iT7Uz5p.js} +1 -1
  98. package/dist/client/assets/diagram-ZIFT7M5P-BUUK0oNw.js +3 -0
  99. package/dist/client/assets/{direct-engine-DVLMOJWp-C-YL5NFb.js → direct-engine-DVLMOJWp-DWtWIifK.js} +1 -1
  100. package/dist/client/assets/dist-26Ce-5t4.js +1 -0
  101. package/dist/client/assets/dist-8MLNn-I6.js +1 -0
  102. package/dist/client/assets/dist-Amm5Pr1C.js +1 -0
  103. package/dist/client/assets/dist-BXXPaoUV.js +1 -0
  104. package/dist/client/assets/{dist-CoXN2D3R.js → dist-Bkb-_VdB.js} +1 -1
  105. package/dist/client/assets/dist-Bl4yuUS9.js +1 -0
  106. package/dist/client/assets/{dist-lw5TGjI6.js → dist-C2CGvCPd.js} +1 -1
  107. package/dist/client/assets/dist-C2niyEh0.js +1 -0
  108. package/dist/client/assets/{dist-DOHWxIty.js → dist-CFCSUmJk.js} +1 -1
  109. package/dist/client/assets/dist-CHUI56fT.js +6 -0
  110. package/dist/client/assets/dist-CJh3Qrid.js +13 -0
  111. package/dist/client/assets/{dist-Ch6Sr9KF.js → dist-CKxETpSC.js} +3 -3
  112. package/dist/client/assets/dist-CSHJthAI.js +2 -0
  113. package/dist/client/assets/dist-CbKnZGN4.js +1 -0
  114. package/dist/client/assets/{dist-KrdpB_GB.js → dist-Cik4RJXj.js} +1 -1
  115. package/dist/client/assets/{dist-uk11ih8u.js → dist-CqkAY6Ot.js} +2 -2
  116. package/dist/client/assets/dist-D2ql2LCd.js +1 -0
  117. package/dist/client/assets/{dist-BpAUe9sL.js → dist-D47MOYFN.js} +1 -1
  118. package/dist/client/assets/dist-DBhUaexM.js +1 -0
  119. package/dist/client/assets/dist-DJux6Vc5.js +1 -0
  120. package/dist/client/assets/{dist-9Eodxf4G.js → dist-DLNdMWPH.js} +1 -1
  121. package/dist/client/assets/dist-DPJj-Y3l.js +1 -0
  122. package/dist/client/assets/{dist-BSAZ-Hek.js → dist-DRYFMufe.js} +6 -6
  123. package/dist/client/assets/{dist-BrKhPlc9.js → dist-DTGcVSU8.js} +1 -1
  124. package/dist/client/assets/dist-DlOxjzWi.js +9 -0
  125. package/dist/client/assets/{dist-BT9gJXCb.js → dist-DogjjM_B.js} +1 -1
  126. package/dist/client/assets/dist-Dyv_RvyB.js +23 -0
  127. package/dist/client/assets/dist-FUFeH5Cx.js +1 -0
  128. package/dist/client/assets/dist-It70ktCU.js +1 -0
  129. package/dist/client/assets/dist-QMvcnJHf.js +1 -0
  130. package/dist/client/assets/dist-QN1K2czv.js +11 -0
  131. package/dist/client/assets/dist-W8ilOKl3.js +1 -0
  132. package/dist/client/assets/dist-_17-t9qP.js +1 -0
  133. package/dist/client/assets/{dist-DeCV9FU4.js → dist-uE774WWs.js} +1 -1
  134. package/dist/client/assets/document-formatter-DtXmKZcy.js +1 -0
  135. package/dist/client/assets/drawioEditor-DO6IOaJ1.js +9 -0
  136. package/dist/client/assets/{ebnfDiagram-PWID7BFC-PfC84FyI.js → ebnfDiagram-PWID7BFC-IemGPf9X.js} +1 -1
  137. package/dist/client/assets/{ebnfDiagram-ZINNZB2B-DYvm_QtE.js → ebnfDiagram-VR2GEFS7-9fXqy1DI.js} +1 -1
  138. package/dist/client/assets/editor-session-CPsqXY_A.js +13 -0
  139. package/dist/client/assets/elk-IJKZMXRS-DsbrbBYg.js +27 -0
  140. package/dist/client/assets/embedded-editor-base-C44ZcCZv.css +1 -0
  141. package/dist/client/assets/{embedpdf-TkbBYowM.js → embedpdf-BfUI-3pv.js} +1 -1
  142. package/dist/client/assets/{embedpdf-cl9qiF45-itpgqvGY.js → embedpdf-cl9qiF45-CLMxJTMP.js} +2 -2
  143. package/dist/client/assets/en-7ZVUCEYH-C_VXKxxb.js +1 -0
  144. package/dist/client/assets/{erDiagram-OPXOYQCR-o8GRv8A_.js → erDiagram-O2IAPWRE-Cdadm-HN.js} +2 -2
  145. package/dist/client/assets/{erDiagram-RLTQ6QDP-C9cJl5NB.js → erDiagram-RLTQ6QDP-CGGmjm8t.js} +1 -1
  146. package/dist/client/assets/eventmodeling-K75KTNOO-BWBlZ5cR.js +1 -0
  147. package/dist/client/assets/eventmodeling-NTZA5JFV-CDy1bN6j.js +1 -1
  148. package/dist/client/assets/excalidraw-element-link-NQnskRkm.js +1 -0
  149. package/dist/client/assets/excalidraw-scene-DLlw4jyc.js +1 -0
  150. package/dist/client/assets/excalidrawEditor-DiKOIudt.css +1 -0
  151. package/dist/client/assets/excalidrawEditor-W2lZ5Mej.js +9 -0
  152. package/dist/client/assets/export-page-Bomh9oEr.js +46 -0
  153. package/dist/client/assets/exportDocument-BFIOECJ3.js +2 -0
  154. package/dist/client/assets/exportDocument-D0mONgQ4.css +1 -0
  155. package/dist/client/assets/file-kind-BGxTVHan.js +1 -0
  156. package/dist/client/assets/{file-search-BA8o_gs0.js → file-search-Dk2O14Vw.js} +1 -1
  157. package/dist/client/assets/flowDiagram-HODETNUW-DcVVYJUZ.js +1 -0
  158. package/dist/client/assets/flowDiagram-OXPTDLAJ-Dy6y46oF.js +1 -0
  159. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-x40PW0Ah.js → ganttDiagram-EL5Y4UJY-Duu4KnsU.js} +1 -1
  160. package/dist/client/assets/ganttDiagram-R7TSEDQI-BWy52pjn.js +292 -0
  161. package/dist/client/assets/gitGraph-4MIJSDKK-B-rpDYbz.js +1 -1
  162. package/dist/client/assets/gitGraph-VSP46ZUC-1499lQAI.js +1 -0
  163. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-ahsNaZsp.js → gitGraphDiagram-WWUBYQGX-CaJ0vC1a.js} +1 -1
  164. package/dist/client/assets/gitGraphDiagram-XJZIOB7I-DJwp9ilx.js +106 -0
  165. package/dist/client/assets/graphlib-DS17s2tU.js +1 -0
  166. package/dist/client/assets/image-6XY3GD3F-DCWbu6d4.js +1 -0
  167. package/dist/client/assets/image-lightbox-controller-DAcXEXxT.js +184 -0
  168. package/dist/client/assets/index-CglpXOg7.css +1 -0
  169. package/dist/client/assets/index-D3WGkxfI.js +960 -0
  170. package/dist/client/assets/info-A6RAGUB7-BlutK7BT.js +1 -1
  171. package/dist/client/assets/info-OHQRW6UA-BDFQOo4H.js +1 -0
  172. package/dist/client/assets/{infoDiagram-27XIBGKW-MrZyptIy.js → infoDiagram-27XIBGKW-DBJh7QOu.js} +1 -1
  173. package/dist/client/assets/{infoDiagram-VRGFBTTK-KkUjDRAQ.js → infoDiagram-5W2HQ5XZ-DmWsqJ0w.js} +1 -1
  174. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-Bnt9yM0O.js → ishikawaDiagram-5VMMS53U-CeEbSJem.js} +1 -1
  175. package/dist/client/assets/{ishikawaDiagram-OU5B5YK6-Di9BcpOY.js → ishikawaDiagram-K3B6WC7H-DH1NI35G.js} +2 -2
  176. package/dist/client/assets/{journeyDiagram-3NMN7TZE-Bl0abK1r.js → journeyDiagram-3NMN7TZE-BoIEEr6U.js} +1 -1
  177. package/dist/client/assets/{journeyDiagram-ZHPQQLJL-J-sGrs9e.js → journeyDiagram-COXZFDF6-CnHV2f9V.js} +3 -3
  178. package/dist/client/assets/{kanban-definition-PNTS6WVX-5ZDV_yIa.js → kanban-definition-P3RFRI5V-Cxvn5ZAG.js} +4 -4
  179. package/dist/client/assets/{kanban-definition-UXKFOSKX-DMgUfNt0.js → kanban-definition-UXKFOSKX-BY16Tgbr.js} +1 -1
  180. package/dist/client/assets/keyboard-shortcuts-BWNCBJqw.js +1 -0
  181. package/dist/client/assets/lobby-presence-DNNXcuwR.js +1 -0
  182. package/dist/client/assets/{markdown-CZ1tYEW8.js → markdown-DU84620G.js} +1 -1
  183. package/dist/client/assets/markdown-headings-zYEUahfW.js +41 -0
  184. package/dist/client/assets/{markdown-math-CyyBVuDO.js → markdown-math-B96cnrz1.js} +11 -11
  185. package/dist/client/assets/{mermaid-parser.core-Bpogunsh.js → mermaid-parser.core-R-uwU5kf.js} +3 -3
  186. package/dist/client/assets/{mermaid-parser.core-DBy--Jc0.js → mermaid-parser.core-mE6t1qEO.js} +1 -1
  187. package/dist/client/assets/mermaid.core-D8Z6LN7O.js +10 -0
  188. package/dist/client/assets/{mindmap-definition-NLK3R4M7-ByVb-0vj.js → mindmap-definition-LZFPQGKD-DVfFenfJ.js} +3 -3
  189. package/dist/client/assets/{mindmap-definition-YA3MSWOX-DBZN5_GH.js → mindmap-definition-YA3MSWOX-DtiE80UJ.js} +1 -1
  190. package/dist/client/assets/packet-AYTQ26CC-DvbY8dI6.js +1 -1
  191. package/dist/client/assets/packet-JDAUHWVQ-DJ9K2MA4.js +1 -0
  192. package/dist/client/assets/{pegDiagram-GJSIUBJH-aost1sSO.js → pegDiagram-BGZESJAR-DxfvEcwv.js} +1 -1
  193. package/dist/client/assets/{pegDiagram-XKGWAZYB-DRo8CZu3.js → pegDiagram-XKGWAZYB-CyozY-Kt.js} +1 -1
  194. package/dist/client/assets/pie-WAS4IAKB-DWSm9SSD.js +1 -1
  195. package/dist/client/assets/pie-XZMESJXO-CQIQasY-.js +1 -0
  196. package/dist/client/assets/{pieDiagram-5QR66LMP-BICdpEuz.js → pieDiagram-CAPJLFHJ-CRlMcrv3.js} +2 -2
  197. package/dist/client/assets/{pieDiagram-E7YTZNPT-CZThaHsc.js → pieDiagram-E7YTZNPT-BRdmTkDq.js} +1 -1
  198. package/dist/client/assets/preload-helper-CYtfOYgs.js +1 -0
  199. package/dist/client/assets/preview-render-compiler-D_aJv7Ew.js +3 -0
  200. package/dist/client/assets/{preview-render-worker-BA4VK_GY.js → preview-render-worker-dFNyrVyB.js} +46 -42
  201. package/dist/client/assets/prod-ItolaHmf.js +145 -0
  202. package/dist/client/assets/prod-rdly4JJQ.js +1 -0
  203. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-C5iowkl_.js → quadrantDiagram-AXDQQJYC-7QyPIXkF.js} +1 -1
  204. package/dist/client/assets/{quadrantDiagram-O4NWA36T-Bgx5bF97.js → quadrantDiagram-GDMTTRAM-DE30uqTl.js} +3 -3
  205. package/dist/client/assets/quick-switcher-controller-DsK70I7K.js +1 -0
  206. package/dist/client/assets/radar-ABXABTNO-ByfpCXh9.js +1 -0
  207. package/dist/client/assets/radar-RG4KPBEZ-BfNQUEQy.js +1 -1
  208. package/dist/client/assets/railroad-74A4TZTK-BaQ6U6eJ.js +1 -1
  209. package/dist/client/assets/railroad-I3PHUGI6-CTUn9HhI.js +1 -0
  210. package/dist/client/assets/railroad-abnf-HS5TGJTU-BJ3KZw7J.js +1 -1
  211. package/dist/client/assets/railroad-abnf-LNEFI6M7-Ct-b-y0F.js +1 -0
  212. package/dist/client/assets/railroad-ebnf-DWYD2UWJ-BZMYMWOe.js +1 -0
  213. package/dist/client/assets/railroad-ebnf-LZEXJU2U-DA0sEq7w.js +1 -1
  214. package/dist/client/assets/railroad-peg-JR7BVNR7-Dj23PxYc.js +1 -0
  215. package/dist/client/assets/railroad-peg-WCYAUIDC-DCmlICzO.js +1 -1
  216. package/dist/client/assets/{railroadDiagram-O6MQD6OU-BTt_rKjT.js → railroadDiagram-O6MQD6OU-BPk3RS2D.js} +1 -1
  217. package/dist/client/assets/{railroadDiagram-XR7U4H2S-DbVoNgSz.js → railroadDiagram-SM67HX2B-CZRXJ647.js} +1 -1
  218. package/dist/client/assets/{requirementDiagram-BXWQKSXE-BkvazG7r.js → requirementDiagram-BXWQKSXE-BRwfym6z.js} +1 -1
  219. package/dist/client/assets/{requirementDiagram-PLB6GJNP-BT0j9C40.js → requirementDiagram-X7JNWC4A-BofCuLJA.js} +4 -4
  220. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-C_IemB9h.js → sankeyDiagram-P5KCCOFB-D4LcrKpW.js} +1 -1
  221. package/dist/client/assets/{sankeyDiagram-IPEJSGJF-X1BCUIrf.js → sankeyDiagram-UM26HJRW-CLKMDbej.js} +3 -3
  222. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CxFBgLGd.js → sequenceDiagram-WJ2MYXX4-DQjtv-Bs.js} +1 -1
  223. package/dist/client/assets/sequenceDiagram-ZO4K6R2Y-CDP_m6ma.js +169 -0
  224. package/dist/client/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
  225. package/dist/client/assets/sizeCapture-INFHLROL-BMR1ovSU.js +1 -0
  226. package/dist/client/assets/src-D5mvpivG.js +2 -0
  227. package/dist/client/assets/{standalone-DbyFjvTU.js → standalone-D_lg5NC9.js} +1 -1
  228. package/dist/client/assets/{stateDiagram-D77RDMKH-BaSQ6Owj.js → stateDiagram-D77RDMKH-DSMvV-kF.js} +1 -1
  229. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-ClmOOUsT.js +1 -0
  230. package/dist/client/assets/{stateDiagram-v2-GCMORJYK-CsVcfwVp.js → stateDiagram-v2-TBUQTH76-Dc5-FKzd.js} +2 -2
  231. package/dist/client/assets/{swimlanes-42K2YHIH-CALFEmiS.js → swimlanes-42K2YHIH-BUz31lO3.js} +1 -1
  232. package/dist/client/assets/{swimlanes-2SLR337P-CnYTVqRX.js → swimlanes-N4OXWK64-Dd3dFdPD.js} +1 -1
  233. package/dist/client/assets/swimlanesDiagram-K3J5GTZL-Bz9gFdls.js +8 -0
  234. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DoDs3zod.js +8 -0
  235. package/dist/client/assets/{timeline-definition-24CTP7MA-zse-5Dbm.js → timeline-definition-24CTP7MA-BvqCvSjy.js} +1 -1
  236. package/dist/client/assets/{timeline-definition-EJHVYXUP-BhqPlKkN.js → timeline-definition-YOQAKGHF-D2XWPSLs.js} +3 -3
  237. package/dist/client/assets/treeView-D4JQ5SDB-DcX7Bx0H.js +1 -0
  238. package/dist/client/assets/treeView-Q6P3EWNA-BvHOzbu9.js +1 -1
  239. package/dist/client/assets/treemap-SAJKECNS-BcCHmcAX.js +1 -0
  240. package/dist/client/assets/treemap-WGGIJYW6-oety7paS.js +1 -1
  241. package/dist/client/assets/usecaseDiagram-VIAY4XPW-CDe-IMjS.js +328 -0
  242. package/dist/client/assets/vault-api-client-DrRW0QwO.js +7 -0
  243. package/dist/client/assets/{vennDiagram-4TSXK5OY-DgHr6cJD.js → vennDiagram-4TSXK5OY-BW6Qp12d.js} +1 -1
  244. package/dist/client/assets/{vennDiagram-UO4OBE2U-BRwLn2Q1.js → vennDiagram-BLWOH2XV-KxIPKpHi.js} +3 -3
  245. package/dist/client/assets/wardley-7MLQ67FV-DOahSwbZ.js +1 -0
  246. package/dist/client/assets/wardley-WFR3VGLG-BdwDpX_I.js +1 -1
  247. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-9A60XZis.js → wardleyDiagram-VM6X3IG4-BoCETVsK.js} +1 -1
  248. package/dist/client/assets/{wardleyDiagram-VNRHLVJA-B_tMcVFG.js → wardleyDiagram-YMQ3BMBF-BNYqEy7M.js} +4 -4
  249. package/dist/client/assets/{wiki-link-resolver-Dch88erO.js → wiki-link-resolver-BCdGUpdu.js} +1 -1
  250. package/dist/client/assets/{worker-engine-BCCrxWYm-CX0qJ_cG.js → worker-engine-BCCrxWYm-1fphds7O.js} +1 -1
  251. package/dist/client/assets/workspace-sync-client-C_P9wtjf.js +1 -0
  252. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-D00iy-wB.js → xychartDiagram-S5SC5T6Z-eB5Fu9P7.js} +1 -1
  253. package/dist/client/assets/xychartDiagram-TAQBALBS-Sv6aaptV.js +7 -0
  254. package/dist/client/assets/yjs-Jd9fna_Y.js +4 -0
  255. package/dist/client/assets/yjs-provider-reset-guard-Cqu0Z6xH.js +1 -0
  256. package/dist/client/canvas-editor.html +16 -0
  257. package/dist/client/drawio-editor.html +7 -6
  258. package/dist/client/excalidraw-editor.html +31 -30
  259. package/dist/client/export-document.html +4 -3
  260. package/dist/client/index.html +2 -2
  261. package/package.json +27 -29
  262. package/scripts/local-compose.mjs +101 -0
  263. package/src/client/app/canvas-editor.html +13 -0
  264. package/src/client/application/app-shell/git-feature.js +3 -2
  265. package/src/client/application/app-shell/ui-feature-identity.js +1 -0
  266. package/src/client/application/app-shell/ui-feature-shell.js +2 -0
  267. package/src/client/application/app-shell/ui-feature-tab-activity.js +4 -0
  268. package/src/client/application/app-shell/workspace-feature.js +8 -34
  269. package/src/client/application/diagram-chrome.js +8 -11
  270. package/src/client/application/diagram-preview-hydrator.js +5 -5
  271. package/src/client/application/mermaid-preview-hydrator.js +3 -2
  272. package/src/client/application/plantuml-preview-hydrator.js +6 -0
  273. package/src/client/application/preview-render-compiler.js +34 -98
  274. package/src/client/application/preview-render-executor.js +3 -0
  275. package/src/client/application/preview-render-worker.js +2 -0
  276. package/src/client/application/preview-renderer.js +9 -5
  277. package/src/client/application/workspace-coordinator.js +18 -10
  278. package/src/client/application/workspace-preview-controller.js +105 -158
  279. package/src/client/application/workspace-route-controller.js +29 -54
  280. package/src/client/bootstrap/canvas-markdown-preview.js +87 -0
  281. package/src/client/bootstrap/collabmd-app-shell.js +28 -13
  282. package/src/client/canvas-editor.js +143 -0
  283. package/src/client/domain/canvas-view.js +50 -0
  284. package/src/client/domain/excalidraw-scene.js +4 -49
  285. package/src/client/domain/highlight-runtime.js +0 -4
  286. package/src/client/domain/markdown-formatting.js +12 -94
  287. package/src/client/domain/markdown-headings.js +98 -0
  288. package/src/client/domain/wiki-link-completions.js +42 -10
  289. package/src/client/infrastructure/browser-preferences-port.js +27 -33
  290. package/src/client/infrastructure/canvas-room-client.js +210 -0
  291. package/src/client/infrastructure/canvas-text-editor.js +36 -0
  292. package/src/client/infrastructure/comment-thread-store.js +19 -77
  293. package/src/client/infrastructure/editor-view-adapter.js +0 -30
  294. package/src/client/infrastructure/excalidraw-room-client.js +22 -10
  295. package/src/client/infrastructure/hosted-api-client.js +4 -2
  296. package/src/client/infrastructure/vault-api-client.js +4 -4
  297. package/src/client/presentation/canvas-editor-view.js +1028 -0
  298. package/src/client/presentation/canvas-embed-controller.js +116 -0
  299. package/src/client/presentation/canvas-picker.js +111 -0
  300. package/src/client/presentation/drawio-embed-controller.js +1 -3
  301. package/src/client/presentation/excalidraw-embed-controller.js +10 -3
  302. package/src/client/presentation/file-action-controller.js +37 -0
  303. package/src/client/presentation/file-explorer-controller.js +1 -2
  304. package/src/client/presentation/file-explorer-view.js +7 -1
  305. package/src/client/presentation/file-icon-svg.js +1 -1
  306. package/src/client/presentation/file-tree-state.js +4 -1
  307. package/src/client/presentation/image-lightbox-controller.js +1 -5
  308. package/src/client/presentation/team-settings-controller.js +44 -10
  309. package/src/client/styles/components/actions.css +4 -16
  310. package/src/client/styles/components/surfaces.css +0 -7
  311. package/src/client/styles/features/comment-card.css +1 -2
  312. package/src/client/styles/features/comment-markdown.css +0 -18
  313. package/src/client/styles/features/diagram-preview.css +0 -18
  314. package/src/client/styles/features/git-branch.css +0 -4
  315. package/src/client/styles/foundation/themes.css +6 -0
  316. package/src/client/styles/foundation/utilities.css +1 -2
  317. package/src/client/styles/layout/editor-page.css +0 -13
  318. package/src/client/styles/layout/responsive.css +2 -7
  319. package/src/client/styles/layout/shell.css +13 -1
  320. package/src/client/styles/layout/view-modes.css +1 -0
  321. package/src/client/styles/surfaces/canvas-editor.css +239 -0
  322. package/src/domain/canvas-room-codec.js +359 -0
  323. package/src/domain/collabmd-content-capabilities.js +10 -1
  324. package/src/domain/comment-threads.js +49 -5
  325. package/src/domain/excalidraw-room-codec.js +23 -73
  326. package/src/domain/excalidraw-scene.js +48 -0
  327. package/src/domain/file-kind.js +13 -1
  328. package/src/server/application/workspace-reconciliation.js +0 -14
  329. package/src/server/domain/backlink-index.js +1 -36
  330. package/src/server/domain/bases/base-index-snapshot-store.js +41 -24
  331. package/src/server/domain/bases/base-query-service.js +1 -47
  332. package/src/server/domain/collaboration/collaboration-room.js +149 -15
  333. package/src/server/domain/collaboration/room-registry.js +7 -7
  334. package/src/server/domain/hosted-workspace.js +29 -2
  335. package/src/server/domain/plantuml-encoder.js +7 -43
  336. package/src/server/infrastructure/git/diff-service.js +1 -5
  337. package/src/server/infrastructure/git/history-service.js +18 -2
  338. package/src/server/infrastructure/git/untracked-files.js +1 -1
  339. package/src/server/infrastructure/http/create-git-api-command-handler.js +60 -149
  340. package/src/server/infrastructure/http/create-hosted-api-handler.js +5 -2
  341. package/src/server/infrastructure/http/create-vault-api-query-handler.js +2 -2
  342. package/src/server/infrastructure/http/http-response.js +2 -36
  343. package/src/server/infrastructure/http/request-body.js +14 -49
  344. package/src/server/infrastructure/persistence/hosted-metadata-store.js +12 -3
  345. package/src/server/infrastructure/persistence/path-utils.js +8 -2
  346. package/src/server/infrastructure/persistence/vault-file-store.js +26 -60
  347. package/src/server/{infrastructure/git → shared}/image-mime.js +2 -2
  348. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-kJfwukbn.js +0 -1
  349. package/dist/client/assets/_basePickBy-QJXUw_pr.js +0 -1
  350. package/dist/client/assets/_baseUniq-DR4vVjyC.js +0 -1
  351. package/dist/client/assets/architecture-7GRP2DOG-DJVtYNt-.js +0 -1
  352. package/dist/client/assets/browser-utils-S4QYY31r.js +0 -1
  353. package/dist/client/assets/chunk-3FUC2YCW-8tZlhTGX.js +0 -1
  354. package/dist/client/assets/chunk-7M6MHVWA-BDakkknV.js +0 -213
  355. package/dist/client/assets/chunk-FOHPRMQF-Dmmo5Kak.js +0 -161
  356. package/dist/client/assets/chunk-O7XYJQB3-D0_ZY2za.js +0 -126
  357. package/dist/client/assets/chunk-T5WUORNS-DD7KlyY6.js +0 -40
  358. package/dist/client/assets/chunk-Z7XXMR3K-P4Vaq8hD.js +0 -10
  359. package/dist/client/assets/chunk-ZIGJFQKS-b8OUHKK5.js +0 -2
  360. package/dist/client/assets/classDiagram-ZZMXUADV-QLbh_Nkf.js +0 -1
  361. package/dist/client/assets/classDiagram-v2-VYDZK3BY-QLbh_Nkf.js +0 -1
  362. package/dist/client/assets/comment-thread-store-CHvy8GF8.js +0 -1
  363. package/dist/client/assets/comment-threads-nJhK1yfc.js +0 -4
  364. package/dist/client/assets/cynefin-OW5HDTMX-e-Quc9Yl.js +0 -1
  365. package/dist/client/assets/dagre-D7FiCLLP.js +0 -1
  366. package/dist/client/assets/deferred-collab-D4cxEdad.css +0 -1
  367. package/dist/client/assets/deferred-git-BHKtkFYi.css +0 -1
  368. package/dist/client/assets/deferred-preview-BAXYgf4T.css +0 -1
  369. package/dist/client/assets/diagram-ATOU4E4O-B5uX-jVO.js +0 -3
  370. package/dist/client/assets/dist-B2ZNQifU.js +0 -1
  371. package/dist/client/assets/dist-B6QCMNze.js +0 -1
  372. package/dist/client/assets/dist-BIAv19LO.js +0 -1
  373. package/dist/client/assets/dist-BKYp1-dl.js +0 -13
  374. package/dist/client/assets/dist-BSTbcioM.js +0 -11
  375. package/dist/client/assets/dist-BTVg1m-V.js +0 -1
  376. package/dist/client/assets/dist-Bi9ZIYXq.js +0 -9
  377. package/dist/client/assets/dist-Bqo-XRVG.js +0 -1
  378. package/dist/client/assets/dist-CBsvKPcT.js +0 -1
  379. package/dist/client/assets/dist-CJBYh9Wy.js +0 -23
  380. package/dist/client/assets/dist-CRwfjtwz.js +0 -2
  381. package/dist/client/assets/dist-CX7Kc61C.js +0 -1
  382. package/dist/client/assets/dist-CYfPeupF.js +0 -1
  383. package/dist/client/assets/dist-CucpLxUY.js +0 -1
  384. package/dist/client/assets/dist-DHBMeCxC.js +0 -6
  385. package/dist/client/assets/dist-DLEg9d4u.js +0 -1
  386. package/dist/client/assets/dist-DYFBD-wW.js +0 -1
  387. package/dist/client/assets/dist-DugWGOjQ.js +0 -1
  388. package/dist/client/assets/dist-NNrRnB-f.js +0 -1
  389. package/dist/client/assets/dist-PUxZqhFe.js +0 -1
  390. package/dist/client/assets/dist-cYiJMWHJ.js +0 -1
  391. package/dist/client/assets/dist-iKC7go4m.js +0 -1
  392. package/dist/client/assets/document-formatter-6I63vstk.js +0 -1
  393. package/dist/client/assets/drawioEditor-DNTjsjzJ.js +0 -9
  394. package/dist/client/assets/editor-session-D8GGkJak.js +0 -14
  395. package/dist/client/assets/elk-276RUBZZ-B8LB3Pac.js +0 -27
  396. package/dist/client/assets/embedded-editor-base-elU0Avik.css +0 -1
  397. package/dist/client/assets/en-JTHJ2SIP-DBz9bRq4.js +0 -1
  398. package/dist/client/assets/eventmodeling-NTZA5JFV-Dxtv5D-P.js +0 -1
  399. package/dist/client/assets/excalidraw-element-link-BRZOTIeI.js +0 -7
  400. package/dist/client/assets/excalidraw-scene-6uROebca.js +0 -1
  401. package/dist/client/assets/excalidrawEditor-BCTFp_CV.css +0 -1
  402. package/dist/client/assets/excalidrawEditor-D0orhhku.js +0 -9
  403. package/dist/client/assets/export-page-CVJNMERq.js +0 -46
  404. package/dist/client/assets/exportDocument-Ba-n1X4U.css +0 -1
  405. package/dist/client/assets/exportDocument-DSshnmnZ.js +0 -2
  406. package/dist/client/assets/file-kind-BwMIcZbi.js +0 -1
  407. package/dist/client/assets/flowDiagram-HODETNUW-Bq4470NR.js +0 -1
  408. package/dist/client/assets/flowDiagram-KWPJA3E3-DQG9qGiU.js +0 -1
  409. package/dist/client/assets/ganttDiagram-FUAMR5RP-BQpsI7gQ.js +0 -292
  410. package/dist/client/assets/gitGraph-4MIJSDKK-ZaBIE4Lb.js +0 -1
  411. package/dist/client/assets/gitGraphDiagram-X574FWY7-wbs_2m9f.js +0 -106
  412. package/dist/client/assets/graphlib-C9Tl7QrK.js +0 -1
  413. package/dist/client/assets/image-I42VHMUI-B2-c_ifV.js +0 -1
  414. package/dist/client/assets/index-BCgNcVNs.css +0 -1
  415. package/dist/client/assets/index-BpmR5Qqk.js +0 -1140
  416. package/dist/client/assets/info-A6RAGUB7-BcGxhjED.js +0 -1
  417. package/dist/client/assets/lobby-presence-KJrYhMdN.js +0 -1
  418. package/dist/client/assets/mermaid.core-BeOUPE3Q.js +0 -10
  419. package/dist/client/assets/packet-AYTQ26CC-CqpSjTWl.js +0 -1
  420. package/dist/client/assets/pie-WAS4IAKB-Bg2eCNl5.js +0 -1
  421. package/dist/client/assets/preload-helper-BAlEavIC.js +0 -1
  422. package/dist/client/assets/preview-render-compiler-BmWQStaL.js +0 -43
  423. package/dist/client/assets/prod-CA0ie9yD.js +0 -145
  424. package/dist/client/assets/prod-yKJZ3E4s.js +0 -1
  425. package/dist/client/assets/quick-switcher-controller-B5vT9Vj5.js +0 -1
  426. package/dist/client/assets/radar-RG4KPBEZ-DxExotg6.js +0 -1
  427. package/dist/client/assets/railroad-74A4TZTK-FfHmeIQk.js +0 -1
  428. package/dist/client/assets/railroad-abnf-HS5TGJTU-CLkdDRsJ.js +0 -1
  429. package/dist/client/assets/railroad-ebnf-LZEXJU2U-B162zvcG.js +0 -1
  430. package/dist/client/assets/railroad-peg-WCYAUIDC-CGs5GeJ5.js +0 -1
  431. package/dist/client/assets/sequenceDiagram-PO4LG4MO-B-2QfXhV.js +0 -169
  432. package/dist/client/assets/sizeCapture-INFHLROL-B0uUizjq.js +0 -1
  433. package/dist/client/assets/sizeCapture-INFHLROL-DrzwYt5z.js +0 -1
  434. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CLlwrdvU.js +0 -1
  435. package/dist/client/assets/swimlanesDiagram-TC7HE7FX-Bh4tmz27.js +0 -8
  436. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-xhehUG9e.js +0 -8
  437. package/dist/client/assets/treeView-Q6P3EWNA-Cccy0ngL.js +0 -1
  438. package/dist/client/assets/treemap-WGGIJYW6-sMRCjCtX.js +0 -1
  439. package/dist/client/assets/usecaseDiagram-POWQR4AR-DR2LHQYs.js +0 -328
  440. package/dist/client/assets/vault-api-client-NS_EVHJ3.js +0 -7
  441. package/dist/client/assets/wardley-WFR3VGLG-Y7xty_3m.js +0 -1
  442. package/dist/client/assets/workspace-sync-client-BrMtf6zN.js +0 -1
  443. package/dist/client/assets/xychartDiagram-PMCCYNJV-fRpZ1MA5.js +0 -7
  444. package/dist/client/assets/yjs-B41tM6Sz.js +0 -4
  445. package/dist/client/assets/yjs-provider-reset-guard-DpntzUDR.js +0 -1
  446. package/scripts/local-plantuml-compose.mjs +0 -92
  447. package/scripts/local-structurizr-compose.mjs +0 -101
@@ -0,0 +1,98 @@
1
+ import markdownIt from 'markdown-it';
2
+ import { extractYamlFrontmatter } from '../../domain/yaml-frontmatter.js';
3
+
4
+ function createHeadingSlug(content = '') {
5
+ return String(content ?? '')
6
+ .trim()
7
+ .toLowerCase()
8
+ .replace(/[^a-z0-9]+/g, '-')
9
+ .replace(/^-|-$/g, '')
10
+ || 'section';
11
+ }
12
+
13
+ function createHeadingId(baseId = '', headingIdCounts = new Map()) {
14
+ const normalizedBaseId = String(baseId ?? '').trim() || 'section';
15
+ const occurrenceIndex = headingIdCounts.get(normalizedBaseId) ?? 0;
16
+ headingIdCounts.set(normalizedBaseId, occurrenceIndex + 1);
17
+ return occurrenceIndex === 0 ? normalizedBaseId : `${normalizedBaseId}-${occurrenceIndex}`;
18
+ }
19
+
20
+ export function assignHeadingIds(state) {
21
+ const headingInfos = [];
22
+ const parentSlugs = [];
23
+
24
+ state.tokens.forEach((token, index) => {
25
+ if (token.type !== 'heading_open' || token.attrGet('id')) {
26
+ return;
27
+ }
28
+
29
+ const level = Number.parseInt(token.tag.slice(1), 10);
30
+ if (!Number.isFinite(level) || level < 1) {
31
+ return;
32
+ }
33
+
34
+ parentSlugs.length = Math.max(level - 1, 0);
35
+ const inlineToken = state.tokens[index + 1];
36
+ const slug = createHeadingSlug(inlineToken?.type === 'inline' ? inlineToken.content : '');
37
+ headingInfos.push({
38
+ parentSlugs: parentSlugs.filter(Boolean),
39
+ slug,
40
+ token,
41
+ });
42
+ parentSlugs[level - 1] = slug;
43
+ });
44
+
45
+ const headingsBySlug = new Map();
46
+ headingInfos.forEach((headingInfo) => {
47
+ const group = headingsBySlug.get(headingInfo.slug) ?? [];
48
+ group.push(headingInfo);
49
+ headingsBySlug.set(headingInfo.slug, group);
50
+ });
51
+
52
+ for (const group of headingsBySlug.values()) {
53
+ if (group.length <= 1) {
54
+ continue;
55
+ }
56
+
57
+ const maxDepth = group.reduce((depth, heading) => Math.max(depth, heading.parentSlugs.length), 0);
58
+ for (let depth = 1; depth <= maxDepth; depth += 1) {
59
+ const candidateCounts = new Map();
60
+ const candidates = group.map((heading) => [...heading.parentSlugs.slice(-depth), heading.slug].join('-'));
61
+ for (const candidate of candidates) {
62
+ candidateCounts.set(candidate, (candidateCounts.get(candidate) ?? 0) + 1);
63
+ }
64
+ group.forEach((heading, index) => {
65
+ if (!heading.baseId && depth <= heading.parentSlugs.length && candidateCounts.get(candidates[index]) === 1) {
66
+ heading.baseId = candidates[index];
67
+ }
68
+ });
69
+ }
70
+ }
71
+
72
+ const headingIdCounts = new Map();
73
+ headingInfos.forEach((headingInfo) => {
74
+ headingInfo.token.attrSet('id', createHeadingId(headingInfo.baseId ?? headingInfo.slug, headingIdCounts));
75
+ });
76
+ }
77
+
78
+ const markdown = markdownIt({ html: false, linkify: true, typographer: true });
79
+
80
+ export function getMarkdownHeadings(content = '') {
81
+ const source = String(content ?? '');
82
+ const tokens = markdown.parse(extractYamlFrontmatter(source)?.bodyMarkdown ?? source, {});
83
+ assignHeadingIds({ tokens });
84
+ return tokens.flatMap((token, index) => token.type === 'heading_open' ? [{
85
+ id: token.attrGet('id'),
86
+ text: tokens[index + 1]?.content ?? '',
87
+ }] : []);
88
+ }
89
+
90
+ export function findMarkdownSection(tokens, anchor) {
91
+ assignHeadingIds({ tokens });
92
+ const index = tokens.findIndex((token) => token.type === 'heading_open' && token.attrGet('id') === anchor);
93
+ if (index < 0) return null;
94
+ const heading = tokens[index];
95
+ const level = Number(heading.tag.slice(1));
96
+ const next = tokens.slice(index + 1).find((token) => token.type === 'heading_open' && Number(token.tag.slice(1)) <= level);
97
+ return { startLine: heading.map[0], endLine: next?.map[0] };
98
+ }
@@ -2,6 +2,11 @@ import { createFileSearchEntry, findFileSearchMatch } from './file-search.js';
2
2
 
3
3
  const MAX_VISIBLE_RESULTS = 30;
4
4
 
5
+ function compareMatches(left, right) {
6
+ return right.match.score - left.match.score
7
+ || left.entry.lowerPath.localeCompare(right.entry.lowerPath);
8
+ }
9
+
5
10
  /**
6
11
  * CodeMirror autocomplete source for [[wiki-links]].
7
12
  *
@@ -12,10 +17,13 @@ const MAX_VISIBLE_RESULTS = 30;
12
17
  /**
13
18
  * Creates a wiki-link completion source.
14
19
  *
15
- * @param {() => string[]} getFileList — returns the current list of vault file paths
20
+ * @param {() => string[]} getFileList — returns the current list, replaced when vault file paths change
16
21
  * @returns {import('@codemirror/autocomplete').CompletionSource}
17
22
  */
18
23
  export function wikiLinkCompletions(getFileList) {
24
+ let lastFileList = null;
25
+ let cachedEntries = null;
26
+
19
27
  return (context) => {
20
28
  // Look backwards from the cursor for `[[` that hasn't been closed
21
29
  const line = context.state.doc.lineAt(context.pos);
@@ -35,17 +43,41 @@ export function wikiLinkCompletions(getFileList) {
35
43
 
36
44
  const query = afterOpen.trim().toLowerCase().replace(/\s+/gu, ' ');
37
45
  const from = line.from + openIndex + 2;
38
- const entries = getFileList().map((filePath) => createFileSearchEntry(filePath));
39
- const matches = query
40
- ? entries
41
- .map((entry) => ({ entry, match: findFileSearchMatch(entry, query) }))
42
- .filter(({ match }) => match)
43
- .sort((left, right) => right.match.score - left.match.score
44
- || left.entry.lowerPath.localeCompare(right.entry.lowerPath))
45
- : entries.map((entry) => ({ entry, match: null }));
46
+ const fileList = getFileList();
47
+ if (fileList !== lastFileList) {
48
+ lastFileList = fileList;
49
+ cachedEntries = null;
50
+ }
51
+
52
+ const matches = query ? [] : fileList.slice(0, MAX_VISIBLE_RESULTS)
53
+ .map((filePath) => ({ entry: createFileSearchEntry(filePath) }));
54
+ if (query) {
55
+ cachedEntries ??= fileList.map((filePath) => createFileSearchEntry(filePath));
56
+ for (const entry of cachedEntries) {
57
+ const match = findFileSearchMatch(entry, query);
58
+ if (!match) {
59
+ continue;
60
+ }
61
+
62
+ const candidate = { entry, match };
63
+ if (matches.length === MAX_VISIBLE_RESULTS
64
+ && compareMatches(candidate, matches[MAX_VISIBLE_RESULTS - 1]) >= 0) {
65
+ continue;
66
+ }
67
+
68
+ const index = matches.findIndex((current) => compareMatches(candidate, current) < 0);
69
+ if (index === -1) {
70
+ matches.push(candidate);
71
+ } else {
72
+ matches.splice(index, 0, candidate);
73
+ }
74
+ if (matches.length > MAX_VISIBLE_RESULTS) {
75
+ matches.pop();
76
+ }
77
+ }
78
+ }
46
79
 
47
80
  const options = matches
48
- .slice(0, MAX_VISIBLE_RESULTS)
49
81
  .map(({ entry }) => ({
50
82
  label: entry.filePath,
51
83
  displayLabel: entry.filePath.split('/').pop(),
@@ -16,70 +16,64 @@ function writeStorage(storage, key, value) {
16
16
  }
17
17
 
18
18
  const MAX_RECENT_FILES = 20;
19
+ const STORAGE_KEYS = {
20
+ fileTreeShowExtensions: 'collabmd-file-tree-show-extensions',
21
+ lineWrapping: 'collabmd-editor-line-wrap',
22
+ recentFiles: 'collabmd-recent-files',
23
+ vimMode: 'collabmd-editor-vim-mode',
24
+ viewMode: 'collabmd-editor-view-mode',
25
+ sidebarVisible: 'collabmd-sidebar-visible',
26
+ userName: 'collabmd-user-name',
27
+ cursorNames: 'collabmd-editor-cursor-names',
28
+ };
19
29
 
20
30
  export class BrowserPreferencesPort {
21
- constructor({
22
- fileTreeShowExtensionsKey,
23
- lineWrappingKey,
24
- recentFilesKey = 'collabmd-recent-files',
25
- vimModeKey = 'collabmd-editor-vim-mode',
26
- viewModeKey = 'collabmd-editor-view-mode',
27
- sidebarVisibleKey,
28
- userNameKey,
29
- storage = globalThis.localStorage,
30
- }) {
31
- this.fileTreeShowExtensionsKey = fileTreeShowExtensionsKey;
32
- this.lineWrappingKey = lineWrappingKey;
33
- this.recentFilesKey = recentFilesKey;
34
- this.sidebarVisibleKey = sidebarVisibleKey;
31
+ constructor({ storage = globalThis.localStorage } = {}) {
35
32
  this.storage = storage;
36
- this.userNameKey = userNameKey;
37
- this.viewModeKey = viewModeKey;
38
- this.vimModeKey = vimModeKey;
39
33
  }
40
34
 
41
35
  getUserName() {
42
- return readStorage(this.storage, this.userNameKey, '') || '';
36
+ return readStorage(this.storage, STORAGE_KEYS.userName, '') || '';
43
37
  }
44
38
 
45
39
  setUserName(name) {
46
- writeStorage(this.storage, this.userNameKey, name);
40
+ writeStorage(this.storage, STORAGE_KEYS.userName, name);
47
41
  }
48
42
 
49
43
  getFileTreeShowExtensions() {
50
- return readStorage(this.storage, this.fileTreeShowExtensionsKey, null) === 'true';
44
+ return readStorage(this.storage, STORAGE_KEYS.fileTreeShowExtensions, null) === 'true';
51
45
  }
52
46
 
53
47
  setFileTreeShowExtensions(showFileExtensions) {
54
- writeStorage(this.storage, this.fileTreeShowExtensionsKey, showFileExtensions ? 'true' : 'false');
48
+ writeStorage(this.storage, STORAGE_KEYS.fileTreeShowExtensions, showFileExtensions ? 'true' : 'false');
55
49
  }
56
50
 
57
51
  getLineWrappingEnabled() {
58
- return readStorage(this.storage, this.lineWrappingKey, null) !== 'false';
52
+ return readStorage(this.storage, STORAGE_KEYS.lineWrapping, null) !== 'false';
59
53
  }
60
54
 
61
55
  setLineWrappingEnabled(enabled) {
62
- writeStorage(this.storage, this.lineWrappingKey, String(enabled));
56
+ writeStorage(this.storage, STORAGE_KEYS.lineWrapping, String(enabled));
63
57
  }
64
58
 
65
59
  getCursorNamesVisible() {
66
- return readStorage(this.storage, 'collabmd-editor-cursor-names', null) !== 'false';
60
+ return readStorage(this.storage, STORAGE_KEYS.cursorNames, null) !== 'false';
67
61
  }
68
62
 
69
63
  setCursorNamesVisible(visible) {
70
- writeStorage(this.storage, 'collabmd-editor-cursor-names', String(visible));
64
+ writeStorage(this.storage, STORAGE_KEYS.cursorNames, String(visible));
71
65
  }
72
66
 
73
67
  getVimModeEnabled() {
74
- return readStorage(this.storage, this.vimModeKey, null) === 'true';
68
+ return readStorage(this.storage, STORAGE_KEYS.vimMode, null) === 'true';
75
69
  }
76
70
 
77
71
  setVimModeEnabled(enabled) {
78
- writeStorage(this.storage, this.vimModeKey, String(enabled));
72
+ writeStorage(this.storage, STORAGE_KEYS.vimMode, String(enabled));
79
73
  }
80
74
 
81
75
  getViewMode() {
82
- const value = readStorage(this.storage, this.viewModeKey, null);
76
+ const value = readStorage(this.storage, STORAGE_KEYS.viewMode, null);
83
77
  if (value === 'editor' || value === 'preview' || value === 'split') {
84
78
  return value;
85
79
  }
@@ -88,12 +82,12 @@ export class BrowserPreferencesPort {
88
82
 
89
83
  setViewMode(view) {
90
84
  if (view === 'editor' || view === 'preview' || view === 'split') {
91
- writeStorage(this.storage, this.viewModeKey, view);
85
+ writeStorage(this.storage, STORAGE_KEYS.viewMode, view);
92
86
  }
93
87
  }
94
88
 
95
89
  getRecentFiles() {
96
- const stored = readStorage(this.storage, this.recentFilesKey, '[]');
90
+ const stored = readStorage(this.storage, STORAGE_KEYS.recentFiles, '[]');
97
91
  try {
98
92
  const recentFiles = JSON.parse(stored);
99
93
  return Array.isArray(recentFiles)
@@ -111,15 +105,15 @@ export class BrowserPreferencesPort {
111
105
  filePath,
112
106
  ...this.getRecentFiles().filter((recentFilePath) => recentFilePath !== filePath),
113
107
  ];
114
- writeStorage(this.storage, this.recentFilesKey, JSON.stringify(recentFiles.slice(0, MAX_RECENT_FILES)));
108
+ writeStorage(this.storage, STORAGE_KEYS.recentFiles, JSON.stringify(recentFiles.slice(0, MAX_RECENT_FILES)));
115
109
  }
116
110
 
117
111
  getSidebarVisible() {
118
- return readStorage(this.storage, this.sidebarVisibleKey, null);
112
+ return readStorage(this.storage, STORAGE_KEYS.sidebarVisible, null);
119
113
  }
120
114
 
121
115
  setSidebarVisible(showSidebar) {
122
- writeStorage(this.storage, this.sidebarVisibleKey, showSidebar ? 'true' : 'false');
116
+ writeStorage(this.storage, STORAGE_KEYS.sidebarVisible, showSidebar ? 'true' : 'false');
123
117
  }
124
118
 
125
119
  }
@@ -0,0 +1,210 @@
1
+ import * as Y from 'yjs';
2
+ import { WebsocketProvider } from 'y-websocket';
3
+ import * as encoding from 'lib0/encoding';
4
+ import * as decoding from 'lib0/decoding';
5
+
6
+ import {
7
+ CANVAS_EDGES_KEY,
8
+ CANVAS_EDGE_ORDER_KEY,
9
+ CANVAS_META_KEY,
10
+ CANVAS_NODES_KEY,
11
+ CANVAS_NODE_ORDER_KEY,
12
+ buildCanvasRoomDocument,
13
+ isCanvasRoomDocStructured,
14
+ parseCanvasJson,
15
+ } from '../../domain/canvas-room-codec.js';
16
+ import { MSG_AGENT_FLUSH, MSG_AGENT_FLUSH_ACK } from '../../domain/collaboration-protocol.js';
17
+ import { resolveWsBaseUrl } from './runtime-config.js';
18
+ import { stopReconnectOnControlledClose } from './yjs-provider-reset-guard.js';
19
+
20
+ export class CanvasRoomClient {
21
+ constructor({ filePath, user, readFile, onChange, onAwareness, onConnection,
22
+ providerFactory = (...args) => new WebsocketProvider(...args), wsBaseUrl = resolveWsBaseUrl() }) {
23
+ Object.assign(this, { filePath, user, readFile, onChange, onAwareness, onConnection, providerFactory, wsBaseUrl });
24
+ this.doc = new Y.Doc();
25
+ this.localOrigin = Symbol('canvas-edit');
26
+ this.meta = this.doc.getMap(CANVAS_META_KEY);
27
+ this.undoManager = new Y.UndoManager([
28
+ this.doc.getMap(CANVAS_NODES_KEY), this.doc.getMap(CANVAS_EDGES_KEY),
29
+ this.doc.getMap(CANVAS_NODE_ORDER_KEY), this.doc.getMap(CANVAS_EDGE_ORDER_KEY),
30
+ ], { trackedOrigins: new Set([this.localOrigin]), captureTimeout: 500 });
31
+ this.pendingFlushes = new Map();
32
+ this.localVersion = 0;
33
+ this.acknowledgedVersion = 0;
34
+ this.hasEverConnected = false;
35
+ this.destroyed = false;
36
+ this.fallback = null;
37
+ this.error = '';
38
+ this.offline = globalThis.navigator?.onLine === false;
39
+ this.handleOffline = () => {
40
+ this.offline = true;
41
+ this.provider?.disconnect();
42
+ this.publish();
43
+ };
44
+ this.handleOnline = () => {
45
+ if (!this.offline) return;
46
+ this.offline = false;
47
+ this.provider?.connect();
48
+ this.publish();
49
+ };
50
+ }
51
+
52
+ get canEdit() {
53
+ return Boolean(this.provider?.synced && isCanvasRoomDocStructured(this.doc) && !this.error
54
+ && !this.meta.get('externalConflict') && !this.disconnecting && !this.offline);
55
+ }
56
+
57
+ get hasPendingWrites() {
58
+ return this.localVersion > this.acknowledgedVersion;
59
+ }
60
+
61
+ async connect() {
62
+ this.doc.on('update', (_update, origin) => {
63
+ if (origin !== this.provider) {
64
+ this.localVersion += 1;
65
+ clearTimeout(this.flushTimer);
66
+ this.flushTimer = setTimeout(() => { void this.flush(); }, 400);
67
+ }
68
+ this.publish();
69
+ });
70
+ this.provider = this.providerFactory(this.wsBaseUrl, this.filePath, this.doc, {
71
+ disableBc: true, maxBackoffTime: 5000,
72
+ });
73
+ stopReconnectOnControlledClose(this.provider);
74
+ this.provider.messageHandlers[MSG_AGENT_FLUSH_ACK] = (_encoder, decoder) => {
75
+ const requestId = decoding.readVarString(decoder);
76
+ this.pendingFlushes.get(requestId)?.(true);
77
+ };
78
+ this.awareness = this.provider.awareness;
79
+ this.setUser(this.user);
80
+ this.awareness.on('change', () => this.publishAwareness());
81
+ this.provider.on('status', ({ status }) => {
82
+ const firstConnection = status === 'connected' && !this.hasEverConnected;
83
+ if (status === 'connected') this.hasEverConnected = true;
84
+ this.onConnection?.({ status, firstConnection, hasEverConnected: this.hasEverConnected, attempts: 0 });
85
+ this.publish();
86
+ });
87
+ this.provider.on('sync', () => {
88
+ this.publish();
89
+ if (this.provider.synced && this.hasPendingWrites) void this.flush();
90
+ });
91
+ globalThis.window?.addEventListener('offline', this.handleOffline);
92
+ globalThis.window?.addEventListener('online', this.handleOnline);
93
+ if (this.offline) this.provider.disconnect();
94
+ this.publish();
95
+ try {
96
+ const response = await this.readFile(this.filePath);
97
+ if (this.destroyed) return;
98
+ this.fallback = parseCanvasJson(response.content);
99
+ } catch (error) {
100
+ if (this.destroyed) return;
101
+ this.error = error.message || 'Unable to open canvas';
102
+ }
103
+ this.publish();
104
+ }
105
+
106
+ publish() {
107
+ if (this.destroyed) return;
108
+ let document = this.fallback;
109
+ if (isCanvasRoomDocStructured(this.doc)) {
110
+ try {
111
+ document = buildCanvasRoomDocument(this.doc);
112
+ this.error = '';
113
+ } catch {
114
+ this.error = 'Canvas data is invalid. Editing is paused to protect the file.';
115
+ }
116
+ } else if (this.meta.get('invalidContent')) {
117
+ this.error = 'Invalid JSON Canvas file. The original file has been preserved.';
118
+ }
119
+ this.onChange?.({
120
+ document, canEdit: this.canEdit, synced: Boolean(this.provider?.synced),
121
+ conflict: this.meta.get('externalConflict'), error: this.error,
122
+ undoAvailable: this.undoManager.canUndo(), redoAvailable: this.undoManager.canRedo(),
123
+ });
124
+ }
125
+
126
+ mutate(operation, { separateUndo = true } = {}) {
127
+ if (!this.canEdit) return false;
128
+ if (separateUndo) this.undoManager.stopCapturing();
129
+ this.doc.transact(() => operation(this.doc), this.localOrigin);
130
+ if (separateUndo) this.undoManager.stopCapturing();
131
+ return true;
132
+ }
133
+
134
+ undo() {
135
+ if (!this.canEdit) return;
136
+ this.undoManager.undo();
137
+ this.publish();
138
+ }
139
+
140
+ redo() {
141
+ if (!this.canEdit) return;
142
+ this.undoManager.redo();
143
+ this.publish();
144
+ }
145
+
146
+ setUser(user) {
147
+ this.user = user;
148
+ this.awareness?.setLocalStateField('user', user);
149
+ }
150
+
151
+ setPresence(field, value) {
152
+ this.awareness?.setLocalStateField(field, value);
153
+ }
154
+
155
+ publishAwareness() {
156
+ const users = [];
157
+ this.awareness?.getStates().forEach((state, clientId) => {
158
+ if (!state.user) return;
159
+ users.push({ ...state.user, clientId, isLocal: clientId === this.doc.clientID,
160
+ pointer: state.pointer, selectedNodeIds: state.selectedNodeIds });
161
+ });
162
+ this.onAwareness?.(users);
163
+ }
164
+
165
+ flush(timeoutMs = 5000) {
166
+ if (!this.hasPendingWrites) return Promise.resolve(true);
167
+ const ws = this.provider?.ws;
168
+ if (!this.provider?.synced || ws?.readyState !== 1) return Promise.resolve(false);
169
+ const version = this.localVersion;
170
+ const requestId = `canvas-${this.doc.clientID}-${crypto.randomUUID()}`;
171
+ return new Promise((resolve) => {
172
+ const finish = (acknowledged) => {
173
+ clearTimeout(timeout);
174
+ this.pendingFlushes.delete(requestId);
175
+ if (acknowledged) this.acknowledgedVersion = Math.max(this.acknowledgedVersion, version);
176
+ resolve(acknowledged && !this.hasPendingWrites);
177
+ };
178
+ const timeout = setTimeout(() => finish(false), timeoutMs);
179
+ this.pendingFlushes.set(requestId, finish);
180
+ try {
181
+ const encoder = encoding.createEncoder();
182
+ encoding.writeVarUint(encoder, MSG_AGENT_FLUSH);
183
+ encoding.writeVarString(encoder, requestId);
184
+ ws.send(encoding.toUint8Array(encoder));
185
+ } catch {
186
+ finish(false);
187
+ }
188
+ });
189
+ }
190
+
191
+ async prepareDisconnect() {
192
+ this.disconnecting = true;
193
+ this.publish();
194
+ const ready = await this.flush();
195
+ this.disconnecting = false;
196
+ this.publish();
197
+ return ready;
198
+ }
199
+
200
+ destroy() {
201
+ this.destroyed = true;
202
+ globalThis.window?.removeEventListener('offline', this.handleOffline);
203
+ globalThis.window?.removeEventListener('online', this.handleOnline);
204
+ clearTimeout(this.flushTimer);
205
+ [...this.pendingFlushes.values()].forEach((finish) => finish(false));
206
+ this.provider?.destroy();
207
+ this.undoManager.destroy();
208
+ this.doc.destroy();
209
+ }
210
+ }
@@ -0,0 +1,36 @@
1
+ import { Compartment, EditorState } from '@codemirror/state';
2
+ import { EditorView, keymap } from '@codemirror/view';
3
+ import { defaultKeymap } from '@codemirror/commands';
4
+ import { markdown } from '@codemirror/lang-markdown';
5
+ import { yCollab, yUndoManagerKeymap } from 'y-codemirror.next';
6
+
7
+ export function createCanvasTextEditor({ parent, text, awareness, undoManager, onClose }) {
8
+ const editable = new Compartment();
9
+ let readOnly = false;
10
+ const view = new EditorView({
11
+ parent,
12
+ state: EditorState.create({
13
+ doc: text.toString(),
14
+ extensions: [
15
+ markdown(), EditorView.lineWrapping,
16
+ EditorView.contentAttributes.of({ 'aria-label': 'Edit canvas text' }),
17
+ editable.of(EditorView.editable.of(true)),
18
+ yCollab(text, awareness, { undoManager }),
19
+ keymap.of([
20
+ { key: 'Escape', run: () => { onClose(); return true; } },
21
+ ...yUndoManagerKeymap, ...defaultKeymap,
22
+ ]),
23
+ ],
24
+ }),
25
+ });
26
+ view.focus();
27
+ return {
28
+ text,
29
+ destroy: () => view.destroy(),
30
+ setReadOnly: (nextReadOnly) => {
31
+ if (readOnly === nextReadOnly) return;
32
+ readOnly = nextReadOnly;
33
+ view.dispatch({ effects: editable.reconfigure(EditorView.editable.of(!readOnly)) });
34
+ },
35
+ };
36
+ }
@@ -2,10 +2,13 @@ import * as Y from 'yjs';
2
2
 
3
3
  import {
4
4
  createCommentId,
5
+ createCommentReactionKey,
5
6
  createCommentThreadSharedType,
6
7
  normalizeCommentAnchor,
7
8
  normalizeCommentBody,
8
9
  normalizeCommentQuote,
10
+ normalizeCommentReactionEmoji,
11
+ serializeCommentThread,
9
12
  serializeCommentThreads,
10
13
  summarizeCommentExcerpt,
11
14
  } from '../../domain/comment-threads.js';
@@ -22,30 +25,6 @@ function createCommentMessage({ body, user }) {
22
25
  };
23
26
  }
24
27
 
25
- function readRecordValue(record, key) {
26
- if (record instanceof Y.Map) {
27
- return record.get(key);
28
- }
29
-
30
- return record?.[key];
31
- }
32
-
33
- function cloneReactionGroups(source = []) {
34
- return Array.isArray(source)
35
- ? source.map((group) => ({
36
- emoji: typeof group?.emoji === 'string' ? group.emoji : '',
37
- users: Array.isArray(group?.users)
38
- ? group.users.map((user) => ({
39
- reactedAt: Number.isFinite(user?.reactedAt) ? user.reactedAt : Date.now(),
40
- userColor: typeof user?.userColor === 'string' ? user.userColor : '',
41
- userId: typeof user?.userId === 'string' ? user.userId : '',
42
- userName: typeof user?.userName === 'string' && user.userName ? user.userName : 'Anonymous',
43
- })).filter((user) => user.userId)
44
- : [],
45
- })).filter((group) => group.emoji && group.users.length > 0)
46
- : [];
47
- }
48
-
49
28
  function normalizeSelectionAnchorPayload(payload, state) {
50
29
  const doc = state?.doc;
51
30
  if (!doc) {
@@ -239,7 +218,11 @@ export class CommentThreadStore {
239
218
  }
240
219
 
241
220
  toggleCommentReaction(threadId, messageId, emoji) {
242
- if (!this.ydoc || !threadId || !messageId || typeof emoji !== 'string' || !emoji.trim()) {
221
+ if (!this.canWrite() || !this.ydoc || !threadId || !messageId || typeof emoji !== 'string') {
222
+ return false;
223
+ }
224
+ const normalizedEmoji = normalizeCommentReactionEmoji(emoji);
225
+ if (!normalizedEmoji) {
243
226
  return false;
244
227
  }
245
228
 
@@ -250,62 +233,21 @@ export class CommentThreadStore {
250
233
  }
251
234
 
252
235
  const thread = this.findSharedCommentThread(threadId);
253
- const messages = thread?.get('messages');
254
- if (!(messages instanceof Y.Array)) {
255
- return false;
256
- }
257
-
258
- const items = messages.toArray();
259
- const messageIndex = items.findIndex((message) => readRecordValue(message, 'id') === messageId);
260
- if (messageIndex < 0) {
236
+ const message = serializeCommentThread(thread)?.messages.find((entry) => entry.id === messageId);
237
+ if (!message) {
261
238
  return false;
262
239
  }
263
-
264
- const messageRecord = items[messageIndex] instanceof Y.Map
265
- ? items[messageIndex].toJSON()
266
- : { ...items[messageIndex] };
267
- const reactions = cloneReactionGroups(messageRecord.reactions);
268
- const reactionIndex = reactions.findIndex((reaction) => reaction.emoji === emoji);
269
-
270
- if (reactionIndex >= 0) {
271
- const nextUsers = reactions[reactionIndex].users.filter((user) => user.userId !== localUserId);
272
- if (nextUsers.length === reactions[reactionIndex].users.length) {
273
- nextUsers.push({
274
- reactedAt: Date.now(),
275
- userColor: localUser?.color ?? '',
276
- userId: localUserId,
277
- userName: localUser?.name ?? 'Anonymous',
278
- });
279
- }
280
-
281
- if (nextUsers.length === 0) {
282
- reactions.splice(reactionIndex, 1);
283
- } else {
284
- reactions[reactionIndex] = {
285
- ...reactions[reactionIndex],
286
- users: nextUsers,
287
- };
288
- }
289
- } else {
290
- reactions.push({
291
- emoji,
292
- users: [{
293
- reactedAt: Date.now(),
294
- userColor: localUser?.color ?? '',
295
- userId: localUserId,
296
- userName: localUser?.name ?? 'Anonymous',
297
- }],
298
- });
299
- }
300
-
301
- const nextMessage = {
302
- ...messageRecord,
303
- reactions,
304
- };
240
+ const hasReaction = message.reactions.some((group) => group.emoji === normalizedEmoji
241
+ && group.users.some((user) => user.userId === localUserId));
305
242
 
306
243
  this.ydoc.transact(() => {
307
- messages.delete(messageIndex, 1);
308
- messages.insert(messageIndex, [nextMessage]);
244
+ // Keep the message identity intact; different users change independent keys.
245
+ thread.set(createCommentReactionKey(messageId, normalizedEmoji, localUserId), hasReaction ? null : {
246
+ reactedAt: Date.now(),
247
+ userColor: localUser?.color ?? '',
248
+ userId: localUserId,
249
+ userName: localUser?.name ?? 'Anonymous',
250
+ });
309
251
  }, 'comment-reaction-toggle');
310
252
 
311
253
  return true;