collabmd 0.1.46 → 0.1.48

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 (317) hide show
  1. package/README.md +9 -5
  2. package/dist/client/assets/{CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js → CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js} +1 -1
  3. package/dist/client/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -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/babel-CY5KOv-I.js +16 -0
  8. package/dist/client/assets/base-BYODPHdS.js +1 -0
  9. package/dist/client/assets/blockDiagram-NRAW4CY4-C6DtJy5Y.js +129 -0
  10. package/dist/client/assets/browser-BKLM0ThC-IxjwuOIz-BBqQnUEb.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-ui-shared-DuoyznXW.js +7 -0
  53. package/dist/client/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
  54. package/dist/client/assets/{cynefinDiagram-MW4NZA55-CJxXfJek.js → cynefinDiagram-5FMLGOSQ-CdTPi2yO.js} +1 -1
  55. package/dist/client/assets/dagre-3AP2YEHR-5gtJtd0O.js +4 -0
  56. package/dist/client/assets/dagre-PrKaheQc.js +1 -0
  57. package/dist/client/assets/{diagram-7IWD3JNH-B2d2Bm15.js → diagram-S7CK7UJ4-BudWz9WU.js} +4 -4
  58. package/dist/client/assets/{diagram-UB23O5K3-DJcKCwv9.js → diagram-UQ7AKVKN-3v4rbH-s.js} +1 -1
  59. package/dist/client/assets/{diagram-B4RE2ZJO-NP4UAwjR.js → diagram-VSXAHHWV-CQRVEZR7.js} +1 -1
  60. package/dist/client/assets/{diagram-Q27KOJAE-DylDuwor.js → diagram-VX7I27RA-C_-rj0sw.js} +1 -1
  61. package/dist/client/assets/{diagram-LBJQPF4R-D1FJQV8t.js → diagram-Z3DM3KII-B4yxavgj.js} +1 -1
  62. package/dist/client/assets/direct-engine-DdrQKpgq-CtKfQzCt.js +5 -0
  63. package/dist/client/assets/dist-73RPdcyU.js +1 -0
  64. package/dist/client/assets/{dist-Bm6pf9Bl.js → dist-BJ1bJs33.js} +1 -1
  65. package/dist/client/assets/{dist-BalScOwZ.js → dist-BK9QvQrc.js} +1 -1
  66. package/dist/client/assets/{dist-DLMiIsNC.js → dist-BPHLJ09L.js} +1 -1
  67. package/dist/client/assets/{dist-Bf8f4HmM.js → dist-B_yQaEv_.js} +1 -1
  68. package/dist/client/assets/{dist-CC5IrQMG.js → dist-BeajGMhO.js} +1 -1
  69. package/dist/client/assets/{dist-PDZ3bSqm.js → dist-Bw3VIE_v.js} +1 -1
  70. package/dist/client/assets/{dist-xElaxbia.js → dist-ByrG2qBA.js} +1 -1
  71. package/dist/client/assets/{dist-ule2n6ak.js → dist-C5NgpdG5.js} +1 -1
  72. package/dist/client/assets/dist-CH-sQu3r.js +1 -0
  73. package/dist/client/assets/dist-CKS_-are.js +1 -0
  74. package/dist/client/assets/{dist-D-Z6FLem.js → dist-CdmKGRGr.js} +1 -1
  75. package/dist/client/assets/dist-ChRoo3IB.js +9 -0
  76. package/dist/client/assets/{dist-dtAdgL39.js → dist-CietwTVS.js} +1 -1
  77. package/dist/client/assets/{dist-jv4WdPnK.js → dist-D37Hyu3h.js} +1 -1
  78. package/dist/client/assets/dist-D3fehAZ2.js +2 -0
  79. package/dist/client/assets/{dist-BEwK4csK.js → dist-D5wCC32z.js} +1 -1
  80. package/dist/client/assets/{dist-shrl6K_0.js → dist-DDUn5zTz.js} +1 -1
  81. package/dist/client/assets/{dist-CZgOmxK6.js → dist-DGEbEfw4.js} +1 -1
  82. package/dist/client/assets/{dist-B5HTH2BO.js → dist-DVy5woUy.js} +1 -1
  83. package/dist/client/assets/{dist-DZKu9Cs8.js → dist-DZAAWTG-.js} +1 -1
  84. package/dist/client/assets/{dist-TjMh8n1u.js → dist-Dir5dDKZ.js} +1 -1
  85. package/dist/client/assets/dist-Dl_tpDSr.js +1 -0
  86. package/dist/client/assets/{dist-FWJsgGYI.js → dist-DuH3ZrHo.js} +1 -1
  87. package/dist/client/assets/{dist-BjHJDtyU.js → dist-DutPuFmC.js} +1 -1
  88. package/dist/client/assets/{dist-Cqhv5LAk.js → dist-LQ4kDCKz.js} +1 -1
  89. package/dist/client/assets/{dist-CCP_AKPG.js → dist-RKBTaQMb.js} +1 -1
  90. package/dist/client/assets/{dist-BoVBdSjk.js → dist-Tw-Fxpeb.js} +1 -1
  91. package/dist/client/assets/dist-eBx8q6Sy.js +1 -0
  92. package/dist/client/assets/{dist-rGBmqxC7.js → dist-mj2Ht75J.js} +1 -1
  93. package/dist/client/assets/{dist-BDt3rz7l.js → dist-netZOQtD.js} +1 -1
  94. package/dist/client/assets/document-formatter-Dxl_a8XO.js +2 -0
  95. package/dist/client/assets/{drawio-editor-FzY1cVw8.js → drawio-editor-BVaNNtYp.js} +2 -2
  96. package/dist/client/assets/{drawioEditor-BjiKJqlc.js → drawioEditor-BkIBlXx0.js} +2 -2
  97. package/dist/client/assets/{ebnfDiagram-BXEA7PRR-nw0wmA6y.js → ebnfDiagram-PWID7BFC-OXa8VEp1.js} +1 -1
  98. package/dist/client/assets/editor-session-DMyVdHLj.js +54 -0
  99. package/dist/client/assets/embedded-editor-base-B3l6Xy65.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/estree-CuT_pB8K.js +44 -0
  105. package/dist/client/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
  106. package/dist/client/assets/excalidraw-editor-BbSbI80a.css +1 -0
  107. package/dist/client/assets/excalidraw-editor-BjYaNEMY.js +9 -0
  108. package/dist/client/assets/excalidrawEditor-ChqF_d82.js +2 -0
  109. package/dist/client/assets/export-page-Dyu0XCbu.js +57 -0
  110. package/dist/client/assets/exportDocument-CJBx4Xjx.css +1 -0
  111. package/dist/client/assets/exportDocument-Dgvfa2Sd.js +2 -0
  112. package/dist/client/assets/{file-icon-svg-CQAjlWjK.js → file-icon-svg-Y_kpF957.js} +1 -1
  113. package/dist/client/assets/file-kind-BwMIcZbi.js +1 -0
  114. package/dist/client/assets/file-search-DDLyyLDU.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/html-Cdnr2qzU.js +25 -0
  122. package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +1 -0
  123. package/dist/client/assets/index-CSNIMyOP.css +1 -0
  124. package/dist/client/assets/index-DG3N2Ps-.js +2 -0
  125. package/dist/client/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
  126. package/dist/client/assets/{infoDiagram-6WML65LV-DYB8HCDQ.js → infoDiagram-RXCK75RN-BMzo6KIn.js} +1 -1
  127. package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-Coz_P3t6.js → ishikawaDiagram-5VMMS53U-BIM3h287.js} +1 -1
  128. package/dist/client/assets/{journeyDiagram-NVQOT4AX-B6edChce.js → journeyDiagram-EYS64GPL-mF6Lh8O-.js} +1 -1
  129. package/dist/client/assets/{kanban-definition-27J2QSJJ-BFd9-cLc.js → kanban-definition-3QL26DDD-DLN2NyoR.js} +2 -2
  130. package/dist/client/assets/{line-B_S_S8kV.js → line-BfTWJmgf.js} +1 -1
  131. package/dist/client/assets/main-D0wCxpVn.js +903 -0
  132. package/dist/client/assets/markdown-CByV-4CU.js +70 -0
  133. package/dist/client/assets/{mermaid-parser.core-CUVdebAV.js → mermaid-parser.core-Bl0efOMt.js} +2 -2
  134. package/dist/client/assets/mermaid.core-BJRLUQZE.js +11 -0
  135. package/dist/client/assets/{mindmap-definition-FAOFIHXS-C9PQ38Al.js → mindmap-definition-FBJOCRG2-CRyHaHTk.js} +1 -1
  136. package/dist/client/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
  137. package/dist/client/assets/pdfium-RAgkpwfK.wasm +0 -0
  138. package/dist/client/assets/{pegDiagram-VL7TDLO6-BMZUrSaU.js → pegDiagram-XKGWAZYB-B31fKPR2.js} +1 -1
  139. package/dist/client/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
  140. package/dist/client/assets/{pieDiagram-7S7Q4E2Y-h8kbCL9o.js → pieDiagram-E7YTZNPT-B5OZPb88.js} +1 -1
  141. package/dist/client/assets/preview-render-compiler-CrJIooVD.js +35 -0
  142. package/dist/client/assets/preview-render-compiler-O0JxBVNF.js +1 -0
  143. package/dist/client/assets/{preview-render-profile-CE-5rVTC.js → preview-render-profile-B4RWYWHt.js} +1 -1
  144. package/dist/client/assets/preview-render-worker-_QTxqKvP.js +60 -0
  145. package/dist/client/assets/prod-BiYPMR1i.js +177 -0
  146. package/dist/client/assets/prod-VxdGZ5Ly.js +1 -0
  147. package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-CkElyfMi.js → quadrantDiagram-AXDQQJYC-Drrw14YH.js} +1 -1
  148. package/dist/client/assets/quick-switcher-controller-CaEjEtq_.js +1 -0
  149. package/dist/client/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
  150. package/dist/client/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
  151. package/dist/client/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
  152. package/dist/client/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
  153. package/dist/client/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
  154. package/dist/client/assets/{railroadDiagram-AXF67PYL-c75gnikD.js → railroadDiagram-O6MQD6OU-BPifP0MB.js} +1 -1
  155. package/dist/client/assets/{requirementDiagram-LRYGKXZP-mNxKLuHS.js → requirementDiagram-EFPCY7ZU-CRZG6mRB.js} +2 -2
  156. package/dist/client/assets/{sankeyDiagram-W5VNT64P-0OhplksI.js → sankeyDiagram-P5KCCOFB-DvarEJpo.js} +1 -1
  157. package/dist/client/assets/{sequenceDiagram-SI44F4Z6-BJ_yAocC.js → sequenceDiagram-WJ2MYXX4-B4oWlb-w.js} +1 -1
  158. package/dist/client/assets/standalone-6z5x8CWe.js +29 -0
  159. package/dist/client/assets/{stateDiagram-OKZ733FA-Cc83O86s.js → stateDiagram-HBIQ2CUA-qbHIIqRZ.js} +1 -1
  160. package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +1 -0
  161. package/dist/client/assets/swimlanes-XN3QIQJK-xB6CtUGj.js +1 -0
  162. package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +8 -0
  163. package/dist/client/assets/{timeline-definition-Z64GVDOM-BWDAGTHz.js → timeline-definition-24CTP7MA-Lj_yTNlY.js} +1 -1
  164. package/dist/client/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
  165. package/dist/client/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
  166. package/dist/client/assets/{vault-api-client-_LpV1aIn.js → vault-api-client-BkwdFjx0.js} +2 -2
  167. package/dist/client/assets/vault-utils-DvoSftJT.js +22 -0
  168. package/dist/client/assets/{vennDiagram-T6HMQDX7-LOSsnlMB.js → vennDiagram-4TSXK5OY-BekRp5Y0.js} +1 -1
  169. package/dist/client/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
  170. package/dist/client/assets/{wardleyDiagram-T6FBY63Y-BxII3foY.js → wardleyDiagram-VM6X3IG4-kdnV_Hdm.js} +1 -1
  171. package/dist/client/assets/worker-engine-m0rlHn8R-S8mpn_O0.js +17369 -0
  172. package/dist/client/assets/{xychartDiagram-ELKLHX3M-B-pTEjju.js → xychartDiagram-S5SC5T6Z-DufGwNSB.js} +3 -3
  173. package/dist/client/assets/yaml-9ltHPDNO.js +154 -0
  174. package/dist/client/drawio-editor.html +1 -1
  175. package/dist/client/excalidraw-editor.html +1 -1
  176. package/dist/client/export-document.html +3 -3
  177. package/dist/client/index.html +56 -24
  178. package/package.json +22 -18
  179. package/src/client/app/index.html +53 -21
  180. package/src/client/application/app-shell/comments-feature.js +2 -3
  181. package/src/client/application/app-shell/export-feature.js +17 -1
  182. package/src/client/application/app-shell/git-feature.js +30 -11
  183. package/src/client/application/app-shell/presence-feature.js +2 -2
  184. package/src/client/application/app-shell/ui-feature-identity.js +13 -0
  185. package/src/client/application/app-shell/ui-feature-shell.js +228 -9
  186. package/src/client/application/app-shell-elements.js +5 -0
  187. package/src/client/application/pdf-preview-controller.js +53 -970
  188. package/src/client/application/preview-render-compiler.js +3 -0
  189. package/src/client/application/quick-switcher-loader.js +1 -1
  190. package/src/client/application/workspace-coordinator.js +21 -4
  191. package/src/client/application/workspace-preview-controller.js +120 -5
  192. package/src/client/application/workspace-route-controller.js +4 -1
  193. package/src/client/assets/vendor/highlight/github-dark.min.css +2 -10
  194. package/src/client/assets/vendor/highlight/github.min.css +2 -10
  195. package/src/client/assets/vendor/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  196. package/src/client/assets/vendor/jetbrains-mono/OFL.txt +93 -0
  197. package/src/client/bootstrap/collabmd-app-shell.js +31 -8
  198. package/src/client/domain/document-formatter.js +193 -0
  199. package/src/client/domain/file-search.js +73 -0
  200. package/src/client/domain/git-labels.js +5 -0
  201. package/src/client/domain/vault-paths.js +6 -4
  202. package/src/client/domain/wiki-link-completions.js +20 -20
  203. package/src/client/drawio-editor.js +8 -3
  204. package/src/client/excalidraw-editor.js +163 -6
  205. package/src/client/export/export-host.js +57 -36
  206. package/src/client/export/export-page.js +20 -6
  207. package/src/client/export/export-pipeline.js +322 -22
  208. package/src/client/infrastructure/editor-session.js +4 -0
  209. package/src/client/infrastructure/editor-view-adapter.js +60 -14
  210. package/src/client/presentation/bases-preview-controller.js +35 -11
  211. package/src/client/presentation/comment-ui/comment-ui-render.js +4 -0
  212. package/src/client/presentation/comment-ui-controller.js +2 -0
  213. package/src/client/presentation/excalidraw-embed-controller.js +7 -0
  214. package/src/client/presentation/file-action-controller.js +15 -2
  215. package/src/client/presentation/file-explorer-controller.js +3 -5
  216. package/src/client/presentation/file-explorer-view.js +43 -45
  217. package/src/client/presentation/file-history-view-controller.js +6 -1
  218. package/src/client/presentation/file-tree-state.js +5 -14
  219. package/src/client/presentation/git-diff-view-controller.js +36 -11
  220. package/src/client/presentation/git-panel-controller.js +12 -8
  221. package/src/client/presentation/quick-switcher-controller.js +92 -153
  222. package/src/client/styles/components/scrollbars.css +10 -0
  223. package/src/client/styles/export-document.css +44 -29
  224. package/src/client/styles/features/collaboration-presence.css +13 -1
  225. package/src/client/styles/features/preview-markdown.css +79 -0
  226. package/src/client/styles/features/quick-switcher.css +8 -0
  227. package/src/client/styles/foundation/themes.css +1 -0
  228. package/src/client/styles/foundation/tokens.css +8 -0
  229. package/src/client/styles/layout/shell.css +73 -339
  230. package/src/client/styles/layout/sidebar.css +42 -4
  231. package/src/client/styles/layout/view-modes.css +2 -0
  232. package/src/client/styles/overrides/codemirror.css +31 -19
  233. package/src/client/styles/overrides/highlightjs.css +20 -4
  234. package/src/client/styles/surfaces/excalidraw-editor.css +42 -0
  235. package/src/domain/file-kind.js +12 -1
  236. package/src/server/domain/ripgrep-search-service.js +110 -37
  237. package/src/server/infrastructure/git/git-service.js +23 -11
  238. package/src/server/infrastructure/http/create-git-api-command-handler.js +3 -1
  239. package/src/server/infrastructure/http/create-structurizr-api-handler.js +4 -2
  240. package/src/server/infrastructure/persistence/path-utils.js +10 -4
  241. package/src/server/infrastructure/persistence/vault-file-store.js +2 -0
  242. package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +33 -7
  243. package/dist/client/assets/architecture-TIHT7OUA-CsHfPErM.js +0 -1
  244. package/dist/client/assets/base-CxPEiHuy.js +0 -1
  245. package/dist/client/assets/blockDiagram-VBNYF7ZC-CipJt9Jl.js +0 -132
  246. package/dist/client/assets/c4Diagram-5PPSVZJV-B5ktVx6g.js +0 -10
  247. package/dist/client/assets/channel-4Jkmq93I.js +0 -1
  248. package/dist/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
  249. package/dist/client/assets/chunk-7Z6QIM7H-BP9Pdsm9.js +0 -10
  250. package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +0 -41
  251. package/dist/client/assets/chunk-I66GZJ75-CVFiiffQ.js +0 -127
  252. package/dist/client/assets/chunk-J7OUQ5F2-4CEb7pvH.js +0 -2
  253. package/dist/client/assets/chunk-JQJVKLGR-cx8X_24v.js +0 -156
  254. package/dist/client/assets/chunk-QR6OTTB3-CDAA_5Ib.js +0 -62
  255. package/dist/client/assets/chunk-RYQCIY6F-D-a0zH8K.js +0 -1
  256. package/dist/client/assets/chunk-UBXNYLIW-Bhk-PbFK.js +0 -1
  257. package/dist/client/assets/chunk-W5SLKNZC-BdNSCxZ9.js +0 -1
  258. package/dist/client/assets/chunk-WRU74C26-h6_XkD3_.js +0 -70
  259. package/dist/client/assets/chunk-ZIRB5QZD-O39ZBUk3.js +0 -32
  260. package/dist/client/assets/classDiagram-JCYQIIEL-L7lsyW_w.js +0 -1
  261. package/dist/client/assets/classDiagram-v2-OCEON4UE-L7lsyW_w.js +0 -1
  262. package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +0 -7
  263. package/dist/client/assets/cynefin-VYW2F7L2-Ds2Fw2Y0.js +0 -1
  264. package/dist/client/assets/dagre-Buvkdvvj.js +0 -1
  265. package/dist/client/assets/dagre-VZM6K2ZE-L1xArOGs.js +0 -4
  266. package/dist/client/assets/dist-B5Jf1SLZ.js +0 -2
  267. package/dist/client/assets/dist-BKW4PALF.js +0 -1
  268. package/dist/client/assets/dist-BWcHHOx-.js +0 -1
  269. package/dist/client/assets/dist-DaYdhcVs.js +0 -1
  270. package/dist/client/assets/dist-Kh2COra_.js +0 -1
  271. package/dist/client/assets/dist-tOaegDYS2.js +0 -1
  272. package/dist/client/assets/editor-session-Cm9ekeNo.js +0 -54
  273. package/dist/client/assets/embedded-editor-base-CwTkk911.css +0 -1
  274. package/dist/client/assets/erDiagram-JOGREHBK-cYUpEKpf.js +0 -85
  275. package/dist/client/assets/es-DXGV3RHd.js +0 -22
  276. package/dist/client/assets/eventmodeling-45OFAUF4-CLEiVx5s.js +0 -1
  277. package/dist/client/assets/excalidraw-editor-B46zsTFI.js +0 -9
  278. package/dist/client/assets/excalidraw-editor-Bt7gMSoh.css +0 -1
  279. package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +0 -2
  280. package/dist/client/assets/export-page-A7NHawJY.js +0 -39
  281. package/dist/client/assets/exportDocument-B2L8fe34.css +0 -1
  282. package/dist/client/assets/exportDocument-BaqcOAAW.js +0 -2
  283. package/dist/client/assets/file-kind-BbqgGzpc.js +0 -1
  284. package/dist/client/assets/flowDiagram-UKHOOZJN-D0ypP0Ez.js +0 -1
  285. package/dist/client/assets/gitGraph-TEB2WS4Q-CZFBqwW-.js +0 -1
  286. package/dist/client/assets/github-dark.min-Z5exjX_n.css +0 -1
  287. package/dist/client/assets/github.min-D2BCvnWf.css +0 -1
  288. package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +0 -1
  289. package/dist/client/assets/index-ClbjMBVt.js +0 -2
  290. package/dist/client/assets/index-Pum8ET-5.css +0 -1
  291. package/dist/client/assets/info-DKCQHKI2--1InqQeT.js +0 -1
  292. package/dist/client/assets/main-B3uPyqic.js +0 -893
  293. package/dist/client/assets/map-BaFkSB1l.js +0 -1
  294. package/dist/client/assets/mermaid.core-DN-lF51U.js +0 -11
  295. package/dist/client/assets/packet-7NZHBO7P-BGMXB0XL.js +0 -1
  296. package/dist/client/assets/pdf-BL9Pr2D5.js +0 -54
  297. package/dist/client/assets/pdf.worker-CLesOks4.mjs +0 -64857
  298. package/dist/client/assets/pdf_viewer-FVkkThnz.js +0 -551
  299. package/dist/client/assets/pie-RZYD4A2V-DgYg3tCt.js +0 -1
  300. package/dist/client/assets/preview-render-compiler-BD-XhkS2.js +0 -35
  301. package/dist/client/assets/preview-render-compiler-BKedaMxx.js +0 -1
  302. package/dist/client/assets/preview-render-worker-CiPqc9n6.js +0 -60
  303. package/dist/client/assets/prod-Dy20X7eT.js +0 -1
  304. package/dist/client/assets/prod-hnwh7wRM.js +0 -177
  305. package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +0 -12
  306. package/dist/client/assets/radar-I7S5WNFK-Ag-zp_Rq.js +0 -1
  307. package/dist/client/assets/railroad-3IZDKUUU-aibJDIDV.js +0 -1
  308. package/dist/client/assets/railroad-abnf-AHOZXSZD-BBMEuvIl.js +0 -1
  309. package/dist/client/assets/railroad-ebnf-EBAXGLYW-Be1nseej.js +0 -1
  310. package/dist/client/assets/railroad-peg-LSFZ7HO6-DIvD-Eyb.js +0 -1
  311. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-B1-BufUk.js +0 -1
  312. package/dist/client/assets/swimlanes-SLNWSIFB-oVTWEJIj.js +0 -2
  313. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-C5OTVwEu.js +0 -8
  314. package/dist/client/assets/treeView-QDETBFTQ-uHZ-3Fz8.js +0 -1
  315. package/dist/client/assets/treemap-6X3UGDF4-BnUKyy3l.js +0 -1
  316. package/dist/client/assets/vault-utils-wdQ0RNR9.js +0 -1
  317. package/dist/client/assets/wardley-OPB4EBWU-Da7TLC2F.js +0 -1
