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
@@ -103,14 +103,16 @@
103
103
  </div>
104
104
  <div class="sidebar-search" id="fileSearch">
105
105
  <input
106
- type="text"
106
+ type="search"
107
107
  class="ui-input sidebar-search-input"
108
108
  id="fileSearchInput"
109
- placeholder="Search files..."
109
+ placeholder="Filter files by name or path…"
110
110
  autocomplete="off"
111
111
  spellcheck="false"
112
- aria-label="Search files"
112
+ aria-label="Filter files by name or path"
113
+ aria-describedby="fileSearchStatus"
113
114
  >
115
+ <div class="sidebar-search-status hidden" id="fileSearchStatus" role="status" aria-live="polite"></div>
114
116
  </div>
115
117
  <div class="sidebar-search hidden" id="gitSearch">
116
118
  <input
@@ -357,6 +359,18 @@
357
359
  <span class="ui-toolbar-button-label">Search files</span>
358
360
  </button>
359
361
 
362
+ <button class="ui-button ui-button--ghost ui-button--toolbar ui-button--toggle toolbar-vim-button" id="toggleVimModeBtn" aria-label="Enable Vim keybindings" aria-pressed="false" title="Enable Vim keybindings">
363
+ <span class="toolbar-vim-icon" aria-hidden="true">
364
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
365
+ <rect x="3" y="4" width="18" height="16" rx="2"></rect>
366
+ <path d="m7 9 3 3-3 3"></path>
367
+ <path d="M13 15h4"></path>
368
+ </svg>
369
+ </span>
370
+ <span class="ui-toolbar-button-label">Vim</span>
371
+ <span class="ui-pill-badge ui-pill-badge--muted toolbar-vim-state" id="vimModeToggleLabel">Off</span>
372
+ </button>
373
+
360
374
  <details class="toolbar-overflow-group hidden" id="exportMenuGroup">
361
375
  <summary class="ui-button ui-button--ghost ui-button--toolbar toolbar-overflow-group-toggle" aria-label="Export options" title="Export">
362
376
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
@@ -368,6 +382,14 @@
368
382
  <span class="ui-toolbar-button-label">Export</span>
369
383
  </summary>
370
384
  <div class="toolbar-overflow-submenu">
385
+ <button class="ui-button ui-button--ghost ui-button--toolbar" id="exportHtmlBtn" aria-label="Export HTML" title="Export HTML">
386
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
387
+ <path d="m8 9-3 3 3 3"></path>
388
+ <path d="m16 9 3 3-3 3"></path>
389
+ <path d="m14 5-4 14"></path>
390
+ </svg>
391
+ <span class="ui-toolbar-button-label">Export HTML</span>
392
+ </button>
371
393
  <button class="ui-button ui-button--ghost ui-button--toolbar" id="exportDocxBtn" aria-label="Export DOCX" title="Export DOCX">
372
394
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
373
395
  <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
@@ -725,10 +747,10 @@
725
747
  <circle cx="11" cy="11" r="8"></circle>
726
748
  <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
727
749
  </svg>
728
- <input type="text" class="qs-input" id="quickSwitcherInput" placeholder="Search files..." aria-label="Search files" aria-controls="quickSwitcherResults" aria-describedby="quickSwitcherHint quickSwitcherScope" autocomplete="off" spellcheck="false">
750
+ <input type="text" class="qs-input" id="quickSwitcherInput" role="combobox" placeholder="Search files..." aria-label="Search files" aria-controls="quickSwitcherResults" aria-describedby="quickSwitcherHint quickSwitcherScope" aria-autocomplete="list" aria-expanded="false" autocomplete="off" spellcheck="false">
729
751
  <kbd class="qs-kbd">esc</kbd>
730
752
  </div>
731
- <div class="qs-scope hidden" id="quickSwitcherScope" role="note" aria-hidden="true">Exact, case-insensitive text in Markdown, Base, Mermaid, PlantUML, and Draw.io files. Images and Excalidraw are excluded.</div>
753
+ <div class="qs-scope hidden" id="quickSwitcherScope" role="note" aria-hidden="true">Exact, case-insensitive text in Markdown, Base, Mermaid, PlantUML, Draw.io, and Excalidraw files. Images are excluded.</div>
732
754
  <div class="qs-results" id="quickSwitcherResults" role="listbox" aria-label="Search results" aria-busy="false" tabindex="-1"></div>
