collabmd 0.1.48 → 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 (268) hide show
  1. package/README.md +61 -5
  2. package/bin/collabmd.js +3 -0
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-C-nmgL5g.js +1 -0
  4. package/dist/client/assets/abnfDiagram-VCTEODGH-CdVZqicp.js +1 -0
  5. package/dist/client/assets/{architectureDiagram-5GKGNRK7-I39sYjHI.js → architectureDiagram-5GKGNRK7-wUo0bXif.js} +3 -3
  6. package/dist/client/assets/{blockDiagram-NRAW4CY4-C6DtJy5Y.js → blockDiagram-I7D4REHJ-BRSK3m49.js} +4 -4
  7. package/dist/client/assets/browser-utils-DbXSMI-6.js +1 -0
  8. package/dist/client/assets/{c4Diagram-UCG6FXSJ-CtWtxS9_.js → c4Diagram-7LVT6UL2-yD2poxqw.js} +1 -1
  9. package/dist/client/assets/channel-Fa4GS_-F.js +1 -0
  10. package/dist/client/assets/chunk-2E4U76K2-BPnTT-J3.js +2 -0
  11. package/dist/client/assets/{chunk-TEH6E4GO-DVayPj-q.js → chunk-4HAMMTFA-DStbIfJ1.js} +20 -20
  12. package/dist/client/assets/{chunk-75Z2AOVW-CdFSaPjz.js → chunk-75Z2AOVW-C5pGd3W9.js} +1 -1
  13. package/dist/client/assets/{chunk-CLGD4ZFX-DQLgbQrv.js → chunk-CLGD4ZFX-BMeArk03.js} +1 -1
  14. package/dist/client/assets/chunk-DU6HZSFF-Zkp69c6c.js +127 -0
  15. package/dist/client/assets/{chunk-F27PBJKO-CNHIFhjo.js → chunk-F27PBJKO-D4xNct1r.js} +1 -1
  16. package/dist/client/assets/{chunk-GMAD6QVW-CbcBqPM5.js → chunk-GMAD6QVW-NZ6WpDb8.js} +1 -1
  17. package/dist/client/assets/{chunk-OBVCFTLP-BJR7u1jh.js → chunk-GVQU2GXP-D8SDprfX.js} +1 -1
  18. package/dist/client/assets/{chunk-G27WJ6UU-D9-eg537.js → chunk-IMKFNOWR-D8jEuU3Q.js} +2 -2
  19. package/dist/client/assets/{chunk-JQ64N6SF-DSFNH4bh.js → chunk-L3NEJ4N5-aijKP-eh.js} +1 -1
  20. package/dist/client/assets/chunk-OSK3NFVY-CCbLQAMa.js +10 -0
  21. package/dist/client/assets/{chunk-P2QGCYS3-BRpqY32H.js → chunk-P2QGCYS3-Oag_FdCq.js} +1 -1
  22. package/dist/client/assets/{chunk-POPQ4Y6H-Cb4SlVZF.js → chunk-POPQ4Y6H-CYnpIgXT.js} +1 -1
  23. package/dist/client/assets/{chunk-PWAF6VOD-Cz8IwBB5.js → chunk-PWAF6VOD-JSNrcBmP.js} +1 -1
  24. package/dist/client/assets/{chunk-RM4UPSZO-CWRwxNWJ.js → chunk-RM4UPSZO-DOWllDY8.js} +4 -4
  25. package/dist/client/assets/{chunk-RHFEMEQ7-CWY8cf4H.js → chunk-SHT3W25Y-BiQGljao.js} +5 -5
  26. package/dist/client/assets/{chunk-SVP7TREG-CyHOmcac.js → chunk-SVP7TREG-DB485R5x.js} +1 -1
  27. package/dist/client/assets/{chunk-LCL6LL3I-DmDkVukh.js → chunk-TICWLB2K-8rAVvoS3.js} +4 -4
  28. package/dist/client/assets/{chunk-CKBBP62Z-DG26UiBv.js → chunk-TLUHSLCS-DTY5v0aj.js} +2 -2
  29. package/dist/client/assets/classDiagram-ZZMXUADV-BsQWoR3Q.js +1 -0
  30. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BsQWoR3Q.js +1 -0
  31. package/dist/client/assets/{comment-thread-store-CdcwrObc.js → comment-thread-store-CX98zR6q.js} +1 -1
  32. package/dist/client/assets/comment-threads-BnRwo5B9.js +4 -0
  33. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CdTPi2yO.js → cynefinDiagram-5FMLGOSQ-rTz9RhFH.js} +1 -1
  34. package/dist/client/assets/{dagre-3AP2YEHR-5gtJtd0O.js → dagre-GXQ25YYZ-Cr67wLpQ.js} +2 -2
  35. package/dist/client/assets/{diagram-S7CK7UJ4-BudWz9WU.js → diagram-S7CK7UJ4-DywERe__.js} +2 -2
  36. package/dist/client/assets/{diagram-UQ7AKVKN-3v4rbH-s.js → diagram-UQ7AKVKN-CNYlXtu4.js} +1 -1
  37. package/dist/client/assets/diagram-VSXAHHWV-0H1a61Bn.js +3 -0
  38. package/dist/client/assets/{diagram-VX7I27RA-C_-rj0sw.js → diagram-VX7I27RA-CsJbsx5F.js} +1 -1
  39. package/dist/client/assets/{diagram-Z3DM3KII-B4yxavgj.js → diagram-Z3DM3KII-D0mLxEgI.js} +1 -1
  40. package/dist/client/assets/dist--hk9I5HQ.js +1 -0
  41. package/dist/client/assets/{dist-DutPuFmC.js → dist-B1XEa-Zs.js} +1 -1
  42. package/dist/client/assets/dist-Bd1JucQF.js +1 -0
  43. package/dist/client/assets/{dist-DZAAWTG-.js → dist-C0G3AaXG.js} +1 -1
  44. package/dist/client/assets/dist-Dm4w5UId.js +1 -0
  45. package/dist/client/assets/{dist-B_yQaEv_.js → dist-DpYDHR9p.js} +1 -1
  46. package/dist/client/assets/{dist-CietwTVS.js → dist-DzNmC81X.js} +1 -1
  47. package/dist/client/assets/{dist-DDUn5zTz.js → dist-GvvCNBe4.js} +1 -1
  48. package/dist/client/assets/dist-JaU3aRlD.js +19 -0
  49. package/dist/client/assets/{dist-Bw3VIE_v.js → dist-TW8iFRyW.js} +1 -1
  50. package/dist/client/assets/{dist-Dir5dDKZ.js → dist-jVU5Be5P.js} +1 -1
  51. package/dist/client/assets/document-formatter-rDDjRfZ2.js +2 -0
  52. package/dist/client/assets/drawioEditor-JubEEha7.js +9 -0
  53. package/dist/client/assets/ebnfDiagram-PWID7BFC-HeTxHpCU.js +1 -0
  54. package/dist/client/assets/editor-session-Bk6oIvSr.js +54 -0
  55. package/dist/client/assets/{embedded-editor-base-B3l6Xy65.css → embedded-editor-base-BlCgz6mw.css} +1 -1
  56. package/dist/client/assets/{erDiagram-SSCWMZ5O-C-iy6VkX.js → erDiagram-RLTQ6QDP-CbrDdd5P.js} +2 -2
  57. package/dist/client/assets/{vault-utils-DvoSftJT.js → es-Bfdp096f.js} +13 -13
  58. package/dist/client/assets/excalidraw-element-link-YLIaTnVF.js +7 -0
  59. package/dist/client/assets/{excalidraw-scene-BOben2gr.js → excalidraw-scene-6uROebca.js} +1 -1
  60. package/dist/client/assets/excalidrawEditor-dBMbRX_S.js +9 -0
  61. package/dist/client/assets/export-page-CRRQ2NEl.js +57 -0
  62. package/dist/client/assets/exportDocument-BlQRd9_m.js +2 -0
  63. package/dist/client/assets/exportDocument-CNSbjZwu.css +1 -0
  64. package/dist/client/assets/flowDiagram-HODETNUW-D5Gy0wmg.js +1 -0
  65. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DtKHAbez.js → ganttDiagram-EL5Y4UJY-BHgOYiZz.js} +1 -1
  66. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-oue8fQ5o.js → gitGraphDiagram-WWUBYQGX-Dl__X3Sk.js} +1 -1
  67. package/dist/client/assets/image-ZVKINSPA-DgzW5zAy.js +1 -0
  68. package/dist/client/assets/index-D3RER1SZ.js +1023 -0
  69. package/dist/client/assets/index-DREVKl_K.css +1 -0
  70. package/dist/client/assets/{infoDiagram-RXCK75RN-BMzo6KIn.js → infoDiagram-27XIBGKW-DBpoANC9.js} +1 -1
  71. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BIM3h287.js → ishikawaDiagram-5VMMS53U-BMPC6hfp.js} +1 -1
  72. package/dist/client/assets/{journeyDiagram-EYS64GPL-mF6Lh8O-.js → journeyDiagram-3NMN7TZE-B1sApUmM.js} +2 -2
  73. package/dist/client/assets/{kanban-definition-3QL26DDD-DLN2NyoR.js → kanban-definition-UXKFOSKX-C9yP4td1.js} +4 -4
  74. package/dist/client/assets/{line-BfTWJmgf.js → line-BFl3ujec.js} +1 -1
  75. package/dist/client/assets/mermaid.core-BrY09czX.js +11 -0
  76. package/dist/client/assets/mermaid.core-DfcNfOTv.js +1 -0
  77. package/dist/client/assets/{mindmap-definition-FBJOCRG2-CRyHaHTk.js → mindmap-definition-YA3MSWOX-BUQHcYbK.js} +8 -8
  78. package/dist/client/assets/pegDiagram-XKGWAZYB-Bi2Iml7Z.js +1 -0
  79. package/dist/client/assets/{pieDiagram-E7YTZNPT-B5OZPb88.js → pieDiagram-E7YTZNPT-_gYXnMoi.js} +1 -1
  80. package/dist/client/assets/preview-render-compiler-CfxJYldJ.js +41 -0
  81. package/dist/client/assets/preview-render-compiler-Cu8_i9iS.js +1 -0
  82. package/dist/client/assets/{preview-render-worker-_QTxqKvP.js → preview-render-worker-c4Yi93qW.js} +41 -35
  83. package/dist/client/assets/prod-BiJL30sl.js +1 -0
  84. package/dist/client/assets/prod-qAk7rHa4.js +177 -0
  85. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-Drrw14YH.js → quadrantDiagram-AXDQQJYC-DwexhEhd.js} +1 -1
  86. package/dist/client/assets/quick-switcher-controller-BoIK_wuG.js +1 -0
  87. package/dist/client/assets/railroadDiagram-O6MQD6OU-BVA5UbMu.js +1 -0
  88. package/dist/client/assets/{requirementDiagram-EFPCY7ZU-CRZG6mRB.js → requirementDiagram-BXWQKSXE-C4cKwiIw.js} +2 -2
  89. package/dist/client/assets/runtime-config-DcmnGcXU.js +1 -0
  90. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DvarEJpo.js → sankeyDiagram-P5KCCOFB-B5HHkRT2.js} +1 -1
  91. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-B4oWlb-w.js → sequenceDiagram-WJ2MYXX4-BX6jqJFD.js} +6 -6
  92. package/dist/client/assets/{stateDiagram-HBIQ2CUA-qbHIIqRZ.js → stateDiagram-D77RDMKH-CmqxyRGx.js} +1 -1
  93. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-zoLLQVyh.js +1 -0
  94. package/dist/client/assets/{swimlanes-XN3QIQJK-xB6CtUGj.js → swimlanes-42K2YHIH-Bmgb9Zld.js} +1 -1
  95. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DS-KQN37.js +8 -0
  96. package/dist/client/assets/{timeline-definition-24CTP7MA-Lj_yTNlY.js → timeline-definition-24CTP7MA-BA-lVH6P.js} +1 -1
  97. package/dist/client/assets/vault-api-client-iZLhMJKe.js +6 -0
  98. package/dist/client/assets/{vennDiagram-4TSXK5OY-BekRp5Y0.js → vennDiagram-4TSXK5OY-DNdUC_Xj.js} +1 -1
  99. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-kdnV_Hdm.js → wardleyDiagram-VM6X3IG4-C2oY-YbF.js} +1 -1
  100. package/dist/client/assets/wiki-link-resolver-DFIwErjc.js +1 -0
  101. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DufGwNSB.js → xychartDiagram-S5SC5T6Z-CoIR8CBX.js} +1 -1
  102. package/dist/client/assets/{yjs-provider-reset-guard-DE0vINDP.js → yjs-provider-reset-guard-9ul1qF6k.js} +1 -1
  103. package/dist/client/drawio-editor.html +11 -3
  104. package/dist/client/excalidraw-editor.html +45 -2
  105. package/dist/client/export-document.html +3 -3
  106. package/dist/client/index.html +67 -35
  107. package/integrations/pi/index.ts +94 -0
  108. package/package.json +41 -32
  109. package/src/client/app/drawio-editor.html +2 -2
  110. package/src/client/app/excalidraw-editor.html +2 -2
  111. package/src/client/app/export-document.html +1 -1
  112. package/src/client/app/index.html +49 -33
  113. package/src/client/app/main-entry.js +15 -1
  114. package/src/client/application/app-shell/chat-feature.js +7 -22
  115. package/src/client/application/app-shell/git-feature.js +23 -5
  116. package/src/client/application/app-shell/presence-feature.js +8 -25
  117. package/src/client/application/app-shell/ui-feature-shell.js +36 -91
  118. package/src/client/application/app-shell/ui-feature-sidebar.js +43 -7
  119. package/src/client/application/app-shell/ui-feature-tab-activity.js +19 -6
  120. package/src/client/application/app-shell/ui-feature-toolbar.js +16 -129
  121. package/src/client/application/app-shell-elements.js +4 -0
  122. package/src/client/application/preview-render-compiler.js +1 -87
  123. package/src/client/application/preview-render-scheduler.js +8 -24
  124. package/src/client/application/preview-renderer.js +2 -2
  125. package/src/client/application/webmcp-diagram-renderer.js +47 -0
  126. package/src/client/application/workspace-coordinator.js +3 -1
  127. package/src/client/application/workspace-route-controller.js +7 -0
  128. package/src/client/bootstrap/collabmd-app-shell.js +78 -1
  129. package/src/client/domain/excalidraw-element-link.js +73 -0
  130. package/src/client/domain/excalidraw-scene.js +21 -0
  131. package/src/client/domain/hash-routes.js +8 -0
  132. package/src/client/domain/room.js +1 -5
  133. package/src/client/domain/runtime-paths.js +5 -0
  134. package/src/client/excalidraw-editor.js +135 -10
  135. package/src/client/export/export-host.js +3 -1
  136. package/src/client/export/export-pipeline.js +4 -17
  137. package/src/client/infrastructure/agent-connection-api-client.js +23 -0
  138. package/src/client/infrastructure/auth-client.js +3 -13
  139. package/src/client/infrastructure/editor-session.js +11 -0
  140. package/src/client/infrastructure/editor-view-adapter.js +13 -0
  141. package/src/client/infrastructure/excalidraw-room-client.js +80 -1
  142. package/src/client/infrastructure/git-api-client.js +39 -33
  143. package/src/client/infrastructure/lobby-presence.js +2 -5
  144. package/src/client/infrastructure/runtime-config.js +11 -1
  145. package/src/client/infrastructure/tab-activity-lock.js +2 -5
  146. package/src/client/infrastructure/vault-api-client.js +33 -26
  147. package/src/client/infrastructure/webmcp-tool-registry.js +283 -0
  148. package/src/client/infrastructure/workspace-sync-client.js +6 -4
  149. package/src/client/presentation/agent-connection-controller.js +255 -0
  150. package/src/client/presentation/backlinks-panel.js +3 -0
  151. package/src/client/presentation/bases-preview-controller.js +27 -18
  152. package/src/client/presentation/comment-ui/comment-ui-card.js +4 -2
  153. package/src/client/presentation/create-menu-presenter.js +19 -16
  154. package/src/client/presentation/excalidraw-embed-controller.js +164 -4
  155. package/src/client/presentation/file-explorer-view.js +70 -12
  156. package/src/client/presentation/file-history-view-controller.js +13 -11
  157. package/src/client/presentation/git-diff-view-controller.js +35 -43
  158. package/src/client/presentation/git-panel-controller.js +77 -27
  159. package/src/client/presentation/image-lightbox-controller.js +20 -16
  160. package/src/client/presentation/layout-controller.js +3 -1
  161. package/src/client/presentation/quick-switcher-controller.js +20 -78
  162. package/src/client/presentation/quick-switcher-text-search.js +10 -14
  163. package/src/client/styles/components/navigation.css +1 -2
  164. package/src/client/styles/features/agent-connection.css +202 -0
  165. package/src/client/styles/features/collaboration-chat.css +8 -1
  166. package/src/client/styles/features/collaboration-dialogs.css +16 -1
  167. package/src/client/styles/features/collaboration-presence.css +17 -4
  168. package/src/client/styles/features/diff-toolbar.css +1 -1
  169. package/src/client/styles/features/media-lightbox.css +12 -3
  170. package/src/client/styles/features/quick-switcher.css +20 -11
  171. package/src/client/styles/foundation/themes.css +2 -0
  172. package/src/client/styles/layout/editor-page.css +25 -11
  173. package/src/client/styles/layout/responsive.css +2 -20
  174. package/src/client/styles/layout/shell.css +1 -0
  175. package/src/client/styles/layout/sidebar.css +7 -4
  176. package/src/client/styles/primitives/controls.css +21 -54
  177. package/src/client/styles/style.css +1 -0
  178. package/src/domain/agent-tool-definitions.js +796 -0
  179. package/src/domain/collabmd-content-capabilities.js +149 -0
  180. package/src/domain/collaboration-protocol.js +2 -0
  181. package/src/domain/comment-threads.js +3 -5
  182. package/src/domain/diagram-source.js +50 -0
  183. package/src/domain/editable-content-revision.js +10 -0
  184. package/src/domain/editable-text.js +3 -0
  185. package/src/domain/exact-text-edits.js +69 -0
  186. package/src/domain/excalidraw-agent-scene.js +522 -0
  187. package/src/domain/excalidraw-agent-verification.js +460 -0
  188. package/src/domain/excalidraw-room-codec.js +1 -5
  189. package/src/domain/video-embed.js +76 -0
  190. package/src/server/application/agent-connection-service.js +178 -0
  191. package/src/server/application/agent-content-service.js +773 -0
  192. package/src/server/application/workspace-reconciliation.js +5 -7
  193. package/src/server/config/env.js +51 -1
  194. package/src/server/create-app-server.js +36 -0
  195. package/src/server/domain/bases/base-query-service.js +1 -5
  196. package/src/server/domain/collaboration/collaboration-room.js +121 -25
  197. package/src/server/domain/collaboration/protocol.js +4 -0
  198. package/src/server/domain/hosted-workspace.js +21 -0
  199. package/src/server/domain/markdown-reference-extractor.js +15 -1
  200. package/src/server/infrastructure/git/git-service.js +19 -1
  201. package/src/server/infrastructure/git/history-service.js +1 -1
  202. package/src/server/infrastructure/git/local-exclude.js +25 -0
  203. package/src/server/infrastructure/git/parsers.js +19 -17
  204. package/src/server/infrastructure/git/path-utils.js +0 -16
  205. package/src/server/infrastructure/git/status-service.js +1 -1
  206. package/src/server/infrastructure/git/untracked-files.js +12 -21
  207. package/src/server/infrastructure/http/create-agent-api-handler.js +171 -0
  208. package/src/server/infrastructure/http/create-git-api-command-handler.js +32 -0
  209. package/src/server/infrastructure/http/create-request-handler.js +28 -0
  210. package/src/server/infrastructure/http/create-static-handler.js +6 -0
  211. package/src/server/infrastructure/mcp/create-agent-mcp-handler.js +199 -0
  212. package/src/server/infrastructure/persistence/agent-connection-store.js +120 -0
  213. package/src/server/infrastructure/persistence/pull-backup-store.js +2 -27
  214. package/src/server/infrastructure/persistence/vault-file-store.js +9 -2
  215. package/src/server/shared/agent-tool-image.js +22 -0
  216. package/src/server/shared/agent-tool-rate-limiter.js +20 -0
  217. package/src/server/startup/git-remote-bootstrap.js +5 -33
  218. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +0 -1
  219. package/dist/client/assets/abnfDiagram-VCTEODGH-BNDLzJ7L.js +0 -1
  220. package/dist/client/assets/browser-utils--H_DkyEF.js +0 -1
  221. package/dist/client/assets/channel-CaoKA_We.js +0 -1
  222. package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +0 -2
  223. package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +0 -127
  224. package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +0 -10
  225. package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +0 -1
  226. package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +0 -1
  227. package/dist/client/assets/comment-threads-9J6Em-_y.js +0 -4
  228. package/dist/client/assets/comment-ui-shared-DuoyznXW.js +0 -7
  229. package/dist/client/assets/diagram-VSXAHHWV-CQRVEZR7.js +0 -3
  230. package/dist/client/assets/dist-73RPdcyU.js +0 -1
  231. package/dist/client/assets/dist-CH-sQu3r.js +0 -1
  232. package/dist/client/assets/document-formatter-Dxl_a8XO.js +0 -2
  233. package/dist/client/assets/drawio-editor-BVaNNtYp.js +0 -9
  234. package/dist/client/assets/drawioEditor-BkIBlXx0.js +0 -2
  235. package/dist/client/assets/ebnfDiagram-PWID7BFC-OXa8VEp1.js +0 -1
  236. package/dist/client/assets/editor-session-DMyVdHLj.js +0 -54
  237. package/dist/client/assets/excalidraw-editor-BjYaNEMY.js +0 -9
  238. package/dist/client/assets/excalidraw-mermaid-stub-BNckbX2n.js +0 -1
  239. package/dist/client/assets/excalidraw-mermaid-stub-DE5FTiZB.js +0 -1
  240. package/dist/client/assets/excalidrawEditor-ChqF_d82.js +0 -2
  241. package/dist/client/assets/export-page-Dyu0XCbu.js +0 -57
  242. package/dist/client/assets/exportDocument-CJBx4Xjx.css +0 -1
  243. package/dist/client/assets/exportDocument-Dgvfa2Sd.js +0 -2
  244. package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +0 -1
  245. package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +0 -1
  246. package/dist/client/assets/index-CSNIMyOP.css +0 -1
  247. package/dist/client/assets/index-DG3N2Ps-.js +0 -2
  248. package/dist/client/assets/main-D0wCxpVn.js +0 -903
  249. package/dist/client/assets/mermaid.core-BJRLUQZE.js +0 -11
  250. package/dist/client/assets/pegDiagram-XKGWAZYB-B31fKPR2.js +0 -1
  251. package/dist/client/assets/preview-render-compiler-CrJIooVD.js +0 -35
  252. package/dist/client/assets/preview-render-compiler-O0JxBVNF.js +0 -1
  253. package/dist/client/assets/preview-render-profile-B4RWYWHt.js +0 -1
  254. package/dist/client/assets/prod-BiYPMR1i.js +0 -177
  255. package/dist/client/assets/prod-VxdGZ5Ly.js +0 -1
  256. package/dist/client/assets/quick-switcher-controller-CaEjEtq_.js +0 -1
  257. package/dist/client/assets/railroadDiagram-O6MQD6OU-BPifP0MB.js +0 -1
  258. package/dist/client/assets/runtime-config-BLrEKVO1.js +0 -1
  259. package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +0 -1
  260. package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +0 -8
  261. package/dist/client/assets/vault-api-client-BkwdFjx0.js +0 -6
  262. package/src/client/app/drawio-editor-entry.js +0 -1
  263. package/src/client/app/excalidraw-editor-entry.js +0 -1
  264. package/src/client/excalidraw-mermaid-stub.js +0 -7
  265. package/src/client/main.js +0 -16
  266. /package/dist/client/assets/{drawio-editor-UQQ8XHou.css → drawioEditor-UQQ8XHou.css} +0 -0
  267. /package/dist/client/assets/{excalidraw-editor-BbSbI80a.css → excalidrawEditor-BbSbI80a.css} +0 -0
  268. /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();
