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
@@ -1,6 +1,7 @@
1
1
  import { setDiagramActionButtonIcon } from '../domain/diagram-action-icons.js';
2
+ import { parseExcalidrawElementLink } from '../domain/excalidraw-element-link.js';
2
3
  import { reconcileEmbedEntries } from './excalidraw-embed-reconciler.js';
3
- import { resolveAppUrl, resolveWsServerOverride } from '../domain/runtime-paths.js';
4
+ import { resolveAppPath, resolveAppUrl, resolveWsServerOverride } from '../domain/runtime-paths.js';
4
5
  import {
5
6
  cancelIdleRender,
6
7
  isNearViewport,
@@ -26,6 +27,7 @@ export class ExcalidrawEmbedController {
26
27
  constructor({
27
28
  getTheme,
28
29
  getLocalUser,
30
+ onOpenElement = null,
29
31
  onOpenFile = null,
30
32
  onStopFollowing = null,
31
33
  onToggleQuickSwitcher = null,
@@ -35,6 +37,7 @@ export class ExcalidrawEmbedController {
35
37
  }) {
36
38
  this.getTheme = getTheme;
37
39
  this.getLocalUser = getLocalUser;
40
+ this.onOpenElement = onOpenElement;
38
41
  this.onOpenFile = onOpenFile;
39
42
  this.onStopFollowing = onStopFollowing;
40
43
  this.onToggleQuickSwitcher = onToggleQuickSwitcher;
@@ -56,6 +59,8 @@ export class ExcalidrawEmbedController {
56
59
  this.followedPeerIdsByFilePath = new Map();
57
60
  this.pendingDisconnectRequests = new Map();
58
61
  this.disconnectRequestCounter = 0;
62
+ this.pendingAgentBridgeRequests = new Map();
63
+ this.agentBridgeRequestCounter = 0;
59
64
 
60
65
  this._onMessage = this._onMessage.bind(this);
61
66
  this._onKeyDown = this._onKeyDown.bind(this);
@@ -96,6 +101,10 @@ export class ExcalidrawEmbedController {
96
101
  request.resolve(false);
97
102
  });
98
103
  this.pendingDisconnectRequests.clear();
104
+ this.pendingAgentBridgeRequests.forEach((request) => {
105
+ request.resolve({ status: 'destroyed' });
106
+ });
107
+ this.pendingAgentBridgeRequests.clear();
99
108
  this.followedPeerIdsByFilePath.clear();
100
109
  this.overlayRoot?.remove();
101
110
  this.overlayRoot = null;
@@ -251,6 +260,28 @@ export class ExcalidrawEmbedController {
251
260
  return true;
252
261
  }
253
262
 
263
+ openElement(filePath, elementId, elementType = 'element') {
264
+ if (!filePath || !elementId) {
265
+ return false;
266
+ }
267
+
268
+ const entry = this._findEntryByFilePath(filePath);
269
+ if (!entry) {
270
+ return false;
271
+ }
272
+
273
+ entry.pendingElementFocus = {
274
+ elementId: String(elementId),
275
+ elementType: elementType === 'group' ? 'group' : 'element',
276
+ };
277
+ if (entry.isReady) {
278
+ this._postPendingElement(entry);
279
+ } else if (!entry.wrapper) {
280
+ this._enqueueHydration(entry, { prioritize: true });
281
+ }
282
+ return true;
283
+ }
284
+
254
285
  prepareFileDisconnect(filePath, { timeoutMs = 10000 } = {}) {
255
286
  const entry = this._findEntryByFilePath(filePath);
256
287
  if (!entry?.iframe?.contentWindow) {
@@ -310,6 +341,73 @@ export class ExcalidrawEmbedController {
310
341
  });
311
342
  }
312
343
 
344
+ flushPendingAgentWrites(filePath, { signal = null, timeoutMs = 1500 } = {}) {
345
+ const entry = this._findEntryByFilePath(filePath);
346
+ if (
347
+ !entry?.isReady
348
+ || !entry.iframe?.contentWindow
349
+ || this._getEntryMode(entry) !== 'edit'
350
+ ) {
351
+ return Promise.resolve({ status: 'not-active' });
352
+ }
353
+ return this._requestAgentBridge(
354
+ entry,
355
+ 'flush-agent-writes',
356
+ {},
357
+ { signal, timeoutMs },
358
+ );
359
+ }
360
+
361
+ waitForAgentPaint(filePath, revision, {
362
+ afterRevision = '',
363
+ signal = null,
364
+ timeoutMs = 5000,
365
+ } = {}) {
366
+ const entry = this._findEntryByFilePath(filePath);
367
+ if (!entry?.isReady || !entry.iframe?.contentWindow || !revision) {
368
+ return Promise.resolve({ status: 'not-active' });
369
+ }
370
+ return this._requestAgentBridge(
371
+ entry,
372
+ 'wait-for-agent-revision',
373
+ { afterRevision, revision },
374
+ { signal, timeoutMs },
375
+ );
376
+ }
377
+
378
+ _requestAgentBridge(entry, type, payload, { signal, timeoutMs }) {
379
+ const requestId = `agent-bridge-${++this.agentBridgeRequestCounter}`;
380
+ return new Promise((resolve) => {
381
+ const finish = (value) => {
382
+ const request = this.pendingAgentBridgeRequests.get(requestId);
383
+ if (!request) return;
384
+ window.clearTimeout(request.timeoutId);
385
+ signal?.removeEventListener?.('abort', request.abort);
386
+ this.pendingAgentBridgeRequests.delete(requestId);
387
+ resolve(value);
388
+ };
389
+ const abort = () => finish({ status: 'cancelled' });
390
+ const timeoutId = window.setTimeout(() => finish({ status: 'timeout' }), timeoutMs);
391
+ this.pendingAgentBridgeRequests.set(requestId, {
392
+ abort,
393
+ entry,
394
+ resolve: finish,
395
+ timeoutId,
396
+ });
397
+ signal?.addEventListener?.('abort', abort, { once: true });
398
+ if (signal?.aborted) {
399
+ abort();
400
+ return;
401
+ }
402
+ this._postMessageToEntry(entry, {
403
+ ...payload,
404
+ requestId,
405
+ source: 'collabmd-host',
406
+ type,
407
+ });
408
+ });
409
+ }
410
+
313
411
  _ensurePlaceholderObserver() {
314
412
  if (!this.previewContainer) {
315
413
  return;
@@ -419,6 +517,9 @@ export class ExcalidrawEmbedController {
419
517
 
420
518
  if (!entry.wrapper) {
421
519
  const mount = this._createEmbedContainer(entry);
520
+ if (!mount) {
521
+ return;
522
+ }
422
523
  entry.header = mount.header;
423
524
  entry.wrapper = mount.wrapper;
424
525
  entry.iframe = mount.iframe;
@@ -491,6 +592,7 @@ export class ExcalidrawEmbedController {
491
592
  reusedEntry.key = nextEntry.key;
492
593
  reusedEntry.label = nextEntry.label;
493
594
  reusedEntry.placeholder = nextEntry.placeholder;
595
+ reusedEntry.pendingElementFocus = null;
494
596
  reusedEntry.queued = false;
495
597
  nextEntries.set(key, reusedEntry);
496
598
  });
@@ -644,7 +746,12 @@ export class ExcalidrawEmbedController {
644
746
  }
645
747
 
646
748
  _buildIframeUrl(entry) {
647
- const iframeUrl = new URL(resolveAppUrl(EDITOR_IFRAME_PATH));
749
+ let iframeUrl;
750
+ try {
751
+ iframeUrl = new URL(resolveAppUrl(EDITOR_IFRAME_PATH));
752
+ } catch {
753
+ return null;
754
+ }
648
755
  const theme = this.getTheme?.() || 'dark';
649
756
 
650
757
  iframeUrl.searchParams.set('file', entry.filePath);
@@ -687,6 +794,11 @@ export class ExcalidrawEmbedController {
687
794
  }
688
795
 
689
796
  _createEmbedContainer(entry) {
797
+ const iframeUrl = this._buildIframeUrl(entry);
798
+ if (!iframeUrl) {
799
+ return null;
800
+ }
801
+
690
802
  const wrapper = document.createElement('div');
691
803
  wrapper.className = 'excalidraw-embed diagram-preview-shell';
692
804
  wrapper.dataset.embedKey = entry.key;
@@ -727,7 +839,7 @@ export class ExcalidrawEmbedController {
727
839
  iframe.className = 'excalidraw-embed-iframe';
728
840
  iframe.dataset.instanceId = String(++this.instanceCounter);
729
841
  entry.instanceId = iframe.dataset.instanceId;
730
- iframe.src = this._buildIframeUrl(entry).toString();
842
+ iframe.src = iframeUrl.toString();
731
843
  // The controller already lazy-hydrates embeds near the viewport, so the
732
844
  // iframe should start loading immediately once it has been mounted.
733
845
  iframe.setAttribute('loading', 'eager');
@@ -1030,6 +1142,7 @@ export class ExcalidrawEmbedController {
1030
1142
  entry.bootMode = null;
1031
1143
  entry.loadedFilePath = null;
1032
1144
  entry.loadedMode = null;
1145
+ entry.pendingElementFocus = null;
1033
1146
  entry.placeholder = null;
1034
1147
  if (entry.wrapper.parentElement !== warmCacheRoot) {
1035
1148
  warmCacheRoot.appendChild(entry.wrapper);
@@ -1053,7 +1166,11 @@ export class ExcalidrawEmbedController {
1053
1166
  entry.bootMode = this._getEntryMode(entry);
1054
1167
  entry.loadedFilePath = null;
1055
1168
  entry.loadedMode = null;
1056
- entry.iframe.src = this._buildIframeUrl(entry).toString();
1169
+ const iframeUrl = this._buildIframeUrl(entry);
1170
+ if (!iframeUrl) {
1171
+ return;
1172
+ }
1173
+ entry.iframe.src = iframeUrl.toString();
1057
1174
  this._armEntryBootTimeout(entry);
1058
1175
  }
1059
1176
 
@@ -1218,6 +1335,7 @@ export class ExcalidrawEmbedController {
1218
1335
  });
1219
1336
  this._syncEntryFollowState(entry);
1220
1337
  this._postPendingCommentThread?.(entry);
1338
+ this._postPendingElement?.(entry);
1221
1339
  if (this._entryNeedsHardReload(entry)) {
1222
1340
  void this._hydrateEntry(entry);
1223
1341
  }
@@ -1234,6 +1352,17 @@ export class ExcalidrawEmbedController {
1234
1352
  return;
1235
1353
  }
1236
1354
 
1355
+ if (msg.type === 'open-element-link') {
1356
+ const target = parseExcalidrawElementLink(msg.href, {
1357
+ appPath: resolveAppPath('/'),
1358
+ origin: window.location.origin,
1359
+ });
1360
+ if (target) {
1361
+ this.onOpenElement?.(target);
1362
+ }
1363
+ return;
1364
+ }
1365
+
1237
1366
  if (msg.type === 'error') {
1238
1367
  if (entry.isParked) {
1239
1368
  return;
@@ -1252,6 +1381,23 @@ export class ExcalidrawEmbedController {
1252
1381
  return;
1253
1382
  }
1254
1383
 
1384
+ if (
1385
+ msg.type === 'agent-writes-flushed'
1386
+ || msg.type === 'agent-revision-painted'
1387
+ || msg.type === 'agent-revision-not-painted'
1388
+ ) {
1389
+ const request = this.pendingAgentBridgeRequests.get(msg.requestId);
1390
+ if (request?.entry === entry) {
1391
+ request.resolve({
1392
+ ...(msg.revision ? { revision: msg.revision } : {}),
1393
+ status: msg.status || (msg.type === 'agent-writes-flushed'
1394
+ ? 'flushed'
1395
+ : (msg.type === 'agent-revision-painted' ? 'painted' : 'not-painted')),
1396
+ });
1397
+ }
1398
+ return;
1399
+ }
1400
+
1255
1401
  if (msg.type === 'disconnect-blocked') {
1256
1402
  entry.authorityState = msg.state || 'reconnecting-readonly';
1257
1403
  this.toastController?.show?.('Waiting for the diagram to reconnect before leaving');
@@ -1300,6 +1446,20 @@ export class ExcalidrawEmbedController {
1300
1446
  entry.pendingCommentThreadId = null;
1301
1447
  }
1302
1448
 
1449
+ _postPendingElement(entry) {
1450
+ if (!entry?.isReady || !entry.pendingElementFocus) {
1451
+ return;
1452
+ }
1453
+
1454
+ this._postMessageToEntry(entry, {
1455
+ elementId: entry.pendingElementFocus.elementId,
1456
+ elementType: entry.pendingElementFocus.elementType,
1457
+ source: 'collabmd-host',
1458
+ type: 'focus-element',
1459
+ });
1460
+ entry.pendingElementFocus = null;
1461
+ }
1462
+
1303
1463
  _requestPreviewViewportFit(entry) {
1304
1464
  if (this._isFilePreviewEntry(entry)) {
1305
1465
  return;
@@ -61,6 +61,7 @@ export class FileActionController {
61
61
  mobileBreakpointQuery = (typeof window !== 'undefined' && typeof window.matchMedia === 'function')
62
62
  ? window.matchMedia('(max-width: 768px)')
63
63
  : { matches: false },
64
+ onDirectoryExport,
64
65
  onFileDelete,
65
66
  onFileSelect,
66
67
  onShowFileExtensionsChange,
@@ -72,6 +73,7 @@ export class FileActionController {
72
73
  view,
73
74
  refresh,
74
75
  }) {
76
+ this.onDirectoryExport = onDirectoryExport;
75
77
  this.onFileDelete = onFileDelete;
76
78
  this.onFileSelect = onFileSelect;
77
79
  this.onShowFileExtensionsChange = onShowFileExtensionsChange;
@@ -226,15 +228,26 @@ export class FileActionController {
226
228
 
227
229
  getDirectoryContextMenuItems(directoryPath) {
228
230
  return [
229
- ...this.createContextMenuItems(directoryPath),
231
+ {
232
+ label: 'New…',
233
+ onSelect: ({ anchor } = {}) => this.openCreateMenu({ anchor, parentDir: directoryPath }),
234
+ },
230
235
  {
231
236
  label: 'Rename / move',
232
237
  onSelect: () => this.handleRenameDirectory(directoryPath),
233
238
  },
234
239
  {
235
- label: 'Download',
240
+ label: 'Download source ZIP',
236
241
  onSelect: () => this.handleDownloadDirectory(directoryPath),
237
242
  },
243
+ {
244
+ label: 'Export HTML',
245
+ onSelect: () => this.onDirectoryExport?.(directoryPath, 'html'),
246
+ },
247
+ {
248
+ label: 'Print / save PDF',
249
+ onSelect: () => this.onDirectoryExport?.(directoryPath, 'pdf'),
250
+ },
238
251
  {
239
252
  label: 'Delete',
240
253
  danger: true,
@@ -8,6 +8,7 @@ export class FileExplorerController {
8
8
  mobileBreakpointQuery = window.matchMedia('(max-width: 768px)'),
9
9
  onFileSelect,
10
10
  onFileDelete,
11
+ onDirectoryExport,
11
12
  pendingWorkspaceRequestIds = null,
12
13
  onShowFileExtensionsChange,
13
14
  showFileExtensions = false,
@@ -16,6 +17,7 @@ export class FileExplorerController {
16
17
  }) {
17
18
  this.onFileSelect = onFileSelect;
18
19
  this.onFileDelete = onFileDelete;
20
+ this.onDirectoryExport = onDirectoryExport;
19
21
  this.onShowFileExtensionsChange = onShowFileExtensionsChange;
20
22
  this.toastController = toastController;
21
23
  this.vaultClient = vaultClient;
@@ -56,6 +58,7 @@ export class FileExplorerController {
56
58
  });
57
59
  this.actionController = new FileActionController({
58
60
  mobileBreakpointQuery,
61
+ onDirectoryExport: this.onDirectoryExport,
59
62
  onFileDelete: this.onFileDelete,
60
63
  onFileSelect: this.onFileSelect,
61
64
  onShowFileExtensionsChange: (enabled) => {
@@ -58,7 +58,10 @@ export class FileExplorerView {
58
58
  this.longPressContext = null;
59
59
  this.suppressedActivationTarget = null;
60
60
  this.contextMenuCloseHandler = null;
61
+ this.contextMenuCloseTimer = null;
62
+ this.contextMenuAnchor = null;
61
63
  this.actionSheetCloseHandler = null;
64
+ this.blockingModalHandler = () => this.removeContextMenu();
62
65
  this.dragSource = null;
63
66
  this.currentSearchQuery = '';
64
67
  this.activeDropTarget = null;
@@ -336,6 +339,8 @@ export class FileExplorerView {
336
339
  button.setAttribute('aria-expanded', String(isExpanded));
337
340
  button.dataset.path = node.path;
338
341
  button.dataset.entryType = 'directory';
342
+ // Dynamic labels are escaped and icons are application-owned markup.
343
+ // pi-lens-ignore: no-inner-html-js
339
344
  button.innerHTML = `
340
345
  <svg class="file-tree-chevron${isExpanded ? ' expanded' : ''}" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
341
346
  <svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
@@ -502,6 +507,8 @@ export class FileExplorerView {
502
507
  ${parentPath ? `<span class="file-tree-search-path">${escapeHtml(parentPath)}</span>` : ''}
503
508
  </span>`
504
509
  : `<span class="file-tree-name">${escapeHtml(displayName)}</span>`;
510
+ // Dynamic labels are escaped and icons are application-owned markup.
511
+ // pi-lens-ignore: no-inner-html-js
505
512
  button.innerHTML = `
506
513
  ${getVaultFileIconSvg(filePath)}
507
514
  ${nameMarkup}
@@ -965,6 +972,8 @@ export class FileExplorerView {
965
972
  return;
966
973
  }
967
974
 
975
+ const contextAnchor = event.currentTarget instanceof Element ? event.currentTarget : event.target;
976
+ this.contextMenuAnchor = contextAnchor instanceof HTMLElement ? contextAnchor : null;
968
977
  if (this.isMobileViewport()) {
969
978
  this.showActionSheet(items);
970
979
  return;
@@ -972,14 +981,18 @@ export class FileExplorerView {
972
981
 
973
982
  const menu = document.createElement('div');
974
983
  menu.className = 'file-context-menu';
984
+ menu.setAttribute('role', 'menu');
985
+ menu.setAttribute('aria-label', 'File actions');
975
986
 
976
987
  for (const item of items) {
977
988
  const button = document.createElement('button');
989
+ button.type = 'button';
978
990
  button.className = `file-context-item${item.danger ? ' file-context-danger' : ''}`;
991
+ button.setAttribute('role', 'menuitem');
979
992
  button.textContent = item.label;
980
993
  button.addEventListener('click', () => {
981
994
  this.removeContextMenu();
982
- item.onSelect?.();
995
+ item.onSelect?.({ anchor: contextAnchor });
983
996
  });
984
997
  menu.appendChild(button);
985
998
  }
@@ -998,17 +1011,47 @@ export class FileExplorerView {
998
1011
  }
999
1012
  };
1000
1013
  this.contextMenuCloseHandler = close;
1001
- setTimeout(() => document.addEventListener('click', close), 0);
1014
+ const handleKeydown = (keyEvent) => {
1015
+ const buttons = Array.from(menu.querySelectorAll('[role="menuitem"]'));
1016
+ const index = Math.max(0, buttons.indexOf(document.activeElement));
1017
+ if (keyEvent.key === 'Escape') {
1018
+ keyEvent.preventDefault();
1019
+ this.removeContextMenu();
1020
+ return;
1021
+ }
1022
+ if (keyEvent.key === 'Tab') {
1023
+ this.removeContextMenu({ restoreFocus: false });
1024
+ return;
1025
+ }
1026
+ const nextIndex = keyEvent.key === 'Home'
1027
+ ? 0
1028
+ : keyEvent.key === 'End'
1029
+ ? buttons.length - 1
1030
+ : keyEvent.key === 'ArrowDown'
1031
+ ? (index + 1) % buttons.length
1032
+ : keyEvent.key === 'ArrowUp'
1033
+ ? (index - 1 + buttons.length) % buttons.length
1034
+ : -1;
1035
+ if (nextIndex >= 0) {
1036
+ keyEvent.preventDefault();
1037
+ buttons[nextIndex]?.focus();
1038
+ }
1039
+ };
1040
+ menu.addEventListener('keydown', handleKeydown);
1041
+ document.addEventListener('collabmd:close-custom-modals', this.blockingModalHandler);
1042
+ this.contextMenuCloseTimer = setTimeout(() => {
1043
+ this.contextMenuCloseTimer = null;
1044
+ if (menu.isConnected && this.contextMenuCloseHandler === close) {
1045
+ document.addEventListener('click', close);
1046
+ }
1047
+ }, 0);
1048
+ menu.querySelector('[role="menuitem"]')?.focus();
1002
1049
  }
1003
1050
 
1004
1051
  showActionSheet(items) {
1005
- const backdrop = document.createElement('button');
1006
- backdrop.type = 'button';
1007
- backdrop.className = 'file-action-sheet-backdrop';
1008
- backdrop.setAttribute('aria-label', 'Close file actions');
1009
-
1010
- const sheet = document.createElement('div');
1052
+ const sheet = document.createElement('dialog');
1011
1053
  sheet.className = 'file-action-sheet';
1054
+ sheet.setAttribute('aria-label', 'File actions');
1012
1055
 
1013
1056
  items.forEach((item) => {
1014
1057
  const button = document.createElement('button');
@@ -1037,26 +1080,42 @@ export class FileExplorerView {
1037
1080
  });
1038
1081
  sheet.appendChild(cancelButton);
1039
1082
 
1040
- backdrop.addEventListener('click', () => {
1083
+ sheet.addEventListener('cancel', (event) => {
1084
+ event.preventDefault();
1041
1085
  this.removeContextMenu();
1042
1086
  });
1087
+ sheet.addEventListener('click', (event) => {
1088
+ const bounds = sheet.getBoundingClientRect();
1089
+ if (event.clientX < bounds.left || event.clientX > bounds.right
1090
+ || event.clientY < bounds.top || event.clientY > bounds.bottom) this.removeContextMenu();
1091
+ });
1043
1092
 
1044
- document.body.append(backdrop, sheet);
1093
+ document.body.append(sheet);
1094
+ document.addEventListener('collabmd:close-custom-modals', this.blockingModalHandler);
1045
1095
  this.actionSheetCloseHandler = () => {
1046
- backdrop.remove();
1096
+ if (sheet.open) sheet.close();
1047
1097
  sheet.remove();
1048
1098
  this.actionSheetCloseHandler = null;
1049
1099
  };
1100
+ sheet.showModal();
1101
+ sheet.querySelector('button:not(:disabled)')?.focus();
1050
1102
  }
1051
1103
 
1052
- removeContextMenu() {
1104
+ removeContextMenu({ restoreFocus = true } = {}) {
1053
1105
  this.cancelLongPress();
1054
1106
  this.clearDragFeedback();
1055
1107
  document.querySelectorAll('.file-context-menu').forEach((menu) => menu.remove());
1108
+ if (this.contextMenuCloseTimer !== null) {
1109
+ clearTimeout(this.contextMenuCloseTimer);
1110
+ this.contextMenuCloseTimer = null;
1111
+ }
1056
1112
  if (this.contextMenuCloseHandler) {
1057
1113
  document.removeEventListener('click', this.contextMenuCloseHandler);
1058
1114
  this.contextMenuCloseHandler = null;
1059
1115
  }
1116
+ document.removeEventListener('collabmd:close-custom-modals', this.blockingModalHandler);
1060
1117
  this.actionSheetCloseHandler?.();
1118
+ if (restoreFocus) this.contextMenuAnchor?.focus?.();
1119
+ this.contextMenuAnchor = null;
1061
1120
  }
1062
1121
  }
@@ -526,6 +526,7 @@ export class FileHistoryViewController {
526
526
  }
527
527
 
528
528
  if (this.stats) {
529
+ // pi-lens-ignore: no-inner-html-js
529
530
  this.stats.innerHTML = `
530
531
  <span class="ui-stat-token ui-stat-token--add diff-stats-add">+${additions}</span>
531
532
  <span class="ui-stat-token ui-stat-token--del diff-stats-del">-${deletions}</span>
@@ -543,12 +544,14 @@ export class FileHistoryViewController {
543
544
  this.actionsDivider?.classList.add('hidden');
544
545
  this.editorActionsGroup?.classList.remove('hidden');
545
546
  this.modeButtons.forEach((button) => {
547
+ const active = button.getAttribute('data-diff-mode') === this.diffMode;
546
548
  button.classList.toggle('hidden', !hasSelection);
547
- button.classList.toggle('active', button.getAttribute('data-diff-mode') === this.diffMode);
549
+ button.classList.toggle('active', active);
550
+ button.setAttribute?.('aria-pressed', String(active));
548
551
  });
549
552
  this.openEditorButton?.toggleAttribute('disabled', !this.currentFilePath);
550
553
  if (this.openEditorButton) {
551
- this.openEditorButton.textContent = 'Open File';
554
+ this.openEditorButton.textContent = 'Open file';
552
555
  }
553
556
  this.primaryActionButton?.toggleAttribute('disabled', true);
554
557
  this.commitButton?.toggleAttribute('disabled', true);
@@ -561,20 +564,21 @@ export class FileHistoryViewController {
561
564
  this.page?.classList.remove('hidden');
562
565
  if (this.content) {
563
566
  this.content.classList.add('diff-content--history');
564
- this.content.innerHTML = '<div class="git-panel-empty">Loading file history...</div>';
567
+ this.content.innerHTML = '<div class="git-panel-empty" role="status" aria-live="polite">Loading file history…</div>';
565
568
  }
566
569
  this.historyListElement = null;
567
570
  this.syncToolbar();
568
571
  }
569
572
 
570
- renderEmpty(message) {
573
+ renderEmpty(message, { alert = false } = {}) {
571
574
  if (!this.isActive) {
572
575
  return;
573
576
  }
574
577
  this.page?.classList.remove('hidden');
575
578
  if (this.content) {
576
579
  this.content.classList.add('diff-content--history');
577
- this.content.innerHTML = `<div class="git-panel-empty">${escapeHtml(message)}</div>`;
580
+ // pi-lens-ignore: no-inner-html-js
581
+ this.content.innerHTML = `<div class="git-panel-empty"${alert ? ' role="alert"' : ''}>${escapeHtml(message)}</div>`;
578
582
  }
579
583
  this.historyListElement = null;
580
584
  this.syncToolbar();
@@ -689,7 +693,7 @@ export class FileHistoryViewController {
689
693
  type="button"
690
694
  data-file-history-open-selected-diff
691
695
  >
692
- Open Standalone Diff
696
+ Open standalone diff
693
697
  </button>
694
698
  ${entry.type === 'commit' ? `
695
699
  <button
@@ -697,7 +701,7 @@ export class FileHistoryViewController {
697
701
  type="button"
698
702
  data-file-history-open-selected-preview
699
703
  >
700
- Preview File
704
+ Preview file
701
705
  </button>
702
706
  ` : ''}
703
707
  </div>
@@ -714,8 +718,10 @@ export class FileHistoryViewController {
714
718
  if (selection.loading) {
715
719
  return `
716
720
  <section class="diff-file-block file-history-diff-block">
717
- ${this.renderDiffFileHeader({ path: entry.pathAtCommit || this.currentFilePath || '' })}
718
- <div class="diff-empty-state">Loading diff...</div>
721
+ <div class="diff-file-header">
722
+ <span class="diff-file-path">${escapeHtml(entry.pathAtCommit || this.currentFilePath || '')}</span>
723
+ </div>
724
+ <div class="diff-empty-state" role="status" aria-live="polite">Loading diff…</div>
719
725
  </section>
720
726
  `;
721
727
  }
@@ -724,7 +730,7 @@ export class FileHistoryViewController {
724
730
  return `
725
731
  <section class="diff-file-block file-history-diff-block">
726
732
  ${this.renderDiffFileHeader({ path: entry.pathAtCommit || this.currentFilePath || '' })}
727
- <div class="diff-empty-state">${escapeHtml(selection.error)}</div>
733
+ <div class="diff-empty-state" role="alert">${escapeHtml(selection.error)}</div>
728
734
  </section>
729
735
  `;
730
736
  }
@@ -760,7 +766,7 @@ export class FileHistoryViewController {
760
766
 
761
767
  const hasRows = Boolean(this.localChanges) || (this.history.commits?.length ?? 0) > 0;
762
768
  if (!hasRows) {
763
- this.renderEmpty(this.history.error || 'No history found for this file.');
769
+ this.renderEmpty(this.history.error || 'No history found for this file.', { alert: Boolean(this.history.error) });
764
770
  return;
765
771
  }
766
772
 
@@ -770,6 +776,7 @@ export class FileHistoryViewController {
770
776
 
771
777
  this.content.classList.add('diff-content--history');
772
778
  const selection = this.getSelectedDetailState();
779
+ // pi-lens-ignore: no-inner-html-js
773
780
  this.content.innerHTML = `
774
781
  <section class="file-history-shell">
775
782
  <aside class="file-history-sidebar" aria-label="File history entries">
@@ -789,7 +796,7 @@ export class FileHistoryViewController {
789
796
  data-file-history-load-more
790
797
  ${this.history.loadingMore ? 'disabled' : ''}
791
798
  >
792
- ${this.history.loadingMore ? 'Loading...' : 'Load More'}
799
+ ${this.history.loadingMore ? 'Loading' : 'Load more'}
793
800
  </button>
794
801
  </div>
795
802
  ` : ''}
@@ -13,17 +13,7 @@ function flattenTree(nodes, files = [], searchEntries = [], fileEntries = []) {
13
13
  type: node.type,
14
14
  });
15
15
 
16
- if (
17
- node.type === 'file'
18
- || node.type === 'base'
19
- || node.type === 'excalidraw'
20
- || node.type === 'drawio'
21
- || node.type === 'mermaid'
22
- || node.type === 'plantuml'
23
- || node.type === 'structurizr'
24
- || node.type === 'pdf'
25
- || node.type === 'image'
26
- ) {
16
+ if (node.type !== 'directory') {
27
17
  files.push(node.path);
28
18
  fileEntries.push({
29
19
  mtimeMs: node.mtimeMs,