collabmd 0.1.54 → 0.1.55
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/bin/collabmd.js +19 -38
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-cMeDxAEL.js +1 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-DxNxad6-.js → abnfDiagram-VCTEODGH-D2UdmYvj.js} +1 -1
- package/dist/client/assets/{abnfDiagram-O67JEVCF-Dwt6a0Ds.js → abnfDiagram-YUKMZFIV-DbmU4Q-X.js} +1 -1
- package/dist/client/assets/api-client-utils-C5ESDKMG.js +1 -0
- package/dist/client/assets/architecture-7GRP2DOG-BBwRXFv3.js +1 -1
- package/dist/client/assets/architecture-WOLXFQ4H-C0NxYiG7.js +1 -0
- package/dist/client/assets/{architectureDiagram-NJMV4G6O-D5CGiX3G.js → architectureDiagram-47P4ROYG-C7OjXK9S.js} +3 -3
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-C1uvg5id.js → architectureDiagram-5GKGNRK7-CSiUzzeN.js} +1 -1
- package/dist/client/assets/base-CaIEJUF9.css +1 -0
- package/dist/client/assets/{blockDiagram-BEXU5L5S-BgTM0var.js → blockDiagram-E7TT5TSB-DMG-YlZw.js} +3 -3
- package/dist/client/assets/{blockDiagram-I7D4REHJ-B2YonsoG.js → blockDiagram-I7D4REHJ-sXfAgK2w.js} +1 -1
- package/dist/client/assets/{browser-BKLM0ThC-BCLQGPD6-K2seJgRL.js → browser-BKLM0ThC-BCLQGPD6-CcMAHZXi.js} +1 -1
- package/dist/client/assets/{c4Diagram-7LVT6UL2-r4IwomZm.js → c4Diagram-7LVT6UL2-C88V_K-8.js} +1 -1
- package/dist/client/assets/{c4Diagram-YGBWAQC7-_xZPrNkB.js → c4Diagram-CRA5TL53-D3F9ElJv.js} +2 -2
- package/dist/client/assets/canvasEditor-BJkwGzO1.css +1 -0
- package/dist/client/assets/canvasEditor-sBK7KfrD.js +1 -0
- package/dist/client/assets/{chunk-6AZGARVD--pTGpDnx.js → chunk-24IY7LWP-flnn6SWX.js} +1 -1
- package/dist/client/assets/chunk-2BW5OAIV-CNFO1U_e.js +10 -0
- package/dist/client/assets/{chunk-2E4U76K2-CIQUuV9n.js → chunk-2E4U76K2-DEnS5IbF.js} +1 -1
- package/dist/client/assets/{chunk-5V3GS4D5-B5uC2oxu.js → chunk-3M4EKCLJ-D-sZGCtO.js} +1 -1
- package/dist/client/assets/chunk-3YJQHVM4-Ctp__8EK.js +2 -0
- package/dist/client/assets/{chunk-7PRAP22T-9uTvtc_-.js → chunk-4EA7E6EY-bU4wRJot.js} +1 -1
- package/dist/client/assets/{chunk-4HAMMTFA-q0ldNVvr.js → chunk-4HAMMTFA-CQy6Skzw.js} +1 -1
- package/dist/client/assets/{chunk-6K3QC6MW-JnuvEGV4.js → chunk-4S7OKTRW-BpUW13a3.js} +1 -1
- package/dist/client/assets/{chunk-ICYGCRZG-odvlDZyA.js → chunk-53FOQ5SW-5lPdGYP4.js} +1 -1
- package/dist/client/assets/{chunk-75Z2AOVW-DitkSoSG.js → chunk-75Z2AOVW-BRdhvVTZ.js} +1 -1
- package/dist/client/assets/{chunk-GWA4HPMP-CGmv2yIq.js → chunk-7TKQ45FW-rA2u-WUl.js} +1 -1
- package/dist/client/assets/{chunk-GTNCS2PH-ChgkzEIi.js → chunk-AW2ZBBNX-DL0KGAdi.js} +3 -3
- package/dist/client/assets/{chunk-747NJXEK-BjqqAsS9.js → chunk-BP2KR52E-DAQ1pzDV.js} +1 -1
- package/dist/client/assets/{chunk-CLGD4ZFX-BfYJtJm1.js → chunk-CLGD4ZFX-DJiSE0gD.js} +1 -1
- package/dist/client/assets/{chunk-5DYCD2WN-BKWCKnK-.js → chunk-DBDB3WZW-CXleNlok.js} +1 -1
- package/dist/client/assets/{chunk-DU6HZSFF-CWlP1Dk_.js → chunk-DU6HZSFF-C-18UZkS.js} +1 -1
- package/dist/client/assets/{chunk-MBY4JIJT-DTJ4UcAm.js → chunk-E2ZNV5FY-D_07zfWd.js} +3 -3
- package/dist/client/assets/chunk-EU5HNXII-CETVNUXF.js +213 -0
- package/dist/client/assets/{chunk-F27PBJKO-DPcsPcEs.js → chunk-F27PBJKO-_beIJos4.js} +1 -1
- package/dist/client/assets/{chunk-2ZTRR5NV-DcPRfelM.js → chunk-F6S3BTY2-DVWKCAHp.js} +1 -1
- package/dist/client/assets/{chunk-ISPTBLVO-BMsDq9Ha.js → chunk-FLFP27SU-CydzbSsY.js} +2 -2
- package/dist/client/assets/chunk-FVRAUYC3-C7b9SJ81.js +1 -0
- package/dist/client/assets/{chunk-GMAD6QVW-Bgk8qnty.js → chunk-GMAD6QVW-DnZH7DxV.js} +1 -1
- package/dist/client/assets/{chunk-GVQU2GXP-CQ8cl-Uu.js → chunk-GVQU2GXP-B0Vm415C.js} +1 -1
- package/dist/client/assets/chunk-H5A3YXKD-ChP7mNJq.js +41 -0
- package/dist/client/assets/{chunk-742MDFTN-D1nh6vNv.js → chunk-HJ2JQQFS-DQX7RePR.js} +1 -1
- package/dist/client/assets/{chunk-NETBCI7D-CmUwDWUZ.js → chunk-HTAEGDNF-C0PA_N-c.js} +1 -1
- package/dist/client/assets/{chunk-IMKFNOWR-CBqxfpcP.js → chunk-IMKFNOWR-DoO8IHzF.js} +1 -1
- package/dist/client/assets/{chunk-IH6LHLGP-BNevEHZK.js → chunk-KPI5JJXK-B2ld5-w4.js} +1 -1
- package/dist/client/assets/{chunk-WEXAMYUT-DDlVNp8J.js → chunk-KQW6MTUR-OQ4MyMr_.js} +1 -1
- package/dist/client/assets/{chunk-L3NEJ4N5-BflzkdTI.js → chunk-L3NEJ4N5-CvHossEU.js} +1 -1
- package/dist/client/assets/chunk-NGNAAXSQ-DqoPQk1a.js +136 -0
- package/dist/client/assets/{chunk-UA2S7LBM-DicYDjDM.js → chunk-NTY3LDVX-CVWLK_fh.js} +1 -1
- package/dist/client/assets/{chunk-UY3FDG6J-QT-rkt0y.js → chunk-OD3NTWWA-Dz9nCUGX.js} +1 -1
- package/dist/client/assets/{chunk-OSK3NFVY-DqeKV_pg.js → chunk-OSK3NFVY-C3JOcBao.js} +1 -1
- package/dist/client/assets/{chunk-P2QGCYS3-BA--DhpW.js → chunk-P2QGCYS3-Bfo8qS8r.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-V_NEc45n.js → chunk-POPQ4Y6H-sv6rCftH.js} +1 -1
- package/dist/client/assets/{chunk-PWAF6VOD-D_kLse-N.js → chunk-PWAF6VOD-B-AT-L2H.js} +1 -1
- package/dist/client/assets/{chunk-3Z5EZCMW-BAnA6eQ7.js → chunk-QNH66VMT-Cb9xxTwQ.js} +1 -1
- package/dist/client/assets/{chunk-SHT3W25Y-YM3S-A_M.js → chunk-SHT3W25Y-C1lu518T.js} +1 -1
- package/dist/client/assets/{chunk-GNY47TPC-CTrH5O5R.js → chunk-SVEVUXB4-BnM-LXop.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-DdNhDOmW.js → chunk-SVP7TREG-CKqt0syj.js} +1 -1
- package/dist/client/assets/{chunk-TICWLB2K-BGGgCr4x.js → chunk-TICWLB2K-D2ZnWAfF.js} +1 -1
- package/dist/client/assets/{chunk-TLUHSLCS-DCZhIDYA.js → chunk-TLUHSLCS-BMIhqv1X.js} +1 -1
- package/dist/client/assets/{chunk-6EIED4P4-DBVWq7Tz.js → chunk-TPMEZKFX-DIPHIqGb.js} +1 -1
- package/dist/client/assets/{chunk-6TQVIW2G-BSj0l_pu.js → chunk-VPELOWWC-DXayM28-.js} +1 -1
- package/dist/client/assets/chunk-VPRB5NB3-BqaJfj_v.js +127 -0
- package/dist/client/assets/{chunk-I5DQTOEV-aBZooNp1.js → chunk-VTWWFHGB-SZnJiyWY.js} +1 -1
- package/dist/client/assets/{chunk-KI3K4JFJ-FTtDwx3-.js → chunk-VZTHESGH-eHyXmK8W.js} +1 -1
- package/dist/client/assets/{chunk-7INBJB4K-CDmiHbaX.js → chunk-XC4XBNZT-D9oehSn8.js} +20 -20
- package/dist/client/assets/{chunk-XHIXRSVI-DN_fEwws.js → chunk-YK26KJH5-DVy3MGMd.js} +1 -1
- package/dist/client/assets/{chunk-6AEJRKK7-DTVZj5bY.js → chunk-Z6ETV63D-DOcrnz-P.js} +1 -1
- package/dist/client/assets/{chunk-OUJLGHUK-75UXlPjl.js → chunk-ZO67DCNQ-BAOMIkLF.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-BmETdFmC.js +1 -0
- package/dist/client/assets/{classDiagram-v2-NBCMYWYE-3TQORq33.js → classDiagram-v2-K4WV4PDN-eaadfJDB.js} +3 -3
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-BmETdFmC.js +1 -0
- package/dist/client/assets/{comment-markdown-renderer-Bxi_JRSR.js → comment-markdown-renderer-CpBKaj7H.js} +1 -1
- package/dist/client/assets/comment-thread-store-DUQsWtnu.js +1 -0
- package/dist/client/assets/comment-threads-DnfkLVJA.js +4 -0
- package/dist/client/assets/comment-ui-shared-D_pgPUti.js +7 -0
- package/dist/client/assets/cynefin-EF2NZ3EQ-DVkJeZ8i.js +1 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CF3N9j1_.js +1 -1
- package/dist/client/assets/{cynefinDiagram-VND7K2PF-DwfMZ3vk.js → cynefinDiagram-3GCD6N5R-DjpAsBHC.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-DTnLZaO7.js → cynefinDiagram-5FMLGOSQ-D2rFQG67.js} +1 -1
- package/dist/client/assets/dagre-C-rnJpZF.js +1 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-Vtyx08Qh.js → dagre-GXQ25YYZ-Cku9eia9.js} +2 -2
- package/dist/client/assets/{dagre-6A5THRUB-DewyAGK6.js → dagre-W4DXFKR2-DOMOIsPd.js} +2 -2
- package/dist/client/assets/deferred-collab-C9aa9cvT.css +1 -0
- package/dist/client/assets/deferred-git-BPQ-UgrN.css +1 -0
- package/dist/client/assets/deferred-preview-B-RebLry.css +1 -0
- package/dist/client/assets/{diagram-22UHCM2B-DbZirFfl.js → diagram-2UJZ2QOL-D8WvnEyx.js} +6 -6
- package/dist/client/assets/{diagram-3UASUU5V-DmhdYv6_.js → diagram-OVF4WLC6-D8_escYE.js} +2 -2
- package/dist/client/assets/{diagram-CDSNMT55-D_qyS11R.js → diagram-PFPMY2P6-B1izZfrN.js} +3 -3
- package/dist/client/assets/{diagram-S7CK7UJ4-CGTzfo2f.js → diagram-S7CK7UJ4-BzicH2t-.js} +1 -1
- package/dist/client/assets/{diagram-MLGK6HIB-DIBG6Ivu.js → diagram-UMYRVEAY-Djf7WDjZ.js} +1 -1
- package/dist/client/assets/{diagram-UQ7AKVKN-CWZS8QtB.js → diagram-UQ7AKVKN-DqX4zTtr.js} +1 -1
- package/dist/client/assets/{diagram-VSXAHHWV-DaO1nOZF.js → diagram-VSXAHHWV-DnVbfI4Y.js} +1 -1
- package/dist/client/assets/{diagram-VX7I27RA-B54P6Cyf.js → diagram-VX7I27RA-D-Dj0TQC.js} +1 -1
- package/dist/client/assets/{diagram-MPIPVDR6-D9XsALDV.js → diagram-YEKJPTXX-B40bP3Yr.js} +2 -2
- package/dist/client/assets/{diagram-Z3DM3KII-Cl2EXiXp.js → diagram-Z3DM3KII-_iT7Uz5p.js} +1 -1
- package/dist/client/assets/diagram-ZIFT7M5P-BUUK0oNw.js +3 -0
- package/dist/client/assets/{direct-engine-DVLMOJWp-C-YL5NFb.js → direct-engine-DVLMOJWp-DWtWIifK.js} +1 -1
- package/dist/client/assets/dist-26Ce-5t4.js +1 -0
- package/dist/client/assets/dist-8MLNn-I6.js +1 -0
- package/dist/client/assets/dist-Amm5Pr1C.js +1 -0
- package/dist/client/assets/dist-BXXPaoUV.js +1 -0
- package/dist/client/assets/{dist-CoXN2D3R.js → dist-Bkb-_VdB.js} +1 -1
- package/dist/client/assets/dist-Bl4yuUS9.js +1 -0
- package/dist/client/assets/{dist-lw5TGjI6.js → dist-C2CGvCPd.js} +1 -1
- package/dist/client/assets/dist-C2niyEh0.js +1 -0
- package/dist/client/assets/{dist-DOHWxIty.js → dist-CFCSUmJk.js} +1 -1
- package/dist/client/assets/dist-CHUI56fT.js +6 -0
- package/dist/client/assets/dist-CJh3Qrid.js +13 -0
- package/dist/client/assets/{dist-Ch6Sr9KF.js → dist-CKxETpSC.js} +3 -3
- package/dist/client/assets/dist-CSHJthAI.js +2 -0
- package/dist/client/assets/dist-CbKnZGN4.js +1 -0
- package/dist/client/assets/{dist-KrdpB_GB.js → dist-Cik4RJXj.js} +1 -1
- package/dist/client/assets/{dist-uk11ih8u.js → dist-CqkAY6Ot.js} +2 -2
- package/dist/client/assets/dist-D2ql2LCd.js +1 -0
- package/dist/client/assets/{dist-BpAUe9sL.js → dist-D47MOYFN.js} +1 -1
- package/dist/client/assets/dist-DBhUaexM.js +1 -0
- package/dist/client/assets/dist-DJux6Vc5.js +1 -0
- package/dist/client/assets/{dist-9Eodxf4G.js → dist-DLNdMWPH.js} +1 -1
- package/dist/client/assets/dist-DPJj-Y3l.js +1 -0
- package/dist/client/assets/{dist-BSAZ-Hek.js → dist-DRYFMufe.js} +6 -6
- package/dist/client/assets/{dist-BrKhPlc9.js → dist-DTGcVSU8.js} +1 -1
- package/dist/client/assets/dist-DlOxjzWi.js +9 -0
- package/dist/client/assets/{dist-BT9gJXCb.js → dist-DogjjM_B.js} +1 -1
- package/dist/client/assets/dist-Dyv_RvyB.js +23 -0
- package/dist/client/assets/dist-FUFeH5Cx.js +1 -0
- package/dist/client/assets/dist-It70ktCU.js +1 -0
- package/dist/client/assets/dist-QMvcnJHf.js +1 -0
- package/dist/client/assets/dist-QN1K2czv.js +11 -0
- package/dist/client/assets/dist-W8ilOKl3.js +1 -0
- package/dist/client/assets/dist-_17-t9qP.js +1 -0
- package/dist/client/assets/{dist-DeCV9FU4.js → dist-uE774WWs.js} +1 -1
- package/dist/client/assets/document-formatter-DtXmKZcy.js +1 -0
- package/dist/client/assets/drawioEditor-DO6IOaJ1.js +9 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-PfC84FyI.js → ebnfDiagram-PWID7BFC-IemGPf9X.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-ZINNZB2B-DYvm_QtE.js → ebnfDiagram-VR2GEFS7-9fXqy1DI.js} +1 -1
- package/dist/client/assets/editor-session-CPsqXY_A.js +13 -0
- package/dist/client/assets/elk-IJKZMXRS-DsbrbBYg.js +27 -0
- package/dist/client/assets/embedded-editor-base-C44ZcCZv.css +1 -0
- package/dist/client/assets/{embedpdf-TkbBYowM.js → embedpdf-BfUI-3pv.js} +1 -1
- package/dist/client/assets/{embedpdf-cl9qiF45-itpgqvGY.js → embedpdf-cl9qiF45-CLMxJTMP.js} +2 -2
- package/dist/client/assets/en-7ZVUCEYH-C_VXKxxb.js +1 -0
- package/dist/client/assets/{erDiagram-OPXOYQCR-o8GRv8A_.js → erDiagram-O2IAPWRE-Cdadm-HN.js} +2 -2
- package/dist/client/assets/{erDiagram-RLTQ6QDP-C9cJl5NB.js → erDiagram-RLTQ6QDP-CGGmjm8t.js} +1 -1
- package/dist/client/assets/eventmodeling-K75KTNOO-BWBlZ5cR.js +1 -0
- package/dist/client/assets/eventmodeling-NTZA5JFV-CDy1bN6j.js +1 -1
- package/dist/client/assets/excalidraw-element-link-NQnskRkm.js +1 -0
- package/dist/client/assets/excalidraw-scene-DLlw4jyc.js +1 -0
- package/dist/client/assets/excalidrawEditor-DiKOIudt.css +1 -0
- package/dist/client/assets/excalidrawEditor-W2lZ5Mej.js +9 -0
- package/dist/client/assets/export-page-Bomh9oEr.js +46 -0
- package/dist/client/assets/exportDocument-BFIOECJ3.js +2 -0
- package/dist/client/assets/exportDocument-D0mONgQ4.css +1 -0
- package/dist/client/assets/file-kind-BGxTVHan.js +1 -0
- package/dist/client/assets/{file-search-BA8o_gs0.js → file-search-Dk2O14Vw.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-DcVVYJUZ.js +1 -0
- package/dist/client/assets/flowDiagram-OXPTDLAJ-Dy6y46oF.js +1 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-x40PW0Ah.js → ganttDiagram-EL5Y4UJY-Duu4KnsU.js} +1 -1
- package/dist/client/assets/ganttDiagram-R7TSEDQI-BWy52pjn.js +292 -0
- package/dist/client/assets/gitGraph-4MIJSDKK-B-rpDYbz.js +1 -1
- package/dist/client/assets/gitGraph-VSP46ZUC-1499lQAI.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-ahsNaZsp.js → gitGraphDiagram-WWUBYQGX-CaJ0vC1a.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-XJZIOB7I-DJwp9ilx.js +106 -0
- package/dist/client/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/client/assets/image-6XY3GD3F-DCWbu6d4.js +1 -0
- package/dist/client/assets/image-lightbox-controller-DAcXEXxT.js +184 -0
- package/dist/client/assets/index-CglpXOg7.css +1 -0
- package/dist/client/assets/index-D3WGkxfI.js +960 -0
- package/dist/client/assets/info-A6RAGUB7-BlutK7BT.js +1 -1
- package/dist/client/assets/info-OHQRW6UA-BDFQOo4H.js +1 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-MrZyptIy.js → infoDiagram-27XIBGKW-DBJh7QOu.js} +1 -1
- package/dist/client/assets/{infoDiagram-VRGFBTTK-KkUjDRAQ.js → infoDiagram-5W2HQ5XZ-DmWsqJ0w.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-Bnt9yM0O.js → ishikawaDiagram-5VMMS53U-CeEbSJem.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-OU5B5YK6-Di9BcpOY.js → ishikawaDiagram-K3B6WC7H-DH1NI35G.js} +2 -2
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-Bl0abK1r.js → journeyDiagram-3NMN7TZE-BoIEEr6U.js} +1 -1
- package/dist/client/assets/{journeyDiagram-ZHPQQLJL-J-sGrs9e.js → journeyDiagram-COXZFDF6-CnHV2f9V.js} +3 -3
- package/dist/client/assets/{kanban-definition-PNTS6WVX-5ZDV_yIa.js → kanban-definition-P3RFRI5V-Cxvn5ZAG.js} +4 -4
- package/dist/client/assets/{kanban-definition-UXKFOSKX-DMgUfNt0.js → kanban-definition-UXKFOSKX-BY16Tgbr.js} +1 -1
- package/dist/client/assets/keyboard-shortcuts-BWNCBJqw.js +1 -0
- package/dist/client/assets/lobby-presence-DNNXcuwR.js +1 -0
- package/dist/client/assets/{markdown-CZ1tYEW8.js → markdown-DU84620G.js} +1 -1
- package/dist/client/assets/markdown-headings-zYEUahfW.js +41 -0
- package/dist/client/assets/{markdown-math-CyyBVuDO.js → markdown-math-B96cnrz1.js} +11 -11
- package/dist/client/assets/{mermaid-parser.core-Bpogunsh.js → mermaid-parser.core-R-uwU5kf.js} +3 -3
- package/dist/client/assets/{mermaid-parser.core-DBy--Jc0.js → mermaid-parser.core-mE6t1qEO.js} +1 -1
- package/dist/client/assets/mermaid.core-D8Z6LN7O.js +10 -0
- package/dist/client/assets/{mindmap-definition-NLK3R4M7-ByVb-0vj.js → mindmap-definition-LZFPQGKD-DVfFenfJ.js} +3 -3
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-DBZN5_GH.js → mindmap-definition-YA3MSWOX-DtiE80UJ.js} +1 -1
- package/dist/client/assets/packet-AYTQ26CC-DvbY8dI6.js +1 -1
- package/dist/client/assets/packet-JDAUHWVQ-DJ9K2MA4.js +1 -0
- package/dist/client/assets/{pegDiagram-GJSIUBJH-aost1sSO.js → pegDiagram-BGZESJAR-DxfvEcwv.js} +1 -1
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DRo8CZu3.js → pegDiagram-XKGWAZYB-CyozY-Kt.js} +1 -1
- package/dist/client/assets/pie-WAS4IAKB-DWSm9SSD.js +1 -1
- package/dist/client/assets/pie-XZMESJXO-CQIQasY-.js +1 -0
- package/dist/client/assets/{pieDiagram-5QR66LMP-BICdpEuz.js → pieDiagram-CAPJLFHJ-CRlMcrv3.js} +2 -2
- package/dist/client/assets/{pieDiagram-E7YTZNPT-CZThaHsc.js → pieDiagram-E7YTZNPT-BRdmTkDq.js} +1 -1
- package/dist/client/assets/preload-helper-CYtfOYgs.js +1 -0
- package/dist/client/assets/preview-render-compiler-D_aJv7Ew.js +3 -0
- package/dist/client/assets/{preview-render-worker-BA4VK_GY.js → preview-render-worker-dFNyrVyB.js} +46 -42
- package/dist/client/assets/prod-ItolaHmf.js +145 -0
- package/dist/client/assets/prod-rdly4JJQ.js +1 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-C5iowkl_.js → quadrantDiagram-AXDQQJYC-7QyPIXkF.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-O4NWA36T-Bgx5bF97.js → quadrantDiagram-GDMTTRAM-DE30uqTl.js} +3 -3
- package/dist/client/assets/quick-switcher-controller-DsK70I7K.js +1 -0
- package/dist/client/assets/radar-ABXABTNO-ByfpCXh9.js +1 -0
- package/dist/client/assets/radar-RG4KPBEZ-BfNQUEQy.js +1 -1
- package/dist/client/assets/railroad-74A4TZTK-BaQ6U6eJ.js +1 -1
- package/dist/client/assets/railroad-I3PHUGI6-CTUn9HhI.js +1 -0
- package/dist/client/assets/railroad-abnf-HS5TGJTU-BJ3KZw7J.js +1 -1
- package/dist/client/assets/railroad-abnf-LNEFI6M7-Ct-b-y0F.js +1 -0
- package/dist/client/assets/railroad-ebnf-DWYD2UWJ-BZMYMWOe.js +1 -0
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-DA0sEq7w.js +1 -1
- package/dist/client/assets/railroad-peg-JR7BVNR7-Dj23PxYc.js +1 -0
- package/dist/client/assets/railroad-peg-WCYAUIDC-DCmlICzO.js +1 -1
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-BTt_rKjT.js → railroadDiagram-O6MQD6OU-BPk3RS2D.js} +1 -1
- package/dist/client/assets/{railroadDiagram-XR7U4H2S-DbVoNgSz.js → railroadDiagram-SM67HX2B-CZRXJ647.js} +1 -1
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-BkvazG7r.js → requirementDiagram-BXWQKSXE-BRwfym6z.js} +1 -1
- package/dist/client/assets/{requirementDiagram-PLB6GJNP-BT0j9C40.js → requirementDiagram-X7JNWC4A-BofCuLJA.js} +4 -4
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-C_IemB9h.js → sankeyDiagram-P5KCCOFB-D4LcrKpW.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-IPEJSGJF-X1BCUIrf.js → sankeyDiagram-UM26HJRW-CLKMDbej.js} +3 -3
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CxFBgLGd.js → sequenceDiagram-WJ2MYXX4-DQjtv-Bs.js} +1 -1
- package/dist/client/assets/sequenceDiagram-ZO4K6R2Y-CDP_m6ma.js +169 -0
- package/dist/client/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BMR1ovSU.js +1 -0
- package/dist/client/assets/src-D5mvpivG.js +2 -0
- package/dist/client/assets/{standalone-DbyFjvTU.js → standalone-D_lg5NC9.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-BaSQ6Owj.js → stateDiagram-D77RDMKH-DSMvV-kF.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-ClmOOUsT.js +1 -0
- package/dist/client/assets/{stateDiagram-v2-GCMORJYK-CsVcfwVp.js → stateDiagram-v2-TBUQTH76-Dc5-FKzd.js} +2 -2
- package/dist/client/assets/{swimlanes-42K2YHIH-CALFEmiS.js → swimlanes-42K2YHIH-BUz31lO3.js} +1 -1
- package/dist/client/assets/{swimlanes-2SLR337P-CnYTVqRX.js → swimlanes-N4OXWK64-Dd3dFdPD.js} +1 -1
- package/dist/client/assets/swimlanesDiagram-K3J5GTZL-Bz9gFdls.js +8 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DoDs3zod.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-zse-5Dbm.js → timeline-definition-24CTP7MA-BvqCvSjy.js} +1 -1
- package/dist/client/assets/{timeline-definition-EJHVYXUP-BhqPlKkN.js → timeline-definition-YOQAKGHF-D2XWPSLs.js} +3 -3
- package/dist/client/assets/treeView-D4JQ5SDB-DcX7Bx0H.js +1 -0
- package/dist/client/assets/treeView-Q6P3EWNA-BvHOzbu9.js +1 -1
- package/dist/client/assets/treemap-SAJKECNS-BcCHmcAX.js +1 -0
- package/dist/client/assets/treemap-WGGIJYW6-oety7paS.js +1 -1
- package/dist/client/assets/usecaseDiagram-VIAY4XPW-CDe-IMjS.js +328 -0
- package/dist/client/assets/vault-api-client-DrRW0QwO.js +7 -0
- package/dist/client/assets/{vennDiagram-4TSXK5OY-DgHr6cJD.js → vennDiagram-4TSXK5OY-BW6Qp12d.js} +1 -1
- package/dist/client/assets/{vennDiagram-UO4OBE2U-BRwLn2Q1.js → vennDiagram-BLWOH2XV-KxIPKpHi.js} +3 -3
- package/dist/client/assets/wardley-7MLQ67FV-DOahSwbZ.js +1 -0
- package/dist/client/assets/wardley-WFR3VGLG-BdwDpX_I.js +1 -1
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-9A60XZis.js → wardleyDiagram-VM6X3IG4-BoCETVsK.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-VNRHLVJA-B_tMcVFG.js → wardleyDiagram-YMQ3BMBF-BNYqEy7M.js} +4 -4
- package/dist/client/assets/{wiki-link-resolver-Dch88erO.js → wiki-link-resolver-BCdGUpdu.js} +1 -1
- package/dist/client/assets/{worker-engine-BCCrxWYm-CX0qJ_cG.js → worker-engine-BCCrxWYm-1fphds7O.js} +1 -1
- package/dist/client/assets/workspace-sync-client-C_P9wtjf.js +1 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-D00iy-wB.js → xychartDiagram-S5SC5T6Z-eB5Fu9P7.js} +1 -1
- package/dist/client/assets/xychartDiagram-TAQBALBS-Sv6aaptV.js +7 -0
- package/dist/client/assets/yjs-Jd9fna_Y.js +4 -0
- package/dist/client/assets/yjs-provider-reset-guard-Cqu0Z6xH.js +1 -0
- package/dist/client/canvas-editor.html +16 -0
- package/dist/client/drawio-editor.html +7 -6
- package/dist/client/excalidraw-editor.html +31 -30
- package/dist/client/export-document.html +4 -3
- package/dist/client/index.html +2 -2
- package/package.json +27 -29
- package/scripts/local-compose.mjs +101 -0
- package/src/client/app/canvas-editor.html +13 -0
- package/src/client/application/app-shell/git-feature.js +3 -2
- package/src/client/application/app-shell/ui-feature-identity.js +1 -0
- package/src/client/application/app-shell/ui-feature-shell.js +2 -0
- package/src/client/application/app-shell/ui-feature-tab-activity.js +4 -0
- package/src/client/application/app-shell/workspace-feature.js +8 -34
- package/src/client/application/diagram-chrome.js +8 -11
- package/src/client/application/diagram-preview-hydrator.js +5 -5
- package/src/client/application/mermaid-preview-hydrator.js +3 -2
- package/src/client/application/plantuml-preview-hydrator.js +6 -0
- package/src/client/application/preview-render-compiler.js +34 -98
- package/src/client/application/preview-render-executor.js +3 -0
- package/src/client/application/preview-render-worker.js +2 -0
- package/src/client/application/preview-renderer.js +9 -5
- package/src/client/application/workspace-coordinator.js +18 -10
- package/src/client/application/workspace-preview-controller.js +105 -158
- package/src/client/application/workspace-route-controller.js +29 -54
- package/src/client/bootstrap/canvas-markdown-preview.js +87 -0
- package/src/client/bootstrap/collabmd-app-shell.js +28 -13
- package/src/client/canvas-editor.js +143 -0
- package/src/client/domain/canvas-view.js +50 -0
- package/src/client/domain/excalidraw-scene.js +4 -49
- package/src/client/domain/highlight-runtime.js +0 -4
- package/src/client/domain/markdown-formatting.js +12 -94
- package/src/client/domain/markdown-headings.js +98 -0
- package/src/client/domain/wiki-link-completions.js +42 -10
- package/src/client/infrastructure/browser-preferences-port.js +27 -33
- package/src/client/infrastructure/canvas-room-client.js +210 -0
- package/src/client/infrastructure/canvas-text-editor.js +36 -0
- package/src/client/infrastructure/comment-thread-store.js +19 -77
- package/src/client/infrastructure/editor-view-adapter.js +0 -30
- package/src/client/infrastructure/excalidraw-room-client.js +22 -10
- package/src/client/infrastructure/hosted-api-client.js +4 -2
- package/src/client/infrastructure/vault-api-client.js +4 -4
- package/src/client/presentation/canvas-editor-view.js +1028 -0
- package/src/client/presentation/canvas-embed-controller.js +116 -0
- package/src/client/presentation/canvas-picker.js +111 -0
- package/src/client/presentation/drawio-embed-controller.js +1 -3
- package/src/client/presentation/excalidraw-embed-controller.js +10 -3
- package/src/client/presentation/file-action-controller.js +37 -0
- package/src/client/presentation/file-explorer-controller.js +1 -2
- package/src/client/presentation/file-explorer-view.js +7 -1
- package/src/client/presentation/file-icon-svg.js +1 -1
- package/src/client/presentation/file-tree-state.js +4 -1
- package/src/client/presentation/image-lightbox-controller.js +1 -5
- package/src/client/presentation/team-settings-controller.js +44 -10
- package/src/client/styles/components/actions.css +4 -16
- package/src/client/styles/components/surfaces.css +0 -7
- package/src/client/styles/features/comment-card.css +1 -2
- package/src/client/styles/features/comment-markdown.css +0 -18
- package/src/client/styles/features/diagram-preview.css +0 -18
- package/src/client/styles/features/git-branch.css +0 -4
- package/src/client/styles/foundation/themes.css +6 -0
- package/src/client/styles/foundation/utilities.css +1 -2
- package/src/client/styles/layout/editor-page.css +0 -13
- package/src/client/styles/layout/responsive.css +2 -7
- package/src/client/styles/layout/shell.css +13 -1
- package/src/client/styles/layout/view-modes.css +1 -0
- package/src/client/styles/surfaces/canvas-editor.css +239 -0
- package/src/domain/canvas-room-codec.js +359 -0
- package/src/domain/collabmd-content-capabilities.js +10 -1
- package/src/domain/comment-threads.js +49 -5
- package/src/domain/excalidraw-room-codec.js +23 -73
- package/src/domain/excalidraw-scene.js +48 -0
- package/src/domain/file-kind.js +13 -1
- package/src/server/application/workspace-reconciliation.js +0 -14
- package/src/server/domain/backlink-index.js +1 -36
- package/src/server/domain/bases/base-index-snapshot-store.js +41 -24
- package/src/server/domain/bases/base-query-service.js +1 -47
- package/src/server/domain/collaboration/collaboration-room.js +149 -15
- package/src/server/domain/collaboration/room-registry.js +7 -7
- package/src/server/domain/hosted-workspace.js +29 -2
- package/src/server/domain/plantuml-encoder.js +7 -43
- package/src/server/infrastructure/git/diff-service.js +1 -5
- package/src/server/infrastructure/git/history-service.js +18 -2
- package/src/server/infrastructure/git/untracked-files.js +1 -1
- package/src/server/infrastructure/http/create-git-api-command-handler.js +60 -149
- package/src/server/infrastructure/http/create-hosted-api-handler.js +5 -2
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +2 -2
- package/src/server/infrastructure/http/http-response.js +2 -36
- package/src/server/infrastructure/http/request-body.js +14 -49
- package/src/server/infrastructure/persistence/hosted-metadata-store.js +12 -3
- package/src/server/infrastructure/persistence/path-utils.js +8 -2
- package/src/server/infrastructure/persistence/vault-file-store.js +26 -60
- package/src/server/{infrastructure/git → shared}/image-mime.js +2 -2
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-kJfwukbn.js +0 -1
- package/dist/client/assets/_basePickBy-QJXUw_pr.js +0 -1
- package/dist/client/assets/_baseUniq-DR4vVjyC.js +0 -1
- package/dist/client/assets/architecture-7GRP2DOG-DJVtYNt-.js +0 -1
- package/dist/client/assets/browser-utils-S4QYY31r.js +0 -1
- package/dist/client/assets/chunk-3FUC2YCW-8tZlhTGX.js +0 -1
- package/dist/client/assets/chunk-7M6MHVWA-BDakkknV.js +0 -213
- package/dist/client/assets/chunk-FOHPRMQF-Dmmo5Kak.js +0 -161
- package/dist/client/assets/chunk-O7XYJQB3-D0_ZY2za.js +0 -126
- package/dist/client/assets/chunk-T5WUORNS-DD7KlyY6.js +0 -40
- package/dist/client/assets/chunk-Z7XXMR3K-P4Vaq8hD.js +0 -10
- package/dist/client/assets/chunk-ZIGJFQKS-b8OUHKK5.js +0 -2
- package/dist/client/assets/classDiagram-ZZMXUADV-QLbh_Nkf.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-QLbh_Nkf.js +0 -1
- package/dist/client/assets/comment-thread-store-CHvy8GF8.js +0 -1
- package/dist/client/assets/comment-threads-nJhK1yfc.js +0 -4
- package/dist/client/assets/cynefin-OW5HDTMX-e-Quc9Yl.js +0 -1
- package/dist/client/assets/dagre-D7FiCLLP.js +0 -1
- package/dist/client/assets/deferred-collab-D4cxEdad.css +0 -1
- package/dist/client/assets/deferred-git-BHKtkFYi.css +0 -1
- package/dist/client/assets/deferred-preview-BAXYgf4T.css +0 -1
- package/dist/client/assets/diagram-ATOU4E4O-B5uX-jVO.js +0 -3
- package/dist/client/assets/dist-B2ZNQifU.js +0 -1
- package/dist/client/assets/dist-B6QCMNze.js +0 -1
- package/dist/client/assets/dist-BIAv19LO.js +0 -1
- package/dist/client/assets/dist-BKYp1-dl.js +0 -13
- package/dist/client/assets/dist-BSTbcioM.js +0 -11
- package/dist/client/assets/dist-BTVg1m-V.js +0 -1
- package/dist/client/assets/dist-Bi9ZIYXq.js +0 -9
- package/dist/client/assets/dist-Bqo-XRVG.js +0 -1
- package/dist/client/assets/dist-CBsvKPcT.js +0 -1
- package/dist/client/assets/dist-CJBYh9Wy.js +0 -23
- package/dist/client/assets/dist-CRwfjtwz.js +0 -2
- package/dist/client/assets/dist-CX7Kc61C.js +0 -1
- package/dist/client/assets/dist-CYfPeupF.js +0 -1
- package/dist/client/assets/dist-CucpLxUY.js +0 -1
- package/dist/client/assets/dist-DHBMeCxC.js +0 -6
- package/dist/client/assets/dist-DLEg9d4u.js +0 -1
- package/dist/client/assets/dist-DYFBD-wW.js +0 -1
- package/dist/client/assets/dist-DugWGOjQ.js +0 -1
- package/dist/client/assets/dist-NNrRnB-f.js +0 -1
- package/dist/client/assets/dist-PUxZqhFe.js +0 -1
- package/dist/client/assets/dist-cYiJMWHJ.js +0 -1
- package/dist/client/assets/dist-iKC7go4m.js +0 -1
- package/dist/client/assets/document-formatter-6I63vstk.js +0 -1
- package/dist/client/assets/drawioEditor-DNTjsjzJ.js +0 -9
- package/dist/client/assets/editor-session-D8GGkJak.js +0 -14
- package/dist/client/assets/elk-276RUBZZ-B8LB3Pac.js +0 -27
- package/dist/client/assets/embedded-editor-base-elU0Avik.css +0 -1
- package/dist/client/assets/en-JTHJ2SIP-DBz9bRq4.js +0 -1
- package/dist/client/assets/eventmodeling-NTZA5JFV-Dxtv5D-P.js +0 -1
- package/dist/client/assets/excalidraw-element-link-BRZOTIeI.js +0 -7
- package/dist/client/assets/excalidraw-scene-6uROebca.js +0 -1
- package/dist/client/assets/excalidrawEditor-BCTFp_CV.css +0 -1
- package/dist/client/assets/excalidrawEditor-D0orhhku.js +0 -9
- package/dist/client/assets/export-page-CVJNMERq.js +0 -46
- package/dist/client/assets/exportDocument-Ba-n1X4U.css +0 -1
- package/dist/client/assets/exportDocument-DSshnmnZ.js +0 -2
- package/dist/client/assets/file-kind-BwMIcZbi.js +0 -1
- package/dist/client/assets/flowDiagram-HODETNUW-Bq4470NR.js +0 -1
- package/dist/client/assets/flowDiagram-KWPJA3E3-DQG9qGiU.js +0 -1
- package/dist/client/assets/ganttDiagram-FUAMR5RP-BQpsI7gQ.js +0 -292
- package/dist/client/assets/gitGraph-4MIJSDKK-ZaBIE4Lb.js +0 -1
- package/dist/client/assets/gitGraphDiagram-X574FWY7-wbs_2m9f.js +0 -106
- package/dist/client/assets/graphlib-C9Tl7QrK.js +0 -1
- package/dist/client/assets/image-I42VHMUI-B2-c_ifV.js +0 -1
- package/dist/client/assets/index-BCgNcVNs.css +0 -1
- package/dist/client/assets/index-BpmR5Qqk.js +0 -1140
- package/dist/client/assets/info-A6RAGUB7-BcGxhjED.js +0 -1
- package/dist/client/assets/lobby-presence-KJrYhMdN.js +0 -1
- package/dist/client/assets/mermaid.core-BeOUPE3Q.js +0 -10
- package/dist/client/assets/packet-AYTQ26CC-CqpSjTWl.js +0 -1
- package/dist/client/assets/pie-WAS4IAKB-Bg2eCNl5.js +0 -1
- package/dist/client/assets/preload-helper-BAlEavIC.js +0 -1
- package/dist/client/assets/preview-render-compiler-BmWQStaL.js +0 -43
- package/dist/client/assets/prod-CA0ie9yD.js +0 -145
- package/dist/client/assets/prod-yKJZ3E4s.js +0 -1
- package/dist/client/assets/quick-switcher-controller-B5vT9Vj5.js +0 -1
- package/dist/client/assets/radar-RG4KPBEZ-DxExotg6.js +0 -1
- package/dist/client/assets/railroad-74A4TZTK-FfHmeIQk.js +0 -1
- package/dist/client/assets/railroad-abnf-HS5TGJTU-CLkdDRsJ.js +0 -1
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-B162zvcG.js +0 -1
- package/dist/client/assets/railroad-peg-WCYAUIDC-CGs5GeJ5.js +0 -1
- package/dist/client/assets/sequenceDiagram-PO4LG4MO-B-2QfXhV.js +0 -169
- package/dist/client/assets/sizeCapture-INFHLROL-B0uUizjq.js +0 -1
- package/dist/client/assets/sizeCapture-INFHLROL-DrzwYt5z.js +0 -1
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CLlwrdvU.js +0 -1
- package/dist/client/assets/swimlanesDiagram-TC7HE7FX-Bh4tmz27.js +0 -8
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-xhehUG9e.js +0 -8
- package/dist/client/assets/treeView-Q6P3EWNA-Cccy0ngL.js +0 -1
- package/dist/client/assets/treemap-WGGIJYW6-sMRCjCtX.js +0 -1
- package/dist/client/assets/usecaseDiagram-POWQR4AR-DR2LHQYs.js +0 -328
- package/dist/client/assets/vault-api-client-NS_EVHJ3.js +0 -7
- package/dist/client/assets/wardley-WFR3VGLG-Y7xty_3m.js +0 -1
- package/dist/client/assets/workspace-sync-client-BrMtf6zN.js +0 -1
- package/dist/client/assets/xychartDiagram-PMCCYNJV-fRpZ1MA5.js +0 -7
- package/dist/client/assets/yjs-B41tM6Sz.js +0 -4
- package/dist/client/assets/yjs-provider-reset-guard-DpntzUDR.js +0 -1
- package/scripts/local-plantuml-compose.mjs +0 -92
- package/scripts/local-structurizr-compose.mjs +0 -101
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import { generateNKeysBetween } from '@excalidraw/fractional-indexing';
|
|
3
|
+
import { simpleDiffString } from 'lib0/diff';
|
|
4
|
+
|
|
5
|
+
export const CANVAS_NODES_KEY = 'canvas-nodes';
|
|
6
|
+
export const CANVAS_EDGES_KEY = 'canvas-edges';
|
|
7
|
+
export const CANVAS_META_KEY = 'canvas-meta';
|
|
8
|
+
export const CANVAS_NODE_ORDER_KEY = 'canvas-node-order';
|
|
9
|
+
export const CANVAS_EDGE_ORDER_KEY = 'canvas-edge-order';
|
|
10
|
+
|
|
11
|
+
function invalid() {
|
|
12
|
+
const error = new Error('Invalid JSON Canvas 1.0 document');
|
|
13
|
+
error.code = 'CANVAS_INVALID';
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function requireObject(value) {
|
|
18
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw invalid();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function requireString(value) {
|
|
22
|
+
if (typeof value !== 'string') throw invalid();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function optionalString(value, key, choices = null) {
|
|
26
|
+
if (!Object.hasOwn(value, key)) return;
|
|
27
|
+
requireString(value[key]);
|
|
28
|
+
if (choices && !choices.includes(value[key])) throw invalid();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateColor(value) {
|
|
32
|
+
if (Object.hasOwn(value, 'color') && !/^(?:[1-6]|#[\da-f]{6})$/i.test(value.color)) throw invalid();
|
|
33
|
+
optionalString(value, 'color');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validateNode(node) {
|
|
37
|
+
requireObject(node);
|
|
38
|
+
requireString(node.id);
|
|
39
|
+
if (!['text', 'file', 'link', 'group'].includes(node.type)) throw invalid();
|
|
40
|
+
for (const key of ['x', 'y', 'width', 'height']) {
|
|
41
|
+
if (!Number.isInteger(node[key])) throw invalid();
|
|
42
|
+
}
|
|
43
|
+
validateColor(node);
|
|
44
|
+
if (node.type === 'text') requireString(node.text);
|
|
45
|
+
if (node.type === 'file') {
|
|
46
|
+
requireString(node.file);
|
|
47
|
+
optionalString(node, 'subpath');
|
|
48
|
+
if (Object.hasOwn(node, 'subpath') && !node.subpath.startsWith('#')) throw invalid();
|
|
49
|
+
}
|
|
50
|
+
if (node.type === 'link') requireString(node.url);
|
|
51
|
+
if (node.type === 'group') {
|
|
52
|
+
optionalString(node, 'label');
|
|
53
|
+
optionalString(node, 'background');
|
|
54
|
+
optionalString(node, 'backgroundStyle', ['cover', 'ratio', 'repeat']);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function validateEdge(edge) {
|
|
59
|
+
requireObject(edge);
|
|
60
|
+
for (const key of ['id', 'fromNode', 'toNode']) requireString(edge[key]);
|
|
61
|
+
for (const key of ['fromSide', 'toSide']) optionalString(edge, key, ['top', 'right', 'bottom', 'left']);
|
|
62
|
+
for (const key of ['fromEnd', 'toEnd']) optionalString(edge, key, ['none', 'arrow']);
|
|
63
|
+
optionalString(edge, 'label');
|
|
64
|
+
validateColor(edge);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function validateCanvasDocument(value) {
|
|
68
|
+
requireObject(value);
|
|
69
|
+
for (const key of ['nodes', 'edges']) {
|
|
70
|
+
if (Object.hasOwn(value, key) && !Array.isArray(value[key])) throw invalid();
|
|
71
|
+
const ids = new Set();
|
|
72
|
+
for (const item of value[key] ?? []) {
|
|
73
|
+
(key === 'nodes' ? validateNode : validateEdge)(item);
|
|
74
|
+
if (ids.has(item.id)) throw invalid();
|
|
75
|
+
ids.add(item.id);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const nodeIds = new Set((value.nodes ?? []).map((node) => node.id));
|
|
79
|
+
if ((value.edges ?? []).some((edge) => !nodeIds.has(edge.fromNode) || !nodeIds.has(edge.toNode))) throw invalid();
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function parseCanvasJson(text) {
|
|
84
|
+
try {
|
|
85
|
+
if (typeof text !== 'string') throw invalid();
|
|
86
|
+
return validateCanvasDocument(JSON.parse(text, (_key, value) => {
|
|
87
|
+
if (typeof value === 'number' && !Number.isFinite(value)) throw invalid();
|
|
88
|
+
return value;
|
|
89
|
+
}));
|
|
90
|
+
} catch {
|
|
91
|
+
throw invalid();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function equal(left, right) {
|
|
96
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function readItem(item) {
|
|
100
|
+
if (!(item instanceof Y.Map)) throw invalid();
|
|
101
|
+
return Object.fromEntries(Array.from(item, ([key, value]) => [
|
|
102
|
+
key, value instanceof Y.Text ? value.toString() : structuredClone(value),
|
|
103
|
+
]));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function updateText(text, value) {
|
|
107
|
+
const { index, remove, insert } = simpleDiffString(text.toString(), value);
|
|
108
|
+
if (remove) text.delete(index, remove);
|
|
109
|
+
if (insert) text.insert(index, insert);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function writeProperties(map, value, { textNode = false, replace = false } = {}) {
|
|
113
|
+
if (replace) {
|
|
114
|
+
for (const key of map.keys()) if (!Object.hasOwn(value, key)) map.delete(key);
|
|
115
|
+
}
|
|
116
|
+
for (const [key, next] of Object.entries(value)) {
|
|
117
|
+
const previous = map.get(key);
|
|
118
|
+
if (next === undefined) {
|
|
119
|
+
map.delete(key);
|
|
120
|
+
} else if (textNode && key === 'text') {
|
|
121
|
+
if (previous instanceof Y.Text) updateText(previous, next);
|
|
122
|
+
else map.set(key, new Y.Text(next));
|
|
123
|
+
} else if (!equal(previous, next)) {
|
|
124
|
+
map.set(key, structuredClone(next));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function sortedItems(doc, mapKey, orderKey) {
|
|
130
|
+
const order = doc.getMap(orderKey);
|
|
131
|
+
return Array.from(doc.getMap(mapKey), ([id, value]) => {
|
|
132
|
+
const item = readItem(value);
|
|
133
|
+
if (item.id !== id) throw invalid();
|
|
134
|
+
return item;
|
|
135
|
+
}).sort((left, right) => {
|
|
136
|
+
const leftOrder = String(order.get(left.id) ?? '');
|
|
137
|
+
const rightOrder = String(order.get(right.id) ?? '');
|
|
138
|
+
if (leftOrder !== rightOrder) return leftOrder < rightOrder ? -1 : 1;
|
|
139
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function isCanvasRoomDocStructured(doc) {
|
|
144
|
+
return doc?.getMap(CANVAS_META_KEY).get('schemaVersion') === 1;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function buildCanvasRoomDocument(doc) {
|
|
148
|
+
if (!isCanvasRoomDocStructured(doc)) throw invalid();
|
|
149
|
+
const meta = doc.getMap(CANVAS_META_KEY);
|
|
150
|
+
const properties = meta.get('properties');
|
|
151
|
+
const result = properties instanceof Y.Map ? readItem(properties) : {};
|
|
152
|
+
const nodes = sortedItems(doc, CANVAS_NODES_KEY, CANVAS_NODE_ORDER_KEY);
|
|
153
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
154
|
+
const edges = sortedItems(doc, CANVAS_EDGES_KEY, CANVAS_EDGE_ORDER_KEY).filter((edge) => {
|
|
155
|
+
validateEdge(edge);
|
|
156
|
+
// A concurrent node deletion wins over an edge connected while offline.
|
|
157
|
+
return nodeIds.has(edge.fromNode) && nodeIds.has(edge.toNode);
|
|
158
|
+
});
|
|
159
|
+
if (meta.get('nodesPresent') || nodes.length) result.nodes = nodes;
|
|
160
|
+
if (meta.get('edgesPresent') || edges.length) result.edges = edges;
|
|
161
|
+
return validateCanvasDocument(result);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function serializeCanvasRoomDocument(doc) {
|
|
165
|
+
return serializeCanvasDocument(buildCanvasRoomDocument(doc));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function serializeCanvasDocument(document) {
|
|
169
|
+
validateCanvasDocument(document);
|
|
170
|
+
return JSON.stringify(document, (_key, value) => {
|
|
171
|
+
if (typeof value === 'number' && !Number.isFinite(value)) throw invalid();
|
|
172
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
173
|
+
? Object.fromEntries(Object.keys(value).sort().map((key) => [key, value[key]]))
|
|
174
|
+
: value;
|
|
175
|
+
}, 2) + '\n';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function replaceCanvasRoomDocument(doc, document) {
|
|
179
|
+
validateCanvasDocument(document);
|
|
180
|
+
const meta = doc.getMap(CANVAS_META_KEY);
|
|
181
|
+
if (meta.get('schemaVersion') !== 1) meta.set('schemaVersion', 1);
|
|
182
|
+
let properties = meta.get('properties');
|
|
183
|
+
if (!(properties instanceof Y.Map)) {
|
|
184
|
+
properties = new Y.Map();
|
|
185
|
+
meta.set('properties', properties);
|
|
186
|
+
}
|
|
187
|
+
writeProperties(properties, Object.fromEntries(Object.entries(document).filter(([key]) => !['nodes', 'edges'].includes(key))), { replace: true });
|
|
188
|
+
for (const [key, mapKey, orderKey] of [
|
|
189
|
+
['nodes', CANVAS_NODES_KEY, CANVAS_NODE_ORDER_KEY],
|
|
190
|
+
['edges', CANVAS_EDGES_KEY, CANVAS_EDGE_ORDER_KEY],
|
|
191
|
+
]) {
|
|
192
|
+
const items = document[key] ?? [];
|
|
193
|
+
const map = doc.getMap(mapKey);
|
|
194
|
+
const order = doc.getMap(orderKey);
|
|
195
|
+
const ids = new Set(items.map((item) => item.id));
|
|
196
|
+
for (const id of map.keys()) if (!ids.has(id)) map.delete(id);
|
|
197
|
+
for (const id of order.keys()) if (!ids.has(id)) order.delete(id);
|
|
198
|
+
const indices = generateNKeysBetween(null, null, items.length);
|
|
199
|
+
items.forEach((item, index) => {
|
|
200
|
+
let entry = map.get(item.id);
|
|
201
|
+
if (!(entry instanceof Y.Map)) {
|
|
202
|
+
entry = new Y.Map();
|
|
203
|
+
map.set(item.id, entry);
|
|
204
|
+
}
|
|
205
|
+
writeProperties(entry, item, { replace: true, textNode: key === 'nodes' && item.type === 'text' });
|
|
206
|
+
if (order.get(item.id) !== indices[index]) order.set(item.id, indices[index]);
|
|
207
|
+
});
|
|
208
|
+
const present = Object.hasOwn(document, key);
|
|
209
|
+
if (meta.get(`${key}Present`) !== present) meta.set(`${key}Present`, present);
|
|
210
|
+
}
|
|
211
|
+
return document;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function addItem(doc, item, mapKey, orderKey) {
|
|
215
|
+
const map = doc.getMap(mapKey);
|
|
216
|
+
if (map.has(item.id)) throw invalid();
|
|
217
|
+
const order = doc.getMap(orderKey);
|
|
218
|
+
const entries = sortedItems(doc, mapKey, orderKey);
|
|
219
|
+
const lastKey = entries.length ? order.get(entries.at(-1).id) : null;
|
|
220
|
+
const nextKey = generateNKeysBetween(lastKey ?? null, null, 1)[0];
|
|
221
|
+
const value = new Y.Map();
|
|
222
|
+
map.set(item.id, value);
|
|
223
|
+
writeProperties(value, item, { textNode: mapKey === CANVAS_NODES_KEY && item.type === 'text' });
|
|
224
|
+
order.set(item.id, nextKey);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function addCanvasNode(doc, node) {
|
|
229
|
+
validateNode(node);
|
|
230
|
+
return addItem(doc, node, CANVAS_NODES_KEY, CANVAS_NODE_ORDER_KEY);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function addCanvasEdge(doc, edge) {
|
|
234
|
+
validateEdge(edge);
|
|
235
|
+
const nodes = doc.getMap(CANVAS_NODES_KEY);
|
|
236
|
+
if (!nodes.has(edge.fromNode) || !nodes.has(edge.toNode)) throw invalid();
|
|
237
|
+
return addItem(doc, edge, CANVAS_EDGES_KEY, CANVAS_EDGE_ORDER_KEY);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function updateItem(doc, id, patch, key, validate) {
|
|
241
|
+
const entry = doc.getMap(key).get(id);
|
|
242
|
+
if (!(entry instanceof Y.Map)) return false;
|
|
243
|
+
requireObject(patch);
|
|
244
|
+
const previous = readItem(entry);
|
|
245
|
+
const next = Object.fromEntries(Object.entries({ ...previous, ...patch }).filter(([, value]) => value !== undefined));
|
|
246
|
+
if (next.id !== id) throw invalid();
|
|
247
|
+
validate(next);
|
|
248
|
+
if (equal(previous, next)) return false;
|
|
249
|
+
writeProperties(entry, patch, { textNode: key === CANVAS_NODES_KEY && next.type === 'text' });
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function updateCanvasNode(doc, id, patch) {
|
|
254
|
+
return updateItem(doc, id, patch, CANVAS_NODES_KEY, validateNode);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function updateCanvasEdge(doc, id, patch) {
|
|
258
|
+
return updateItem(doc, id, patch, CANVAS_EDGES_KEY, (edge) => {
|
|
259
|
+
validateEdge(edge);
|
|
260
|
+
const nodes = doc.getMap(CANVAS_NODES_KEY);
|
|
261
|
+
if (!nodes.has(edge.fromNode) || !nodes.has(edge.toNode)) throw invalid();
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function deleteCanvasEdges(doc, ids) {
|
|
266
|
+
let changed = false;
|
|
267
|
+
for (const id of ids) {
|
|
268
|
+
if (!doc.getMap(CANVAS_EDGES_KEY).has(id)) continue;
|
|
269
|
+
doc.getMap(CANVAS_EDGES_KEY).delete(id);
|
|
270
|
+
doc.getMap(CANVAS_EDGE_ORDER_KEY).delete(id);
|
|
271
|
+
changed = true;
|
|
272
|
+
}
|
|
273
|
+
return changed;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function deleteCanvasNodes(doc, ids) {
|
|
277
|
+
let changed = false;
|
|
278
|
+
const removed = new Set(ids);
|
|
279
|
+
for (const id of removed) {
|
|
280
|
+
if (!doc.getMap(CANVAS_NODES_KEY).has(id)) continue;
|
|
281
|
+
doc.getMap(CANVAS_NODES_KEY).delete(id);
|
|
282
|
+
doc.getMap(CANVAS_NODE_ORDER_KEY).delete(id);
|
|
283
|
+
changed = true;
|
|
284
|
+
}
|
|
285
|
+
const edgeIds = Array.from(doc.getMap(CANVAS_EDGES_KEY), ([id, edge]) => (
|
|
286
|
+
removed.has(edge.get('fromNode')) || removed.has(edge.get('toNode')) ? id : null
|
|
287
|
+
)).filter((id) => id !== null);
|
|
288
|
+
return deleteCanvasEdges(doc, edgeIds) || changed;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function reorderCanvasNode(doc, id, direction) {
|
|
292
|
+
if (!['front', 'back'].includes(direction)) throw invalid();
|
|
293
|
+
const nodes = sortedItems(doc, CANVAS_NODES_KEY, CANVAS_NODE_ORDER_KEY);
|
|
294
|
+
if (!nodes.some((node) => node.id === id) || nodes.length < 2) return false;
|
|
295
|
+
const last = direction === 'front' ? nodes.at(-1) : nodes[0];
|
|
296
|
+
if (last.id === id) return false;
|
|
297
|
+
const order = doc.getMap(CANVAS_NODE_ORDER_KEY);
|
|
298
|
+
const bound = order.get(last.id);
|
|
299
|
+
const nextKey = direction === 'front'
|
|
300
|
+
? generateNKeysBetween(bound, null, 1)[0]
|
|
301
|
+
: generateNKeysBetween(null, bound, 1)[0];
|
|
302
|
+
order.set(id, nextKey);
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function resolveCanvasExternalConflict(doc, choice) {
|
|
307
|
+
const meta = doc.getMap(CANVAS_META_KEY);
|
|
308
|
+
const conflict = meta.get('externalConflict');
|
|
309
|
+
if (!conflict) return false;
|
|
310
|
+
if (!['local', 'external'].includes(choice)) throw invalid();
|
|
311
|
+
if (choice === 'external') replaceCanvasRoomDocument(doc, parseCanvasJson(conflict.content));
|
|
312
|
+
meta.delete('externalConflict');
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Merge independent fields; overlapping changes stay preserved for explicit resolution.
|
|
317
|
+
export function mergeCanvasDocuments(baseline, local, external) {
|
|
318
|
+
let conflict = false;
|
|
319
|
+
function mergeValue(before, current, next) {
|
|
320
|
+
if (equal(current, before)) return next;
|
|
321
|
+
if (equal(next, before) || equal(current, next)) return current;
|
|
322
|
+
if (before && current && next && ![before, current, next].some((value) => typeof value !== 'object' || Array.isArray(value))) {
|
|
323
|
+
return Object.fromEntries(Array.from(new Set([...Object.keys(before), ...Object.keys(current), ...Object.keys(next)]), (key) => [
|
|
324
|
+
key, mergeValue(before[key], current[key], next[key]),
|
|
325
|
+
]).filter(([, value]) => value !== undefined));
|
|
326
|
+
}
|
|
327
|
+
conflict = true;
|
|
328
|
+
return current;
|
|
329
|
+
}
|
|
330
|
+
const result = mergeValue(
|
|
331
|
+
Object.fromEntries(Object.entries(baseline).filter(([key]) => !['nodes', 'edges'].includes(key))),
|
|
332
|
+
Object.fromEntries(Object.entries(local).filter(([key]) => !['nodes', 'edges'].includes(key))),
|
|
333
|
+
Object.fromEntries(Object.entries(external).filter(([key]) => !['nodes', 'edges'].includes(key))),
|
|
334
|
+
);
|
|
335
|
+
for (const key of ['nodes', 'edges']) {
|
|
336
|
+
const before = baseline[key] ?? [];
|
|
337
|
+
const current = local[key] ?? [];
|
|
338
|
+
const next = external[key] ?? [];
|
|
339
|
+
const currentById = new Map(current.map((item) => [item.id, item]));
|
|
340
|
+
const nextById = new Map(next.map((item) => [item.id, item]));
|
|
341
|
+
const beforeById = new Map(before.map((item) => [item.id, item]));
|
|
342
|
+
const beforeIds = before.map((item) => item.id);
|
|
343
|
+
const currentIds = current.map((item) => item.id);
|
|
344
|
+
const nextIds = next.map((item) => item.id);
|
|
345
|
+
const localReordered = !equal(currentIds.filter((id) => beforeById.has(id)), beforeIds.filter((id) => currentById.has(id)));
|
|
346
|
+
const externalReordered = !equal(nextIds.filter((id) => beforeById.has(id)), beforeIds.filter((id) => nextById.has(id)));
|
|
347
|
+
if (localReordered && externalReordered && !equal(currentIds, nextIds)) conflict = true;
|
|
348
|
+
const ids = Array.from(new Set(localReordered ? [...currentIds, ...nextIds] : [...nextIds, ...currentIds]));
|
|
349
|
+
const merged = ids.map((id) => mergeValue(beforeById.get(id), currentById.get(id), nextById.get(id))).filter((item) => item !== undefined);
|
|
350
|
+
const present = mergeValue(Object.hasOwn(baseline, key), Object.hasOwn(local, key), Object.hasOwn(external, key));
|
|
351
|
+
if (present || merged.length) result[key] = merged;
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
validateCanvasDocument(result);
|
|
355
|
+
} catch {
|
|
356
|
+
conflict = true;
|
|
357
|
+
}
|
|
358
|
+
return { conflict, document: conflict ? local : result };
|
|
359
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BASE_FILE_EXTENSION,
|
|
3
|
+
CANVAS_FILE_EXTENSION,
|
|
3
4
|
DRAWIO_FILE_EXTENSION,
|
|
4
5
|
EXCALIDRAW_FILE_EXTENSION,
|
|
5
6
|
HTML_FILE_EXTENSIONS,
|
|
@@ -64,6 +65,14 @@ const EXCALIDRAW_CREATION_EXAMPLES = [
|
|
|
64
65
|
].map((example) => JSON.stringify(example));
|
|
65
66
|
|
|
66
67
|
const CAPABILITIES = Object.freeze({
|
|
68
|
+
canvas: {
|
|
69
|
+
agentCreatable: false,
|
|
70
|
+
agentEditable: false,
|
|
71
|
+
editable: true,
|
|
72
|
+
extensions: [CANVAS_FILE_EXTENSION],
|
|
73
|
+
guide: 'Standard JSON Canvas 1.0 document edited collaboratively in CollabMD. Collaboration metadata is stored separately. Agent writes are disabled.',
|
|
74
|
+
examples: ['{"nodes": [], "edges": []}'],
|
|
75
|
+
},
|
|
67
76
|
base: {
|
|
68
77
|
agentCreatable: true,
|
|
69
78
|
agentEditable: true,
|
|
@@ -167,7 +176,7 @@ export function getCollabMdContentCapability(kindOrPath) {
|
|
|
167
176
|
commentsSupported: commentPath ? supportsCommentsForFilePath(commentPath) : false,
|
|
168
177
|
kind,
|
|
169
178
|
readable: capability.editable,
|
|
170
|
-
searchable: kind !== 'image' && kind !== 'pdf',
|
|
179
|
+
searchable: kind !== 'image' && kind !== 'pdf' && kind !== 'canvas',
|
|
171
180
|
};
|
|
172
181
|
}
|
|
173
182
|
|
|
@@ -6,6 +6,7 @@ export const COMMENT_ANCHOR_QUOTE_MAX_LENGTH = 280;
|
|
|
6
6
|
export const COMMENT_REACTION_EMOJI_MAX_LENGTH = 16;
|
|
7
7
|
|
|
8
8
|
const COMMENT_ANCHOR_KINDS = new Set(['diagram-element', 'line', 'text']);
|
|
9
|
+
const REACTION_KEY_PREFIX = 'reaction:';
|
|
9
10
|
|
|
10
11
|
function asFiniteNumber(value) {
|
|
11
12
|
return Number.isFinite(value) ? value : null;
|
|
@@ -125,12 +126,16 @@ export function summarizeCommentExcerpt(value, maxLength = COMMENT_EXCERPT_MAX_L
|
|
|
125
126
|
return `${normalized.slice(0, Math.max(maxLength - 1, 1)).trimEnd()}…`;
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
function
|
|
129
|
+
export function normalizeCommentReactionEmoji(value) {
|
|
129
130
|
return Array.from(String(value ?? '').trim())
|
|
130
131
|
.slice(0, COMMENT_REACTION_EMOJI_MAX_LENGTH)
|
|
131
132
|
.join('');
|
|
132
133
|
}
|
|
133
134
|
|
|
135
|
+
export function createCommentReactionKey(messageId, emoji, userId) {
|
|
136
|
+
return `${REACTION_KEY_PREFIX}${JSON.stringify([messageId, emoji, userId])}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
134
139
|
function createReactionUserRecord(user) {
|
|
135
140
|
const userId = asString(readRecordValue(user, 'userId')).trim();
|
|
136
141
|
if (!userId) {
|
|
@@ -146,7 +151,7 @@ function createReactionUserRecord(user) {
|
|
|
146
151
|
}
|
|
147
152
|
|
|
148
153
|
function createReactionGroupRecord(group) {
|
|
149
|
-
const emoji =
|
|
154
|
+
const emoji = normalizeCommentReactionEmoji(readRecordValue(group, 'emoji'));
|
|
150
155
|
if (!emoji) {
|
|
151
156
|
return null;
|
|
152
157
|
}
|
|
@@ -212,10 +217,49 @@ function createMessageRecord(message) {
|
|
|
212
217
|
};
|
|
213
218
|
}
|
|
214
219
|
|
|
215
|
-
function serializeMessages(messages) {
|
|
216
|
-
|
|
220
|
+
function serializeMessages(messages, thread) {
|
|
221
|
+
const records = asArray(messages)
|
|
217
222
|
.map((message) => createMessageRecord(message))
|
|
218
223
|
.filter(Boolean);
|
|
224
|
+
if (!(thread instanceof Y.Map)) {
|
|
225
|
+
return records;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const byId = new Map(records.map((message) => [message.id, message]));
|
|
229
|
+
const overrides = [...thread.entries()]
|
|
230
|
+
.filter(([key]) => key.startsWith(REACTION_KEY_PREFIX))
|
|
231
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
232
|
+
for (const [key, value] of overrides) {
|
|
233
|
+
let identity;
|
|
234
|
+
try {
|
|
235
|
+
identity = JSON.parse(key.slice(REACTION_KEY_PREFIX.length));
|
|
236
|
+
} catch {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (!Array.isArray(identity) || identity.length !== 3 || !identity.every((part) => typeof part === 'string')) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const [messageId, rawEmoji, userId] = identity;
|
|
243
|
+
const emoji = normalizeCommentReactionEmoji(rawEmoji);
|
|
244
|
+
const message = byId.get(messageId);
|
|
245
|
+
const user = value === null ? null : createReactionUserRecord(value);
|
|
246
|
+
if (!message || !emoji || !userId || (value !== null && user?.userId !== userId)) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const groupIndex = message.reactions.findIndex((group) => group.emoji === emoji);
|
|
251
|
+
const users = new Map((message.reactions[groupIndex]?.users ?? []).map((entry) => [entry.userId, entry]));
|
|
252
|
+
if (user) users.set(userId, user);
|
|
253
|
+
else users.delete(userId);
|
|
254
|
+
if (users.size > 0) {
|
|
255
|
+
const group = { emoji, users: [...users.values()] };
|
|
256
|
+
if (groupIndex < 0) message.reactions.push(group);
|
|
257
|
+
else message.reactions[groupIndex] = group;
|
|
258
|
+
} else if (groupIndex >= 0) {
|
|
259
|
+
message.reactions.splice(groupIndex, 1);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return records;
|
|
219
263
|
}
|
|
220
264
|
|
|
221
265
|
export function createCommentId(prefix = 'comment') {
|
|
@@ -323,7 +367,7 @@ export function serializeCommentThread(thread) {
|
|
|
323
367
|
anchorSnapshot: readThreadValue(thread, 'anchorSnapshot'),
|
|
324
368
|
elementId: readThreadValue(thread, 'elementId'),
|
|
325
369
|
});
|
|
326
|
-
const messages = serializeMessages(readThreadValue(thread, 'messages'));
|
|
370
|
+
const messages = serializeMessages(readThreadValue(thread, 'messages'), thread);
|
|
327
371
|
|
|
328
372
|
if (!anchor || messages.length === 0) {
|
|
329
373
|
return null;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { createEmptyScene, normalizeScene, tryParseSceneJson } from './excalidraw-scene.js';
|
|
4
|
+
|
|
5
|
+
export { tryParseSceneJson as tryParseExcalidrawSceneJson } from './excalidraw-scene.js';
|
|
6
6
|
|
|
7
7
|
export const EXCALIDRAW_APP_STATE_KEY = 'excalidraw-app-state';
|
|
8
8
|
export const EXCALIDRAW_ELEMENTS_KEY = 'excalidraw-elements';
|
|
@@ -13,65 +13,10 @@ export const EXCALIDRAW_ROOM_TEXT_KEY = 'codemirror';
|
|
|
13
13
|
export const EXCALIDRAW_SCHEMA_VERSION_KEY = 'schemaVersion';
|
|
14
14
|
export const EXCALIDRAW_REPLACE_GENERATION_KEY = 'replaceGeneration';
|
|
15
15
|
|
|
16
|
-
function createEmptyScene() {
|
|
17
|
-
return {
|
|
18
|
-
type: EXCALIDRAW_TYPE,
|
|
19
|
-
version: EXCALIDRAW_VERSION,
|
|
20
|
-
source: EXCALIDRAW_SOURCE,
|
|
21
|
-
elements: [],
|
|
22
|
-
appState: {
|
|
23
|
-
gridSize: null,
|
|
24
|
-
viewBackgroundColor: '#ffffff',
|
|
25
|
-
},
|
|
26
|
-
files: {},
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function normalizeAppState(appState = {}) {
|
|
31
|
-
return {
|
|
32
|
-
gridSize: appState?.gridSize ?? null,
|
|
33
|
-
viewBackgroundColor: appState?.viewBackgroundColor ?? '#ffffff',
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function normalizeScene(raw) {
|
|
38
|
-
if (!raw || typeof raw !== 'object') {
|
|
39
|
-
return createEmptyScene();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
type: EXCALIDRAW_TYPE,
|
|
44
|
-
version: EXCALIDRAW_VERSION,
|
|
45
|
-
source: EXCALIDRAW_SOURCE,
|
|
46
|
-
elements: Array.isArray(raw.elements) ? raw.elements : [],
|
|
47
|
-
appState: normalizeAppState(raw.appState),
|
|
48
|
-
files: raw.files && typeof raw.files === 'object' ? raw.files : {},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function parseSceneJson(rawJson, { fallbackToEmpty = true } = {}) {
|
|
53
|
-
if (!rawJson) {
|
|
54
|
-
return fallbackToEmpty ? createEmptyScene() : null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
return normalizeScene(JSON.parse(rawJson));
|
|
59
|
-
} catch {
|
|
60
|
-
return fallbackToEmpty ? createEmptyScene() : null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
16
|
function cloneJsonValue(value) {
|
|
65
17
|
return structuredClone(value);
|
|
66
18
|
}
|
|
67
19
|
|
|
68
|
-
function getRevisionKey(element) {
|
|
69
|
-
const version = Number.isFinite(Number(element?.version)) ? Number(element.version) : 0;
|
|
70
|
-
const versionNonce = Number.isFinite(Number(element?.versionNonce)) ? Number(element.versionNonce) : 0;
|
|
71
|
-
const updated = Number.isFinite(Number(element?.updated)) ? Number(element.updated) : 0;
|
|
72
|
-
return `${version}:${versionNonce}:${updated}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
20
|
function compareElementVersions(left, right) {
|
|
76
21
|
const leftVersion = Number(left?.version) || 0;
|
|
77
22
|
const rightVersion = Number(right?.version) || 0;
|
|
@@ -143,14 +88,18 @@ function selectWinningElementFromSlot(slot) {
|
|
|
143
88
|
}
|
|
144
89
|
|
|
145
90
|
let winningElement = null;
|
|
146
|
-
|
|
91
|
+
let winningKey = '';
|
|
92
|
+
slot.forEach((value, key) => {
|
|
147
93
|
const candidate = readRevisionEntry(value);
|
|
148
94
|
if (!candidate || !candidate.id) {
|
|
149
95
|
return;
|
|
150
96
|
}
|
|
151
97
|
|
|
152
|
-
|
|
98
|
+
// Y.Map iteration order can differ across replicas; equal revisions need a stable tie-break.
|
|
99
|
+
const comparison = winningElement ? compareElementVersions(candidate, winningElement) : 1;
|
|
100
|
+
if (comparison > 0 || (comparison === 0 && key > winningKey)) {
|
|
153
101
|
winningElement = candidate;
|
|
102
|
+
winningKey = key;
|
|
154
103
|
}
|
|
155
104
|
});
|
|
156
105
|
|
|
@@ -160,12 +109,16 @@ function selectWinningElementFromSlot(slot) {
|
|
|
160
109
|
function writeElementSlot(slot, element, {
|
|
161
110
|
maxRevisions = 2,
|
|
162
111
|
} = {}) {
|
|
163
|
-
|
|
112
|
+
// Each writer keeps its own candidate so concurrent version/nonce winners survive.
|
|
113
|
+
// ponytail: metadata grows with writers, not edits; authoritative replace compacts
|
|
114
|
+
// legacy keys. Never replace a live slot while reconnecting peers can edit it.
|
|
115
|
+
const revisionKey = `writer:${slot.doc.clientID}`;
|
|
116
|
+
const previous = readRevisionEntry(slot.get(revisionKey));
|
|
117
|
+
if (previous && compareElementVersions(element, previous) < 0) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
164
120
|
const serialized = cloneJsonValue(element);
|
|
165
|
-
const
|
|
166
|
-
const previousSerialized = previous && typeof previous === 'object'
|
|
167
|
-
? JSON.stringify(previous)
|
|
168
|
-
: '';
|
|
121
|
+
const previousSerialized = previous ? JSON.stringify(previous) : '';
|
|
169
122
|
const nextSerialized = JSON.stringify(serialized);
|
|
170
123
|
if (previousSerialized === nextSerialized) {
|
|
171
124
|
return false;
|
|
@@ -199,7 +152,8 @@ function pruneElementSlot(slot, maxRevisions = 2) {
|
|
|
199
152
|
if (!right.element) {
|
|
200
153
|
return 1;
|
|
201
154
|
}
|
|
202
|
-
return compareElementVersions(left.element, right.element)
|
|
155
|
+
return compareElementVersions(left.element, right.element)
|
|
156
|
+
|| (left.key < right.key ? -1 : left.key > right.key ? 1 : 0);
|
|
203
157
|
});
|
|
204
158
|
|
|
205
159
|
while (revisions.length > maxRevisions) {
|
|
@@ -328,7 +282,7 @@ export function replaceExcalidrawRoomScene(ydoc, rawScene, {
|
|
|
328
282
|
filesMap.set(key, cloneJsonValue(value));
|
|
329
283
|
});
|
|
330
284
|
|
|
331
|
-
const nextAppState =
|
|
285
|
+
const nextAppState = scene.appState;
|
|
332
286
|
appStateMap.set('gridSize', nextAppState.gridSize);
|
|
333
287
|
appStateMap.set('viewBackgroundColor', nextAppState.viewBackgroundColor);
|
|
334
288
|
meta.set(EXCALIDRAW_REPLACE_GENERATION_KEY, readExcalidrawReplaceGeneration(ydoc) + 1);
|
|
@@ -372,7 +326,7 @@ export function applySceneDiffToExcalidrawRoom(ydoc, rawScene, {
|
|
|
372
326
|
changed = true;
|
|
373
327
|
});
|
|
374
328
|
|
|
375
|
-
const nextAppState =
|
|
329
|
+
const nextAppState = scene.appState;
|
|
376
330
|
Object.entries(nextAppState).forEach(([key, value]) => {
|
|
377
331
|
if (appStateMap.get(key) === value) {
|
|
378
332
|
return;
|
|
@@ -398,7 +352,7 @@ export function readLegacyExcalidrawRoomScene(ydoc) {
|
|
|
398
352
|
}
|
|
399
353
|
|
|
400
354
|
const rawJson = ydoc.getText(EXCALIDRAW_ROOM_TEXT_KEY).toString();
|
|
401
|
-
return
|
|
355
|
+
return tryParseSceneJson(rawJson);
|
|
402
356
|
}
|
|
403
357
|
|
|
404
358
|
export function migrateLegacyExcalidrawRoomData(ydoc, rawScene, options = {}) {
|
|
@@ -406,7 +360,3 @@ export function migrateLegacyExcalidrawRoomData(ydoc, rawScene, options = {}) {
|
|
|
406
360
|
replaceExcalidrawRoomScene(ydoc, scene, options);
|
|
407
361
|
return scene;
|
|
408
362
|
}
|
|
409
|
-
|
|
410
|
-
export function tryParseExcalidrawSceneJson(rawJson) {
|
|
411
|
-
return parseSceneJson(rawJson, { fallbackToEmpty: false });
|
|
412
|
-
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function createEmptyScene() {
|
|
2
|
+
return {
|
|
3
|
+
type: 'excalidraw',
|
|
4
|
+
version: 2,
|
|
5
|
+
source: 'collabmd',
|
|
6
|
+
elements: [],
|
|
7
|
+
appState: {
|
|
8
|
+
gridSize: null,
|
|
9
|
+
viewBackgroundColor: '#ffffff',
|
|
10
|
+
},
|
|
11
|
+
files: {},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function normalizeScene(raw) {
|
|
16
|
+
if (!raw || typeof raw !== 'object') {
|
|
17
|
+
return createEmptyScene();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
type: 'excalidraw',
|
|
22
|
+
version: 2,
|
|
23
|
+
source: 'collabmd',
|
|
24
|
+
elements: Array.isArray(raw.elements) ? raw.elements : [],
|
|
25
|
+
appState: {
|
|
26
|
+
gridSize: raw.appState?.gridSize ?? null,
|
|
27
|
+
viewBackgroundColor: raw.appState?.viewBackgroundColor ?? '#ffffff',
|
|
28
|
+
},
|
|
29
|
+
files: raw.files && typeof raw.files === 'object' ? raw.files : {},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parseSceneJson(rawJson) {
|
|
34
|
+
const parsed = tryParseSceneJson(rawJson);
|
|
35
|
+
return parsed || createEmptyScene();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function tryParseSceneJson(rawJson) {
|
|
39
|
+
if (!rawJson) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
return normalizeScene(JSON.parse(rawJson));
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|