@@ -346,10 +346,10 @@ function renderSortPanel(result) {
346
346
  <div class="bases-panel-section">
347
347
  <div class="bases-panel-title">Group by</div>
348
348
  <div class="bases-sort-row">
349
- <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">
350
350
  ${createSelectOptions(result, { allowBlank: true, selected: viewConfig.groupBy?.property ?? '' })}
351
351
  </select>
352
- <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'}>
353
353
  ${groupDirections.map((direction) => `<option value="${escapeHtml(direction.id)}"${direction.id === (viewConfig.groupBy?.direction ?? 'asc') ? ' selected' : ''}>${escapeHtml(direction.label)}</option>`).join('')}
354
354
  </select>
355
355
  <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-clear-group-by${viewConfig.groupBy?.property ? '' : ' disabled'}>Clear</button>
@@ -366,15 +366,15 @@ function renderSortPanel(result) {
366
366
  ];
367
367
  return `
368
368
  <div class="bases-sort-row">
369
- <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">
370
370
  ${createSelectOptions(result, { selected: sortConfig.property })}
371
371
  </select>
372
- <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">
373
373
  ${directions.map((direction) => `<option value="${escapeHtml(direction.id)}"${direction.id === sortConfig.direction ? ' selected' : ''}>${escapeHtml(direction.label)}</option>`).join('')}
374
374
  </select>
375
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-move="${index}:up"${index === 0 ? ' disabled' : ''}>↑</button>
376
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-sort-move="${index}:down"${index === sorts.length - 1 ? ' disabled' : ''}>↓</button>
377
- <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>
378
378
  </div>
379
379
  `;
380
380
  }).join('')}
