collabmd 0.1.44 → 0.1.46

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 (291) hide show
  1. package/README.md +39 -19
  2. package/bin/collabmd.js +27 -1
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js +1 -0
  4. package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
  5. package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +41 -0
  6. package/dist/client/assets/{chunk-CZQZEIFA-DT3N9fo7.js → chunk-YGJVY4SA-CjSe7Bx7.js} +2 -2
  7. package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
  8. package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
  9. package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +7 -0
  10. package/dist/client/assets/dist-B5HTH2BO.js +1 -0
  11. package/dist/client/assets/{dist-DhLvC8DS.js → dist-B5Jf1SLZ.js} +2 -2
  12. package/dist/client/assets/{dist-CnL7Y2sM.js → dist-BDt3rz7l.js} +1 -1
  13. package/dist/client/assets/dist-BEwK4csK.js +1 -0
  14. package/dist/client/assets/dist-BKW4PALF.js +1 -0
  15. package/dist/client/assets/dist-BWcHHOx-.js +1 -0
  16. package/dist/client/assets/{dist-CYZmbokb.js → dist-BalScOwZ.js} +9 -9
  17. package/dist/client/assets/dist-Bf8f4HmM.js +6 -0
  18. package/dist/client/assets/dist-BjHJDtyU.js +1 -0
  19. package/dist/client/assets/dist-Bm6pf9Bl.js +1 -0
  20. package/dist/client/assets/dist-BoVBdSjk.js +1 -0
  21. package/dist/client/assets/dist-CC5IrQMG.js +1 -0
  22. package/dist/client/assets/{dist-C11hHqv_.js → dist-CCP_AKPG.js} +12 -12
  23. package/dist/client/assets/dist-CZgOmxK6.js +1 -0
  24. package/dist/client/assets/{dist-D2AQ-76g.js → dist-Cqhv5LAk.js} +1 -1
  25. package/dist/client/assets/{dist-MvcV4190.js → dist-D-Z6FLem.js} +1 -1
  26. package/dist/client/assets/dist-DLMiIsNC.js +11 -0
  27. package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZKu9Cs8.js} +1 -1
  28. package/dist/client/assets/dist-DaYdhcVs.js +1 -0
  29. package/dist/client/assets/dist-FWJsgGYI.js +1 -0
  30. package/dist/client/assets/dist-Kh2COra_.js +1 -0
  31. package/dist/client/assets/{dist-JYEDRrPT.js → dist-PDZ3bSqm.js} +1 -1
  32. package/dist/client/assets/{dist-Drp8uu5J.js → dist-TjMh8n1u.js} +1 -1
  33. package/dist/client/assets/{dist-DGb27Dl6.js → dist-dtAdgL39.js} +1 -1
  34. package/dist/client/assets/dist-jv4WdPnK.js +9 -0
  35. package/dist/client/assets/{dist-DK5Gs2wa.js → dist-rGBmqxC7.js} +1 -1
  36. package/dist/client/assets/{dist-DCzuX1AF.js → dist-shrl6K_0.js} +1 -1
  37. package/dist/client/assets/dist-tOaegDYS2.js +1 -0
  38. package/dist/client/assets/dist-ule2n6ak.js +1 -0
  39. package/dist/client/assets/dist-xElaxbia.js +1 -0
  40. package/dist/client/assets/drawio-editor-FzY1cVw8.js +9 -0
  41. package/dist/client/assets/drawioEditor-BjiKJqlc.js +2 -0
  42. package/dist/client/assets/editor-session-Cm9ekeNo.js +54 -0
  43. package/dist/client/assets/embedded-editor-base-CwTkk911.css +1 -0
  44. package/dist/client/assets/{en-GADW4C6J-DQ6zyCEM.js → en-7NFCYGKX-BrgYZAEe.js} +1 -1
  45. package/dist/client/assets/{preview-render-profile-BZzGSg6K.js → es-DXGV3RHd.js} +13 -13
  46. package/dist/client/assets/excalidraw-editor-B46zsTFI.js +9 -0
  47. package/dist/client/assets/excalidraw-editor-Bt7gMSoh.css +1 -0
  48. package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
  49. package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +2 -0
  50. package/dist/client/assets/export-page-A7NHawJY.js +39 -0
  51. package/dist/client/assets/exportDocument-B2L8fe34.css +1 -0
  52. package/dist/client/assets/exportDocument-BaqcOAAW.js +2 -0
  53. package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
  54. package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
  55. package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +1 -0
  56. package/dist/client/assets/index-ClbjMBVt.js +2 -0
  57. package/dist/client/assets/index-Pum8ET-5.css +1 -0
  58. package/dist/client/assets/main-B3uPyqic.js +893 -0
  59. package/dist/client/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
  60. package/dist/client/assets/pdf-BL9Pr2D5.js +54 -0
  61. package/dist/client/assets/pdf.worker-CLesOks4.mjs +64857 -0
  62. package/dist/client/assets/pdf_viewer-FVkkThnz.js +551 -0
  63. package/dist/client/assets/preview-diagram-utils-txBCoW8d.js +1 -0
  64. package/dist/client/assets/{preview-render-compiler-BSaQO_p3.js → preview-render-compiler-BD-XhkS2.js} +2 -2
  65. package/dist/client/assets/preview-render-compiler-BKedaMxx.js +1 -0
  66. package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
  67. package/dist/client/assets/{preview-render-worker-B42LWvrD.js → preview-render-worker-CiPqc9n6.js} +20 -20
  68. package/dist/client/assets/prod-Dy20X7eT.js +1 -0
  69. package/dist/client/assets/prod-hnwh7wRM.js +177 -0
  70. package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +12 -0
  71. package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
  72. package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
  73. package/dist/client/assets/{yjs-provider-reset-guard-Cox4nxwu.js → yjs-provider-reset-guard-DE0vINDP.js} +1 -1
  74. package/dist/client/drawio-editor.html +3 -2
  75. package/dist/client/excalidraw-editor.html +3 -2
  76. package/dist/client/export-document.html +4 -3
  77. package/dist/client/index.html +109 -27
  78. package/docker-compose.yml +42 -0
  79. package/package.json +26 -15
  80. package/scripts/local-structurizr-compose.mjs +101 -0
  81. package/src/client/app/drawio-editor-entry.js +0 -3
  82. package/src/client/app/drawio-editor.html +1 -0
  83. package/src/client/app/excalidraw-editor-entry.js +0 -3
  84. package/src/client/app/excalidraw-editor.html +1 -0
  85. package/src/client/app/export-document-entry.js +0 -3
  86. package/src/client/app/export-document.html +1 -0
  87. package/src/client/app/index.html +106 -24
  88. package/src/client/app/main-entry.js +0 -5
  89. package/src/client/application/app-shell/chat-feature.js +180 -124
  90. package/src/client/application/app-shell/comments-feature.js +20 -11
  91. package/src/client/application/app-shell/git-feature.js +16 -9
  92. package/src/client/application/app-shell/presence-feature.js +18 -11
  93. package/src/client/application/app-shell/ui-feature-shell.js +78 -17
  94. package/src/client/application/app-shell/ui-feature-sidebar.js +74 -1
  95. package/src/client/application/app-shell/ui-feature-tab-activity.js +2 -2
  96. package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
  97. package/src/client/application/app-shell/workspace-feature.js +27 -84
  98. package/src/client/application/app-shell-elements.js +9 -1
  99. package/src/client/application/diagram-chrome.js +161 -166
  100. package/src/client/application/diagram-preview-export.js +7 -34
  101. package/src/client/application/diagram-preview-hydrator.js +1 -19
  102. package/src/client/application/markdown-frontmatter.js +0 -2
  103. package/src/client/application/mermaid-preview-hydrator.js +1 -14
  104. package/src/client/application/pdf-preview-controller.js +992 -0
  105. package/src/client/application/plantuml-preview-hydrator.js +0 -10
  106. package/src/client/application/preview-diagram-utils.js +31 -2
  107. package/src/client/application/preview-render-compiler.js +2 -1
  108. package/src/client/application/preview-renderer.js +2 -2
  109. package/src/client/application/quick-switcher-loader.js +3 -1
  110. package/src/client/application/structurizr-preview-controller.js +395 -0
  111. package/src/client/application/workspace-coordinator.js +25 -3
  112. package/src/client/application/workspace-preview-controller.js +91 -5
  113. package/src/client/bootstrap/collabmd-app-shell.js +155 -43
  114. package/src/client/browser-utils.js +44 -0
  115. package/src/client/domain/excalidraw-collaboration.js +0 -2
  116. package/src/client/domain/room.js +38 -0
  117. package/src/client/domain/runtime-paths.js +26 -5
  118. package/src/client/domain/structurizr-language.js +99 -0
  119. package/src/client/domain/vault-paths.js +48 -0
  120. package/src/client/excalidraw-editor.js +833 -51
  121. package/src/client/export/export-pipeline.js +6 -34
  122. package/src/client/infrastructure/auth-client.js +4 -10
  123. package/src/client/infrastructure/browser-notification-port.js +33 -20
  124. package/src/client/infrastructure/browser-preferences-port.js +46 -9
  125. package/src/client/infrastructure/comment-thread-store.js +34 -13
  126. package/src/client/infrastructure/editor-session.js +10 -0
  127. package/src/client/infrastructure/editor-view-adapter.js +133 -5
  128. package/src/client/infrastructure/excalidraw-room-client.js +103 -14
  129. package/src/client/infrastructure/git-api-client.js +5 -0
  130. package/src/client/infrastructure/lobby-presence.js +1 -0
  131. package/src/client/infrastructure/vault-api-client.js +51 -0
  132. package/src/client/infrastructure/workspace-sync-client.js +13 -1
  133. package/src/client/presentation/comment-overview-controller.js +18 -53
  134. package/src/client/presentation/comment-ui/comment-ui-card.js +1 -12
  135. package/src/client/presentation/comment-ui/comment-ui-layout.js +242 -219
  136. package/src/client/presentation/comment-ui/comment-ui-render.js +32 -45
  137. package/src/client/presentation/comment-ui/comment-ui-shared.js +68 -13
  138. package/src/client/presentation/comment-ui-controller.js +5 -1
  139. package/src/client/presentation/drawio-embed-controller.js +9 -102
  140. package/src/client/presentation/drawio-viewer.js +118 -0
  141. package/src/client/presentation/excalidraw-embed-controller.js +74 -8
  142. package/src/client/presentation/file-action-controller.js +134 -0
  143. package/src/client/presentation/file-explorer-controller.js +33 -3
  144. package/src/client/presentation/file-explorer-view.js +59 -34
  145. package/src/client/presentation/file-icon-svg.js +40 -0
  146. package/src/client/presentation/file-tree-state.js +12 -4
  147. package/src/client/presentation/git-diff-view-controller.js +731 -3
  148. package/src/client/presentation/outline-controller.js +86 -0
  149. package/src/client/presentation/quick-switcher-controller.js +257 -74
  150. package/src/client/presentation/quick-switcher-text-search.js +3 -3
  151. package/src/client/presentation/toast-controller.js +3 -0
  152. package/src/client/styles/components/feedback.css +6 -0
  153. package/src/client/styles/components/navigation.css +11 -1
  154. package/src/client/styles/features/collaboration-chat.css +49 -53
  155. package/src/client/styles/features/collaboration-presence.css +9 -7
  156. package/src/client/styles/features/comment-card.css +25 -11
  157. package/src/client/styles/features/comment-overlays.css +0 -4
  158. package/src/client/styles/features/comment-overview.css +44 -21
  159. package/src/client/styles/features/comments-drawer.css +2 -100
  160. package/src/client/styles/features/diagram-preview.css +81 -18
  161. package/src/client/styles/features/diff-special.css +309 -0
  162. package/src/client/styles/features/embedded-preview.css +8 -33
  163. package/src/client/styles/features/quick-switcher.css +8 -0
  164. package/src/client/styles/foundation/themes.css +6 -0
  165. package/src/client/styles/foundation/tokens.css +1 -0
  166. package/src/client/styles/layout/editor-page.css +40 -0
  167. package/src/client/styles/layout/responsive.css +31 -1
  168. package/src/client/styles/layout/shell.css +383 -7
  169. package/src/client/styles/layout/sidebar.css +47 -15
  170. package/src/client/styles/layout/view-modes.css +1 -0
  171. package/src/client/styles/overrides/codemirror.css +217 -15
  172. package/src/client/styles/style.css +1 -0
  173. package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
  174. package/src/domain/comment-threads.js +63 -5
  175. package/src/domain/excalidraw-room-codec.js +6 -4
  176. package/src/domain/file-kind.js +35 -3
  177. package/src/server/application/workspace-reconciliation.js +1 -1
  178. package/src/server/config/env.js +79 -0
  179. package/src/server/create-app-server.js +14 -3
  180. package/src/server/domain/bases/base-definition.js +1 -1
  181. package/src/server/domain/bases/base-expression-runtime.js +1 -4
  182. package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
  183. package/src/server/domain/bases/base-query-service.js +1 -77
  184. package/src/server/domain/bases/base-transform.js +1 -4
  185. package/src/server/domain/collaboration/collaboration-room.js +55 -21
  186. package/src/server/domain/comment-overview.js +25 -8
  187. package/src/server/domain/docx-exporter.js +24 -33
  188. package/src/server/domain/ripgrep-search-service.js +7 -1
  189. package/src/server/domain/workspace-state.js +32 -11
  190. package/src/server/infrastructure/git/diff-service.js +29 -13
  191. package/src/server/infrastructure/git/git-service.js +61 -10
  192. package/src/server/infrastructure/git/history-service.js +96 -29
  193. package/src/server/infrastructure/git/image-mime.js +14 -0
  194. package/src/server/infrastructure/git/path-utils.js +1 -1
  195. package/src/server/infrastructure/git/responses.js +0 -95
  196. package/src/server/infrastructure/git/untracked-files.js +28 -1
  197. package/src/server/infrastructure/github/github-app-client.js +22 -15
  198. package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
  199. package/src/server/infrastructure/http/create-request-handler.js +18 -31
  200. package/src/server/infrastructure/http/create-static-handler.js +5 -2
  201. package/src/server/infrastructure/http/create-structurizr-api-handler.js +145 -0
  202. package/src/server/infrastructure/http/create-vault-api-command-handler.js +70 -8
  203. package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
  204. package/src/server/infrastructure/http/http-errors.js +7 -1
  205. package/src/server/infrastructure/http/http-request-helpers.js +22 -0
  206. package/src/server/infrastructure/persistence/path-utils.js +1 -1
  207. package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
  208. package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
  209. package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +374 -0
  210. package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
  211. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
  212. package/dist/client/assets/bases-preview-controller-2pLHk0uk.js +0 -143
  213. package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
  214. package/dist/client/assets/button-BCEiw4Rw.js +0 -1
  215. package/dist/client/assets/chunk-AXR3VHVW-0GTk_evH.js +0 -41
  216. package/dist/client/assets/comment-threads-D-iKphIp.js +0 -4
  217. package/dist/client/assets/diagram-action-icons-aa_KOv9h.js +0 -36
  218. package/dist/client/assets/dist-BH87JoHx.js +0 -11
  219. package/dist/client/assets/dist-BIkPRZO7.js +0 -6
  220. package/dist/client/assets/dist-BqCsFpx4.js +0 -1
  221. package/dist/client/assets/dist-C2JHCt8U.js +0 -1
  222. package/dist/client/assets/dist-CZjTtmTu.js +0 -1
  223. package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
  224. package/dist/client/assets/dist-CqQEC8gR.js +0 -1
  225. package/dist/client/assets/dist-D4Rzqod1.js +0 -1
  226. package/dist/client/assets/dist-D5sn-mwk.js +0 -9
  227. package/dist/client/assets/dist-D7G-n87v.js +0 -1
  228. package/dist/client/assets/dist-DRqOrNgN.js +0 -1
  229. package/dist/client/assets/dist-DetkMS_4.js +0 -1
  230. package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
  231. package/dist/client/assets/dist-DtbjNB28.js +0 -1
  232. package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
  233. package/dist/client/assets/dist-GrygD2zE.js +0 -1
  234. package/dist/client/assets/dist-HmEpWUow.js +0 -1
  235. package/dist/client/assets/dist-RTa81Km7.js +0 -1
  236. package/dist/client/assets/drawio-editor-DDvFoarP.js +0 -9
  237. package/dist/client/assets/drawio-embed-controller-BD1GTRwn.js +0 -1
  238. package/dist/client/assets/drawioEditor-Bu5grlH5.js +0 -2
  239. package/dist/client/assets/editor-session-DGlpiAsK.js +0 -16
  240. package/dist/client/assets/embedded-editor-base-C9roKYpW.css +0 -1
  241. package/dist/client/assets/excalidraw-editor-BHu6k8d4.js +0 -9
  242. package/dist/client/assets/excalidraw-editor-Cr_184EF.css +0 -1
  243. package/dist/client/assets/excalidraw-embed-controller-y2fenN_g.js +0 -1
  244. package/dist/client/assets/excalidraw-embed-reconciler-DGiagExg.js +0 -1
  245. package/dist/client/assets/excalidraw-scene-BvsQ6mLP.js +0 -176
  246. package/dist/client/assets/excalidrawEditor-4vFE--Uw.js +0 -2
  247. package/dist/client/assets/export-page-Cby8JzPO.js +0 -39
  248. package/dist/client/assets/exportDocument-C9v-bc7I.css +0 -1
  249. package/dist/client/assets/exportDocument-CRHCfsdo.js +0 -2
  250. package/dist/client/assets/file-history-view-controller-BDjEV9xJ.js +0 -112
  251. package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
  252. package/dist/client/assets/git-diff-view-controller-DgdM1aqb.js +0 -150
  253. package/dist/client/assets/git-panel-controller-BDTR2HVp.js +0 -207
  254. package/dist/client/assets/image-XGD7JQR6-XDUWHRpO.js +0 -1
  255. package/dist/client/assets/index-7xOyXDhP.css +0 -1
  256. package/dist/client/assets/index-Bz1KuQ6y.js +0 -2
  257. package/dist/client/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  258. package/dist/client/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  259. package/dist/client/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  260. package/dist/client/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  261. package/dist/client/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  262. package/dist/client/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  263. package/dist/client/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  264. package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  265. package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  266. package/dist/client/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  267. package/dist/client/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  268. package/dist/client/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  269. package/dist/client/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  270. package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  271. package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  272. package/dist/client/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  273. package/dist/client/assets/main-Ci07m9oP.js +0 -152
  274. package/dist/client/assets/preview-diagram-utils-n_bbJA9V.js +0 -1
  275. package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
  276. package/dist/client/assets/quick-switcher-controller-CoiYvllm.js +0 -12
  277. package/dist/client/assets/room-CDrmIdDt.js +0 -1
  278. package/dist/client/assets/runtime-config-CDgeuPWN.js +0 -1
  279. package/dist/client/assets/runtime-config-loader-BHGniHP5.js +0 -1
  280. package/dist/client/assets/runtime-paths-DvpihCEq.js +0 -1
  281. package/dist/client/assets/segmented-control-DJrHSz8M.js +0 -1
  282. package/dist/client/assets/vault-api-client-sLB2bo56.js +0 -1
  283. package/dist/client/assets/vault-paths-CVkuh0j-.js +0 -5
  284. package/src/client/bootstrap/lazy-controller-feature.js +0 -433
  285. package/src/client/infrastructure/backlinks-api-client.js +0 -16
  286. package/src/client/infrastructure/plantuml-api-client.js +0 -19
  287. package/src/client/infrastructure/runtime-config-loader.js +0 -56
  288. package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
  289. package/src/server/infrastructure/git/command-runner.js +0 -49
  290. package/src/server/infrastructure/git/errors.js +0 -8
  291. package/src/server/infrastructure/http/create-esm-proxy-handler.js +0 -130
