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
@@ -4,6 +4,7 @@ import hljs from 'highlight.js';
4
4
  import { isImageAttachmentFilePath } from '../../domain/file-kind.js';
5
5
  import { resolveVaultRelativePath } from '../../domain/vault-paths.js';
6
6
  import { resolveWikiTargetPath } from '../../domain/wiki-link-resolver.js';
7
+ import { classifyPublicVideoEmbed } from '../../domain/video-embed.js';
7
8
  import { escapeHtml } from '../domain/vault-utils.js';
8
9
  import { extractYamlFrontmatter } from '../../domain/yaml-frontmatter.js';
9
10
  import { renderFrontmatterBlock } from './markdown-frontmatter.js';
@@ -12,21 +13,6 @@ import { analyzeMarkdownComplexity } from './preview-render-profile.js';
12
13
  hljs.registerAliases(['cql', 'mariadb', 'mssql', 'mysql', 'plsql', 'sqlite'], { languageName: 'sql' });
13
14
  hljs.registerAliases(['ecmascript', 'node'], { languageName: 'javascript' });
14
15
 
15
- const DIRECT_VIDEO_MIME_TYPES = Object.freeze({
16
- '.mp4': 'video/mp4',
17
- '.ogg': 'video/ogg',
18
- '.webm': 'video/webm',
19
- });
20
-
21
- const YOUTUBE_HOSTS = new Set([
22
- 'youtube.com',
23
- 'www.youtube.com',
24
- 'm.youtube.com',
25
- 'youtu.be',
26
- 'www.youtu.be',
27
- 'youtube-nocookie.com',
28
- 'www.youtube-nocookie.com',
29
- ]);
30
16
 
