collabmd 0.1.45 → 0.1.47

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 (356) hide show
  1. package/README.md +40 -19
  2. package/bin/collabmd.js +27 -1
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +1 -0
  4. package/dist/client/assets/{abnfDiagram-N423BO3Z-CUMTFJhI.js → abnfDiagram-VCTEODGH-BNDLzJ7L.js} +1 -1
  5. package/dist/client/assets/architecture-7GRP2DOG-BMOPAkUC.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-T3A2C74G-w25m71oV.js → architectureDiagram-5GKGNRK7-I39sYjHI.js} +1 -1
  7. package/dist/client/assets/base-BYODPHdS.js +1 -0
  8. package/dist/client/assets/blockDiagram-NRAW4CY4-C6DtJy5Y.js +129 -0
  9. package/dist/client/assets/browser-BKLM0ThC-IxjwuOIz-BBqQnUEb.js +1 -0
  10. package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
  11. package/dist/client/assets/c4Diagram-UCG6FXSJ-CtWtxS9_.js +38 -0
  12. package/dist/client/assets/channel-CaoKA_We.js +1 -0
  13. package/dist/client/assets/{chunk-U6XO7XAA-BFxjP5U3.js → chunk-2ZTRR5NV-C2qZJila.js} +1 -1
  14. package/dist/client/assets/{chunk-YOTPTUD7-D0vRKqYp.js → chunk-3Z5EZCMW-DMniOlPd.js} +1 -1
  15. package/dist/client/assets/{chunk-BIQX33UG-DZEsGuSn.js → chunk-5V3GS4D5-i-ZDOEnP.js} +1 -1
  16. package/dist/client/assets/{chunk-MOZMSUNE-XVucA-n6.js → chunk-6AZGARVD-BB6FqMl-.js} +1 -1
  17. package/dist/client/assets/{chunk-5JV3BV7I-Cy5pj3QY.js → chunk-6EIED4P4-DdUJkP0f.js} +1 -1
  18. package/dist/client/assets/{chunk-R7FJI6CG-Cnn3gE-Y.js → chunk-6K3QC6MW-CqVsVn-y.js} +1 -1
  19. package/dist/client/assets/{chunk-OSBZ3O6U-CrKZOUOr.js → chunk-6TQVIW2G-DKlLbNIP.js} +1 -1
  20. package/dist/client/assets/{chunk-JG7HCLWE-BYfiSaVt.js → chunk-747NJXEK-BFkZPlab.js} +1 -1
  21. package/dist/client/assets/{chunk-NSK5VX7P-TLH37VXI.js → chunk-75Z2AOVW-CdFSaPjz.js} +1 -1
  22. package/dist/client/assets/chunk-CKBBP62Z-DG26UiBv.js +2 -0
  23. package/dist/client/assets/{chunk-3NCLNEKW-Y2Jf_LI-.js → chunk-CLGD4ZFX-DQLgbQrv.js} +1 -1
  24. package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +2 -0
  25. package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +127 -0
  26. package/dist/client/assets/{chunk-2GRJ4B5K-DhnlUv3F.js → chunk-F27PBJKO-CNHIFhjo.js} +1 -1
  27. package/dist/client/assets/{chunk-YGJVY4SA-CjSe7Bx7.js → chunk-FLYJHZSQ-Be7lKF17.js} +1 -1
  28. package/dist/client/assets/{chunk-KEIR6QF5-BICK3FdT.js → chunk-FOHPRMQF-DHwB1DNv.js} +2 -2
  29. package/dist/client/assets/{chunk-5RXB4S5H-BWrwFqMI.js → chunk-G27WJ6UU-D9-eg537.js} +2 -2
  30. package/dist/client/assets/chunk-GMAD6QVW-CbcBqPM5.js +72 -0
  31. package/dist/client/assets/{chunk-QBLGF6JB-CxAyS2mW.js → chunk-I5DQTOEV-Hm2LfiYI.js} +1 -1
  32. package/dist/client/assets/{chunk-5FCAYU7R-Dh5nylU8.js → chunk-ICYGCRZG-749o-pGJ.js} +1 -1
  33. package/dist/client/assets/{chunk-CQNSW5MT-B8S09OIh.js → chunk-IH6LHLGP-CC6I9F3i.js} +1 -1
  34. package/dist/client/assets/chunk-JQ64N6SF-DSFNH4bh.js +1 -0
  35. package/dist/client/assets/{chunk-CYSBUYHQ-C1Rf4art.js → chunk-KI3K4JFJ-Rma-0k_M.js} +1 -1
  36. package/dist/client/assets/{chunk-GF5L2VYU-DkJpO7ff.js → chunk-LCL6LL3I-DmDkVukh.js} +1 -1
  37. package/dist/client/assets/chunk-LNGE3PJU-DWpGwHrW.js +34 -0
  38. package/dist/client/assets/chunk-OBVCFTLP-BJR7u1jh.js +1 -0
  39. package/dist/client/assets/{chunk-5TONJI2A-DQOY3q1H.js → chunk-OUJLGHUK-D0j2JRSg.js} +1 -1
  40. package/dist/client/assets/chunk-P2QGCYS3-BRpqY32H.js +1 -0
  41. package/dist/client/assets/{chunk-KBJHAD2P-CB_3zWUf.js → chunk-POPQ4Y6H-Cb4SlVZF.js} +1 -1
  42. package/dist/client/assets/{chunk-4I5QYGJK-CCB2YcIH.js → chunk-PWAF6VOD-Cz8IwBB5.js} +1 -1
  43. package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +10 -0
  44. package/dist/client/assets/chunk-RHFEMEQ7-CWY8cf4H.js +168 -0
  45. package/dist/client/assets/chunk-RM4UPSZO-CWRwxNWJ.js +41 -0
  46. package/dist/client/assets/{chunk-6Q2QTUOP-CPlWzBtc.js → chunk-SVP7TREG-CyHOmcac.js} +1 -1
  47. package/dist/client/assets/chunk-TEH6E4GO-DVayPj-q.js +62 -0
  48. package/dist/client/assets/{chunk-EMLP6XTP-RLD4nXpI.js → chunk-UY3FDG6J-CnmaAkG5.js} +1 -1
  49. package/dist/client/assets/{chunk-5HE753X5-Bw-yg4kO.js → chunk-XHIXRSVI-BFQ6B8BE.js} +1 -1
  50. package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +1 -0
  51. package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +1 -0
  52. package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
  53. package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
  54. package/dist/client/assets/comment-ui-shared-DWkk9Vh3.js +7 -0
  55. package/dist/client/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
  56. package/dist/client/assets/{cynefinDiagram-MW4NZA55-CJxXfJek.js → cynefinDiagram-5FMLGOSQ-CdTPi2yO.js} +1 -1
  57. package/dist/client/assets/dagre-3AP2YEHR-5gtJtd0O.js +4 -0
  58. package/dist/client/assets/dagre-PrKaheQc.js +1 -0
  59. package/dist/client/assets/{diagram-7IWD3JNH-B2d2Bm15.js → diagram-S7CK7UJ4-BudWz9WU.js} +4 -4
  60. package/dist/client/assets/{diagram-UB23O5K3-DJcKCwv9.js → diagram-UQ7AKVKN-3v4rbH-s.js} +1 -1
  61. package/dist/client/assets/{diagram-B4RE2ZJO-NP4UAwjR.js → diagram-VSXAHHWV-CQRVEZR7.js} +1 -1
  62. package/dist/client/assets/{diagram-Q27KOJAE-DylDuwor.js → diagram-VX7I27RA-C_-rj0sw.js} +1 -1
  63. package/dist/client/assets/{diagram-LBJQPF4R-D1FJQV8t.js → diagram-Z3DM3KII-B4yxavgj.js} +1 -1
  64. package/dist/client/assets/direct-engine-DdrQKpgq-CtKfQzCt.js +5 -0
  65. package/dist/client/assets/dist-73RPdcyU.js +1 -0
  66. package/dist/client/assets/dist-BJ1bJs33.js +1 -0
  67. package/dist/client/assets/{dist-CYZmbokb.js → dist-BK9QvQrc.js} +9 -9
  68. package/dist/client/assets/dist-BPHLJ09L.js +11 -0
  69. package/dist/client/assets/dist-B_yQaEv_.js +6 -0
  70. package/dist/client/assets/dist-BeajGMhO.js +1 -0
  71. package/dist/client/assets/{dist-JYEDRrPT.js → dist-Bw3VIE_v.js} +1 -1
  72. package/dist/client/assets/dist-ByrG2qBA.js +1 -0
  73. package/dist/client/assets/dist-C5NgpdG5.js +1 -0
  74. package/dist/client/assets/dist-CH-sQu3r.js +1 -0
  75. package/dist/client/assets/dist-CKS_-are.js +1 -0
  76. package/dist/client/assets/{dist-MvcV4190.js → dist-CdmKGRGr.js} +1 -1
  77. package/dist/client/assets/{dist-DGb27Dl6.js → dist-CietwTVS.js} +1 -1
  78. package/dist/client/assets/dist-D37Hyu3h.js +9 -0
  79. package/dist/client/assets/dist-D3fehAZ2.js +2 -0
  80. package/dist/client/assets/dist-D5wCC32z.js +1 -0
  81. package/dist/client/assets/{dist-DCzuX1AF.js → dist-DDUn5zTz.js} +1 -1
  82. package/dist/client/assets/dist-DGEbEfw4.js +1 -0
  83. package/dist/client/assets/dist-DVy5woUy.js +1 -0
  84. package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZAAWTG-.js} +1 -1
  85. package/dist/client/assets/{dist-Drp8uu5J.js → dist-Dir5dDKZ.js} +1 -1
  86. package/dist/client/assets/dist-Dl_tpDSr.js +1 -0
  87. package/dist/client/assets/dist-DuH3ZrHo.js +1 -0
  88. package/dist/client/assets/dist-DutPuFmC.js +1 -0
  89. package/dist/client/assets/{dist-D2AQ-76g.js → dist-LQ4kDCKz.js} +1 -1
  90. package/dist/client/assets/{dist-C11hHqv_.js → dist-RKBTaQMb.js} +12 -12
  91. package/dist/client/assets/dist-Tw-Fxpeb.js +1 -0
  92. package/dist/client/assets/dist-eBx8q6Sy.js +1 -0
  93. package/dist/client/assets/{dist-DK5Gs2wa.js → dist-mj2Ht75J.js} +1 -1
  94. package/dist/client/assets/{dist-CnL7Y2sM.js → dist-netZOQtD.js} +1 -1
  95. package/dist/client/assets/{drawio-editor-ClE8jpoG.js → drawio-editor-BjrxTe3l.js} +1 -1
  96. package/dist/client/assets/drawioEditor-DUVZjSSr.js +2 -0
  97. package/dist/client/assets/{ebnfDiagram-BXEA7PRR-nw0wmA6y.js → ebnfDiagram-PWID7BFC-OXa8VEp1.js} +1 -1
  98. package/dist/client/assets/editor-session-DfZuRgB_.js +54 -0
  99. package/dist/client/assets/embedded-editor-base-DvMynhKu.css +1 -0
  100. package/dist/client/assets/embedpdf-BmE13ZZ0.js +1 -0
  101. package/dist/client/assets/embedpdf-DA4bA119-BPqZxwjc.js +2363 -0
  102. package/dist/client/assets/{en-7NFCYGKX-BrgYZAEe.js → en-SJ2YIMFF-CJBL3nkq.js} +1 -1
  103. package/dist/client/assets/erDiagram-SSCWMZ5O-C-iy6VkX.js +99 -0
  104. package/dist/client/assets/es-CAsWR6hG.js +22 -0
  105. package/dist/client/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
  106. package/dist/client/assets/excalidraw-editor-BlcN6umJ.css +1 -0
  107. package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +9 -0
  108. package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
  109. package/dist/client/assets/excalidrawEditor-B0jOK3lv.js +2 -0
  110. package/dist/client/assets/export-page-Bs3xE1fg.js +57 -0
  111. package/dist/client/assets/exportDocument-Dg9MjkIk.js +2 -0
  112. package/dist/client/assets/exportDocument-PnYU3v1s.css +1 -0
  113. package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
  114. package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
  115. package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +1 -0
  116. package/dist/client/assets/{ganttDiagram-PKOTCBZU-BnM97BCT.js → ganttDiagram-EL5Y4UJY-DtKHAbez.js} +1 -1
  117. package/dist/client/assets/gitGraph-4MIJSDKK-DYlXv_yJ.js +1 -0
  118. package/dist/client/assets/{gitGraphDiagram-DS77QQ5N-BQpQhjuN.js → gitGraphDiagram-WWUBYQGX-oue8fQ5o.js} +1 -1
  119. package/dist/client/assets/github-dark.min-BV82VuKq.css +1 -0
  120. package/dist/client/assets/github.min-v3dd8CNH.css +1 -0
  121. package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +1 -0
  122. package/dist/client/assets/index-BIpgxyGZ.css +1 -0
  123. package/dist/client/assets/index-Z5sI0Bj-.js +2 -0
  124. package/dist/client/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
  125. package/dist/client/assets/{infoDiagram-6WML65LV-DYB8HCDQ.js → infoDiagram-RXCK75RN-BMzo6KIn.js} +1 -1
  126. package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-Coz_P3t6.js → ishikawaDiagram-5VMMS53U-BIM3h287.js} +1 -1
  127. package/dist/client/assets/{journeyDiagram-NVQOT4AX-B6edChce.js → journeyDiagram-EYS64GPL-mF6Lh8O-.js} +1 -1
  128. package/dist/client/assets/{kanban-definition-27J2QSJJ-BFd9-cLc.js → kanban-definition-3QL26DDD-DLN2NyoR.js} +2 -2
  129. package/dist/client/assets/{line-B_S_S8kV.js → line-BfTWJmgf.js} +1 -1
  130. package/dist/client/assets/main-Dwnj9Gaq.js +903 -0
  131. package/dist/client/assets/{mermaid-parser.core-CUVdebAV.js → mermaid-parser.core-Bl0efOMt.js} +2 -2
  132. package/dist/client/assets/mermaid.core-CLZBlpIW.js +11 -0
  133. package/dist/client/assets/{mindmap-definition-FAOFIHXS-C9PQ38Al.js → mindmap-definition-FBJOCRG2-CRyHaHTk.js} +1 -1
  134. package/dist/client/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
  135. package/dist/client/assets/pdfium-RAgkpwfK.wasm +0 -0
  136. package/dist/client/assets/{pegDiagram-VL7TDLO6-BMZUrSaU.js → pegDiagram-XKGWAZYB-B31fKPR2.js} +1 -1
  137. package/dist/client/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
  138. package/dist/client/assets/{pieDiagram-7S7Q4E2Y-h8kbCL9o.js → pieDiagram-E7YTZNPT-B5OZPb88.js} +1 -1
  139. package/dist/client/assets/preview-render-compiler-BApvlLLo.js +35 -0
  140. package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +1 -0
  141. package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
  142. package/dist/client/assets/preview-render-worker-BmN79bVc.js +60 -0
  143. package/dist/client/assets/prod-BiYPMR1i.js +177 -0
  144. package/dist/client/assets/prod-VxdGZ5Ly.js +1 -0
  145. package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-CkElyfMi.js → quadrantDiagram-AXDQQJYC-Drrw14YH.js} +1 -1
  146. package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +12 -0
  147. package/dist/client/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
  148. package/dist/client/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
  149. package/dist/client/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
  150. package/dist/client/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
  151. package/dist/client/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
  152. package/dist/client/assets/{railroadDiagram-AXF67PYL-c75gnikD.js → railroadDiagram-O6MQD6OU-BPifP0MB.js} +1 -1
  153. package/dist/client/assets/{requirementDiagram-LRYGKXZP-mNxKLuHS.js → requirementDiagram-EFPCY7ZU-CRZG6mRB.js} +2 -2
  154. package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
  155. package/dist/client/assets/{sankeyDiagram-W5VNT64P-0OhplksI.js → sankeyDiagram-P5KCCOFB-DvarEJpo.js} +1 -1
  156. package/dist/client/assets/{sequenceDiagram-SI44F4Z6-BJ_yAocC.js → sequenceDiagram-WJ2MYXX4-B4oWlb-w.js} +1 -1
  157. package/dist/client/assets/{stateDiagram-OKZ733FA-Cc83O86s.js → stateDiagram-HBIQ2CUA-qbHIIqRZ.js} +1 -1
  158. package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +1 -0
  159. package/dist/client/assets/swimlanes-XN3QIQJK-xB6CtUGj.js +1 -0
  160. package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +8 -0
  161. package/dist/client/assets/{timeline-definition-Z64GVDOM-BWDAGTHz.js → timeline-definition-24CTP7MA-Lj_yTNlY.js} +1 -1
  162. package/dist/client/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
  163. package/dist/client/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
  164. package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
  165. package/dist/client/assets/{vennDiagram-T6HMQDX7-LOSsnlMB.js → vennDiagram-4TSXK5OY-BekRp5Y0.js} +1 -1
  166. package/dist/client/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
  167. package/dist/client/assets/{wardleyDiagram-T6FBY63Y-BxII3foY.js → wardleyDiagram-VM6X3IG4-kdnV_Hdm.js} +1 -1
  168. package/dist/client/assets/worker-engine-m0rlHn8R-S8mpn_O0.js +17369 -0
  169. package/dist/client/assets/{xychartDiagram-ELKLHX3M-B-pTEjju.js → xychartDiagram-S5SC5T6Z-DufGwNSB.js} +3 -3
  170. package/dist/client/drawio-editor.html +1 -1
  171. package/dist/client/excalidraw-editor.html +1 -1
  172. package/dist/client/export-document.html +3 -3
  173. package/dist/client/index.html +30 -8
  174. package/docker-compose.yml +42 -0
  175. package/package.json +32 -18
  176. package/scripts/local-structurizr-compose.mjs +101 -0
  177. package/src/client/app/index.html +27 -5
  178. package/src/client/application/app-shell/comments-feature.js +21 -13
  179. package/src/client/application/app-shell/git-feature.js +14 -2
  180. package/src/client/application/app-shell/presence-feature.js +4 -3
  181. package/src/client/application/app-shell/ui-feature-shell.js +109 -2
  182. package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
  183. package/src/client/application/app-shell/workspace-feature.js +5 -1
  184. package/src/client/application/app-shell-elements.js +3 -0
  185. package/src/client/application/pdf-preview-controller.js +75 -0
  186. package/src/client/application/preview-render-compiler.js +3 -0
  187. package/src/client/application/quick-switcher-loader.js +1 -1
  188. package/src/client/application/structurizr-preview-controller.js +395 -0
  189. package/src/client/application/workspace-coordinator.js +25 -3
  190. package/src/client/application/workspace-preview-controller.js +88 -2
  191. package/src/client/assets/vendor/highlight/github-dark.min.css +2 -10
  192. package/src/client/assets/vendor/highlight/github.min.css +2 -10
  193. package/src/client/bootstrap/collabmd-app-shell.js +39 -6
  194. package/src/client/browser-utils.js +44 -0
  195. package/src/client/domain/runtime-paths.js +26 -5
  196. package/src/client/domain/structurizr-language.js +99 -0
  197. package/src/client/domain/vault-paths.js +48 -0
  198. package/src/client/excalidraw-editor.js +844 -51
  199. package/src/client/export/export-host.js +1 -1
  200. package/src/client/export/export-page.js +6 -2
  201. package/src/client/export/export-pipeline.js +60 -1
  202. package/src/client/infrastructure/browser-preferences-port.js +10 -0
  203. package/src/client/infrastructure/comment-thread-store.js +34 -13
  204. package/src/client/infrastructure/editor-session.js +10 -0
  205. package/src/client/infrastructure/editor-view-adapter.js +46 -2
  206. package/src/client/infrastructure/excalidraw-room-client.js +103 -14
  207. package/src/client/infrastructure/git-api-client.js +5 -0
  208. package/src/client/infrastructure/vault-api-client.js +26 -0
  209. package/src/client/presentation/comment-ui/comment-ui-render.js +4 -0
  210. package/src/client/presentation/comment-ui/comment-ui-shared.js +4 -0
  211. package/src/client/presentation/comment-ui-controller.js +2 -0
  212. package/src/client/presentation/drawio-embed-controller.js +9 -102
  213. package/src/client/presentation/drawio-viewer.js +118 -0
  214. package/src/client/presentation/excalidraw-embed-controller.js +81 -8
  215. package/src/client/presentation/file-action-controller.js +102 -0
  216. package/src/client/presentation/file-explorer-controller.js +0 -5
  217. package/src/client/presentation/file-explorer-view.js +51 -73
  218. package/src/client/presentation/file-icon-svg.js +40 -0
  219. package/src/client/presentation/file-tree-state.js +6 -3
  220. package/src/client/presentation/git-diff-view-controller.js +731 -3
  221. package/src/client/presentation/git-panel-controller.js +2 -2
  222. package/src/client/presentation/outline-controller.js +86 -0
  223. package/src/client/presentation/quick-switcher-controller.js +73 -22
  224. package/src/client/presentation/quick-switcher-text-search.js +1 -1
  225. package/src/client/styles/components/navigation.css +11 -1
  226. package/src/client/styles/features/diagram-preview.css +76 -0
  227. package/src/client/styles/features/diff-special.css +309 -0
  228. package/src/client/styles/features/preview-markdown.css +25 -0
  229. package/src/client/styles/features/quick-switcher.css +8 -0
  230. package/src/client/styles/foundation/themes.css +7 -0
  231. package/src/client/styles/layout/shell.css +40 -1
  232. package/src/client/styles/layout/sidebar.css +42 -4
  233. package/src/client/styles/layout/view-modes.css +1 -0
  234. package/src/client/styles/overrides/highlightjs.css +20 -4
  235. package/src/client/styles/style.css +1 -0
  236. package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
  237. package/src/domain/comment-threads.js +63 -5
  238. package/src/domain/excalidraw-room-codec.js +6 -4
  239. package/src/domain/file-kind.js +35 -3
  240. package/src/server/config/env.js +79 -0
  241. package/src/server/create-app-server.js +12 -0
  242. package/src/server/domain/bases/base-definition.js +1 -1
  243. package/src/server/domain/bases/base-expression-runtime.js +1 -4
  244. package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
  245. package/src/server/domain/bases/base-transform.js +1 -4
  246. package/src/server/domain/collaboration/collaboration-room.js +55 -21
  247. package/src/server/domain/comment-overview.js +15 -5
  248. package/src/server/domain/ripgrep-search-service.js +4 -0
  249. package/src/server/infrastructure/git/diff-service.js +5 -0
  250. package/src/server/infrastructure/git/git-service.js +44 -11
  251. package/src/server/infrastructure/git/history-service.js +54 -0
  252. package/src/server/infrastructure/git/image-mime.js +14 -0
  253. package/src/server/infrastructure/git/untracked-files.js +28 -1
  254. package/src/server/infrastructure/http/create-git-api-command-handler.js +3 -1
  255. package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
  256. package/src/server/infrastructure/http/create-request-handler.js +16 -23
  257. package/src/server/infrastructure/http/create-static-handler.js +5 -2
  258. package/src/server/infrastructure/http/create-structurizr-api-handler.js +147 -0
  259. package/src/server/infrastructure/http/create-vault-api-command-handler.js +66 -4
  260. package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
  261. package/src/server/infrastructure/http/http-request-helpers.js +22 -0
  262. package/src/server/infrastructure/persistence/path-utils.js +1 -1
  263. package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
  264. package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
  265. package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +400 -0
  266. package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
  267. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
  268. package/dist/client/assets/architecture-TIHT7OUA-CsHfPErM.js +0 -1
  269. package/dist/client/assets/base-CxPEiHuy.js +0 -1
  270. package/dist/client/assets/blockDiagram-VBNYF7ZC-CipJt9Jl.js +0 -132
  271. package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
  272. package/dist/client/assets/c4Diagram-5PPSVZJV-B5ktVx6g.js +0 -10
  273. package/dist/client/assets/channel-4Jkmq93I.js +0 -1
  274. package/dist/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
  275. package/dist/client/assets/chunk-7Z6QIM7H-BP9Pdsm9.js +0 -10
  276. package/dist/client/assets/chunk-ERSWUTIN-ME9RVNel.js +0 -41
  277. package/dist/client/assets/chunk-I66GZJ75-CVFiiffQ.js +0 -127
  278. package/dist/client/assets/chunk-J7OUQ5F2-4CEb7pvH.js +0 -2
  279. package/dist/client/assets/chunk-JQJVKLGR-cx8X_24v.js +0 -156
  280. package/dist/client/assets/chunk-QR6OTTB3-CDAA_5Ib.js +0 -62
  281. package/dist/client/assets/chunk-RYQCIY6F-D-a0zH8K.js +0 -1
  282. package/dist/client/assets/chunk-UBXNYLIW-Bhk-PbFK.js +0 -1
  283. package/dist/client/assets/chunk-W5SLKNZC-BdNSCxZ9.js +0 -1
  284. package/dist/client/assets/chunk-WRU74C26-h6_XkD3_.js +0 -70
  285. package/dist/client/assets/chunk-ZIRB5QZD-O39ZBUk3.js +0 -32
  286. package/dist/client/assets/classDiagram-JCYQIIEL-L7lsyW_w.js +0 -1
  287. package/dist/client/assets/classDiagram-v2-OCEON4UE-L7lsyW_w.js +0 -1
  288. package/dist/client/assets/comment-threads-DDegmaFO.js +0 -4
  289. package/dist/client/assets/cynefin-VYW2F7L2-Ds2Fw2Y0.js +0 -1
  290. package/dist/client/assets/dagre-Buvkdvvj.js +0 -1
  291. package/dist/client/assets/dagre-VZM6K2ZE-L1xArOGs.js +0 -4
  292. package/dist/client/assets/dist-BH87JoHx.js +0 -11
  293. package/dist/client/assets/dist-BIkPRZO7.js +0 -6
  294. package/dist/client/assets/dist-BqCsFpx4.js +0 -1
  295. package/dist/client/assets/dist-C2JHCt8U.js +0 -1
  296. package/dist/client/assets/dist-CZjTtmTu.js +0 -1
  297. package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
  298. package/dist/client/assets/dist-CqQEC8gR.js +0 -1
  299. package/dist/client/assets/dist-D4Rzqod1.js +0 -1
  300. package/dist/client/assets/dist-D5sn-mwk.js +0 -9
  301. package/dist/client/assets/dist-D7G-n87v.js +0 -1
  302. package/dist/client/assets/dist-DRqOrNgN.js +0 -1
  303. package/dist/client/assets/dist-DetkMS_4.js +0 -1
  304. package/dist/client/assets/dist-DhLvC8DS.js +0 -2
  305. package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
  306. package/dist/client/assets/dist-DtbjNB28.js +0 -1
  307. package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
  308. package/dist/client/assets/dist-GrygD2zE.js +0 -1
  309. package/dist/client/assets/dist-HmEpWUow.js +0 -1
  310. package/dist/client/assets/dist-RTa81Km7.js +0 -1
  311. package/dist/client/assets/drawioEditor-CXaTM9Ds.js +0 -2
  312. package/dist/client/assets/editor-session-GTwZS6Df.js +0 -16
  313. package/dist/client/assets/embedded-editor-base-CiNEc8vy.css +0 -1
  314. package/dist/client/assets/erDiagram-JOGREHBK-cYUpEKpf.js +0 -85
  315. package/dist/client/assets/eventmodeling-45OFAUF4-CLEiVx5s.js +0 -1
  316. package/dist/client/assets/excalidraw-editor-BZVM81BO.js +0 -9
  317. package/dist/client/assets/excalidraw-editor-CyGwOhLz.css +0 -1
  318. package/dist/client/assets/excalidraw-scene-C8n9CHsK.js +0 -176
  319. package/dist/client/assets/excalidrawEditor-DxJanCze.js +0 -2
  320. package/dist/client/assets/export-page-CUfws2T-.js +0 -39
  321. package/dist/client/assets/exportDocument-BCuxqXDY.js +0 -2
  322. package/dist/client/assets/exportDocument-DV-OX74E.css +0 -1
  323. package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
  324. package/dist/client/assets/flowDiagram-UKHOOZJN-D0ypP0Ez.js +0 -1
  325. package/dist/client/assets/gitGraph-TEB2WS4Q-CZFBqwW-.js +0 -1
  326. package/dist/client/assets/github-dark.min-Z5exjX_n.css +0 -1
  327. package/dist/client/assets/github.min-D2BCvnWf.css +0 -1
  328. package/dist/client/assets/image-QXRYHUNP-lfTHE3bb.js +0 -1
  329. package/dist/client/assets/index-C3YnUgMD.css +0 -1
  330. package/dist/client/assets/index-D2wXraJ1.js +0 -2
  331. package/dist/client/assets/info-DKCQHKI2--1InqQeT.js +0 -1
  332. package/dist/client/assets/main-DtiGz7uI.js +0 -795
  333. package/dist/client/assets/map-BaFkSB1l.js +0 -1
  334. package/dist/client/assets/mermaid.core-DN-lF51U.js +0 -11
  335. package/dist/client/assets/packet-7NZHBO7P-BGMXB0XL.js +0 -1
  336. package/dist/client/assets/pie-RZYD4A2V-DgYg3tCt.js +0 -1
  337. package/dist/client/assets/preview-render-compiler-BSaQO_p3.js +0 -35
  338. package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
  339. package/dist/client/assets/preview-render-profile-BZzGSg6K.js +0 -22
  340. package/dist/client/assets/preview-render-worker-B42LWvrD.js +0 -60
  341. package/dist/client/assets/quick-switcher-controller-shW_6vPY.js +0 -12
  342. package/dist/client/assets/radar-I7S5WNFK-Ag-zp_Rq.js +0 -1
  343. package/dist/client/assets/railroad-3IZDKUUU-aibJDIDV.js +0 -1
  344. package/dist/client/assets/railroad-abnf-AHOZXSZD-BBMEuvIl.js +0 -1
  345. package/dist/client/assets/railroad-ebnf-EBAXGLYW-Be1nseej.js +0 -1
  346. package/dist/client/assets/railroad-peg-LSFZ7HO6-DIvD-Eyb.js +0 -1
  347. package/dist/client/assets/room-CQLoqtMo.js +0 -1
  348. package/dist/client/assets/runtime-config-DSGmGE9V.js +0 -1
  349. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-B1-BufUk.js +0 -1
  350. package/dist/client/assets/swimlanes-SLNWSIFB-oVTWEJIj.js +0 -2
  351. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-C5OTVwEu.js +0 -8
  352. package/dist/client/assets/treeView-QDETBFTQ-uHZ-3Fz8.js +0 -1
  353. package/dist/client/assets/treemap-6X3UGDF4-BnUKyy3l.js +0 -1
  354. package/dist/client/assets/vault-api-client-C6bcGwy-.js +0 -5
  355. package/dist/client/assets/wardley-OPB4EBWU-Da7TLC2F.js +0 -1
  356. package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