@@ -2,12 +2,16 @@ import { createAuthApiHandler } from './create-auth-api-handler.js';
2
2
  import { createGitApiCommandHandler } from './create-git-api-command-handler.js';
3
3
  import { createGitApiQueryHandler } from './create-git-api-query-handler.js';
4
4
  import { createHostedApiHandler } from './create-hosted-api-handler.js';
5
- import { createEsmProxyHandler } from './create-esm-proxy-handler.js';
6
5
  import { createPlantUmlApiHandler } from './create-plantuml-api-handler.js';
7
6
  import { createStaticHandler } from './create-static-handler.js';
7
+ import { createStructurizrApiHandler } from './create-structurizr-api-handler.js';
8
8
  import { createVaultApiCommandHandler } from './create-vault-api-command-handler.js';
9
9
  import { createVaultApiQueryHandler } from './create-vault-api-query-handler.js';
10
10
  import { parseJsonBody } from './request-body.js';
11
+ import {
12
+ createRequestUrlWithPathname,
13
+ stripBasePath,
14
+ } from './http-request-helpers.js';
11
15
  import {
12
16
  applyCorsHeaders,
13
17
  jsonResponse,
@@ -17,35 +21,13 @@ import {
17
21
  WRITE_METHODS,
18
22
  } from './http-response.js';
19
23
 
20
- function stripBasePath(pathname, basePath) {
21
- if (!basePath) {
22
- return pathname;
23
- }
24
-
25
- if (pathname === basePath) {
26
- return '/';
27
- }
28
-
29
- if (pathname.startsWith(`${basePath}/`)) {
30
- return pathname.slice(basePath.length) || '/';
31
- }
32
-
33
- return pathname;
34
- }
35
-
36
- function createRequestUrlWithPathname(requestUrl, pathname) {
37
- const nextUrl = new URL(requestUrl.toString());
38
- nextUrl.pathname = pathname || '/';
39
- return nextUrl;
40
- }
41
-
42
24
  export function createRequestHandler(
43
25
  config,
44
26
  authService,
45
27
  vaultFileStore,
46
28
  backlinkIndex,
47
29
  baseQueryService = null,
48
- docxExporter = null,
30
+ renderDocx = null,
49
31
  roomRegistry = null,
50
32
  plantUmlRenderer = null,
51
33
  gitService = null,
@@ -55,8 +37,8 @@ export function createRequestHandler(
55
37
  fileSystemSyncService = null,
56
38
  hostedWorkspaceService = null,
57
39
  githubSetupFlow = null,
40
+ structurizrWorkspaceService = null,
58
41
  ) {
59
- const handleEsmProxy = createEsmProxyHandler();
60
42
  const handleStaticRequest = createStaticHandler(config, authService, searchService);
61
43
  const handleAuthApi = createAuthApiHandler({ authService });
62
44
  const handleHostedApi = createHostedApiHandler({
@@ -80,12 +62,17 @@ export function createRequestHandler(
80
62
  });
81
63
  const handleVaultApiCommand = createVaultApiCommandHandler({
82
64
  backlinkIndex,
83
- docxExporter,
65
+ maxPdfUploadBytes: config.maxPdfUploadBytes,
66
+ renderDocx,
84
67
  roomRegistry,
85
68
  vaultFileStore,
86
69
  workspaceMutationCoordinator,
87
70
  });
88
71
  const handlePlantUmlApi = createPlantUmlApiHandler({ plantUmlRenderer });
72
+ const handleStructurizrApi = createStructurizrApiHandler({
73
+ basePath: config.basePath,
74
+ service: structurizrWorkspaceService,
75
+ });
89
76
 
90
77
  function handleBasePathRedirect(req, res, originalRequestUrl) {
91
78
  if (
@@ -195,11 +182,7 @@ export function createRequestHandler(
195
182
  return;
196
183
  }
197
184
 
198
- if (await handleEsmProxy(req, res, requestUrl)) {
199
- return;
200
- }
201
-
202
- if (requestUrl.pathname.startsWith('/api/')) {
185
+ if (requestUrl.pathname.startsWith('/api/') || handleStructurizrApi.requiresAuthorization(requestUrl)) {
203
186
  const authorization = authService.authorizeApiRequest(req);
204
187
  if (!authorization.ok) {
205
188
  jsonResponse(req, res, authorization.statusCode, authorization.body);
@@ -215,6 +198,10 @@ export function createRequestHandler(
215
198
  }
216
199
  }
217
200
 
201
+ if (await handleStructurizrApi(req, res, requestUrl)) {
202
+ return;
203
+ }
204
+
218
205
  if (requestUrl.pathname.startsWith('/api/')) {
219
206
  if (await handleVaultApiQuery(req, res, requestUrl)) {
220
207
  return;
@@ -7,12 +7,13 @@ const CONTENT_TYPES = {
7
7
  '.css': 'text/css; charset=utf-8',
8
8
  '.html': 'text/html; charset=utf-8',
9
9
  '.js': 'text/javascript; charset=utf-8',
10
+ '.mjs': 'text/javascript; charset=utf-8',
10
11
  '.json': 'application/json; charset=utf-8',
11
12
  '.svg': 'image/svg+xml',
12
13
  };
13
14
 
14
15
  function isVersionedAssetPath(pathname = '') {
15
- return /-[A-Za-z0-9_-]{8,}\.(?:css|js|woff2?|ttf|svg)$/u.test(pathname);
16
+ return /-[A-Za-z0-9_-]{8,}\.(?:css|js|mjs|woff2?|ttf|svg)$/u.test(pathname);
16
17
  }
17
18
 
18
19
  function getStaticCacheControl(pathname = '', extension = '') {
@@ -33,7 +34,7 @@ function getStaticCacheControl(pathname = '', extension = '') {
33
34
  return 'public, max-age=31536000, immutable';
34
35
  }
35
36
 
36
- if (extension === '.css' || extension === '.js') {
37
+ if (extension === '.css' || extension === '.js' || extension === '.mjs') {
37
38
  return 'public, max-age=0, must-revalidate';
38
39
  }
39
40
 
@@ -53,6 +54,7 @@ function buildRuntimeConfig({
53
54
  nodeEnv,
54
55
  publicWsBaseUrl,
55
56
  search,
57
+ structurizr,
56
58
  wikiLinkAutoCreate,
57
59
  wsBasePath,
58
60
  }) {
@@ -68,6 +70,7 @@ function buildRuntimeConfig({
68
70
  },
69
71
  publicWsBaseUrl,
70
72
  search,
73
+ structurizrEnabled: Boolean(structurizr?.enabled ?? structurizr?.serverUrl),
71
74
  wikiLinkAutoCreate,
72
75
  wsBasePath,
73
76
  })};\n`;
@@ -0,0 +1,145 @@
1
+ import { readFile } from 'node:fs/promises';
2
+
3
+ import { getRequestErrorStatusCode } from './http-errors.js';
4
+ import { jsonResponse, sendResponse, textResponse } from './http-response.js';
5
+ import { parseJsonBody } from './request-body.js';
6
+
7
+ const EMBED_STYLESHEET_PATH = new URL('../structurizr/structurizr-embed.css', import.meta.url);
8
+
9
+ function isSyncRequest(requestUrl, req) {
10
+ return requestUrl.pathname === '/api/structurizr/sync' && req.method === 'POST';
11
+ }
12
+
13
+ function isEmbedStylesheetRequest(requestUrl) {
14
+ return requestUrl.pathname === '/api/structurizr/embed.css';
15
+ }
16
+
17
+ function rewriteBasePath(body, contentType, basePath, { injectEmbedStylesheet = false } = {}) {
18
+ if (!/(?:text\/(?:html|css)|javascript|ecmascript)/iu.test(contentType)) {
19
+ return body;
20
+ }
21
+
22
+ let source = body.toString('utf8');
23
+ if (basePath) {
24
+ source = source.replace(
25
+ /(["'`(=])\/(static|workspace|api\/workspace)(?=[/"'`?#)])/gu,
26
+ `$1${basePath}/$2`,
27
+ );
28
+ }
29
+
30
+ if (injectEmbedStylesheet && /text\/html/iu.test(contentType)) {
31
+ const normalizedBasePath = String(basePath || '').replace(/\/+$/u, '');
32
+ const stylesheetPath = `${normalizedBasePath}/api/structurizr/embed.css`;
33
+ source = source.replace(
34
+ /<head(?:\s[^>]*)?>/iu,
35
+ (head) => `${head}\n<link rel="stylesheet" href="${stylesheetPath}">`,
36
+ );
37
+ }
38
+
39
+ return Buffer.from(source);
40
+ }
41
+
42
+ export function createStructurizrApiHandler({ basePath = '', service = null } = {}) {
43
+ async function handleRequest(req, res, requestUrl) {
44
+ if (isSyncRequest(requestUrl, req)) {
45
+ try {
46
+ const body = await parseJsonBody(req);
47
+ if (!body || typeof body.path !== 'string') {
48
+ jsonResponse(req, res, 400, { error: 'Missing Structurizr workspace path' });
49
+ return true;
50
+ }
51
+
52
+ if (!service?.isEnabled()) {
53
+ jsonResponse(req, res, 503, { error: 'Structurizr renderer is not configured' });
54
+ return true;
55
+ }
56
+
57
+ jsonResponse(req, res, 200, await service.sync({
58
+ content: body.source,
59
+ rootPath: body.path,
60
+ }));
61
+ } catch (error) {
62
+ const statusCode = getRequestErrorStatusCode(error) || Number(error?.statusCode) || 502;
63
+ if (statusCode >= 500) {
64
+ console.error('[api] Failed to sync Structurizr workspace:', error.message);
65
+ }
66
+ jsonResponse(req, res, statusCode, {
67
+ code: error?.requestCode,
68
+ error: error instanceof Error ? error.message : 'Failed to sync Structurizr workspace',
69
+ });
70
+ }
71
+ return true;
72
+ }
73
+
74
+ if (isEmbedStylesheetRequest(requestUrl)) {
75
+ if (!service?.isEnabled()) {
76
+ return false;
77
+ }
78
+
79
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
80
+ textResponse(req, res, 405, 'Method Not Allowed', { Allow: 'GET, HEAD' });
81
+ return true;
82
+ }
83
+
84
+ try {
85
+ sendResponse(req, res, {
86
+ body: await readFile(EMBED_STYLESHEET_PATH),
87
+ headers: {
88
+ 'Cache-Control': 'no-store',
89
+ 'Content-Type': 'text/css; charset=utf-8',
90
+ },
91
+ statusCode: 200,
92
+ });
93
+ } catch (error) {
94
+ console.error('[http] Failed to serve Structurizr embed stylesheet:', error.message);
95
+ textResponse(req, res, 500, 'Internal Server Error');
96
+ }
97
+ return true;
98
+ }
99
+
100
+ if (!service?.isProxyPath(requestUrl.pathname)) {
101
+ return false;
102
+ }
103
+
104
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
105
+ textResponse(req, res, 405, 'Method Not Allowed', { Allow: 'GET, HEAD' });
106
+ return true;
107
+ }
108
+
109
+ try {
110
+ const response = await service.proxy(requestUrl, {
111
+ headers: req.headers,
112
+ method: req.method,
113
+ });
114
+ if (!response) {
115
+ return false;
116
+ }
117
+
118
+ sendResponse(req, res, {
119
+ body: rewriteBasePath(response.body, response.contentType, basePath, {
120
+ injectEmbedStylesheet: requestUrl.pathname === '/workspace/1/diagrams',
121
+ }),
122
+ headers: {
123
+ 'Cache-Control': requestUrl.pathname.startsWith('/static/')
124
+ ? 'public, max-age=31536000, immutable'
125
+ : 'no-store',
126
+ 'Content-Type': response.contentType,
127
+ },
128
+ statusCode: response.statusCode,
129
+ });
130
+ } catch (error) {
131
+ const statusCode = getRequestErrorStatusCode(error) || Number(error?.statusCode) || 502;
132
+ console.error('[http] Failed to proxy Structurizr request:', error.message);
133
+ textResponse(req, res, statusCode, error.message || 'Structurizr renderer unavailable');
134
+ }
135
+
136
+ return true;
137
+ }
138
+
139
+ handleRequest.requiresAuthorization = (requestUrl) => (
140
+ Boolean(service?.isProxyPath(requestUrl.pathname))
141
+ && !requestUrl.pathname.startsWith('/api/')
142
+ );
143
+
144
+ return handleRequest;
145
+ }
@@ -1,4 +1,5 @@
1
1
  import { basename } from 'node:path';
2
+ import { isPdfFilePath } from '../../../domain/file-kind.js';
2
3
  import { createRequestError } from './http-errors.js';
3
4
  import { handleApiError, readRequestId } from './http-request-helpers.js';
4
5
  import {
@@ -7,10 +8,15 @@ import {
7
8
  jsonResponse,
8
9
  sendResponse,
9
10
  } from './http-response.js';
10
- import { parseJsonBody, readBinaryRequestBody } from './request-body.js';
11
+ import {
12
+ parseJsonBody,
13
+ readBinaryRequestBody,
14
+ REQUEST_BODY_LIMIT_BYTES,
15
+ } from './request-body.js';
11
16
 
12
17
  const DOCX_EXPORT_REQUEST_LIMIT_BYTES = 33_554_432;
13
18
  const DOCX_MIME_TYPE = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
19
+ const PDF_SIGNATURE = Buffer.from('%PDF-');
14
20
 
15
21
  function createDocxDownloadHeaders(filePath) {
16
22
  const fileName = basename(String(filePath ?? 'document')).replace(/\.[^.]+$/u, '') || 'document';
@@ -23,7 +29,7 @@ function createDocxDownloadHeaders(filePath) {
23
29
  };
24
30
  }
25
31
 
26
- function decodeHeaderMetadata(value) {
32
+ function decodeHeaderMetadata(value, label = 'attachment') {
27
33
  const normalized = String(value ?? '').trim();
28
34
  if (!normalized) {
29
35
  return '';
@@ -32,7 +38,7 @@ function decodeHeaderMetadata(value) {
32
38
  try {
33
39
  return decodeURIComponent(normalized);
34
40
  } catch {
35
- throw createRequestError(400, 'Invalid attachment metadata header encoding');
41
+ throw createRequestError(400, `Invalid ${label} metadata header encoding`);
36
42
  }
37
43
  }
38
44
 
@@ -40,7 +46,7 @@ function getDirectoryDeleteStatusCode(message = '') {
40
46
  return String(message).includes('Directory is not empty') ? 409 : 400;
41
47
  }
42
48
 
43
- async function handleExportDocx({ docxExporter }, req, res) {
49
+ async function handleExportDocx({ renderDocx }, req, res) {
44
50
  try {
45
51
  const body = await parseJsonBody(req, DOCX_EXPORT_REQUEST_LIMIT_BYTES);
46
52
  if (!body?.filePath || typeof body?.html !== 'string') {
@@ -48,12 +54,12 @@ async function handleExportDocx({ docxExporter }, req, res) {
48
54
  return true;
49
55
  }
50
56
 
51
- if (!docxExporter?.render) {
57
+ if (typeof renderDocx !== 'function') {
52
58
  jsonResponse(req, res, 503, { error: 'DOCX export is unavailable' });
53
59
  return true;
54
60
  }
55
61
 
56
- const docxBuffer = await docxExporter.render({
62
+ const docxBuffer = await renderDocx({
57
63
  html: body.html,
58
64
  title: body.title || '',
59
65
  });
@@ -129,6 +135,55 @@ async function handleUploadAttachment({ workspaceMutationCoordinator }, req, res
129
135
  return true;
130
136
  }
131
137
 
138
+ async function handleUploadFile({ maxPdfUploadBytes, workspaceMutationCoordinator }, req, res) {
139
+ try {
140
+ const filePath = decodeHeaderMetadata(req.headers['x-collabmd-file-path'], 'file upload');
141
+ if (!filePath) {
142
+ jsonResponse(req, res, 400, { error: 'Missing file path' });
143
+ return true;
144
+ }
145
+
146
+ if (typeof workspaceMutationCoordinator?.createFile !== 'function') {
147
+ jsonResponse(req, res, 503, { error: 'File upload is unavailable' });
148
+ return true;
149
+ }
150
+
151
+ const isPdfUpload = isPdfFilePath(filePath);
152
+ if (isPdfUpload) {
153
+ const contentType = String(req.headers['content-type'] || '')
154
+ .split(';', 1)[0]
155
+ .trim()
156
+ .toLowerCase();
157
+ if (contentType !== 'application/pdf') {
158
+ jsonResponse(req, res, 400, { error: 'PDF uploads must use application/pdf' });
159
+ return true;
160
+ }
161
+ }
162
+
163
+ const maxUploadBytes = isPdfUpload ? maxPdfUploadBytes : REQUEST_BODY_LIMIT_BYTES;
164
+ const content = await readBinaryRequestBody(req, maxUploadBytes);
165
+ if (isPdfUpload && !content.subarray(0, PDF_SIGNATURE.length).equals(PDF_SIGNATURE)) {
166
+ jsonResponse(req, res, 400, { error: 'Invalid PDF file' });
167
+ return true;
168
+ }
169
+
170
+ const result = await workspaceMutationCoordinator.createFile({
171
+ content,
172
+ path: filePath,
173
+ requestId: readRequestId(req),
174
+ });
175
+ if (!result.ok) {
176
+ jsonResponse(req, res, result.error === 'File already exists' ? 409 : 400, { error: result.error });
177
+ return true;
178
+ }
179
+
180
+ jsonResponse(req, res, 201, { ok: true, path: filePath });
181
+ } catch (error) {
182
+ handleApiError(req, res, error, '[api] Failed to upload file:', 'Failed to upload file');
183
+ }
184
+ return true;
185
+ }
186
+
132
187
  async function handleCreateFile({ workspaceMutationCoordinator }, req, res) {
133
188
  try {
134
189
  const body = await parseJsonBody(req);
@@ -280,6 +335,7 @@ const ROUTE_TABLE = [
280
335
  { method: 'POST', path: '/api/export/docx', handler: handleExportDocx },
281
336
  { method: 'PUT', path: '/api/file', handler: handleWriteFile },
282
337
  { method: 'POST', path: '/api/attachments', handler: handleUploadAttachment },
338
+ { method: 'POST', path: '/api/file/upload', handler: handleUploadFile },
283
339
  { method: 'POST', path: '/api/file', handler: handleCreateFile },
284
340
  { method: 'DELETE', path: '/api/file', handler: handleDeleteFile },
285
341
  { method: 'PATCH', path: '/api/file', handler: handleRenameFile },
@@ -289,11 +345,17 @@ const ROUTE_TABLE = [
289
345
  ];
290
346
 
291
347
  export function createVaultApiCommandHandler({
292
- docxExporter = null,
348
+ maxPdfUploadBytes = REQUEST_BODY_LIMIT_BYTES,
349
+ renderDocx = null,
293
350
  vaultFileStore,
294
351
  workspaceMutationCoordinator = null,
295
352
  }) {
296
- const context = { docxExporter, vaultFileStore, workspaceMutationCoordinator };
353
+ const context = {
354
+ maxPdfUploadBytes,
355
+ renderDocx,
356
+ vaultFileStore,
357
+ workspaceMutationCoordinator,
358
+ };
297
359
 
298
360
  return async function handleVaultApiCommand(req, res, requestUrl) {
299
361
  for (const route of ROUTE_TABLE) {
@@ -3,7 +3,7 @@ import { readdir } from 'node:fs/promises';
3
3
  import { basename } from 'node:path';
4
4
  import { join } from 'node:path';
5
5
 
6
- import { isImageAttachmentFilePath } from '../../../domain/file-kind.js';
6
+ import { getVaultFileKind, isImageAttachmentFilePath } from '../../../domain/file-kind.js';
7
7
  import { isIgnoredVaultEntry } from '../persistence/path-utils.js';
8
8
  import { parseJsonBody } from './request-body.js';
9
9
  import {
@@ -19,6 +19,36 @@ const SVG_ATTACHMENT_CSP = "default-src 'none'; img-src 'self' data: blob:; styl
19
19
  const DEFAULT_MAX_ARCHIVE_ENTRIES = 10_000;
20
20
  const DEFAULT_MAX_DOWNLOAD_FILE_BYTES = 268_435_456;
21
21
 
22
+ function createSearchCancellation(req, res) {
23
+ const controller = new AbortController();
24
+
25
+ const abort = () => {
26
+ if (controller.signal.aborted || res.writableEnded || res.writableFinished) {
27
+ return;
28
+ }
29
+
30
+ controller.abort();
31
+ };
32
+ const handleRequestClose = () => {
33
+ if (!req.complete) {
34
+ abort();
35
+ }
36
+ };
37
+
38
+ req.once('aborted', abort);
39
+ req.once('close', handleRequestClose);
40
+ res.once('close', abort);
41
+
42
+ return {
43
+ cleanup() {
44
+ req.off('aborted', abort);
45
+ req.off('close', handleRequestClose);
46
+ res.off('close', abort);
47
+ },
48
+ signal: controller.signal,
49
+ };
50
+ }
51
+
22
52
  function createAttachmentHeaders(attachment) {
23
53
  const fileName = basename(String(attachment?.path ?? 'attachment'));
24
54
  const headers = {
@@ -35,10 +65,10 @@ function createAttachmentHeaders(attachment) {
35
65
  return headers;
36
66
  }
37
67
 
38
- function createDownloadHeaders(fileName, contentType) {
68
+ function createDownloadHeaders(fileName, contentType, { inline = false } = {}) {
39
69
  return {
40
70
  'Cache-Control': 'no-store',
41
- 'Content-Disposition': `attachment; filename="${createSafeAsciiFilename(fileName)}"; filename*=UTF-8''${encodeContentDispositionFilename(fileName)}`,
71
+ 'Content-Disposition': `${inline ? 'inline' : 'attachment'}; filename="${createSafeAsciiFilename(fileName)}"; filename*=UTF-8''${encodeContentDispositionFilename(fileName)}`,
42
72
  'Content-Type': contentType,
43
73
  'X-Content-Type-Options': 'nosniff',
44
74
  };
@@ -304,6 +334,7 @@ async function handleFileDownload(req, res, requestUrl, { config, vaultFileStore
304
334
  headers: createDownloadHeaders(
305
335
  basename(String(download.path ?? 'download')),
306
336
  download.mimeType || 'application/octet-stream',
337
+ { inline: getVaultFileKind(download.path) === 'pdf' },
307
338
  ),
308
339
  statusCode: 200,
309
340
  stream: download.stream,
@@ -418,13 +449,28 @@ async function handleSearch(req, res, requestUrl, { searchService }) {
418
449
  return;
419
450
  }
420
451
 
452
+ const cancellation = createSearchCancellation(req, res);
453
+
421
454
  try {
422
455
  const result = await searchService.search({
423
456
  limit: requestUrl.searchParams.get('limit') || '',
424
457
  query: requestUrl.searchParams.get('q') || '',
458
+ signal: cancellation.signal,
425
459
  });
460
+ if (cancellation.signal.aborted || res.destroyed || res.writableEnded) {
461
+ return;
462
+ }
463
+
426
464
  jsonResponse(req, res, 200, result);
427
465
  } catch (error) {
466
+ if (
467
+ cancellation.signal.aborted
468
+ || res.destroyed
469
+ || res.writableEnded
470
+ ) {
471
+ return;
472
+ }
473
+
428
474
  const statusCode = Number(error?.statusCode) || 500;
429
475
  console.error('[api] Failed to search vault:', error.message);
430
476
  jsonResponse(req, res, statusCode, {
@@ -435,6 +481,8 @@ async function handleSearch(req, res, requestUrl, { searchService }) {
435
481
  backend: 'ripgrep',
436
482
  },
437
483
  });
484
+ } finally {
485
+ cancellation.cleanup();
438
486
  }
439
487
  }
440
488
 
@@ -1,6 +1,12 @@
1
- export function createRequestError(statusCode, message) {
1
+ export function createRequestError(statusCode, message, { code = null, requestCode = null } = {}) {
2
2
  const error = new Error(message);
3
3
  error.statusCode = statusCode;
4
+ if (code) {
5
+ error.code = code;
6
+ }
7
+ if (requestCode) {
8
+ error.requestCode = requestCode;
9
+ }
4
10
  return error;
5
11
  }
6
12
 
@@ -1,6 +1,28 @@
1
1
  import { getRequestErrorStatusCode } from './http-errors.js';
2
2
  import { jsonResponse } from './http-response.js';
3
3
 
4
+ export function stripBasePath(pathname, basePath) {
5
+ if (!basePath) {
6
+ return pathname;
7
+ }
8
+
9
+ if (pathname === basePath) {
10
+ return '/';
11
+ }
12
+
13
+ if (pathname.startsWith(`${basePath}/`)) {
14
+ return pathname.slice(basePath.length) || '/';
15
+ }
16
+
17
+ return pathname;
18
+ }
19
+
20
+ export function createRequestUrlWithPathname(requestUrl, pathname) {
21
+ const nextUrl = new URL(requestUrl.toString());
22
+ nextUrl.pathname = pathname || '/';
23
+ return nextUrl;
24
+ }
25
+
4
26
  export function readRequestId(req) {
5
27
  const value = String(req.headers['x-collabmd-request-id'] || '').trim();
6
28
  return value ? value.slice(0, 120) : null;
@@ -3,7 +3,7 @@ import { isAbsolute, normalize, relative, resolve } from 'path';
3
3
  import { isVaultFilePath } from '../../../domain/file-kind.js';
4
4
 
5
5
  export const IGNORED_DIRECTORIES = new Set(['.git', '.obsidian', '.trash', 'node_modules', '.DS_Store']);
6
- export const VAULT_FILE_PATH_REQUIREMENT = '.md, .base, .excalidraw, .drawio, .mmd, .mermaid, .puml, .plantuml, .png, .jpg, .jpeg, .webp, .gif, or .svg';
6
+ export const VAULT_FILE_PATH_REQUIREMENT = '.md, .markdown, .mdx, .base, .excalidraw, .drawio, .mmd, .mermaid, .puml, .plantuml, .dsl, .png, .jpg, .jpeg, .webp, .gif, or .svg';
7
7
  export const INVALID_VAULT_FILE_PATH_ERROR = `Invalid file path — must end in ${VAULT_FILE_PATH_REQUIREMENT}`;
8
8
  export const INVALID_DIRECTORY_PATH_ERROR = 'Invalid directory path';
9
9
 
@@ -31,6 +31,7 @@ const EDITABLE_VAULT_CONTENT_PATH_ERRORS = {
31
31
  markdown: 'Invalid file path',
32
32
  mermaid: 'Invalid file path — must end in .mmd or .mermaid',
33
33
  plantuml: 'Invalid file path — must end in .puml or .plantuml',
34
+ structurizr: 'Invalid file path — must end in .dsl',
34
35
  };
35
36
 
36
37
  function getEditableVaultContentKind(filePath) {
@@ -67,7 +68,9 @@ const TEXT_FILE_MIME_TYPES = Object.freeze({
67
68
  excalidraw: 'application/json; charset=utf-8',
68
69
  markdown: 'text/markdown; charset=utf-8',
69
70
  mermaid: 'text/plain; charset=utf-8',
71
+ pdf: 'application/pdf',
70
72
  plantuml: 'text/plain; charset=utf-8',
73
+ structurizr: 'text/plain; charset=utf-8',
71
74
  });
72
75
 
73
76
  function createTransactionalPath(targetPath, label) {
@@ -0,0 +1,43 @@
1
+ html, body, #content {
2
+ height: 100% !important;
3
+ min-height: 0 !important;
4
+ }
5
+
6
+ body {
7
+ margin: 0 !important;
8
+ overflow: hidden !important;
9
+ background: transparent !important;
10
+ }
11
+
12
+ #diagramControls,
13
+ #diagramNavigationPanel,
14
+ #embeddedControls {
15
+ display: none !important;
16
+ }
17
+
18
+ #diagramControls + .row,
19
+ #diagramControls + .row > .col-10,
20
+ #diagramControls + .row #diagram,
21
+ #diagram-viewport {
22
+ height: 100% !important;
23
+ min-height: 0 !important;
24
+ }
25
+
26
+ #diagramControls + .row > .col-10 {
27
+ width: 100% !important;
28
+ }
29
+
30
+ #diagram {
31
+ background: transparent !important;
32
+ }
33
+
34
+ #diagram-viewport {
35
+ overflow: auto !important;
36
+ touch-action: pan-x pan-y !important;
37
+ overscroll-behavior: contain !important;
38
+ background: transparent !important;
39
+ }
40
+
41
+ #diagram-canvas {
42
+ margin: 0 auto !important;
43
+ }