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
@@ -51,6 +51,10 @@ export function areAnchorsEqual(left, right) {
51
51
  }
52
52
 
53
53
  export function formatAnchorLabel(anchor) {
54
+ if (getAnchorKind(anchor) === 'diagram-element') {
55
+ return 'Diagram element';
56
+ }
57
+
54
58
  const startLine = Number(anchor?.startLine || 0);
55
59
  const endLine = Number(anchor?.endLine || startLine);
56
60
  if (!Number.isFinite(startLine) || startLine <= 0) {
@@ -12,6 +12,7 @@ export class CommentUiController {
12
12
  commentsDrawerList,
13
13
  commentsToggleButton,
14
14
  editorContainer,
15
+ mobileBreakpointQuery = window.matchMedia('(max-width: 768px)'),
15
16
  onWillOpenDrawer,
16
17
  onCreateThread,
17
18
  onNavigateToLine,
@@ -27,6 +28,7 @@ export class CommentUiController {
27
28
  this.commentsDrawerList = commentsDrawerList;
28
29
  this.commentsToggleButton = commentsToggleButton;
29
30
  this.editorContainer = editorContainer;
31
+ this.mobileBreakpointQuery = mobileBreakpointQuery;
30
32
  this.onWillOpenDrawer = onWillOpenDrawer;
31
33
  this.onCreateThread = onCreateThread;
32
34
  this.onNavigateToLine = onNavigateToLine;
@@ -1,6 +1,7 @@
1
1
  import { createDrawioLeaseRoomName } from '../../domain/drawio-room.js';
2
2
  import { setDiagramActionButtonIcon } from '../domain/diagram-action-icons.js';
3
3
  import { resolveAppUrl } from '../domain/runtime-paths.js';
4
+ import { renderDrawioViewer } from './drawio-viewer.js';
4
5
  import {
5
6
  cancelIdleRender,
6
7
  isNearViewport,
@@ -8,62 +9,6 @@ import {
8
9
  } from '../browser-utils.js';
9
10
 
10
11
  const HYDRATE_VIEWPORT_MARGIN_PX = 360;
11
- const DRAWIO_VIEWER_SCRIPT_URL = 'https://viewer.diagrams.net/js/viewer-static.min.js';
12
-
13
- let drawioViewerLoadPromise = null;
14
-
15
- function ensureDrawioViewerLoaded() {
16
- if (window.GraphViewer?.processElements) {
17
- return Promise.resolve(window.GraphViewer);
18
- }
19
-
20
- if (drawioViewerLoadPromise) {
21
- return drawioViewerLoadPromise;
22
- }
23
-
24
- drawioViewerLoadPromise = new Promise((resolve, reject) => {
25
- const existingScript = document.querySelector('script[data-collabmd-drawio-viewer]');
26
- const script = existingScript instanceof HTMLScriptElement
27
- ? existingScript
28
- : document.createElement('script');
29
-
30
- const cleanup = () => {
31
- script.removeEventListener('error', handleError);
32
- script.removeEventListener('load', handleLoad);
33
- };
34
-
35
- const handleError = () => {
36
- cleanup();
37
- drawioViewerLoadPromise = null;
38
- reject(new Error('Failed to load draw.io viewer'));
39
- };
40
-
41
- const handleLoad = () => {
42
- cleanup();
43
- if (!window.GraphViewer?.processElements) {
44
- drawioViewerLoadPromise = null;
45
- reject(new Error('draw.io viewer did not initialize'));
46
- return;
47
- }
48
-
49
- resolve(window.GraphViewer);
50
- };
51
-
52
- script.addEventListener('error', handleError, { once: true });
53
- script.addEventListener('load', handleLoad, { once: true });
54
-
55
- if (!existingScript) {
56
- script.src = DRAWIO_VIEWER_SCRIPT_URL;
57
- script.async = true;
58
- script.dataset.collabmdDrawioViewer = 'true';
59
- document.head.append(script);
60
- } else if (window.GraphViewer?.processElements) {
61
- handleLoad();
62
- }
63
- });
64
-
65
- return drawioViewerLoadPromise;
66
- }
67
12
 
68
13
  export class DrawioEmbedController {
69
14
  constructor({
@@ -350,59 +295,21 @@ export class DrawioEmbedController {
350
295
  }
351
296
 
352
297
  async renderViewerEntry(entry) {
353
- const [{ content }, viewer] = await Promise.all([
354
- this.vaultApiClient.readFile(entry.filePath),
355
- ensureDrawioViewerLoaded(),
356
- ]);
298
+ const { content } = await this.vaultApiClient.readFile(entry.filePath);
357
299
 
358
300
  if (!entry.wrapper?.isConnected || !entry.viewerElement?.isConnected) {
359
301
  return;
360
302
  }
361
303
 
362
304
  const theme = this.getTheme?.() === 'light' ? 'light' : 'dark';
363
- const graphElement = document.createElement('div');
364
- graphElement.className = 'mxgraph drawio-viewer-frame';
365
- graphElement.dataset.action = 'open-file';
366
- graphElement.dataset.filePath = entry.filePath;
367
- graphElement.setAttribute('role', 'button');
368
- graphElement.setAttribute('tabindex', '0');
369
- graphElement.setAttribute('aria-label', `Open ${entry.label.replace(/\.drawio$/i, '')}`);
370
- graphElement.addEventListener('click', (event) => {
371
- event.preventDefault();
372
- this.onOpenFile?.(entry.filePath);
373
- });
374
- graphElement.addEventListener('keydown', (event) => {
375
- if (event.key !== 'Enter' && event.key !== ' ') {
376
- return;
377
- }
378
-
379
- event.preventDefault();
380
- this.onOpenFile?.(entry.filePath);
381
- });
382
- graphElement.dataset.mxgraph = JSON.stringify({
383
- 'check-visible-state': false,
384
- center: true,
385
- border: 0,
386
- 'dark-mode': theme,
387
- editable: false,
388
- fit: 1,
389
- lightbox: false,
390
- nav: false,
391
- resize: false,
392
- tooltips: false,
393
- xml: String(content ?? ''),
394
- });
395
-
396
305
  const viewerHost = entry.wrapper.querySelector('.drawio-viewer-shell') ?? entry.viewerElement;
397
- viewerHost.replaceChildren(graphElement);
398
- entry.viewerElement = graphElement;
399
-
400
- if (typeof viewer.createViewerForElement === 'function') {
401
- viewer.createViewerForElement(graphElement);
402
- return;
403
- }
404
-
405
- viewer.processElements();
306
+ entry.viewerElement = await renderDrawioViewer(viewerHost, {
307
+ ariaLabel: `Open ${entry.label.replace(/\.drawio$/i, '')}`,
308
+ className: 'mxgraph drawio-viewer-frame',
309
+ onActivate: () => this.onOpenFile?.(entry.filePath),
310
+ source: String(content ?? ''),
311
+ theme,
312
+ });
406
313
  }
407
314
 
408
315
  buildIframeUrl(entry) {
@@ -0,0 +1,118 @@
1
+ const DRAWIO_VIEWER_SCRIPT_URL = 'https://viewer.diagrams.net/js/viewer-static.min.js';
2
+
3
+ let drawioViewerLoadPromise = null;
4
+
5
+ export function ensureDrawioViewerLoaded() {
6
+ if (window.GraphViewer?.processElements) {
7
+ return Promise.resolve(window.GraphViewer);
8
+ }
9
+
10
+ if (drawioViewerLoadPromise) {
11
+ return drawioViewerLoadPromise;
12
+ }
13
+
14
+ drawioViewerLoadPromise = new Promise((resolve, reject) => {
15
+ const existingScript = document.querySelector('script[data-collabmd-drawio-viewer]');
16
+ const script = existingScript instanceof HTMLScriptElement
17
+ ? existingScript
18
+ : document.createElement('script');
19
+
20
+ const cleanup = () => {
21
+ script.removeEventListener('error', handleError);
22
+ script.removeEventListener('load', handleLoad);
23
+ };
24
+
25
+ const handleError = () => {
26
+ cleanup();
27
+ drawioViewerLoadPromise = null;
28
+ reject(new Error('Failed to load draw.io viewer'));
29
+ };
30
+
31
+ const handleLoad = () => {
32
+ cleanup();
33
+ if (!window.GraphViewer?.processElements) {
34
+ drawioViewerLoadPromise = null;
35
+ reject(new Error('draw.io viewer did not initialize'));
36
+ return;
37
+ }
38
+
39
+ resolve(window.GraphViewer);
40
+ };
41
+
42
+ script.addEventListener('error', handleError, { once: true });
43
+ script.addEventListener('load', handleLoad, { once: true });
44
+
45
+ if (!existingScript) {
46
+ script.src = DRAWIO_VIEWER_SCRIPT_URL;
47
+ script.async = true;
48
+ script.dataset.collabmdDrawioViewer = 'true';
49
+ document.head.append(script);
50
+ } else if (window.GraphViewer?.processElements) {
51
+ handleLoad();
52
+ }
53
+ });
54
+
55
+ return drawioViewerLoadPromise;
56
+ }
57
+
58
+ export function createDrawioViewerElement({
59
+ ariaLabel = '',
60
+ className = 'drawio-viewer-frame',
61
+ onActivate = null,
62
+ source = '',
63
+ theme = 'dark',
64
+ } = {}) {
65
+ const graphElement = document.createElement('div');
66
+ graphElement.className = className;
67
+ if (ariaLabel) {
68
+ graphElement.setAttribute('aria-label', ariaLabel);
69
+ }
70
+
71
+ if (typeof onActivate === 'function') {
72
+ graphElement.dataset.action = 'open-file';
73
+ graphElement.setAttribute('role', 'button');
74
+ graphElement.setAttribute('tabindex', '0');
75
+ graphElement.addEventListener('click', (event) => {
76
+ event.preventDefault();
77
+ onActivate();
78
+ });
79
+ graphElement.addEventListener('keydown', (event) => {
80
+ if (event.key !== 'Enter' && event.key !== ' ') {
81
+ return;
82
+ }
83
+
84
+ event.preventDefault();
85
+ onActivate();
86
+ });
87
+ }
88
+
89
+ graphElement.dataset.mxgraph = JSON.stringify({
90
+ 'check-visible-state': false,
91
+ center: true,
92
+ border: 0,
93
+ 'dark-mode': theme === 'light' ? 'light' : 'dark',
94
+ editable: false,
95
+ fit: 1,
96
+ lightbox: false,
97
+ nav: false,
98
+ resize: false,
99
+ tooltips: false,
100
+ xml: String(source ?? ''),
101
+ });
102
+
103
+ return graphElement;
104
+ }
105
+
106
+ export async function renderDrawioViewer(host, options = {}) {
107
+ const viewer = await ensureDrawioViewerLoaded();
108
+ const graphElement = createDrawioViewerElement(options);
109
+ host.replaceChildren(graphElement);
110
+
111
+ if (typeof viewer.createViewerForElement === 'function') {
112
+ viewer.createViewerForElement(graphElement);
113
+ } else {
114
+ viewer.processElements();
115
+ }
116
+
117
+ return graphElement;
118
+ }
@@ -1,6 +1,6 @@
1
1
  import { setDiagramActionButtonIcon } from '../domain/diagram-action-icons.js';
2
2
  import { reconcileEmbedEntries } from './excalidraw-embed-reconciler.js';
3
- import { resolveAppUrl } from '../domain/runtime-paths.js';
3
+ import { resolveAppUrl, resolveWsServerOverride } from '../domain/runtime-paths.js';
4
4
  import {
5
5
  cancelIdleRender,
6
6
  isNearViewport,
@@ -15,11 +15,19 @@ const MAX_IFRAME_BOOT_ATTEMPTS = 3;
15
15
  const MIN_HEIGHT = 200;
16
16
  const EDITOR_IFRAME_PATH = '/excalidraw-editor.html';
17
17
 
18
+ function findEmbedPlaceholder(previewElement, key) {
19
+ const placeholders = previewElement?.querySelectorAll?.(
20
+ '.excalidraw-embed-placeholder[data-embed-key]',
21
+ );
22
+ return Array.from(placeholders || []).find((placeholder) => placeholder.dataset?.embedKey === key) || null;
23
+ }
24
+
18
25
  export class ExcalidrawEmbedController {
19
26
  constructor({
20
27
  getTheme,
21
28
  getLocalUser,
22
29
  onOpenFile = null,
30
+ onStopFollowing = null,
23
31
  onToggleQuickSwitcher = null,
24
32
  previewContainer,
25
33
  previewElement,
@@ -28,6 +36,7 @@ export class ExcalidrawEmbedController {
28
36
  this.getTheme = getTheme;
29
37
  this.getLocalUser = getLocalUser;
30
38
  this.onOpenFile = onOpenFile;
39
+ this.onStopFollowing = onStopFollowing;
31
40
  this.onToggleQuickSwitcher = onToggleQuickSwitcher;
32
41
  this.previewContainer = previewContainer;
33
42
  this.previewElement = previewElement;
@@ -70,12 +79,16 @@ export class ExcalidrawEmbedController {
70
79
 
71
80
  this.embedEntries.forEach((entry) => {
72
81
  this._clearEntryBootTimeout(entry);
82
+ entry.resizerCleanup?.();
83
+ entry.resizerCleanup = null;
73
84
  entry.wrapper?.remove();
74
85
  entry.placeholder = null;
75
86
  });
76
87
  this.embedEntries.clear();
77
88
  if (this.warmEntry) {
78
89
  this._clearEntryBootTimeout(this.warmEntry);
90
+ this.warmEntry.resizerCleanup?.();
91
+ this.warmEntry.resizerCleanup = null;
79
92
  this.warmEntry.wrapper?.remove();
80
93
  this.warmEntry = null;
81
94
  }
@@ -219,6 +232,25 @@ export class ExcalidrawEmbedController {
219
232
  });
220
233
  }
221
234
 
235
+ openCommentThread(filePath, threadId) {
236
+ if (!filePath || !threadId) {
237
+ return false;
238
+ }
239
+
240
+ const entry = this._findEntryByFilePath(filePath);
241
+ if (!entry) {
242
+ return false;
243
+ }
244
+
245
+ entry.pendingCommentThreadId = threadId;
246
+ if (entry.isReady) {
247
+ this._postPendingCommentThread(entry);
248
+ } else if (!entry.wrapper) {
249
+ this._enqueueHydration(entry, { prioritize: true });
250
+ }
251
+ return true;
252
+ }
253
+
222
254
  prepareFileDisconnect(filePath, { timeoutMs = 10000 } = {}) {
223
255
  const entry = this._findEntryByFilePath(filePath);
224
256
  if (!entry?.iframe?.contentWindow) {
@@ -394,6 +426,7 @@ export class ExcalidrawEmbedController {
394
426
  entry.maxButton = mount.maxButton;
395
427
  entry.openButton = mount.openButton;
396
428
  entry.instanceId = mount.instanceId;
429
+ entry.resizerCleanup = mount.resizerCleanup;
397
430
  entry.isReady = false;
398
431
  entry.bootFilePath = entry.filePath;
399
432
  entry.bootMode = this._getEntryMode(entry);
@@ -414,7 +447,7 @@ export class ExcalidrawEmbedController {
414
447
  _attachWrapper(entry) {
415
448
  const placeholder = entry.placeholder?.isConnected
416
449
  ? entry.placeholder
417
- : this.previewElement?.querySelector(`.excalidraw-embed-placeholder[data-embed-key="${entry.key}"]`);
450
+ : findEmbedPlaceholder(this.previewElement, entry.key);
418
451
 
419
452
  if (!placeholder) {
420
453
  return;
@@ -476,6 +509,8 @@ export class ExcalidrawEmbedController {
476
509
  }
477
510
 
478
511
  this._clearEntryBootTimeout(entry);
512
+ entry.resizerCleanup?.();
513
+ entry.resizerCleanup = null;
479
514
  if (this.warmEntry === entry) {
480
515
  this.warmEntry = null;
481
516
  }
@@ -634,7 +669,7 @@ export class ExcalidrawEmbedController {
634
669
  }
635
670
 
636
671
  const hostSearchParams = new URLSearchParams(window.location.search);
637
- const serverOverride = hostSearchParams.get('server');
672
+ const serverOverride = resolveWsServerOverride();
638
673
  if (serverOverride) {
639
674
  iframeUrl.searchParams.set('server', serverOverride);
640
675
  }
@@ -702,7 +737,7 @@ export class ExcalidrawEmbedController {
702
737
  const resizer = document.createElement('div');
703
738
  resizer.className = 'excalidraw-embed-resizer';
704
739
  resizer.title = 'Drag to resize';
705
- this._setupResizer(resizer, iframe, () => {
740
+ const resizerCleanup = this._setupResizer(resizer, iframe, () => {
706
741
  this._syncEntryLayout(entry);
707
742
  this._requestPreviewViewportFit(entry);
708
743
  });
@@ -769,6 +804,7 @@ export class ExcalidrawEmbedController {
769
804
  labelElement: label,
770
805
  maxButton: maxBtn,
771
806
  openButton: openBtn,
807
+ resizerCleanup,
772
808
  wrapper,
773
809
  };
774
810
  }
@@ -793,6 +829,7 @@ export class ExcalidrawEmbedController {
793
829
  _setupResizer(resizer, iframe, onResizeEnd = null) {
794
830
  let startY = 0;
795
831
  let startHeight = 0;
832
+ let isDragging = false;
796
833
 
797
834
  const onPointerMove = (event) => {
798
835
  const delta = event.clientY - startY;
@@ -801,20 +838,37 @@ export class ExcalidrawEmbedController {
801
838
  iframe.style.pointerEvents = 'none';
802
839
  };
803
840
 
804
- const onPointerUp = () => {
841
+ const stopDragging = () => {
842
+ if (!isDragging) {
843
+ return;
844
+ }
845
+
846
+ isDragging = false;
805
847
  iframe.style.pointerEvents = '';
806
848
  document.removeEventListener('pointermove', onPointerMove);
807
849
  document.removeEventListener('pointerup', onPointerUp);
850
+ };
851
+
852
+ const onPointerUp = () => {
853
+ stopDragging();
808
854
  onResizeEnd?.();
809
855
  };
810
856
 
811
- resizer.addEventListener('pointerdown', (event) => {
857
+ const onPointerDown = (event) => {
812
858
  event.preventDefault();
859
+ isDragging = true;
813
860
  startY = event.clientY;
814
861
  startHeight = iframe.offsetHeight;
815
862
  document.addEventListener('pointermove', onPointerMove);
816
863
  document.addEventListener('pointerup', onPointerUp);
817
- });
864
+ };
865
+
866
+ resizer.addEventListener('pointerdown', onPointerDown);
867
+
868
+ return () => {
869
+ resizer.removeEventListener?.('pointerdown', onPointerDown);
870
+ stopDragging();
871
+ };
818
872
  }
819
873
 
820
874
  _ensureOverlayRoot() {
@@ -1064,7 +1118,7 @@ export class ExcalidrawEmbedController {
1064
1118
 
1065
1119
  const placeholder = entry.placeholder?.isConnected
1066
1120
  ? entry.placeholder
1067
- : this.previewElement?.querySelector(`.excalidraw-embed-placeholder[data-embed-key="${entry.key}"]`);
1121
+ : findEmbedPlaceholder(this.previewElement, entry.key);
1068
1122
 
1069
1123
  if (!placeholder) {
1070
1124
  return;
@@ -1163,6 +1217,7 @@ export class ExcalidrawEmbedController {
1163
1217
  theme: this.getTheme?.() || 'dark',
1164
1218
  });
1165
1219
  this._syncEntryFollowState(entry);
1220
+ this._postPendingCommentThread?.(entry);
1166
1221
  if (this._entryNeedsHardReload(entry)) {
1167
1222
  void this._hydrateEntry(entry);
1168
1223
  }
@@ -1174,6 +1229,11 @@ export class ExcalidrawEmbedController {
1174
1229
  return;
1175
1230
  }
1176
1231
 
1232
+ if (msg.type === 'stop-following') {
1233
+ this.onStopFollowing?.();
1234
+ return;
1235
+ }
1236
+
1177
1237
  if (msg.type === 'error') {
1178
1238
  if (entry.isParked) {
1179
1239
  return;
@@ -1227,6 +1287,19 @@ export class ExcalidrawEmbedController {
1227
1287
  entry?.iframe?.contentWindow?.postMessage(payload, window.location.origin);
1228
1288
  }
1229
1289
 
1290
+ _postPendingCommentThread(entry) {
1291
+ if (!entry?.isReady || !entry.pendingCommentThreadId) {
1292
+ return;
1293
+ }
1294
+
1295
+ this._postMessageToEntry(entry, {
1296
+ source: 'collabmd-host',
1297
+ threadId: entry.pendingCommentThreadId,
1298
+ type: 'open-comment-thread',
1299
+ });
1300
+ entry.pendingCommentThreadId = null;
1301
+ }
1302
+
1230
1303
  _requestPreviewViewportFit(entry) {
1231
1304
  if (this._isFilePreviewEntry(entry)) {
1232
1305
  return;
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  getVaultFileExtension,
3
+ isVaultFilePath,
4
+ VAULT_FILE_EXTENSIONS,
3
5
  } from '../../domain/file-kind.js';
6
+ import { pickFiles } from '../browser-utils.js';
4
7
  import {
5
8
  createBaseStarter,
6
9
  composeVaultChildPath,
@@ -8,6 +11,7 @@ import {
8
11
  createMarkdownStarter,
9
12
  createMermaidStarter,
10
13
  createPlantUmlStarter,
14
+ createStructurizrStarter,
11
15
  ensureVaultExtension,
12
16
  getVaultPathLeaf,
13
17
  getVaultPathParent,
@@ -28,6 +32,8 @@ function formatCount(value, singularLabel, pluralLabel) {
28
32
  return `${value} ${value === 1 ? singularLabel : pluralLabel}`;
29
33
  }
30
34
 
35
+ const VAULT_FILE_PICKER_ACCEPT = VAULT_FILE_EXTENSIONS.join(',');
36
+
31
37
  function createEmptyExcalidrawScene() {
32
38
  return JSON.stringify({
33
39
  type: 'excalidraw',
@@ -116,6 +122,18 @@ export class FileActionController {
116
122
 
117
123
  getCreateActions({ parentDir = '' } = {}) {
118
124
  return [
125
+ {
126
+ contextLabel: 'Upload files',
127
+ group: 'File',
128
+ hint: 'Supported vault files',
129
+ icon: this.getCreateActionIcon('upload'),
130
+ id: 'upload',
131
+ label: 'Upload files',
132
+ meta: 'Multiple',
133
+ onSelect: () => {
134
+ void this.handleUploadFiles({ parentDir });
135
+ },
136
+ },
119
137
  {
120
138
  contextLabel: 'New markdown file',
121
139
  group: 'Note',
@@ -176,6 +194,16 @@ export class FileActionController {
176
194
  meta: '.puml',
177
195
  onSelect: () => this.handleNewPlantUml({ parentDir }),
178
196
  },
197
+ {
198
+ contextLabel: 'New Structurizr workspace',
199
+ group: 'Diagram',
200
+ hint: 'Structurizr DSL',
201
+ icon: this.getCreateActionIcon('structurizr'),
202
+ id: 'structurizr',
203
+ label: 'Structurizr workspace',
204
+ meta: '.dsl',
205
+ onSelect: () => this.handleNewStructurizr({ parentDir }),
206
+ },
179
207
  {
180
208
  contextLabel: 'New folder',
181
209
  group: 'Structure',
@@ -241,10 +269,13 @@ export class FileActionController {
241
269
 
242
270
  getCreateActionIcon(kind) {
243
271
  switch (kind) {
272
+ case 'upload':
273
+ return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/><path d="M5 20h14"/></svg>';
244
274
  case 'base':
245
275
  return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 4h7l5 5v11a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><path d="M14 4v5h5"/><path d="M8 13h8"/><path d="M8 17h6"/><path d="M8 9h3"/></svg>';
246
276
  case 'drawio':
247
277
  case 'plantuml':
278
+ case 'structurizr':
248
279
  return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="7" height="6" rx="1"/><rect x="14" y="4" width="7" height="6" rx="1"/><rect x="8.5" y="14" width="7" height="6" rx="1"/><path d="M10 7h4"/><path d="M17.5 10v2.5"/><path d="M6.5 10v2.5"/><path d="M6.5 12.5h11"/></svg>';
249
280
  case 'excalidraw':
250
281
  return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/></svg>';
@@ -476,6 +507,47 @@ export class FileActionController {
476
507
  }
477
508
  }
478
509
 
510
+ async uploadVaultFiles(files = [], { parentDir = '' } = {}) {
511
+ const normalizedParentDir = normalizeVaultPathInput(parentDir);
512
+ let uploaded = false;
513
+
514
+ for (const file of Array.from(files ?? [])) {
515
+ const fileName = getVaultPathLeaf(file?.name);
516
+ const filePath = composeVaultChildPath(normalizedParentDir, fileName);
517
+ if (!fileName || !isVaultFilePath(filePath)) {
518
+ this.showToast(`Unsupported file type: ${fileName || 'unnamed file'}`);
519
+ continue;
520
+ }
521
+
522
+ try {
523
+ await this.runWorkspaceMutation((requestId) => this.vaultClient.uploadFile({
524
+ file,
525
+ path: filePath,
526
+ requestId,
527
+ }));
528
+ uploaded = true;
529
+ this.state.expandDirectoryPath(filePath, { includeLeaf: false });
530
+ } catch (error) {
531
+ this.showToast(`${fileName}: ${error?.message || 'Failed to upload file'}`);
532
+ }
533
+ }
534
+
535
+ if (uploaded) {
536
+ await this.refresh();
537
+ }
538
+
539
+ return uploaded;
540
+ }
541
+
542
+ async handleUploadFiles({ parentDir = '' } = {}) {
543
+ const files = await pickFiles({ accept: VAULT_FILE_PICKER_ACCEPT, multiple: true });
544
+ if (files.length === 0) {
545
+ return false;
546
+ }
547
+
548
+ return this.uploadVaultFiles(files, { parentDir });
549
+ }
550
+
479
551
  async createVaultFile(filePath, content, { openAfterCreate = false, errorMessage = 'Failed to create file' } = {}) {
480
552
  try {
481
553
  await this.runWorkspaceMutation((requestId) => this.vaultClient.createFile(withRequestId({
@@ -953,6 +1025,36 @@ export class FileActionController {
953
1025
  });
954
1026
  }
955
1027
 
1028
+ handleNewStructurizr({ parentDir = '' } = {}) {
1029
+ const context = this.getCreateContext(parentDir);
1030
+
1031
+ this.openActionDialog({
1032
+ title: 'Create Structurizr workspace',
1033
+ copy: context.normalizedParentDir
1034
+ ? 'Create a collaborative C4 workspace inside the selected folder.'
1035
+ : 'Create a collaborative C4 workspace in the vault.',
1036
+ label: `Workspace ${context.inputLabelSuffix}`,
1037
+ hint: `${context.hintPrefix} ".dsl" is added automatically. workspace.dsl is conventional; any workspace .dsl file can render.`,
1038
+ note: context.note,
1039
+ placeholder: 'workspace',
1040
+ submitLabel: 'Create workspace',
1041
+ emptyMessage: 'Workspace path is required',
1042
+ onSubmit: (value) => {
1043
+ const normalizedPath = normalizeVaultPathInput(value);
1044
+ if (!normalizedPath) {
1045
+ this.showToast('Workspace path is required');
1046
+ return false;
1047
+ }
1048
+
1049
+ const starter = createStructurizrStarter(composeVaultChildPath(context.normalizedParentDir, normalizedPath));
1050
+ return this.createVaultFile(starter.path, starter.content, {
1051
+ errorMessage: 'Failed to create Structurizr workspace',
1052
+ openAfterCreate: true,
1053
+ });
1054
+ },
1055
+ });
1056
+ }
1057
+
956
1058
  handleRenameFile(filePath) {
957
1059
  const extension = getVaultFileExtension(filePath) || '.md';
958
1060
 
@@ -24,11 +24,6 @@ export class FileExplorerController {
24
24
  this.threadCounts = new Map();
25
25
  this.view = new FileExplorerView({
26
26
  mobileBreakpointQuery,
27
- onDirectorySelect: (pathValue) => {
28
- this.state.expandDirectoryPath(pathValue);
29
- this.state.setSearchQuery('');
30
- this.renderTree({ reset: true });
31
- },
32
27
  onDirectoryToggle: (pathValue) => {
33
28
  this.state.toggleDirectory(pathValue);
34
29
  if (!this.view.rerenderDirectoryBranch(pathValue, this.state.tree, {