@@ -12,7 +12,7 @@ function createJobId() {
12
12
  }
13
13
 
14
14
  function normalizeFormat(format) {
15
- return format === 'pdf' ? 'pdf' : 'docx';
15
+ return ['html', 'pdf'].includes(format) ? format : 'docx';
16
16
  }
17
17
 
18
18
  function buildExportUrl({ filePath, format, jobId }) {
@@ -51,9 +51,13 @@ async function bootstrap() {
51
51
  const snapshot = await prepareExportSnapshot(payload);
52
52
  const mount = renderSnapshot(snapshot);
53
53
  snapshot.html = await waitForRenderedExportContent(mount);
54
- setStatus(payload.action === 'pdf' ? 'Opening print dialog…' : 'Preparing DOCX download…');
54
+ setStatus(payload.action === 'pdf'
55
+ ? 'Opening print dialog…'
56
+ : `Preparing ${payload.action.toUpperCase()} download…`);
55
57
  await runExportAdapter(snapshot, payload.action);
56
- setStatus(payload.action === 'pdf' ? 'Print dialog opened.' : 'DOCX download started.');
58
+ setStatus(payload.action === 'pdf'
59
+ ? 'Print dialog opened.'
60
+ : `${payload.action.toUpperCase()} download started.`);
57
61
  postExportPageMessage('complete', { jobId: new URL(window.location.href).searchParams.get('job') || '' });
58
62
  } catch (error) {
59
63
  const message = error instanceof Error ? error.message : 'Export failed';
@@ -1,5 +1,12 @@
1
1
  import { exportToSvg as exportExcalidrawToSvg } from '@excalidraw/excalidraw';
2
2
 
3
+ import baseStyles from '../styles/base.css?inline';
4
+ import tokenStyles from '../styles/foundation/tokens.css?inline';
5
+ import themeStyles from '../styles/foundation/themes.css?inline';
6
+ import previewStyles from '../styles/features/preview-markdown.css?inline';
7
+ import highlightOverrideStyles from '../styles/overrides/highlightjs.css?inline';
8
+ import exportStyles from '../styles/export-document.css?inline';
9
+ import highlightStyles from '../assets/vendor/highlight/github.min.css?inline';
3
10
  import {
4
11
  encodeSvgDataUrl,
5
12
  loadImage,
@@ -9,6 +16,7 @@ import {
9
16
  import { compilePreviewDocument } from '../application/preview-render-compiler.js';
10
17
  import { stripVaultFileExtension } from '../../domain/file-kind.js';
11
18
  import { parseSceneJson, sceneToInitialData } from '../domain/excalidraw-scene.js';
19
+ import { escapeHtml } from '../domain/vault-utils.js';
12
20
  import { downloadBlob } from '../browser-utils.js';
13
21
  import { resolveApiUrl, resolveAppUrl } from '../infrastructure/runtime-config.js';
14
22
 
@@ -46,6 +54,20 @@ const DOCX_IMAGE_TYPE_LABELS = new Map([
46
54
  ['image/gif', 'GIF'],
47
55
  ['image/webp', 'WebP'],
48
56
  ]);
57
+ const HTML_EXPORT_STYLES = [
58
+ baseStyles,
59
+ tokenStyles,
60
+ themeStyles,
61
+ previewStyles,
62
+ highlightOverrideStyles,
63
+ exportStyles,
64
+ highlightStyles,
65
+ `html, body {
66
+ height: auto;
67
+ min-height: 100%;
68
+ overflow: visible;
69
+ }`,
70
+ ].join('\n');
49
71
 
50
72
  let mermaidLoaderPromise = null;
51
73
  let assetCounter = 0;
@@ -1387,6 +1409,37 @@ export function buildDocxHtmlDocument(snapshot) {
1387
1409
  </html>`;
1388
1410
  }
1389
1411
 
1412
+ export function buildHtmlDocument(snapshot) {
1413
+ const template = document.createElement('template');
1414
+ template.innerHTML = snapshot.html;
1415
+ Array.from(template.content.querySelectorAll('[data-export-docx-src]')).forEach((element) => {
1416
+ element.removeAttribute('data-export-docx-src');
1417
+ });
1418
+
1419
+ return `<!DOCTYPE html>
1420
+ <html lang="en" data-theme="light">
1421
+ <head>
1422
+ <meta charset="UTF-8">
1423
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1424
+ <title>${escapeHtml(snapshot.title)}</title>
1425
+ <style>${HTML_EXPORT_STYLES.replace(/<\/style/giu, '<\\/style')}</style>
1426
+ </head>
1427
+ <body data-theme="light">
1428
+ <main class="export-page-shell">
1429
+ <article class="preview-content export-content">${template.innerHTML}</article>
1430
+ </main>
1431
+ </body>
1432
+ </html>`;
1433
+ }
1434
+
1435
+ export function htmlAdapter(snapshot) {
1436
+ downloadBlob(
1437
+ new Blob([buildHtmlDocument(snapshot)], { type: 'text/html;charset=utf-8' }),
1438
+ `${createDocumentTitle(snapshot.filePath, snapshot.title)}.html`,
1439
+ { removeDelayMs: 0, revokeDelayMs: 1000 },
1440
+ );
1441
+ }
1442
+
1390
1443
  export async function docxAdapter(snapshot) {
1391
1444
  const response = await fetch(resolveApiUrl('/export/docx'), {
1392
1445
  body: JSON.stringify({
@@ -1461,6 +1514,11 @@ export async function runExportAdapter(snapshot, format) {
1461
1514
  return;
1462
1515
  }
1463
1516
 
1517
+ if (format === 'html') {
1518
+ await htmlAdapter(snapshot);
1519
+ return;
1520
+ }
1521
+
1464
1522
  await docxAdapter(snapshot);
1465
1523
  }
1466
1524
 
@@ -1478,7 +1536,8 @@ export function postExportPageMessage(type, payload = {}) {
1478
1536
 
1479
1537
  export async function waitForBootstrapPayload() {
1480
1538
  const requestUrl = new URL(window.location.href);
1481
- const action = requestUrl.searchParams.get('action') === 'pdf' ? 'pdf' : 'docx';
1539
+ const requestedAction = requestUrl.searchParams.get('action');
1540
+ const action = ['html', 'pdf'].includes(requestedAction) ? requestedAction : 'docx';
1482
1541
  const filePath = requestUrl.searchParams.get('file') || '';
1483
1542
  const jobId = requestUrl.searchParams.get('job') || '';
1484
1543
 
@@ -22,6 +22,7 @@ export class BrowserPreferencesPort {
22
22
  fileTreeShowExtensionsKey,
23
23
  lineWrappingKey,
24
24
  recentFilesKey = 'collabmd-recent-files',
25
+ vimModeKey = 'collabmd-editor-vim-mode',
25
26
  sidebarVisibleKey,
26
27
  userNameKey,
27
28
  storage = globalThis.localStorage,
@@ -32,6 +33,7 @@ export class BrowserPreferencesPort {
32
33
  this.sidebarVisibleKey = sidebarVisibleKey;
33
34
  this.storage = storage;
34
35
  this.userNameKey = userNameKey;
36
+ this.vimModeKey = vimModeKey;
35
37
  }
36
38
 
37
39
  getUserName() {
@@ -58,6 +60,14 @@ export class BrowserPreferencesPort {
58
60
  writeStorage(this.storage, this.lineWrappingKey, String(enabled));
59
61
  }
60
62
 
63
+ getVimModeEnabled() {
64
+ return readStorage(this.storage, this.vimModeKey, null) === 'true';
65
+ }
66
+
67
+ setVimModeEnabled(enabled) {
68
+ writeStorage(this.storage, this.vimModeKey, String(enabled));
69
+ }
70
+
61
71
  getRecentFiles() {
62
72
  const stored = readStorage(this.storage, this.recentFilesKey, '[]');
63
73
  try {
@@ -83,15 +83,21 @@ function normalizeSelectionAnchorPayload(payload, state) {
83
83
 
84
84
  export class CommentThreadStore {
85
85
  constructor({
86
+ canWrite = () => true,
87
+ createAnchor = null,
86
88
  getDoc,
87
89
  getEditorState,
88
90
  getLocalUser,
89
91
  onCommentsChange = null,
92
+ resolveThread = null,
90
93
  }) {
94
+ this.canWrite = canWrite;
95
+ this.createAnchor = createAnchor;
91
96
  this.getDoc = getDoc;
92
97
  this.getEditorState = getEditorState;
93
98
  this.getLocalUser = getLocalUser;
94
99
  this.onCommentsChange = onCommentsChange;
100
+ this.resolveThread = resolveThread;
95
101
  this.commentThreads = null;
96
102
  this.ydoc = null;
97
103
  this.ytext = null;
@@ -144,33 +150,44 @@ export class CommentThreadStore {
144
150
  }
145
151
 
146
152
  return serializeCommentThreads(this.commentThreads)
147
- .map((thread) => this.resolveCommentThread(thread))
153
+ .map((thread) => this.resolveThread
154
+ ? this.resolveThread(thread)
155
+ : this.resolveCommentThread(thread))
148
156
  .filter(Boolean);
149
157
  }
150
158
 
151
159
  createCommentThread({ anchor, body }) {
152
- const state = this.getEditorState();
153
- if (!state || !this.commentThreads || !this.ytext || !this.ydoc) {
160
+ if (!this.canWrite() || !this.commentThreads || !this.ydoc) {
154
161
  return null;
155
162
  }
156
163
 
164
+ const state = this.getEditorState?.();
157
165
  const normalizedBody = normalizeCommentBody(body);
158
- const normalizedAnchor = normalizeSelectionAnchorPayload(anchor, state);
166
+ if (!this.createAnchor && (!state || !this.ytext)) {
167
+ return null;
168
+ }
169
+
170
+ const normalizedAnchor = this.createAnchor
171
+ ? this.createAnchor(anchor)
172
+ : normalizeSelectionAnchorPayload(anchor, state);
159
173
  if (!normalizedBody || !normalizedAnchor) {
160
174
  return null;
161
175
  }
162
176
 
163
177
  const thread = createCommentThreadSharedType({
164
- anchorEnd: Y.relativePositionToJSON(
165
- Y.createRelativePositionFromTypeIndex(this.ytext, normalizedAnchor.endIndex),
166
- ),
167
- anchorEndLine: normalizedAnchor.anchorEndLine,
178
+ ...normalizedAnchor,
179
+ ...(this.createAnchor ? {} : {
180
+ anchorEnd: Y.relativePositionToJSON(
181
+ Y.createRelativePositionFromTypeIndex(this.ytext, normalizedAnchor.endIndex),
182
+ ),
183
+ }),
168
184
  anchorKind: normalizedAnchor.anchorKind,
169
185
  anchorQuote: normalizedAnchor.anchorQuote,
170
- anchorStart: Y.relativePositionToJSON(
171
- Y.createRelativePositionFromTypeIndex(this.ytext, normalizedAnchor.startIndex),
172
- ),
173
- anchorStartLine: normalizedAnchor.anchorStartLine,
186
+ ...(this.createAnchor ? {} : {
187
+ anchorStart: Y.relativePositionToJSON(
188
+ Y.createRelativePositionFromTypeIndex(this.ytext, normalizedAnchor.startIndex),
189
+ ),
190
+ }),
174
191
  createdAt: Date.now(),
175
192
  createdByColor: this.getLocalUser()?.color ?? '',
176
193
  createdByName: this.getLocalUser()?.name ?? 'Anonymous',
@@ -194,6 +211,10 @@ export class CommentThreadStore {
194
211
  }
195
212
 
196
213
  replyToCommentThread(threadId, body) {
214
+ if (!this.canWrite()) {
215
+ return null;
216
+ }
217
+
197
218
  const normalizedBody = normalizeCommentBody(body);
198
219
  if (!normalizedBody || !this.ydoc) {
199
220
  return null;
@@ -291,7 +312,7 @@ export class CommentThreadStore {
291
312
  }
292
313
 
293
314
  deleteCommentThread(threadId) {
294
- if (!this.commentThreads || !this.ydoc) {
315
+ if (!this.canWrite() || !this.commentThreads || !this.ydoc) {
295
316
  return false;
296
317
  }
297
318
 
@@ -8,6 +8,7 @@ export class EditorSession {
8
8
  constructor({
9
9
  editorContainer,
10
10
  lineWrappingEnabled = true,
11
+ vimModeEnabled = false,
11
12
  initialTheme,
12
13
  lineInfoElement,
13
14
  onAwarenessChange,
@@ -44,6 +45,7 @@ export class EditorSession {
44
45
  initialTheme,
45
46
  lineInfoElement,
46
47
  lineWrappingEnabled,
48
+ vimModeEnabled,
47
49
  onDocChanged: () => {
48
50
  this.emitContentChange();
49
51
  },
@@ -210,6 +212,14 @@ export class EditorSession {
210
212
  return this.viewAdapter.isLineWrappingEnabled();
211
213
  }
212
214
 
215
+ isVimModeEnabled() {
216
+ return this.viewAdapter.isVimModeEnabled();
217
+ }
218
+
219
+ setVimMode(enabled) {
220
+ return this.viewAdapter.setVimMode(enabled);
221
+ }
222
+
213
223
  setLineWrapping(enabled) {
214
224
  return this.viewAdapter.setLineWrapping(enabled);
215
225
  }
@@ -1,3 +1,4 @@
1
+ import { vim } from '@replit/codemirror-vim';
1
2
  import { autocompletion, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
2
3
  import {
3
4
  defaultKeymap,
@@ -45,15 +46,26 @@ import {
45
46
  } from '@codemirror/view';
46
47
  import { yCollab } from 'y-codemirror.next';
47
48
 
48
- import { isBaseFilePath, isMermaidFilePath, isPlantUmlFilePath } from '../../domain/file-kind.js';
49
+ import {
50
+ isBaseFilePath,
51
+ isMermaidFilePath,
52
+ isPlantUmlFilePath,
53
+ isStructurizrFilePath,
54
+ } from '../../domain/file-kind.js';
49
55
  import { normalizeCommentQuote } from '../../domain/comment-threads.js';
50
56
  import { createMarkdownToolbarEdit } from '../domain/markdown-formatting.js';
51
57
  import { mermaidLanguage, mermaidLanguageDescription } from '../domain/mermaid-language.js';
52
58
  import { wikiLinkCompletions } from '../domain/wiki-link-completions.js';
53
59
  import { plantUmlLanguage, plantUmlLanguageDescription } from '../domain/plantuml-language.js';
60
+ import { structurizrLanguage, structurizrLanguageDescription } from '../domain/structurizr-language.js';
54
61
  import { handleImagePasteEvent } from './editor-paste-utils.js';
55
62
 
56
- const markdownCodeLanguages = [...languages, mermaidLanguageDescription, plantUmlLanguageDescription];
63
+ const markdownCodeLanguages = [
64
+ ...languages,
65
+ mermaidLanguageDescription,
66
+ plantUmlLanguageDescription,
67
+ structurizrLanguageDescription,
68
+ ];
57
69
  const pairedMatchingBracketMark = Decoration.mark({ class: 'cm-matchingBracket cm-matchingBracket-paired' });
58
70
  const nonmatchingBracketMark = Decoration.mark({ class: 'cm-nonmatchingBracket' });
59
71
  const remoteUpdateMark = Decoration.mark({ class: 'cm-remoteUpdateFlash' });
@@ -387,6 +399,10 @@ export function createLanguageExtension(filePath) {
387
399
  return plantUmlLanguage;
388
400
  }
389
401
 
402
+ if (isStructurizrFilePath(filePath)) {
403
+ return structurizrLanguage;
404
+ }
405
+
390
406
  if (isBaseFilePath(filePath)) {
391
407
  return yaml();
392
408
  }
@@ -408,6 +424,7 @@ export class EditorViewAdapter {
408
424
  initialTheme,
409
425
  lineInfoElement,
410
426
  lineWrappingEnabled = true,
427
+ vimModeEnabled = false,
411
428
  onDocChanged = null,
412
429
  onImagePaste = null,
413
430
  onSelectionChanged = null,
@@ -418,6 +435,7 @@ export class EditorViewAdapter {
418
435
  this.initialTheme = initialTheme;
419
436
  this.lineInfoElement = lineInfoElement;
420
437
  this.lineWrappingEnabled = lineWrappingEnabled;
438
+ this.vimModeEnabled = Boolean(vimModeEnabled);
421
439
  this.onDocChanged = onDocChanged;
422
440
  this.onImagePaste = onImagePaste;
423
441
  this.onSelectionChanged = onSelectionChanged;
@@ -426,6 +444,7 @@ export class EditorViewAdapter {
426
444
  this.themeCompartment = new Compartment();
427
445
  this.syntaxThemeCompartment = new Compartment();
428
446
  this.lineWrappingCompartment = new Compartment();
447
+ this.vimModeCompartment = new Compartment();
429
448
  this.viewportFrame = 0;
430
449
  this.remoteUpdateFlashTimer = 0;
431
450
  this.lastLocalInputAt = 0;
@@ -478,6 +497,7 @@ export class EditorViewAdapter {
478
497
  highlightActiveLine(),
479
498
  highlightSelectionMatches(),
480
499
  createSearchNavigationListener(),
500
+ this.vimModeCompartment.of(!readOnly && this.vimModeEnabled ? vim({ status: true }) : []),
481
501
  keymap.of([
482
502
  ...closeBracketsKeymap,
483
503
  ...defaultKeymap,
@@ -645,6 +665,30 @@ export class EditorViewAdapter {
645
665
  return this.lineWrappingEnabled;
646
666
  }
647
667
 
668
+ isVimModeEnabled() {
669
+ return this.vimModeEnabled;
670
+ }
671
+
672
+ setVimMode(enabled) {
673
+ const nextEnabled = Boolean(enabled);
674
+ if (nextEnabled === this.vimModeEnabled) {
675
+ return nextEnabled;
676
+ }
677
+
678
+ this.vimModeEnabled = nextEnabled;
679
+ if (!this.editorView || this.editorView.state.readOnly) {
680
+ return nextEnabled;
681
+ }
682
+
683
+ this.editorView.dispatch({
684
+ effects: this.vimModeCompartment.reconfigure(
685
+ nextEnabled ? vim({ status: true }) : [],
686
+ ),
687
+ });
688
+ this.editorView.focus();
689
+ return nextEnabled;
690
+ }
691
+
648
692
  setLineWrapping(enabled) {
649
693
  this.lineWrappingEnabled = Boolean(enabled);
650
694
 
@@ -24,6 +24,10 @@ import {
24
24
  tryParseSceneJson,
25
25
  } from '../domain/excalidraw-scene.js';
26
26
  import { resolveWsBaseUrl } from '../domain/runtime-paths.js';
27
+ import {
28
+ normalizeCommentQuote,
29
+ } from '../../domain/comment-threads.js';
30
+ import { CommentThreadStore } from './comment-thread-store.js';
27
31
  import { stopReconnectOnControlledClose } from './yjs-provider-reset-guard.js';
28
32
 
29
33
  const DEFAULT_EMPTY_SCENE_GUARD_MS = 250;
@@ -39,16 +43,21 @@ export const EXCALIDRAW_ROOM_CONNECTION_STATE = Object.freeze({
39
43
  RECONNECTING_READONLY: 'reconnecting-readonly',
40
44
  });
41
45
 
42
- function stableJson(value) {
43
- if (Array.isArray(value)) {
44
- return `[${value.map((entry) => stableJson(entry)).join(',')}]`;
46
+ function areExcalidrawFilesEqual(left, right) {
47
+ if (left === right) {
48
+ return true;
45
49
  }
46
50
 
47
- if (value && typeof value === 'object') {
48
- return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;
51
+ if (!left || !right || typeof left !== 'object' || typeof right !== 'object') {
52
+ return false;
49
53
  }
50
54
 
51
- return JSON.stringify(value);
55
+ return left.id === right.id
56
+ && left.mimeType === right.mimeType
57
+ && left.dataURL === right.dataURL
58
+ && left.created === right.created
59
+ && left.lastRetrieved === right.lastRetrieved
60
+ && left.version === right.version;
52
61
  }
53
62
 
54
63
  function normalizeElementVersion(value) {
@@ -81,6 +90,35 @@ function shouldSyncElementToRoom(localElement, roomElement) {
81
90
  return localVersionNonce < roomVersionNonce;
82
91
  }
83
92
 
93
+ function createDiagramCommentAnchor(element) {
94
+ const elementId = typeof element?.id === 'string' ? element.id.trim() : '';
95
+ const x = Number(element?.x);
96
+ const y = Number(element?.y);
97
+ const width = Number(element?.width);
98
+ const height = Number(element?.height);
99
+ if (!elementId || ![x, y, width, height].every(Number.isFinite)) {
100
+ return null;
101
+ }
102
+
103
+ return {
104
+ anchorKind: 'diagram-element',
105
+ anchorPoint: {
106
+ x: x + (width / 2),
107
+ y: y + (height / 2),
108
+ },
109
+ anchorQuote: normalizeCommentQuote(element.text),
110
+ anchorSnapshot: {
111
+ height,
112
+ text: normalizeCommentQuote(element.text),
113
+ type: typeof element.type === 'string' ? element.type : 'element',
114
+ width,
115
+ x,
116
+ y,
117
+ },
118
+ elementId,
119
+ };
120
+ }
121
+
84
122
  export class ExcalidrawRoomClient {
85
123
  constructor({
86
124
  cancelAnimationFrameFn = (frameId) => cancelAnimationFrame(frameId),
@@ -89,6 +127,7 @@ export class ExcalidrawRoomClient {
89
127
  filePath = '',
90
128
  now = () => Date.now(),
91
129
  onCollaboratorsChange = () => {},
130
+ onCommentThreadsChange = () => {},
92
131
  onConnectionStateChange = () => {},
93
132
  onRemoteSceneJson = () => {},
94
133
  requestAnimationFrameFn = (callback) => requestAnimationFrame(callback),
@@ -106,6 +145,7 @@ export class ExcalidrawRoomClient {
106
145
  this.filePath = filePath;
107
146
  this.now = now;
108
147
  this.onCollaboratorsChange = onCollaboratorsChange;
148
+ this.onCommentThreadsChange = onCommentThreadsChange;
109
149
  this.onConnectionStateChange = onConnectionStateChange;
110
150
  this.onRemoteSceneJson = onRemoteSceneJson;
111
151
  this.requestAnimationFrameFn = requestAnimationFrameFn;
@@ -138,12 +178,22 @@ export class ExcalidrawRoomClient {
138
178
  this.lastViewportSignature = '';
139
179
  this.lastSelectedIdsSignature = '';
140
180
  this.pendingEmptySceneCandidate = null;
181
+ this.structuredObserversAttached = false;
141
182
  this.canWriteToRoom = false;
142
183
  this.waitingForAuthoritativeSync = false;
143
184
  this.connectionState = EXCALIDRAW_ROOM_CONNECTION_STATE.CLOSED;
144
185
  this.hasAuthoritativeSync = false;
145
186
  this.applyingSharedSnapshotDepth = 0;
146
187
  this.suppressStructuredSceneUpdateDepth = 0;
188
+ this.commentThreadStore = new CommentThreadStore({
189
+ canWrite: () => this.canWriteToRoom,
190
+ createAnchor: createDiagramCommentAnchor,
191
+ getDoc: () => this.ydoc,
192
+ getEditorState: () => null,
193
+ getLocalUser: () => this.getLocalUser(),
194
+ onCommentsChange: (threads) => this.onCommentThreadsChange(threads),
195
+ resolveThread: (thread) => thread,
196
+ });
147
197
 
148
198
  this.handleAwarenessChange = () => {
149
199
  this.onCollaboratorsChange(buildCollaboratorsMap(this.awareness));
@@ -177,6 +227,25 @@ export class ExcalidrawRoomClient {
177
227
  return this.localUser;
178
228
  }
179
229
 
230
+ getCommentThreads() {
231
+ return this.commentThreadStore.getCommentThreads();
232
+ }
233
+
234
+ createCommentThread({ body, element } = {}) {
235
+ return this.commentThreadStore.createCommentThread({
236
+ anchor: element,
237
+ body,
238
+ });
239
+ }
240
+
241
+ replyToCommentThread(threadId, body) {
242
+ return this.commentThreadStore.replyToCommentThread(threadId, body);
243
+ }
244
+
245
+ deleteCommentThread(threadId) {
246
+ return this.commentThreadStore.deleteCommentThread(threadId);
247
+ }
248
+
180
249
  getConnectionState() {
181
250
  return this.connectionState;
182
251
  }
@@ -268,7 +337,16 @@ export class ExcalidrawRoomClient {
268
337
  return this.localUser;
269
338
  }
270
339
 
271
- async connect({ initialUser = null } = {}) {
340
+ async connect(options = {}) {
341
+ try {
342
+ return await this.connectInternal(options);
343
+ } catch (error) {
344
+ this.disconnect();
345
+ throw error;
346
+ }
347
+ }
348
+
349
+ async connectInternal({ initialUser = null } = {}) {
272
350
  this.localUser = initialUser;
273
351
  this.setConnectionState(EXCALIDRAW_ROOM_CONNECTION_STATE.CONNECTING);
274
352
 
@@ -277,6 +355,7 @@ export class ExcalidrawRoomClient {
277
355
  this.lastSceneJson = JSON.stringify(scene);
278
356
  this.hasAuthoritativeSync = true;
279
357
  this.setConnectionState(EXCALIDRAW_ROOM_CONNECTION_STATE.AUTHORITATIVE);
358
+ this.onCommentThreadsChange([]);
280
359
  return scene;
281
360
  }
282
361
 
@@ -367,6 +446,11 @@ export class ExcalidrawRoomClient {
367
446
  this.roomElements.observeDeep(this.handleStructuredSceneUpdate);
368
447
  this.roomFiles.observeDeep(this.handleStructuredSceneUpdate);
369
448
  this.roomAppState.observe(this.handleStructuredSceneUpdate);
449
+ this.structuredObserversAttached = true;
450
+ this.commentThreadStore.bind({
451
+ commentThreads: this.ydoc.getArray('comments'),
452
+ ydoc: this.ydoc,
453
+ });
370
454
  if (usedApiFallback && this.canWriteToRoom) {
371
455
  this.commitSceneJson(this.lastSceneJson, {
372
456
  origin: 'excalidraw-api-fallback',
@@ -501,7 +585,7 @@ export class ExcalidrawRoomClient {
501
585
 
502
586
  mergeLocallyChangedAppState(appState, baseSceneJson) {
503
587
  const baseScene = parseSceneJson(baseSceneJson);
504
- const roomScene = parseSceneJson(this.getStructuredSceneJson() || this.lastSceneJson);
588
+ const roomScene = parseSceneJson(this.lastSceneJson || this.getStructuredSceneJson());
505
589
  const nextAppState = {
506
590
  ...roomScene.appState,
507
591
  };
@@ -523,7 +607,7 @@ export class ExcalidrawRoomClient {
523
607
  elements,
524
608
  files,
525
609
  } = {}) {
526
- const roomSceneJson = this.getStructuredSceneJson() || this.lastSceneJson || baseSceneJson;
610
+ const roomSceneJson = this.lastSceneJson || this.getStructuredSceneJson() || baseSceneJson;
527
611
  const roomScene = parseSceneJson(roomSceneJson);
528
612
  const nextAppState = this.mergeLocallyChangedAppState(appState, baseSceneJson);
529
613
  const roomElementsById = new Map(roomScene.elements.map((element) => [element.id, element]));
@@ -533,7 +617,7 @@ export class ExcalidrawRoomClient {
533
617
  const nextFiles = files && typeof files === 'object' ? files : {};
534
618
  const changedFiles = Object.fromEntries(
535
619
  Object.entries(nextFiles).filter(([fileId, file]) => (
536
- stableJson(file) !== stableJson(roomScene.files?.[fileId])
620
+ !areExcalidrawFilesEqual(file, roomScene.files?.[fileId])
537
621
  )),
538
622
  );
539
623
  const appStateChanged = (
@@ -869,10 +953,15 @@ export class ExcalidrawRoomClient {
869
953
  this.lastSelectedIdsSignature = '';
870
954
  this.pendingEmptySceneCandidate = null;
871
955
 
872
- this.roomMeta?.unobserve(this.handleStructuredSceneUpdate);
873
- this.roomElements?.unobserveDeep(this.handleStructuredSceneUpdate);
874
- this.roomFiles?.unobserveDeep(this.handleStructuredSceneUpdate);
875
- this.roomAppState?.unobserve(this.handleStructuredSceneUpdate);
956
+ if (this.structuredObserversAttached) {
957
+ this.roomMeta?.unobserve(this.handleStructuredSceneUpdate);
958
+ this.roomElements?.unobserveDeep(this.handleStructuredSceneUpdate);
959
+ this.roomFiles?.unobserveDeep(this.handleStructuredSceneUpdate);
960
+ this.roomAppState?.unobserve(this.handleStructuredSceneUpdate);
961
+ this.structuredObserversAttached = false;
962
+ }
963
+
964
+ this.commentThreadStore.unbind();
876
965
 
877
966
  if (this.awareness) {
878
967
  this.awareness.off('change', this.handleAwarenessChange);
@@ -74,6 +74,11 @@ export class GitApiClient {
74
74
  return parseApiResponse(response, 'Failed to load historical file preview');
75
75
  }
76
76
 
77
+ getFileAttachmentUrl({ hash, path } = {}) {
78
+ const params = createSearchParams({ hash, path });
79
+ return resolveApiUrl(`/git/file-attachment?${params.toString()}`);
80
+ }
81
+
77
82
  async stageFile({ path, requestId = null } = {}) {
78
83
  return this.#postJson('/git/stage', { path }, { requestId, fallbackError: 'Failed to stage file' });
79
84
  }
@@ -51,6 +51,20 @@ export class VaultApiClient {
51
51
  return Array.isArray(data.backlinks) ? data.backlinks : [];
52
52
  }
53
53
 
54
+ async syncStructurizrWorkspace({ path, source }) {
55
+ const response = await fetch(resolveApiUrl('/structurizr/sync'), {
56
+ body: JSON.stringify({
57
+ path,
58
+ source,
59
+ }),
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ method: 'POST',
64
+ });
65
+ return parseApiResponse(response, 'Failed to sync Structurizr workspace');
66
+ }
67
+
54
68
  async renderSvg(source) {
55
69
  const response = await fetch(resolveApiUrl('/plantuml/render'), {
56
70
  body: JSON.stringify({ source }),
@@ -261,6 +275,18 @@ export class VaultApiClient {
261
275
  return parseApiResponse(response, 'Failed to upload image');
262
276
  }
263
277
 
278
+ async uploadFile({ file, path, requestId = null }) {
279
+ const response = await fetch(resolveApiUrl('/file/upload'), {
280
+ body: file,
281
+ headers: createRequestHeaders(requestId, {
282
+ 'Content-Type': file?.type || 'application/octet-stream',
283
+ 'X-CollabMD-File-Path': encodeHeaderMetadata(path),
284
+ }),
285
+ method: 'POST',
286
+ });
287
+ return parseApiResponse(response, 'Failed to upload file');
288
+ }
289
+
264
290
  async downloadFile(path) {
265
291
  const fallbackFileName = getVaultPathLeaf(path) || 'download';
266
292
  return triggerDownload(resolveApiUrl(`/download/file?path=${encodeURIComponent(path)}`), {
@@ -17,6 +17,7 @@ import {
17
17
  * @property {HTMLElement | null} commentsDrawerList
18
18
  * @property {HTMLElement | null} commentsDrawerEmpty
19
19
  * @property {HTMLElement | null} cardRoot
20
+ * @property {{ matches: boolean }} mobileBreakpointQuery
20
21
  * @property {HTMLElement | null} pendingCardFocusElement
21
22
  * @property {any} reactionPicker
22
23
  * @property {() => Array<any>} getThreadGroups
@@ -103,6 +104,9 @@ function renderDrawer() {
103
104
  });
104
105
  button.addEventListener('click', () => {
105
106
  this.onNavigateToLine?.(group.anchor?.startLine ?? 1);
107
+ if (this.mobileBreakpointQuery?.matches) {
108
+ this.setDrawerOpen(false);
109
+ }
106
110
  this.openThreadGroup(group, {
107
111
  anchor: group.anchor,
108
112
  origin: 'editor',