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
@@ -0,0 +1,374 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, readdir, realpath, rm, stat, utimes, writeFile } from 'node:fs/promises';
3
+ import { basename, dirname, join, resolve } from 'node:path';
4
+
5
+ import {
6
+ isStructurizrFilePath,
7
+ isVaultFilePath,
8
+ } from '../../../domain/file-kind.js';
9
+ import { sanitizeVaultPath } from '../persistence/path-utils.js';
10
+
11
+ const MANIFEST_FILE_NAME = '.collabmd-manifest.json';
12
+ const WORKSPACE_DSL_FILE_NAME = 'workspace.dsl';
13
+ const WORKSPACE_JSON_FILE_NAME = 'workspace.json';
14
+ const SIDECAR_TIMEOUT_MS = 15_000;
15
+
16
+ function createServiceError(message, statusCode, code) {
17
+ const error = new Error(message);
18
+ error.statusCode = statusCode;
19
+ error.requestCode = code;
20
+ return error;
21
+ }
22
+
23
+ function normalizeWorkspaceRootPath(pathValue) {
24
+ const normalized = String(pathValue ?? '').trim().replace(/\\/g, '/');
25
+ if (!normalized || normalized.startsWith('/') || normalized.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {
26
+ return '';
27
+ }
28
+
29
+ return normalized;
30
+ }
31
+
32
+ function validateStructurizrDsl(source, { trustedExecutableDsl = false } = {}) {
33
+ if (trustedExecutableDsl) {
34
+ return;
35
+ }
36
+
37
+ const text = String(source ?? '');
38
+ if (/^\s*!script\b/im.test(text) || /^\s*!plugin\b/im.test(text)) {
39
+ throw createServiceError(
40
+ 'Executable Structurizr DSL is disabled for this workspace.',
41
+ 422,
42
+ 'STRUCTURIZR_EXECUTABLE_DSL_DISABLED',
43
+ );
44
+ }
45
+
46
+ if (/^\s*!include\s+<?(?:https?|file):/im.test(text)) {
47
+ throw createServiceError(
48
+ 'Remote Structurizr DSL includes are disabled for this workspace.',
49
+ 422,
50
+ 'STRUCTURIZR_REMOTE_INCLUDE_DISABLED',
51
+ );
52
+ }
53
+ }
54
+
55
+ function isMirrorFileAllowed(relativePath) {
56
+ return basename(relativePath) === WORKSPACE_JSON_FILE_NAME || isVaultFilePath(relativePath);
57
+ }
58
+
59
+ async function collectWorkspaceFiles(directoryPath, prefix = '') {
60
+ const files = new Map();
61
+ const entries = (await readdir(directoryPath, { withFileTypes: true }))
62
+ .sort((left, right) => left.name.localeCompare(right.name, undefined, { sensitivity: 'base' }));
63
+
64
+ for (const entry of entries) {
65
+ if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === '.git') {
66
+ continue;
67
+ }
68
+
69
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
70
+ const absolutePath = join(directoryPath, entry.name);
71
+ if (entry.isDirectory()) {
72
+ const nested = await collectWorkspaceFiles(absolutePath, relativePath);
73
+ nested.forEach((content, pathValue) => files.set(pathValue, content));
74
+ continue;
75
+ }
76
+
77
+ if (!entry.isFile() || !isMirrorFileAllowed(relativePath)) {
78
+ continue;
79
+ }
80
+
81
+ files.set(relativePath, await readFile(absolutePath));
82
+ }
83
+
84
+ return files;
85
+ }
86
+
87
+ async function collectMirrorPaths(directoryPath, prefix = '') {
88
+ let entries;
89
+ try {
90
+ entries = await readdir(directoryPath, { withFileTypes: true });
91
+ } catch (error) {
92
+ if (error.code === 'ENOENT') {
93
+ return [];
94
+ }
95
+ throw error;
96
+ }
97
+
98
+ const paths = [];
99
+ for (const entry of entries) {
100
+ if (entry.name === '.structurizr') {
101
+ continue;
102
+ }
103
+
104
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
105
+ const absolutePath = join(directoryPath, entry.name);
106
+ if (entry.isDirectory()) {
107
+ paths.push(...await collectMirrorPaths(absolutePath, relativePath));
108
+ } else if (entry.isFile()) {
109
+ paths.push(relativePath);
110
+ }
111
+ }
112
+
113
+ return paths;
114
+ }
115
+
116
+ async function readManifest(manifestPath) {
117
+ try {
118
+ const parsed = JSON.parse(await readFile(manifestPath, 'utf8'));
119
+ return Array.isArray(parsed?.paths) ? parsed.paths : [];
120
+ } catch (error) {
121
+ if (error.code === 'ENOENT' || error instanceof SyntaxError) {
122
+ return [];
123
+ }
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ async function writeMirrorFiles(mirrorDir, files) {
129
+ await Promise.all(Array.from(files, async ([relativePath, content]) => {
130
+ const targetPath = join(mirrorDir, relativePath);
131
+ await mkdir(dirname(targetPath), { recursive: true });
132
+ await writeFile(targetPath, content);
133
+ }));
134
+ }
135
+
136
+ async function removeMirrorPaths(mirrorDir, paths) {
137
+ await Promise.all(paths.map((relativePath) => rm(join(mirrorDir, relativePath), {
138
+ force: true,
139
+ recursive: true,
140
+ })));
141
+ }
142
+
143
+ async function snapshotMirror(mirrorDir) {
144
+ const paths = await collectMirrorPaths(mirrorDir);
145
+ const files = new Map();
146
+ await Promise.all(paths.map(async (relativePath) => {
147
+ files.set(relativePath, await readFile(join(mirrorDir, relativePath)));
148
+ }));
149
+ return files;
150
+ }
151
+
152
+ async function restoreMirror(mirrorDir, snapshot) {
153
+ await removeMirrorPaths(mirrorDir, await collectMirrorPaths(mirrorDir));
154
+ await writeMirrorFiles(mirrorDir, snapshot);
155
+ }
156
+
157
+ function hashFiles(files) {
158
+ const hash = createHash('sha256');
159
+ Array.from(files.keys()).sort().forEach((pathValue) => {
160
+ hash.update(pathValue);
161
+ hash.update('\0');
162
+ hash.update(files.get(pathValue));
163
+ hash.update('\0');
164
+ });
165
+ return hash.digest('hex');
166
+ }
167
+
168
+ export class StructurizrWorkspaceService {
169
+ constructor({
170
+ mirrorDir,
171
+ serverUrl = '',
172
+ trustedExecutableDsl = false,
173
+ vaultDir,
174
+ }) {
175
+ this.mirrorDir = resolve(mirrorDir);
176
+ this.serverUrl = String(serverUrl ?? '').trim().replace(/\/+$/u, '');
177
+ this.trustedExecutableDsl = Boolean(trustedExecutableDsl);
178
+ this.vaultDir = resolve(vaultDir);
179
+ this.syncPromise = Promise.resolve();
180
+ }
181
+
182
+ isEnabled() {
183
+ return Boolean(this.serverUrl);
184
+ }
185
+
186
+ isProxyPath(pathname) {
187
+ return this.isEnabled() && (
188
+ pathname === '/workspace/1'
189
+ || pathname.startsWith('/workspace/1/')
190
+ || pathname.startsWith('/static/')
191
+ || pathname === '/api/workspace/1'
192
+ || pathname.startsWith('/api/workspace/1/')
193
+ );
194
+ }
195
+
196
+ async sync({ content, rootPath }) {
197
+ if (!this.isEnabled()) {
198
+ throw createServiceError('Structurizr renderer is not configured.', 503, 'STRUCTURIZR_NOT_CONFIGURED');
199
+ }
200
+
201
+ const run = async () => this.syncWorkspace({ content, rootPath });
202
+ this.syncPromise = this.syncPromise.catch(() => {}).then(run);
203
+ return this.syncPromise;
204
+ }
205
+
206
+ async syncWorkspace({ content, rootPath }) {
207
+ const normalizedRootPath = normalizeWorkspaceRootPath(rootPath);
208
+ if (!isStructurizrFilePath(normalizedRootPath)) {
209
+ throw createServiceError('Structurizr previews require a workspace.dsl root file.', 400, 'STRUCTURIZR_ROOT_REQUIRED');
210
+ }
211
+
212
+ const rootAbsolutePath = sanitizeVaultPath(this.vaultDir, normalizedRootPath);
213
+ if (!rootAbsolutePath) {
214
+ throw createServiceError('Invalid Structurizr workspace path.', 400, 'STRUCTURIZR_ROOT_INVALID');
215
+ }
216
+
217
+ let rootInfo;
218
+ try {
219
+ rootInfo = await stat(rootAbsolutePath);
220
+ } catch (error) {
221
+ if (error.code === 'ENOENT') {
222
+ throw createServiceError('Structurizr workspace not found.', 404, 'STRUCTURIZR_ROOT_NOT_FOUND');
223
+ }
224
+ throw error;
225
+ }
226
+ if (!rootInfo.isFile()) {
227
+ throw createServiceError('Structurizr workspace root is not a file.', 400, 'STRUCTURIZR_ROOT_INVALID');
228
+ }
229
+
230
+ const [vaultRealPath, rootRealPath] = await Promise.all([
231
+ realpath(this.vaultDir),
232
+ realpath(rootAbsolutePath),
233
+ ]);
234
+ if (rootRealPath !== vaultRealPath && !rootRealPath.startsWith(`${vaultRealPath}/`)) {
235
+ throw createServiceError('Structurizr workspace must remain inside the vault.', 400, 'STRUCTURIZR_ROOT_INVALID');
236
+ }
237
+
238
+ const sourceDirectory = dirname(rootRealPath);
239
+ const files = await collectWorkspaceFiles(sourceDirectory);
240
+ const sourceRootFileName = basename(normalizedRootPath);
241
+ const rootSource = typeof content === 'string'
242
+ ? content
243
+ : files.get(sourceRootFileName)?.toString('utf8');
244
+ if (typeof rootSource !== 'string') {
245
+ throw createServiceError('Structurizr workspace source is unavailable.', 400, 'STRUCTURIZR_ROOT_UNREADABLE');
246
+ }
247
+
248
+ files.delete(sourceRootFileName);
249
+ files.set(WORKSPACE_DSL_FILE_NAME, Buffer.from(rootSource, 'utf8'));
250
+ for (const [pathValue, fileContent] of files) {
251
+ if (pathValue.toLowerCase().endsWith('.dsl')) {
252
+ validateStructurizrDsl(fileContent.toString('utf8'), {
253
+ trustedExecutableDsl: this.trustedExecutableDsl,
254
+ });
255
+ }
256
+ }
257
+
258
+ await mkdir(this.mirrorDir, { recursive: true });
259
+ const snapshot = await snapshotMirror(this.mirrorDir);
260
+ const previousManifest = await readManifest(join(this.mirrorDir, MANIFEST_FILE_NAME));
261
+ const sourcePaths = Array.from(files.keys());
262
+ const stalePaths = previousManifest.filter((pathValue) => !files.has(pathValue));
263
+
264
+ try {
265
+ await removeMirrorPaths(this.mirrorDir, stalePaths);
266
+ await writeMirrorFiles(this.mirrorDir, files);
267
+ // Local caches workspace.json; an old mtime makes it parse the DSL without a delete race.
268
+ const workspaceJsonPath = join(this.mirrorDir, WORKSPACE_JSON_FILE_NAME);
269
+ try {
270
+ await utimes(workspaceJsonPath, new Date(0), new Date(0));
271
+ } catch (error) {
272
+ if (error.code !== 'ENOENT') {
273
+ throw error;
274
+ }
275
+ }
276
+ await this.validateMirror();
277
+ if (files.has(WORKSPACE_JSON_FILE_NAME)) {
278
+ await writeMirrorFiles(this.mirrorDir, new Map([
279
+ [WORKSPACE_JSON_FILE_NAME, files.get(WORKSPACE_JSON_FILE_NAME)],
280
+ ]));
281
+ }
282
+ await writeFile(
283
+ join(this.mirrorDir, MANIFEST_FILE_NAME),
284
+ `${JSON.stringify({ paths: sourcePaths }, null, 2)}\n`,
285
+ 'utf8',
286
+ );
287
+ } catch (error) {
288
+ await restoreMirror(this.mirrorDir, snapshot);
289
+ throw error;
290
+ }
291
+
292
+ return {
293
+ ok: true,
294
+ rootPath: normalizedRootPath,
295
+ version: hashFiles(files),
296
+ };
297
+ }
298
+
299
+ async validateMirror() {
300
+ for (let attempt = 0; attempt < 8; attempt += 1) {
301
+ let response;
302
+ try {
303
+ response = await fetch(`${this.serverUrl}/api/workspace/1`, {
304
+ headers: { Accept: 'application/json' },
305
+ signal: AbortSignal.timeout(SIDECAR_TIMEOUT_MS),
306
+ });
307
+ } catch (error) {
308
+ throw createServiceError(
309
+ `Structurizr renderer is unavailable: ${error.message}`,
310
+ 503,
311
+ 'STRUCTURIZR_UNAVAILABLE',
312
+ );
313
+ }
314
+
315
+ const payload = await response.json().catch(() => ({}));
316
+ if (response.ok && payload.success !== false) {
317
+ return;
318
+ }
319
+
320
+ const message = String(payload.message || payload.error || `Structurizr renderer returned HTTP ${response.status}`)
321
+ .replace(/^com\.structurizr\.dsl\.StructurizrDslParserException:\s*/u, '');
322
+ const isTransientStartupFailure = response.status >= 500
323
+ && /(?:No workspace|FileNotFoundException|workspace\.json)/iu.test(message);
324
+ if (isTransientStartupFailure && attempt < 7) {
325
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 250));
326
+ continue;
327
+ }
328
+
329
+ throw createServiceError(
330
+ message,
331
+ response.status >= 400 && response.status < 500 ? 422 : 502,
332
+ response.status >= 400 && response.status < 500
333
+ ? 'STRUCTURIZR_DSL_INVALID'
334
+ : 'STRUCTURIZR_VALIDATION_FAILED',
335
+ );
336
+ }
337
+ }
338
+
339
+ async proxy(requestUrl, { method = 'GET', headers = {} } = {}) {
340
+ if (!this.isProxyPath(requestUrl.pathname)) {
341
+ return null;
342
+ }
343
+
344
+ const upstreamUrl = `${this.serverUrl}${requestUrl.pathname}${requestUrl.search}`;
345
+ let response;
346
+ try {
347
+ const proxyHeaders = {
348
+ Accept: headers.accept || '*/*',
349
+ };
350
+ if (headers.range) {
351
+ proxyHeaders.Range = headers.range;
352
+ }
353
+ response = await fetch(upstreamUrl, {
354
+ headers: proxyHeaders,
355
+ method,
356
+ redirect: 'follow',
357
+ signal: AbortSignal.timeout(SIDECAR_TIMEOUT_MS),
358
+ });
359
+ } catch (error) {
360
+ throw createServiceError(
361
+ `Structurizr renderer is unavailable: ${error.message}`,
362
+ 503,
363
+ 'STRUCTURIZR_UNAVAILABLE',
364
+ );
365
+ }
366
+
367
+ return {
368
+ body: Buffer.from(await response.arrayBuffer()),
369
+ contentType: response.headers.get('content-type') || 'application/octet-stream',
370
+ statusCode: response.status,
371
+ };
372
+ }
373
+ }
374
+
@@ -1,6 +1,10 @@
1
1
  import { WebSocketServer } from 'ws';
