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
@@ -1,3 +1,4 @@
1
+ import { createExcalidrawExportOptions } from '../domain/excalidraw-scene.js';
1
2
  import { escapeHtml } from '../domain/vault-utils.js';
2
3
  import { commitButtonLabel } from '../domain/git-labels.js';
3
4
  import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
@@ -200,29 +201,25 @@ function getDrawioSourcePair(detail = {}) {
200
201
  };
201
202
  }
202
203
 
203
- function decodeXmlAttribute(value) {
204
- return String(value ?? '')
205
- .replace(/"/gu, '"')
206
- .replace(/'/gu, "'")
207
- .replace(/&/gu, '&')
208
- .replace(/&lt;/gu, '<')
209
- .replace(/&gt;/gu, '>');
210
- }
211
-
212
204
  function getDrawioCellMap(source) {
213
205
  const cells = new Map();
214
- const cellPattern = /<mxCell\b([^>]*?)(?:\/|>([\s\S]*?)<\/mxCell\s*)>/giu;
215
- let match;
206
+ if (typeof DOMParser === 'undefined' || typeof XMLSerializer === 'undefined') {
207
+ return cells;
208
+ }
216
209
 
217
- while ((match = cellPattern.exec(String(source ?? ''))) !== null) {
218
- const idMatch = match[1].match(/\bid\s*=\s*(['"])(.*?)\1/iu);
219
- const id = decodeXmlAttribute(idMatch?.[2] || '');
210
+ const documentNode = new DOMParser().parseFromString(String(source ?? ''), 'application/xml');
211
+ if (documentNode.querySelector('parsererror')) {
212
+ return cells;
213
+ }
214
+
215
+ const serializer = new XMLSerializer();
216
+ for (const cell of documentNode.getElementsByTagName('mxCell')) {
217
+ const id = cell.getAttribute('id');
220
218
  if (!id || id === '0' || id === '1') {
221
219
  continue;
222
220
  }
223
221
 
224
- const signature = `${match[1]}${match[2] || ''}`.replace(/\s+/gu, ' ').trim();
225
- cells.set(id, signature);
222
+ cells.set(id, serializer.serializeToString(cell).replace(/\s+/gu, ' ').trim());
226
223
  }
227
224
 
228
225
  return cells;
@@ -647,7 +644,7 @@ export class GitDiffViewController {
647
644
  this.excalidrawPreviewPayloads.clear();
648
645
  this.drawioPreviewPayloads.clear();
649
646
  this.requestScope = scope;
650
- this.renderLoading('Loading diff summary...');
647
+ this.renderLoading('Loading diff summary');
651
648
 
652
649
  try {
653
650
  const data = await this.gitApiClient.readDiff({
@@ -677,7 +674,7 @@ export class GitDiffViewController {
677
674
  source: 'workspace',
678
675
  summary: { additions: 0, deletions: 0, filesChanged: 0 },
679
676
  };
680
- this.renderEmpty('Failed to load git diff');
677
+ this.renderEmpty('Failed to load git diff', { alert: true });
681
678
  return this.data;
682
679
  }
683
680
  }
@@ -700,7 +697,7 @@ export class GitDiffViewController {
700
697
  this.excalidrawPreviewPayloads.clear();
701
698
  this.drawioPreviewPayloads.clear();
702
699
  this.requestScope = 'all';
703
- this.renderLoading('Loading commit summary...');
700
+ this.renderLoading('Loading commit summary');
704
701
 
705
702
  try {
706
703
  const data = await this.gitApiClient.readCommit({
@@ -741,7 +738,7 @@ export class GitDiffViewController {
741
738
  summary: { additions: 0, deletions: 0, filesChanged: 0 },
742
739
  };
743
740
  this.commitMeta = null;
744
- this.renderEmpty('Failed to load git commit');
741
+ this.renderEmpty('Failed to load git commit', { alert: true });
745
742
  return this.data;
746
743
  }
747
744
  }
@@ -790,27 +787,27 @@ export class GitDiffViewController {
790
787
  void this.loadCurrentFile();
791
788
  }
792
789
 
793
- renderLoading(message = 'Loading git diff...') {
790
+ renderLoading(message = 'Loading git diff') {
794
791
  if (!this.isActive) {
795
792
  return;
796
793
  }
797
794
  this.page?.classList.remove('hidden');
798
795
  if (this.content) {
799
796
  // pi-lens-ignore: ast-grep:no-inner-html-js
800
- this.content.innerHTML = `<div class="diff-empty-state">${escapeHtml(message)}</div>`;
797
+ this.content.innerHTML = `<div class="diff-empty-state" role="status" aria-live="polite">${escapeHtml(message)}</div>`;
801
798
  }
802
799
  this.data = null;
803
800
  this.syncToolbar();
804
801
  }
805
802
 
806
- renderEmpty(message) {
803
+ renderEmpty(message, { alert = false } = {}) {
807
804
  if (!this.isActive) {
808
805
  return;
809
806
  }
810
807
  this.page?.classList.remove('hidden');
811
808
  if (this.content) {
812
809
  // pi-lens-ignore: ast-grep:no-inner-html-js
813
- this.content.innerHTML = `<div class="diff-empty-state">${escapeHtml(message)}</div>`;
810
+ this.content.innerHTML = `<div class="diff-empty-state"${alert ? ' role="alert"' : ''}>${escapeHtml(message)}</div>`;
814
811
  }
815
812
  this.syncToolbar();
816
813
  }
@@ -1194,16 +1191,7 @@ export class GitDiffViewController {
1194
1191
  }
1195
1192
 
1196
1193
  try {
1197
- const svg = await exportToSvg({
1198
- appState: {
1199
- exportBackground: true,
1200
- exportWithDarkMode: false,
1201
- viewBackgroundColor: scene.appState?.viewBackgroundColor || '#ffffff',
1202
- },
1203
- elements: getVisibleSceneElements(scene),
1204
- exportPadding: 24,
1205
- files: scene.files || null,
1206
- });
1194
+ const svg = await exportToSvg(createExcalidrawExportOptions(scene));
1207
1195
  preview.replaceChildren(svg);
1208
1196
  preview.classList.add('is-ready');
1209
1197
  } catch (error) {
@@ -1345,7 +1333,7 @@ export class GitDiffViewController {
1345
1333
  return `
1346
1334
  <section class="diff-file-block">
1347
1335
  ${this.renderFileHeader(currentFile)}
1348
- <div class="diff-empty-state">Loading file diff...</div>
1336
+ <div class="diff-empty-state" role="status" aria-live="polite">Loading file diff…</div>
1349
1337
  </section>
1350
1338
  `;
1351
1339
  }
@@ -1355,7 +1343,7 @@ export class GitDiffViewController {
1355
1343
  return `
1356
1344
  <section class="diff-file-block">
1357
1345
  ${this.renderFileHeader(currentFile)}
1358
- <div class="diff-empty-state">${escapeHtml(errorMessage)}</div>
1346
+ <div class="diff-empty-state" role="alert">${escapeHtml(errorMessage)}</div>
1359
1347
  </section>
1360
1348
  `;
1361
1349
  }
@@ -1435,9 +1423,9 @@ export class GitDiffViewController {
1435
1423
  const bodyMarkup = isCollapsed
1436
1424
  ? ''
1437
1425
  : isLoading
1438
- ? '<div class="diff-empty-state">Loading file diff...</div>'
1426
+ ? '<div class="diff-empty-state" role="status" aria-live="polite">Loading file diff…</div>'
1439
1427
  : errorMessage
1440
- ? `<div class="diff-empty-state">${escapeHtml(errorMessage)}</div>`
1428
+ ? `<div class="diff-empty-state" role="alert">${escapeHtml(errorMessage)}</div>`
1441
1429
  : this.renderDiffDetail(detail, index, { includeHeader: false });
1442
1430
 
1443
1431
  return `
@@ -1877,11 +1865,15 @@ export class GitDiffViewController {
1877
1865
  }
1878
1866
 
1879
1867
  this.modeButtons.forEach((button) => {
1868
+ const active = button.getAttribute('data-diff-mode') === this.mode;
1880
1869
  button.classList.remove('hidden');
1881
- button.classList.toggle('active', button.getAttribute('data-diff-mode') === this.mode);
1870
+ button.classList.toggle('active', active);
1871
+ button.setAttribute?.('aria-pressed', String(active));
1882
1872
  });
1883
1873
  this.layoutButtons.forEach((button) => {
1884
- button.classList.toggle('active', button.getAttribute('data-diff-layout') === this.layoutMode);
1874
+ const active = button.getAttribute('data-diff-layout') === this.layoutMode;
1875
+ button.classList.toggle('active', active);
1876
+ button.setAttribute?.('aria-pressed', String(active));
1885
1877
  });
1886
1878
 
1887
1879
  const hasCurrentFile = Boolean(this.getCurrentFile()?.path);
@@ -1906,8 +1898,8 @@ export class GitDiffViewController {
1906
1898
  this.primaryActionButton.classList.toggle('ui-button--primary', emphasizeInclude);
1907
1899
  this.primaryActionButton.classList.toggle('ui-button--secondary', !emphasizeInclude);
1908
1900
  this.primaryActionButton.classList.toggle('ui-button--surface', !emphasizeInclude);
1909
- this.primaryActionButton.textContent = this.pendingAction === primaryAction
1910
- ? 'Working...'
1901
+ this.primaryActionButton.textContent = this.pendingAction && this.pendingAction === primaryAction
1902
+ ? 'Working'
1911
1903
  : primaryAction === 'unstage'
1912
1904
  ? 'Remove'
1913
1905
  : 'Include in commit';
@@ -1918,7 +1910,7 @@ export class GitDiffViewController {
1918
1910
  }
1919
1911
  if (this.commitButton) {
1920
1912
  this.commitButton.textContent = this.pendingAction === 'commit'
1921
- ? 'Working...'
1913
+ ? 'Working'
1922
1914
  : commitButtonLabel(includedFileCount);
1923
1915
  }
1924
1916
  this.commitButton?.classList.toggle(
@@ -94,7 +94,9 @@ export class GitPanelController {
94
94
  onResetFile = () => {},
95
95
  onSelectCommit = () => {},
96
96
  onSelectDiff = () => {},
97
+ onStageAll = () => {},
97
98
  onStageFile = () => {},
99
+ onUnstageAll = () => {},
98
100
  onUnstageFile = () => {},
99
101
  onViewAllDiff = () => {},
100
102
  searchInput = null,
@@ -110,7 +112,9 @@ export class GitPanelController {
110
112
  this.onResetFile = onResetFile;
111
113
  this.onSelectCommit = onSelectCommit;
112
114
  this.onSelectDiff = onSelectDiff;
115
+ this.onStageAll = onStageAll;
113
116
  this.onStageFile = onStageFile;
117
+ this.onUnstageAll = onUnstageAll;
114
118
  this.onUnstageFile = onUnstageFile;
115
119
  this.onViewAllDiff = onViewAllDiff;
116
120
  this.searchInput = searchInput;
@@ -118,6 +122,7 @@ export class GitPanelController {
118
122
  this.panel = document.getElementById('gitPanel');
119
123
  this.pullBackups = [];
120
124
  this.status = null;
125
+ this.statusError = '';
121
126
  this.active = false;
122
127
  this.refreshTimer = null;
123
128
  this.searchQuery = '';
@@ -236,6 +241,17 @@ export class GitPanelController {
236
241
 
237
242
  if (this._handleFileActionClick(event)) return;
238
243
 
244
+ const bulkActionButton = event.target instanceof Element
245
+ ? event.target.closest('[data-git-bulk-action]')
246
+ : null;
247
+ if (bulkActionButton) {
248
+ const action = bulkActionButton.getAttribute('data-git-bulk-action');
249
+ if (action === 'stage-all' || action === 'unstage-all') {
250
+ void this.handleBulkAction(action);
251
+ }
252
+ return;
253
+ }
254
+
239
255
  if (event.target instanceof Element && event.target.closest('[data-git-view-all]')) {
240
256
  this.onViewAllDiff();
241
257
  return;
@@ -377,7 +393,7 @@ export class GitPanelController {
377
393
 
378
394
  async refresh({ force = false, includeHistory = this.panelMode === 'history' } = {}) {
379
395
  const status = await this.refreshStatus({ force });
380
- if (includeHistory && status.isGitRepo) {
396
+ if (includeHistory && status?.isGitRepo) {
381
397
  await this.refreshHistory({ force });
382
398
  }
383
399
  return status;
@@ -385,6 +401,7 @@ export class GitPanelController {
385
401
 
386
402
  async refreshStatus({ force = false } = {}) {
387
403
  if (!this.enabled) {
404
+ this.statusError = '';
388
405
  this.status = {
389
406
  isGitRepo: false,
390
407
  sections: [],
@@ -409,6 +426,7 @@ export class GitPanelController {
409
426
  try {
410
427
  const data = await this.gitApiClient.readStatus({ force });
411
428
  this.status = data;
429
+ this.statusError = '';
412
430
  this.pullBackups = [];
413
431
  if (data.isGitRepo) {
414
432
  try {
@@ -435,25 +453,9 @@ export class GitPanelController {
435
453
  } catch (error) {
436
454
  console.error('[git-panel] Failed to load git status:', error);
437
455
  this.toastController?.show('Failed to load git status');
438
- this.status = {
439
- isGitRepo: false,
440
- sections: [],
441
- summary: { changedFiles: 0 },
442
- };
443
- this.pullBackups = [];
444
- this.history = {
445
- ...this.history,
446
- commits: [],
447
- error: '',
448
- hasMore: false,
449
- loaded: false,
450
- loading: false,
451
- loadingMore: false,
452
- offset: 0,
453
- };
454
- this.onRepoChange(false, this.status);
456
+ this.statusError = 'Failed to load Git status. Try again.';
455
457
  this.render();
456
- return this.status;
458
+ return null;
457
459
  }
458
460
  }
459
461
 
@@ -615,6 +617,22 @@ export class GitPanelController {
615
617
  }
616
618
  }
617
619
 
620
+ async handleBulkAction(action) {
621
+ if (this.pendingActionKey) {
622
+ return;
623
+ }
624
+
625
+ this.pendingActionKey = action;
626
+ this.render();
627
+
628
+ try {
629
+ await (action === 'stage-all' ? this.onStageAll() : this.onUnstageAll());
630
+ } finally {
631
+ this.pendingActionKey = null;
632
+ this.render();
633
+ }
634
+ }
635
+
618
636
  async handleCommitStaged() {
619
637
  const actionKey = 'commit-staged';
620
638
  if (this.pendingActionKey === actionKey) {
@@ -835,11 +853,11 @@ export class GitPanelController {
835
853
  const commits = this.filterCommits(this.history.commits);
836
854
 
837
855
  if (this.history.loading && !this.history.loaded) {
838
- return '<div class="git-panel-empty">Loading git history...</div>';
856
+ return '<div class="git-panel-empty" role="status" aria-live="polite">Loading git history…</div>';
839
857
  }
840
858
 
841
859
  if (this.history.error && !this.history.loaded) {
842
- return `<div class="git-panel-empty">${escapeHtml(this.history.error)}</div>`;
860
+ return `<div class="git-panel-empty" role="alert">${escapeHtml(this.history.error)}</div>`;
843
861
  }
844
862
 
845
863
  if (this.history.loaded && commits.length === 0 && this.history.commits.length === 0) {
@@ -862,7 +880,7 @@ export class GitPanelController {
862
880
  data-git-history-load-more
863
881
  ${this.history.loadingMore ? 'disabled' : ''}
864
882
  >
865
- ${this.history.loadingMore ? 'Loading...' : 'Load More'}
883
+ ${this.history.loadingMore ? 'Loading' : 'Load more'}
866
884
  </button>
867
885
  </div>
868
886
  ` : ''}
@@ -877,6 +895,8 @@ export class GitPanelController {
877
895
  .join('');
878
896
  const hasChanges = Boolean(this.status.summary?.changedFiles);
879
897
  const includedFileCount = Number(this.status.summary?.staged || 0);
898
+ const excludedFileCount = Number(this.status.summary?.workingTree || 0)
899
+ + Number(this.status.summary?.untracked || 0);
880
900
  const isCommitPending = this.pendingActionKey === 'commit-staged';
881
901
 
882
902
  return `
@@ -885,9 +905,29 @@ export class GitPanelController {
885
905
  ${hasChanges ? `
886
906
  <div class="git-panel-footer">
887
907
  <div class="git-panel-footer-actions">
908
+ ${excludedFileCount > 0 ? `
909
+ <button
910
+ class="${buttonClassNames({ variant: 'secondary', size: 'compact', action: true, surface: true })}"
911
+ type="button"
912
+ data-git-bulk-action="stage-all"
913
+ ${this.pendingActionKey ? 'disabled' : ''}
914
+ >
915
+ ${this.pendingActionKey === 'stage-all' ? 'Working...' : 'Include all changes'}
916
+ </button>
917
+ ` : ''}
918
+ ${includedFileCount > 0 ? `
919
+ <button
920
+ class="${buttonClassNames({ variant: 'secondary', size: 'compact', action: true, surface: true })}"
921
+ type="button"
922
+ data-git-bulk-action="unstage-all"
923
+ ${this.pendingActionKey ? 'disabled' : ''}
924
+ >
925
+ ${this.pendingActionKey === 'unstage-all' ? 'Working...' : 'Remove all from commit'}
926
+ </button>
927
+ ` : ''}
888
928
  <button class="${buttonClassNames({ variant: 'secondary', size: 'compact', action: true, surface: true })}" type="button" data-git-view-all>
889
929
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M2 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
890
- View Full Diff
930
+ View full diff
891
931
  </button>
892
932
  <button
893
933
  class="${buttonClassNames({ variant: 'primary', action: true, wide: true, extra: 'git-footer-commit-btn' })}"
@@ -895,7 +935,7 @@ export class GitPanelController {
895
935
  data-git-commit-staged
896
936
  ${includedFileCount === 0 || isCommitPending ? 'disabled' : ''}
897
937
  >
898
- ${isCommitPending ? 'Working...' : commitButtonLabel(includedFileCount)}
938
+ ${isCommitPending ? 'Working' : commitButtonLabel(includedFileCount)}
899
939
  </button>
900
940
  </div>
901
941
  </div>
@@ -903,14 +943,19 @@ export class GitPanelController {
903
943
  `;
904
944
  }
905
945
 
906
- renderEmpty(message) {
946
+ renderEmpty(message, { alert = false, status = false } = {}) {
907
947
  if (!this.panel) {
908
948
  return;
909
949
  }
910
950
 
951
+ const semantics = alert
952
+ ? ' role="alert"'
953
+ : status
954
+ ? ' role="status" aria-live="polite"'
955
+ : '';
911
956
  // pi-lens-ignore: ast-grep:no-inner-html-js
912
957
  this.panel.innerHTML = `
913
- <div class="git-panel-empty ui-empty-state ui-empty-state--compact">
958
+ <div class="git-panel-empty ui-empty-state ui-empty-state--compact"${semantics}>
914
959
  <p class="ui-empty-state-copy">${escapeHtml(message)}</p>
915
960
  </div>
916
961
  `;
@@ -921,8 +966,13 @@ export class GitPanelController {
921
966
  return;
922
967
  }
923
968
 
969
+ if (this.statusError) {
970
+ this.renderEmpty(this.statusError, { alert: true });
971
+ return;
972
+ }
973
+
924
974
  if (!this.status) {
925
- this.renderEmpty('Loading git status...');
975
+ this.renderEmpty('Loading git status', { status: true });
926
976
  return;
927
977
  }
928
978
 
@@ -76,6 +76,8 @@ export class ImageLightboxController {
76
76
  this.pinchStartCenter = { x: 0, y: 0 };
77
77
  this.didDrag = false;
78
78
  this.resetButton = null;
79
+ this.previouslyFocusedElement = null;
80
+ this.blockingModalHandler = () => this.close();
79
81
 
80
82
  this.handlePreviewClick = (event) => {
81
83
  const target = event.target;
@@ -102,12 +104,6 @@ export class ImageLightboxController {
102
104
  return;
103
105
  }
104
106
 
105
- if (event.key === 'Escape') {
106
- event.preventDefault();
107
- this.close();
108
- return;
109
- }
110
-
111
107
  if (event.key === '+' || event.key === '=') {
112
108
  event.preventDefault();
113
109
  this.zoomBy(ZOOM_STEP);
@@ -161,15 +157,19 @@ export class ImageLightboxController {
161
157
  }
162
158
 
163
159
  this.ensureOverlayRoot();
160
+ this.previouslyFocusedElement = this.document.activeElement instanceof this.window.HTMLElement
161
+ ? this.document.activeElement
162
+ : null;
164
163
  this.resetView();
165
164
  this.titleElement.textContent = image.getAttribute('alt') || 'Image preview';
166
165
  this.imageElement.src = src;
167
166
  this.imageElement.alt = image.getAttribute('alt') || '';
168
- this.overlayRoot.hidden = false;
167
+ this.overlayRoot.showModal();
169
168
  this.isOpen = true;
170
169
  this.document.body.classList.add('image-lightbox-open');
170
+ this.document.addEventListener('collabmd:close-custom-modals', this.blockingModalHandler);
171
171
  this.syncTransform();
172
- this.overlayRoot.focus?.();
172
+ this.overlayRoot.querySelector('button:not(:disabled)')?.focus();
173
173
  return true;
174
174
  }
175
175
 
@@ -184,11 +184,14 @@ export class ImageLightboxController {
184
184
  this.pinchStartDistance = 0;
185
185
  this.didDrag = false;
186
186
  this.viewport?.classList?.remove('is-dragging');
187
- if (this.overlayRoot) {
188
- this.overlayRoot.hidden = true;
187
+ if (this.overlayRoot?.open) {
188
+ this.overlayRoot.close();
189
189
  }
190
190
  this.isOpen = false;
191
191
  this.document.body.classList.remove('image-lightbox-open');
192
+ this.document.removeEventListener('collabmd:close-custom-modals', this.blockingModalHandler);
193
+ this.previouslyFocusedElement?.focus?.();
194
+ this.previouslyFocusedElement = null;
192
195
  }
193
196
 
194
197
  resetView() {
@@ -442,11 +445,14 @@ export class ImageLightboxController {
442
445
  return this.overlayRoot;
443
446
  }
444
447
 
445
- const overlayRoot = this.document.createElement('div');
448
+ const overlayRoot = this.document.createElement('dialog');
446
449
  overlayRoot.className = 'image-lightbox-root';
447
450
  overlayRoot.dataset.imageLightboxRoot = 'true';
448
- overlayRoot.hidden = true;
449
- overlayRoot.tabIndex = -1;
451
+ overlayRoot.setAttribute('aria-labelledby', 'imageLightboxTitle');
452
+ overlayRoot.addEventListener('cancel', (event) => {
453
+ event.preventDefault();
454
+ this.close();
455
+ });
450
456
 
451
457
  const backdrop = this.document.createElement('button');
452
458
  backdrop.className = 'image-lightbox-backdrop';
@@ -460,9 +466,6 @@ export class ImageLightboxController {
460
466
 
461
467
  const shell = this.document.createElement('div');
462
468
  shell.className = 'image-lightbox-shell';
463
- shell.setAttribute('role', 'dialog');
464
- shell.setAttribute('aria-modal', 'true');
465
- shell.setAttribute('aria-label', 'Image preview');
466
469
  shell.addEventListener('click', (event) => event.stopPropagation());
467
470
  shell.addEventListener('pointerdown', (event) => event.stopPropagation());
468
471
 
@@ -471,6 +474,7 @@ export class ImageLightboxController {
471
474
 
472
475
  const title = this.document.createElement('div');
473
476
  title.className = 'image-lightbox-title';
477
+ title.id = 'imageLightboxTitle';
474
478
  title.textContent = 'Image preview';
475
479
 
476
480
  const controls = this.document.createElement('div');
@@ -15,7 +15,7 @@ export class LayoutController {
15
15
  this.previewPane = document.getElementById('previewPane');
16
16
  this.mobileToggleButton = document.getElementById('mobileViewToggle');
17
17
  this.resizer = document.getElementById('resizer');
18
- this.viewButtons = Array.from(document.querySelectorAll('.view-btn'));
18
+ this.viewButtons = Array.from(document.querySelectorAll('#toolbarViewToggle [data-view]'));
19
19
  }
20
20
 
21
21
  isMobileViewport() {
@@ -128,6 +128,8 @@ export class LayoutController {
128
128
  const previewIcon = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>';
129
129
  const editorIcon = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="8" y1="8" x2="16" y2="8"/><line x1="8" y1="12" x2="14" y2="12"/><line x1="8" y1="16" x2="12" y2="16"/></svg>';
130
130
 
131
+ // Icons and labels are fixed application-owned markup.
132
+ // pi-lens-ignore: no-inner-html-js
131
133
  this.mobileToggleButton.innerHTML = `${label === 'Editor' ? editorIcon : previewIcon}<span class="ui-toolbar-button-label">${label}</span>`;
132
134
  }
133
135
 
@@ -98,28 +98,20 @@ export class QuickSwitcherController {
98
98
  this.selectedIndex = 0;
99
99
  this.selectedTextIndex = 0;
100
100
  this.isOpen = false;
101
- this.previousActiveElement = null;
102
101
  this.mode = 'files';
103
102
  this.textResults = null;
104
103
  this.textResultItems = [];
105
104
  this.textSearchRunner = new QuickSwitcherTextSearchRunner({
106
105
  debounceMs: this.searchDebounceMs,
107
106
  });
107
+ this.blockingModalHandler = () => this.close();
108
108
 
109
109
  this.bindEvents();
110
110
  }
111
111
 
112
112
  bindEvents() {
113
- this.overlay?.addEventListener('mousedown', (e) => {
114
- if (e.target === this.overlay) this.close();
115
- });
116
-
117
- this.overlay?.addEventListener('keydown', (e) => {
118
- if (e.key === 'Escape') {
119
- e.preventDefault();
120
- this.close();
121
- }
122
- });
113
+ document.addEventListener?.('collabmd:close-custom-modals', this.blockingModalHandler);
114
+ this.overlay?.addEventListener('close', () => this.handleClose());
123
115
 
124
116
  this.modeTabs.forEach((tab, index) => {
125
117
  tab.addEventListener('click', () => {
@@ -173,86 +165,36 @@ export class QuickSwitcherController {
173
165
  });
174
166
  }
175
167
 
168
+ handleClose() {
169
+ this.abortTextSearch();
170
+ this.isOpen = false;
171
+ this.input?.setAttribute('aria-expanded', 'false');
172
+ this.input.value = '';
173
+ this.resultsList.innerHTML = '';
174
+ this.setActiveDescendant('');
175
+ }
176
+
176
177
  open() {
177
- if (!this.overlay) return;
178
+ if (!this.overlay || this.overlay.open) return;
178
179
 
179
- this.previousActiveElement = document.activeElement;
180
180
  this.isOpen = true;
181
181
  this.input.value = '';
182
182
  this.selectedIndex = 0;
183
183
  this.selectedTextIndex = 0;
184
- this.overlay.classList.add('visible');
185
- this.overlay.setAttribute('aria-hidden', 'false');
186
- this.input?.setAttribute('aria-expanded', 'true');
187
184
  this.setMode('files', { preserveInput: true });
188
-
189
- // The overlay transitions visibility hidden→visible over 120ms.
190
- // Browsers ignore .focus() while the element is still visibility:hidden,
191
- // so we must wait for the transition to complete.
192
- this._focusAfterTransition();
193
- }
194
-
195
- /** Focus the input once the overlay visibility transition finishes. */
196
- _focusAfterTransition() {
197
- this._cancelPendingFocus();
198
-
199
- const tryFocus = () => {
200
- this._focusCleanup = null;
201
- this.input?.focus();
202
- if (this.isOpen && this.input && document.activeElement !== this.input) {
203
- setTimeout(() => this.input?.focus(), 50);
204
- }
205
- };
206
-
207
- const onEnd = (e) => {
208
- if (e.propertyName === 'visibility' || e.propertyName === 'opacity') {
209
- this.overlay.removeEventListener('transitionend', onEnd);
210
- clearTimeout(fallbackTimer);
211
- tryFocus();
212
- }
213
- };
214
- this.overlay.addEventListener('transitionend', onEnd);
215
-
216
- const fallbackTimer = setTimeout(() => {
217
- this.overlay.removeEventListener('transitionend', onEnd);
218
- tryFocus();
219
- }, 160);
220
-
221
- this._focusCleanup = () => {
222
- this.overlay.removeEventListener('transitionend', onEnd);
223
- clearTimeout(fallbackTimer);
224
- };
225
- }
226
-
227
- _cancelPendingFocus() {
228
- if (this._focusCleanup) {
229
- this._focusCleanup();
230
- this._focusCleanup = null;
231
- }
185
+ this.overlay.showModal();
186
+ this.input?.setAttribute('aria-expanded', 'true');
232
187
  }
233
188
 
234
- close({ restoreFocus = true } = {}) {
235
- if (!this.overlay) return;
236
-
237
- const previousActiveElement = this.previousActiveElement;
238
- this._cancelPendingFocus();
239
- this.abortTextSearch();
240
- this.isOpen = false;
241
- this.overlay.classList.remove('visible');
242
- this.overlay.setAttribute('aria-hidden', 'true');
243
- this.input?.setAttribute('aria-expanded', 'false');
244
- this.input.value = '';
245
- this.resultsList.innerHTML = '';
246
- this.setActiveDescendant('');
247
- this.previousActiveElement = null;
248
-
249
- if (restoreFocus) {
250
- previousActiveElement?.focus?.();
189
+ close() {
190
+ this.handleClose();
191
+ if (this.overlay?.open) {
192
+ this.overlay.close();
251
193
  }
252
194
  }
253
195
 
254
196
  toggle() {
255
- if (this.isOpen) {
197
+ if (this.overlay?.open) {
256
198
  this.close();
257
199
  } else {
258
200
  this.open();