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
@@ -11,6 +11,7 @@ import {
11
11
  import { getElementBounds } from '@excalidraw/element';
12
12
  import '@excalidraw/excalidraw/index.css';
13
13
 
14
+ import { createEditableContentRevision } from '../domain/editable-content-revision.js';
14
15
  import {
15
16
  buildRenderableCollaboratorsMap,
16
17
  findCollaboratorByPeerId,
@@ -45,6 +46,11 @@ import {
45
46
  summarizeExcalidrawScene,
46
47
  } from './domain/excalidraw-diagnostics.js';
47
48
  import { isPlainQuickSwitcherShortcut } from './domain/keyboard-shortcuts.js';
49
+ import {
50
+ createExcalidrawElementLink,
51
+ parseExcalidrawElementLink,
52
+ } from './domain/excalidraw-element-link.js';
53
+ import { resolveAppPath, resolveAppUrl } from './domain/runtime-paths.js';
48
54
  import { ensureClientAuthenticated } from './infrastructure/auth-client.js';
49
55
  import {
50
56
  EXCALIDRAW_ROOM_CONNECTION_STATE,
@@ -58,6 +64,8 @@ const diagnostics = createExcalidrawDiagnosticRing({
58
64
  enabled: params.get('excalidrawDebug') === '1',
59
65
  });
60
66
  const parentOrigin = window.location.origin;
67
+ const appPath = resolveAppPath('/');
68
+ const appUrl = resolveAppUrl('/');
61
69
  const syncTimeoutMs = Number.parseInt(params.get('syncTimeoutMs') || '', 10);
62
70
 
63
71
  let currentDocument = {
@@ -71,6 +79,12 @@ let localAwarenessUser = resolveLocalAwarenessUser({
71
79
  storedUserName: localStorage.getItem('collabmd-user-name'),
72
80
  });
73
81
  let appliedSceneJson = '';
82
+ let appliedSceneRevisionPromise = createEditableContentRevision(appliedSceneJson);
83
+
84
+ function setAppliedSceneJson(value) {
85
+ appliedSceneJson = value;
86
+ appliedSceneRevisionPromise = createEditableContentRevision(value);
87
+ }
74
88
 
75
89
  let collabReady = false;
76
90
  let pendingRemoteSceneJson = '';
@@ -327,17 +341,34 @@ function getDiagramCommentMarkerPosition(thread, api = getMountedExcalidrawAPI()
327
341
  );
328
342
  }
329
343
 
330
- function focusDiagramCommentThread(thread) {
344
+ function getDiagramElementTargets(elementId, elementType = 'element', api = getMountedExcalidrawAPI()) {
345
+ const normalizedElementId = String(elementId ?? '').trim();
346
+ const elements = api?.getSceneElementsIncludingDeleted?.()
347
+ ?.filter((element) => !element.isDeleted) || [];
348
+ if (!normalizedElementId) {
349
+ return [];
350
+ }
351
+
352
+ if (elementType === 'group') {
353
+ const groupElements = elements.filter((element) => element.groupIds?.includes(normalizedElementId));
354
+ if (groupElements.length > 0) {
355
+ return groupElements;
356
+ }
357
+ }
358
+
359
+ return elements.filter((element) => element.id === normalizedElementId);
360
+ }
361
+
362
+ function focusDiagramElement(elementId, elementType = 'element') {
331
363
  const api = getMountedExcalidrawAPI();
332
- const element = api?.getSceneElementsIncludingDeleted?.()
333
- ?.find((candidate) => candidate.id === thread?.elementId && !candidate.isDeleted);
334
- if (!api || !element) {
364
+ const elements = getDiagramElementTargets(elementId, elementType, api);
365
+ if (!api || elements.length === 0) {
335
366
  return;
336
367
  }
337
368
 
338
369
  api.updateScene({
339
370
  appState: {
340
- selectedElementIds: { [element.id]: true },
371
+ selectedElementIds: Object.fromEntries(elements.map((element) => [element.id, true])),
341
372
  },
342
373
  captureUpdate: CaptureUpdateAction.NEVER,
343
374
  });
@@ -352,11 +383,16 @@ function focusDiagramCommentThread(thread) {
352
383
  animation: false,
353
384
  fit: 'contain',
354
385
  offsets: { ui: true },
355
- target: element,
386
+ target: elements,
356
387
  });
357
388
  } finally {
358
389
  releaseViewportBroadcastSuppressionAfterPaint();
359
390
  }
391
+
392
+ }
393
+
394
+ function focusDiagramCommentThread(thread) {
395
+ focusDiagramElement(thread?.elementId);
360
396
  }
361
397
 
362
398
  function applyDocumentMode(mode = currentDocument.mode) {
@@ -413,6 +449,23 @@ function createRoomClient(filePath) {
413
449
  };
414
450
  }
415
451
 
452
+ function handleExcalidrawLinkOpen(element, event) {
453
+ if (window.parent === window) {
454
+ return;
455
+ }
456
+
457
+ const target = parseExcalidrawElementLink(element?.link, {
458
+ appPath,
459
+ origin: parentOrigin,
460
+ });
461
+ if (!target) {
462
+ return;
463
+ }
464
+
465
+ event?.preventDefault?.();
466
+ postToParent('open-element-link', { href: element.link });
467
+ }
468
+
416
469
  function buildExcalidrawProps({ initialData, renderTopRightUI, viewModeEnabled } = {}) {
417
470
  const props = {
418
471
  onMount: handleEditorMount,
@@ -431,11 +484,17 @@ function buildExcalidrawProps({ initialData, renderTopRightUI, viewModeEnabled }
431
484
  collabReady = false;
432
485
  },
433
486
  aiEnabled: false,
487
+ generateLinkForSelection: (elementId, elementType) => createExcalidrawElementLink(
488
+ currentDocument.filePath,
489
+ elementId,
490
+ { appUrl, elementType },
491
+ ),
434
492
  isCollaborating: true,
435
493
  onChange: (elements, appState, files) => {
436
494
  scheduleSyncToRoom(elements, appState, files);
437
495
  roomClient?.syncLocalSelectionAwareness(appState);
438
496
  },
497
+ onLinkOpen: handleExcalidrawLinkOpen,
439
498
  onPointerUpdate: (payload) => {
440
499
  roomClient?.scheduleLocalPointerAwareness(payload);
441
500
  },
@@ -1199,6 +1258,9 @@ if (isTestMode) {
1199
1258
  .map((element) => element.id)
1200
1259
  .sort() ?? []
1201
1260
  ),
1261
+ getSelectedElementIds: () => (
1262
+ Object.keys(getMountedExcalidrawAPI()?.getAppState?.()?.selectedElementIds || {}).sort()
1263
+ ),
1202
1264
  getElementStatus: (elementId) => (
1203
1265
  getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.()
1204
1266
  ?.find((entry) => entry.id === elementId && !entry.isDeleted)?.status ?? null
@@ -1347,7 +1409,7 @@ function applySceneFromJson(rawJson, {
1347
1409
  return;
1348
1410
  }
1349
1411
 
1350
- appliedSceneJson = normalizedJson;
1412
+ setAppliedSceneJson(normalizedJson);
1351
1413
  recordSceneDiagnostic('remote-scene-received', {}, normalizedJson);
1352
1414
 
1353
1415
  if (!getMountedExcalidrawAPI() || !collabReady) {
@@ -1423,7 +1485,7 @@ function requestEditorRemount(scene) {
1423
1485
  const normalizedScene = normalizeScene(scene);
1424
1486
  const normalizedJson = JSON.stringify(normalizedScene);
1425
1487
  pendingRemoteSceneJson = normalizedJson;
1426
- appliedSceneJson = normalizedJson;
1488
+ setAppliedSceneJson(normalizedJson);
1427
1489
  pendingCollaborators = activeCollaborators;
1428
1490
  initialViewportFitPending = true;
1429
1491
  clearPreviewViewportFitTimers();
@@ -1499,7 +1561,7 @@ function applyLocalScene(scene, {
1499
1561
  const normalizedScene = normalizeScene(scene);
1500
1562
  const normalizedJson = JSON.stringify(normalizedScene);
1501
1563
 
1502
- appliedSceneJson = normalizedJson;
1564
+ setAppliedSceneJson(normalizedJson);
1503
1565
 
1504
1566
  if (!getMountedExcalidrawAPI() || !collabReady) {
1505
1567
  pendingRemoteSceneJson = normalizedJson;
@@ -1845,6 +1907,36 @@ async function waitForPendingRoomWrites({
1845
1907
  }
1846
1908
  }
1847
1909
 
1910
+ function waitForAnimationFrame() {
1911
+ return new Promise((resolve) => requestAnimationFrame(resolve));
1912
+ }
1913
+
1914
+ async function waitForAppliedSceneRevision(revision, {
1915
+ afterRevision = '',
1916
+ maxWaitMs = 4500,
1917
+ } = {}) {
1918
+ const startedAt = performance.now();
1919
+ while ((performance.now() - startedAt) < maxWaitMs) {
1920
+ const appliedRevision = await appliedSceneRevisionPromise;
1921
+ if (
1922
+ !pendingRemoteSceneJson
1923
+ && (appliedRevision === revision || (afterRevision && appliedRevision !== afterRevision))
1924
+ ) {
1925
+ await waitForAnimationFrame();
1926
+ await waitForAnimationFrame();
1927
+ const paintedRevision = await appliedSceneRevisionPromise;
1928
+ if (
1929
+ !pendingRemoteSceneJson
1930
+ && (paintedRevision === revision || (afterRevision && paintedRevision !== afterRevision))
1931
+ ) {
1932
+ return paintedRevision;
1933
+ }
1934
+ }
1935
+ await new Promise((resolve) => window.setTimeout(resolve, 16));
1936
+ }
1937
+ return '';
1938
+ }
1939
+
1848
1940
  async function prepareRealtimeRoomDisconnect() {
1849
1941
  if (!roomClient) {
1850
1942
  return true;
@@ -1942,6 +2034,11 @@ window.addEventListener('message', (event) => {
1942
2034
  return;
1943
2035
  }
1944
2036
 
2037
+ if (message.type === 'focus-element') {
2038
+ focusDiagramElement(message.elementId, message.elementType);
2039
+ return;
2040
+ }
2041
+
1945
2042
  if (message.type === 'follow-user') {
1946
2043
  applyHostFollowRequest(message.peerId || null);
1947
2044
  return;
@@ -1952,6 +2049,34 @@ window.addEventListener('message', (event) => {
1952
2049
  return;
1953
2050
  }
1954
2051
 
2052
+ if (message.type === 'flush-agent-writes') {
2053
+ void (async () => {
2054
+ roomClient?.flushSceneSync();
2055
+ await waitForPendingRoomWrites({ maxWaitMs: 1000 });
2056
+ const serverFlush = await roomClient?.waitForServerFlush?.({ timeoutMs: 1000 });
2057
+ postToParent('agent-writes-flushed', {
2058
+ requestId: message.requestId || '',
2059
+ revision: await appliedSceneRevisionPromise,
2060
+ status: serverFlush?.status || 'unavailable',
2061
+ });
2062
+ })();
2063
+ return;
2064
+ }
2065
+
2066
+ if (message.type === 'wait-for-agent-revision') {
2067
+ void (async () => {
2068
+ const revision = String(message.revision || '');
2069
+ const paintedRevision = revision && await waitForAppliedSceneRevision(revision, {
2070
+ afterRevision: String(message.afterRevision || ''),
2071
+ });
2072
+ postToParent(paintedRevision ? 'agent-revision-painted' : 'agent-revision-not-painted', {
2073
+ requestId: message.requestId || '',
2074
+ revision: paintedRevision || revision,
2075
+ });
2076
+ })();
2077
+ return;
2078
+ }
2079
+
1955
2080
  if (message.type === 'prepare-disconnect') {
1956
2081
  void (async () => {
1957
2082
  const requestId = message.requestId || '';
@@ -2053,7 +2178,7 @@ function initializeEditor(api) {
2053
2178
  const sceneJson = pendingRemoteSceneJson || roomClient?.getLastSceneJson?.() || '';
2054
2179
  const initialScene = parseSceneJson(sceneJson);
2055
2180
  pendingRemoteSceneJson = '';
2056
- appliedSceneJson = JSON.stringify(initialScene);
2181
+ setAppliedSceneJson(JSON.stringify(initialScene));
2057
2182
  updateApiScene(initialScene);
2058
2183
 
2059
2184
  if (pendingCollaborators) {
@@ -8,7 +8,9 @@ const pendingJobs = new Map();
8
8
  let exportBridgeErrorHandler = null;
9
9
 
10
10
  function createJobId() {
11
- return `export-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
11
+ const id = globalThis.crypto?.randomUUID?.()
12
+ ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
13
+ return `export-${id}`;
12
14
  }
13
15
 
14
16
  function normalizeFormat(format) {
@@ -17,10 +17,11 @@ import { compilePreviewDocument } from '../application/preview-render-compiler.j
17
17
  import { isMarkdownFilePath, stripVaultFileExtension } from '../../domain/file-kind.js';
18
18
  import { resolveVaultRelativePath } from '../../domain/vault-paths.js';
19
19
  import { resolveWikiTargetPath } from '../../domain/wiki-link-resolver.js';
20
- import { parseSceneJson, sceneToInitialData } from '../domain/excalidraw-scene.js';
20
+ import { createExcalidrawExportOptions, parseSceneJson } from '../domain/excalidraw-scene.js';
21
21
  import { escapeHtml } from '../domain/vault-utils.js';
22
22
  import { downloadBlob } from '../browser-utils.js';
23
23
  import { resolveApiUrl, resolveAppUrl } from '../infrastructure/runtime-config.js';
24
+ import { parseApiResponse } from '../infrastructure/api-client-utils.js';
24
25
 
25
26
  const EXPORT_PAGE_SOURCE = 'collabmd-export-page';
26
27
  const EXPORT_HOST_SOURCE = 'collabmd-export-host';
@@ -355,11 +356,7 @@ async function fetchJson(url, init = {}) {
355
356
  },
356
357
  ...init,
357
358
  });
358
- const data = await response.json().catch(() => null);
359
- if (!response.ok) {
360
- throw new Error(data?.error || `Request failed: ${response.status}`);
361
- }
362
- return data;
359
+ return parseApiResponse(response, `Request failed: ${response.status}`);
363
360
  }
364
361
 
365
362
  async function fetchTextFile(filePath) {
@@ -820,17 +817,7 @@ async function renderPlantUmlToSvgMarkup(source) {
820
817
  async function renderExcalidrawToSvgMarkup(filePath) {
821
818
  const rawScene = await fetchTextFile(filePath);
822
819
  const scene = parseSceneJson(rawScene);
823
- const initialData = sceneToInitialData(scene, { theme: 'light' });
824
- const svgElement = await exportExcalidrawToSvg({
825
- appState: {
826
- ...initialData.appState,
827
- exportBackground: true,
828
- exportWithDarkMode: false,
829
- },
830
- elements: initialData.elements.filter((element) => !element.isDeleted),
831
- exportPadding: 24,
832
- files: initialData.files || null,
833
- });
820
+ const svgElement = await exportExcalidrawToSvg(createExcalidrawExportOptions(scene));
834
821
  return normalizeExportSvgMarkup(svgElement.outerHTML, { padding: 24 });
835
822
  }
836
823
 
@@ -0,0 +1,23 @@
1
+ import { resolveApiUrl } from '../domain/runtime-paths.js';
2
+ import { parseApiResponse } from './api-client-utils.js';
3
+
4
+ export const agentConnectionApiClient = {
5
+ listConnections() {
6
+ return fetch(resolveApiUrl('/agent/connections'))
7
+ .then((response) => parseApiResponse(response, 'Failed to load Agent Connections'));
8
+ },
9
+
10
+ createConnection(payload) {
11
+ return fetch(resolveApiUrl('/agent/connections'), {
12
+ body: JSON.stringify(payload),
13
+ headers: { 'Content-Type': 'application/json' },
14
+ method: 'POST',
15
+ }).then((response) => parseApiResponse(response, 'Failed to create Agent Connection'));
16
+ },
17
+
18
+ revokeConnection(connectionId) {
19
+ return fetch(resolveApiUrl(`/agent/connections/${encodeURIComponent(connectionId)}`), {
20
+ method: 'DELETE',
21
+ }).then((response) => parseApiResponse(response, 'Failed to revoke Agent Connection'));
22
+ },
23
+ };
@@ -1,6 +1,7 @@
1
1
  import '../styles/features/auth-gate.css';
2
2
 
3
3
  import { getHashParamsFromRaw } from '../domain/hash-routes.js';
4
+ import { parseApiResponse } from './api-client-utils.js';
4
5
  import { getRuntimeConfig } from './runtime-config.js';
5
6
 
6
7
  const DEFAULT_AUTH_CONFIG = {
@@ -66,12 +67,7 @@ async function fetchAuthStatus(config) {
66
67
  },
67
68
  });
68
69
 
69
- const payload = await response.json().catch(() => ({}));
70
- if (!response.ok) {
71
- throw new Error(payload.error || 'Failed to read auth status');
72
- }
73
-
74
- return payload;
70
+ return parseApiResponse(response, 'Failed to read auth status');
75
71
  }
76
72
 
77
73
  function syncOidcIdentityToLocalState(status) {
@@ -100,13 +96,7 @@ async function submitPassword(config, password) {
100
96
  },
101
97
  body: JSON.stringify({ password }),
102
98
  });
103
- const payload = await response.json().catch(() => ({}));
104
-
105
- if (!response.ok) {
106
- throw new Error(payload.error || 'Authentication failed');
107
- }
108
-
109
- return payload;
99
+ return parseApiResponse(response, 'Authentication failed');
110
100
  }
111
101
 
112
102
  function createOverlayShell() {
@@ -290,6 +290,13 @@ export class EditorSession {
290
290
  return this.viewAdapter.insertText(text);
291
291
  }
292
292
 
293
+ applyTextReplacements(replacements) {
294
+ if (!this.collaborationClient.initialSyncComplete) {
295
+ throw new Error('The collaborative document is not synchronized');
296
+ }
297
+ return this.viewAdapter.applyTextReplacements(replacements);
298
+ }
299
+
293
300
  replaceText(text) {
294
301
  return this.viewAdapter.replaceText(text);
295
302
  }
@@ -306,6 +313,10 @@ export class EditorSession {
306
313
  return this.viewAdapter.flashRemoteRange(range);
307
314
  }
308
315
 
316
+ isInitialSyncComplete() {
317
+ return this.collaborationClient.initialSyncComplete;
318
+ }
319
+
309
320
  waitForInitialSync(timeoutMs = 1500) {
310
321
  return this.collaborationClient.waitForInitialSync(timeoutMs);
311
322
  }
@@ -50,6 +50,7 @@ import {
50
50
  isPlantUmlFilePath,
51
51
  isStructurizrFilePath,
52
52
  } from '../../domain/file-kind.js';
53
+ import { resolveExactTextChanges } from '../../domain/exact-text-edits.js';
53
54
  import { normalizeCommentQuote } from '../../domain/comment-threads.js';
54
55
  import { formatDocumentText } from '../domain/document-formatter.js';
55
56
  import { createMarkdownToolbarEdit } from '../domain/markdown-formatting.js';
@@ -1143,6 +1144,18 @@ export class EditorViewAdapter {
1143
1144
  return true;
1144
1145
  }
1145
1146
 
1147
+ applyTextReplacements(replacements) {
1148
+ if (!this.editorView) {
1149
+ throw new Error('The editor is not available');
1150
+ }
1151
+
1152
+ const { state } = this.editorView;
1153
+ const changes = resolveExactTextChanges(state.doc.toString(), replacements);
1154
+
1155
+ this.editorView.dispatch({ changes, userEvent: 'input' });
1156
+ return changes.length;
1157
+ }
1158
+
1146
1159
  replaceText(text) {
1147
1160
  if (!this.editorView) {
1148
1161
  return false;
@@ -1,5 +1,7 @@
1
1
  import { Doc } from 'yjs';
2
2
  import { WebsocketProvider } from 'y-websocket';
3
+ import * as decoding from 'lib0/decoding';
4
+ import * as encoding from 'lib0/encoding';
3
5
  import {
4
6
  EXCALIDRAW_APP_STATE_KEY,
5
7
  EXCALIDRAW_ELEMENTS_KEY,
@@ -12,6 +14,10 @@ import {
12
14
  readLegacyExcalidrawRoomScene,
13
15
  replaceExcalidrawRoomScene,
14
16
  } from '../../domain/excalidraw-room-codec.js';
17
+ import {
18
+ MSG_AGENT_FLUSH,
19
+ MSG_AGENT_FLUSH_ACK,
20
+ } from '../../domain/collaboration-protocol.js';
15
21
 
16
22
  import {
17
23
  buildCollaboratorsMap,
@@ -33,6 +39,7 @@ import { stopReconnectOnControlledClose } from './yjs-provider-reset-guard.js';
33
39
  const DEFAULT_EMPTY_SCENE_GUARD_MS = 250;
34
40
  const DEFAULT_SAVE_THROTTLE_MS = 48;
35
41
  const DEFAULT_SYNC_TIMEOUT_MS = 4000;
42
+ const DEFAULT_AGENT_FLUSH_TIMEOUT_MS = 1000;
36
43
 
37
44
  export const EXCALIDRAW_ROOM_CONNECTION_STATE = Object.freeze({
38
45
  AUTHORITATIVE: 'authoritative',
@@ -178,6 +185,8 @@ export class ExcalidrawRoomClient {
178
185
  this.lastViewportSignature = '';
179
186
  this.lastSelectedIdsSignature = '';
180
187
  this.pendingEmptySceneCandidate = null;
188
+ this.agentFlushRequestCounter = 0;
189
+ this.pendingAgentFlushRequests = new Map();
181
190
  this.structuredObserversAttached = false;
182
191
  this.canWriteToRoom = false;
183
192
  this.waitingForAuthoritativeSync = false;
@@ -250,6 +259,74 @@ export class ExcalidrawRoomClient {
250
259
  return this.connectionState;
251
260
  }
252
261
 
262
+ installAgentFlushHandler() {
263
+ const messageHandlers = this.provider?.messageHandlers;
264
+ if (!messageHandlers || typeof messageHandlers !== 'object') {
265
+ return false;
266
+ }
267
+
268
+ messageHandlers[MSG_AGENT_FLUSH_ACK] = (_encoder, decoder) => {
269
+ let requestId;
270
+ try {
271
+ requestId = decoding.readVarString(decoder);
272
+ } catch {
273
+ return;
274
+ }
275
+
276
+ const request = this.pendingAgentFlushRequests.get(requestId);
277
+ if (!request) {
278
+ return;
279
+ }
280
+
281
+ request.resolve({ status: 'acknowledged' });
282
+ };
283
+ return true;
284
+ }
285
+
286
+ settleAgentFlushRequests(status) {
287
+ const requests = [...this.pendingAgentFlushRequests.values()];
288
+ requests.forEach((request) => request.resolve({ status }));
289
+ }
290
+
291
+ waitForServerFlush({ timeoutMs = DEFAULT_AGENT_FLUSH_TIMEOUT_MS } = {}) {
292
+ const ws = this.provider?.ws;
293
+ const openState = ws?.OPEN ?? globalThis.WebSocket?.OPEN ?? 1;
294
+ if (
295
+ !this.canWriteToRoom
296
+ || !ws
297
+ || ws.readyState !== openState
298
+ || !this.provider?.messageHandlers?.[MSG_AGENT_FLUSH_ACK]
299
+ ) {
300
+ return Promise.resolve({ status: 'unavailable' });
301
+ }
302
+
303
+ const requestId = `agent-flush-${this.ydoc?.clientID ?? 'client'}-${++this.agentFlushRequestCounter}`;
304
+ return new Promise((resolve) => {
305
+ const finish = (result) => {
306
+ const request = this.pendingAgentFlushRequests.get(requestId);
307
+ if (!request) {
308
+ return;
309
+ }
310
+ this.clearTimeoutFn(request.timeoutId);
311
+ this.pendingAgentFlushRequests.delete(requestId);
312
+ resolve(result);
313
+ };
314
+ this.pendingAgentFlushRequests.set(requestId, {
315
+ resolve: finish,
316
+ timeoutId: this.setTimeoutFn(() => finish({ status: 'timeout' }), timeoutMs),
317
+ });
318
+
319
+ try {
320
+ const encoder = encoding.createEncoder();
321
+ encoding.writeVarUint(encoder, MSG_AGENT_FLUSH);
322
+ encoding.writeVarString(encoder, requestId);
323
+ ws.send(encoding.toUint8Array(encoder));
324
+ } catch {
325
+ finish({ status: 'unavailable' });
326
+ }
327
+ });
328
+ }
329
+
253
330
  hasPendingWrites() {
254
331
  return Boolean(this.pendingSceneSyncPayload);
255
332
  }
@@ -368,6 +445,7 @@ export class ExcalidrawRoomClient {
368
445
  disableBc: true,
369
446
  maxBackoffTime: 5000,
370
447
  });
448
+ this.installAgentFlushHandler();
371
449
  stopReconnectOnControlledClose(this.provider);
372
450
 
373
451
  this.awareness = this.provider.awareness;
@@ -636,7 +714,7 @@ export class ExcalidrawRoomClient {
636
714
  }
637
715
 
638
716
  scheduleSceneSync(elements, appState, files) {
639
- if (!this.canWriteToRoom) {
717
+ if (!this.canWriteToRoom || this.isApplyingSharedSnapshot()) {
640
718
  return false;
641
719
  }
642
720
 
@@ -933,6 +1011,7 @@ export class ExcalidrawRoomClient {
933
1011
 
934
1012
  disconnect() {
935
1013
  this.flushSceneSync();
1014
+ this.settleAgentFlushRequests('disconnected');
936
1015
  this.setConnectionState(EXCALIDRAW_ROOM_CONNECTION_STATE.DISCONNECTING);
937
1016
  this.clearTimeoutFn(this.sceneSyncTimer);
938
1017
  this.sceneSyncTimer = null;