@@ -389,6 +389,10 @@ function encodePath(path = []) {
389
389
  return path.join('.');
390
390
  }
391
391
 
392
+ function formatFilterPath(path = []) {
393
+ return path.map((index) => index + 1).join('.');
394
+ }
395
+
392
396
  function decodePath(value = '') {
393
397
  return String(value ?? '')
394
398
  .split('.')
@@ -773,9 +777,10 @@ function renderFilterSuggestionList(presentation, currentValue = '') {
773
777
 
774
778
  function renderFilterValueCombobox(result, rule, path, entry) {
775
779
  const presentation = buildFilterValuePresentation(result, rule, path, entry);
780
+ const filterLabel = `Filter ${formatFilterPath(path)}`;
776
781
  return `
777
782
  <div class="bases-filter-value-combobox${presentation.suggestionsEnabled ? ' has-suggestions' : ''}">
778
- <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' : ''}>
779
784
  ${renderFilterSuggestionList(presentation, rule.value ?? '')}
780
785
  </div>
781
786
  `;
@@ -783,17 +788,18 @@ function renderFilterValueCombobox(result, rule, path, entry) {
783
788
 
784
789
  function renderFilterRule(result, rule, path, entry) {
785
790
  const presentation = buildFilterValuePresentation(result, rule, path, entry);
791
+ const filterLabel = `Filter ${formatFilterPath(path)}`;
786
792
 
787
793
  return `
788
794
  <div class="bases-filter-row">
789
- <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">
790
796
  ${getPropertyOptionsMarkup(result, rule.propertyId)}
791
797
  </select>
792
- <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">
793
799
  ${presentation.operators.map((operator) => `<option value="${escapeHtml(operator)}"${operator === rule.operator ? ' selected' : ''}>${escapeHtml(operator)}</option>`).join('')}
794
800
  </select>
795
801
  ${renderFilterValueCombobox(result, rule, path, entry)}
796
- <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>
797
803
  </div>
798
804
  `;
799
805
  }
@@ -819,6 +825,7 @@ function syncFilterValueCombobox(entry, result, path, { fallbackToFullRender = t
819
825
  combobox.classList.toggle('has-suggestions', presentation.suggestionsEnabled);
820
826
  input.className = presentation.valueInputClasses;
821
827
  input.disabled = presentation.valueDisabled;
828
+ input.setAttribute('aria-label', `Filter ${formatFilterPath(path)} value`);
822
829
  if (input.value !== String(rule.value ?? '')) {
823
830
  input.value = String(rule.value ?? '');
824
831
  }
@@ -861,17 +868,18 @@ function forEachFilterRule(node, callback, path = []) {
861
868
 
862
869
  function renderFilterGroup(result, group, path, entry, { isRoot = false } = {}) {
863
870
  const encodedPath = encodePath(path);
871
+ const groupLabel = isRoot ? 'Root filter group' : `Filter group ${formatFilterPath(path)}`;
864
872
  return `
865
873
  <div class="bases-filter-group"${isRoot ? ' data-base-filter-root' : ''}>
866
874
  <div class="bases-filter-group-header">
867
- <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">
868
876
  ${['and', 'or', 'not'].map((conjunction) => `<option value="${conjunction}"${group.conjunction === conjunction ? ' selected' : ''}>${escapeHtml({
869
877
  and: 'All the following are true',
870
878
  not: 'None of the following are true',
871
879
  or: 'Any of the following are true',
872
880
  }[conjunction])}</option>`).join('')}
873
881
  </select>
874
- ${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>`}
875
883
  </div>
876
884
  <div class="bases-filter-children">
877
885
  ${(group.children ?? []).map((child, index) => (
@@ -881,8 +889,8 @@ function renderFilterGroup(result, group, path, entry, { isRoot = false } = {})
881
889
  )).join('')}
882
890
  </div>
883
891
  <div class="bases-filter-actions-row">
884
- <button type="button" class="${escapeHtml(buttonClassNames({ variant: 'secondary', size: 'compact' }))}" data-base-filter-add-rule="${escapeHtml(encodedPath)}">Add filter</button>
885
- <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>
886
894
  </div>
887
895
  </div>
888
896
  `;
@@ -911,6 +919,7 @@ function renderFilterPanel(result, entry) {
911
919
  ${mode === 'advanced'
912
920
  ? `
913
921
  <label class="bases-filter-advanced-label">
922
+ <span class="bases-panel-title">Filter expression</span>
914
923
  <textarea class="${escapeHtml(inputClassNames({ extra: 'bases-filter-advanced' }))}" data-base-filter-advanced>${escapeHtml(rawText)}</textarea>
915
924
  </label>
916
925
  <div class="bases-filter-actions-row">
@@ -1687,7 +1696,7 @@ export class BasesPreviewController {
1687
1696
  entry.requestVersion = requestVersion;
1688
1697
  const hasShell = Boolean(findShellElement(entry));
1689
1698
  if (!hasShell) {
1690
- replaceChildrenFromHtml(entry.placeholder, '<div class="preview-shell">Loading base…</div>');
1699
+ replaceChildrenFromHtml(entry.placeholder, '<div class="preview-shell" role="status" aria-live="polite">Loading base…</div>');
1691
1700
  }
1692
1701
  try {
1693
1702
  const response = await this.vaultApiClient.queryBase({
@@ -1711,11 +1720,11 @@ export class BasesPreviewController {
1711
1720
  const shell = findShellElement(entry);
1712
1721
  const content = shell?.querySelector?.('[data-base-content]') ?? null;
1713
1722
  if (content) {
1714
- replaceChildrenFromHtml(content, `<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>`);
1715
1724
  return;
1716
1725
  }
1717
1726
  }
1718
- replaceChildrenFromHtml(entry.placeholder, `<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>`);
1719
1728
  }
1720
1729
  }
1721
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