collabmd 0.1.45 → 0.1.47
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.
- package/README.md +40 -19
- package/bin/collabmd.js +27 -1
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +1 -0
- package/dist/client/assets/{abnfDiagram-N423BO3Z-CUMTFJhI.js → abnfDiagram-VCTEODGH-BNDLzJ7L.js} +1 -1
- package/dist/client/assets/architecture-7GRP2DOG-BMOPAkUC.js +1 -0
- package/dist/client/assets/{architectureDiagram-T3A2C74G-w25m71oV.js → architectureDiagram-5GKGNRK7-I39sYjHI.js} +1 -1
- package/dist/client/assets/base-BYODPHdS.js +1 -0
- package/dist/client/assets/blockDiagram-NRAW4CY4-C6DtJy5Y.js +129 -0
- package/dist/client/assets/browser-BKLM0ThC-IxjwuOIz-BBqQnUEb.js +1 -0
- package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
- package/dist/client/assets/c4Diagram-UCG6FXSJ-CtWtxS9_.js +38 -0
- package/dist/client/assets/channel-CaoKA_We.js +1 -0
- package/dist/client/assets/{chunk-U6XO7XAA-BFxjP5U3.js → chunk-2ZTRR5NV-C2qZJila.js} +1 -1
- package/dist/client/assets/{chunk-YOTPTUD7-D0vRKqYp.js → chunk-3Z5EZCMW-DMniOlPd.js} +1 -1
- package/dist/client/assets/{chunk-BIQX33UG-DZEsGuSn.js → chunk-5V3GS4D5-i-ZDOEnP.js} +1 -1
- package/dist/client/assets/{chunk-MOZMSUNE-XVucA-n6.js → chunk-6AZGARVD-BB6FqMl-.js} +1 -1
- package/dist/client/assets/{chunk-5JV3BV7I-Cy5pj3QY.js → chunk-6EIED4P4-DdUJkP0f.js} +1 -1
- package/dist/client/assets/{chunk-R7FJI6CG-Cnn3gE-Y.js → chunk-6K3QC6MW-CqVsVn-y.js} +1 -1
- package/dist/client/assets/{chunk-OSBZ3O6U-CrKZOUOr.js → chunk-6TQVIW2G-DKlLbNIP.js} +1 -1
- package/dist/client/assets/{chunk-JG7HCLWE-BYfiSaVt.js → chunk-747NJXEK-BFkZPlab.js} +1 -1
- package/dist/client/assets/{chunk-NSK5VX7P-TLH37VXI.js → chunk-75Z2AOVW-CdFSaPjz.js} +1 -1
- package/dist/client/assets/chunk-CKBBP62Z-DG26UiBv.js +2 -0
- package/dist/client/assets/{chunk-3NCLNEKW-Y2Jf_LI-.js → chunk-CLGD4ZFX-DQLgbQrv.js} +1 -1
- package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +2 -0
- package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +127 -0
- package/dist/client/assets/{chunk-2GRJ4B5K-DhnlUv3F.js → chunk-F27PBJKO-CNHIFhjo.js} +1 -1
- package/dist/client/assets/{chunk-YGJVY4SA-CjSe7Bx7.js → chunk-FLYJHZSQ-Be7lKF17.js} +1 -1
- package/dist/client/assets/{chunk-KEIR6QF5-BICK3FdT.js → chunk-FOHPRMQF-DHwB1DNv.js} +2 -2
- package/dist/client/assets/{chunk-5RXB4S5H-BWrwFqMI.js → chunk-G27WJ6UU-D9-eg537.js} +2 -2
- package/dist/client/assets/chunk-GMAD6QVW-CbcBqPM5.js +72 -0
- package/dist/client/assets/{chunk-QBLGF6JB-CxAyS2mW.js → chunk-I5DQTOEV-Hm2LfiYI.js} +1 -1
- package/dist/client/assets/{chunk-5FCAYU7R-Dh5nylU8.js → chunk-ICYGCRZG-749o-pGJ.js} +1 -1
- package/dist/client/assets/{chunk-CQNSW5MT-B8S09OIh.js → chunk-IH6LHLGP-CC6I9F3i.js} +1 -1
- package/dist/client/assets/chunk-JQ64N6SF-DSFNH4bh.js +1 -0
- package/dist/client/assets/{chunk-CYSBUYHQ-C1Rf4art.js → chunk-KI3K4JFJ-Rma-0k_M.js} +1 -1
- package/dist/client/assets/{chunk-GF5L2VYU-DkJpO7ff.js → chunk-LCL6LL3I-DmDkVukh.js} +1 -1
- package/dist/client/assets/chunk-LNGE3PJU-DWpGwHrW.js +34 -0
- package/dist/client/assets/chunk-OBVCFTLP-BJR7u1jh.js +1 -0
- package/dist/client/assets/{chunk-5TONJI2A-DQOY3q1H.js → chunk-OUJLGHUK-D0j2JRSg.js} +1 -1
- package/dist/client/assets/chunk-P2QGCYS3-BRpqY32H.js +1 -0
- package/dist/client/assets/{chunk-KBJHAD2P-CB_3zWUf.js → chunk-POPQ4Y6H-Cb4SlVZF.js} +1 -1
- package/dist/client/assets/{chunk-4I5QYGJK-CCB2YcIH.js → chunk-PWAF6VOD-Cz8IwBB5.js} +1 -1
- package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +10 -0
- package/dist/client/assets/chunk-RHFEMEQ7-CWY8cf4H.js +168 -0
- package/dist/client/assets/chunk-RM4UPSZO-CWRwxNWJ.js +41 -0
- package/dist/client/assets/{chunk-6Q2QTUOP-CPlWzBtc.js → chunk-SVP7TREG-CyHOmcac.js} +1 -1
- package/dist/client/assets/chunk-TEH6E4GO-DVayPj-q.js +62 -0
- package/dist/client/assets/{chunk-EMLP6XTP-RLD4nXpI.js → chunk-UY3FDG6J-CnmaAkG5.js} +1 -1
- package/dist/client/assets/{chunk-5HE753X5-Bw-yg4kO.js → chunk-XHIXRSVI-BFQ6B8BE.js} +1 -1
- package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +1 -0
- package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +1 -0
- package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
- package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
- package/dist/client/assets/comment-ui-shared-DWkk9Vh3.js +7 -0
- package/dist/client/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
- package/dist/client/assets/{cynefinDiagram-MW4NZA55-CJxXfJek.js → cynefinDiagram-5FMLGOSQ-CdTPi2yO.js} +1 -1
- package/dist/client/assets/dagre-3AP2YEHR-5gtJtd0O.js +4 -0
- package/dist/client/assets/dagre-PrKaheQc.js +1 -0
- package/dist/client/assets/{diagram-7IWD3JNH-B2d2Bm15.js → diagram-S7CK7UJ4-BudWz9WU.js} +4 -4
- package/dist/client/assets/{diagram-UB23O5K3-DJcKCwv9.js → diagram-UQ7AKVKN-3v4rbH-s.js} +1 -1
- package/dist/client/assets/{diagram-B4RE2ZJO-NP4UAwjR.js → diagram-VSXAHHWV-CQRVEZR7.js} +1 -1
- package/dist/client/assets/{diagram-Q27KOJAE-DylDuwor.js → diagram-VX7I27RA-C_-rj0sw.js} +1 -1
- package/dist/client/assets/{diagram-LBJQPF4R-D1FJQV8t.js → diagram-Z3DM3KII-B4yxavgj.js} +1 -1
- package/dist/client/assets/direct-engine-DdrQKpgq-CtKfQzCt.js +5 -0
- package/dist/client/assets/dist-73RPdcyU.js +1 -0
- package/dist/client/assets/dist-BJ1bJs33.js +1 -0
- package/dist/client/assets/{dist-CYZmbokb.js → dist-BK9QvQrc.js} +9 -9
- package/dist/client/assets/dist-BPHLJ09L.js +11 -0
- package/dist/client/assets/dist-B_yQaEv_.js +6 -0
- package/dist/client/assets/dist-BeajGMhO.js +1 -0
- package/dist/client/assets/{dist-JYEDRrPT.js → dist-Bw3VIE_v.js} +1 -1
- package/dist/client/assets/dist-ByrG2qBA.js +1 -0
- package/dist/client/assets/dist-C5NgpdG5.js +1 -0
- package/dist/client/assets/dist-CH-sQu3r.js +1 -0
- package/dist/client/assets/dist-CKS_-are.js +1 -0
- package/dist/client/assets/{dist-MvcV4190.js → dist-CdmKGRGr.js} +1 -1
- package/dist/client/assets/{dist-DGb27Dl6.js → dist-CietwTVS.js} +1 -1
- package/dist/client/assets/dist-D37Hyu3h.js +9 -0
- package/dist/client/assets/dist-D3fehAZ2.js +2 -0
- package/dist/client/assets/dist-D5wCC32z.js +1 -0
- package/dist/client/assets/{dist-DCzuX1AF.js → dist-DDUn5zTz.js} +1 -1
- package/dist/client/assets/dist-DGEbEfw4.js +1 -0
- package/dist/client/assets/dist-DVy5woUy.js +1 -0
- package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZAAWTG-.js} +1 -1
- package/dist/client/assets/{dist-Drp8uu5J.js → dist-Dir5dDKZ.js} +1 -1
- package/dist/client/assets/dist-Dl_tpDSr.js +1 -0
- package/dist/client/assets/dist-DuH3ZrHo.js +1 -0
- package/dist/client/assets/dist-DutPuFmC.js +1 -0
- package/dist/client/assets/{dist-D2AQ-76g.js → dist-LQ4kDCKz.js} +1 -1
- package/dist/client/assets/{dist-C11hHqv_.js → dist-RKBTaQMb.js} +12 -12
- package/dist/client/assets/dist-Tw-Fxpeb.js +1 -0
- package/dist/client/assets/dist-eBx8q6Sy.js +1 -0
- package/dist/client/assets/{dist-DK5Gs2wa.js → dist-mj2Ht75J.js} +1 -1
- package/dist/client/assets/{dist-CnL7Y2sM.js → dist-netZOQtD.js} +1 -1
- package/dist/client/assets/{drawio-editor-ClE8jpoG.js → drawio-editor-BjrxTe3l.js} +1 -1
- package/dist/client/assets/drawioEditor-DUVZjSSr.js +2 -0
- package/dist/client/assets/{ebnfDiagram-BXEA7PRR-nw0wmA6y.js → ebnfDiagram-PWID7BFC-OXa8VEp1.js} +1 -1
- package/dist/client/assets/editor-session-DfZuRgB_.js +54 -0
- package/dist/client/assets/embedded-editor-base-DvMynhKu.css +1 -0
- package/dist/client/assets/embedpdf-BmE13ZZ0.js +1 -0
- package/dist/client/assets/embedpdf-DA4bA119-BPqZxwjc.js +2363 -0
- package/dist/client/assets/{en-7NFCYGKX-BrgYZAEe.js → en-SJ2YIMFF-CJBL3nkq.js} +1 -1
- package/dist/client/assets/erDiagram-SSCWMZ5O-C-iy6VkX.js +99 -0
- package/dist/client/assets/es-CAsWR6hG.js +22 -0
- package/dist/client/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
- package/dist/client/assets/excalidraw-editor-BlcN6umJ.css +1 -0
- package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +9 -0
- package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
- package/dist/client/assets/excalidrawEditor-B0jOK3lv.js +2 -0
- package/dist/client/assets/export-page-Bs3xE1fg.js +57 -0
- package/dist/client/assets/exportDocument-Dg9MjkIk.js +2 -0
- package/dist/client/assets/exportDocument-PnYU3v1s.css +1 -0
- package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
- package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
- package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +1 -0
- package/dist/client/assets/{ganttDiagram-PKOTCBZU-BnM97BCT.js → ganttDiagram-EL5Y4UJY-DtKHAbez.js} +1 -1
- package/dist/client/assets/gitGraph-4MIJSDKK-DYlXv_yJ.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-DS77QQ5N-BQpQhjuN.js → gitGraphDiagram-WWUBYQGX-oue8fQ5o.js} +1 -1
- package/dist/client/assets/github-dark.min-BV82VuKq.css +1 -0
- package/dist/client/assets/github.min-v3dd8CNH.css +1 -0
- package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +1 -0
- package/dist/client/assets/index-BIpgxyGZ.css +1 -0
- package/dist/client/assets/index-Z5sI0Bj-.js +2 -0
- package/dist/client/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
- package/dist/client/assets/{infoDiagram-6WML65LV-DYB8HCDQ.js → infoDiagram-RXCK75RN-BMzo6KIn.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-Coz_P3t6.js → ishikawaDiagram-5VMMS53U-BIM3h287.js} +1 -1
- package/dist/client/assets/{journeyDiagram-NVQOT4AX-B6edChce.js → journeyDiagram-EYS64GPL-mF6Lh8O-.js} +1 -1
- package/dist/client/assets/{kanban-definition-27J2QSJJ-BFd9-cLc.js → kanban-definition-3QL26DDD-DLN2NyoR.js} +2 -2
- package/dist/client/assets/{line-B_S_S8kV.js → line-BfTWJmgf.js} +1 -1
- package/dist/client/assets/main-Dwnj9Gaq.js +903 -0
- package/dist/client/assets/{mermaid-parser.core-CUVdebAV.js → mermaid-parser.core-Bl0efOMt.js} +2 -2
- package/dist/client/assets/mermaid.core-CLZBlpIW.js +11 -0
- package/dist/client/assets/{mindmap-definition-FAOFIHXS-C9PQ38Al.js → mindmap-definition-FBJOCRG2-CRyHaHTk.js} +1 -1
- package/dist/client/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
- package/dist/client/assets/pdfium-RAgkpwfK.wasm +0 -0
- package/dist/client/assets/{pegDiagram-VL7TDLO6-BMZUrSaU.js → pegDiagram-XKGWAZYB-B31fKPR2.js} +1 -1
- package/dist/client/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
- package/dist/client/assets/{pieDiagram-7S7Q4E2Y-h8kbCL9o.js → pieDiagram-E7YTZNPT-B5OZPb88.js} +1 -1
- package/dist/client/assets/preview-render-compiler-BApvlLLo.js +35 -0
- package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +1 -0
- package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
- package/dist/client/assets/preview-render-worker-BmN79bVc.js +60 -0
- package/dist/client/assets/prod-BiYPMR1i.js +177 -0
- package/dist/client/assets/prod-VxdGZ5Ly.js +1 -0
- package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-CkElyfMi.js → quadrantDiagram-AXDQQJYC-Drrw14YH.js} +1 -1
- package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +12 -0
- package/dist/client/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
- package/dist/client/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
- package/dist/client/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
- package/dist/client/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
- package/dist/client/assets/{railroadDiagram-AXF67PYL-c75gnikD.js → railroadDiagram-O6MQD6OU-BPifP0MB.js} +1 -1
- package/dist/client/assets/{requirementDiagram-LRYGKXZP-mNxKLuHS.js → requirementDiagram-EFPCY7ZU-CRZG6mRB.js} +2 -2
- package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
- package/dist/client/assets/{sankeyDiagram-W5VNT64P-0OhplksI.js → sankeyDiagram-P5KCCOFB-DvarEJpo.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-SI44F4Z6-BJ_yAocC.js → sequenceDiagram-WJ2MYXX4-B4oWlb-w.js} +1 -1
- package/dist/client/assets/{stateDiagram-OKZ733FA-Cc83O86s.js → stateDiagram-HBIQ2CUA-qbHIIqRZ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +1 -0
- package/dist/client/assets/swimlanes-XN3QIQJK-xB6CtUGj.js +1 -0
- package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +8 -0
- package/dist/client/assets/{timeline-definition-Z64GVDOM-BWDAGTHz.js → timeline-definition-24CTP7MA-Lj_yTNlY.js} +1 -1
- package/dist/client/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
- package/dist/client/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
- package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
- package/dist/client/assets/{vennDiagram-T6HMQDX7-LOSsnlMB.js → vennDiagram-4TSXK5OY-BekRp5Y0.js} +1 -1
- package/dist/client/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
- package/dist/client/assets/{wardleyDiagram-T6FBY63Y-BxII3foY.js → wardleyDiagram-VM6X3IG4-kdnV_Hdm.js} +1 -1
- package/dist/client/assets/worker-engine-m0rlHn8R-S8mpn_O0.js +17369 -0
- package/dist/client/assets/{xychartDiagram-ELKLHX3M-B-pTEjju.js → xychartDiagram-S5SC5T6Z-DufGwNSB.js} +3 -3
- package/dist/client/drawio-editor.html +1 -1
- package/dist/client/excalidraw-editor.html +1 -1
- package/dist/client/export-document.html +3 -3
- package/dist/client/index.html +30 -8
- package/docker-compose.yml +42 -0
- package/package.json +32 -18
- package/scripts/local-structurizr-compose.mjs +101 -0
- package/src/client/app/index.html +27 -5
- package/src/client/application/app-shell/comments-feature.js +21 -13
- package/src/client/application/app-shell/git-feature.js +14 -2
- package/src/client/application/app-shell/presence-feature.js +4 -3
- package/src/client/application/app-shell/ui-feature-shell.js +109 -2
- package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
- package/src/client/application/app-shell/workspace-feature.js +5 -1
- package/src/client/application/app-shell-elements.js +3 -0
- package/src/client/application/pdf-preview-controller.js +75 -0
- package/src/client/application/preview-render-compiler.js +3 -0
- package/src/client/application/quick-switcher-loader.js +1 -1
- package/src/client/application/structurizr-preview-controller.js +395 -0
- package/src/client/application/workspace-coordinator.js +25 -3
- package/src/client/application/workspace-preview-controller.js +88 -2
- package/src/client/assets/vendor/highlight/github-dark.min.css +2 -10
- package/src/client/assets/vendor/highlight/github.min.css +2 -10
- package/src/client/bootstrap/collabmd-app-shell.js +39 -6
- package/src/client/browser-utils.js +44 -0
- package/src/client/domain/runtime-paths.js +26 -5
- package/src/client/domain/structurizr-language.js +99 -0
- package/src/client/domain/vault-paths.js +48 -0
- package/src/client/excalidraw-editor.js +844 -51
- package/src/client/export/export-host.js +1 -1
- package/src/client/export/export-page.js +6 -2
- package/src/client/export/export-pipeline.js +60 -1
- package/src/client/infrastructure/browser-preferences-port.js +10 -0
- package/src/client/infrastructure/comment-thread-store.js +34 -13
- package/src/client/infrastructure/editor-session.js +10 -0
- package/src/client/infrastructure/editor-view-adapter.js +46 -2
- package/src/client/infrastructure/excalidraw-room-client.js +103 -14
- package/src/client/infrastructure/git-api-client.js +5 -0
- package/src/client/infrastructure/vault-api-client.js +26 -0
- package/src/client/presentation/comment-ui/comment-ui-render.js +4 -0
- package/src/client/presentation/comment-ui/comment-ui-shared.js +4 -0
- package/src/client/presentation/comment-ui-controller.js +2 -0
- package/src/client/presentation/drawio-embed-controller.js +9 -102
- package/src/client/presentation/drawio-viewer.js +118 -0
- package/src/client/presentation/excalidraw-embed-controller.js +81 -8
- package/src/client/presentation/file-action-controller.js +102 -0
- package/src/client/presentation/file-explorer-controller.js +0 -5
- package/src/client/presentation/file-explorer-view.js +51 -73
- package/src/client/presentation/file-icon-svg.js +40 -0
- package/src/client/presentation/file-tree-state.js +6 -3
- package/src/client/presentation/git-diff-view-controller.js +731 -3
- package/src/client/presentation/git-panel-controller.js +2 -2
- package/src/client/presentation/outline-controller.js +86 -0
- package/src/client/presentation/quick-switcher-controller.js +73 -22
- package/src/client/presentation/quick-switcher-text-search.js +1 -1
- package/src/client/styles/components/navigation.css +11 -1
- package/src/client/styles/features/diagram-preview.css +76 -0
- package/src/client/styles/features/diff-special.css +309 -0
- package/src/client/styles/features/preview-markdown.css +25 -0
- package/src/client/styles/features/quick-switcher.css +8 -0
- package/src/client/styles/foundation/themes.css +7 -0
- package/src/client/styles/layout/shell.css +40 -1
- package/src/client/styles/layout/sidebar.css +42 -4
- package/src/client/styles/layout/view-modes.css +1 -0
- package/src/client/styles/overrides/highlightjs.css +20 -4
- package/src/client/styles/style.css +1 -0
- package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
- package/src/domain/comment-threads.js +63 -5
- package/src/domain/excalidraw-room-codec.js +6 -4
- package/src/domain/file-kind.js +35 -3
- package/src/server/config/env.js +79 -0
- package/src/server/create-app-server.js +12 -0
- package/src/server/domain/bases/base-definition.js +1 -1
- package/src/server/domain/bases/base-expression-runtime.js +1 -4
- package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
- package/src/server/domain/bases/base-transform.js +1 -4
- package/src/server/domain/collaboration/collaboration-room.js +55 -21
- package/src/server/domain/comment-overview.js +15 -5
- package/src/server/domain/ripgrep-search-service.js +4 -0
- package/src/server/infrastructure/git/diff-service.js +5 -0
- package/src/server/infrastructure/git/git-service.js +44 -11
- package/src/server/infrastructure/git/history-service.js +54 -0
- package/src/server/infrastructure/git/image-mime.js +14 -0
- package/src/server/infrastructure/git/untracked-files.js +28 -1
- package/src/server/infrastructure/http/create-git-api-command-handler.js +3 -1
- package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
- package/src/server/infrastructure/http/create-request-handler.js +16 -23
- package/src/server/infrastructure/http/create-static-handler.js +5 -2
- package/src/server/infrastructure/http/create-structurizr-api-handler.js +147 -0
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +66 -4
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
- package/src/server/infrastructure/http/http-request-helpers.js +22 -0
- package/src/server/infrastructure/persistence/path-utils.js +1 -1
- package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
- package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
- package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +400 -0
- package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
- package/dist/client/assets/architecture-TIHT7OUA-CsHfPErM.js +0 -1
- package/dist/client/assets/base-CxPEiHuy.js +0 -1
- package/dist/client/assets/blockDiagram-VBNYF7ZC-CipJt9Jl.js +0 -132
- package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
- package/dist/client/assets/c4Diagram-5PPSVZJV-B5ktVx6g.js +0 -10
- package/dist/client/assets/channel-4Jkmq93I.js +0 -1
- package/dist/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/dist/client/assets/chunk-7Z6QIM7H-BP9Pdsm9.js +0 -10
- package/dist/client/assets/chunk-ERSWUTIN-ME9RVNel.js +0 -41
- package/dist/client/assets/chunk-I66GZJ75-CVFiiffQ.js +0 -127
- package/dist/client/assets/chunk-J7OUQ5F2-4CEb7pvH.js +0 -2
- package/dist/client/assets/chunk-JQJVKLGR-cx8X_24v.js +0 -156
- package/dist/client/assets/chunk-QR6OTTB3-CDAA_5Ib.js +0 -62
- package/dist/client/assets/chunk-RYQCIY6F-D-a0zH8K.js +0 -1
- package/dist/client/assets/chunk-UBXNYLIW-Bhk-PbFK.js +0 -1
- package/dist/client/assets/chunk-W5SLKNZC-BdNSCxZ9.js +0 -1
- package/dist/client/assets/chunk-WRU74C26-h6_XkD3_.js +0 -70
- package/dist/client/assets/chunk-ZIRB5QZD-O39ZBUk3.js +0 -32
- package/dist/client/assets/classDiagram-JCYQIIEL-L7lsyW_w.js +0 -1
- package/dist/client/assets/classDiagram-v2-OCEON4UE-L7lsyW_w.js +0 -1
- package/dist/client/assets/comment-threads-DDegmaFO.js +0 -4
- package/dist/client/assets/cynefin-VYW2F7L2-Ds2Fw2Y0.js +0 -1
- package/dist/client/assets/dagre-Buvkdvvj.js +0 -1
- package/dist/client/assets/dagre-VZM6K2ZE-L1xArOGs.js +0 -4
- package/dist/client/assets/dist-BH87JoHx.js +0 -11
- package/dist/client/assets/dist-BIkPRZO7.js +0 -6
- package/dist/client/assets/dist-BqCsFpx4.js +0 -1
- package/dist/client/assets/dist-C2JHCt8U.js +0 -1
- package/dist/client/assets/dist-CZjTtmTu.js +0 -1
- package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
- package/dist/client/assets/dist-CqQEC8gR.js +0 -1
- package/dist/client/assets/dist-D4Rzqod1.js +0 -1
- package/dist/client/assets/dist-D5sn-mwk.js +0 -9
- package/dist/client/assets/dist-D7G-n87v.js +0 -1
- package/dist/client/assets/dist-DRqOrNgN.js +0 -1
- package/dist/client/assets/dist-DetkMS_4.js +0 -1
- package/dist/client/assets/dist-DhLvC8DS.js +0 -2
- package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
- package/dist/client/assets/dist-DtbjNB28.js +0 -1
- package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
- package/dist/client/assets/dist-GrygD2zE.js +0 -1
- package/dist/client/assets/dist-HmEpWUow.js +0 -1
- package/dist/client/assets/dist-RTa81Km7.js +0 -1
- package/dist/client/assets/drawioEditor-CXaTM9Ds.js +0 -2
- package/dist/client/assets/editor-session-GTwZS6Df.js +0 -16
- package/dist/client/assets/embedded-editor-base-CiNEc8vy.css +0 -1
- package/dist/client/assets/erDiagram-JOGREHBK-cYUpEKpf.js +0 -85
- package/dist/client/assets/eventmodeling-45OFAUF4-CLEiVx5s.js +0 -1
- package/dist/client/assets/excalidraw-editor-BZVM81BO.js +0 -9
- package/dist/client/assets/excalidraw-editor-CyGwOhLz.css +0 -1
- package/dist/client/assets/excalidraw-scene-C8n9CHsK.js +0 -176
- package/dist/client/assets/excalidrawEditor-DxJanCze.js +0 -2
- package/dist/client/assets/export-page-CUfws2T-.js +0 -39
- package/dist/client/assets/exportDocument-BCuxqXDY.js +0 -2
- package/dist/client/assets/exportDocument-DV-OX74E.css +0 -1
- package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
- package/dist/client/assets/flowDiagram-UKHOOZJN-D0ypP0Ez.js +0 -1
- package/dist/client/assets/gitGraph-TEB2WS4Q-CZFBqwW-.js +0 -1
- package/dist/client/assets/github-dark.min-Z5exjX_n.css +0 -1
- package/dist/client/assets/github.min-D2BCvnWf.css +0 -1
- package/dist/client/assets/image-QXRYHUNP-lfTHE3bb.js +0 -1
- package/dist/client/assets/index-C3YnUgMD.css +0 -1
- package/dist/client/assets/index-D2wXraJ1.js +0 -2
- package/dist/client/assets/info-DKCQHKI2--1InqQeT.js +0 -1
- package/dist/client/assets/main-DtiGz7uI.js +0 -795
- package/dist/client/assets/map-BaFkSB1l.js +0 -1
- package/dist/client/assets/mermaid.core-DN-lF51U.js +0 -11
- package/dist/client/assets/packet-7NZHBO7P-BGMXB0XL.js +0 -1
- package/dist/client/assets/pie-RZYD4A2V-DgYg3tCt.js +0 -1
- package/dist/client/assets/preview-render-compiler-BSaQO_p3.js +0 -35
- package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
- package/dist/client/assets/preview-render-profile-BZzGSg6K.js +0 -22
- package/dist/client/assets/preview-render-worker-B42LWvrD.js +0 -60
- package/dist/client/assets/quick-switcher-controller-shW_6vPY.js +0 -12
- package/dist/client/assets/radar-I7S5WNFK-Ag-zp_Rq.js +0 -1
- package/dist/client/assets/railroad-3IZDKUUU-aibJDIDV.js +0 -1
- package/dist/client/assets/railroad-abnf-AHOZXSZD-BBMEuvIl.js +0 -1
- package/dist/client/assets/railroad-ebnf-EBAXGLYW-Be1nseej.js +0 -1
- package/dist/client/assets/railroad-peg-LSFZ7HO6-DIvD-Eyb.js +0 -1
- package/dist/client/assets/room-CQLoqtMo.js +0 -1
- package/dist/client/assets/runtime-config-DSGmGE9V.js +0 -1
- package/dist/client/assets/stateDiagram-v2-UEYNNEHI-B1-BufUk.js +0 -1
- package/dist/client/assets/swimlanes-SLNWSIFB-oVTWEJIj.js +0 -2
- package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-C5OTVwEu.js +0 -8
- package/dist/client/assets/treeView-QDETBFTQ-uHZ-3Fz8.js +0 -1
- package/dist/client/assets/treemap-6X3UGDF4-BnUKyy3l.js +0 -1
- package/dist/client/assets/vault-api-client-C6bcGwy-.js +0 -5
- package/dist/client/assets/wardley-OPB4EBWU-Da7TLC2F.js +0 -1
- package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
reconcileElements,
|
|
7
7
|
restoreAppState,
|
|
8
8
|
restoreElements,
|
|
9
|
+
sceneCoordsToViewportCoords,
|
|
9
10
|
} from '@excalidraw/excalidraw';
|
|
11
|
+
import { getElementBounds } from '@excalidraw/element';
|
|
10
12
|
import '@excalidraw/excalidraw/index.css';
|
|
11
13
|
|
|
12
14
|
import {
|
|
@@ -17,6 +19,9 @@ import {
|
|
|
17
19
|
resolveLocalAwarenessUser,
|
|
18
20
|
} from './domain/excalidraw-collaboration.js';
|
|
19
21
|
import './styles/surfaces/embedded-editor-base.css';
|
|
22
|
+
import './styles/features/comment-markdown.css';
|
|
23
|
+
import './styles/features/comment-overview.css';
|
|
24
|
+
import './styles/features/comments-drawer.css';
|
|
20
25
|
import './styles/surfaces/excalidraw-editor.css';
|
|
21
26
|
import {
|
|
22
27
|
applySceneUpdateWithFiles,
|
|
@@ -27,6 +32,11 @@ import {
|
|
|
27
32
|
parseSceneJson,
|
|
28
33
|
sceneToInitialData,
|
|
29
34
|
} from './domain/excalidraw-scene.js';
|
|
35
|
+
import {
|
|
36
|
+
createCommentOverviewThread,
|
|
37
|
+
formatAnchorLabel,
|
|
38
|
+
getLatestMessage,
|
|
39
|
+
} from './presentation/comment-ui/comment-ui-shared.js';
|
|
30
40
|
import {
|
|
31
41
|
buildReconciledExcalidrawSceneUpdate,
|
|
32
42
|
} from './domain/excalidraw-scene-reconcile.js';
|
|
@@ -76,17 +86,39 @@ let apiStateCleanupCallbacks = [];
|
|
|
76
86
|
let collaboratorRenderFrame = 0;
|
|
77
87
|
let queuedCollaborators = null;
|
|
78
88
|
let initialViewportFitPending = true;
|
|
89
|
+
let viewportFitGeneration = 0;
|
|
79
90
|
let previewViewportFitTimerId = 0;
|
|
91
|
+
let previewViewportFitRetryTimerId = 0;
|
|
80
92
|
let roomClient = null;
|
|
81
93
|
let roomClientGeneration = 0;
|
|
82
94
|
let reactRoot = null;
|
|
83
95
|
let editorRenderKey = 0;
|
|
84
96
|
let skipRoomDisconnectOnUnmount = false;
|
|
85
97
|
let roomConnectionState = EXCALIDRAW_ROOM_CONNECTION_STATE.CONNECTING;
|
|
98
|
+
let diagramCommentThreads = [];
|
|
99
|
+
let pendingDiagramCommentFocus = null;
|
|
100
|
+
let diagramCommentFocusRequestId = 0;
|
|
86
101
|
let parkRequestedWhileBlocked = false;
|
|
87
102
|
const pendingDisconnectRequestIds = new Set();
|
|
88
103
|
const reportedFileConflictSignatures = new Set();
|
|
89
104
|
|
|
105
|
+
function getMountedExcalidrawAPI() {
|
|
106
|
+
return excalidrawAPI && !excalidrawAPI.isDestroyed ? excalidrawAPI : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function requestDiagramCommentFocus(threadId) {
|
|
110
|
+
const normalizedThreadId = String(threadId ?? '').trim();
|
|
111
|
+
if (!normalizedThreadId) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pendingDiagramCommentFocus = {
|
|
116
|
+
requestId: ++diagramCommentFocusRequestId,
|
|
117
|
+
threadId: normalizedThreadId,
|
|
118
|
+
};
|
|
119
|
+
renderExcalidrawApp();
|
|
120
|
+
}
|
|
121
|
+
|
|
90
122
|
if (diagnostics.enabled) {
|
|
91
123
|
window.__COLLABMD_EXCALIDRAW_DEBUG__ = diagnostics;
|
|
92
124
|
}
|
|
@@ -120,6 +152,213 @@ function getAuthorityBannerText() {
|
|
|
120
152
|
return '';
|
|
121
153
|
}
|
|
122
154
|
|
|
155
|
+
function getSelectedDiagramElement() {
|
|
156
|
+
const api = getMountedExcalidrawAPI();
|
|
157
|
+
const selectedElementIds = api?.getAppState?.()?.selectedElementIds || {};
|
|
158
|
+
const selectedElements = api?.getSceneElementsIncludingDeleted?.()
|
|
159
|
+
?.filter((element) => selectedElementIds[element.id] && !element.isDeleted) ?? [];
|
|
160
|
+
return selectedElements.length === 1 ? selectedElements[0] : null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeDiagramLabelText(value, maxLength = 72) {
|
|
164
|
+
const text = typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
|
|
165
|
+
return text.length > maxLength ? `${text.slice(0, maxLength - 1)}…` : text;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getDiagramElementContent(element, api = getMountedExcalidrawAPI()) {
|
|
169
|
+
const directText = normalizeDiagramLabelText(element?.text);
|
|
170
|
+
if (directText) {
|
|
171
|
+
return directText;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const sceneElements = api?.getSceneElementsIncludingDeleted?.() || [];
|
|
175
|
+
const boundElementIds = new Set(
|
|
176
|
+
Array.isArray(element?.boundElements)
|
|
177
|
+
? element.boundElements.map((boundElement) => boundElement?.id).filter(Boolean)
|
|
178
|
+
: [],
|
|
179
|
+
);
|
|
180
|
+
if (boundElementIds.size === 0) {
|
|
181
|
+
return '';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return normalizeDiagramLabelText(
|
|
185
|
+
sceneElements
|
|
186
|
+
.filter((sceneElement) => boundElementIds.has(sceneElement.id) && !sceneElement.isDeleted)
|
|
187
|
+
.map((sceneElement) => sceneElement.text)
|
|
188
|
+
.filter((text) => typeof text === 'string' && text.trim())
|
|
189
|
+
.join(' '),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function formatDiagramAnchorLabel(type, content = '') {
|
|
194
|
+
const normalizedType = typeof type === 'string' && type.trim() ? type.trim() : 'element';
|
|
195
|
+
const typeLabel = normalizedType === 'element'
|
|
196
|
+
? 'Diagram element'
|
|
197
|
+
: `${normalizedType.charAt(0).toUpperCase()}${normalizedType.slice(1)} element`;
|
|
198
|
+
const normalizedContent = normalizeDiagramLabelText(content);
|
|
199
|
+
return normalizedContent ? `${typeLabel} · ${normalizedContent}` : typeLabel;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getDiagramAnchorLabel(thread) {
|
|
203
|
+
const snapshot = thread?.anchorSnapshot;
|
|
204
|
+
const element = getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.()
|
|
205
|
+
?.find((candidate) => candidate.id === thread?.elementId && !candidate.isDeleted);
|
|
206
|
+
const elementContent = normalizeDiagramLabelText(snapshot?.text) || getDiagramElementContent(element);
|
|
207
|
+
const firstMessageContent = thread?.messages?.[0]?.body;
|
|
208
|
+
return formatDiagramAnchorLabel(snapshot?.type || element?.type, elementContent || firstMessageContent);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const diagramCommentTimeFormatter = new Intl.DateTimeFormat(undefined, {
|
|
212
|
+
day: 'numeric',
|
|
213
|
+
hour: 'numeric',
|
|
214
|
+
minute: '2-digit',
|
|
215
|
+
month: 'short',
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
function formatDiagramCommentTimestamp(value) {
|
|
219
|
+
if (!Number.isFinite(value)) {
|
|
220
|
+
return '';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
return diagramCommentTimeFormatter.format(new Date(value));
|
|
225
|
+
} catch {
|
|
226
|
+
return '';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function renderDiagramCommentOverviewThread(thread, activeThreadId, onOpen) {
|
|
231
|
+
const latestMessage = getLatestMessage(thread?.messages ?? []);
|
|
232
|
+
const messageCount = Array.isArray(thread?.messages) ? thread.messages.length : 0;
|
|
233
|
+
const button = createCommentOverviewThread({
|
|
234
|
+
authorName: latestMessage?.userName || thread?.createdByName || 'Anonymous',
|
|
235
|
+
buttonClassName: 'comment-overview-thread comments-drawer-item',
|
|
236
|
+
footerClassName: 'comment-overview-thread-footer comments-drawer-item-footer',
|
|
237
|
+
headerClassName: 'comment-overview-thread-header comments-drawer-item-header',
|
|
238
|
+
lineClassName: 'comment-overview-thread-line comments-drawer-item-title',
|
|
239
|
+
lineLabel: formatAnchorLabel(thread),
|
|
240
|
+
messageCount,
|
|
241
|
+
previewBody: latestMessage?.body || '',
|
|
242
|
+
previewClassName: 'comment-markdown comment-overview-thread-preview comments-drawer-item-preview',
|
|
243
|
+
quote: thread?.anchorQuote || thread?.anchorSnapshot?.text || 'Diagram element',
|
|
244
|
+
quoteClassName: 'comment-overview-thread-quote comments-drawer-item-quote',
|
|
245
|
+
timestamp: formatDiagramCommentTimestamp(latestMessage?.createdAt),
|
|
246
|
+
});
|
|
247
|
+
button.classList.toggle('is-active', thread.id === activeThreadId);
|
|
248
|
+
|
|
249
|
+
return React.createElement('button', {
|
|
250
|
+
className: button.className,
|
|
251
|
+
dangerouslySetInnerHTML: { __html: button.innerHTML },
|
|
252
|
+
key: thread.id,
|
|
253
|
+
onClick: () => onOpen(thread.id),
|
|
254
|
+
type: 'button',
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function DiagramCommentIcon({ add = false }) {
|
|
259
|
+
const paths = [React.createElement('path', {
|
|
260
|
+
d: 'M3 4.75A1.75 1.75 0 0 1 4.75 3h6.5A1.75 1.75 0 0 1 13 4.75v4.5A1.75 1.75 0 0 1 11.25 11H8.9L6.5 13v-2H4.75A1.75 1.75 0 0 1 3 9.25v-4.5Z',
|
|
261
|
+
fill: 'none',
|
|
262
|
+
key: 'bubble',
|
|
263
|
+
stroke: 'currentColor',
|
|
264
|
+
strokeLinejoin: 'round',
|
|
265
|
+
strokeWidth: '1.35',
|
|
266
|
+
})];
|
|
267
|
+
if (add) {
|
|
268
|
+
paths.push(React.createElement('path', {
|
|
269
|
+
d: 'M8 5.5v3.5M6.25 7.25h3.5',
|
|
270
|
+
fill: 'none',
|
|
271
|
+
key: 'plus',
|
|
272
|
+
stroke: 'currentColor',
|
|
273
|
+
strokeLinecap: 'round',
|
|
274
|
+
strokeWidth: '1.35',
|
|
275
|
+
}));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return React.createElement('svg', {
|
|
279
|
+
'aria-hidden': 'true',
|
|
280
|
+
className: 'diagram-comment-icon',
|
|
281
|
+
fill: 'none',
|
|
282
|
+
focusable: 'false',
|
|
283
|
+
viewBox: '0 0 16 16',
|
|
284
|
+
}, paths);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function getDiagramCommentMarkerPosition(thread, api = getMountedExcalidrawAPI()) {
|
|
288
|
+
if (!api || thread?.anchorKind !== 'diagram-element') {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let scenePoint = thread.anchorPoint;
|
|
293
|
+
const element = api.getSceneElementsIncludingDeleted?.()
|
|
294
|
+
?.find((candidate) => candidate.id === thread.elementId && !candidate.isDeleted);
|
|
295
|
+
if (element) {
|
|
296
|
+
try {
|
|
297
|
+
const bounds = getElementBounds(element, api.getSceneElementsMapIncludingDeleted?.());
|
|
298
|
+
scenePoint = {
|
|
299
|
+
x: bounds[2],
|
|
300
|
+
y: bounds[1],
|
|
301
|
+
};
|
|
302
|
+
} catch {
|
|
303
|
+
scenePoint = thread.anchorPoint;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const x = Number(scenePoint?.x);
|
|
308
|
+
const y = Number(scenePoint?.y);
|
|
309
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const appState = api.getAppState?.();
|
|
314
|
+
if (!appState?.zoom || !Number.isFinite(appState.scrollX) || !Number.isFinite(appState.scrollY)) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return sceneCoordsToViewportCoords(
|
|
319
|
+
{ sceneX: x, sceneY: y },
|
|
320
|
+
{
|
|
321
|
+
offsetLeft: Number.isFinite(appState.offsetLeft) ? appState.offsetLeft : 0,
|
|
322
|
+
offsetTop: Number.isFinite(appState.offsetTop) ? appState.offsetTop : 0,
|
|
323
|
+
scrollX: appState.scrollX,
|
|
324
|
+
scrollY: appState.scrollY,
|
|
325
|
+
zoom: appState.zoom,
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function focusDiagramCommentThread(thread) {
|
|
331
|
+
const api = getMountedExcalidrawAPI();
|
|
332
|
+
const element = api?.getSceneElementsIncludingDeleted?.()
|
|
333
|
+
?.find((candidate) => candidate.id === thread?.elementId && !candidate.isDeleted);
|
|
334
|
+
if (!api || !element) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
api.updateScene({
|
|
339
|
+
appState: {
|
|
340
|
+
selectedElementIds: { [element.id]: true },
|
|
341
|
+
},
|
|
342
|
+
captureUpdate: CaptureUpdateAction.NEVER,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
if (typeof api.setViewport !== 'function') {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
suppressViewportBroadcast = true;
|
|
350
|
+
try {
|
|
351
|
+
api.setViewport({
|
|
352
|
+
animation: false,
|
|
353
|
+
fit: 'contain',
|
|
354
|
+
offsets: { ui: true },
|
|
355
|
+
target: element,
|
|
356
|
+
});
|
|
357
|
+
} finally {
|
|
358
|
+
releaseViewportBroadcastSuppressionAfterPaint();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
123
362
|
function applyDocumentMode(mode = currentDocument.mode) {
|
|
124
363
|
document.body.dataset.documentMode = normalizeDocumentMode(mode);
|
|
125
364
|
}
|
|
@@ -140,6 +379,16 @@ function createRoomClient(filePath) {
|
|
|
140
379
|
|
|
141
380
|
queueCollaboratorsRender(collaborators);
|
|
142
381
|
},
|
|
382
|
+
onCommentThreadsChange: (threads) => {
|
|
383
|
+
if (generation !== roomClientGeneration) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
diagramCommentThreads = Array.isArray(threads) ? threads : [];
|
|
388
|
+
if (reactRoot) {
|
|
389
|
+
renderExcalidrawApp();
|
|
390
|
+
}
|
|
391
|
+
},
|
|
143
392
|
onConnectionStateChange: (event) => {
|
|
144
393
|
if (generation !== roomClientGeneration) {
|
|
145
394
|
return;
|
|
@@ -164,7 +413,7 @@ function createRoomClient(filePath) {
|
|
|
164
413
|
};
|
|
165
414
|
}
|
|
166
415
|
|
|
167
|
-
function buildExcalidrawProps({ initialData } = {}) {
|
|
416
|
+
function buildExcalidrawProps({ initialData, renderTopRightUI, viewModeEnabled } = {}) {
|
|
168
417
|
const props = {
|
|
169
418
|
onMount: handleEditorMount,
|
|
170
419
|
onInitialize: (api) => {
|
|
@@ -172,6 +421,7 @@ function buildExcalidrawProps({ initialData } = {}) {
|
|
|
172
421
|
},
|
|
173
422
|
onUnmount: () => {
|
|
174
423
|
clearEditorApiStateBindings();
|
|
424
|
+
clearPreviewViewportFitTimers();
|
|
175
425
|
if (skipRoomDisconnectOnUnmount) {
|
|
176
426
|
skipRoomDisconnectOnUnmount = false;
|
|
177
427
|
} else {
|
|
@@ -189,12 +439,18 @@ function buildExcalidrawProps({ initialData } = {}) {
|
|
|
189
439
|
onPointerUpdate: (payload) => {
|
|
190
440
|
roomClient?.scheduleLocalPointerAwareness(payload);
|
|
191
441
|
},
|
|
442
|
+
onPointerDown: () => {
|
|
443
|
+
initialViewportFitPending = false;
|
|
444
|
+
stopFollowingFromLocalInteraction();
|
|
445
|
+
},
|
|
446
|
+
renderTopRightUI,
|
|
192
447
|
historyOptions: {
|
|
193
448
|
traversal: 'single-entry',
|
|
194
449
|
},
|
|
195
450
|
onHistoryAction: handleHistoryAction,
|
|
196
451
|
theme: currentTheme,
|
|
197
452
|
...getDocumentViewState(),
|
|
453
|
+
...(typeof viewModeEnabled === 'boolean' ? { viewModeEnabled } : {}),
|
|
198
454
|
UIOptions: {
|
|
199
455
|
canvasActions: {
|
|
200
456
|
export: false,
|
|
@@ -212,6 +468,425 @@ function buildExcalidrawProps({ initialData } = {}) {
|
|
|
212
468
|
return props;
|
|
213
469
|
}
|
|
214
470
|
|
|
471
|
+
const diagramCommentsContext = React.createContext(null);
|
|
472
|
+
|
|
473
|
+
function DiagramCommentsToolbar() {
|
|
474
|
+
const context = React.useContext(diagramCommentsContext);
|
|
475
|
+
if (!context?.visible || !context.room) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const addCommentButton = context.selectedElement
|
|
480
|
+
? React.createElement('button', {
|
|
481
|
+
'aria-label': 'Add comment',
|
|
482
|
+
className: 'diagram-comment-add sidebar-trigger',
|
|
483
|
+
'data-testid': 'diagram-add-comment',
|
|
484
|
+
disabled: !context.canWrite,
|
|
485
|
+
key: 'add',
|
|
486
|
+
onClick: context.openComposer,
|
|
487
|
+
title: context.canWrite ? 'Add comment to selected element' : 'Reconnect to add a comment',
|
|
488
|
+
type: 'button',
|
|
489
|
+
}, React.createElement(DiagramCommentIcon, { add: true }))
|
|
490
|
+
: null;
|
|
491
|
+
|
|
492
|
+
return React.createElement('div', {
|
|
493
|
+
'aria-label': 'Diagram comments',
|
|
494
|
+
className: 'diagram-comments-toolbar',
|
|
495
|
+
role: 'toolbar',
|
|
496
|
+
}, [
|
|
497
|
+
addCommentButton,
|
|
498
|
+
React.createElement('button', {
|
|
499
|
+
'aria-expanded': context.drawerOpen,
|
|
500
|
+
'aria-label': 'Comments',
|
|
501
|
+
className: `diagram-comment-button sidebar-trigger${context.drawerOpen ? ' active' : ''}`,
|
|
502
|
+
'data-testid': 'diagram-comments-toggle',
|
|
503
|
+
key: 'toggle',
|
|
504
|
+
onClick: context.toggleDrawer,
|
|
505
|
+
title: context.drawerOpen ? 'Close comments' : 'Open comments',
|
|
506
|
+
type: 'button',
|
|
507
|
+
}, [
|
|
508
|
+
React.createElement(DiagramCommentIcon, { key: 'icon' }),
|
|
509
|
+
context.threads.length > 0
|
|
510
|
+
? React.createElement('span', {
|
|
511
|
+
className: 'diagram-comment-count',
|
|
512
|
+
key: 'count',
|
|
513
|
+
}, String(context.threads.length))
|
|
514
|
+
: null,
|
|
515
|
+
]),
|
|
516
|
+
]);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const DiagramCommentsExcalidraw = React.memo(function DiagramCommentsExcalidraw({ initialData = null, renderKey = 0, viewModeEnabled }) {
|
|
520
|
+
const renderTopRightUI = React.useCallback(
|
|
521
|
+
() => React.createElement(DiagramCommentsToolbar),
|
|
522
|
+
[],
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
return React.createElement(Excalidraw, {
|
|
526
|
+
key: `editor-${renderKey}`,
|
|
527
|
+
...buildExcalidrawProps({ initialData, renderTopRightUI, viewModeEnabled }),
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
function useCloseDiagramCommentsOnCanvas(drawerOpen, setDrawerOpen) {
|
|
532
|
+
React.useEffect(() => {
|
|
533
|
+
if (!drawerOpen) {
|
|
534
|
+
return undefined;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const closeOnCanvasPointerDown = (event) => {
|
|
538
|
+
if (!(event.target instanceof Element) || !event.target.closest('canvas.excalidraw__canvas')) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
setDrawerOpen(false);
|
|
543
|
+
};
|
|
544
|
+
const closeOnEscape = (event) => {
|
|
545
|
+
if (event.key !== 'Escape') {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
event.preventDefault();
|
|
550
|
+
event.stopPropagation();
|
|
551
|
+
setDrawerOpen(false);
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
document.addEventListener('pointerdown', closeOnCanvasPointerDown, true);
|
|
555
|
+
document.addEventListener('keydown', closeOnEscape, true);
|
|
556
|
+
return () => {
|
|
557
|
+
document.removeEventListener('pointerdown', closeOnCanvasPointerDown, true);
|
|
558
|
+
document.removeEventListener('keydown', closeOnEscape, true);
|
|
559
|
+
};
|
|
560
|
+
}, [drawerOpen, setDrawerOpen]);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function DiagramCommentsEditor({ apiId = '', canWrite = false, focusRequest = null, initialData = null, room = null, threads = [], visible = false }) {
|
|
564
|
+
const [activeThreadId, setActiveThreadId] = React.useState(null);
|
|
565
|
+
const [composerOpen, setComposerOpen] = React.useState(false);
|
|
566
|
+
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
|
567
|
+
const [draft, setDraft] = React.useState('');
|
|
568
|
+
const [replyDraft, setReplyDraft] = React.useState('');
|
|
569
|
+
const [, setViewportRevision] = React.useState(0);
|
|
570
|
+
|
|
571
|
+
React.useEffect(() => {
|
|
572
|
+
const api = getMountedExcalidrawAPI();
|
|
573
|
+
if (!api) {
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const refresh = () => setViewportRevision((revision) => revision + 1);
|
|
578
|
+
const cleanups = [
|
|
579
|
+
api.onChange?.(refresh),
|
|
580
|
+
api.onScrollChange?.(refresh),
|
|
581
|
+
].filter((cleanup) => typeof cleanup === 'function');
|
|
582
|
+
window.addEventListener('resize', refresh);
|
|
583
|
+
return () => {
|
|
584
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
585
|
+
window.removeEventListener('resize', refresh);
|
|
586
|
+
};
|
|
587
|
+
}, [apiId]);
|
|
588
|
+
|
|
589
|
+
useCloseDiagramCommentsOnCanvas(drawerOpen, setDrawerOpen);
|
|
590
|
+
|
|
591
|
+
React.useEffect(() => {
|
|
592
|
+
if (activeThreadId && !threads.some((thread) => thread.id === activeThreadId)) {
|
|
593
|
+
setActiveThreadId(null);
|
|
594
|
+
}
|
|
595
|
+
}, [activeThreadId, threads]);
|
|
596
|
+
|
|
597
|
+
React.useEffect(() => {
|
|
598
|
+
const threadId = focusRequest?.threadId;
|
|
599
|
+
const thread = threads.find((candidate) => candidate.id === threadId);
|
|
600
|
+
if (!thread) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
setDrawerOpen(true);
|
|
605
|
+
setComposerOpen(false);
|
|
606
|
+
setActiveThreadId(threadId);
|
|
607
|
+
setReplyDraft('');
|
|
608
|
+
focusDiagramCommentThread(thread);
|
|
609
|
+
}, [focusRequest?.requestId, threads]);
|
|
610
|
+
|
|
611
|
+
const selectedElement = getSelectedDiagramElement();
|
|
612
|
+
const activeThread = threads.find((thread) => thread.id === activeThreadId) ?? null;
|
|
613
|
+
const openComposer = () => {
|
|
614
|
+
if (!canWrite || !selectedElement) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
setDrawerOpen(true);
|
|
619
|
+
setComposerOpen(true);
|
|
620
|
+
setActiveThreadId(null);
|
|
621
|
+
};
|
|
622
|
+
const openThread = (threadId) => {
|
|
623
|
+
setDrawerOpen(true);
|
|
624
|
+
setComposerOpen(false);
|
|
625
|
+
setActiveThreadId(threadId);
|
|
626
|
+
setReplyDraft('');
|
|
627
|
+
};
|
|
628
|
+
const submitComposer = (event) => {
|
|
629
|
+
event.preventDefault();
|
|
630
|
+
const selectedElementForAnchor = getSelectedDiagramElement();
|
|
631
|
+
const elementContent = getDiagramElementContent(selectedElementForAnchor);
|
|
632
|
+
const threadId = room.createCommentThread({
|
|
633
|
+
body: draft,
|
|
634
|
+
element: elementContent
|
|
635
|
+
? { ...selectedElementForAnchor, text: elementContent }
|
|
636
|
+
: selectedElementForAnchor,
|
|
637
|
+
});
|
|
638
|
+
if (!threadId) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
setDraft('');
|
|
643
|
+
setComposerOpen(false);
|
|
644
|
+
setActiveThreadId(threadId);
|
|
645
|
+
};
|
|
646
|
+
const submitReply = (event) => {
|
|
647
|
+
event.preventDefault();
|
|
648
|
+
if (!activeThread) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const messageId = room.replyToCommentThread(activeThread.id, replyDraft);
|
|
653
|
+
if (!messageId) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
setReplyDraft('');
|
|
658
|
+
};
|
|
659
|
+
const resolveThread = () => {
|
|
660
|
+
if (!activeThread || !room.deleteCommentThread(activeThread.id)) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
setActiveThreadId(null);
|
|
665
|
+
};
|
|
666
|
+
const renderComposer = () => React.createElement('form', {
|
|
667
|
+
className: 'diagram-comment-form',
|
|
668
|
+
onSubmit: submitComposer,
|
|
669
|
+
}, [
|
|
670
|
+
React.createElement('p', {
|
|
671
|
+
className: 'diagram-comment-anchor-label',
|
|
672
|
+
key: 'anchor',
|
|
673
|
+
}, selectedElement
|
|
674
|
+
? `On ${formatDiagramAnchorLabel(selectedElement.type, getDiagramElementContent(selectedElement))}`
|
|
675
|
+
: 'On selected element'),
|
|
676
|
+
React.createElement('textarea', {
|
|
677
|
+
'aria-label': 'Comment',
|
|
678
|
+
autoFocus: true,
|
|
679
|
+
className: 'diagram-comment-input',
|
|
680
|
+
key: 'input',
|
|
681
|
+
maxLength: 2000,
|
|
682
|
+
onChange: (event) => setDraft(event.target.value),
|
|
683
|
+
placeholder: 'Add context, feedback, or a question…',
|
|
684
|
+
rows: 4,
|
|
685
|
+
value: draft,
|
|
686
|
+
}),
|
|
687
|
+
React.createElement('div', {
|
|
688
|
+
className: 'diagram-comment-form-actions',
|
|
689
|
+
key: 'actions',
|
|
690
|
+
}, [
|
|
691
|
+
React.createElement('button', {
|
|
692
|
+
className: 'diagram-comment-button is-secondary',
|
|
693
|
+
key: 'cancel',
|
|
694
|
+
onClick: () => setComposerOpen(false),
|
|
695
|
+
type: 'button',
|
|
696
|
+
}, 'Cancel'),
|
|
697
|
+
React.createElement('button', {
|
|
698
|
+
className: 'diagram-comment-button is-primary',
|
|
699
|
+
disabled: !draft.trim() || !canWrite,
|
|
700
|
+
key: 'submit',
|
|
701
|
+
type: 'submit',
|
|
702
|
+
}, 'Post comment'),
|
|
703
|
+
]),
|
|
704
|
+
]);
|
|
705
|
+
const renderThread = () => React.createElement(React.Fragment, null, [
|
|
706
|
+
React.createElement('button', {
|
|
707
|
+
className: 'diagram-comment-back',
|
|
708
|
+
key: 'back',
|
|
709
|
+
onClick: () => setActiveThreadId(null),
|
|
710
|
+
type: 'button',
|
|
711
|
+
}, '← All comments'),
|
|
712
|
+
React.createElement('p', {
|
|
713
|
+
className: 'diagram-comment-anchor-label',
|
|
714
|
+
key: 'label',
|
|
715
|
+
}, getDiagramAnchorLabel(activeThread)),
|
|
716
|
+
React.createElement('div', {
|
|
717
|
+
className: 'diagram-comment-messages',
|
|
718
|
+
key: 'messages',
|
|
719
|
+
}, activeThread.messages?.map((message) => React.createElement('article', {
|
|
720
|
+
className: 'diagram-comment-message',
|
|
721
|
+
key: message.id,
|
|
722
|
+
}, [
|
|
723
|
+
React.createElement('div', {
|
|
724
|
+
className: 'diagram-comment-message-meta',
|
|
725
|
+
key: 'meta',
|
|
726
|
+
}, message.userName || 'Anonymous'),
|
|
727
|
+
React.createElement('p', {
|
|
728
|
+
className: 'diagram-comment-message-body',
|
|
729
|
+
key: 'body',
|
|
730
|
+
}, message.body),
|
|
731
|
+
]))),
|
|
732
|
+
React.createElement('form', {
|
|
733
|
+
className: 'diagram-comment-form',
|
|
734
|
+
key: 'reply-form',
|
|
735
|
+
onSubmit: submitReply,
|
|
736
|
+
}, [
|
|
737
|
+
React.createElement('textarea', {
|
|
738
|
+
'aria-label': 'Reply',
|
|
739
|
+
className: 'diagram-comment-input',
|
|
740
|
+
disabled: !canWrite,
|
|
741
|
+
key: 'reply-input',
|
|
742
|
+
maxLength: 2000,
|
|
743
|
+
onChange: (event) => setReplyDraft(event.target.value),
|
|
744
|
+
placeholder: canWrite ? 'Reply…' : 'Reconnect to reply',
|
|
745
|
+
rows: 3,
|
|
746
|
+
value: replyDraft,
|
|
747
|
+
}),
|
|
748
|
+
React.createElement('div', {
|
|
749
|
+
className: 'diagram-comment-form-actions',
|
|
750
|
+
key: 'reply-actions',
|
|
751
|
+
}, [
|
|
752
|
+
React.createElement('button', {
|
|
753
|
+
className: 'diagram-comment-button is-primary',
|
|
754
|
+
disabled: !replyDraft.trim() || !canWrite,
|
|
755
|
+
type: 'submit',
|
|
756
|
+
}, 'Reply'),
|
|
757
|
+
React.createElement('button', {
|
|
758
|
+
className: 'diagram-comment-button is-danger',
|
|
759
|
+
disabled: !canWrite,
|
|
760
|
+
onClick: resolveThread,
|
|
761
|
+
type: 'button',
|
|
762
|
+
}, 'Resolve'),
|
|
763
|
+
]),
|
|
764
|
+
]),
|
|
765
|
+
]);
|
|
766
|
+
const renderDrawer = () => {
|
|
767
|
+
if (!drawerOpen) {
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const isListView = !composerOpen && !activeThread;
|
|
772
|
+
const content = composerOpen
|
|
773
|
+
? renderComposer()
|
|
774
|
+
: activeThread
|
|
775
|
+
? renderThread()
|
|
776
|
+
: threads.length > 0
|
|
777
|
+
? threads.map((thread) => renderDiagramCommentOverviewThread(thread, activeThreadId, openThread))
|
|
778
|
+
: React.createElement('div', {
|
|
779
|
+
className: 'comments-drawer-empty ui-empty-state ui-empty-state--compact',
|
|
780
|
+
}, [
|
|
781
|
+
React.createElement('p', {
|
|
782
|
+
className: 'ui-empty-state-title',
|
|
783
|
+
key: 'title',
|
|
784
|
+
}, 'No comments yet'),
|
|
785
|
+
React.createElement('p', {
|
|
786
|
+
className: 'ui-empty-state-copy',
|
|
787
|
+
key: 'copy',
|
|
788
|
+
}, 'Select an element and add the first comment.'),
|
|
789
|
+
]);
|
|
790
|
+
const drawerAddButton = selectedElement && !composerOpen
|
|
791
|
+
? React.createElement('button', {
|
|
792
|
+
'aria-label': 'Add comment',
|
|
793
|
+
className: 'diagram-comment-add',
|
|
794
|
+
disabled: !canWrite,
|
|
795
|
+
key: 'add',
|
|
796
|
+
onClick: openComposer,
|
|
797
|
+
title: canWrite ? 'Add comment to selected element' : 'Reconnect to add a comment',
|
|
798
|
+
type: 'button',
|
|
799
|
+
}, React.createElement(DiagramCommentIcon, { add: true }))
|
|
800
|
+
: null;
|
|
801
|
+
|
|
802
|
+
return React.createElement('aside', {
|
|
803
|
+
'aria-label': 'Diagram comments',
|
|
804
|
+
className: 'diagram-comments-drawer',
|
|
805
|
+
'data-testid': 'diagram-comments-drawer',
|
|
806
|
+
}, [
|
|
807
|
+
React.createElement('div', {
|
|
808
|
+
className: 'comments-drawer-header diagram-comments-drawer-header',
|
|
809
|
+
key: 'header',
|
|
810
|
+
}, [
|
|
811
|
+
React.createElement('span', {
|
|
812
|
+
className: 'comments-drawer-title',
|
|
813
|
+
key: 'title',
|
|
814
|
+
}, composerOpen ? 'New comment' : activeThread ? 'Comment thread' : 'Comments'),
|
|
815
|
+
React.createElement('div', {
|
|
816
|
+
className: 'diagram-comments-drawer-header-actions',
|
|
817
|
+
key: 'actions',
|
|
818
|
+
}, [
|
|
819
|
+
drawerAddButton,
|
|
820
|
+
React.createElement('button', {
|
|
821
|
+
'aria-label': 'Close comments',
|
|
822
|
+
className: 'diagram-comment-close',
|
|
823
|
+
key: 'close',
|
|
824
|
+
onClick: () => setDrawerOpen(false),
|
|
825
|
+
type: 'button',
|
|
826
|
+
}, '×'),
|
|
827
|
+
]),
|
|
828
|
+
]),
|
|
829
|
+
React.createElement('div', {
|
|
830
|
+
className: `diagram-comments-drawer-content${isListView ? ' comments-drawer-list is-list' : ''}`,
|
|
831
|
+
key: 'content',
|
|
832
|
+
}, content),
|
|
833
|
+
]);
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const renderCommentsOverlay = visible && room
|
|
837
|
+
? React.createElement('div', {
|
|
838
|
+
'aria-label': 'Diagram comments overlay',
|
|
839
|
+
className: 'diagram-comments-overlay',
|
|
840
|
+
key: 'overlay',
|
|
841
|
+
}, [
|
|
842
|
+
React.createElement('div', {
|
|
843
|
+
className: 'diagram-comment-markers',
|
|
844
|
+
key: 'markers',
|
|
845
|
+
}, threads.map((thread) => {
|
|
846
|
+
const position = getDiagramCommentMarkerPosition(thread);
|
|
847
|
+
if (!position) {
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return React.createElement('button', {
|
|
852
|
+
'aria-label': `Open comment on ${getDiagramAnchorLabel(thread)}`,
|
|
853
|
+
className: `diagram-comment-marker${thread.id === activeThreadId ? ' is-active' : ''}`,
|
|
854
|
+
'data-comment-thread-id': thread.id,
|
|
855
|
+
key: thread.id,
|
|
856
|
+
onClick: () => openThread(thread.id),
|
|
857
|
+
style: {
|
|
858
|
+
left: `${position.x}px`,
|
|
859
|
+
top: `${position.y}px`,
|
|
860
|
+
},
|
|
861
|
+
type: 'button',
|
|
862
|
+
}, React.createElement(DiagramCommentIcon));
|
|
863
|
+
})),
|
|
864
|
+
renderDrawer(),
|
|
865
|
+
])
|
|
866
|
+
: null;
|
|
867
|
+
|
|
868
|
+
return React.createElement(diagramCommentsContext.Provider, {
|
|
869
|
+
value: {
|
|
870
|
+
canWrite,
|
|
871
|
+
drawerOpen,
|
|
872
|
+
openComposer,
|
|
873
|
+
room,
|
|
874
|
+
selectedElement,
|
|
875
|
+
threads,
|
|
876
|
+
toggleDrawer: () => setDrawerOpen((open) => !open),
|
|
877
|
+
visible,
|
|
878
|
+
},
|
|
879
|
+
}, [
|
|
880
|
+
React.createElement(DiagramCommentsExcalidraw, {
|
|
881
|
+
initialData,
|
|
882
|
+
key: 'excalidraw',
|
|
883
|
+
renderKey: editorRenderKey,
|
|
884
|
+
viewModeEnabled: getDocumentViewState().viewModeEnabled,
|
|
885
|
+
}),
|
|
886
|
+
renderCommentsOverlay,
|
|
887
|
+
]);
|
|
888
|
+
}
|
|
889
|
+
|
|
215
890
|
function renderExcalidrawApp({ initialData } = {}) {
|
|
216
891
|
if (!reactRoot) {
|
|
217
892
|
return;
|
|
@@ -221,9 +896,14 @@ function renderExcalidrawApp({ initialData } = {}) {
|
|
|
221
896
|
React.createElement(
|
|
222
897
|
'div',
|
|
223
898
|
{ className: 'excalidraw-editor-shell' },
|
|
224
|
-
React.createElement(
|
|
225
|
-
|
|
226
|
-
|
|
899
|
+
React.createElement(DiagramCommentsEditor, {
|
|
900
|
+
apiId: getMountedExcalidrawAPI()?.id || '',
|
|
901
|
+
canWrite: roomConnectionState === EXCALIDRAW_ROOM_CONNECTION_STATE.AUTHORITATIVE,
|
|
902
|
+
focusRequest: pendingDiagramCommentFocus,
|
|
903
|
+
initialData,
|
|
904
|
+
room: roomClient,
|
|
905
|
+
threads: diagramCommentThreads,
|
|
906
|
+
visible: normalizeDocumentMode(currentDocument.mode) !== 'preview',
|
|
227
907
|
}),
|
|
228
908
|
getAuthorityBannerText()
|
|
229
909
|
? React.createElement('div', {
|
|
@@ -260,12 +940,13 @@ function recordSceneDiagnostic(event, details = {}, sceneJson = '') {
|
|
|
260
940
|
|
|
261
941
|
function handleHistoryAction({ action = '', outcome = '' } = {}) {
|
|
262
942
|
recordSceneDiagnostic('history-action', { action, outcome });
|
|
263
|
-
|
|
943
|
+
const api = getMountedExcalidrawAPI();
|
|
944
|
+
if (outcome !== 'no-visible-change' || !api) {
|
|
264
945
|
return;
|
|
265
946
|
}
|
|
266
947
|
|
|
267
948
|
const label = action === 'redo' ? 'Redo' : 'Undo';
|
|
268
|
-
|
|
949
|
+
api.setToast?.({
|
|
269
950
|
message: `${label} skipped: a collaborator changed that item`,
|
|
270
951
|
});
|
|
271
952
|
}
|
|
@@ -298,7 +979,7 @@ function handleRoomConnectionStateChange({
|
|
|
298
979
|
roomConnectionState === EXCALIDRAW_ROOM_CONNECTION_STATE.AUTHORITATIVE
|
|
299
980
|
&& previousState === EXCALIDRAW_ROOM_CONNECTION_STATE.RECONNECTING_READONLY
|
|
300
981
|
) {
|
|
301
|
-
|
|
982
|
+
getMountedExcalidrawAPI()?.setToast?.({ message: 'Live diagram reconnected' });
|
|
302
983
|
}
|
|
303
984
|
|
|
304
985
|
if (roomConnectionState === EXCALIDRAW_ROOM_CONNECTION_STATE.AUTHORITATIVE) {
|
|
@@ -349,7 +1030,7 @@ if (isTestMode) {
|
|
|
349
1030
|
window.__COLLABMD_EXCALIDRAW_TEST__ = {
|
|
350
1031
|
disconnectTransport: () => roomClient?.provider?.disconnect?.(),
|
|
351
1032
|
getElementBounds: (elementId) => {
|
|
352
|
-
const element =
|
|
1033
|
+
const element = getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.()?.find((entry) => entry.id === elementId && !entry.isDeleted);
|
|
353
1034
|
if (!element) {
|
|
354
1035
|
return null;
|
|
355
1036
|
}
|
|
@@ -364,49 +1045,66 @@ if (isTestMode) {
|
|
|
364
1045
|
};
|
|
365
1046
|
},
|
|
366
1047
|
getElementCount: () => (
|
|
367
|
-
|
|
1048
|
+
getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.()?.filter((element) => !element.isDeleted).length ?? 0
|
|
368
1049
|
),
|
|
369
1050
|
getElementIds: () => (
|
|
370
|
-
|
|
1051
|
+
getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.()
|
|
371
1052
|
?.filter((element) => !element.isDeleted)
|
|
372
1053
|
.map((element) => element.id)
|
|
373
1054
|
.sort() ?? []
|
|
374
1055
|
),
|
|
375
1056
|
getElementStatus: (elementId) => (
|
|
376
|
-
|
|
1057
|
+
getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.()
|
|
377
1058
|
?.find((entry) => entry.id === elementId && !entry.isDeleted)?.status ?? null
|
|
378
1059
|
),
|
|
379
1060
|
getFileIds: () => (
|
|
380
|
-
Object.keys(
|
|
1061
|
+
Object.keys(getMountedExcalidrawAPI()?.getFiles?.() || {}).sort()
|
|
381
1062
|
),
|
|
382
|
-
getFileVersion: (fileId) =>
|
|
383
|
-
getEditorId: () =>
|
|
1063
|
+
getFileVersion: (fileId) => getMountedExcalidrawAPI()?.getFiles?.()?.[fileId]?.version ?? null,
|
|
1064
|
+
getEditorId: () => getMountedExcalidrawAPI()?.id || null,
|
|
384
1065
|
getForkCapabilities: () => ({
|
|
385
|
-
replaceFiles: typeof
|
|
1066
|
+
replaceFiles: typeof getMountedExcalidrawAPI()?.replaceFiles === 'function',
|
|
386
1067
|
}),
|
|
387
1068
|
getAuthorityState: () => roomConnectionState,
|
|
1069
|
+
getCommentThreads: () => roomClient?.getCommentThreads?.() || [],
|
|
388
1070
|
getDiagnosticTrace: () => diagnostics.exportTrace(),
|
|
389
1071
|
getHistoryState: () => getNativeHistoryState(),
|
|
390
1072
|
getLocalUserName: () => localAwarenessUser?.name || '',
|
|
391
1073
|
getLocalPeerId: () => localAwarenessUser?.peerId || '',
|
|
392
1074
|
getViewport: () => {
|
|
393
|
-
const appState =
|
|
1075
|
+
const appState = getMountedExcalidrawAPI()?.getAppState?.();
|
|
394
1076
|
return appState ? {
|
|
1077
|
+
offsetLeft: appState.offsetLeft,
|
|
1078
|
+
offsetTop: appState.offsetTop,
|
|
395
1079
|
scrollX: appState.scrollX,
|
|
396
1080
|
scrollY: appState.scrollY,
|
|
397
1081
|
zoom: appState.zoom?.value ?? null,
|
|
398
1082
|
} : null;
|
|
399
1083
|
},
|
|
400
|
-
isViewMode: () => Boolean(
|
|
1084
|
+
isViewMode: () => Boolean(getMountedExcalidrawAPI()?.getAppState?.().viewModeEnabled),
|
|
1085
|
+
selectElement: (elementId) => {
|
|
1086
|
+
const api = getMountedExcalidrawAPI();
|
|
1087
|
+
if (!api || !elementId) {
|
|
1088
|
+
return false;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
api.updateScene({
|
|
1092
|
+
appState: {
|
|
1093
|
+
selectedElementIds: { [elementId]: true },
|
|
1094
|
+
},
|
|
1095
|
+
captureUpdate: CaptureUpdateAction.NEVER,
|
|
1096
|
+
});
|
|
1097
|
+
return true;
|
|
1098
|
+
},
|
|
401
1099
|
getSceneJson: () => roomClient?.getLastSceneJson?.() || '',
|
|
402
1100
|
isAuthoritativeReady: () => (
|
|
403
|
-
Boolean(
|
|
1101
|
+
Boolean(getMountedExcalidrawAPI())
|
|
404
1102
|
&& collabReady
|
|
405
1103
|
&& roomClient?.canWriteToRoom === true
|
|
406
1104
|
&& roomClient?.waitingForAuthoritativeSync === false
|
|
407
1105
|
&& roomClient?.isApplyingSharedSnapshot?.() === false
|
|
408
1106
|
),
|
|
409
|
-
isReady: () => collabReady && Boolean(
|
|
1107
|
+
isReady: () => collabReady && Boolean(getMountedExcalidrawAPI()),
|
|
410
1108
|
redoShared: () => triggerNativeHistory('redo'),
|
|
411
1109
|
reconnectTransport: () => roomClient?.provider?.connect?.(),
|
|
412
1110
|
setScene: (scene) => {
|
|
@@ -415,12 +1113,13 @@ if (isTestMode) {
|
|
|
415
1113
|
});
|
|
416
1114
|
},
|
|
417
1115
|
setViewport: (viewport) => {
|
|
418
|
-
|
|
1116
|
+
const api = getMountedExcalidrawAPI();
|
|
1117
|
+
if (!api) {
|
|
419
1118
|
return;
|
|
420
1119
|
}
|
|
421
1120
|
|
|
422
|
-
const currentAppState =
|
|
423
|
-
|
|
1121
|
+
const currentAppState = api.getAppState();
|
|
1122
|
+
api.updateScene({
|
|
424
1123
|
appState: {
|
|
425
1124
|
scrollX: Number.isFinite(viewport?.scrollX) ? viewport.scrollX : currentAppState.scrollX,
|
|
426
1125
|
scrollY: Number.isFinite(viewport?.scrollY) ? viewport.scrollY : currentAppState.scrollY,
|
|
@@ -440,14 +1139,15 @@ function applyCollaborators(collaborators) {
|
|
|
440
1139
|
const renderableCollaborators = buildRenderableCollaboratorsMap(activeCollaborators);
|
|
441
1140
|
const renderSignature = getCollaboratorsRenderSignature(renderableCollaborators);
|
|
442
1141
|
|
|
443
|
-
|
|
1142
|
+
const api = getMountedExcalidrawAPI();
|
|
1143
|
+
if (!api) {
|
|
444
1144
|
pendingCollaborators = activeCollaborators;
|
|
445
1145
|
return;
|
|
446
1146
|
}
|
|
447
1147
|
|
|
448
1148
|
if (renderSignature !== lastRenderedCollaboratorsSignature) {
|
|
449
1149
|
lastRenderedCollaboratorsSignature = renderSignature;
|
|
450
|
-
|
|
1150
|
+
api.updateScene({
|
|
451
1151
|
collaborators: renderableCollaborators,
|
|
452
1152
|
captureUpdate: CaptureUpdateAction.NEVER,
|
|
453
1153
|
});
|
|
@@ -476,11 +1176,11 @@ function queueCollaboratorsRender(collaborators) {
|
|
|
476
1176
|
}
|
|
477
1177
|
|
|
478
1178
|
function isEditingTextElement() {
|
|
479
|
-
return Boolean(
|
|
1179
|
+
return Boolean(getMountedExcalidrawAPI()?.getAppState?.()?.editingTextElement);
|
|
480
1180
|
}
|
|
481
1181
|
|
|
482
1182
|
function flushPendingRemoteScene() {
|
|
483
|
-
if (!pendingRemoteSceneJson || !
|
|
1183
|
+
if (!pendingRemoteSceneJson || !getMountedExcalidrawAPI() || !collabReady || isEditingTextElement()) {
|
|
484
1184
|
return false;
|
|
485
1185
|
}
|
|
486
1186
|
|
|
@@ -504,7 +1204,7 @@ function applySceneFromJson(rawJson, {
|
|
|
504
1204
|
appliedSceneJson = normalizedJson;
|
|
505
1205
|
recordSceneDiagnostic('remote-scene-received', {}, normalizedJson);
|
|
506
1206
|
|
|
507
|
-
if (!
|
|
1207
|
+
if (!getMountedExcalidrawAPI() || !collabReady) {
|
|
508
1208
|
pendingRemoteSceneJson = normalizedJson;
|
|
509
1209
|
return;
|
|
510
1210
|
}
|
|
@@ -531,9 +1231,14 @@ function releaseViewportBroadcastSuppressionAfterPaint() {
|
|
|
531
1231
|
|
|
532
1232
|
function buildApiSceneUpdate(scene, {
|
|
533
1233
|
appStateOverrides = {},
|
|
1234
|
+
api = getMountedExcalidrawAPI(),
|
|
534
1235
|
} = {}) {
|
|
535
|
-
|
|
536
|
-
|
|
1236
|
+
if (!api) {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
const currentAppState = api.getAppState();
|
|
1241
|
+
const currentElements = api.getSceneElementsIncludingDeleted?.() || api.getSceneElements();
|
|
537
1242
|
|
|
538
1243
|
return buildReconciledExcalidrawSceneUpdate({
|
|
539
1244
|
appStateOverrides,
|
|
@@ -575,6 +1280,7 @@ function requestEditorRemount(scene) {
|
|
|
575
1280
|
appliedSceneJson = normalizedJson;
|
|
576
1281
|
pendingCollaborators = activeCollaborators;
|
|
577
1282
|
initialViewportFitPending = true;
|
|
1283
|
+
clearPreviewViewportFitTimers();
|
|
578
1284
|
clearEditorApiStateBindings();
|
|
579
1285
|
skipRoomDisconnectOnUnmount = true;
|
|
580
1286
|
editorRenderKey += 1;
|
|
@@ -589,8 +1295,14 @@ function applySceneToMountedApi(scene, {
|
|
|
589
1295
|
captureUpdate = CaptureUpdateAction.NEVER,
|
|
590
1296
|
trackedSharedSnapshot = false,
|
|
591
1297
|
} = {}) {
|
|
1298
|
+
const api = getMountedExcalidrawAPI();
|
|
1299
|
+
if (!api) {
|
|
1300
|
+
return { skipped: true };
|
|
1301
|
+
}
|
|
1302
|
+
|
|
592
1303
|
const nextSceneUpdate = buildApiSceneUpdate(scene, {
|
|
593
1304
|
appStateOverrides,
|
|
1305
|
+
api,
|
|
594
1306
|
});
|
|
595
1307
|
let applyResult;
|
|
596
1308
|
|
|
@@ -599,7 +1311,7 @@ function applySceneToMountedApi(scene, {
|
|
|
599
1311
|
}
|
|
600
1312
|
|
|
601
1313
|
try {
|
|
602
|
-
applyResult = applySceneUpdateWithFiles(
|
|
1314
|
+
applyResult = applySceneUpdateWithFiles(api, {
|
|
603
1315
|
captureUpdate,
|
|
604
1316
|
files: scene?.files || {},
|
|
605
1317
|
sceneUpdate: nextSceneUpdate,
|
|
@@ -643,7 +1355,7 @@ function applyLocalScene(scene, {
|
|
|
643
1355
|
|
|
644
1356
|
appliedSceneJson = normalizedJson;
|
|
645
1357
|
|
|
646
|
-
if (!
|
|
1358
|
+
if (!getMountedExcalidrawAPI() || !collabReady) {
|
|
647
1359
|
pendingRemoteSceneJson = normalizedJson;
|
|
648
1360
|
return;
|
|
649
1361
|
}
|
|
@@ -661,14 +1373,24 @@ function onRoomTextUpdate() {
|
|
|
661
1373
|
}
|
|
662
1374
|
|
|
663
1375
|
function getLiveSceneElementsForSync(fallbackElements = []) {
|
|
664
|
-
return
|
|
1376
|
+
return getMountedExcalidrawAPI()?.getSceneElementsIncludingDeleted?.() || fallbackElements;
|
|
665
1377
|
}
|
|
666
1378
|
|
|
667
1379
|
function postToParent(type, payload = {}) {
|
|
668
1380
|
window.parent.postMessage({ source: 'excalidraw-editor', type, ...payload }, parentOrigin);
|
|
669
1381
|
}
|
|
670
1382
|
|
|
1383
|
+
function stopFollowingFromLocalInteraction() {
|
|
1384
|
+
if (!pendingHostFollowPeerId && !followedSocketId) {
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
applyHostFollowRequest(null);
|
|
1389
|
+
postToParent('stop-following');
|
|
1390
|
+
}
|
|
1391
|
+
|
|
671
1392
|
function handleQuickSwitcherKeyDown(event) {
|
|
1393
|
+
stopFollowingFromLocalInteraction();
|
|
672
1394
|
if (!isPlainQuickSwitcherShortcut(event)) {
|
|
673
1395
|
return;
|
|
674
1396
|
}
|
|
@@ -679,8 +1401,9 @@ function handleQuickSwitcherKeyDown(event) {
|
|
|
679
1401
|
}
|
|
680
1402
|
|
|
681
1403
|
function getSceneElementsForPreviewFit() {
|
|
1404
|
+
const api = getMountedExcalidrawAPI();
|
|
682
1405
|
return (
|
|
683
|
-
|
|
1406
|
+
api?.getSceneElementsIncludingDeleted?.()
|
|
684
1407
|
?.filter((element) => !element.isDeleted) ?? []
|
|
685
1408
|
);
|
|
686
1409
|
}
|
|
@@ -691,7 +1414,8 @@ function scheduleViewportFit({
|
|
|
691
1414
|
consumeInitialFit = false,
|
|
692
1415
|
} = {}) {
|
|
693
1416
|
const normalizedMode = normalizeDocumentMode(currentDocument.mode);
|
|
694
|
-
|
|
1417
|
+
const api = getMountedExcalidrawAPI();
|
|
1418
|
+
if (!api || (forcePreview && normalizedMode !== 'preview')) {
|
|
695
1419
|
return;
|
|
696
1420
|
}
|
|
697
1421
|
|
|
@@ -699,6 +1423,11 @@ function scheduleViewportFit({
|
|
|
699
1423
|
return;
|
|
700
1424
|
}
|
|
701
1425
|
|
|
1426
|
+
if (!forcePreview && api.getAppState?.().cursorButton === 'down') {
|
|
1427
|
+
initialViewportFitPending = false;
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
702
1431
|
const elements = getSceneElementsForPreviewFit();
|
|
703
1432
|
if (elements.length === 0) {
|
|
704
1433
|
return;
|
|
@@ -707,15 +1436,42 @@ function scheduleViewportFit({
|
|
|
707
1436
|
initialViewportFitPending = false;
|
|
708
1437
|
}
|
|
709
1438
|
|
|
1439
|
+
if (!forcePreview && delayMs === 0) {
|
|
1440
|
+
api.setViewport({
|
|
1441
|
+
target: elements,
|
|
1442
|
+
animation: false,
|
|
1443
|
+
fit: 'contain',
|
|
1444
|
+
});
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
const generation = ++viewportFitGeneration;
|
|
1449
|
+
|
|
710
1450
|
if (previewViewportFitTimerId) {
|
|
711
1451
|
window.clearTimeout(previewViewportFitTimerId);
|
|
712
1452
|
}
|
|
713
1453
|
|
|
714
1454
|
previewViewportFitTimerId = window.setTimeout(() => {
|
|
715
1455
|
previewViewportFitTimerId = 0;
|
|
1456
|
+
if (generation !== viewportFitGeneration) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
716
1460
|
requestAnimationFrame(() => {
|
|
1461
|
+
if (generation !== viewportFitGeneration) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
717
1465
|
requestAnimationFrame(() => {
|
|
718
|
-
if (
|
|
1466
|
+
if (generation !== viewportFitGeneration) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
const api = getMountedExcalidrawAPI();
|
|
1471
|
+
if (!api) {
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
if (!forcePreview && api.getAppState?.().cursorButton === 'down') {
|
|
719
1475
|
return;
|
|
720
1476
|
}
|
|
721
1477
|
|
|
@@ -728,11 +1484,10 @@ function scheduleViewportFit({
|
|
|
728
1484
|
suppressViewportBroadcast = true;
|
|
729
1485
|
}
|
|
730
1486
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
viewportZoomFactor: normalizedMode === 'preview' ? 0.92 : 0.88,
|
|
1487
|
+
api.setViewport({
|
|
1488
|
+
target: latestElements,
|
|
1489
|
+
animation: false,
|
|
1490
|
+
fit: 'contain',
|
|
736
1491
|
});
|
|
737
1492
|
|
|
738
1493
|
if (forcePreview) {
|
|
@@ -748,18 +1503,38 @@ function scheduleInitialViewportFit() {
|
|
|
748
1503
|
}
|
|
749
1504
|
|
|
750
1505
|
function schedulePreviewViewportFit() {
|
|
1506
|
+
if (previewViewportFitRetryTimerId) {
|
|
1507
|
+
window.clearTimeout(previewViewportFitRetryTimerId);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
751
1510
|
scheduleViewportFit({ forcePreview: true, delayMs: 80 });
|
|
752
|
-
window.setTimeout(() => {
|
|
1511
|
+
previewViewportFitRetryTimerId = window.setTimeout(() => {
|
|
1512
|
+
previewViewportFitRetryTimerId = 0;
|
|
753
1513
|
scheduleViewportFit({ forcePreview: true });
|
|
754
1514
|
}, 240);
|
|
755
1515
|
}
|
|
756
1516
|
|
|
1517
|
+
function clearPreviewViewportFitTimers() {
|
|
1518
|
+
viewportFitGeneration += 1;
|
|
1519
|
+
|
|
1520
|
+
if (previewViewportFitTimerId) {
|
|
1521
|
+
window.clearTimeout(previewViewportFitTimerId);
|
|
1522
|
+
previewViewportFitTimerId = 0;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (previewViewportFitRetryTimerId) {
|
|
1526
|
+
window.clearTimeout(previewViewportFitRetryTimerId);
|
|
1527
|
+
previewViewportFitRetryTimerId = 0;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
757
1531
|
function syncLocalViewportToRoom() {
|
|
758
|
-
|
|
1532
|
+
const api = getMountedExcalidrawAPI();
|
|
1533
|
+
if (!collabReady || !api || !roomClient || suppressViewportBroadcast) {
|
|
759
1534
|
return;
|
|
760
1535
|
}
|
|
761
1536
|
|
|
762
|
-
const appState =
|
|
1537
|
+
const appState = api.getAppState();
|
|
763
1538
|
roomClient.scheduleLocalViewportAwareness({
|
|
764
1539
|
scrollX: appState.scrollX,
|
|
765
1540
|
scrollY: appState.scrollY,
|
|
@@ -781,7 +1556,8 @@ function setFollowedSocket(nextSocketId, { force = false } = {}) {
|
|
|
781
1556
|
}
|
|
782
1557
|
|
|
783
1558
|
function applyFollowedViewport(collaborators = activeCollaborators, { force = false } = {}) {
|
|
784
|
-
|
|
1559
|
+
const api = getMountedExcalidrawAPI();
|
|
1560
|
+
if (!api || !followedSocketId) {
|
|
785
1561
|
return;
|
|
786
1562
|
}
|
|
787
1563
|
|
|
@@ -798,7 +1574,7 @@ function applyFollowedViewport(collaborators = activeCollaborators, { force = fa
|
|
|
798
1574
|
|
|
799
1575
|
lastAppliedFollowViewportSignature = nextSignature;
|
|
800
1576
|
suppressViewportBroadcast = true;
|
|
801
|
-
|
|
1577
|
+
api.updateScene({
|
|
802
1578
|
appState: {
|
|
803
1579
|
scrollX: viewport.scrollX,
|
|
804
1580
|
scrollY: viewport.scrollY,
|
|
@@ -811,12 +1587,13 @@ function applyFollowedViewport(collaborators = activeCollaborators, { force = fa
|
|
|
811
1587
|
|
|
812
1588
|
function applyHostFollowRequest(peerId) {
|
|
813
1589
|
pendingHostFollowPeerId = peerId || null;
|
|
814
|
-
|
|
1590
|
+
const api = getMountedExcalidrawAPI();
|
|
1591
|
+
if (!api) {
|
|
815
1592
|
return;
|
|
816
1593
|
}
|
|
817
1594
|
|
|
818
1595
|
if (!peerId) {
|
|
819
|
-
|
|
1596
|
+
api.updateScene({
|
|
820
1597
|
appState: { userToFollow: null },
|
|
821
1598
|
captureUpdate: CaptureUpdateAction.NEVER,
|
|
822
1599
|
});
|
|
@@ -830,7 +1607,7 @@ function applyHostFollowRequest(peerId) {
|
|
|
830
1607
|
return;
|
|
831
1608
|
}
|
|
832
1609
|
|
|
833
|
-
|
|
1610
|
+
api.updateScene({
|
|
834
1611
|
appState: {
|
|
835
1612
|
userToFollow: {
|
|
836
1613
|
socketId: collaborator.socketId,
|
|
@@ -853,6 +1630,8 @@ function resetRealtimeRoomState() {
|
|
|
853
1630
|
pendingRemoteSceneJson = '';
|
|
854
1631
|
pendingCollaborators = null;
|
|
855
1632
|
activeCollaborators = new Map();
|
|
1633
|
+
diagramCommentThreads = [];
|
|
1634
|
+
pendingDiagramCommentFocus = null;
|
|
856
1635
|
followedSocketId = null;
|
|
857
1636
|
pendingHostFollowPeerId = null;
|
|
858
1637
|
suppressViewportBroadcast = false;
|
|
@@ -984,6 +1763,10 @@ window.addEventListener('message', (event) => {
|
|
|
984
1763
|
return;
|
|
985
1764
|
}
|
|
986
1765
|
|
|
1766
|
+
if (event.source !== window.parent) {
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
987
1770
|
const message = event.data;
|
|
988
1771
|
if (!message || message.source !== 'collabmd-host') {
|
|
989
1772
|
return;
|
|
@@ -992,8 +1775,9 @@ window.addEventListener('message', (event) => {
|
|
|
992
1775
|
if (message.type === 'set-theme') {
|
|
993
1776
|
currentTheme = message.theme || 'dark';
|
|
994
1777
|
applySurfaceTheme(currentTheme);
|
|
995
|
-
|
|
996
|
-
|
|
1778
|
+
const api = getMountedExcalidrawAPI();
|
|
1779
|
+
if (api) {
|
|
1780
|
+
api.updateScene({
|
|
997
1781
|
appState: { theme: currentTheme },
|
|
998
1782
|
captureUpdate: CaptureUpdateAction.NEVER,
|
|
999
1783
|
});
|
|
@@ -1007,6 +1791,11 @@ window.addEventListener('message', (event) => {
|
|
|
1007
1791
|
return;
|
|
1008
1792
|
}
|
|
1009
1793
|
|
|
1794
|
+
if (message.type === 'open-comment-thread') {
|
|
1795
|
+
requestDiagramCommentFocus(message.threadId);
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1010
1799
|
if (message.type === 'follow-user') {
|
|
1011
1800
|
applyHostFollowRequest(message.peerId || null);
|
|
1012
1801
|
return;
|
|
@@ -1102,6 +1891,9 @@ function initializeEditor(api) {
|
|
|
1102
1891
|
|
|
1103
1892
|
setFollowedSocket(null, { force: true });
|
|
1104
1893
|
}));
|
|
1894
|
+
apiStateCleanupCallbacks.push(api.onStateChange('selectedElementIds', () => {
|
|
1895
|
+
renderExcalidrawApp();
|
|
1896
|
+
}));
|
|
1105
1897
|
apiStateCleanupCallbacks.push(api.onStateChange('editingTextElement', (editingTextElement) => {
|
|
1106
1898
|
if (!editingTextElement) {
|
|
1107
1899
|
requestAnimationFrame(() => {
|
|
@@ -1121,7 +1913,7 @@ function initializeEditor(api) {
|
|
|
1121
1913
|
if (pendingCollaborators) {
|
|
1122
1914
|
const renderableCollaborators = buildRenderableCollaboratorsMap(pendingCollaborators);
|
|
1123
1915
|
lastRenderedCollaboratorsSignature = getCollaboratorsRenderSignature(renderableCollaborators);
|
|
1124
|
-
|
|
1916
|
+
api.updateScene({
|
|
1125
1917
|
collaborators: renderableCollaborators,
|
|
1126
1918
|
captureUpdate: CaptureUpdateAction.NEVER,
|
|
1127
1919
|
});
|
|
@@ -1137,6 +1929,7 @@ function initializeEditor(api) {
|
|
|
1137
1929
|
|
|
1138
1930
|
scheduleInitialViewportFit();
|
|
1139
1931
|
postToParent('ready');
|
|
1932
|
+
renderExcalidrawApp();
|
|
1140
1933
|
}
|
|
1141
1934
|
|
|
1142
1935
|
function handleEditorMount({ excalidrawAPI: api }) {
|