31
17
  function renderToken(renderer, tokens, index, options, env, self) {
32
18
  return renderer?.(tokens, index, options, env, self) ?? self.renderToken(tokens, index, options);
@@ -192,78 +178,6 @@ function createBaseFileEmbedPlaceholder({ embedKey, label, target, viewName = ''
192
178
  return `<span class="bases-embed-placeholder diagram-preview-shell" data-base-key="${escapeHtml(embedKey)}" data-base-path="${escapeHtml(target)}" data-base-label="${escapeHtml(label)}"${viewAttribute}${sourceFileAttribute}><span class="diagram-preview-placeholder-card"><span class="diagram-preview-placeholder-copy"><strong>${escapeHtml(label)}</strong><span>Loads query results when preview renders</span></span></span></span>`;
193
179
  }
194
180
 
195
- function getDirectVideoMimeType(pathname = '') {
196
- const match = pathname.toLowerCase().match(/\.(mp4|ogg|webm)$/i);
197
- if (!match) {
198
- return null;
199
- }
200
-
201
- return DIRECT_VIDEO_MIME_TYPES[`.${match[1].toLowerCase()}`] || null;
202
- }
203
-
204
- function normalizeYouTubeVideoId(candidate = '') {
205
- const normalized = String(candidate || '').trim();
206
- return /^[A-Za-z0-9_-]{11}$/.test(normalized) ? normalized : null;
207
- }
208
-
209
- function getCanonicalYouTubeEmbedUrl(url) {
210
- const host = url.hostname.toLowerCase();
211
- let videoId = null;
212
-
213
- if (host === 'youtu.be' || host === 'www.youtu.be') {
214
- videoId = normalizeYouTubeVideoId(url.pathname.split('/').filter(Boolean)[0] || '');
215
- } else if (host.includes('youtube') || host.includes('youtube-nocookie')) {
216
- if (url.pathname === '/watch') {
217
- videoId = normalizeYouTubeVideoId(url.searchParams.get('v') || '');
218
- } else if (url.pathname.startsWith('/embed/')) {
219
- videoId = normalizeYouTubeVideoId(url.pathname.split('/').filter(Boolean)[1] || '');
220
- }
221
- }
222
-
223
- if (!videoId) {
224
- return null;
225
- }
226
-
227
- return `https://www.youtube-nocookie.com/embed/${videoId}`;
228
- }
229
-
230
- function classifyPublicVideoEmbed(source = '') {
231
- let url;
232
-
233
- try {
234
- url = new URL(String(source || '').trim());
235
- } catch {
236
- return null;
237
- }
238
-
239
- if (url.protocol !== 'https:') {
240
- return null;
241
- }
242
-
243
- const host = url.hostname.toLowerCase();
244
- if (YOUTUBE_HOSTS.has(host)) {
245
- const embedUrl = getCanonicalYouTubeEmbedUrl(url);
246
- if (!embedUrl) {
247
- return null;
248
- }
249
-
250
- return {
251
- embedUrl,
252
- type: 'youtube',
253
- };
254
- }
255
-
256
- const mimeType = getDirectVideoMimeType(url.pathname);
257
- if (!mimeType) {
258
- return null;
259
- }
260
-
261
- return {
262
- mimeType,
263
- sourceUrl: url.toString(),
264
- type: 'direct-video',
265
- };
266
- }
267
181
 
268
182
  function createVideoEmbedPlaceholder({
269
183
  embedKey,
@@ -6,24 +6,8 @@ import {
6
6
  import { getRenderProfile } from './preview-render-profile.js';
7
7
 
8
8
  export class PreviewRenderScheduler {
9
- constructor({
10
- cancelAnimationFrameFn = (frameId) => cancelAnimationFrame(frameId),
11
- cancelIdleRenderFn = cancelIdleRender,
12
- clearTimeoutFn = (timeoutId) => clearTimeout(timeoutId),
13
- getRenderProfileFn = getRenderProfile,
14
- idleTimeoutMs = IDLE_RENDER_TIMEOUT_MS,
15
- requestAnimationFrameFn = (callback) => requestAnimationFrame(callback),
16
- requestIdleRenderFn = requestIdleRender,
17
- setTimeoutFn = (callback, delay) => setTimeout(callback, delay),
18
- } = {}) {
19
- this.cancelAnimationFrameFn = cancelAnimationFrameFn;
20
- this.cancelIdleRenderFn = cancelIdleRenderFn;
21
- this.clearTimeoutFn = clearTimeoutFn;
9
+ constructor({ getRenderProfileFn = getRenderProfile } = {}) {
22
10
  this.getRenderProfileFn = getRenderProfileFn;
23
- this.idleTimeoutMs = idleTimeoutMs;
24
- this.requestAnimationFrameFn = requestAnimationFrameFn;
25
- this.requestIdleRenderFn = requestIdleRenderFn;
26
- this.setTimeoutFn = setTimeoutFn;
27
11
  this.frameId = null;
28
12
  this.idleId = null;
29
13
  this.timeoutId = null;
@@ -34,7 +18,7 @@ export class PreviewRenderScheduler {
34
18
  this.cancel();
35
19
 
36
20
  const scheduleFrame = () => {
37
- this.frameId = this.requestAnimationFrameFn(() => {
21
+ this.frameId = requestAnimationFrame(() => {
38
22
  this.frameId = null;
39
23
  this.timeoutId = null;
40
24
  onRenderRequested?.(markdownText, renderVersion);
@@ -43,25 +27,25 @@ export class PreviewRenderScheduler {
43
27
 
44
28
  const scheduleRender = () => {
45
29
  if (renderProfile.deferUntilIdle) {
46
- this.idleId = this.requestIdleRenderFn(() => {
30
+ this.idleId = requestIdleRender(() => {
47
31
  this.idleId = null;
48
32
  scheduleFrame();
49
- }, this.idleTimeoutMs);
33
+ }, IDLE_RENDER_TIMEOUT_MS);
50
34
  return;
51
35
  }
52
36
 
53
37
  scheduleFrame();
54
38
  };
55
39
 
56
- this.timeoutId = this.setTimeoutFn(scheduleRender, renderProfile.debounceMs);
40
+ this.timeoutId = setTimeout(scheduleRender, renderProfile.debounceMs);
57
41
  }
58
42
 
59
43
  cancel() {
60
- this.clearTimeoutFn(this.timeoutId);
44
+ clearTimeout(this.timeoutId);
61
45
  if (this.frameId) {
62
- this.cancelAnimationFrameFn(this.frameId);
46
+ cancelAnimationFrame(this.frameId);
63
47
  }
64
- this.cancelIdleRenderFn(this.idleId);
48
+ cancelIdleRender(this.idleId);
65
49
  this.frameId = null;
66
50
  this.idleId = null;
67
51
  this.timeoutId = null;
@@ -3,7 +3,7 @@ import { PlantUmlPreviewHydrator } from './plantuml-preview-hydrator.js';
3
3
  import { DiagramChrome } from './diagram-chrome.js';
4
4
  import { IDLE_RENDER_TIMEOUT_MS } from './preview-diagram-utils.js';
5
5
  import { PreviewRenderExecutor } from './preview-render-executor.js';
6
- import { getRenderProfile, isLargeDocumentStats } from './preview-render-profile.js';
6
+ import { isLargeDocumentStats } from './preview-render-profile.js';
7
7
  import { PreviewRenderScheduler } from './preview-render-scheduler.js';
8
8
  import { resolveApiUrl } from '../domain/runtime-paths.js';
9
9
 
@@ -105,7 +105,7 @@ export class PreviewRenderer {
105
105
  loadFileSource,
106
106
  renderClient: plantUmlRenderClient,
107
107
  });
108
- this.renderScheduler = new PreviewRenderScheduler({ getRenderProfileFn: getRenderProfile });
108
+ this.renderScheduler = new PreviewRenderScheduler();
109
109
  this.renderExecutor = new PreviewRenderExecutor({
110
110
  attachmentApiPath: resolveApiUrl('/attachment'),
111
111
  getFileList: () => this.getFileList?.() ?? [],
@@ -0,0 +1,47 @@
1
+ import { resolveRenderableDiagram } from '../../domain/diagram-source.js';
2
+ import {
3
+ rasterizeSvgMarkupToPngBlob,
4
+ renderMermaidExportSvgMarkup,
5
+ } from './diagram-preview-export.js';
6
+
7
+ export async function renderWebMcpDiagram(input = {}, {
8
+ callTool,
9
+ getMermaid,
10
+ signal,
11
+ } = {}) {
12
+ const requestedStartLine = Number.parseInt(input.startLine, 10);
13
+ const read = await callTool('read_document', {
14
+ lineCount: 500,
15
+ path: input.path,
16
+ startLine: Number.isInteger(requestedStartLine) ? requestedStartLine : 1,
17
+ }, { signal });
18
+ const diagram = resolveRenderableDiagram(read.content, input.path, {
19
+ contentStartLine: read.startLine,
20
+ startLine: input.startLine,
21
+ });
22
+ if (!diagram || diagram.kind !== 'mermaid') {
23
+ return callTool('render_diagram', input, { signal });
24
+ }
25
+ if (read.kind === 'mermaid' && read.truncated) {
26
+ throw new Error('Mermaid source exceeds the WebMCP read limit');
27
+ }
28
+
29
+ const svg = await renderMermaidExportSvgMarkup(await getMermaid(), diagram.source);
30
+ const format = input.format ?? 'png';
31
+ const mimeType = format === 'svg' ? 'image/svg+xml' : 'image/png';
32
+ const bytes = format === 'svg'
33
+ ? new TextEncoder().encode(svg)
34
+ : new Uint8Array(await (await rasterizeSvgMarkupToPngBlob(svg)).arrayBuffer());
35
+ return {
36
+ endLine: diagram.endLine,
37
+ format,
38
+ image: { data: bytes.toBase64(), encoding: 'base64', mimeType },
39
+ kind: diagram.kind,
40
+ mimeType,
41
+ path: read.path,
42
+ renderer: 'mermaid-browser',
43
+ revision: read.revision,
44
+ startLine: diagram.startLine,
45
+ warnings: [],
46
+ };
47
+ }
@@ -21,6 +21,7 @@ export class WorkspaceCoordinator {
21
21
  createEditorSession,
22
22
  getDisplayName,
23
23
  getFileList,
24
+ getVaultFileList = getFileList,
24
25
  getLineWrappingEnabled,
25
26
  getVimModeEnabled,
26
27
  getLocalUser,
@@ -74,6 +75,7 @@ export class WorkspaceCoordinator {
74
75
  this.createEditorSession = createEditorSession;
75
76
  this.getDisplayName = getDisplayName;
76
77
  this.getFileList = getFileList;
78
+ this.getVaultFileList = getVaultFileList;
77
79
  this.getLineWrappingEnabled = getLineWrappingEnabled;
78
80
  this.getVimModeEnabled = getVimModeEnabled ?? (() => false);
79
81
  this.getLocalUser = getLocalUser;
@@ -208,7 +210,7 @@ export class WorkspaceCoordinator {
208
210
  return false;
209
211
  }
210
212
 
211
- if (!filePath || !this.getFileList().includes(filePath)) {
213
+ if (!filePath || !this.getVaultFileList().includes(filePath)) {
212
214
  this.cleanupSession();
213
215
  this.stateStore.sessionLoadToken += 1;
214
216
  this.onFileOpenError({ code: 'not-found', filePath });
@@ -143,6 +143,13 @@ export class WorkspaceRouteController {
143
143
  return;
144
144
  }
145
145
  this.revealEditorMatch(route);
146
+ if (route.elementId) {
147
+ this.excalidrawEmbed?.openElement(
148
+ route.filePath,
149
+ route.elementId,
150
+ route.elementType,
151
+ );
152
+ }
146
153
  this.requestPreviewRouteAnchor?.(route.anchor ?? null, route.filePath);
147
154
  }
148
155
 
@@ -1,5 +1,6 @@
1
1
  import { PdfPreviewController } from '../application/pdf-preview-controller.js';
2
2
  import { PreviewRenderer } from '../application/preview-renderer.js';
3
+ import { renderWebMcpDiagram } from '../application/webmcp-diagram-renderer.js';
3
4
  import { ensureQuickSwitcherInstance, toggleQuickSwitcherInstance } from '../application/quick-switcher-loader.js';
4
5
  import { WorkspaceRouteController } from '../application/workspace-route-controller.js';
5
6
  import { WikiLinkFileController } from '../application/wiki-link-file-controller.js';
@@ -18,6 +19,7 @@ import { uiFeatureSidebarMethods } from '../application/app-shell/ui-feature-sid
18
19
  import { uiFeatureTabActivityMethods } from '../application/app-shell/ui-feature-tab-activity.js';
19
20
  import { uiFeatureToolbarMethods } from '../application/app-shell/ui-feature-toolbar.js';
20
21
  import { workspaceFeature } from '../application/app-shell/workspace-feature.js';
22
+ import { agentConnectionApiClient } from '../infrastructure/agent-connection-api-client.js';
21
23
  import { LOBBY_CHAT_MESSAGE_MAX_LENGTH, LobbyPresence } from '../infrastructure/lobby-presence.js';
22
24
  import { BrowserPreferencesPort } from '../infrastructure/browser-preferences-port.js';
23
25
  import { BrowserNotificationPort } from '../infrastructure/browser-notification-port.js';
@@ -35,7 +37,9 @@ import {
35
37
  } from '../infrastructure/runtime-config.js';
36
38
  import { TabActivityLock } from '../infrastructure/tab-activity-lock.js';
37
39
  import { vaultApiClient } from '../infrastructure/vault-api-client.js';
40
+ import { WebMcpToolRegistry } from '../infrastructure/webmcp-tool-registry.js';
38
41
  import { WorkspaceSyncClient } from '../infrastructure/workspace-sync-client.js';
42
+ import { AgentConnectionController } from '../presentation/agent-connection-controller.js';
39
43
  import { BacklinksPanel } from '../presentation/backlinks-panel.js';
40
44
  import { CommentOverviewController } from '../presentation/comment-overview-controller.js';
41
45
  import { CommentUiController } from '../presentation/comment-ui-controller.js';
@@ -166,6 +170,66 @@ export class CollabMdAppShell {
166
170
 
167
171
  this.toastController = new ToastController(this.elements.toastContainer);
168
172
  this.chatToastController = new ToastController(this.elements.chatToastContainer);
173
+ this.agentConnectionController = new AgentConnectionController({
174
+ apiClient: agentConnectionApiClient,
175
+ closeToolbarMenu: () => this.closeToolbarOverflowMenu(),
176
+ content: this.elements.agentConnectionContent,
177
+ dialog: this.elements.agentConnectionDialog,
178
+ runtimeConfig: this.runtimeConfig,
179
+ toastController: this.toastController,
180
+ trigger: this.elements.connectAgentButton,
181
+ });
182
+ const callAgentTool = (...args) => this.vaultApiClient.callAgentTool(...args);
183
+ this.webMcpTools = new WebMcpToolRegistry({
184
+ acknowledgeToolCall: ({ input, name, preparation, result, signal }) => {
185
+ if (
186
+ name !== 'edit_excalidraw'
187
+ || input.path !== this.currentFilePath
188
+ ) {
189
+ return null;
190
+ }
191
+ return this.excalidrawEmbed?.waitForAgentPaint(input.path, result.revision, {
192
+ afterRevision: preparation?.revision,
193
+ signal,
194
+ });
195
+ },
196
+ callTool: (name, input, options) => name === 'render_diagram'
197
+ ? renderWebMcpDiagram(input, {
198
+ callTool: callAgentTool,
199
+ getMermaid: () => this.previewRenderer.mermaidHydrator.ensureMermaid(),
200
+ signal: options?.signal,
201
+ })
202
+ : callAgentTool(name, input, options),
203
+ getActiveContext: () => ({
204
+ activeDiagramPath: this.isExcalidrawFile(this.currentFilePath)
205
+ ? this.currentFilePath
206
+ : null,
207
+ activePath: this.currentFilePath,
208
+ }),
209
+ getIsTabActive: () => this.isTabActive,
210
+ onDidMutate: () => {
211
+ this.toastController.show('Agent-assisted Vault change applied. Review it before committing.');
212
+ },
213
+ prepareToolCall: async ({ input, name, signal }) => {
214
+ if (
215
+ ![
216
+ 'edit_excalidraw',
217
+ 'inspect_excalidraw',
218
+ 'read_document',
219
+ 'render_excalidraw',
220
+ 'verify_excalidraw',
221
+ ].includes(name)
222
+ || input.path !== this.currentFilePath
223
+ ) {
224
+ return null;
225
+ }
226
+ const preparation = await this.excalidrawEmbed?.flushPendingAgentWrites(input.path, { signal });
227
+ if (['timeout', 'unavailable'].includes(preparation?.status)) {
228
+ throw new Error('The active Excalidraw editor did not confirm its latest collaboration update');
229
+ }
230
+ return preparation;
231
+ },
232
+ });
169
233
  this.fileExplorer = new FileExplorerController({
170
234
  mobileBreakpointQuery: this.mobileBreakpointQuery,
171
235
  onDirectoryExport: (directoryPath, format) => this.handleDirectoryExportRequest(directoryPath, format),
@@ -201,7 +265,9 @@ export class CollabMdAppShell {
201
265
  onResetFile: (filePath, { scope }) => this.openGitResetDialog(filePath, { scope }),
202
266
  onSelectCommit: (hash, { path }) => this.handleGitCommitSelection(hash, { closeSidebarOnMobile: true, path }),
203
267
  onSelectDiff: (filePath, { scope }) => this.handleGitDiffSelection(filePath, { closeSidebarOnMobile: true, scope }),
268
+ onStageAll: () => this.stageAllGitFiles(),
204
269
  onStageFile: (filePath, { scope }) => this.stageGitFile(filePath, { scope }),
270
+ onUnstageAll: () => this.unstageAllGitFiles(),
205
271
  onUnstageFile: (filePath, { scope }) => this.unstageGitFile(filePath, { scope }),
206
272
  onViewAllDiff: () => this.handleGitDiffSelection(null, { closeSidebarOnMobile: true, scope: 'all' }),
207
273
  searchInput: this.elements.gitSearchInput,
@@ -307,6 +373,7 @@ export class CollabMdAppShell {
307
373
  headerPanelElement: this.elements.backlinksHeaderPanel,
308
374
  inlinePanelElement: this.elements.backlinksInlinePanel,
309
375
  loadBacklinks: (filePath, options = {}) => this.vaultApiClient.readBacklinks(filePath, options),
376
+ onBeforeToggle: () => this.scrollSyncController.suspendSync(250),
310
377
  onFileSelect: (filePath) => this.workspaceRouteController.handleFileSelection(filePath, {
311
378
  closeSidebarOnMobile: true,
312
379
  }),
@@ -315,6 +382,11 @@ export class CollabMdAppShell {
315
382
  this.excalidrawEmbed = new ExcalidrawEmbedController({
316
383
  getLocalUser: () => this.lobby.getLocalUser(),
317
384
  getTheme: () => this.themeController.getTheme(),
385
+ onOpenElement: ({ elementId, elementType, filePath } = {}) => {
386
+ if (filePath && elementId) {
387
+ this.navigation.navigateToFile(filePath, { elementId, elementType });
388
+ }
389
+ },
318
390
  onOpenFile: (filePath) => filePath && this.navigation.navigateToFile(filePath),
319
391
  onStopFollowing: () => this.stopFollowingUser(),
320
392
  onToggleQuickSwitcher: () => {
@@ -473,6 +545,7 @@ export class CollabMdAppShell {
473
545
  }),
474
546
  getDisplayName: (filePath) => this.getDisplayName(filePath),
475
547
  getFileList: () => this.fileExplorer.flatDocumentFiles,
548
+ getVaultFileList: () => this.fileExplorer.flatFiles,
476
549
  getLineWrappingEnabled: () => this.getStoredLineWrapping(),
477
550
  getVimModeEnabled: () => this.getStoredVimMode(),
478
551
  getLocalUser: () => this.lobby.getLocalUser(),
@@ -507,6 +580,7 @@ export class CollabMdAppShell {
507
580
  },
508
581
  onConnectionChange: (state) => this.handleConnectionChange(state),
509
582
  onContentChange: ({ isBase, isHtml, isMermaid, isPlantUml, isStructurizrWorkspace }) => {
583
+ void this.webMcpTools.refresh();
510
584
  this.handleCommentEditorContentChange();
511
585
  if (isHtml) {
512
586
  this.workspacePreviewController.renderHtmlFilePreview({
@@ -642,7 +716,10 @@ export class CollabMdAppShell {
642
716
  }
643
717
 
644
718
  get session() { return this._session; }
645
- set session(value) { this._session = value; }
719
+ set session(value) {
720
+ this._session = value;
721
+ void this.webMcpTools?.refresh();
722
+ }
646
723
 
647
724
  publishFileOpenPerf() {
648
725
  if (typeof window === 'undefined') {
@@ -0,0 +1,73 @@
1
+ import { createFileRouteHash, getHashParamsFromRaw } from './hash-routes.js';
2
+ import { normalizeVaultPathInput } from './vault-paths.js';
3
+
4
+ const MAX_ELEMENT_ID_LENGTH = 256;
5
+ const ELEMENT_ID_PATTERN = /^[A-Za-z0-9_-]+$/u;
6
+
7
+ function normalizeElementId(value) {
8
+ const normalized = String(value ?? '').trim();
9
+ return normalized.length <= MAX_ELEMENT_ID_LENGTH && ELEMENT_ID_PATTERN.test(normalized)
10
+ ? normalized
11
+ : '';
12
+ }
13
+
14
+ function normalizeUrlPath(value) {
15
+ const normalized = String(value ?? '').trim().replace(/\/+$/u, '');
16
+ return normalized || '/';
17
+ }
18
+
19
+ export function createExcalidrawElementLink(filePath, elementId, {
20
+ appUrl,
21
+ elementType = 'element',
22
+ } = {}) {
23
+ const normalizedFilePath = normalizeVaultPathInput(filePath);
24
+ const normalizedElementId = normalizeElementId(elementId);
25
+ if (!normalizedFilePath || !normalizedElementId || !appUrl) {
26
+ return '';
27
+ }
28
+
29
+ let url;
30
+ try {
31
+ url = new URL(appUrl);
32
+ } catch {
33
+ return '';
34
+ }
35
+
36
+ url.hash = createFileRouteHash(normalizedFilePath, {
37
+ elementId: normalizedElementId,
38
+ elementType,
39
+ });
40
+ return url.toString();
41
+ }
42
+
43
+ export function parseExcalidrawElementLink(link, {
44
+ appPath = '/',
45
+ origin = '',
46
+ } = {}) {
47
+ let url;
48
+ try {
49
+ url = new URL(String(link ?? ''), origin || 'http://collabmd.invalid');
50
+ } catch {
51
+ return null;
52
+ }
53
+
54
+ if (origin && url.origin !== origin) {
55
+ return null;
56
+ }
57
+ if (normalizeUrlPath(url.pathname) !== normalizeUrlPath(appPath)) {
58
+ return null;
59
+ }
60
+
61
+ const params = getHashParamsFromRaw(url.hash);
62
+ const filePath = normalizeVaultPathInput(params.get('file'));
63
+ const elementId = normalizeElementId(params.get('element'));
64
+ if (!filePath || !elementId) {
65
+ return null;
66
+ }
67
+
68
+ return {
69
+ elementId,
70
+ elementType: params.get('elementType') === 'group' ? 'group' : 'element',
71
+ filePath,
72
+ };
73
+ }
@@ -63,6 +63,27 @@ export function sceneToInitialData(parsedScene, { theme = 'dark' } = {}) {
63
63
  };
64
64
  }
65
65
 
66
+ export function createExcalidrawExportOptions(scene, {
67
+ padding = 24,
68
+ scale = 1,
69
+ } = {}) {
70
+ return {
71
+ appState: {
72
+ exportBackground: true,
73
+ exportScale: scale,
74
+ exportWithDarkMode: false,
75
+ gridSize: scene.appState?.gridSize ?? null,
76
+ theme: 'light',
77
+ viewBackgroundColor: scene.appState?.viewBackgroundColor ?? '#ffffff',
78
+ },
79
+ elements: Array.isArray(scene.elements)
80
+ ? scene.elements.filter((element) => element && !element.isDeleted)
81
+ : [],
82
+ exportPadding: padding,
83
+ files: scene.files || null,
84
+ };
85
+ }
86
+
66
87
  export function buildStoredScene(elements, appState, files) {
67
88
  return normalizeScene({
68
89
  elements: elements.filter((element) => !element.isDeleted),
@@ -38,6 +38,8 @@ export function createFileRouteHash(filePath, {
38
38
  anchor = null,
39
39
  column = null,
40
40
  drawioMode = null,
41
+ elementId = null,
42
+ elementType = null,
41
43
  line = null,
42
44
  matchLength = null,
43
45
  } = {}) {
@@ -51,6 +53,12 @@ export function createFileRouteHash(filePath, {
51
53
  if (anchor) {
52
54
  params.set('anchor', String(anchor).trim());
53
55
  }
56
+ if (elementId) {
57
+ params.set('element', String(elementId).trim());
58
+ }
59
+ if (elementType === 'group') {
60
+ params.set('elementType', 'group');
61
+ }
54
62
  if (line != null) {
55
63
  params.set('line', String(Math.max(1, Math.round(Number(line) || 1))));
56
64
  }
@@ -47,11 +47,7 @@ export function generatePeerId() {
47
47
  }
48
48
 
49
49
  function generateUserId() {
50
- if (globalThis.crypto?.randomUUID) {
51
- return globalThis.crypto.randomUUID();
52
- }
53
-
54
- return `${generatePeerId()}-${Date.now().toString(36)}`;
50
+ return globalThis.crypto?.randomUUID?.() ?? `${generatePeerId()}-${Date.now().toString(36)}`;
55
51
  }
56
52
 
57
53
  // A stable peer ID for this browser tab, shared across lobby and per-file sessions.
@@ -60,6 +60,11 @@ function applyBasePath(basePath, pathValue) {
60
60
 
61
61
  export function getClientRuntimeConfig() {
62
62
  const rawConfig = {
63
+ agentAccess: {
64
+ enabled: false,
65
+ endpoint: '',
66
+ managed: false,
67
+ },
63
68
  auth: {
64
69
  enabled: false,
65
70
  implemented: true,