2
2
 
3
3
  import { normalizeHostedEmail } from '../../domain/hosted-workspace-contract.js';
4
+ import {
5
+ createRequestUrlWithPathname,
6
+ stripBasePath,
7
+ } from '../http/http-request-helpers.js';
4
8
  import { ClientSocketSession } from './client-socket-session.js';
5
9
 
6
10
  function rejectUpgrade(socket, statusCode, statusMessage, {
@@ -25,28 +29,6 @@ function extractRoomName(pathname, wsBasePath) {
25
29
  return decodeURIComponent(roomSegment || 'default');
26
30
  }
27
31
 
28
- function stripBasePath(pathname, basePath) {
29
- if (!basePath) {
30
- return pathname;
31
- }
32
-
33
- if (pathname === basePath) {
34
- return '/';
35
- }
36
-
37
- if (pathname.startsWith(`${basePath}/`)) {
38
- return pathname.slice(basePath.length) || '/';
39
- }
40
-
41
- return pathname;
42
- }
43
-
44
- function createRequestUrlWithPathname(requestUrl, pathname) {
45
- const nextUrl = new URL(requestUrl.toString());
46
- nextUrl.pathname = pathname || '/';
47
- return nextUrl;
48
- }
49
-
50
32
  export function attachCollaborationGateway({
51
33
  authService,
52
34
  basePath = '',
@@ -1 +0,0 @@
1
- import{i as e}from"./rolldown-runtime-Dd_uD5pT.js";import{A as t,F as n,J as r,L as i,P as a,V as o,X as s,c,lt as l,q as u,st as d,t as f}from"./dist-C11hHqv_.js";import"./chunk-SRAX5OIU-KFQikY-4.js";import{n as p,t as m}from"./jsx-runtime-BpzPEenQ.js";import{n as h,r as g,s as _,t as v}from"./dist-GrygD2zE.js";var y=e(p(),1),b=m(),x=c.define({token(e){return e.match(/^%%.*$/)?`comment`:e.match(/^"(?:[^"\\]|\\.)*"/)?`string`:e.match(/^(flowchart|graph|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|mindmap|journey|gitGraph|timeline|quadrantChart|sankey|xychart)\b/i)||e.match(/^(TB|TD|BT|RL|LR)\b/)||e.match(/^(subgraph|end|participant|actor|loop|alt|else|opt|par|critical|break|rect|note|over|activate|deactivate|title|section|class|style|linkStyle|classDef|click)\b/i)?`keyword`:e.match(/^[-.=<>|ox]+>/)||e.match(/^<[-.=<>|ox]+/)||e.match(/^--+|\.\.+|==+/)?`operator`:e.match(/^[[\](){}|<>]/)?`bracket`:e.match(/^[A-Za-z_][A-Za-z0-9_]*/)?`variableName`:e.match(/^\d+(\.\d+)?/)?`number`:e.match(/^[,:;]/)?`punctuation`:(e.eatSpace()||e.next(),null)}});function S(){return x}var C=i.theme({"&":{backgroundColor:`#1e1e1e`,color:`#d4d4d4`},".cm-content":{caretColor:`#fff`},".cm-cursor":{borderLeftColor:`#fff`},".cm-gutters":{backgroundColor:`#1e1e1e`,color:`#858585`,border:`none`},".cm-activeLineGutter":{backgroundColor:`#2a2a2a`},".cm-activeLine":{backgroundColor:`#2a2a2a`},".cm-errorLine":{backgroundColor:`rgba(255, 0, 0, 0.15)`}},{dark:!0}),w=f.define([{tag:a.keyword,color:`#569cd6`},{tag:a.string,color:`#ce9178`},{tag:a.comment,color:`#6a9955`},{tag:a.number,color:`#b5cea8`},{tag:a.operator,color:`#d4d4d4`},{tag:a.punctuation,color:`#d4d4d4`},{tag:a.variableName,color:`#9cdcfe`},{tag:a.bracket,color:`#ffd700`}]),T=i.theme({"&":{backgroundColor:`#ffffff`,color:`#1e1e1e`},".cm-content":{caretColor:`#000`},".cm-cursor":{borderLeftColor:`#000`},".cm-gutters":{backgroundColor:`#fff`,color:`#999`,border:`none`},".cm-activeLineGutter":{backgroundColor:`#e8e8e8`},".cm-activeLine":{backgroundColor:`#e8e8e8`},".cm-errorLine":{backgroundColor:`rgba(255, 0, 0, 0.1)`}}),E=f.define([{tag:a.keyword,color:`#0000ff`},{tag:a.string,color:`#a31515`},{tag:a.comment,color:`#008000`},{tag:a.number,color:`#098658`},{tag:a.operator,color:`#1e1e1e`},{tag:a.punctuation,color:`#1e1e1e`},{tag:a.variableName,color:`#001080`},{tag:a.bracket,color:`#af00db`}]),D=n.line({class:`cm-errorLine`}),O=(e,t)=>{if(!e||e<1||e>t.lines)return i.decorations.of(n.none);let r=t.line(e);return i.decorations.of(n.set([D.range(r.from)]))},k=e=>e===`dark`?[C,t(w)]:[T,t(E)],A=({value:e,onChange:t,onKeyboardSubmit:n,placeholder:a,theme:c,errorLine:f})=>{let p=(0,y.useRef)(null),m=(0,y.useRef)(null),x=(0,y.useRef)(t),C=(0,y.useRef)(n),w=(0,y.useRef)(new d),T=(0,y.useRef)(new d);return x.current=t,C.current=n,(0,y.useEffect)(()=>{if(!p.current)return;let t=w.current,n=new i({state:l.create({doc:e,extensions:[u.of([{key:`Mod-Enter`,run:()=>(C.current?.(),!0)},{key:`Mod-Shift-z`,run:_,preventDefault:!0}]),i.updateListener.of(e=>{e.docChanged&&x.current(e.state.doc.toString())}),h(),u.of([...v,...g]),r(),i.lineWrapping,t.of(k(c)),T.current.of([]),S(),o({drawRangeCursor:!0}),...a?[s(a)]:[]]}),parent:p.current});return m.current=n,n.focus(),()=>{n.destroy(),m.current=null}},[]),(0,y.useEffect)(()=>{let e=m.current;e&&e.dispatch({effects:w.current.reconfigure(k(c))})},[c]),(0,y.useEffect)(()=>{let e=m.current;e&&e.dispatch({effects:T.current.reconfigure(O(f,e.state.doc))})},[f]),(0,y.useEffect)(()=>{let t=m.current;if(!t)return;let n=t.state.doc.toString();e!==n&&t.dispatch({changes:{from:0,to:n.length,insert:e}})},[e]),(0,b.jsx)(`div`,{ref:p,className:`ttd-dialog-input ttd-dialog-input--codemirror`})};export{A as default};
@@ -1,143 +0,0 @@
1
- import{c as e}from"./file-kind-BKPOIRPE.js";import{n as t}from"./vault-utils-wdQ0RNR9.js";import{n}from"./runtime-paths-DvpihCEq.js";import{i as r,n as i}from"./browser-utils-CKtI_rZl.js";import{r as a,t as o}from"./button-BCEiw4Rw.js";import{n as s,t as c}from"./segmented-control-DJrHSz8M.js";function l(){return`base-${Math.random().toString(36).slice(2,10)}`}function u(e=``){return/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/iu.test(String(e??``).trim())}function d(e=``){return/^https?:\/\/.+\.(?:png|jpe?g|webp|gif|svg)(?:[?#].*)?$/iu.test(String(e??``).trim())}function f(e){return Object.prototype.toString.call(e)===`[object Object]`}function p(e=`and`){return{children:[],conjunction:e,type:`group`}}function m(e,t,n){let r=e.image||t.find(e=>[`cover`,`image`].includes(String(e.id??``).toLowerCase()))?.id||null;return r?n.cells[r]??null:null}function h(e){return String(e?.text??``)}function g(e=``){return n(`/attachment?path=${encodeURIComponent(e)}`)}function ee(t={}){if(t?.type===`image`&&t.path)return g(t.path);if(t?.type===`link`){if(t.external&&d(t.url))return t.url;if(t.path&&e(t.path))return g(t.path)}let n=h(t);return d(n)?n:e(n)?g(n):``}function _(e={}){let n=e?.type||`string`;if(n===`empty`)return`<span class="bases-value bases-value-empty">—</span>`;if(n===`boolean`)return`<span class="bases-value bases-value-boolean">${e.value?`True`:`False`}</span>`;if(n===`image`&&e.path)return`<img class="bases-inline-image" src="${t(g(e.path))}" alt="${t(h(e))}">`;if(n===`list`)return`<span class="bases-value-list">${(e.items??[]).map(e=>`<span class="bases-value-pill">${_(e)}</span>`).join(``)}</span>`;if(n===`link`){if(e.external&&e.url)return`<a class="bases-link" href="${t(e.url)}" target="_blank" rel="noopener noreferrer">${t(h(e))}</a>`;if(e.path)return`<button type="button" class="${t(o({variant:`secondary`,size:`compact`,pill:!0,extra:[`bases-link`,`bases-link-button`]}))}" data-base-open-file="${t(e.path)}">${t(h(e))}</button>`}if(n===`file`&&e.path)return`<button type="button" class="${t(o({variant:`secondary`,size:`compact`,pill:!0,extra:[`bases-link`,`bases-link-button`]}))}" data-base-open-file="${t(e.path)}">${t(h(e))}</button>`;let r=h(e);return`<span class="bases-value">${t(r)}</span>`}function v(e,n){let r=n?.cells?.[e.id]??null;return n?.path&&r&&r.type!==`empty`&&(e.id===`file.name`||e.id===`file.basename`)?`<button type="button" class="${t(o({variant:`secondary`,size:`compact`,pill:!0,extra:[`bases-link`,`bases-link-button`]}))}" data-base-open-file="${t(n.path)}">${t(h(r))}</button>`:_(r)}function y(e=[]){return!Array.isArray(e)||e.length===0?``:`<div class="bases-summaries">${e.map(e=>`<div class="bases-summary"><span class="bases-summary-label">${t(e.label)}</span><span class="bases-summary-value">${t(h(e.value))}</span></div>`).join(``)}</div>`}function te(e,n){return n.map(n=>`<section class="bases-group"><header class="bases-group-header"><h3>${t(n.label)}</h3>${y(n.summaries)}</header><div class="bases-table-shell"><table class="bases-table"><thead><tr>${e.map(e=>`<th>${t(e.label)}</th>`).join(``)}</tr></thead><tbody>${(n.rows??[]).map(t=>`<tr>${e.map(e=>`<td>${v(e,t)}</td>`).join(``)}</tr>`).join(``)}</tbody></table></div></section>`).join(``)}function ne(e,n){return n.map(n=>`<section class="bases-group"><header class="bases-group-header"><h3>${t(n.label)}</h3>${y(n.summaries)}</header><ul class="bases-list">${(n.rows??[]).map(n=>`<li class="bases-list-item">${e.map(e=>{let r=n.cells[e.id];return!r||r.type===`empty`?``:`<span class="bases-list-field"><strong>${t(e.label)}:</strong> ${v(e,n)}</span>`}).join(``)}</li>`).join(``)}</ul></section>`).join(``)}function re(e,n,r){return n.map(n=>`<section class="bases-group"><header class="bases-group-header"><h3>${t(n.label)}</h3>${y(n.summaries)}</header><div class="bases-cards">${(n.rows??[]).map(n=>{let i=m(r,e,n),a=h(i),o=``;if(u(a))o=`<div class="bases-card-media bases-card-media-color" style="--bases-card-swatch:${t(a)}"></div>`;else{let e=ee(i);e&&(o=`<img class="bases-card-media" src="${t(e)}" alt="${t(a)}">`)}return`<article class="bases-card">${o}<div class="bases-card-body">${e.map(e=>{let r=n.cells[e.id];return!r||r.type===`empty`?``:`<div class="bases-card-field"><div class="bases-card-label">${t(e.label)}</div><div class="bases-card-value">${v(e,n)}</div></div>`}).join(``)}</div></article>`}).join(``)}</div></section>`).join(``)}function b(e){if(!e.view.supported)return`<div class="bases-unsupported">CollabMD parsed this view but does not render the <code>${t(e.view.type)}</code> layout yet.</div>`;switch(e.view.type){case`cards`:return re(e.columns,e.groups,e.view);case`list`:return ne(e.columns,e.groups);default:return te(e.columns,e.groups)}}function x(e){return(e.views??[]).map(n=>`<button type="button" class="${t(c({active:n.id===e.view.id,extra:`bases-view-tab`}))}" data-base-view="${t(n.id)}">${t(n.name)}</button>`).join(``)}function S(e){return e?.meta??{}}function C(e){return S(e).availableProperties??[]}function w(e){let t=S(e).activeViewConfig?.order;return Array.isArray(t)&&t.length>0?[...t]:Array.isArray(e?.columns)?e.columns.map(e=>e.id).filter(Boolean):[]}function T(e,t=``){return C(e).find(e=>e.id===t)??null}function E(e,t=``){return T(e,t)?.filterOperators??[`is`,`is not`,`contains`,`does not contain`,`is empty`,`is not empty`]}function D(e){let t=S(e).activeViewConfig??{};return{filters:t.filters??null,groupBy:t.groupBy??null,order:Array.isArray(t.order)?[...t.order]:[],sort:Array.isArray(t.sort)?t.sort.map(e=>({...e})):[]}}function ie(e){return!!(e?.payload?.path&&e?.payload?.sourcePath&&e.payload.path===e.payload.sourcePath)}function O(e,n,r,i){let a=r.ui?.openPanel===n,s=!!S(i).editable;return`<button type="button" class="${t(o({variant:a?`primary`:`secondary`,pill:!0,extra:[`bases-toolbar-btn`,a?`is-active`:``]}))}" data-base-panel="${t(n)}"${s?``:` disabled`}>${t(e)}</button>`}function k(e,t=``){if(!e||e.type!==`group`||!t)return e;let n=(e.children??[]).flatMap(e=>{if(e?.type===`group`){let n=k(e,t);return n?[n]:[]}return e?.propertyId===t?[]:[e]});return{...e,children:n}}function A(e,t=``,n=e?.result){let r=S(n).activeViewConfig?.filters??null,i=R(r),a=t&&i?I(k(i,t),n):r;return JSON.stringify({filters:a,path:e?.payload?.path??``,source:e?.payload?.source??null,sourcePath:e?.payload?.sourcePath??``,view:n?.view?.id??e?.payload?.view??``})}function ae(e,t=``,n=e?.result){let r=e?.propertyValueOptions?.get(t)??null;return r&&r.cacheKey===A(e,t,n)?r.values??[]:[]}function j(e,t){return[O(`Sort`,`sort`,t,e),O(`Filter`,`filter`,t,e),O(`Properties`,`properties`,t,e)].join(``)}function M(e,{allowBlank:n=!1,selected:r=``}={}){let i=[];return n&&i.push(`<option value=""${r?``:` selected`}>Property</option>`),C(e).forEach(e=>{i.push(`<option value="${t(e.id)}"${e.id===r?` selected`:``}>${t(e.label)}</option>`)}),i.join(``)}function oe(e){let n=D(e),r=T(e,n.groupBy?.property)?.sortDirections??[{id:`asc`,label:`A → Z`},{id:`desc`,label:`Z → A`}],i=n.sort??[];return`
2
- <section class="bases-panel-card">
3
- <div class="bases-panel-section">
4
- <div class="bases-panel-title">Group by</div>
5
- <div class="bases-sort-row">
6
- <select class="${t(a({extra:`bases-select bases-sort-property`}))}" data-base-group-by-property>
7
- ${M(e,{allowBlank:!0,selected:n.groupBy?.property??``})}
8
- </select>
9
- <select class="${t(a({extra:`bases-select bases-sort-direction`}))}" data-base-group-by-direction${n.groupBy?.property?``:` disabled`}>
10
- ${r.map(e=>`<option value="${t(e.id)}"${e.id===(n.groupBy?.direction??`asc`)?` selected`:``}>${t(e.label)}</option>`).join(``)}
11
- </select>
12
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-clear-group-by${n.groupBy?.property?``:` disabled`}>Clear</button>
13
- </div>
14
- </div>
15
- <div class="bases-panel-section">
16
- <div class="bases-panel-title">Sort by</div>
17
- <div class="bases-sort-list">
18
- ${i.map((n,r)=>{let s=T(e,n.property)?.sortDirections??[{id:`asc`,label:`A → Z`},{id:`desc`,label:`Z → A`}];return`
19
- <div class="bases-sort-row">
20
- <select class="${t(a({extra:`bases-select bases-sort-property`}))}" data-base-sort-property="${r}">
21
- ${M(e,{selected:n.property})}
22
- </select>
23
- <select class="${t(a({extra:`bases-select bases-sort-direction`}))}" data-base-sort-direction="${r}">
24
- ${s.map(e=>`<option value="${t(e.id)}"${e.id===n.direction?` selected`:``}>${t(e.label)}</option>`).join(``)}
25
- </select>
26
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-sort-move="${r}:up"${r===0?` disabled`:``}>↑</button>
27
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-sort-move="${r}:down"${r===i.length-1?` disabled`:``}>↓</button>
28
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-sort-delete="${r}">Delete</button>
29
- </div>
30
- `}).join(``)}
31
- </div>
32
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-sort-add>Add sort</button>
33
- </div>
34
- </section>
35
- `}function N(e=[]){return e.join(`.`)}function P(e=``){return String(e??``).split(`.`).filter(e=>e!==``).map(e=>Number.parseInt(e,10)).filter(Number.isFinite)}function se(e=``){return JSON.stringify(String(e??``))}function F(e=``,t=`text`,n=`is`){let r=String(e??``).trim();return!r&&n!==`is`&&n!==`is not`?`""`:t===`boolean`?r===`false`?`false`:`true`:t===`number`&&Number.isFinite(Number(r))?String(Number(r)):se(r)}function ce(e,t=null){if(!e?.propertyId||!e?.operator)return``;let n=e.propertyId,r=t?.valueType??`text`;switch(e.operator){case`contains`:return`${n}.contains(${F(e.value,r,e.operator)})`;case`does not contain`:return`!${n}.contains(${F(e.value,r,e.operator)})`;case`starts with`:return`${n}.startsWith(${F(e.value,r,e.operator)})`;case`ends with`:return`${n}.endsWith(${F(e.value,r,e.operator)})`;case`is`:return`${n} == ${F(e.value,r,e.operator)}`;case`is not`:return`${n} != ${F(e.value,r,e.operator)}`;case`>`:case`>=`:case`<`:case`<=`:return`${n} ${e.operator} ${F(e.value,r,e.operator)}`;case`is empty`:return`${n}.isEmpty()`;case`is not empty`:return`!${n}.isEmpty()`;default:return``}}function I(e,t){if(!e||e.type!==`group`)return null;let n=(e.children??[]).map(e=>e?.type===`group`?I(e,t):ce(e,T(t,e?.propertyId))).filter(Boolean);return n.length===0?null:e.conjunction===`not`?{not:n}:{[e.conjunction]:n}}function L(e=``){let t=String(e??``).trim();return t?t===`true`||t===`false`?t:t===`null`?``:/^-?\d+(?:\.\d+)?$/u.test(t)?t:t.startsWith(`"`)&&t.endsWith(`"`)||t.startsWith(`'`)&&t.endsWith(`'`)?t.slice(1,-1).replace(/\\(["'])/gu,`$1`):t:``}function le(e=``){let t=String(e??``).trim(),n=[[/^!([A-Za-z0-9_.]+)\.isEmpty\(\)$/u,(e,t)=>({operator:`is not empty`,propertyId:t,type:`rule`,value:``})],[/^([A-Za-z0-9_.]+)\.isEmpty\(\)$/u,(e,t)=>({operator:`is empty`,propertyId:t,type:`rule`,value:``})],[/^!([A-Za-z0-9_.]+)\.contains\((.+)\)$/u,(e,t,n)=>({operator:`does not contain`,propertyId:t,type:`rule`,value:L(n)})],[/^([A-Za-z0-9_.]+)\.contains\((.+)\)$/u,(e,t,n)=>({operator:`contains`,propertyId:t,type:`rule`,value:L(n)})],[/^([A-Za-z0-9_.]+)\.startsWith\((.+)\)$/u,(e,t,n)=>({operator:`starts with`,propertyId:t,type:`rule`,value:L(n)})],[/^([A-Za-z0-9_.]+)\.endsWith\((.+)\)$/u,(e,t,n)=>({operator:`ends with`,propertyId:t,type:`rule`,value:L(n)})],[/^([A-Za-z0-9_.]+)\s*(==|!=|>=|<=|>|<)\s*(.+)$/u,(e,t,n,r)=>({operator:{"!=":`is not`,"==":`is`,"<":`<`,"<=":`<=`,">":`>`,">=":`>=`}[n]??n,propertyId:t,type:`rule`,value:L(r)})]];for(let[e,r]of n){let n=t.match(e);if(n)return r(...n)}return null}function R(e){if(!e)return p(`and`);if(typeof e==`string`){let t=le(e);return t?{children:[t],conjunction:`and`,type:`group`}:null}if(!f(e))return null;if(Array.isArray(e.and)){let t=e.and.map(e=>R(e)).flatMap(e=>e?.type===`group`&&e.conjunction===`and`&&e.children?.length===1&&e.children[0]?.type===`rule`?e.children:[e]);return t.every(Boolean)?{children:t,conjunction:`and`,type:`group`}:null}if(Array.isArray(e.or)){let t=e.or.map(e=>R(e)).flatMap(e=>e?.type===`group`&&e.conjunction===`and`&&e.children?.length===1&&e.children[0]?.type===`rule`?e.children:[e]);return t.every(Boolean)?{children:t,conjunction:`or`,type:`group`}:null}if(e.not!=null){let t=(Array.isArray(e.not)?e.not:(()=>{let t=R(e.not);return t?t.type===`group`&&t.conjunction===`or`?t.children??[]:[t]:[]})()).flatMap(e=>{let t=e?.type?e:R(e);return t?t.type===`group`&&t.conjunction===`and`&&t.children?.length===1&&t.children[0]?.type===`rule`?t.children:[t]:[null]});return t.every(Boolean)?{children:t,conjunction:`not`,type:`group`}:null}return null}function z(e){return e?typeof e==`string`?e:JSON.stringify(e,null,2):``}function ue(e=``){let t=String(e??``).trim();return t?t.startsWith(`{`)||t.startsWith(`[`)?JSON.parse(t):t:null}function B(e,t=[]){let n=e;for(let e of t)n=n?.children?.[e];return n??null}function V(e,t=[],n){if(t.length===0)return n(e);let[r,...i]=t;return{...e,children:(e.children??[]).map((e,t)=>t===r?V(e,i,n):e)}}function de(e,t=[]){if(t.length===0)return e;let n=t.slice(0,-1),r=t[t.length-1];return V(e,n,e=>({...e,children:(e.children??[]).filter((e,t)=>t!==r)}))}function H(e,t=[],n){return V(e,t,e=>({...e,children:[...e.children??[],n]}))}function U(e,t=``){return M(e,{allowBlank:!0,selected:t})}function fe(e={}){return String(e?.text??e?.value??``).trim()}function W(e,t){return e.ui.builderFilter??R(D(t).filters)??p(`and`)}function G(e,n,r,i){let o=N(r),s=E(e,n.propertyId),c=String(n.value??``).trim().toLowerCase(),l=ae(i,n.propertyId,e).map(e=>({count:e?.count,text:fe(e)})).filter((e,t,n)=>e.text&&n.findIndex(t=>t.text===e.text)===t),u=l.some(e=>e.text.toLowerCase()===c),d=l.filter(e=>!c||e.text.toLowerCase().includes(c)),f=!c||u?l:d.length>0?d:l,p=f.length>0&&![`is empty`,`is not empty`].includes(n.operator);return{encodedPath:o,operators:s,suggestionListId:`bases-filter-suggestions-${t(i.key)}-${t(o.replace(/\./g,`-`))}`,suggestionsEnabled:p,valueInputClasses:a({extra:p?`bases-filter-value bases-filter-value--suggestions`:`bases-filter-value`}),valueSuggestions:f,valueDisabled:[`is empty`,`is not empty`].includes(n.operator)}}function K(e,n=``){return e.suggestionsEnabled?`
36
- <div class="bases-filter-suggestion-list" id="${e.suggestionListId}" role="listbox">
37
- ${e.valueSuggestions.slice(0,8).map(r=>`
38
- <button
39
- type="button"
40
- class="bases-filter-suggestion"
41
- data-base-filter-suggestion="${t(e.encodedPath)}"
42
- data-base-filter-suggestion-value="${t(r.text)}"
43
- role="option"
44
- aria-selected="${r.text===String(n??``)?`true`:`false`}"
45
- >
46
- <span class="bases-filter-suggestion-label">${t(r.text)}</span>
47
- ${r.count==null?``:`<span class="bases-filter-suggestion-meta">${t(String(r.count))}</span>`}
48
- </button>
49
- `).join(``)}
50
- </div>
51
- `:``}function pe(e,n,r,i){let a=G(e,n,r,i);return`
52
- <div class="bases-filter-value-combobox${a.suggestionsEnabled?` has-suggestions`:``}">
53
- <input class="${t(a.valueInputClasses)}" type="text" value="${t(n.value??``)}" data-base-filter-value="${t(a.encodedPath)}"${a.suggestionsEnabled?` aria-controls="${a.suggestionListId}" aria-haspopup="listbox" aria-autocomplete="list" autocomplete="off"`:``}${a.valueDisabled?` disabled`:``}>
54
- ${K(a,n.value??``)}
55
- </div>
56
- `}function me(e,n,r,i){let s=G(e,n,r,i);return`
57
- <div class="bases-filter-row">
58
- <select class="${t(a({extra:`bases-select bases-filter-property`}))}" data-base-filter-property="${t(s.encodedPath)}">
59
- ${U(e,n.propertyId)}
60
- </select>
61
- <select class="${t(a({extra:`bases-select bases-filter-operator`}))}" data-base-filter-operator="${t(s.encodedPath)}">
62
- ${s.operators.map(e=>`<option value="${t(e)}"${e===n.operator?` selected`:``}>${t(e)}</option>`).join(``)}
63
- </select>
64
- ${pe(e,n,r,i)}
65
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-filter-remove="${t(s.encodedPath)}">Delete</button>
66
- </div>
67
- `}function q(e,t,n,{fallbackToFullRender:r=!0}={}){let i=Z(e)?.querySelector?.(`[data-base-panel-slot]`)??null,a=N(n),o=i?.querySelector?.(`[data-base-filter-value="${a}"]`)??null,s=o?.parentElement?.classList?.contains(`bases-filter-value-combobox`)?o.parentElement:null,c=B(W(e,t),n);if(!o||!s||!c||c.type!==`rule`||typeof o.insertAdjacentHTML!=`function`)return r&&$(e,t),!1;let l=G(t,c,n,e);return s.classList.toggle(`has-suggestions`,l.suggestionsEnabled),o.className=l.valueInputClasses,o.disabled=l.valueDisabled,o.value!==String(c.value??``)&&(o.value=String(c.value??``)),l.suggestionsEnabled?(o.setAttribute(`aria-controls`,l.suggestionListId),o.setAttribute(`aria-haspopup`,`listbox`),o.setAttribute(`aria-autocomplete`,`list`),o.setAttribute(`autocomplete`,`off`)):(o.removeAttribute(`aria-controls`),o.removeAttribute(`aria-haspopup`),o.removeAttribute(`aria-autocomplete`),o.removeAttribute(`autocomplete`)),s.querySelector(`.bases-filter-suggestion-list`)?.remove?.(),l.suggestionsEnabled&&o.insertAdjacentHTML(`afterend`,K(l,c.value??``)),!0}function J(e,t,n=[]){if(e){if(e.type===`rule`){t(e,n);return}(e.children??[]).forEach((e,r)=>{J(e,t,[...n,r])})}}function Y(e,n,r,i,{isRoot:s=!1}={}){let c=N(r);return`
68
- <div class="bases-filter-group"${s?` data-base-filter-root`:``}>
69
- <div class="bases-filter-group-header">
70
- <select class="${t(a({extra:`bases-select bases-filter-conjunction`}))}" data-base-filter-conjunction="${t(c)}">
71
- ${[`and`,`or`,`not`].map(e=>`<option value="${e}"${n.conjunction===e?` selected`:``}>${t({and:`All the following are true`,not:`None of the following are true`,or:`Any of the following are true`}[e])}</option>`).join(``)}
72
- </select>
73
- ${s?``:`<button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-filter-remove="${t(c)}">Delete group</button>`}
74
- </div>
75
- <div class="bases-filter-children">
76
- ${(n.children??[]).map((t,n)=>t?.type===`group`?Y(e,t,[...r,n],i):me(e,t,[...r,n],i)).join(``)}
77
- </div>
78
- <div class="bases-filter-actions-row">
79
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-filter-add-rule="${t(c)}">Add filter</button>
80
- <button type="button" class="${t(o({variant:`secondary`,size:`compact`}))}" data-base-filter-add-group="${t(c)}">Add filter group</button>
81
- </div>
82
- </div>
83
- `}function he(e,n){let r=D(e).filters,i=n.ui.builderFilter??R(r);n.ui.builderFilter??(n.ui.builderFilter=i);let s=i?n.ui.filterMode??`builder`:`advanced`,c=n.ui.rawFilterText??z(r);return`
84
- <section class="bases-panel-card">
85
- <div class="bases-panel-title-row">
86
- <div class="bases-panel-title">This view</div>
87
- ${i?`
88
- <div class="bases-toggle-row">
89
- <button type="button" class="${t(o({variant:s===`builder`?`primary`:`secondary`,size:`compact`}))}" data-base-filter-mode="builder">Builder</button>
90
- <button type="button" class="${t(o({variant:s===`advanced`?`primary`:`secondary`,size:`compact`}))}" data-base-filter-mode="advanced">Advanced</button>
91
- </div>
92
- `:``}
93
- </div>
94
- ${s===`advanced`?`
95
- <label class="bases-filter-advanced-label">
96
- <textarea class="${t(a({extra:`bases-filter-advanced`}))}" data-base-filter-advanced>${t(c)}</textarea>
97
- </label>
98
- <div class="bases-filter-actions-row">
99
- <button type="button" class="${t(o({variant:`primary`,size:`compact`}))}" data-base-filter-save-advanced>Save filter</button>
100
- </div>
101
- `:Y(e,i??p(`and`),[],n,{isRoot:!0})}
102
- </section>
103
- `}function ge(e,n){let r=String(n.ui.propertySearch??``).trim().toLowerCase(),i=new Set(w(e)),o=C(e).filter(e=>!r||e.label.toLowerCase().includes(r)||e.id.toLowerCase().includes(r));return`
104
- <section class="bases-panel-card">
105
- <label class="bases-properties-search">
106
- <input class="${t(a({extra:`bases-properties-search-input`}))}" type="search" value="${t(n.ui.propertySearch??``)}" placeholder="Find property" data-base-properties-search>
107
- </label>
108
- <div class="bases-properties-list" data-base-properties-list>
109
- ${o.map(e=>`
110
- <label class="bases-property-option">
111
- <input type="checkbox" data-base-property-toggle="${t(e.id)}"${i.has(e.id)?` checked`:``}>
112
- <span class="bases-property-option-label">${t(e.label)}</span>
113
- <span class="bases-property-option-meta">${t(e.kind)}</span>
114
- </label>
115
- `).join(``)}
116
- </div>
117
- </section>
118
- `}function _e(e){let t=e?.querySelector?.(`[data-base-properties-list]`)??null,n=e?.querySelector?.(`[data-base-properties-search]`)??null,r=typeof document<`u`?document.activeElement:null,i=null;if(n&&r===n){let e=typeof n.selectionStart==`number`?n.selectionStart:null;i={selectionEnd:typeof n.selectionEnd==`number`?n.selectionEnd:e,selectionStart:e,type:`properties-search`}}else if(r?.dataset?.baseFilterValue!=null){let t=String(r.dataset.baseFilterValue),n=e?.querySelector?.(`[data-base-filter-value="${t}"]`)??null;if(n&&r===n){let e=typeof n.selectionStart==`number`?n.selectionStart:null;i={path:t,selectionEnd:typeof n.selectionEnd==`number`?n.selectionEnd:e,selectionStart:e,type:`filter-value`}}}return{focusedControl:i,propertiesListScrollTop:t?t.scrollTop:null}}function ve(e,t){if(t){if(t.propertiesListScrollTop!=null){let n=e?.querySelector?.(`[data-base-properties-list]`)??null;n&&(n.scrollTop=t.propertiesListScrollTop)}if(t.focusedControl?.type===`properties-search`){let n=e?.querySelector?.(`[data-base-properties-search]`)??null;n&&(n.focus?.(),typeof n.setSelectionRange==`function`&&t.focusedControl.selectionStart!=null&&n.setSelectionRange(t.focusedControl.selectionStart,t.focusedControl.selectionEnd??t.focusedControl.selectionStart));return}if(t.focusedControl?.type===`filter-value`&&t.focusedControl.path!=null){let n=e?.querySelector?.(`[data-base-filter-value="${t.focusedControl.path}"]`)??null;n&&(n.focus?.(),typeof n.setSelectionRange==`function`&&t.focusedControl.selectionStart!=null&&n.setSelectionRange(t.focusedControl.selectionStart,t.focusedControl.selectionEnd??t.focusedControl.selectionStart))}}}function X(e,t){if(!t.ui?.openPanel)return``;if(!S(e).editable)return`<section class="bases-panel-card"><div class="bases-readonly-note">Inline base previews are read-only. Open the .base file to edit its sort, filter, and properties.</div></section>`;switch(t.ui.openPanel){case`filter`:return he(e,t);case`properties`:return ge(e,t);default:return oe(e)}}function ye(e,n){return`
119
- <section class="bases-shell" data-base-shell-key="${t(n.key)}">
120
- <header class="bases-toolbar">
121
- <div class="bases-toolbar-main">
122
- <div class="${t(s({pill:!0,extra:`bases-tabs`}))}" data-base-tabs>
123
- ${x(e)}
124
- </div>
125
- <div class="bases-meta" data-base-meta>${t(String(e.totalRows??0))} results</div>
126
- </div>
127
- <div class="bases-toolbar-actions">
128
- <div class="bases-toolbar-edit-actions" data-base-action-slot>${j(e,n)}</div>
129
- <label class="bases-search-shell" aria-label="Search this base">
130
- <svg class="bases-search-icon" 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">
131
- <circle cx="11" cy="11" r="7"></circle>
132
- <path d="m20 20-3.5-3.5"></path>
133
- </svg>
134
- <input class="${t(a({extra:`bases-search-input`}))}" type="search" value="${t(n.search)}" placeholder="Search this base" aria-label="Search this base">
135
- </label>
136
- <button type="button" class="${t(o({variant:`secondary`,pill:!0,extra:`bases-export-btn`}))}">Export CSV</button>
137
- </div>
138
- </header>
139
- <div class="bases-panels" data-base-panel-slot>${X(e,n)}</div>
140
- <div data-base-summary-slot>${y(e.summaries)}</div>
141
- <div class="bases-content" data-base-content>${b(e)}</div>
142
- </section>
143
- `}function Z(e){return e?.placeholder?.querySelector?e.placeholder.querySelector(`[data-base-shell-key="${e.key}"]`)??e.placeholder.querySelector(`.bases-shell`):null}function be(e){e?.classList&&(e.classList.add(`is-hydrated`),e.classList.remove(`diagram-preview-shell`))}function Q(e,t,{actions:n=!0,body:r=!0,meta:i=!0,panel:a=!0,summary:o=!0,tabs:s=!0}={}){let c=Z(e);if(!c?.querySelector){e.placeholder.innerHTML=ye(t,e),be(e.placeholder);return}let l=c.querySelector(`[data-base-tabs]`);s&&l&&(l.innerHTML=x(t));let u=c.querySelector(`[data-base-meta]`);i&&u&&(u.textContent=`${String(t.totalRows??0)} results`);let d=c.querySelector(`[data-base-action-slot]`);n&&d&&(d.innerHTML=j(t,e));let f=c.querySelector(`[data-base-panel-slot]`);if(a&&f){let n=_e(f);f.innerHTML=X(t,e),ve(f,n)}let p=c.querySelector(`[data-base-summary-slot]`);o&&p&&(p.innerHTML=y(t.summaries));let m=c.querySelector(`[data-base-content]`);r&&m&&(m.innerHTML=b(t));let h=c.querySelector(`.bases-search-input`);h&&h.value!==e.search&&(h.value=e.search)}function $(e,t){Q(e,t,{actions:!1,body:!1,meta:!1,summary:!1,tabs:!1})}var xe=class{constructor({getActiveFilePath:e=()=>``,getSession:t=()=>null,onOpenFile:n,previewElement:r,replaceBaseSource:i=null,toastController:a,vaultApiClient:o}){this.getActiveFilePath=e,this.getSession=t,this.onOpenFile=n,this.previewElement=r,this.replaceBaseSource=i,this.toastController=a,this.vaultApiClient=o,this.entries=new Map,this.searchTimers=new Map,this.applyFilterSuggestion=(e,t,n)=>{this.updateBuilderFilter(e,e=>V(e,t,e=>({...e,value:n})))},this.persistBuilderFilter=e=>{let t=W(e,e.result);e.ui.builderFilter=t,this.updateViewConfig(e,n=>(n.filters=I(t,e.result),n))},this.handlePointerDown=e=>{let t=e.target.closest(`[data-base-filter-suggestion]`);if(!t)return;let n=e.target.closest(`[data-base-shell-key]`),r=n?this.entries.get(n.dataset.baseShellKey||``):null;if(!r)return;e.preventDefault();let i=P(t.dataset.baseFilterSuggestion||``),a=t.dataset.baseFilterSuggestionValue||``;this.applyFilterSuggestion(r,i,a)},this.handleClick=e=>{let t=e.target.closest(`[data-base-open-file]`);if(t){e.preventDefault(),this.onOpenFile?.(t.dataset.baseOpenFile||``);return}let n=e.target.closest(`[data-base-shell-key]`),r=n?this.entries.get(n.dataset.baseShellKey||``):null;if(!r)return;let i=e.target.closest(`.bases-view-tab`);if(i){r.payload.view=i.dataset.baseView||``,r.ui.openPanel=``,this.renderEntry(r);return}let a=e.target.closest(`[data-base-panel]`);if(a){let e=a.dataset.basePanel||``;r.ui.openPanel=r.ui.openPanel===e?``:e,r.ui.rawFilterText=z(D(r.result).filters),$(r,r.result);return}if(e.target.closest(`.bases-export-btn`)){this.exportEntry(r);return}if(e.target.closest(`[data-base-sort-add]`)){this.updateViewConfig(r,e=>{let t=new Set((e.sort??[]).map(e=>e.property)),n=C(r.result).find(e=>!t.has(e.id))?.id??`file.name`;return e.sort=[...e.sort??[],{direction:`asc`,property:n}],e});return}let o=e.target.closest(`[data-base-sort-delete]`);if(o){let e=Number.parseInt(o.dataset.baseSortDelete||``,10);this.updateViewConfig(r,t=>(t.sort=(t.sort??[]).filter((t,n)=>n!==e),t));return}let s=e.target.closest(`[data-base-sort-move]`);if(s){let[e,t]=String(s.dataset.baseSortMove||``).split(`:`),n=Number.parseInt(e,10);this.updateViewConfig(r,e=>{let r=[...e.sort??[]],i=t===`up`?n-1:n+1;return i<0||i>=r.length?e:([r[n],r[i]]=[r[i],r[n]],e.sort=r,e)});return}if(e.target.closest(`[data-base-clear-group-by]`)){this.updateViewConfig(r,e=>(e.groupBy=null,e));return}let c=e.target.closest(`[data-base-filter-mode]`);if(c){r.ui.filterMode=c.dataset.baseFilterMode||`builder`,r.ui.filterMode===`advanced`&&(r.ui.rawFilterText=z(D(r.result).filters),r.ui.builderFilter=null),$(r,r.result);return}if(e.target.closest(`[data-base-filter-save-advanced]`)){this.saveAdvancedFilter(r);return}let l=e.target.closest(`[data-base-filter-add-rule]`);if(l){let e=P(l.dataset.baseFilterAddRule||``);this.updateBuilderFilter(r,t=>H(t,e,{operator:`is`,propertyId:C(r.result)[0]?.id??`file.name`,type:`rule`,value:``}));return}let u=e.target.closest(`[data-base-filter-add-group]`);if(u){let e=P(u.dataset.baseFilterAddGroup||``);this.updateBuilderFilter(r,t=>H(t,e,p(`and`)));return}let d=e.target.closest(`[data-base-filter-remove]`);if(d){let e=P(d.dataset.baseFilterRemove||``);this.updateBuilderFilter(r,t=>de(t,e));return}let f=e.target.closest(`[data-base-filter-suggestion]`);if(f){if(e.preventDefault?.(),e.detail!=null&&e.detail!==0)return;let t=P(f.dataset.baseFilterSuggestion||``),n=f.dataset.baseFilterSuggestionValue||``;this.applyFilterSuggestion(r,t,n)}},this.handleInput=e=>{let t=e.target.closest(`.bases-search-input`);if(t){let e=t.closest(`[data-base-shell-key]`),n=e?this.entries.get(e.dataset.baseShellKey||``):null;if(!n)return;n.search=t.value||``,clearTimeout(this.searchTimers.get(n.key)),this.searchTimers.set(n.key,setTimeout(()=>{n.payload.search=n.search,this.renderEntry(n)},180));return}let n=e.target.closest(`[data-base-properties-search]`);if(n){let e=n.closest(`[data-base-shell-key]`),t=e?this.entries.get(e.dataset.baseShellKey||``):null;if(!t)return;t.ui.propertySearch=n.value||``,$(t,t.result);return}let r=e.target.closest(`[data-base-filter-advanced]`);if(r){let e=r.closest(`[data-base-shell-key]`),t=e?this.entries.get(e.dataset.baseShellKey||``):null;if(!t)return;t.ui.rawFilterText=r.value||``;return}let i=e.target.closest(`[data-base-filter-value]`);if(i){let e=i.closest(`[data-base-shell-key]`),t=e?this.entries.get(e.dataset.baseShellKey||``):null;if(!t)return;let n=P(i.dataset.baseFilterValue||``),r=t.ui.builderFilter??R(D(t.result).filters)??p(`and`);t.ui.builderFilter=V(r,n,e=>({...e,value:i.value||``})),q(t,t.result,n)}},this.handleChange=e=>{let t=e.target.closest(`[data-base-shell-key]`),n=t?this.entries.get(t.dataset.baseShellKey||``):null;if(!n)return;let r=e.target.closest(`[data-base-group-by-property]`);if(r){this.updateViewConfig(n,e=>(e.groupBy=r.value?{direction:e.groupBy?.direction??`asc`,property:r.value}:null,e));return}let i=e.target.closest(`[data-base-group-by-direction]`);if(i){this.updateViewConfig(n,e=>(e.groupBy=e.groupBy?.property?{direction:i.value||`asc`,property:e.groupBy.property}:null,e));return}let a=e.target.closest(`[data-base-sort-property]`);if(a){let e=Number.parseInt(a.dataset.baseSortProperty||``,10);this.updateViewConfig(n,t=>(t.sort=(t.sort??[]).map((t,n)=>n===e?{...t,property:a.value}:t),t));return}let o=e.target.closest(`[data-base-sort-direction]`);if(o){let e=Number.parseInt(o.dataset.baseSortDirection||``,10);this.updateViewConfig(n,t=>(t.sort=(t.sort??[]).map((t,n)=>n===e?{...t,direction:o.value||`asc`}:t),t));return}let s=e.target.closest(`[data-base-property-toggle]`);if(s){let e=s.dataset.basePropertyToggle||``;this.updateViewConfig(n,t=>{let r=t.order?.length>0?[...t.order]:w(n.result),i=r.indexOf(e);return s.checked&&i===-1&&r.push(e),!s.checked&&i>=0&&r.splice(i,1),t.order=r,t});return}let c=e.target.closest(`[data-base-filter-conjunction]`);if(c){let e=P(c.dataset.baseFilterConjunction||``);this.updateBuilderFilter(n,t=>V(t,e,e=>({...e,conjunction:c.value||`and`})));return}let l=e.target.closest(`[data-base-filter-property]`);if(l){let e=P(l.dataset.baseFilterProperty||``);(async()=>{await this.updateBuilderFilter(n,t=>V(t,e,e=>{let t=E(n.result,l.value)[0]??`is`;return{...e,operator:t,propertyId:l.value,value:``}})),await this.ensurePropertyValues(n,l.value)})();return}let u=e.target.closest(`[data-base-filter-operator]`);if(u){let e=P(u.dataset.baseFilterOperator||``);this.updateBuilderFilter(n,t=>V(t,e,e=>({...e,operator:u.value||`is`})));return}e.target.closest(`[data-base-filter-value]`)&&this.persistBuilderFilter(n)},this.handleFocusIn=e=>{let t=e.target.closest(`[data-base-filter-value]`);if(!t)return;let n=t.closest(`[data-base-shell-key]`),r=n?this.entries.get(n.dataset.baseShellKey||``):null;if(!r)return;let i=P(t.dataset.baseFilterValue||``),a=R(D(r.result).filters),o=a?B(a,i):null;o?.propertyId&&this.ensurePropertyValues(r,o.propertyId)},this.previewElement?.addEventListener(`pointerdown`,this.handlePointerDown),this.previewElement?.addEventListener(`click`,this.handleClick),this.previewElement?.addEventListener(`input`,this.handleInput),this.previewElement?.addEventListener(`change`,this.handleChange),this.previewElement?.addEventListener(`focusin`,this.handleFocusIn)}ensureEntryState(e){e.ui||={builderFilter:null,filterMode:`builder`,openPanel:``,propertySearch:``,rawFilterText:``},Object.prototype.hasOwnProperty.call(e.ui,`builderFilter`)||(e.ui.builderFilter=null),e.propertyValueOptions||=new Map}destroy(){this.previewElement?.removeEventListener(`pointerdown`,this.handlePointerDown),this.previewElement?.removeEventListener(`click`,this.handleClick),this.previewElement?.removeEventListener(`input`,this.handleInput),this.previewElement?.removeEventListener(`change`,this.handleChange),this.previewElement?.removeEventListener(`focusin`,this.handleFocusIn),this.searchTimers.forEach(e=>clearTimeout(e)),this.searchTimers.clear(),this.entries.clear()}reconcileEmbeds(e=this.previewElement){e&&Array.from(e.querySelectorAll(`.bases-embed-placeholder[data-base-key]`)).forEach(e=>{let t=e.dataset.baseKey||l(),n=this.entries.get(t),r={path:e.dataset.basePath||``,search:``,source:e.dataset.baseSource||null,sourcePath:e.dataset.baseSourcePath||``,view:e.dataset.baseView||``},i=n??{key:t,payload:r,placeholder:e,propertyValueOptions:new Map,requestVersion:0,result:null,search:``,ui:{builderFilter:null,filterMode:`builder`,openPanel:``,propertySearch:``,rawFilterText:``}};i.payload=r,i.placeholder=e,this.entries.set(t,i),this.renderEntry(i)})}async renderStandalone({filePath:e,renderHost:t,source:n=null}){let r=`standalone:${e}`,i=this.entries.get(r)??{key:r,payload:{path:e,search:``,source:n,sourcePath:e,view:``},placeholder:t,propertyValueOptions:new Map,requestVersion:0,result:null,search:``,ui:{builderFilter:null,filterMode:`builder`,openPanel:``,propertySearch:``,rawFilterText:``}};i.placeholder=t,i.payload.path=e,i.payload.source=typeof n==`string`?n:null,i.payload.sourcePath=e,this.entries.set(r,i),await this.renderEntry(i)}async renderEntry(e){if(this.ensureEntryState(e),!e?.placeholder?.isConnected)return;let n=(e.requestVersion??0)+1;e.requestVersion=n;let r=!!Z(e);r||(e.placeholder.innerHTML=`<div class="preview-shell">Loading base…</div>`);try{let t=await this.vaultApiClient.queryBase({activeFilePath:this.getActiveFilePath?.()??``,...e.payload});if(n!==e.requestVersion||!e?.placeholder?.isConnected)return;e.result=t.result,e.ui.builderFilter=R(D(e.result).filters),e.ui.rawFilterText||(e.ui.rawFilterText=z(D(e.result).filters)),Q(e,e.result)}catch(i){if(n!==e.requestVersion||!e?.placeholder?.isConnected)return;if(r){let n=Z(e)?.querySelector?.(`[data-base-content]`)??null;if(n){n.innerHTML=`<div class="preview-shell">Failed to load base: ${t(i.message||`Unknown error`)}</div>`;return}}e.placeholder.innerHTML=`<div class="preview-shell">Failed to load base: ${t(i.message||`Unknown error`)}</div>`}}async exportEntry(e){this.ensureEntryState(e);try{let t=await this.vaultApiClient.exportBaseCsv({activeFilePath:this.getActiveFilePath?.()??``,...e.payload});i(t.blob,r(t.contentDisposition,`base.csv`),{removeDelayMs:0,revokeDelayMs:0})}catch(e){this.toastController?.show?.(e.message||`Failed to export base CSV`)}}async ensurePropertyValues(e,t=``){if(this.ensureEntryState(e),!t)return;let n=A(e,t);if((e.propertyValueOptions.get(t)??null)?.cacheKey!==n)try{let r=await this.vaultApiClient.queryBasePropertyValues({activeFilePath:this.getActiveFilePath?.()??``,...e.payload,propertyId:t});if(e.propertyValueOptions.set(t,{cacheKey:n,values:r.result?.values??[]}),e.result&&e.ui.openPanel===`filter`){let n=!1;J(W(e,e.result),(r,i)=>{r.propertyId===t&&(n=q(e,e.result,i,{fallbackToFullRender:!1})||n)}),n||$(e,e.result)}}catch{e.propertyValueOptions.set(t,{cacheKey:n,values:[]})}}async applyTransformedSource(e,t){if(this.ensureEntryState(e),ie(e)){e.payload.source=t.source,this.replaceBaseSource?.({path:e.payload.path,source:t.source});return}await this.vaultApiClient.writeFile({content:t.source,path:e.payload.path}),e.payload.source=null}async updateViewConfig(e,t){if(this.ensureEntryState(e),!e?.result||!S(e.result).editable)return;let n=t(D(e.result));try{let t=(await this.vaultApiClient.transformBase({activeFilePath:this.getActiveFilePath?.()??``,mutation:{config:n,type:`set-view-config`,view:e.result.view.id},...e.payload})).result;await this.applyTransformedSource(e,t),e.result=t.result,e.ui.rawFilterText=z(D(e.result).filters),Q(e,e.result)}catch(e){this.toastController?.show?.(e.message||`Failed to update base`)}}async updateBuilderFilter(e,t){this.ensureEntryState(e);let n=t(e.ui.builderFilter??R(D(e.result).filters)??p(`and`));e.ui.builderFilter=n,await this.updateViewConfig(e,t=>(t.filters=I(n,e.result),t))}async saveAdvancedFilter(e){this.ensureEntryState(e);try{let t=ue(e.ui.rawFilterText??``);e.ui.builderFilter=R(t),await this.updateViewConfig(e,e=>(e.filters=t,e))}catch(e){this.toastController?.show?.(e.message||`Invalid advanced filter`)}}};export{xe as BasesPreviewController};
@@ -1 +0,0 @@
1
- function e(){return typeof window>`u`?globalThis:window}function t(t,n){let r=e();return typeof r.requestIdleCallback==`function`?r.requestIdleCallback(t,{timeout:n}):r.setTimeout(()=>t({didTimeout:!1,timeRemaining:()=>0}),1)}function n(t){if(t===null)return;let n=e();if(typeof n.cancelIdleCallback==`function`){n.cancelIdleCallback(t);return}n.clearTimeout(t)}function r(e,t,n){if(!e||!t)return!1;let r=t.getBoundingClientRect(),i=e.getBoundingClientRect();return i.bottom>=r.top-n&&i.top<=r.bottom+n}function i(t,n,{removeDelayMs:r=3e4,revokeDelayMs:i=r}={}){let a=e(),o=URL.createObjectURL(t),s=document.createElement(`a`);s.href=o,s.download=n,s.style&&(s.style.display=`none`),document.body.appendChild(s),s.click(),r>0?a.setTimeout(()=>s.remove(),r):s.remove(),a.setTimeout(()=>URL.revokeObjectURL(o),i)}function a(e=``,t=`download`){let n=String(e).match(/filename\*=UTF-8''([^;]+)/iu);if(n)try{return decodeURIComponent(n[1])}catch{return t}return String(e).match(/filename="([^"]+)"/iu)?.[1]||t}export{t as a,a as i,i as n,r,n as t};
@@ -1 +0,0 @@
1
- function e(...e){return e.flat().flatMap(e=>e&&typeof e==`object`?Object.keys(e).filter(t=>e[t]):e||[]).join(` `)}function t(t={}){return e(`ui-input`,t.extra??[])}function n(t={}){let{variant:n=`secondary`,size:r=`md`,toggle:i=!1,toolbar:a=!1,action:o=!1,wide:s=!1,surface:c=!1,pill:l=!1,hidden:u=!1,active:d=!1,extra:f=[]}=t;return e(`ui-button`,n&&`ui-button--${n}`,r!==`md`&&`ui-button--${r}`,{"ui-button--toggle":i,"ui-button--toolbar":a,"ui-button--action":o,"ui-button--wide":s,"ui-button--surface":c,"ui-button--pill":l,active:d,hidden:u},f)}export{e as n,t as r,n as t};