733
755
  <div class="qs-hint" id="quickSwitcherHint" role="status" aria-live="polite" aria-atomic="true">Type to search files</div>
734
756
  <div class="qs-footer">
@@ -1,4 +1,7 @@
1
- import { getVaultFileKind, supportsCommentsForFilePath } from '../../../domain/file-kind.js';
1
+ import {
2
+ getVaultFileKind,
3
+ supportsCommentsForFilePath,
4
+ } from '../../../domain/file-kind.js';
2
5
 
3
6
  export const commentsFeature = {
4
7
  createCommentThread(payload) {
@@ -82,19 +85,20 @@ export const commentsFeature = {
82
85
  return;
83
86
  }
84
87
 
88
+ const isDiagram = getVaultFileKind(filePath) === 'excalidraw';
85
89
  const line = Number.isFinite(anchor?.startLine) ? anchor.startLine : null;
86
- this._pendingCommentOverviewFocus = {
87
- attempts: 0,
88
- filePath,
89
- line,
90
- threadId,
91
- };
90
+ if (!isDiagram) {
91
+ this._pendingCommentOverviewFocus = {
92
+ attempts: 0,
93
+ filePath,
94
+ line,
95
+ threadId,
96
+ };
97
+ }
92
98
  this.setSidebarTab('comments');
93
99
  this.setSidebarVisibility(true);
94
100
  this.workspaceRouteController?.preserveSidebarTabForNextFileRoute?.(filePath);
95
- this.navigation.navigateToFile(filePath, {
96
- line,
97
- });
101
+ this.navigation.navigateToFile(filePath, isDiagram ? undefined : { line });
98
102
 
99
103
  if (this.currentFilePath !== filePath || !this.session) {
100
104
  await this.workspaceRouteController.openFile(filePath);
@@ -102,7 +106,14 @@ export const commentsFeature = {
102
106
 
103
107
  this.setSidebarTab('comments');
104
108
  this.setSidebarVisibility(true);
109
+ if (isDiagram) {
110
+ this.excalidrawEmbed?.openCommentThread?.(filePath, threadId);
111
+ this.closeSidebarOnMobile?.();
112
+ return;
113
+ }
114
+
105
115
  this.focusPendingCommentOverviewThread();
116
+ this.closeSidebarOnMobile?.();
106
117
  },
107
118
 
108
119
  focusPendingCommentOverviewThread() {
@@ -119,14 +130,11 @@ export const commentsFeature = {
119
130
  if (didFocus) {
120
131
  this._pendingCommentOverviewFocus = null;
121
132
  this.setSidebarTab('comments');
122
- this.setSidebarVisibility(true);
123
133
  return true;
124
134
  }
125
135
 
126
136
  if (pending.attempts >= 8) {
127
137
  this._pendingCommentOverviewFocus = null;
128
- this.setSidebarTab('comments');
129
- this.setSidebarVisibility(true);
130
138
  return false;
131
139
  }
132
140
 
@@ -446,6 +446,18 @@ export const gitFeature = {
446
446
  showToast: true,
447
447
  });
448
448
 
449
+ const structurizrDependencyChanged = [
450
+ ...workspaceChange.changedPaths,
451
+ ...workspaceChange.deletedPaths,
452
+ ...workspaceChange.renamedPaths.flatMap((entry) => [entry.oldPath, entry.newPath]),
453
+ ].some((pathValue) => /(?:\.dsl$|(?:^|\/)workspace\.json$)/iu.test(pathValue));
454
+ if (this.currentFilePath && this.isStructurizrWorkspaceFile?.(this.currentFilePath) && structurizrDependencyChanged) {
455
+ this.structurizrPreview?.queueSync({
456
+ filePath: this.currentFilePath,
457
+ source: this.session?.getText?.() ?? '',
458
+ });
459
+ }
460
+
449
461
  if (
450
462
  event.origin === 'filesystem'
451
463
  && this.currentFilePath
@@ -531,8 +543,8 @@ export const gitFeature = {
531
543
  this.toastController.show(this.formatPullBackupToast(result.pullBackup), 5000);
532
544
  }
533
545
  } catch (error) {
534
- if (error?.code === 'pull_diverged_ff_only') {
535
- this.toastController.show('Cannot pull because local and remote commits have diverged. Fast-forward only pull is not possible.', 5000);
546
+ if (error?.code === 'pull_conflicted_commits') {
547
+ this.toastController.show('Cannot pull automatically because local and remote commits conflict.', 5000);
536
548
  return;
537
549
  }
538
550
 
@@ -13,8 +13,9 @@ function getConnectedPresenceState(context) {
13
13
  const isDrawioRoute = Boolean(context.currentFilePath && context.isDrawioFile?.(context.currentFilePath));
14
14
  const isExcalidrawRoute = Boolean(context.currentFilePath && context.isExcalidrawFile?.(context.currentFilePath));
15
15
  const isImageRoute = Boolean(context.currentFilePath && context.isImageFile?.(context.currentFilePath));
16
+ const isPdfRoute = Boolean(context.currentFilePath && context.isPdfFile?.(context.currentFilePath));
16
17
  const shouldUseLobbyState = !hasEditorSession
17
- && (!context.currentFilePath || isDrawioRoute || isExcalidrawRoute || isImageRoute);
18
+ && (!context.currentFilePath || isDrawioRoute || isExcalidrawRoute || isImageRoute || isPdfRoute);
18
19
  return shouldUseLobbyState
19
20
  ? context.lobby?.getConnectionState?.() ?? context.connectionState
20
21
  : context.connectionState;
@@ -357,7 +358,7 @@ export const presenceFeature = {
357
358
  }
358
359
 
359
360
  if (user.currentFile && user.currentFile !== this.currentFilePath) {
360
- this.navigation.navigateToFile(user.currentFile);
361
+ this.navigation.navigateToFile(user.currentFile, { preserveFollow: true });
361
362
  return;
362
363
  }
363
364
 
@@ -403,7 +404,7 @@ export const presenceFeature = {
403
404
  }
404
405
 
405
406
  if (user.currentFile && user.currentFile !== this.currentFilePath) {
406
- this.navigation.navigateToFile(user.currentFile);
407
+ this.navigation.navigateToFile(user.currentFile, { preserveFollow: true });
407
408
  return;
408
409
  }
409
410
 
@@ -5,6 +5,12 @@ import { createFileRouteHash, isCollabMdHashRoute } from '../../domain/hash-rout
5
5
  const VERSION_RELOAD_TOAST_DURATION_MS = 0;
6
6
  const PREVIEW_ROUTE_ANCHOR_STABILIZE_MS = 2000;
7
7
  const PREVIEW_ROUTE_ANCHOR_MAX_APPLICATIONS = 8;
8
+ const PREVIEW_CODE_COPY_ICON = `
9
+ <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" focusable="false">
10
+ <rect x="8" y="8" width="11" height="11" rx="2" fill="none" stroke="currentColor" stroke-width="2"/>
11
+ <path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
12
+ </svg>
13
+ `;
8
14
  const PREVIEW_HEADING_LINK_ICON = `
9
15
  <svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true" focusable="false">
10
16
  <path d="M10 13.5 14 9.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
@@ -62,6 +68,7 @@ function initialize() {
62
68
  this.syncIdentityManagementUi();
63
69
  this.syncCurrentUserName();
64
70
  this.syncWrapToggle();
71
+ this.syncVimModeToggle();
65
72
  this.syncToolbarOverflowVisibility?.();
66
73
  this.syncFileHistoryButton({ mode: 'empty' });
67
74
  this.renderChat();
@@ -167,6 +174,11 @@ function bindToolbarEvents() {
167
174
  this.elements.toolbarSearchButton?.addEventListener('click', openQuickSwitcher);
168
175
  this.elements.searchFilesButton?.addEventListener('click', openQuickSwitcher);
169
176
 
177
+ this.elements.exportHtmlButton?.addEventListener('click', () => {
178
+ void this.handleExportRequest?.('html');
179
+ this.closeToolbarOverflowMenu?.();
180
+ });
181
+
170
182
  this.elements.exportDocxButton?.addEventListener('click', () => {
171
183
  void this.handleExportRequest?.('docx');
172
184
  this.closeToolbarOverflowMenu?.();
@@ -245,6 +257,13 @@ function bindDialogEvents() {
245
257
 
246
258
  /** @this {UiShellContext} */
247
259
  function bindEvents() {
260
+ const stopFollowing = () => this.stopFollowingUser?.();
261
+ this.elements.editorContainer?.addEventListener('pointerdown', stopFollowing);
262
+ this.elements.editorContainer?.addEventListener('keydown', stopFollowing);
263
+ this.elements.editorContainer?.addEventListener('wheel', stopFollowing, { passive: true });
264
+ this.elements.previewContainer?.addEventListener('pointerdown', stopFollowing);
265
+ this.elements.previewContainer?.addEventListener('wheel', stopFollowing, { passive: true });
266
+
248
267
  this.elements.emptyStateNewFileBtn?.addEventListener('click', () => {
249
268
  this.fileExplorer.actionController.openRootCreateMenu({
250
269
  anchor: this.elements.emptyStateNewFileBtn,
@@ -297,6 +316,10 @@ function bindEvents() {
297
316
  this.toggleLineWrapping();
298
317
  });
299
318
 
319
+ this.elements.toggleVimModeButton?.addEventListener('click', () => {
320
+ this.toggleVimMode();
321
+ });
322
+
300
323
  this.elements.userCount?.addEventListener('click', (event) => {
301
324
  event.preventDefault();
302
325
  this.togglePresencePanel?.();
@@ -313,7 +336,8 @@ function bindEvents() {
313
336
  });
314
337
 
315
338
  this.elements.toolbarOverflowMenu?.addEventListener('click', (event) => {
316
- if (event.target instanceof Element && event.target.closest('button')) {
339
+ const button = event.target instanceof Element ? event.target.closest('button') : null;
340
+ if (button && button !== this.elements.toggleVimModeButton) {
317
341
  this.closeToolbarOverflowMenu();
318
342
  }
319
343
  });
@@ -394,6 +418,10 @@ function handleDocumentPointerDown(event) {
394
418
 
395
419
  /** @this {UiShellContext} */
396
420
  function handleDocumentKeydown(event) {
421
+ if (event.key === 'Escape') {
422
+ this.stopFollowingUser?.();
423
+ }
424
+
397
425
  if (event.key === 'Escape' && this.toolbarOverflowOpen) {
398
426
  this.closeToolbarOverflowMenu();
399
427
  return;
@@ -545,6 +573,38 @@ async function copyPreviewHeadingLink(anchorId) {
545
573
  }
546
574
  }
547
575
 
576
+ /** @this {UiShellContext} */
577
+ async function copyPreviewCodeBlock(button) {
578
+ const code = button?.closest('.preview-code-block')?.querySelector(':scope > pre > code');
579
+ if (!(code instanceof HTMLElement)) {
580
+ return;
581
+ }
582
+
583
+ try {
584
+ await copyTextToClipboard(code.textContent);
585
+ this.toastController.show('Code copied');
586
+ } catch {
587
+ this.toastController.show('Failed to copy code');
588
+ }
589
+ }
590
+
591
+ /** @this {UiShellContext} */
592
+ function syncPreviewCodeCopyButtons() {
593
+ this.elements.previewContent?.querySelectorAll('pre > code').forEach((code) => {
594
+ const pre = code.parentElement;
595
+ const wrapper = document.createElement('div');
596
+ wrapper.className = 'preview-code-block';
597
+ wrapper.innerHTML = `
598
+ <div class="preview-code-actions">
599
+ <button type="button" class="ui-button ui-button--secondary ui-button--compact preview-code-copy-button" aria-label="Copy code" title="Copy code">
600
+ ${PREVIEW_CODE_COPY_ICON}<span data-preview-code-copy-label>Copy</span>
601
+ </button>
602
+ </div>`;
603
+ pre.replaceWith(wrapper);
604
+ wrapper.append(pre);
605
+ });
606
+ }
607
+
548
608
  /** @this {UiShellContext} */
549
609
  function syncPreviewHeadingLinkButtons() {
550
610
  const headings = this.elements.previewContent?.querySelectorAll?.('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]') ?? [];
@@ -633,6 +693,13 @@ function handlePreviewContentClick(event) {
633
693
  return;
634
694
  }
635
695
 
696
+ const codeCopyButton = event.target.closest('button.preview-code-copy-button');
697
+ if (codeCopyButton instanceof HTMLButtonElement) {
698
+ event.preventDefault();
699
+ void this.copyPreviewCodeBlock(codeCopyButton);
700
+ return;
701
+ }
702
+
636
703
  const headingLinkButton = event.target.closest('button.preview-heading-link-button[data-preview-heading-anchor]');
637
704
  if (headingLinkButton instanceof HTMLButtonElement) {
638
705
  event.preventDefault();
@@ -731,12 +798,18 @@ function toggleToolbarOverflowMenu() {
731
798
  /** @this {UiShellContext} */
732
799
  function handleThemeChange(theme) {
733
800
  this.previewRenderer.applyTheme(theme);
734
- if (!this.isExcalidrawFile(this.currentFilePath) && !this.isImageFile?.(this.currentFilePath)) {
801
+ if (
802
+ !this.isExcalidrawFile(this.currentFilePath)
803
+ && !this.isImageFile?.(this.currentFilePath)
804
+ && !this.isPdfFile?.(this.currentFilePath)
805
+ && !this.isStructurizrWorkspaceFile?.(this.currentFilePath)
806
+ ) {
735
807
  this.previewRenderer.queueRender();
736
808
  }
737
809
  this.session?.applyTheme(theme);
738
810
  this.excalidrawEmbed.updateTheme(theme);
739
811
  this.drawioEmbed.updateTheme(theme);
812
+ this.pdfPreview?.setTheme(theme);
740
813
  }
741
814
 
742
815
  /** @this {UiShellContext} */
@@ -773,6 +846,35 @@ function getStoredLineWrapping() {
773
846
  return this.preferences.getLineWrappingEnabled();
774
847
  }
775
848
 
849
+ /** @this {UiShellContext} */
850
+ function toggleVimMode() {
851
+ const currentState = this.session?.isVimModeEnabled?.() ?? this.getStoredVimMode();
852
+ const nextState = !currentState;
853
+
854
+ this.session?.setVimMode?.(nextState);
855
+ this.preferences.setVimModeEnabled(nextState);
856
+ this.syncVimModeToggle(nextState);
857
+ }
858
+
859
+ /** @this {UiShellContext} */
860
+ function getStoredVimMode() {
861
+ return this.preferences.getVimModeEnabled();
862
+ }
863
+
864
+ /** @this {UiShellContext} */
865
+ function syncVimModeToggle(state) {
866
+ const enabled = state ?? this.session?.isVimModeEnabled?.() ?? this.getStoredVimMode();
867
+ const label = this.elements.vimModeToggleLabel;
868
+ const button = this.elements.toggleVimModeButton;
869
+ const nextLabel = enabled ? 'On' : 'Off';
870
+ if (label) label.textContent = nextLabel;
871
+ if (button) {
872
+ button.setAttribute('aria-label', `${enabled ? 'Disable' : 'Enable'} Vim keybindings`);
873
+ button.setAttribute('title', `${enabled ? 'Disable' : 'Enable'} Vim keybindings`);
874
+ button.setAttribute('aria-pressed', String(enabled));
875
+ }
876
+ }
877
+
776
878
  /** @this {UiShellContext} */
777
879
  function syncWrapToggle(state) {
778
880
  const enabled = state ?? this.session?.isLineWrappingEnabled() ?? this.getStoredLineWrapping();
@@ -833,9 +935,11 @@ export const uiFeatureShellMethods = {
833
935
  bindEvents,
834
936
  clearInitialFileBootstrap,
835
937
  closeToolbarOverflowMenu,
938
+ copyPreviewCodeBlock,
836
939
  copyPreviewHeadingLink,
837
940
  createPreviewHeadingLinkUrl,
838
941
  getStoredLineWrapping,
942
+ getStoredVimMode,
839
943
  handleConnectionChange,
840
944
  handleDocumentKeydown,
841
945
  handleDocumentPointerDown,
@@ -853,10 +957,13 @@ export const uiFeatureShellMethods = {
853
957
  setToolbarOverflowOpen,
854
958
  showEditorLoadError,
855
959
  showEditorLoading,
960
+ syncPreviewCodeCopyButtons,
856
961
  syncPreviewHeadingLinkButtons,
857
962
  syncVisualViewportBounds,
858
963
  syncToolbarOverflowVisibility,
964
+ syncVimModeToggle,
859
965
  syncWrapToggle,
860
966
  toggleToolbarOverflowMenu,
861
967
  toggleLineWrapping,
968
+ toggleVimMode,
862
969
  };
@@ -1,4 +1,5 @@
1
1
  import { isMarkdownFilePath } from '../../../domain/file-kind.js';
2
+ import { pickFiles } from '../../browser-utils.js';
2
3
  import {
3
4
  getMarkdownBlockActionLabel,
4
5
  getMarkdownBlockMenuActions,
@@ -91,7 +92,6 @@ function renderMarkdownToolbarMarkup(activeAction) {
91
92
  * @property {{ show(message: string): void }} toastController
92
93
  * @property {{ uploadImageAttachment(payload: { file: File, fileName: string, sourcePath: string }): Promise<{ markdown?: string, path?: string }>} } vaultApiClient
93
94
  * @property {{ applyMarkdownToolbarAction(action: string): boolean, insertText(text: string): void, runEditorCommand?(commandId: string): boolean } | null} session
94
- * @property {() => Promise<File | null>} pickImageFile
95
95
  * @property {(file: File) => Promise<boolean>} handleEditorImageInsert
96
96
  * @property {() => Promise<void>} handleToolbarImageInsert
97
97
  */
@@ -395,7 +395,7 @@ function runEditorCommand(commandId) {
395
395
 
396
396
  /** @this {UiToolbarContext} */
397
397
  async function handleToolbarImageInsert() {
398
- const file = await this.pickImageFile();
398
+ const [file] = await pickFiles({ accept: IMAGE_FILE_PICKER_ACCEPT });
399
399
  if (!file) {
400
400
  return;
401
401
  }
@@ -403,55 +403,6 @@ async function handleToolbarImageInsert() {
403
403
  await this.handleEditorImageInsert(file);
404
404
  }
405
405
 
406
- /** @this {UiToolbarContext} */
407
- function pickImageFile() {
408
- return new Promise((resolve) => {
409
- const input = document.createElement('input');
410
- input.type = 'file';
411
- input.accept = IMAGE_FILE_PICKER_ACCEPT;
412
- input.style.position = 'fixed';
413
- input.style.left = '-9999px';
414
- document.body.appendChild(input);
415
- let settled = false;
416
- let focusTimer = null;
417
-
418
- const cleanup = (value) => {
419
- if (settled) {
420
- return;
421
- }
422
-
423
- settled = true;
424
- if (focusTimer) {
425
- window.clearTimeout(focusTimer);
426
- }
427
- window.removeEventListener('focus', handleWindowFocus);
428
- input.remove();
429
- resolve(value);
430
- };
431
-
432
- const handleWindowFocus = () => {
433
- focusTimer = window.setTimeout(() => {
434
- if (settled || input.files?.length) {
435
- return;
436
- }
437
-
438
- cleanup(null);
439
- }, 250);
440
- };
441
-
442
- input.addEventListener('change', () => {
443
- cleanup(input.files?.[0] ?? null);
444
- }, { once: true });
445
-
446
- input.addEventListener('cancel', () => {
447
- cleanup(null);
448
- }, { once: true });
449
-
450
- window.addEventListener('focus', handleWindowFocus, { once: true });
451
- input.click();
452
- });
453
- }
454
-
455
406
  /** @this {UiToolbarContext} */
456
407
  async function handleEditorImageInsert(file) {
457
408
  if (!this.session || !isMarkdownFilePath(this.currentFilePath)) {
@@ -526,7 +477,6 @@ export const uiFeatureToolbarMethods = {
526
477
  openMarkdownBlockMenu,
527
478
  positionMarkdownBlockMenu,
528
479
  renderMarkdownBlockMenuPopover,
529
- pickImageFile,
530
480
  renderMarkdownToolbar,
531
481
  runEditorCommand,
532
482
  setActiveMarkdownBlockAction,
@@ -5,7 +5,9 @@ import {
5
5
  isExcalidrawFilePath,
6
6
  isImageAttachmentFilePath,
7
7
  isMermaidFilePath,
8
+ isPdfFilePath,
8
9
  isPlantUmlFilePath,
10
+ isStructurizrFilePath,
9
11
  stripVaultFileExtension,
10
12
  } from '../../../domain/file-kind.js';
11
13
 
@@ -14,8 +16,10 @@ export const workspaceFeature = {
14
16
  isDrawioFile: isDrawioFilePath,
15
17
  isExcalidrawFile: isExcalidrawFilePath,
16
18
  isImageFile: isImageAttachmentFilePath,
19
+ isPdfFile: isPdfFilePath,
17
20
  isMermaidFile: isMermaidFilePath,
18
21
  isPlantUmlFile: isPlantUmlFilePath,
22
+ isStructurizrWorkspaceFile: isStructurizrFilePath,
19
23
 
20
24
  getPreviewSource() {
21
25
  const previewDocument = this.getStaticPreviewDocument?.();
@@ -61,7 +65,7 @@ export const workspaceFeature = {
61
65
 
62
66
  supportsFileHistory(filePath) {
63
67
  const kind = getVaultFileKind(filePath);
64
- return kind !== null && kind !== 'image';
68
+ return kind !== null && kind !== 'image' && kind !== 'pdf';
65
69
  },
66
70
 
67
71
  getDisplayName(filePath) {
@@ -39,6 +39,7 @@ export function bindAppShellElements(doc = document) {
39
39
  editorFindButton: doc.getElementById('editorFindBtn'),
40
40
  editorContainer: doc.getElementById('editorContainer'),
41
41
  exportDocxButton: doc.getElementById('exportDocxBtn'),
42
+ exportHtmlButton: doc.getElementById('exportHtmlBtn'),
42
43
  exportMenuGroup: doc.getElementById('exportMenuGroup'),
43
44
  exportPdfButton: doc.getElementById('exportPdfBtn'),
44
45
  markdownToolbar: doc.getElementById('markdownToolbar'),
@@ -64,9 +65,11 @@ export function bindAppShellElements(doc = document) {
64
65
  shareButton: doc.getElementById('shareBtn'),
65
66
  toastContainer: doc.getElementById('toastContainer'),
66
67
  toggleWrapButton: doc.getElementById('toggleWrapBtn'),
68
+ toggleVimModeButton: doc.getElementById('toggleVimModeBtn'),
67
69
  userAvatars: doc.getElementById('userAvatars'),
68
70
  userCount: doc.getElementById('userCount'),
69
71
  wrapToggleLabel: doc.getElementById('wrapToggleLabel'),
72
+ vimModeToggleLabel: doc.getElementById('vimModeToggleLabel'),
70
73
  activeFileName: doc.getElementById('activeFileName'),
71
74
  sidebarToggle: doc.getElementById('sidebarToggle'),
72
75
  sidebarClose: doc.getElementById('sidebarClose'),
@@ -0,0 +1,75 @@
1
+ import { resolveApiUrl } from '../domain/runtime-paths.js';
2
+
3
+ const READ_ONLY_CATEGORIES = Object.freeze([
4
+ 'annotation',
5
+ 'document-capture',
6
+ 'document-close',
7
+ 'document-open',
8
+ 'document-print',
9
+ 'document-protect',
10
+ 'form',
11
+ 'history',
12
+ 'insert',
13
+ 'panel-comment',
14
+ 'redaction',
15
+ 'rotate',
16
+ ]);
17
+
18
+ export class PdfPreviewController {
19
+ constructor({ getTheme = () => 'dark', loadEmbedPdf } = {}) {
20
+ this.getTheme = getTheme;
21
+ this.loadEmbedPdf = loadEmbedPdf ?? (() => import('@embedpdf/snippet'));
22
+ this.renderToken = 0;
23
+ this.viewer = null;
24
+ }
25
+
26
+ async render({ filePath, renderHost }) {
27
+ this.cancel();
28
+ const token = this.renderToken;
29
+ const viewerHost = document.createElement('div');
30
+ viewerHost.className = 'pdf-file-preview-viewer';
31
+ viewerHost.setAttribute('aria-label', `${String(filePath).split('/').pop() || filePath} PDF preview`);
32
+ renderHost.replaceChildren(viewerHost);
33
+
34
+ try {
35
+ const { default: EmbedPDF, LockModeType, ZoomMode } = await this.loadEmbedPdf();
36
+ if (token !== this.renderToken || !viewerHost.isConnected) return;
37
+
38
+ const viewer = EmbedPDF.init({
39
+ type: 'container',
40
+ target: viewerHost,
41
+ src: resolveApiUrl(`/download/file?path=${encodeURIComponent(filePath)}`),
42
+ annotations: { locked: { type: LockModeType.All } },
43
+ disabledCategories: READ_ONLY_CATEGORIES,
44
+ fontFallback: null,
45
+ fonts: { signature: null, ui: null },
46
+ stamp: { defaultLibrary: false, manifests: [] },
47
+ tabBar: 'never',
48
+ theme: { preference: this.getTheme() },
49
+ wasmUrl: new URL('@embedpdf/pdfium/pdfium.wasm', import.meta.url).toString(),
50
+ zoom: {
51
+ defaultZoomLevel: ZoomMode.FitWidth,
52
+ maxZoom: 2,
53
+ minZoom: 0.5,
54
+ zoomStep: 0.25,
55
+ },
56
+ });
57
+
58
+ this.viewer = viewer;
59
+ } catch {
60
+ if (token !== this.renderToken) return;
61
+ viewerHost.classList.add('pdf-file-preview-error');
62
+ viewerHost.textContent = 'This PDF could not be rendered in the browser.';
63
+ }
64
+ }
65
+
66
+ setTheme(theme) {
67
+ this.viewer?.setTheme?.(theme);
68
+ }
69
+
70
+ cancel() {
71
+ this.renderToken += 1;
72
+ this.viewer?.remove();
73
+ this.viewer = null;
74
+ }
75
+ }
@@ -9,6 +9,9 @@ import { extractYamlFrontmatter } from '../../domain/yaml-frontmatter.js';
9
9
  import { renderFrontmatterBlock } from './markdown-frontmatter.js';
10
10
  import { analyzeMarkdownComplexity } from './preview-render-profile.js';
11
11
 
12
+ hljs.registerAliases(['cql', 'mariadb', 'mssql', 'mysql', 'plsql', 'sqlite'], { languageName: 'sql' });
13
+ hljs.registerAliases(['ecmascript', 'node'], { languageName: 'javascript' });
14
+
12
15
  const DIRECT_VIDEO_MIME_TYPES = Object.freeze({
13
16
  '.mp4': 'video/mp4',
14
17
  '.ogg': 'video/ogg',
@@ -27,7 +27,7 @@ export async function ensureQuickSwitcherInstance(host) {
27
27
  getSearchConfig: () => host.runtimeConfig.search ?? {},
28
28
  onFileSelect: (filePath) => host.workspaceRouteController.handleFileSelection(filePath, {
29
29
  closeSidebarOnMobile: true,
30
- revealInTree: true,
30
+ revealInTree: !host.isMobileViewport(),
31
31
  }),
32
32
  onTextMatchSelect: (match) => {
33
33
  if (!match?.file) {