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
@@ -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
  },
@@ -469,6 +528,32 @@ function buildExcalidrawProps({ initialData, renderTopRightUI, viewModeEnabled }
469
528
  }
470
529
 
471
530
  const diagramCommentsContext = React.createContext(null);
531
+ const presentationContext = React.createContext(null);
532
+
533
+ function PresentationToolbar() {
534
+ const context = React.useContext(presentationContext);
535
+ if (!context) {
536
+ return null;
537
+ }
538
+
539
+ const label = context.active ? 'Exit presentation' : 'Start presentation';
540
+ const icon = context.active ? '×' : '▶';
541
+ let title = context.active ? 'Exit presentation' : 'Present frames';
542
+ if (context.frameCount === 0) {
543
+ title = 'Add a frame to create slides';
544
+ }
545
+
546
+ return React.createElement('button', {
547
+ 'aria-label': label,
548
+ 'aria-pressed': context.active,
549
+ className: `diagram-comment-button excalidraw-presentation-toggle sidebar-trigger${context.active ? ' is-active' : ''}`,
550
+ 'data-testid': 'excalidraw-presentation-toggle',
551
+ disabled: context.frameCount === 0,
552
+ onClick: context.toggle,
553
+ title,
554
+ type: 'button',
555
+ }, React.createElement('span', { 'aria-hidden': 'true' }, icon));
556
+ }
472
557
 
