collabmd 0.1.47 → 0.1.49

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 (305) hide show
  1. package/README.md +70 -11
  2. package/bin/collabmd.js +3 -0
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-C-nmgL5g.js +1 -0
  4. package/dist/client/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -0
  5. package/dist/client/assets/abnfDiagram-VCTEODGH-CdVZqicp.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-5GKGNRK7-I39sYjHI.js → architectureDiagram-5GKGNRK7-wUo0bXif.js} +3 -3
  7. package/dist/client/assets/babel-CY5KOv-I.js +16 -0
  8. package/dist/client/assets/{blockDiagram-NRAW4CY4-C6DtJy5Y.js → blockDiagram-I7D4REHJ-BRSK3m49.js} +4 -4
  9. package/dist/client/assets/browser-utils-DbXSMI-6.js +1 -0
  10. package/dist/client/assets/{c4Diagram-UCG6FXSJ-CtWtxS9_.js → c4Diagram-7LVT6UL2-yD2poxqw.js} +1 -1
  11. package/dist/client/assets/channel-Fa4GS_-F.js +1 -0
  12. package/dist/client/assets/chunk-2E4U76K2-BPnTT-J3.js +2 -0
  13. package/dist/client/assets/{chunk-TEH6E4GO-DVayPj-q.js → chunk-4HAMMTFA-DStbIfJ1.js} +20 -20
  14. package/dist/client/assets/{chunk-75Z2AOVW-CdFSaPjz.js → chunk-75Z2AOVW-C5pGd3W9.js} +1 -1
  15. package/dist/client/assets/{chunk-CLGD4ZFX-DQLgbQrv.js → chunk-CLGD4ZFX-BMeArk03.js} +1 -1
  16. package/dist/client/assets/chunk-DU6HZSFF-Zkp69c6c.js +127 -0
  17. package/dist/client/assets/{chunk-F27PBJKO-CNHIFhjo.js → chunk-F27PBJKO-D4xNct1r.js} +1 -1
  18. package/dist/client/assets/{chunk-GMAD6QVW-CbcBqPM5.js → chunk-GMAD6QVW-NZ6WpDb8.js} +1 -1
  19. package/dist/client/assets/{chunk-OBVCFTLP-BJR7u1jh.js → chunk-GVQU2GXP-D8SDprfX.js} +1 -1
  20. package/dist/client/assets/{chunk-G27WJ6UU-D9-eg537.js → chunk-IMKFNOWR-D8jEuU3Q.js} +2 -2
  21. package/dist/client/assets/{chunk-JQ64N6SF-DSFNH4bh.js → chunk-L3NEJ4N5-aijKP-eh.js} +1 -1
  22. package/dist/client/assets/chunk-OSK3NFVY-CCbLQAMa.js +10 -0
  23. package/dist/client/assets/{chunk-P2QGCYS3-BRpqY32H.js → chunk-P2QGCYS3-Oag_FdCq.js} +1 -1
  24. package/dist/client/assets/{chunk-POPQ4Y6H-Cb4SlVZF.js → chunk-POPQ4Y6H-CYnpIgXT.js} +1 -1
  25. package/dist/client/assets/{chunk-PWAF6VOD-Cz8IwBB5.js → chunk-PWAF6VOD-JSNrcBmP.js} +1 -1
  26. package/dist/client/assets/{chunk-RM4UPSZO-CWRwxNWJ.js → chunk-RM4UPSZO-DOWllDY8.js} +4 -4
  27. package/dist/client/assets/{chunk-RHFEMEQ7-CWY8cf4H.js → chunk-SHT3W25Y-BiQGljao.js} +5 -5
  28. package/dist/client/assets/{chunk-SVP7TREG-CyHOmcac.js → chunk-SVP7TREG-DB485R5x.js} +1 -1
  29. package/dist/client/assets/{chunk-LCL6LL3I-DmDkVukh.js → chunk-TICWLB2K-8rAVvoS3.js} +4 -4
  30. package/dist/client/assets/{chunk-CKBBP62Z-DG26UiBv.js → chunk-TLUHSLCS-DTY5v0aj.js} +2 -2
  31. package/dist/client/assets/classDiagram-ZZMXUADV-BsQWoR3Q.js +1 -0
  32. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BsQWoR3Q.js +1 -0
  33. package/dist/client/assets/{comment-thread-store-CdcwrObc.js → comment-thread-store-CX98zR6q.js} +1 -1
  34. package/dist/client/assets/comment-threads-BnRwo5B9.js +4 -0
  35. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CdTPi2yO.js → cynefinDiagram-5FMLGOSQ-rTz9RhFH.js} +1 -1
  36. package/dist/client/assets/{dagre-3AP2YEHR-5gtJtd0O.js → dagre-GXQ25YYZ-Cr67wLpQ.js} +2 -2
  37. package/dist/client/assets/{diagram-S7CK7UJ4-BudWz9WU.js → diagram-S7CK7UJ4-DywERe__.js} +2 -2
  38. package/dist/client/assets/{diagram-UQ7AKVKN-3v4rbH-s.js → diagram-UQ7AKVKN-CNYlXtu4.js} +1 -1
  39. package/dist/client/assets/diagram-VSXAHHWV-0H1a61Bn.js +3 -0
  40. package/dist/client/assets/{diagram-VX7I27RA-C_-rj0sw.js → diagram-VX7I27RA-CsJbsx5F.js} +1 -1
  41. package/dist/client/assets/{diagram-Z3DM3KII-B4yxavgj.js → diagram-Z3DM3KII-D0mLxEgI.js} +1 -1
  42. package/dist/client/assets/dist--hk9I5HQ.js +1 -0
  43. package/dist/client/assets/{dist-DutPuFmC.js → dist-B1XEa-Zs.js} +1 -1
  44. package/dist/client/assets/dist-Bd1JucQF.js +1 -0
  45. package/dist/client/assets/{dist-DZAAWTG-.js → dist-C0G3AaXG.js} +1 -1
  46. package/dist/client/assets/dist-ChRoo3IB.js +9 -0
  47. package/dist/client/assets/dist-Dm4w5UId.js +1 -0
  48. package/dist/client/assets/{dist-B_yQaEv_.js → dist-DpYDHR9p.js} +1 -1
  49. package/dist/client/assets/{dist-CietwTVS.js → dist-DzNmC81X.js} +1 -1
  50. package/dist/client/assets/{dist-DDUn5zTz.js → dist-GvvCNBe4.js} +1 -1
  51. package/dist/client/assets/dist-JaU3aRlD.js +19 -0
  52. package/dist/client/assets/{dist-Bw3VIE_v.js → dist-TW8iFRyW.js} +1 -1
  53. package/dist/client/assets/{dist-Dir5dDKZ.js → dist-jVU5Be5P.js} +1 -1
  54. package/dist/client/assets/document-formatter-rDDjRfZ2.js +2 -0
  55. package/dist/client/assets/drawioEditor-JubEEha7.js +9 -0
  56. package/dist/client/assets/ebnfDiagram-PWID7BFC-HeTxHpCU.js +1 -0
  57. package/dist/client/assets/editor-session-Bk6oIvSr.js +54 -0
  58. package/dist/client/assets/embedded-editor-base-BlCgz6mw.css +1 -0
  59. package/dist/client/assets/{erDiagram-SSCWMZ5O-C-iy6VkX.js → erDiagram-RLTQ6QDP-CbrDdd5P.js} +2 -2
  60. package/dist/client/assets/{es-CAsWR6hG.js → es-Bfdp096f.js} +13 -13
  61. package/dist/client/assets/estree-CuT_pB8K.js +44 -0
  62. package/dist/client/assets/excalidraw-element-link-YLIaTnVF.js +7 -0
  63. package/dist/client/assets/{excalidraw-scene-BOben2gr.js → excalidraw-scene-6uROebca.js} +1 -1
  64. package/dist/client/assets/{excalidraw-editor-BlcN6umJ.css → excalidrawEditor-BbSbI80a.css} +1 -1
  65. package/dist/client/assets/excalidrawEditor-dBMbRX_S.js +9 -0
  66. package/dist/client/assets/export-page-CRRQ2NEl.js +57 -0
  67. package/dist/client/assets/exportDocument-BlQRd9_m.js +2 -0
  68. package/dist/client/assets/exportDocument-CNSbjZwu.css +1 -0
  69. package/dist/client/assets/{file-icon-svg-CQAjlWjK.js → file-icon-svg-Y_kpF957.js} +1 -1
  70. package/dist/client/assets/file-kind-BwMIcZbi.js +1 -0
  71. package/dist/client/assets/file-search-DDLyyLDU.js +1 -0
  72. package/dist/client/assets/flowDiagram-HODETNUW-D5Gy0wmg.js +1 -0
  73. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DtKHAbez.js → ganttDiagram-EL5Y4UJY-BHgOYiZz.js} +1 -1
  74. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-oue8fQ5o.js → gitGraphDiagram-WWUBYQGX-Dl__X3Sk.js} +1 -1
  75. package/dist/client/assets/html-Cdnr2qzU.js +25 -0
  76. package/dist/client/assets/image-ZVKINSPA-DgzW5zAy.js +1 -0
  77. package/dist/client/assets/index-D3RER1SZ.js +1023 -0
  78. package/dist/client/assets/index-DREVKl_K.css +1 -0
  79. package/dist/client/assets/{infoDiagram-RXCK75RN-BMzo6KIn.js → infoDiagram-27XIBGKW-DBpoANC9.js} +1 -1
  80. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BIM3h287.js → ishikawaDiagram-5VMMS53U-BMPC6hfp.js} +1 -1
  81. package/dist/client/assets/{journeyDiagram-EYS64GPL-mF6Lh8O-.js → journeyDiagram-3NMN7TZE-B1sApUmM.js} +2 -2
  82. package/dist/client/assets/{kanban-definition-3QL26DDD-DLN2NyoR.js → kanban-definition-UXKFOSKX-C9yP4td1.js} +4 -4
  83. package/dist/client/assets/{line-BfTWJmgf.js → line-BFl3ujec.js} +1 -1
  84. package/dist/client/assets/markdown-CByV-4CU.js +70 -0
  85. package/dist/client/assets/{mermaid.core-CLZBlpIW.js → mermaid.core-BrY09czX.js} +5 -5
  86. package/dist/client/assets/mermaid.core-DfcNfOTv.js +1 -0
  87. package/dist/client/assets/{mindmap-definition-FBJOCRG2-CRyHaHTk.js → mindmap-definition-YA3MSWOX-BUQHcYbK.js} +8 -8
  88. package/dist/client/assets/pegDiagram-XKGWAZYB-Bi2Iml7Z.js +1 -0
  89. package/dist/client/assets/{pieDiagram-E7YTZNPT-B5OZPb88.js → pieDiagram-E7YTZNPT-_gYXnMoi.js} +1 -1
  90. package/dist/client/assets/preview-render-compiler-CfxJYldJ.js +41 -0
  91. package/dist/client/assets/preview-render-compiler-Cu8_i9iS.js +1 -0
  92. package/dist/client/assets/{preview-render-worker-BmN79bVc.js → preview-render-worker-c4Yi93qW.js} +41 -35
  93. package/dist/client/assets/prod-BiJL30sl.js +1 -0
  94. package/dist/client/assets/prod-qAk7rHa4.js +177 -0
  95. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-Drrw14YH.js → quadrantDiagram-AXDQQJYC-DwexhEhd.js} +1 -1
  96. package/dist/client/assets/quick-switcher-controller-BoIK_wuG.js +1 -0
  97. package/dist/client/assets/railroadDiagram-O6MQD6OU-BVA5UbMu.js +1 -0
  98. package/dist/client/assets/{requirementDiagram-EFPCY7ZU-CRZG6mRB.js → requirementDiagram-BXWQKSXE-C4cKwiIw.js} +2 -2
  99. package/dist/client/assets/runtime-config-DcmnGcXU.js +1 -0
  100. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DvarEJpo.js → sankeyDiagram-P5KCCOFB-B5HHkRT2.js} +1 -1
  101. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-B4oWlb-w.js → sequenceDiagram-WJ2MYXX4-BX6jqJFD.js} +6 -6
  102. package/dist/client/assets/standalone-6z5x8CWe.js +29 -0
  103. package/dist/client/assets/{stateDiagram-HBIQ2CUA-qbHIIqRZ.js → stateDiagram-D77RDMKH-CmqxyRGx.js} +1 -1
  104. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-zoLLQVyh.js +1 -0
  105. package/dist/client/assets/{swimlanes-XN3QIQJK-xB6CtUGj.js → swimlanes-42K2YHIH-Bmgb9Zld.js} +1 -1
  106. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DS-KQN37.js +8 -0
  107. package/dist/client/assets/{timeline-definition-24CTP7MA-Lj_yTNlY.js → timeline-definition-24CTP7MA-BA-lVH6P.js} +1 -1
  108. package/dist/client/assets/vault-api-client-iZLhMJKe.js +6 -0
  109. package/dist/client/assets/{vennDiagram-4TSXK5OY-BekRp5Y0.js → vennDiagram-4TSXK5OY-DNdUC_Xj.js} +1 -1
  110. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-kdnV_Hdm.js → wardleyDiagram-VM6X3IG4-C2oY-YbF.js} +1 -1
  111. package/dist/client/assets/wiki-link-resolver-DFIwErjc.js +1 -0
  112. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DufGwNSB.js → xychartDiagram-S5SC5T6Z-CoIR8CBX.js} +1 -1
  113. package/dist/client/assets/yaml-9ltHPDNO.js +154 -0
  114. package/dist/client/assets/{yjs-provider-reset-guard-DE0vINDP.js → yjs-provider-reset-guard-9ul1qF6k.js} +1 -1
  115. package/dist/client/drawio-editor.html +11 -3
  116. package/dist/client/excalidraw-editor.html +45 -2
  117. package/dist/client/export-document.html +3 -3
  118. package/dist/client/index.html +106 -52
  119. package/integrations/pi/index.ts +94 -0
  120. package/package.json +41 -29
  121. package/src/client/app/drawio-editor.html +2 -2
  122. package/src/client/app/excalidraw-editor.html +2 -2
  123. package/src/client/app/export-document.html +1 -1
  124. package/src/client/app/index.html +88 -50
  125. package/src/client/app/main-entry.js +15 -1
  126. package/src/client/application/app-shell/chat-feature.js +7 -22
  127. package/src/client/application/app-shell/export-feature.js +17 -1
  128. package/src/client/application/app-shell/git-feature.js +51 -14
  129. package/src/client/application/app-shell/presence-feature.js +8 -25
  130. package/src/client/application/app-shell/ui-feature-identity.js +13 -0
  131. package/src/client/application/app-shell/ui-feature-shell.js +199 -99
  132. package/src/client/application/app-shell/ui-feature-sidebar.js +43 -7
  133. package/src/client/application/app-shell/ui-feature-tab-activity.js +19 -6
  134. package/src/client/application/app-shell/ui-feature-toolbar.js +16 -129
  135. package/src/client/application/app-shell-elements.js +8 -0
  136. package/src/client/application/preview-render-compiler.js +1 -87
  137. package/src/client/application/preview-render-scheduler.js +8 -24
  138. package/src/client/application/preview-renderer.js +2 -2
  139. package/src/client/application/webmcp-diagram-renderer.js +47 -0
  140. package/src/client/application/workspace-coordinator.js +23 -4
  141. package/src/client/application/workspace-preview-controller.js +120 -2
  142. package/src/client/application/workspace-route-controller.js +11 -1
  143. package/src/client/assets/vendor/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  144. package/src/client/assets/vendor/jetbrains-mono/OFL.txt +93 -0
  145. package/src/client/bootstrap/collabmd-app-shell.js +99 -6
  146. package/src/client/domain/document-formatter.js +193 -0
  147. package/src/client/domain/excalidraw-element-link.js +73 -0
  148. package/src/client/domain/excalidraw-scene.js +21 -0
  149. package/src/client/domain/file-search.js +73 -0
  150. package/src/client/domain/git-labels.js +5 -0
  151. package/src/client/domain/hash-routes.js +8 -0
  152. package/src/client/domain/room.js +1 -5
  153. package/src/client/domain/runtime-paths.js +5 -0
  154. package/src/client/domain/vault-paths.js +6 -4
  155. package/src/client/domain/wiki-link-completions.js +20 -20
  156. package/src/client/drawio-editor.js +8 -3
  157. package/src/client/excalidraw-editor.js +287 -16
  158. package/src/client/export/export-host.js +59 -36
  159. package/src/client/export/export-page.js +14 -4
  160. package/src/client/export/export-pipeline.js +270 -42
  161. package/src/client/infrastructure/agent-connection-api-client.js +23 -0
  162. package/src/client/infrastructure/auth-client.js +3 -13
  163. package/src/client/infrastructure/editor-session.js +15 -0
  164. package/src/client/infrastructure/editor-view-adapter.js +73 -14
  165. package/src/client/infrastructure/excalidraw-room-client.js +80 -1
  166. package/src/client/infrastructure/git-api-client.js +39 -33
  167. package/src/client/infrastructure/lobby-presence.js +2 -5
  168. package/src/client/infrastructure/runtime-config.js +11 -1
  169. package/src/client/infrastructure/tab-activity-lock.js +2 -5
  170. package/src/client/infrastructure/vault-api-client.js +33 -26
  171. package/src/client/infrastructure/webmcp-tool-registry.js +283 -0
  172. package/src/client/infrastructure/workspace-sync-client.js +6 -4
  173. package/src/client/presentation/agent-connection-controller.js +255 -0
  174. package/src/client/presentation/backlinks-panel.js +3 -0
  175. package/src/client/presentation/bases-preview-controller.js +59 -26
  176. package/src/client/presentation/comment-ui/comment-ui-card.js +4 -2
  177. package/src/client/presentation/create-menu-presenter.js +19 -16
  178. package/src/client/presentation/excalidraw-embed-controller.js +164 -4
  179. package/src/client/presentation/file-action-controller.js +15 -2
  180. package/src/client/presentation/file-explorer-controller.js +3 -0
  181. package/src/client/presentation/file-explorer-view.js +71 -12
  182. package/src/client/presentation/file-history-view-controller.js +19 -12
  183. package/src/client/presentation/file-tree-state.js +1 -11
  184. package/src/client/presentation/git-diff-view-controller.js +70 -53
  185. package/src/client/presentation/git-panel-controller.js +86 -32
  186. package/src/client/presentation/image-lightbox-controller.js +20 -16
  187. package/src/client/presentation/layout-controller.js +3 -1
  188. package/src/client/presentation/quick-switcher-controller.js +74 -232
  189. package/src/client/presentation/quick-switcher-text-search.js +10 -14
  190. package/src/client/styles/components/navigation.css +1 -2
  191. package/src/client/styles/components/scrollbars.css +10 -0
  192. package/src/client/styles/export-document.css +44 -29
  193. package/src/client/styles/features/agent-connection.css +202 -0
  194. package/src/client/styles/features/collaboration-chat.css +8 -1
  195. package/src/client/styles/features/collaboration-dialogs.css +16 -1
  196. package/src/client/styles/features/collaboration-presence.css +30 -5
  197. package/src/client/styles/features/diff-toolbar.css +1 -1
  198. package/src/client/styles/features/media-lightbox.css +12 -3
  199. package/src/client/styles/features/preview-markdown.css +54 -0
  200. package/src/client/styles/features/quick-switcher.css +20 -11
  201. package/src/client/styles/foundation/themes.css +2 -0
  202. package/src/client/styles/foundation/tokens.css +8 -0
  203. package/src/client/styles/layout/editor-page.css +25 -11
  204. package/src/client/styles/layout/responsive.css +2 -20
  205. package/src/client/styles/layout/shell.css +77 -24
  206. package/src/client/styles/layout/sidebar.css +7 -4
  207. package/src/client/styles/layout/view-modes.css +2 -0
  208. package/src/client/styles/overrides/codemirror.css +31 -19
  209. package/src/client/styles/primitives/controls.css +21 -54
  210. package/src/client/styles/style.css +1 -0
  211. package/src/client/styles/surfaces/excalidraw-editor.css +42 -0
  212. package/src/domain/agent-tool-definitions.js +796 -0
  213. package/src/domain/collabmd-content-capabilities.js +149 -0
  214. package/src/domain/collaboration-protocol.js +2 -0
  215. package/src/domain/comment-threads.js +3 -5
  216. package/src/domain/diagram-source.js +50 -0
  217. package/src/domain/editable-content-revision.js +10 -0
  218. package/src/domain/editable-text.js +3 -0
  219. package/src/domain/exact-text-edits.js +69 -0
  220. package/src/domain/excalidraw-agent-scene.js +522 -0
  221. package/src/domain/excalidraw-agent-verification.js +460 -0
  222. package/src/domain/excalidraw-room-codec.js +1 -5
  223. package/src/domain/file-kind.js +12 -1
  224. package/src/domain/video-embed.js +76 -0
  225. package/src/server/application/agent-connection-service.js +178 -0
  226. package/src/server/application/agent-content-service.js +773 -0
  227. package/src/server/application/workspace-reconciliation.js +5 -7
  228. package/src/server/config/env.js +51 -1
  229. package/src/server/create-app-server.js +36 -0
  230. package/src/server/domain/bases/base-query-service.js +1 -5
  231. package/src/server/domain/collaboration/collaboration-room.js +121 -25
  232. package/src/server/domain/collaboration/protocol.js +4 -0
  233. package/src/server/domain/hosted-workspace.js +21 -0
  234. package/src/server/domain/markdown-reference-extractor.js +15 -1
  235. package/src/server/domain/ripgrep-search-service.js +110 -37
  236. package/src/server/infrastructure/git/git-service.js +19 -1
  237. package/src/server/infrastructure/git/history-service.js +1 -1
  238. package/src/server/infrastructure/git/local-exclude.js +25 -0
  239. package/src/server/infrastructure/git/parsers.js +19 -17
  240. package/src/server/infrastructure/git/path-utils.js +0 -16
  241. package/src/server/infrastructure/git/status-service.js +1 -1
  242. package/src/server/infrastructure/git/untracked-files.js +12 -21
  243. package/src/server/infrastructure/http/create-agent-api-handler.js +171 -0
  244. package/src/server/infrastructure/http/create-git-api-command-handler.js +32 -0
  245. package/src/server/infrastructure/http/create-request-handler.js +28 -0
  246. package/src/server/infrastructure/http/create-static-handler.js +6 -0
  247. package/src/server/infrastructure/mcp/create-agent-mcp-handler.js +199 -0
  248. package/src/server/infrastructure/persistence/agent-connection-store.js +120 -0
  249. package/src/server/infrastructure/persistence/path-utils.js +10 -4
  250. package/src/server/infrastructure/persistence/pull-backup-store.js +2 -27
  251. package/src/server/infrastructure/persistence/vault-file-store.js +11 -2
  252. package/src/server/shared/agent-tool-image.js +22 -0
  253. package/src/server/shared/agent-tool-rate-limiter.js +20 -0
  254. package/src/server/startup/git-remote-bootstrap.js +5 -33
  255. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +0 -1
  256. package/dist/client/assets/abnfDiagram-VCTEODGH-BNDLzJ7L.js +0 -1
  257. package/dist/client/assets/browser-utils--H_DkyEF.js +0 -1
  258. package/dist/client/assets/channel-CaoKA_We.js +0 -1
  259. package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +0 -2
  260. package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +0 -127
  261. package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +0 -10
  262. package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +0 -1
  263. package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +0 -1
  264. package/dist/client/assets/comment-threads-9J6Em-_y.js +0 -4
  265. package/dist/client/assets/comment-ui-shared-DWkk9Vh3.js +0 -7
  266. package/dist/client/assets/diagram-VSXAHHWV-CQRVEZR7.js +0 -3
  267. package/dist/client/assets/dist-73RPdcyU.js +0 -1
  268. package/dist/client/assets/dist-CH-sQu3r.js +0 -1
  269. package/dist/client/assets/drawio-editor-BjrxTe3l.js +0 -9
  270. package/dist/client/assets/drawioEditor-DUVZjSSr.js +0 -2
  271. package/dist/client/assets/ebnfDiagram-PWID7BFC-OXa8VEp1.js +0 -1
  272. package/dist/client/assets/editor-session-DfZuRgB_.js +0 -54
  273. package/dist/client/assets/embedded-editor-base-DvMynhKu.css +0 -1
  274. package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +0 -9
  275. package/dist/client/assets/excalidraw-mermaid-stub-BNckbX2n.js +0 -1
  276. package/dist/client/assets/excalidraw-mermaid-stub-DE5FTiZB.js +0 -1
  277. package/dist/client/assets/excalidrawEditor-B0jOK3lv.js +0 -2
  278. package/dist/client/assets/export-page-Bs3xE1fg.js +0 -57
  279. package/dist/client/assets/exportDocument-Dg9MjkIk.js +0 -2
  280. package/dist/client/assets/exportDocument-PnYU3v1s.css +0 -1
  281. package/dist/client/assets/file-kind-BbqgGzpc.js +0 -1
  282. package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +0 -1
  283. package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +0 -1
  284. package/dist/client/assets/index-BIpgxyGZ.css +0 -1
  285. package/dist/client/assets/index-Z5sI0Bj-.js +0 -2
  286. package/dist/client/assets/main-Dwnj9Gaq.js +0 -903
  287. package/dist/client/assets/pegDiagram-XKGWAZYB-B31fKPR2.js +0 -1
  288. package/dist/client/assets/preview-render-compiler-BApvlLLo.js +0 -35
  289. package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +0 -1
  290. package/dist/client/assets/preview-render-profile-CE-5rVTC.js +0 -1
  291. package/dist/client/assets/prod-BiYPMR1i.js +0 -177
  292. package/dist/client/assets/prod-VxdGZ5Ly.js +0 -1
  293. package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +0 -12
  294. package/dist/client/assets/railroadDiagram-O6MQD6OU-BPifP0MB.js +0 -1
  295. package/dist/client/assets/runtime-config-BLrEKVO1.js +0 -1
  296. package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +0 -1
  297. package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +0 -8
  298. package/dist/client/assets/vault-api-client-_LpV1aIn.js +0 -6
  299. package/dist/client/assets/vault-utils-wdQ0RNR9.js +0 -1
  300. package/src/client/app/drawio-editor-entry.js +0 -1
  301. package/src/client/app/excalidraw-editor-entry.js +0 -1
  302. package/src/client/excalidraw-mermaid-stub.js +0 -7
  303. package/src/client/main.js +0 -16
  304. /package/dist/client/assets/{drawio-editor-UQQ8XHou.css → drawioEditor-UQQ8XHou.css} +0 -0
  305. /package/dist/client/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