@@ -0,0 +1,193 @@
1
+ import { getVaultFileKind } from '../../domain/file-kind.js';
2
+
3
+ const FORMATTABLE_FILE_KINDS = new Set(['base', 'html', 'markdown', 'mermaid', 'plantuml']);
4
+ const PLANTUML_BLOCK_OPENERS = [
5
+ [/^if\b.*\bthen\b/iu, 'endif'],
6
+ [/^while\b/iu, 'endwhile'],
7
+ [/^repeat\b(?!\s+while\b)/iu, 'repeat while'],
8
+ [/^switch\b/iu, 'endswitch'],
9
+ [/^fork\b(?!\s+again\b)/iu, 'end fork'],
10
+ [/^split\b(?!\s+again\b)/iu, 'end split'],
11
+ [/^(?:alt|opt|loop|par|critical|group|box|ref\s+over)\b/iu, 'end'],
12
+ [/^break\s+\S/iu, 'end'],
13
+ ];
14
+ const PLANTUML_MIDDLE_PATTERN = /^(?:else(?:if)?|and|fork\s+again|split\s+again|case|option)\b/iu;
15
+
16
+ function getPlantUmlOpaqueBlock(trimmedLine) {
17
+ if (trimmedLine.startsWith("/'")) {
18
+ return trimmedLine.slice(2).includes("'/")
19
+ ? { oneLine: true }
20
+ : { end: (line) => line.includes("'/") };
21
+ }
22
+ const tripleQuoteCount = trimmedLine.split('"""').length - 1;
23
+ if (tripleQuoteCount % 2 === 1) return { end: (line) => line.includes('"""') };
24
+ if (/^<style>.*<\/style>\s*$/iu.test(trimmedLine)) return { oneLine: true };
25
+ if (/^<style>\s*$/iu.test(trimmedLine)) return { end: (line) => /<\/style>/iu.test(line) };
26
+ if (/^!(?:if|ifdef|ifndef)\b/iu.test(trimmedLine)) {
27
+ let depth = 1;
28
+ return { end: (line) => {
29
+ if (/^!(?:if|ifdef|ifndef)\b/iu.test(line)) depth += 1;
30
+ if (/^!endif\b/iu.test(line)) depth -= 1;
31
+ return depth === 0;
32
+ } };
33
+ }
34
+ if (/^!(?:while|foreach)\b/iu.test(trimmedLine)) {
35
+ const endPattern = /^!while\b/iu.test(trimmedLine) ? /^!endwhile\b/iu : /^!endfor\b/iu;
36
+ return { end: (line) => endPattern.test(line) };
37
+ }
38
+ if (/^!(?:definelong|procedure|function)\b/iu.test(trimmedLine)) {
39
+ return { end: (line) => /^!end(?:definelong|procedure|function)\b/iu.test(line) };
40
+ }
41
+ if (/^!\w+/u.test(trimmedLine)) return { oneLine: true };
42
+ if (/^(?:[hr]?note|legend)\b(?!.*:\s*\S)/iu.test(trimmedLine)) {
43
+ return { end: (line) => /^(?:end\s+note|endlegend)\b/iu.test(line) };
44
+ }
45
+ if (/^(?:title|header|footer|caption)\s*$/iu.test(trimmedLine)) {
46
+ const blockName = trimmedLine.toLowerCase();
47
+ return { end: (line) => line.toLowerCase().replace(/\s/gu, '') === `end${blockName}` };
48
+ }
49
+ if (/^(?:json|yaml|salt|sprite)\b.*\{\s*$/iu.test(trimmedLine)) {
50
+ return { braceDepth: 1 };
51
+ }
52
+ return null;
53
+ }
54
+
55
+ function countPlantUmlBraces(line) {
56
+ let count = 0;
57
+ let escaped = false;
58
+ let quoted = false;
59
+ for (const character of line) {
60
+ if (escaped) {
61
+ escaped = false;
62
+ } else if (character === '\\') {
63
+ escaped = true;
64
+ } else if (character === '"') {
65
+ quoted = !quoted;
66
+ } else if (!quoted && character === '{') {
67
+ count += 1;
68
+ } else if (!quoted && character === '}') {
69
+ count -= 1;
70
+ }
71
+ }
72
+ return count;
73
+ }
74
+
75
+ function getPlantUmlCloser(trimmedLine) {
76
+ if (/^\}\s*$/u.test(trimmedLine)) return '}';
77
+ if (/^endif\b/iu.test(trimmedLine)) return 'endif';
78
+ if (/^endwhile\b/iu.test(trimmedLine)) return 'endwhile';
79
+ if (/^repeat\s+while\b/iu.test(trimmedLine)) return 'repeat while';
80
+ if (/^endswitch\b/iu.test(trimmedLine)) return 'endswitch';
81
+ if (/^end\s+fork\b/iu.test(trimmedLine)) return 'end fork';
82
+ if (/^end\s+split\b/iu.test(trimmedLine)) return 'end split';
83
+ if (/^end(?:\s+\w+)?\s*$/iu.test(trimmedLine)) return 'end';
84
+ return null;
85
+ }
86
+
87
+ function getPlantUmlOpener(trimmedLine) {
88
+ if (/\{\s*$/u.test(trimmedLine)) return '}';
89
+ return PLANTUML_BLOCK_OPENERS.find(([pattern]) => pattern.test(trimmedLine))?.[1] ?? null;
90
+ }
91
+
92
+ function indentPlantUml(source) {
93
+ const parts = String(source ?? '').split(/(\r\n|\n|\r)/u);
94
+ const stack = [];
95
+ let opaqueBlock = null;
96
+
97
+ for (let index = 0; index < parts.length; index += 2) {
98
+ const line = parts[index];
99
+ const content = line.replace(/^[\t ]*/u, '');
100
+ const trimmedLine = content.trimEnd();
101
+
102
+ if (opaqueBlock) {
103
+ if (opaqueBlock.braceDepth) {
104
+ opaqueBlock.braceDepth += countPlantUmlBraces(trimmedLine);
105
+ if (opaqueBlock.braceDepth <= 0) opaqueBlock = null;
106
+ } else if (opaqueBlock.end(trimmedLine)) {
107
+ opaqueBlock = null;
108
+ }
109
+ continue;
110
+ }
111
+
112
+ const nextOpaqueBlock = getPlantUmlOpaqueBlock(trimmedLine);
113
+ if (nextOpaqueBlock) {
114
+ opaqueBlock = nextOpaqueBlock.oneLine ? null : nextOpaqueBlock;
115
+ continue;
116
+ }
117
+ if (!trimmedLine) continue;
118
+
119
+ const closer = getPlantUmlCloser(trimmedLine);
120
+ const closesCurrentBlock = closer && stack.at(-1) === closer;
121
+ const isMiddle = PLANTUML_MIDDLE_PATTERN.test(trimmedLine) && stack.length > 0;
122
+ const opener = getPlantUmlOpener(trimmedLine);
123
+
124
+ if (stack.length > 0 || closesCurrentBlock || isMiddle || opener) {
125
+ const depth = Math.max(0, stack.length - (closesCurrentBlock || isMiddle ? 1 : 0));
126
+ parts[index] = `${' '.repeat(depth)}${content}`;
127
+ }
128
+ if (closesCurrentBlock) stack.pop();
129
+ if (opener) stack.push(opener);
130
+ }
131
+
132
+ return parts.join('');
133
+ }
134
+
135
+ export function canFormatDocument(filePath) {
136
+ return FORMATTABLE_FILE_KINDS.has(getVaultFileKind(filePath));
137
+ }
138
+
139
+ async function formatWithPrettier(source, parser) {
140
+ let pluginImport = import('prettier/plugins/markdown');
141
+ if (parser === 'yaml') pluginImport = import('prettier/plugins/yaml');
142
+ if (parser === 'html') pluginImport = import('prettier/plugins/html');
143
+
144
+ const [{ format }, plugin] = await Promise.all([
145
+ import('prettier/standalone'),
146
+ pluginImport,
147
+ ]);
148
+ const plugins = [plugin.default];
149
+
150
+ if (parser === 'mdx') {
151
+ const [babel, estree] = await Promise.all([
152
+ import('prettier/plugins/babel'),
153
+ import('prettier/plugins/estree'),
154
+ ]);
155
+ plugins.push(babel.default, estree.default);
156
+ }
157
+
158
+ return format(source, {
159
+ parser,
160
+ plugins,
161
+ proseWrap: 'preserve',
162
+ });
163
+ }
164
+
165
+ async function formatMermaid(source) {
166
+ const [{ default: mermaid }, { formatMermaid }] = await Promise.all([
167
+ import('mermaid'),
168
+ import('mermaid-formatter'),
169
+ ]);
170
+ if (!await mermaid.parse(source, { suppressErrors: true })) {
171
+ throw new Error('Mermaid syntax is invalid');
172
+ }
173
+
174
+ const formatted = formatMermaid(source, { indentSize: 2 });
175
+ if (!await mermaid.parse(formatted, { suppressErrors: true })) {
176
+ throw new Error('The formatter produced invalid Mermaid syntax');
177
+ }
178
+ return formatted;
179
+ }
180
+
181
+ export async function formatDocumentText(filePath, source) {
182
+ const kind = getVaultFileKind(filePath);
183
+
184
+ if (kind === 'mermaid') return formatMermaid(source);
185
+ if (kind === 'plantuml') return indentPlantUml(source);
186
+ if (kind === 'base') return formatWithPrettier(source, 'yaml');
187
+ if (kind === 'html') return formatWithPrettier(source, 'html');
188
+ if (kind === 'markdown') {
189
+ const parser = String(filePath).toLowerCase().endsWith('.mdx') ? 'mdx' : 'markdown';
190
+ return formatWithPrettier(source, parser);
191
+ }
192
+ return null;
193
+ }
@@ -0,0 +1,73 @@
1
+ import { stripVaultFileExtension } from '../../domain/file-kind.js';
2
+
3
+ function createRangeIndices(start, length) {
4
+ return Array.from({ length }, (_, index) => start + index);
5
+ }
6
+
7
+ function findFuzzyMatch(text, query) {
8
+ const indices = [];
9
+ let queryIndex = 0;
10
+ let score = 0;
11
+ let consecutiveBonus = 0;
12
+
13
+ for (let index = 0; index < text.length && queryIndex < query.length; index += 1) {
14
+ if (/\s/u.test(query[queryIndex]) && /[\s/\\_-]/u.test(text[index])) {
15
+ queryIndex += 1;
16
+ consecutiveBonus = 0;
17
+ continue;
18
+ }
19
+
20
+ if (text[index] !== query[queryIndex]) {
21
+ if (queryIndex > 0) score -= 0.25;
22
+ consecutiveBonus = 0;
23
+ continue;
24
+ }
25
+
26
+ queryIndex += 1;
27
+ indices.push(index);
28
+ consecutiveBonus += 1;
29
+ score += consecutiveBonus;
30
+
31
+ if (index === 0 || /[/\-_ ]/u.test(text[index - 1])) score += 5;
32
+ }
33
+
34
+ return queryIndex === query.length && score > 0 ? { indices, score } : null;
35
+ }
36
+
37
+ export function createFileSearchEntry(filePath) {
38
+ const displayName = stripVaultFileExtension(filePath);
39
+ const fileName = displayName.split('/').pop() || displayName;
40
+ const rawFileName = String(filePath ?? '').split('/').pop() || String(filePath ?? '');
41
+
42
+ return {
43
+ displayName,
44
+ fileName,
45
+ filePath,
46
+ lowerDisplayName: displayName.toLowerCase(),
47
+ lowerFileName: fileName.toLowerCase(),
48
+ lowerPath: String(filePath).toLowerCase(),
49
+ lowerRawFileName: rawFileName.toLowerCase(),
50
+ };
51
+ }
52
+
53
+ export function findFileSearchMatch(entry, query) {
54
+ const fileNameStart = entry.displayName.length - entry.fileName.length;
55
+ const searches = [
56
+ [entry.lowerFileName, fileNameStart, 100],
57
+ [entry.lowerDisplayName, 0, 50],
58
+ [entry.lowerRawFileName, fileNameStart, 40],
59
+ [entry.lowerPath, 0, 25],
60
+ ];
61
+
62
+ for (const [text, offset, baseScore] of searches) {
63
+ const index = text.indexOf(query);
64
+ if (index >= 0) {
65
+ return {
66
+ indices: createRangeIndices(index + offset, query.length),
67
+ score: baseScore + (1 / text.length),
68
+ };
69
+ }
70
+ }
71
+
72
+ return findFuzzyMatch(entry.lowerDisplayName, query);
73
+ }
@@ -0,0 +1,5 @@
1
+ export function commitButtonLabel(count) {
2
+ return count > 0
3
+ ? `Commit ${count} file${count === 1 ? '' : 's'}`
4
+ : 'Commit included files';
5
+ }
@@ -8,10 +8,12 @@ import {
8
8
  } from '../../domain/file-kind.js';
9
9
 
10
10
  export function normalizeVaultPathInput(value) {
11
- const segments = String(value ?? '')
12
- .trim()
13
- .replace(/\\/g, '/')
14
- .replace(/^\/+/u, '')
11
+ const normalized = String(value ?? '').trim().replace(/\\/g, '/');
12
+ if (!normalized || normalized.startsWith('/')) {
13
+ return '';
14
+ }
15
+
16
+ const segments = normalized
15
17
  .split('/')
16
18
  .map((segment) => segment.trim())
17
19
  .filter(Boolean);
@@ -1,3 +1,7 @@
1
+ import { createFileSearchEntry, findFileSearchMatch } from './file-search.js';
2
+
3
+ const MAX_VISIBLE_RESULTS = 30;
4
+
1
5
  /**
2
6
  * CodeMirror autocomplete source for [[wiki-links]].
3
7
  *
@@ -29,31 +33,28 @@ export function wikiLinkCompletions(getFileList) {
29
33
  return null;
30
34
  }
31
35
 
32
- const query = afterOpen.toLowerCase();
36
+ const query = afterOpen.trim().toLowerCase().replace(/\s+/gu, ' ');
33
37
  const from = line.from + openIndex + 2;
34
- const files = getFileList();
38
+ const entries = getFileList().map((filePath) => createFileSearchEntry(filePath));
39
+ const matches = query
40
+ ? entries
41
+ .map((entry) => ({ entry, match: findFileSearchMatch(entry, query) }))
42
+ .filter(({ match }) => match)
43
+ .sort((left, right) => right.match.score - left.match.score
44
+ || left.entry.lowerPath.localeCompare(right.entry.lowerPath))
45
+ : entries.map((entry) => ({ entry, match: null }));
35
46
 
36
- // Build completion options: show file paths without .md extension
37
- const options = files
38
- .map((filePath) => {
39
- const label = filePath.replace(/\.md$/i, '');
40
- // Match against full path and just the filename
41
- const fileName = label.split('/').pop();
42
- return { filePath, label, fileName };
43
- })
44
- .filter(({ label, fileName }) =>
45
- label.toLowerCase().includes(query) || fileName.toLowerCase().includes(query),
46
- )
47
- .map(({ label, fileName }) => ({
48
- label,
49
- // Show just the filename as detail when in a subdirectory
50
- detail: label.includes('/') ? ` ${fileName}` : undefined,
47
+ const options = matches
48
+ .slice(0, MAX_VISIBLE_RESULTS)
49
+ .map(({ entry }) => ({
50
+ label: entry.filePath,
51
+ displayLabel: entry.filePath.split('/').pop(),
52
+ detail: entry.displayName.slice(0, -entry.fileName.length - 1) || undefined,
51
53
  apply: (view, completion, from, to) => {
52
54
  // Replace from the query start to cursor, and also consume any trailing `]]`
53
- const docText = view.state.doc.toString();
54
55
  let end = to;
55
56
  // If there's already a `]]` right after cursor, consume it
56
- if (docText.slice(to, to + 2) === ']]') {
57
+ if (view.state.sliceDoc(to, to + 2) === ']]') {
57
58
  end = to + 2;
58
59
  }
59
60
  view.dispatch({
@@ -62,7 +63,6 @@ export function wikiLinkCompletions(getFileList) {
62
63
  });
63
64
  },
64
65
  type: 'text',
65
- boost: label.toLowerCase().startsWith(query) ? 1 : 0,
66
66
  }));
67
67
 
68
68
  if (options.length === 0) {
@@ -155,8 +155,13 @@ function teardownDrawioFrame() {
155
155
  }
156
156
 
157
157
  function buildDrawioFrameUrl() {
158
- const baseUrl = runtimeConfig.drawioBaseUrl || 'https://embed.diagrams.net';
159
- const url = new URL(baseUrl);
158
+ const fallbackUrl = 'https://embed.diagrams.net';
159
+ let url;
160
+ try {
161
+ url = new URL(runtimeConfig.drawioBaseUrl || fallbackUrl);
162
+ } catch {
163
+ url = new URL(fallbackUrl);
164
+ }
160
165
  url.searchParams.set('embed', '1');
161
166
  url.searchParams.set('proto', 'json');
162
167
  url.searchParams.set('spin', '1');
@@ -396,7 +401,7 @@ function handleLeaseState(nextState) {
396
401
  state.canClaim = Boolean(nextState?.canClaim);
397
402
 
398
403
  if (state.isEditor) {
399
- state.bannerText = 'Editing';
404
+ state.bannerText = 'Changes save automatically.';
400
405
  } else if (nextState?.hasHealthyHolder) {
401
406
  const holderName = nextState.holderName || 'Another user';
402
407
  state.bannerText = `${holderName} is editing this diagram. Read-only mode. Updates appear after they save.`;
@@ -441,6 +441,7 @@ function buildExcalidrawProps({ initialData, renderTopRightUI, viewModeEnabled }
441
441
  },
442
442
  onPointerDown: () => {
443
443
  initialViewportFitPending = false;
444
+ stopFollowingFromLocalInteraction();
444
445
  },
445
446
  renderTopRightUI,
446
447
  historyOptions: {
@@ -468,6 +469,32 @@ function buildExcalidrawProps({ initialData, renderTopRightUI, viewModeEnabled }
468
469
  }
469
470
 
470
471
  const diagramCommentsContext = React.createContext(null);
472
+ const presentationContext = React.createContext(null);
473
+
474
+ function PresentationToolbar() {
475
+ const context = React.useContext(presentationContext);
476
+ if (!context) {
477
+ return null;
478
+ }
479
+
480
+ const label = context.active ? 'Exit presentation' : 'Start presentation';
481
+ const icon = context.active ? '×' : '▶';
482
+ let title = context.active ? 'Exit presentation' : 'Present frames';
483
+ if (context.frameCount === 0) {
484
+ title = 'Add a frame to create slides';
485
+ }
486
+
487
+ return React.createElement('button', {
488
+ 'aria-label': label,
489
+ 'aria-pressed': context.active,
490
+ className: `diagram-comment-button excalidraw-presentation-toggle sidebar-trigger${context.active ? ' is-active' : ''}`,
491
+ 'data-testid': 'excalidraw-presentation-toggle',
492
+ disabled: context.frameCount === 0,
493
+ onClick: context.toggle,
494
+ title,
495
+ type: 'button',
496
+ }, React.createElement('span', { 'aria-hidden': 'true' }, icon));
497
+ }
471
498
 
472
499
  function DiagramCommentsToolbar() {
473
500
  const context = React.useContext(diagramCommentsContext);
@@ -517,7 +544,12 @@ function DiagramCommentsToolbar() {
517
544
 
518
545
  const DiagramCommentsExcalidraw = React.memo(function DiagramCommentsExcalidraw({ initialData = null, renderKey = 0, viewModeEnabled }) {
519
546
  const renderTopRightUI = React.useCallback(
520
- () => React.createElement(DiagramCommentsToolbar),
547
+ () => React.createElement('div', {
548
+ className: 'excalidraw-editor-toolbar',
549
+ }, [
550
+ React.createElement(PresentationToolbar, { key: 'presentation' }),
551
+ React.createElement(DiagramCommentsToolbar, { key: 'comments' }),
552
+ ]),
521
553
  [],
522
554
  );
523
555
 
@@ -565,7 +597,13 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
565
597
  const [drawerOpen, setDrawerOpen] = React.useState(false);
566
598
  const [draft, setDraft] = React.useState('');
567
599
  const [replyDraft, setReplyDraft] = React.useState('');
600
+ const [presentationFrameId, setPresentationFrameId] = React.useState(null);
568
601
  const [, setViewportRevision] = React.useState(0);
602
+ const presentationFrames = (
603
+ getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.() || []
604
+ ).filter((element) => element?.type === 'frame' && !element.isDeleted);
605
+ const presentationIndex = presentationFrames.findIndex((frame) => frame.id === presentationFrameId);
606
+ const presentationMode = presentationFrameId !== null;
569
607
 
570
608
  React.useEffect(() => {
571
609
  const api = getMountedExcalidrawAPI();
@@ -587,6 +625,76 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
587
625
 
588
626
  useCloseDiagramCommentsOnCanvas(drawerOpen, setDrawerOpen);
589
627
 
628
+ const goToPresentationFrame = React.useCallback((index) => {
629
+ const targetIndex = Math.min(Math.max(index, 0), presentationFrames.length - 1);
630
+ const frame = presentationFrames[targetIndex];
631
+ const api = getMountedExcalidrawAPI();
632
+ if (!api || !frame) {
633
+ return;
634
+ }
635
+
636
+ setPresentationFrameId(frame.id);
637
+ api.setViewport({
638
+ animation: true,
639
+ fit: 'contain',
640
+ offsets: { ui: true },
641
+ target: frame,
642
+ });
643
+ }, [presentationFrames]);
644
+
645
+ const togglePresentation = React.useCallback(() => {
646
+ if (presentationMode) {
647
+ setPresentationFrameId(null);
648
+ return;
649
+ }
650
+
651
+ setDrawerOpen(false);
652
+ setComposerOpen(false);
653
+ setActiveThreadId(null);
654
+ goToPresentationFrame(0);
655
+ }, [goToPresentationFrame, presentationMode]);
656
+
657
+ React.useEffect(() => {
658
+ if (!presentationFrameId || presentationIndex >= 0) {
659
+ return;
660
+ }
661
+
662
+ if (presentationFrames.length > 0) {
663
+ goToPresentationFrame(0);
664
+ } else {
665
+ setPresentationFrameId(null);
666
+ }
667
+ }, [goToPresentationFrame, presentationFrameId, presentationFrames.length, presentationIndex]);
668
+
669
+ React.useEffect(() => {
670
+ if (!presentationMode) {
671
+ return undefined;
672
+ }
673
+
674
+ const handlePresentationKeyDown = (event) => {
675
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) {
676
+ return;
677
+ }
678
+
679
+ if (event.key === 'Escape') {
680
+ event.preventDefault();
681
+ event.stopPropagation();
682
+ setPresentationFrameId(null);
683
+ } else if (event.key === 'ArrowRight') {
684
+ event.preventDefault();
685
+ event.stopPropagation();
686
+ goToPresentationFrame(presentationIndex + 1);
687
+ } else if (event.key === 'ArrowLeft') {
688
+ event.preventDefault();
689
+ event.stopPropagation();
690
+ goToPresentationFrame(presentationIndex - 1);
691
+ }
692
+ };
693
+
694
+ document.addEventListener('keydown', handlePresentationKeyDown, true);
695
+ return () => document.removeEventListener('keydown', handlePresentationKeyDown, true);
696
+ }, [goToPresentationFrame, presentationIndex, presentationMode]);
697
+
590
698
  React.useEffect(() => {
591
699
  if (activeThreadId && !threads.some((thread) => thread.id === activeThreadId)) {
592
700
  setActiveThreadId(null);
@@ -832,7 +940,8 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
832
940
  ]);
833
941
  };
834
942
 
835
- const renderCommentsOverlay = visible && room
943
+ const commentsVisible = visible && !presentationMode;
944
+ const renderCommentsOverlay = commentsVisible && room
836
945
  ? React.createElement('div', {
837
946
  'aria-label': 'Diagram comments overlay',
838
947
  className: 'diagram-comments-overlay',
@@ -864,7 +973,44 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
864
973
  ])
865
974
  : null;
866
975
 
867
- return React.createElement(diagramCommentsContext.Provider, {
976
+ const renderPresentationNavigation = presentationMode
977
+ ? React.createElement('div', {
978
+ 'aria-label': 'Presentation navigation',
979
+ className: 'excalidraw-presentation-navigation',
980
+ key: 'presentation-navigation',
981
+ role: 'toolbar',
982
+ }, [
983
+ React.createElement('button', {
984
+ 'aria-label': 'Previous slide',
985
+ className: 'diagram-comment-button',
986
+ disabled: presentationIndex <= 0,
987
+ key: 'previous',
988
+ onClick: () => goToPresentationFrame(presentationIndex - 1),
989
+ type: 'button',
990
+ }, '←'),
991
+ React.createElement('span', {
992
+ 'aria-live': 'polite',
993
+ className: 'excalidraw-presentation-status',
994
+ key: 'status',
995
+ }, `${presentationFrames[presentationIndex]?.name || `Slide ${presentationIndex + 1}`} · ${presentationIndex + 1} / ${presentationFrames.length}`),
996
+ React.createElement('button', {
997
+ 'aria-label': 'Next slide',
998
+ className: 'diagram-comment-button',
999
+ disabled: presentationIndex >= presentationFrames.length - 1,
1000
+ key: 'next',
1001
+ onClick: () => goToPresentationFrame(presentationIndex + 1),
1002
+ type: 'button',
1003
+ }, '→'),
1004
+ ])
1005
+ : null;
1006
+
1007
+ return React.createElement(presentationContext.Provider, {
1008
+ value: {
1009
+ active: presentationMode,
1010
+ frameCount: presentationFrames.length,
1011
+ toggle: togglePresentation,
1012
+ },
1013
+ }, React.createElement(diagramCommentsContext.Provider, {
868
1014
  value: {
869
1015
  canWrite,
870
1016
  drawerOpen,
@@ -873,17 +1019,18 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
873
1019
  selectedElement,
874
1020
  threads,
875
1021
  toggleDrawer: () => setDrawerOpen((open) => !open),
876
- visible,
1022
+ visible: commentsVisible,
877
1023
  },
878
1024
  }, [
879
1025
  React.createElement(DiagramCommentsExcalidraw, {
880
1026
  initialData,
881
1027
  key: 'excalidraw',
882
1028
  renderKey: editorRenderKey,
883
- viewModeEnabled: getDocumentViewState().viewModeEnabled,
1029
+ viewModeEnabled: presentationMode || getDocumentViewState().viewModeEnabled,
884
1030
  }),
885
1031
  renderCommentsOverlay,
886
- ]);
1032
+ renderPresentationNavigation,
1033
+ ]));
887
1034
  }
888
1035
 
889
1036
  function renderExcalidrawApp({ initialData } = {}) {
@@ -1379,7 +1526,17 @@ function postToParent(type, payload = {}) {
1379
1526
  window.parent.postMessage({ source: 'excalidraw-editor', type, ...payload }, parentOrigin);
1380
1527
  }
1381
1528
 
1529
+ function stopFollowingFromLocalInteraction() {
1530
+ if (!pendingHostFollowPeerId && !followedSocketId) {
1531
+ return;
1532
+ }
1533
+
1534
+ applyHostFollowRequest(null);
1535
+ postToParent('stop-following');
1536
+ }
1537
+
1382
1538
  function handleQuickSwitcherKeyDown(event) {
1539
+ stopFollowingFromLocalInteraction();
1383
1540
  if (!isPlainQuickSwitcherShortcut(event)) {
1384
1541
  return;
1385
1542
  }