473
558
  function DiagramCommentsToolbar() {
474
559
  const context = React.useContext(diagramCommentsContext);
@@ -518,7 +603,12 @@ function DiagramCommentsToolbar() {
518
603
 
519
604
  const DiagramCommentsExcalidraw = React.memo(function DiagramCommentsExcalidraw({ initialData = null, renderKey = 0, viewModeEnabled }) {
520
605
  const renderTopRightUI = React.useCallback(
521
- () => React.createElement(DiagramCommentsToolbar),
606
+ () => React.createElement('div', {
607
+ className: 'excalidraw-editor-toolbar',
608
+ }, [
609
+ React.createElement(PresentationToolbar, { key: 'presentation' }),
610
+ React.createElement(DiagramCommentsToolbar, { key: 'comments' }),
611
+ ]),
522
612
  [],
523
613
  );
524
614
 
@@ -566,7 +656,13 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
566
656
  const [drawerOpen, setDrawerOpen] = React.useState(false);
567
657
  const [draft, setDraft] = React.useState('');
568
658
  const [replyDraft, setReplyDraft] = React.useState('');
659
+ const [presentationFrameId, setPresentationFrameId] = React.useState(null);
569
660
  const [, setViewportRevision] = React.useState(0);
661
+ const presentationFrames = (
662
+ getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.() || []
663
+ ).filter((element) => element?.type === 'frame' && !element.isDeleted);
664
+ const presentationIndex = presentationFrames.findIndex((frame) => frame.id === presentationFrameId);
665
+ const presentationMode = presentationFrameId !== null;
570
666
 
571
667
  React.useEffect(() => {
572
668
  const api = getMountedExcalidrawAPI();
@@ -588,6 +684,76 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
588
684
 
589
685
  useCloseDiagramCommentsOnCanvas(drawerOpen, setDrawerOpen);
590
686
 
687
+ const goToPresentationFrame = React.useCallback((index) => {
688
+ const targetIndex = Math.min(Math.max(index, 0), presentationFrames.length - 1);
689
+ const frame = presentationFrames[targetIndex];
690
+ const api = getMountedExcalidrawAPI();
691
+ if (!api || !frame) {
692
+ return;
693
+ }
694
+
695
+ setPresentationFrameId(frame.id);
696
+ api.setViewport({
697
+ animation: true,
698
+ fit: 'contain',
699
+ offsets: { ui: true },
700
+ target: frame,
701
+ });
702
+ }, [presentationFrames]);
703
+
704
+ const togglePresentation = React.useCallback(() => {
705
+ if (presentationMode) {
706
+ setPresentationFrameId(null);
707
+ return;
708
+ }
709
+
710
+ setDrawerOpen(false);
711
+ setComposerOpen(false);
712
+ setActiveThreadId(null);
713
+ goToPresentationFrame(0);
714
+ }, [goToPresentationFrame, presentationMode]);
715
+
716
+ React.useEffect(() => {
717
+ if (!presentationFrameId || presentationIndex >= 0) {
718
+ return;
719
+ }
720
+
721
+ if (presentationFrames.length > 0) {
722
+ goToPresentationFrame(0);
723
+ } else {
724
+ setPresentationFrameId(null);
725
+ }
726
+ }, [goToPresentationFrame, presentationFrameId, presentationFrames.length, presentationIndex]);
727
+
728
+ React.useEffect(() => {
729
+ if (!presentationMode) {
730
+ return undefined;
731
+ }
732
+
733
+ const handlePresentationKeyDown = (event) => {
734
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) {
735
+ return;
736
+ }
737
+
738
+ if (event.key === 'Escape') {
739
+ event.preventDefault();
740
+ event.stopPropagation();
741
+ setPresentationFrameId(null);
742
+ } else if (event.key === 'ArrowRight') {
743
+ event.preventDefault();
744
+ event.stopPropagation();
745
+ goToPresentationFrame(presentationIndex + 1);
746
+ } else if (event.key === 'ArrowLeft') {
747
+ event.preventDefault();
748
+ event.stopPropagation();
749
+ goToPresentationFrame(presentationIndex - 1);
750
+ }
751
+ };
752
+
753
+ document.addEventListener('keydown', handlePresentationKeyDown, true);
754
+ return () => document.removeEventListener('keydown', handlePresentationKeyDown, true);
755
+ }, [goToPresentationFrame, presentationIndex, presentationMode]);
756
+
591
757
  React.useEffect(() => {
592
758
  if (activeThreadId && !threads.some((thread) => thread.id === activeThreadId)) {
593
759
  setActiveThreadId(null);
@@ -833,7 +999,8 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
833
999
  ]);
834
1000
  };
835
1001
 
836
- const renderCommentsOverlay = visible && room
1002
+ const commentsVisible = visible && !presentationMode;
1003
+ const renderCommentsOverlay = commentsVisible && room
837
1004
  ? React.createElement('div', {
838
1005
  'aria-label': 'Diagram comments overlay',
839
1006
  className: 'diagram-comments-overlay',
@@ -865,7 +1032,44 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
865
1032
  ])
866
1033
  : null;
867
1034
 
868
- return React.createElement(diagramCommentsContext.Provider, {
1035
+ const renderPresentationNavigation = presentationMode
1036
+ ? React.createElement('div', {
1037
+ 'aria-label': 'Presentation navigation',
1038
+ className: 'excalidraw-presentation-navigation',
1039
+ key: 'presentation-navigation',
1040
+ role: 'toolbar',
1041
+ }, [
1042
+ React.createElement('button', {
1043
+ 'aria-label': 'Previous slide',
1044
+ className: 'diagram-comment-button',
1045
+ disabled: presentationIndex <= 0,
1046
+ key: 'previous',
1047
+ onClick: () => goToPresentationFrame(presentationIndex - 1),
1048
+ type: 'button',
1049
+ }, '←'),
1050
+ React.createElement('span', {
1051
+ 'aria-live': 'polite',
1052
+ className: 'excalidraw-presentation-status',
1053
+ key: 'status',
1054
+ }, `${presentationFrames[presentationIndex]?.name || `Slide ${presentationIndex + 1}`} · ${presentationIndex + 1} / ${presentationFrames.length}`),
1055
+ React.createElement('button', {
1056
+ 'aria-label': 'Next slide',
1057
+ className: 'diagram-comment-button',
1058
+ disabled: presentationIndex >= presentationFrames.length - 1,
1059
+ key: 'next',
1060
+ onClick: () => goToPresentationFrame(presentationIndex + 1),
1061
+ type: 'button',
1062
+ }, '→'),
1063
+ ])
1064
+ : null;
1065
+
1066
+ return React.createElement(presentationContext.Provider, {
1067
+ value: {
1068
+ active: presentationMode,
1069
+ frameCount: presentationFrames.length,
1070
+ toggle: togglePresentation,
1071
+ },
1072
+ }, React.createElement(diagramCommentsContext.Provider, {
869
1073
  value: {
870
1074
  canWrite,
871
1075
  drawerOpen,
@@ -874,17 +1078,18 @@ function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = nu
874
1078
  selectedElement,
875
1079
  threads,
876
1080
  toggleDrawer: () => setDrawerOpen((open) => !open),
877
- visible,
1081
+ visible: commentsVisible,
878
1082
  },
879
1083
  }, [
880
1084
  React.createElement(DiagramCommentsExcalidraw, {
881
1085
  initialData,
882
1086
  key: 'excalidraw',
883
1087
  renderKey: editorRenderKey,
884
- viewModeEnabled: getDocumentViewState().viewModeEnabled,
1088
+ viewModeEnabled: presentationMode || getDocumentViewState().viewModeEnabled,
885
1089
  }),
886
1090
  renderCommentsOverlay,
887
- ]);
1091
+ renderPresentationNavigation,
1092
+ ]));
888
1093
  }
889
1094
 
890
1095
  function renderExcalidrawApp({ initialData } = {}) {
@@ -1053,6 +1258,9 @@ if (isTestMode) {
1053
1258
  .map((element) => element.id)
1054
1259
  .sort() ?? []
1055
1260
  ),
1261
+ getSelectedElementIds: () => (
1262
+ Object.keys(getMountedExcalidrawAPI()?.getAppState?.()?.selectedElementIds || {}).sort()
1263
+ ),
1056
1264
  getElementStatus: (elementId) => (
1057
1265
  getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.()
1058
1266
  ?.find((entry) => entry.id === elementId && !entry.isDeleted)?.status ?? null
@@ -1201,7 +1409,7 @@ function applySceneFromJson(rawJson, {
1201
1409
  return;
1202
1410
  }
1203
1411
 
1204
- appliedSceneJson = normalizedJson;
1412
+ setAppliedSceneJson(normalizedJson);
1205
1413
  recordSceneDiagnostic('remote-scene-received', {}, normalizedJson);
1206
1414
 
1207
1415
  if (!getMountedExcalidrawAPI() || !collabReady) {
@@ -1277,7 +1485,7 @@ function requestEditorRemount(scene) {
1277
1485
  const normalizedScene = normalizeScene(scene);
1278
1486
  const normalizedJson = JSON.stringify(normalizedScene);
1279
1487
  pendingRemoteSceneJson = normalizedJson;
1280
- appliedSceneJson = normalizedJson;
1488
+ setAppliedSceneJson(normalizedJson);
1281
1489
  pendingCollaborators = activeCollaborators;
1282
1490
  initialViewportFitPending = true;
1283
1491
  clearPreviewViewportFitTimers();
@@ -1353,7 +1561,7 @@ function applyLocalScene(scene, {
1353
1561
  const normalizedScene = normalizeScene(scene);
1354
1562
  const normalizedJson = JSON.stringify(normalizedScene);
1355
1563
 
1356
- appliedSceneJson = normalizedJson;
1564
+ setAppliedSceneJson(normalizedJson);
1357
1565
 
1358
1566
  if (!getMountedExcalidrawAPI() || !collabReady) {
1359
1567
  pendingRemoteSceneJson = normalizedJson;
@@ -1699,6 +1907,36 @@ async function waitForPendingRoomWrites({
1699
1907
  }
1700
1908
  }
1701
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
+
1702
1940
  async function prepareRealtimeRoomDisconnect() {
1703
1941
  if (!roomClient) {
1704
1942
  return true;
@@ -1796,6 +2034,11 @@ window.addEventListener('message', (event) => {
1796
2034
  return;
1797
2035
  }
1798
2036
 
2037
+ if (message.type === 'focus-element') {
2038
+ focusDiagramElement(message.elementId, message.elementType);
2039
+ return;
2040
+ }
2041
+
1799
2042
  if (message.type === 'follow-user') {
1800
2043
  applyHostFollowRequest(message.peerId || null);
1801
2044
  return;
@@ -1806,6 +2049,34 @@ window.addEventListener('message', (event) => {
1806
2049
  return;
1807
2050
  }
1808
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
+
1809
2080
  if (message.type === 'prepare-disconnect') {
1810
2081
  void (async () => {
1811
2082
  const requestId = message.requestId || '';
@@ -1907,7 +2178,7 @@ function initializeEditor(api) {
1907
2178
  const sceneJson = pendingRemoteSceneJson || roomClient?.getLastSceneJson?.() || '';
1908
2179
  const initialScene = parseSceneJson(sceneJson);
1909
2180
  pendingRemoteSceneJson = '';
1910
- appliedSceneJson = JSON.stringify(initialScene);
2181
+ setAppliedSceneJson(JSON.stringify(initialScene));
1911
2182
  updateApiScene(initialScene);
1912
2183
 
1913
2184
  if (pendingCollaborators) {
@@ -3,43 +3,43 @@ import { resolveAppUrl } from '../infrastructure/runtime-config.js';
3
3
  const EXPORT_PAGE_SOURCE = 'collabmd-export-page';
4
4
  const EXPORT_HOST_SOURCE = 'collabmd-export-host';
5
5
  const EXPORT_JOB_CLOSE_POLL_MS = 500;
6
- const EXPORT_JOB_TIMEOUT_MS = 60_000;
6
+ const EXPORT_JOB_TIMEOUT_MS = 300_000;
7
7
  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) {
15
17
  return ['html', 'pdf'].includes(format) ? format : 'docx';
16
18
  }
17
19
 
18
- function buildExportUrl({ filePath, format, jobId }) {
19
- const url = new URL(resolveAppUrl('/export-document.html'), window.location.origin);
20
- url.searchParams.set('action', normalizeFormat(format));
21
- url.searchParams.set('job', jobId);
22
- if (filePath) {
23
- url.searchParams.set('file', filePath);
24
- }
25
- return url.toString();
26
- }
27
-
28
20
  function createBootstrapPayload({
21
+ currentFilePath = '',
22
+ currentMarkdownText = '',
23
+ directoryPath = '',
29
24
  fileList = [],
30
25
  filePath,
31
26
  format,
32
27
  jobId,
33
28
  markdownText = '',
29
+ theme = 'light',
34
30
  title = '',
35
31
  }) {
36
32
  return {
37
33
  action: normalizeFormat(format),
34
+ currentFilePath: String(currentFilePath ?? ''),
35
+ currentMarkdownText: String(currentMarkdownText ?? ''),
36
+ directoryPath: String(directoryPath ?? ''),
38
37
  fileList: Array.isArray(fileList) ? fileList.slice() : [],
39
38
  filePath: String(filePath ?? ''),
40
39
  jobId,
41
40
  markdownText: String(markdownText ?? ''),
42
41
  source: EXPORT_HOST_SOURCE,
42
+ theme: theme === 'dark' ? 'dark' : 'light',
43
43
  title: String(title ?? ''),
44
44
  type: 'bootstrap',
45
45
  };
@@ -136,26 +136,9 @@ export function initializeExportBridge({
136
136
  window.__collabmdExportBridgeInitialized = true;
137
137
  }
138
138
 
139
- export async function exportDocument({
140
- fileList = [],
141
- filePath,
142
- format,
143
- markdownText = '',
144
- title = '',
145
- } = {}) {
146
- const normalizedFilePath = String(filePath ?? '').trim();
147
- if (!normalizedFilePath) {
148
- throw new Error('No markdown note is open');
149
- }
150
-
139
+ async function startExport(payload) {
151
140
  const jobId = createJobId();
152
- const url = buildExportUrl({
153
- filePath: normalizedFilePath,
154
- format,
155
- jobId,
156
- });
157
-
158
- const exportWindow = window.open(url, '_blank');
141
+ const exportWindow = globalThis.open('', jobId);
159
142
  if (!exportWindow) {
160
143
  throw new Error('Export popup was blocked');
161
144
  }
@@ -172,12 +155,9 @@ export async function exportDocument({
172
155
  });
173
156
  }, EXPORT_JOB_CLOSE_POLL_MS),
174
157
  payload: createBootstrapPayload({
175
- fileList,
176
- filePath: normalizedFilePath,
177
- format,
158
+ ...payload,
178
159
  jobId,
179
- markdownText,
180
- title,
160
+ theme: document.documentElement.dataset.theme,
181
161
  }),
182
162
  timeoutId: window.setTimeout(() => {
183
163
  finishPendingJob(jobId, {
@@ -188,6 +168,49 @@ export async function exportDocument({
188
168
  window: exportWindow,
189
169
  });
190
170
 
171
+ exportWindow.location.replace(resolveAppUrl('/export-document.html'));
191
172
  exportWindow.focus?.();
192
173
  return jobId;
193
174
  }
175
+
176
+ export async function exportDirectory({
177
+ currentFilePath = '',
178
+ currentMarkdownText = '',
179
+ directoryPath,
180
+ fileList = [],
181
+ format,
182
+ } = {}) {
183
+ const normalizedDirectoryPath = String(directoryPath ?? '').replace(/\\/gu, '/').replace(/^\/+|\/+$/gu, '').trim();
184
+ if (!normalizedDirectoryPath) {
185
+ throw new Error('No folder was selected');
186
+ }
187
+
188
+ return startExport({
189
+ currentFilePath,
190
+ currentMarkdownText,
191
+ directoryPath: normalizedDirectoryPath,
192
+ fileList,
193
+ format,
194
+ });
195
+ }
196
+
197
+ export async function exportDocument({
198
+ fileList = [],
199
+ filePath,
200
+ format,
201
+ markdownText = '',
202
+ title = '',
203
+ } = {}) {
204
+ const normalizedFilePath = String(filePath ?? '').trim();
205
+ if (!normalizedFilePath) {
206
+ throw new Error('No markdown note is open');
207
+ }
208
+
209
+ return startExport({
210
+ fileList,
211
+ filePath: normalizedFilePath,
212
+ format,
213
+ markdownText,
214
+ title,
215
+ });
216
+ }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  postExportPageMessage,
3
+ prepareDirectoryExportSnapshot,
3
4
  prepareExportSnapshot,
4
5
  runExportAdapter,
5
6
  waitForBootstrapPayload,
@@ -7,6 +8,10 @@ import {
7
8
  } from './export-pipeline.js';
8
9
  import { groupHeadingWithFollowingBlock } from './export-print-layout.js';
9
10
 
11
+ function getExportJobId() {
12
+ return new URLSearchParams(window.location.search).get('job') || window.name || '';
13
+ }
14
+
10
15
  function setStatus(message) {
11
16
  const status = document.getElementById('exportStatus');
12
17
  if (status) {
@@ -38,7 +43,8 @@ function renderSnapshot(snapshot) {
38
43
  }
39
44
 
40
45
  document.title = `${snapshot.title} — Export`;
41
- mount.innerHTML = snapshot.html;
46
+ const parsed = new DOMParser().parseFromString(`<body>${snapshot.html}</body>`, 'text/html');
47
+ mount.replaceChildren(...parsed.body.childNodes);
42
48
  groupHeadingWithFollowingBlock(mount);
43
49
  renderWarnings(snapshot);
44
50
  return mount;
@@ -48,7 +54,11 @@ async function bootstrap() {
48
54
  try {
49
55
  setStatus('Loading export content…');
50
56
  const payload = await waitForBootstrapPayload();
51
- const snapshot = await prepareExportSnapshot(payload);
57
+ document.documentElement.dataset.theme = payload.theme;
58
+ document.body.dataset.theme = payload.theme;
59
+ const snapshot = payload.directoryPath
60
+ ? await prepareDirectoryExportSnapshot(payload)
61
+ : await prepareExportSnapshot(payload);
52
62
  const mount = renderSnapshot(snapshot);
53
63
  snapshot.html = await waitForRenderedExportContent(mount);
54
64
  setStatus(payload.action === 'pdf'
@@ -58,12 +68,12 @@ async function bootstrap() {
58
68
  setStatus(payload.action === 'pdf'
59
69
  ? 'Print dialog opened.'
60
70
  : `${payload.action.toUpperCase()} download started.`);
61
- postExportPageMessage('complete', { jobId: new URL(window.location.href).searchParams.get('job') || '' });
71
+ postExportPageMessage('complete', { jobId: getExportJobId() });
62
72
  } catch (error) {
63
73
  const message = error instanceof Error ? error.message : 'Export failed';
64
74
  setStatus(message);
65
75
  postExportPageMessage('error', {
66
- jobId: new URL(window.location.href).searchParams.get('job') || '',
76
+ jobId: getExportJobId(),
67
77
  message,
68
78
  });
69
79
  }