@@ -0,0 +1,255 @@
1
+ import { escapeHtml } from '../domain/vault-utils.js';
2
+
3
+ function createAgentSetup({ authRequired, endpoint }) {
4
+ return [
5
+ `MCP endpoint: ${endpoint}`,
6
+ 'Transport: Streamable HTTP',
7
+ `Authorization: ${authRequired ? 'Bearer $COLLABMD_ACCESS_TOKEN' : 'None'}`,
8
+ ].join('\n');
9
+ }
10
+
11
+ function createAgentTokenExport(token) {
12
+ return `export COLLABMD_ACCESS_TOKEN='${String(token).replaceAll("'", "'\\''")}'`;
13
+ }
14
+
15
+ function formatDate(value) {
16
+ return value ? new Date(value).toLocaleString() : 'Never';
17
+ }
18
+
19
+ export class AgentConnectionController {
20
+ constructor({ apiClient, closeToolbarMenu, content, dialog, runtimeConfig, toastController, trigger }) {
21
+ this.apiClient = apiClient;
22
+ this.closeToolbarMenu = closeToolbarMenu;
23
+ this.content = content;
24
+ this.dialog = dialog;
25
+ this.runtimeConfig = runtimeConfig;
26
+ this.toastController = toastController;
27
+ this.trigger = trigger;
28
+ this.secretToken = '';
29
+ this.config = {
30
+ authRequired: runtimeConfig.auth?.strategy !== 'none',
31
+ endpoint: runtimeConfig.agentAccess?.endpoint || '',
32
+ managed: Boolean(runtimeConfig.agentAccess?.managed),
33
+ };
34
+ this.endpoint = new URL(this.config.endpoint, window.location.origin).toString();
35
+
36
+ this.trigger?.classList.toggle('hidden', !runtimeConfig.agentAccess?.enabled);
37
+ this.trigger?.addEventListener('click', () => void this.open());
38
+ this.dialog?.addEventListener('close', () => {
39
+ this.secretToken = '';
40
+ this.content?.replaceChildren();
41
+ this.trigger?.focus?.();
42
+ });
43
+ this.dialog?.addEventListener('click', (event) => void this.handleClick(event));
44
+ this.dialog?.addEventListener('submit', (event) => void this.handleSubmit(event));
45
+ }
46
+
47
+ async open() {
48
+ this.closeToolbarMenu?.();
49
+ this.dialog?.showModal?.();
50
+ this.renderStatus('Loading Agent Access…');
51
+ try {
52
+ const connections = this.config.managed
53
+ ? (await this.apiClient.listConnections()).connections
54
+ : [];
55
+ this.renderOverview(connections);
56
+ } catch (error) {
57
+ this.renderStatus(error.message || 'Failed to load Agent Access', { error: true });
58
+ }
59
+ }
60
+
61
+ renderStatus(message, { error = false } = {}) {
62
+ if (!this.content) return;
63
+ this.content.innerHTML = `<p class="agent-connection-status${error ? ' is-error' : ''}" role="status">${escapeHtml(message)}</p>`;
64
+ }
65
+
66
+ renderOverview(connections = []) {
67
+ const connectionMarkup = connections.length > 0
68
+ ? connections.map((connection) => `
69
+ <article class="agent-connection-card">
70
+ <div>
71
+ <strong>${escapeHtml(connection.label)}</strong>
72
+ <span>${escapeHtml(connection.scopes.join(', '))}</span>
73
+ <small>Expires ${escapeHtml(formatDate(connection.expiresAt))}</small>
74
+ </div>
75
+ <button class="ui-button ui-button--secondary ui-button--compact" type="button" data-agent-revoke="${escapeHtml(connection.id)}">Revoke</button>
76
+ </article>
77
+ `).join('')
78
+ : `
79
+ <div class="agent-connection-empty">
80
+ <strong>Connect your first agent</strong>
81
+ <p>Choose its access, create a secure token, then copy the setup into your agent.</p>
82
+ </div>
83
+ `;
84
+ const management = this.config.managed
85
+ ? `<button class="ui-button ui-button--primary" type="button" data-agent-create>${connections.length > 0 ? 'New connection' : 'Start setup'}</button>`
86
+ : `
87
+ <section class="agent-connection-empty">
88
+ <strong>No authentication required</strong>
89
+ <ol class="agent-connection-steps">
90
+ <li>Copy the connection details below into your agent.</li>
91
+ <li>Add this CollabMD MCP endpoint and restart the agent.</li>
92
+ <li>Anyone who can reach this no-auth workspace can use its read and edit tools.</li>
93
+ </ol>
94
+ </section>
95
+ `;
96
+ const connectionsSection = this.config.managed
97
+ ? `
98
+ <section class="agent-connection-section">
99
+ <h3>Connections</h3>
100
+ <div class="agent-connection-list">${connectionMarkup}</div>
101
+ </section>
102
+ `
103
+ : '';
104
+ this.content.innerHTML = `
105
+ ${connectionsSection}
106
+ ${this.config.managed ? '' : management}
107
+ <details class="agent-connection-details">
108
+ <summary>Connection details</summary>
109
+ <code>${escapeHtml(this.endpoint)}</code>
110
+ <p>Streamable HTTP · ${this.config.authRequired ? 'Bearer token' : 'No authentication'} · Read and edit tools</p>
111
+ </details>
112
+ <div class="agent-connection-actions">
113
+ ${this.config.managed ? management : '<button class="ui-button ui-button--primary" type="button" data-agent-copy-setup>Copy connection details</button>'}
114
+ <button class="ui-button ui-button--ghost" type="button" data-agent-close>Close</button>
115
+ </div>
116
+ <p class="agent-connection-live" role="status" aria-live="polite"></p>
117
+ `;
118
+ }
119
+
120
+ renderCreate() {
121
+ this.content.innerHTML = `
122
+ <form class="agent-connection-form" data-agent-create-form>
123
+ <header class="agent-connection-step-header">
124
+ <span>Step 1 of 2</span>
125
+ <h3>Choose access</h3>
126
+ <p>You can revoke this connection at any time.</p>
127
+ </header>
128
+ <label class="app-dialog-field">
129
+ <span class="app-dialog-label">Connection name</span>
130
+ <input class="ui-input" name="label" maxlength="80" value="My Agent" required>
131
+ </label>
132
+ <fieldset class="agent-connection-scopes">
133
+ <legend>Access</legend>
134
+ <label><input type="checkbox" checked disabled> Read Vault Content</label>
135
+ <label><input type="checkbox" name="edit" checked> Edit and create supported text files</label>
136
+ </fieldset>
137
+ <p class="agent-connection-note">Token expires after 30 days and can be revoked here. Delete, rename, Git, and publish are not exposed.</p>
138
+ <div class="agent-connection-actions">
139
+ <button class="ui-button ui-button--secondary" type="button" data-agent-back>Back</button>
140
+ <button class="ui-button ui-button--primary" type="submit">Create secure token</button>
141
+ </div>
142
+ <p class="agent-connection-live" role="status" aria-live="polite"></p>
143
+ </form>
144
+ `;
145
+ }
146
+
147
+ renderToken({ token }) {
148
+ this.secretToken = token;
149
+ const setup = createAgentSetup({
150
+ authRequired: this.config.authRequired,
151
+ endpoint: this.endpoint,
152
+ });
153
+ this.content.innerHTML = `
154
+ <header class="agent-connection-step-header">
155
+ <span>Step 2 of 2</span>
156
+ <h3>Finish setup in your MCP client</h3>
157
+ <p class="agent-connection-warning">The token is shown once. Finish these steps before closing.</p>
158
+ </header>
159
+ <ol class="agent-connection-steps agent-connection-steps--setup">
160
+ <li>
161
+ <div><strong>Save the access token</strong><span>Run the copied command in the terminal that starts your MCP client.</span></div>
162
+ <button class="ui-button ui-button--primary" type="button" data-agent-copy-token>Copy token command</button>
163
+ </li>
164
+ <li>
165
+ <div><strong>Add this workspace</strong><span>Paste the setup into your MCP client.</span></div>
166
+ <button class="ui-button ui-button--secondary" type="button" data-agent-copy-setup>Copy MCP setup</button>
167
+ </li>
168
+ </ol>
169
+ <details class="agent-connection-details">
170
+ <summary>View manual setup</summary>
171
+ <input class="ui-input agent-connection-token" value="${escapeHtml(token)}" readonly aria-label="Agent Connection token">
172
+ <pre><code>${escapeHtml(setup)}</code></pre>
173
+ </details>
174
+ <div class="agent-connection-actions">
175
+ <button class="ui-button ui-button--primary" type="button" data-agent-close>Done</button>
176
+ </div>
177
+ <p class="agent-connection-live" role="status" aria-live="polite"></p>
178
+ `;
179
+ }
180
+
181
+ async refresh() {
182
+ const connections = this.config.managed
183
+ ? (await this.apiClient.listConnections()).connections
184
+ : [];
185
+ this.renderOverview(connections);
186
+ }
187
+
188
+ setLiveMessage(message) {
189
+ const live = this.content?.querySelector('.agent-connection-live');
190
+ if (live) live.textContent = message;
191
+ }
192
+
193
+ async copy(text, message) {
194
+ await navigator.clipboard.writeText(text);
195
+ this.setLiveMessage(message);
196
+ }
197
+
198
+ async handleClick(event) {
199
+ const target = event.target.closest('button');
200
+ if (!target) return;
201
+ if (target.dataset.agentClose != null) {
202
+ this.dialog.close();
203
+ return;
204
+ }
205
+ if (target.dataset.agentCreate != null) {
206
+ this.renderCreate();
207
+ return;
208
+ }
209
+ if (target.dataset.agentBack != null) {
210
+ await this.refresh();
211
+ return;
212
+ }
213
+ if (target.dataset.agentCopyToken != null) {
214
+ await this.copy(createAgentTokenExport(this.secretToken), 'Token command copied — run it in the agent terminal.');
215
+ return;
216
+ }
217
+ if (target.dataset.agentCopySetup != null) {
218
+ await this.copy(createAgentSetup({
219
+ authRequired: this.config.authRequired,
220
+ endpoint: this.endpoint,
221
+ }), 'Setup copied — add it to your MCP client.');
222
+ return;
223
+ }
224
+ if (target.dataset.agentRevoke) {
225
+ if (!window.confirm('Revoke this Agent Connection?')) return;
226
+ try {
227
+ await this.apiClient.revokeConnection(target.dataset.agentRevoke);
228
+ await this.refresh();
229
+ this.setLiveMessage('Agent Connection revoked');
230
+ } catch (error) {
231
+ this.toastController?.show(error.message || 'Failed to revoke Agent Connection');
232
+ }
233
+ }
234
+ }
235
+
236
+ async handleSubmit(event) {
237
+ const form = event.target.closest('[data-agent-create-form]');
238
+ if (!form) return;
239
+ event.preventDefault();
240
+ const data = new FormData(form);
241
+ const submit = form.querySelector('[type="submit"]');
242
+ submit.disabled = true;
243
+ try {
244
+ const result = await this.apiClient.createConnection({
245
+ clientKind: 'generic',
246
+ label: data.get('label'),
247
+ scopes: data.get('edit') === 'on' ? ['vault:read', 'vault:edit'] : ['vault:read'],
248
+ });
249
+ this.renderToken(result);
250
+ } catch (error) {
251
+ this.setLiveMessage(error.message || 'Failed to create Agent Connection');
252
+ submit.disabled = false;
253
+ }
254
+ }
255
+ }
@@ -13,6 +13,7 @@ export class BacklinksPanel {
13
13
  headerPanelElement = null,
14
14
  inlinePanelElement = null,
15
15
  loadBacklinks = null,
16
+ onBeforeToggle = null,
16
17
  onFileSelect,
17
18
  panelElement,
18
19
  }) {
@@ -21,6 +22,7 @@ export class BacklinksPanel {
21
22
  this.panelRoot = panelElement;
22
23
  this.inlinePanel = inlinePanelElement;
23
24
  this.loadBacklinks = loadBacklinks;
25
+ this.onBeforeToggle = onBeforeToggle;
24
26
  this.onFileSelect = onFileSelect;
25
27
 
26
28
  this._expanded = false;
@@ -102,6 +104,7 @@ export class BacklinksPanel {
102
104
  this.panels.forEach((refs) => {
103
105
  refs.header?.addEventListener('click', () => {
104
106
  if (this._backlinks.length === 0) return;
107
+ this.onBeforeToggle?.();
105
108
  this._expanded = !this._expanded;
106
109
  this._pendingOutsidePointer = null;
107
110
  this._applyExpandState();
@@ -10,6 +10,26 @@ function createShellKey() {
10
10
  return `base-${Math.random().toString(36).slice(2, 10)}`;
11
11
  }
12
12
 
13
+ function replaceChildrenFromHtml(target, html) {
14
+ const markup = String(html ?? '');
15
+ if (typeof DOMParser === 'function' && typeof target?.replaceChildren === 'function') {
16
+ const parsed = new DOMParser().parseFromString(`<body>${markup}</body>`, 'text/html');
17
+ target.replaceChildren(...parsed.body.childNodes);
18
+ return;
19
+ }
20
+
21
+ // Node tests use lightweight DOM doubles without parsing APIs.
22
+ Reflect.set(target, 'innerHTML', markup);
23
+ }
24
+
25
+ function insertAfterFromHtml(target, html) {
26
+ if (typeof DOMParser !== 'function' || typeof target?.after !== 'function') {
27
+ return;
28
+ }
29
+ const parsed = new DOMParser().parseFromString(`<body>${String(html ?? '')}</body>`, 'text/html');
30
+ target.after(...parsed.body.childNodes);
31
+ }
32
+
13
33
  function looksLikeHexColor(value = '') {
14
34
  return /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/iu.test(String(value ?? '').trim());
15
35
  }
@@ -326,10 +346,10 @@ function renderSortPanel(result) {
326
346
  <div class="bases-panel-section">
327
347
  <div class="bases-panel-title">Group by</div>
328
348
  <div class="bases-sort-row">
329
- <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-property' }))}" data-base-group-by-property>
349
+ <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-property' }))}" data-base-group-by-property aria-label="Group by property">
330
350
  ${createSelectOptions(result, { allowBlank: true, selected: viewConfig.groupBy?.property ?? '' })}
331
351
  </select>
332
- <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-direction' }))}" data-base-group-by-direction${viewConfig.groupBy?.property ? '' : ' disabled'}>
352
+ <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-direction' }))}" data-base-group-by-direction aria-label="Group direction"${viewConfig.groupBy?.property ? '' : ' disabled'}>
333
353
  ${groupDirections.map((direction) => `<option value="${escapeHtml(direction.id)}"${direction.id === (viewConfig.groupBy?.direction ?? 'asc') ? ' selected' : ''}>${escapeHtml(direction.label)}</option>`).join('')}
334
354
  </select>
335
355
  <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-clear-group-by${viewConfig.groupBy?.property ? '' : ' disabled'}>Clear</button>
@@ -346,15 +366,15 @@ function renderSortPanel(result) {
346
366
  ];
347
367
  return `
348
368
  <div class="bases-sort-row">
349
- <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-property' }))}" data-base-sort-property="${index}">
369
+ <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-property' }))}" data-base-sort-property="${index}" aria-label="Sort rule ${index + 1} property">
350
370
  ${createSelectOptions(result, { selected: sortConfig.property })}
351
371
  </select>
352
- <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-direction' }))}" data-base-sort-direction="${index}">
372
+ <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-sort-direction' }))}" data-base-sort-direction="${index}" aria-label="Sort rule ${index + 1} direction">
353
373
  ${directions.map((direction) => `<option value="${escapeHtml(direction.id)}"${direction.id === sortConfig.direction ? ' selected' : ''}>${escapeHtml(direction.label)}</option>`).join('')}
354
374
  </select>
355
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-move="${index}:up"${index === 0 ? ' disabled' : ''}>↑</button>
356
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-move="${index}:down"${index === sorts.length - 1 ? ' disabled' : ''}>↓</button>
357
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-delete="${index}">Delete</button>
375
+ <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-move="${index}:up" aria-label="Move sort rule ${index + 1} up"${index === 0 ? ' disabled' : ''}>↑</button>
376
+ <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-move="${index}:down" aria-label="Move sort rule ${index + 1} down"${index === sorts.length - 1 ? ' disabled' : ''}>↓</button>
377
+ <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-delete="${index}" aria-label="Delete sort rule ${index + 1}">Delete</button>
358
378
  </div>
359
379
  `;
360
380
  }).join('')}
@@ -369,6 +389,10 @@ function encodePath(path = []) {
369
389
  return path.join('.');
370
390
  }
371
391
 
392
+ function formatFilterPath(path = []) {
393
+ return path.map((index) => index + 1).join('.');
394
+ }
395
+
372
396
  function decodePath(value = '') {
373
397
  return String(value ?? '')
374
398
  .split('.')
@@ -617,7 +641,11 @@ function parseRawFilterText(text = '') {
617
641
  }
618
642
 
619
643
  if (normalized.startsWith('{') || normalized.startsWith('[')) {
620
- return JSON.parse(normalized);
644
+ try {
645
+ return JSON.parse(normalized);
646
+ } catch (error) {
647
+ throw new Error('Invalid advanced filter JSON', { cause: error });
648
+ }
621
649
  }
622
650
 
623
651
  return normalized;
@@ -749,9 +777,10 @@ function renderFilterSuggestionList(presentation, currentValue = '') {
749
777
 
750
778
  function renderFilterValueCombobox(result, rule, path, entry) {
751
779
  const presentation = buildFilterValuePresentation(result, rule, path, entry);
780
+ const filterLabel = `Filter ${formatFilterPath(path)}`;
752
781
  return `
753
782
  <div class="bases-filter-value-combobox${presentation.suggestionsEnabled ? ' has-suggestions' : ''}">
754
- <input class="${escapeHtml(presentation.valueInputClasses)}" type="text" value="${escapeHtml(rule.value ?? '')}" data-base-filter-value="${escapeHtml(presentation.encodedPath)}"${presentation.suggestionsEnabled ? ` aria-controls="${presentation.suggestionListId}" aria-haspopup="listbox" aria-autocomplete="list" autocomplete="off"` : ''}${presentation.valueDisabled ? ' disabled' : ''}>
783
+ <input class="${escapeHtml(presentation.valueInputClasses)}" type="text" value="${escapeHtml(rule.value ?? '')}" data-base-filter-value="${escapeHtml(presentation.encodedPath)}" aria-label="${filterLabel} value"${presentation.suggestionsEnabled ? ` aria-controls="${presentation.suggestionListId}" aria-haspopup="listbox" aria-autocomplete="list" autocomplete="off"` : ''}${presentation.valueDisabled ? ' disabled' : ''}>
755
784
  ${renderFilterSuggestionList(presentation, rule.value ?? '')}
756
785
  </div>
757
786
  `;
@@ -759,17 +788,18 @@ function renderFilterValueCombobox(result, rule, path, entry) {
759
788
 
760
789
  function renderFilterRule(result, rule, path, entry) {
761
790
  const presentation = buildFilterValuePresentation(result, rule, path, entry);
791
+ const filterLabel = `Filter ${formatFilterPath(path)}`;
762
792
 
763
793
  return `
764
794
  <div class="bases-filter-row">
765
- <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-filter-property' }))}" data-base-filter-property="${escapeHtml(presentation.encodedPath)}">
795
+ <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-filter-property' }))}" data-base-filter-property="${escapeHtml(presentation.encodedPath)}" aria-label="${filterLabel} property">
766
796
  ${getPropertyOptionsMarkup(result, rule.propertyId)}
767
797
  </select>
768
- <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-filter-operator' }))}" data-base-filter-operator="${escapeHtml(presentation.encodedPath)}">
798
+ <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-filter-operator' }))}" data-base-filter-operator="${escapeHtml(presentation.encodedPath)}" aria-label="${filterLabel} operator">
769
799
  ${presentation.operators.map((operator) => `<option value="${escapeHtml(operator)}"${operator === rule.operator ? ' selected' : ''}>${escapeHtml(operator)}</option>`).join('')}
770
800
  </select>
771
801
  ${renderFilterValueCombobox(result, rule, path, entry)}
772
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-remove="${escapeHtml(presentation.encodedPath)}">Delete</button>
802
+ <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-remove="${escapeHtml(presentation.encodedPath)}" aria-label="Delete ${filterLabel.toLowerCase()}">Delete</button>
773
803
  </div>
774
804
  `;
775
805
  }
@@ -795,6 +825,7 @@ function syncFilterValueCombobox(entry, result, path, { fallbackToFullRender = t
795
825
  combobox.classList.toggle('has-suggestions', presentation.suggestionsEnabled);
796
826
  input.className = presentation.valueInputClasses;
797
827
  input.disabled = presentation.valueDisabled;
828
+ input.setAttribute('aria-label', `Filter ${formatFilterPath(path)} value`);
798
829
  if (input.value !== String(rule.value ?? '')) {
799
830
  input.value = String(rule.value ?? '');
800
831
  }
@@ -814,7 +845,7 @@ function syncFilterValueCombobox(entry, result, path, { fallbackToFullRender = t
814
845
  const suggestionList = combobox.querySelector('.bases-filter-suggestion-list');
815
846
  suggestionList?.remove?.();
816
847
  if (presentation.suggestionsEnabled) {
817
- input.insertAdjacentHTML('afterend', renderFilterSuggestionList(presentation, rule.value ?? ''));
848
+ insertAfterFromHtml(input, renderFilterSuggestionList(presentation, rule.value ?? ''));
818
849
  }
819
850
 
820
851
  return true;
@@ -837,17 +868,18 @@ function forEachFilterRule(node, callback, path = []) {
837
868
 
838
869
  function renderFilterGroup(result, group, path, entry, { isRoot = false } = {}) {
839
870
  const encodedPath = encodePath(path);
871
+ const groupLabel = isRoot ? 'Root filter group' : `Filter group ${formatFilterPath(path)}`;
840
872
  return `
841
873
  <div class="bases-filter-group"${isRoot ? ' data-base-filter-root' : ''}>
842
874
  <div class="bases-filter-group-header">
843
- <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-filter-conjunction' }))}" data-base-filter-conjunction="${escapeHtml(encodedPath)}">
875
+ <select class="${escapeHtml(inputClassNames({ extra: 'bases-select bases-filter-conjunction' }))}" data-base-filter-conjunction="${escapeHtml(encodedPath)}" aria-label="${groupLabel} condition">
844
876
  ${['and', 'or', 'not'].map((conjunction) => `<option value="${conjunction}"${group.conjunction === conjunction ? ' selected' : ''}>${escapeHtml({
845
877
  and: 'All the following are true',
846
878
  not: 'None of the following are true',
847
879
  or: 'Any of the following are true',
848
880
  }[conjunction])}</option>`).join('')}
849
881
  </select>
850
- ${isRoot ? '' : `<button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-remove="${escapeHtml(encodedPath)}">Delete group</button>`}
882
+ ${isRoot ? '' : `<button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-remove="${escapeHtml(encodedPath)}" aria-label="Delete ${groupLabel.toLowerCase()}">Delete group</button>`}
851
883
  </div>
852
884
  <div class="bases-filter-children">
853
885
  ${(group.children ?? []).map((child, index) => (
@@ -857,8 +889,8 @@ function renderFilterGroup(result, group, path, entry, { isRoot = false } = {})
857
889
  )).join('')}
858
890
  </div>
859
891
  <div class="bases-filter-actions-row">
860
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-add-rule="${escapeHtml(encodedPath)}">Add filter</button>
861
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-add-group="${escapeHtml(encodedPath)}">Add filter group</button>
892
+ <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-add-rule="${escapeHtml(encodedPath)}" aria-label="Add filter to ${groupLabel.toLowerCase()}">Add filter</button>
893
+ <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-add-group="${escapeHtml(encodedPath)}" aria-label="Add filter group to ${groupLabel.toLowerCase()}">Add filter group</button>
862
894
  </div>
863
895
  </div>
864
896
  `;
@@ -887,6 +919,7 @@ function renderFilterPanel(result, entry) {
887
919
  ${mode === 'advanced'
888
920
  ? `
889
921
  <label class="bases-filter-advanced-label">
922
+ <span class="bases-panel-title">Filter expression</span>
890
923
  <textarea class="${escapeHtml(inputClassNames({ extra: 'bases-filter-advanced' }))}" data-base-filter-advanced>${escapeHtml(rawText)}</textarea>
891
924
  </label>
892
925
  <div class="bases-filter-actions-row">
@@ -1091,14 +1124,14 @@ function updateShellContent(entry, result, {
1091
1124
  } = {}) {
1092
1125
  const shell = findShellElement(entry);
1093
1126
  if (!shell?.querySelector) {
1094
- entry.placeholder.innerHTML = renderShellHtml(result, entry);
1127
+ replaceChildrenFromHtml(entry.placeholder, renderShellHtml(result, entry));
1095
1128
  markPlaceholderHydrated(entry.placeholder);
1096
1129
  return;
1097
1130
  }
1098
1131
 
1099
1132
  const tabsSlot = shell.querySelector('[data-base-tabs]');
1100
1133
  if (tabs && tabsSlot) {
1101
- tabsSlot.innerHTML = renderViewTabs(result);
1134
+ replaceChildrenFromHtml(tabsSlot, renderViewTabs(result));
1102
1135
  }
1103
1136
 
1104
1137
  const metaSlot = shell.querySelector('[data-base-meta]');
@@ -1108,24 +1141,24 @@ function updateShellContent(entry, result, {
1108
1141
 
1109
1142
  const actionSlot = shell.querySelector('[data-base-action-slot]');
1110
1143
  if (actions && actionSlot) {
1111
- actionSlot.innerHTML = renderActionButtons(result, entry);
1144
+ replaceChildrenFromHtml(actionSlot, renderActionButtons(result, entry));
1112
1145
  }
1113
1146
 
1114
1147
  const panelSlot = shell.querySelector('[data-base-panel-slot]');
1115
1148
  if (panel && panelSlot) {
1116
1149
  const panelState = capturePanelState(panelSlot);
1117
- panelSlot.innerHTML = renderPanel(result, entry);
1150
+ replaceChildrenFromHtml(panelSlot, renderPanel(result, entry));
1118
1151
  restorePanelState(panelSlot, panelState);
1119
1152
  }
1120
1153
 
1121
1154
  const summarySlot = shell.querySelector('[data-base-summary-slot]');
1122
1155
  if (summary && summarySlot) {
1123
- summarySlot.innerHTML = renderSummaryBar(result.summaries);
1156
+ replaceChildrenFromHtml(summarySlot, renderSummaryBar(result.summaries));
1124
1157
  }
1125
1158
 
1126
1159
  const content = shell.querySelector('[data-base-content]');
1127
1160
  if (body && content) {
1128
- content.innerHTML = renderViewBody(result);
1161
+ replaceChildrenFromHtml(content, renderViewBody(result));
1129
1162
  }
1130
1163
 
1131
1164
  const input = shell.querySelector('.bases-search-input');
@@ -1663,7 +1696,7 @@ export class BasesPreviewController {
1663
1696
  entry.requestVersion = requestVersion;
1664
1697
  const hasShell = Boolean(findShellElement(entry));
1665
1698
  if (!hasShell) {
1666
- entry.placeholder.innerHTML = '<div class="preview-shell">Loading base…</div>';
1699
+ replaceChildrenFromHtml(entry.placeholder, '<div class="preview-shell" role="status" aria-live="polite">Loading base…</div>');
1667
1700
  }
1668
1701
  try {
1669
1702
  const response = await this.vaultApiClient.queryBase({
@@ -1687,11 +1720,11 @@ export class BasesPreviewController {
1687
1720
  const shell = findShellElement(entry);
1688
1721
  const content = shell?.querySelector?.('[data-base-content]') ?? null;
1689
1722
  if (content) {
1690
- content.innerHTML = `<div class="preview-shell">Failed to load base: ${escapeHtml(error.message || 'Unknown error')}</div>`;
1723
+ replaceChildrenFromHtml(content, `<div class="preview-shell" role="alert">Failed to load base: ${escapeHtml(error.message || 'Unknown error')}</div>`);
1691
1724
  return;
1692
1725
  }
1693
1726
  }
1694
- entry.placeholder.innerHTML = `<div class="preview-shell">Failed to load base: ${escapeHtml(error.message || 'Unknown error')}</div>`;
1727
+ replaceChildrenFromHtml(entry.placeholder, `<div class="preview-shell" role="alert">Failed to load base: ${escapeHtml(error.message || 'Unknown error')}</div>`);
1695
1728
  }
1696
1729
  }
1697
1730
 
@@ -314,7 +314,8 @@ function createComposer() {
314
314
  textarea.className = inputClassNames({ extra: 'comment-card-input' });
315
315
  textarea.rows = 4;
316
316
  textarea.maxLength = COMMENT_BODY_MAX_LENGTH;
317
- textarea.placeholder = 'Add context, feedback, or a question...';
317
+ textarea.setAttribute('aria-label', 'Add comment');
318
+ textarea.placeholder = 'Add context, feedback, or a question…';
318
319
  textarea.value = this.activeCard?.composerDraft?.value ?? '';
319
320
 
320
321
  const actions = document.createElement('div');
@@ -575,7 +576,8 @@ function createReplyComposer(thread) {
575
576
  textarea.className = inputClassNames({ extra: 'comment-card-input' });
576
577
  textarea.rows = 3;
577
578
  textarea.maxLength = COMMENT_BODY_MAX_LENGTH;
578
- textarea.placeholder = 'Reply to thread...';
579
+ textarea.setAttribute('aria-label', 'Reply to thread');
580
+ textarea.placeholder = 'Reply to thread…';
579
581
  textarea.value = draft?.value ?? '';
580
582
 
581
583
  const actions = document.createElement('div');
@@ -13,7 +13,7 @@ function getFocusableItems(container) {
13
13
  return [];
14
14
  }
15
15
 
16
- return Array.from(container.querySelectorAll('.create-menu-item'));
16
+ return Array.from(container.querySelectorAll('.create-menu-item, .file-action-sheet-item:not(:disabled)'));
17
17
  }
18
18
 
19
19
  export class CreateMenuPresenter {
@@ -23,10 +23,10 @@ export class CreateMenuPresenter {
23
23
  this.mobileBreakpointQuery = mobileBreakpointQuery;
24
24
  this.activeAnchor = null;
25
25
  this.desktopMenu = null;
26
- this.mobileBackdrop = null;
27
26
  this.mobileSheet = null;
28
27
  this.documentPointerDownHandler = null;
29
28
  this.windowResizeHandler = null;
29
+ this.blockingModalHandler = () => this.close();
30
30
  this.boundClose = () => this.close();
31
31
  }
32
32
 
@@ -71,15 +71,12 @@ export class CreateMenuPresenter {
71
71
  this.desktopMenu = null;
72
72
  }
73
73
 
74
- if (this.mobileBackdrop) {
75
- this.mobileBackdrop.remove();
76
- this.mobileBackdrop = null;
77
- }
78
-
79
74
  if (this.mobileSheet) {
75
+ if (this.mobileSheet.open) this.mobileSheet.close();
80
76
  this.mobileSheet.remove();
81
77
  this.mobileSheet = null;
82
78
  }
79
+ document.removeEventListener('collabmd:close-custom-modals', this.blockingModalHandler);
83
80
 
84
81
  if (this.documentPointerDownHandler) {
85
82
  document.removeEventListener('pointerdown', this.documentPointerDownHandler);
@@ -136,13 +133,9 @@ export class CreateMenuPresenter {
136
133
  }
137
134
 
138
135
  openMobileSheet({ items, title }) {
139
- const backdrop = document.createElement('button');
140
- backdrop.type = 'button';
141
- backdrop.className = 'file-action-sheet-backdrop';
142
- backdrop.setAttribute('aria-label', `Close ${title.toLowerCase()} menu`);
143
-
144
- const sheet = document.createElement('div');
136
+ const sheet = document.createElement('dialog');
145
137
  sheet.className = 'file-action-sheet create-action-sheet';
138
+ sheet.setAttribute('aria-label', title);
146
139
 
147
140
  const header = document.createElement('div');
148
141
  header.className = 'create-action-sheet-header';
@@ -163,13 +156,21 @@ export class CreateMenuPresenter {
163
156
  });
164
157
  sheet.appendChild(cancelButton);
165
158
 
166
- backdrop.addEventListener('click', () => {
159
+ sheet.addEventListener('cancel', (event) => {
160
+ event.preventDefault();
167
161
  this.close();
168
162
  });
163
+ sheet.addEventListener('click', (event) => {
164
+ const bounds = sheet.getBoundingClientRect();
165
+ if (event.clientX < bounds.left || event.clientX > bounds.right
166
+ || event.clientY < bounds.top || event.clientY > bounds.bottom) this.close();
167
+ });
169
168
 
170
- document.body.append(backdrop, sheet);
171
- this.mobileBackdrop = backdrop;
169
+ document.body.append(sheet);
170
+ document.addEventListener('collabmd:close-custom-modals', this.blockingModalHandler);
172
171
  this.mobileSheet = sheet;
172
+ sheet.showModal();
173
+ getFocusableItems(sheet)[0]?.focus();
173
174
  }
174
175
 
175
176
  renderMenuItems(container, items, { mobile = false } = {}) {
@@ -198,6 +199,8 @@ export class CreateMenuPresenter {
198
199
  const icon = document.createElement('span');
199
200
  icon.className = 'create-menu-item-icon';
200
201
  icon.setAttribute('aria-hidden', 'true');
202
+ // Menu icons are fixed application-owned markup.
203
+ // pi-lens-ignore: no-inner-html-js
201
204
  icon.innerHTML = item.icon || '';
202
205
  button.appendChild(icon);
203
206