@trohde/agentic-canvas 0.1.0 → 0.2.0
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/CHANGELOG.md +13 -1
- package/README.md +10 -7
- package/dist/cli/index.js +660 -52
- package/dist/cli/index.js.map +1 -1
- package/dist/web/assets/{ar-SA-G6X2FPQ2-Bx_JGCzc.js → ar-SA-G6X2FPQ2-Cm_aowk8.js} +1 -1
- package/dist/web/assets/{arc-CZEYm-X2.js → arc-BfEOcH9b.js} +1 -1
- package/dist/web/assets/architecture-7EHR7CIX-D9uaHVVA.js +1 -0
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-BX4UqKAe.js → architectureDiagram-3BPJPVTR-D6EV9nh8.js} +1 -1
- package/dist/web/assets/{az-AZ-76LH7QW2-CQnzCEm_.js → az-AZ-76LH7QW2-BKDm9AO9.js} +1 -1
- package/dist/web/assets/{bg-BG-XCXSNQG7-B6OhJNg1.js → bg-BG-XCXSNQG7-Bhj2ixUd.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-DOaLZNH2.js → blockDiagram-GPEHLZMM-uuVOs3ER.js} +1 -1
- package/dist/web/assets/{bn-BD-2XOGV67Q-CL8DmeD-.js → bn-BD-2XOGV67Q-CnDq7W6e.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-BOYbD4o9.js → c4Diagram-AAUBKEIU-CyjNcZVo.js} +1 -1
- package/dist/web/assets/{ca-ES-6MX7JW3Y-DqXuGb3N.js → ca-ES-6MX7JW3Y-CzFlQ5Bd.js} +1 -1
- package/dist/web/assets/channel-DcqTbL2I.js +1 -0
- package/dist/web/assets/{chunk-2J33WTMH-9pRDhFmL.js → chunk-2J33WTMH-DJms322I.js} +1 -1
- package/dist/web/assets/{chunk-3OPIFGDE-Cxtej-eZ.js → chunk-3OPIFGDE-DDQRiaw9.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-WGnRPgB4.js → chunk-4BX2VUAB-8fkcN1qh.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-By23tM0L.js → chunk-55IACEB6-C_yQ14T3.js} +1 -1
- package/dist/web/assets/{chunk-5ZQYHXKU-BbvpmZDT.js → chunk-5ZQYHXKU-ZNEPgpb3.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-DeN-6nx2.js → chunk-727SXJPM-DH8nxAzX.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-3vlT2osY.js → chunk-AQP2D5EJ-DfLHvN4q.js} +1 -1
- package/dist/web/assets/{chunk-BSJP7CBP-ggZwEsYs.js → chunk-BSJP7CBP-Cf2UEop4.js} +1 -1
- package/dist/web/assets/{chunk-CSCIHK7Q-CkCt7BGB.js → chunk-CSCIHK7Q-Bt5Wp92e.js} +1 -1
- package/dist/web/assets/{chunk-EIO257PC-DhAXVvAc.js → chunk-EIO257PC-CzqCX7ih.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-BWW1dpup.js → chunk-FMBD7UC4-Dn7yJ4Og.js} +1 -1
- package/dist/web/assets/{chunk-KSCS5N6A-ByGpruIk.js → chunk-KSCS5N6A-Ckb-EIkQ.js} +1 -1
- package/dist/web/assets/{chunk-L5ZTLDWV-Brj903IS.js → chunk-L5ZTLDWV-DMOYXaNY.js} +1 -1
- package/dist/web/assets/{chunk-LZXEDZCA-B0E6SbHa.js → chunk-LZXEDZCA-BZJbFie6.js} +2 -2
- package/dist/web/assets/{chunk-ND2GUHAM-cgO1vAyy.js → chunk-ND2GUHAM-CH3bRn4I.js} +1 -1
- package/dist/web/assets/{chunk-NZK2D7GU-rJf6zpwf.js → chunk-NZK2D7GU-I17AXEJG.js} +1 -1
- package/dist/web/assets/{chunk-O5CBEL6O-DZpUP7B-.js → chunk-O5CBEL6O-hgdRiomR.js} +1 -1
- package/dist/web/assets/chunk-QZHKN3VN-DsIBQWIa.js +1 -0
- package/dist/web/assets/chunk-WU5MYG2G-CvtGRUhe.js +1 -0
- package/dist/web/assets/{chunk-XPW4576I-DRG_5uRX.js → chunk-XPW4576I-9ec1XdXl.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-BnjqOVph.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BnjqOVph.js +1 -0
- package/dist/web/assets/{cose-bilkent-S5V4N54A-Dzdtq3ax.js → cose-bilkent-S5V4N54A-CTAH7-Na.js} +1 -1
- package/dist/web/assets/{cs-CZ-2BRQDIVT-C9Hi60ft.js → cs-CZ-2BRQDIVT-BqCGlckY.js} +1 -1
- package/dist/web/assets/{da-DK-5WZEPLOC-DuscK2dU.js → da-DK-5WZEPLOC-CQW71ILY.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-DG-1WRTH.js → dagre-BM42HDAG-jBLuWLOm.js} +1 -1
- package/dist/web/assets/{de-DE-XR44H4JA-BUknVi9m.js → de-DE-XR44H4JA-BGJ_QF0p.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-C01yy19V.js → diagram-2AECGRRQ-Bdk2bzIQ.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-9Ly54gPf.js → diagram-5GNKFQAL-BtXDpnfX.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-Bnzn_Fk2.js → diagram-KO2AKTUF-Dzpy-rDd.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-CkQJH7qS.js → diagram-LMA3HP47-DpyU3oY9.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-sYTSt9S6.js → diagram-OG6HWLK6-D_vZYBEy.js} +1 -1
- package/dist/web/assets/{dist-CHteZrRU.js → dist-D2xqQQPY.js} +1 -1
- package/dist/web/assets/{dist--k9jX8ko.js → dist-DbhSHjHs.js} +4 -4
- package/dist/web/assets/{el-GR-BZB4AONW-CBx8IJCe.js → el-GR-BZB4AONW-CtK8iWZk.js} +1 -1
- package/dist/web/assets/{en-B4ZKOASM-D04FpeSQ.js → en-B4ZKOASM-QIuF9S_L.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-kBTcPhaQ.js → erDiagram-TEJ5UH35-CPSP5rtu.js} +1 -1
- package/dist/web/assets/{es-ES-U4NZUMDT-nQPOkQIb.js → es-ES-U4NZUMDT-BGRuwoLW.js} +1 -1
- package/dist/web/assets/{eu-ES-A7QVB2H4-OVL5j0Bz.js → eu-ES-A7QVB2H4-BffwZaTm.js} +1 -1
- package/dist/web/assets/eventmodeling-FCH6USID-DsUO1N_d.js +1 -0
- package/dist/web/assets/{fa-IR-HGAKTJCU-BXJbxYFk.js → fa-IR-HGAKTJCU-DPSCuyoW.js} +1 -1
- package/dist/web/assets/{fi-FI-Z5N7JZ37-DoZfr4yv.js → fi-FI-Z5N7JZ37-8dRu4kif.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-CwQyslgb.js → flowDiagram-I6XJVG4X-pB7KUsSZ.js} +1 -1
- package/dist/web/assets/{fr-FR-RHASNOE6-CXLv0m_p.js → fr-FR-RHASNOE6-C8e_ndFO.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-B-1gME9q.js → ganttDiagram-6RSMTGT7-DEwygfiX.js} +1 -1
- package/dist/web/assets/{gitGraph-WXDBUCRP-BC3bsb5A.js → gitGraph-WXDBUCRP-DyH-gphX.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-DUh_E2g-.js → gitGraphDiagram-PVQCEYII-C580aOqP.js} +1 -1
- package/dist/web/assets/{gl-ES-HMX3MZ6V-DMYtQjyy.js → gl-ES-HMX3MZ6V-DZvyfyMN.js} +1 -1
- package/dist/web/assets/{he-IL-6SHJWFNN-BXviMgxV.js → he-IL-6SHJWFNN-DdcdKhtV.js} +1 -1
- package/dist/web/assets/{hi-IN-IWLTKZ5I-B6s0F0bv.js → hi-IN-IWLTKZ5I-BluyCJ3K.js} +1 -1
- package/dist/web/assets/{hu-HU-A5ZG7DT2-DZY88thP.js → hu-HU-A5ZG7DT2-Dg3oGdTy.js} +1 -1
- package/dist/web/assets/{id-ID-SAP4L64H-CsRPE_UY.js → id-ID-SAP4L64H-BLUaAQdX.js} +1 -1
- package/dist/web/assets/image-GAAHSSAO-Du_312iO.js +1 -0
- package/dist/web/assets/{index-Do_WCY2x.js → index-CaeNa0Nd.js} +8 -8
- package/dist/web/assets/{info-J43DQDTF-DgcAwO7L.js → info-J43DQDTF-CT6GvwIw.js} +1 -1
- package/dist/web/assets/{infoDiagram-5YYISTIA-BVoRZtf6.js → infoDiagram-5YYISTIA-DajsEaol.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-Pc0lSZjn.js → ishikawaDiagram-YF4QCWOH-B4l6Aaj1.js} +1 -1
- package/dist/web/assets/{it-IT-JPQ66NNP-D6QIIUKx.js → it-IT-JPQ66NNP-CjKMpPiP.js} +1 -1
- package/dist/web/assets/{ja-JP-DBVTYXUO-BKIOpiiN.js → ja-JP-DBVTYXUO-7hTYylEN.js} +1 -1
- package/dist/web/assets/{journeyDiagram-JHISSGLW-CUTIkP_3.js → journeyDiagram-JHISSGLW-BYZtl1Hi.js} +1 -1
- package/dist/web/assets/{kaa-6HZHGXH3-Ck2PqClI.js → kaa-6HZHGXH3-DZbKTMJH.js} +1 -1
- package/dist/web/assets/{kab-KAB-ZGHBKWFO-9067fQ1h.js → kab-KAB-ZGHBKWFO-CfiL0AkF.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-f9h-Kl6S.js → kanban-definition-UN3LZRKU-3KTfHyGo.js} +1 -1
- package/dist/web/assets/{kk-KZ-P5N5QNE5-Ds5pD5Rs.js → kk-KZ-P5N5QNE5-CL1X8K-V.js} +1 -1
- package/dist/web/assets/{km-KH-HSX4SM5Z-BvgABkMn.js → km-KH-HSX4SM5Z-DkiDM0om.js} +1 -1
- package/dist/web/assets/{ko-KR-MTYHY66A-BI-VJ_qS.js → ko-KR-MTYHY66A-DeAZ7jFA.js} +1 -1
- package/dist/web/assets/{ku-TR-6OUDTVRD-CzO0QxpT.js → ku-TR-6OUDTVRD-BOybWoIi.js} +1 -1
- package/dist/web/assets/{line-BM7n-WSY.js → line-BVaj9bp2.js} +1 -1
- package/dist/web/assets/{linear-CoV0e-iv.js → linear-BbEWsyY4.js} +1 -1
- package/dist/web/assets/{lt-LT-XHIRWOB4-Wdr8437y.js → lt-LT-XHIRWOB4-C5H7N0uM.js} +1 -1
- package/dist/web/assets/{lv-LV-5QDEKY6T-Baqs6ETz.js → lv-LV-5QDEKY6T-T9e_HCTF.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-BxU7L1C-.js → mermaid-parser.core-CBlXvD0u.js} +2 -2
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-e2CkjxCV.js → mindmap-definition-RKZ34NQL-DQbTUk5-.js} +1 -1
- package/dist/web/assets/{mr-IN-CRQNXWMA-_P3j2iZu.js → mr-IN-CRQNXWMA-B-PthEKF.js} +1 -1
- package/dist/web/assets/{my-MM-5M5IBNSE-u-YXjqOx.js → my-MM-5M5IBNSE-BvaKSQfh.js} +1 -1
- package/dist/web/assets/{nb-NO-T6EIAALU-BVTkGOfM.js → nb-NO-T6EIAALU-AVukMlbc.js} +1 -1
- package/dist/web/assets/{nl-NL-IS3SIHDZ-Dz45KRHt.js → nl-NL-IS3SIHDZ-1Ais5hls.js} +1 -1
- package/dist/web/assets/{nn-NO-6E72VCQL-Bv1T99Os.js → nn-NO-6E72VCQL-CMMf615t.js} +1 -1
- package/dist/web/assets/{oc-FR-POXYY2M6-CWPx0BPy.js → oc-FR-POXYY2M6-COfJfqJD.js} +1 -1
- package/dist/web/assets/{pa-IN-N4M65BXN-CUAufnLD.js → pa-IN-N4M65BXN-BMA3pTXK.js} +1 -1
- package/dist/web/assets/{packet-YPE3B663-DSUdqwD6.js → packet-YPE3B663-BBkze78i.js} +1 -1
- package/dist/web/assets/percentages-BXMCSKIN-D6JI391u.js +1 -0
- package/dist/web/assets/{pica-lghYzniR.js → pica-0kffCCbc.js} +1 -1
- package/dist/web/assets/{pie-LRSECV5Y-CtaLKxkL.js → pie-LRSECV5Y-CKSNEbTP.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-KQxiMI7y.js → pieDiagram-4H26LBE5-BBbcOiMW.js} +1 -1
- package/dist/web/assets/{pl-PL-T2D74RX3-BYDGKTrw.js → pl-PL-T2D74RX3-BynL7lcx.js} +1 -1
- package/dist/web/assets/{pt-BR-5N22H2LF-DiLHA1Fv.js → pt-BR-5N22H2LF-C4hPdgZm.js} +1 -1
- package/dist/web/assets/{pt-PT-UZXXM6DQ-DO4nqrjh.js → pt-PT-UZXXM6DQ-CohDul9s.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-DYnhMBii.js → quadrantDiagram-W4KKPZXB-DbFZ4mVF.js} +1 -1
- package/dist/web/assets/{radar-GUYGQ44K-BWVL_5jd.js → radar-GUYGQ44K-nUUcbbyf.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-CLSyCDmb.js → requirementDiagram-4Y6WPE33-BE4DIJ6B.js} +1 -1
- package/dist/web/assets/{ro-RO-JPDTUUEW-CkArAo01.js → ro-RO-JPDTUUEW-NPZ6H5_k.js} +1 -1
- package/dist/web/assets/{ru-RU-B4JR7IUQ-CF7qZR2L.js → ru-RU-B4JR7IUQ-DUYJexox.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-fCrXqVCu.js → sankeyDiagram-5OEKKPKP-BBgQr7tO.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-Ibg1UsPm.js → sequenceDiagram-3UESZ5HK-Bve3KCrM.js} +1 -1
- package/dist/web/assets/{si-LK-N5RQ5JYF-SlyZ_B_5.js → si-LK-N5RQ5JYF-YQIyxfwu.js} +1 -1
- package/dist/web/assets/{sk-SK-C5VTKIMK-B59JZXsV.js → sk-SK-C5VTKIMK-naC01XZT.js} +1 -1
- package/dist/web/assets/{sl-SI-NN7IZMDC-CkVIpRnH.js → sl-SI-NN7IZMDC-c8M-xB_J.js} +1 -1
- package/dist/web/assets/{src-Dc-yDLup.js → src-5GYS8Ljo.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-Cg6ervwo.js → stateDiagram-AJRCARHV-ooB93unN.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CQKTJr2T.js +1 -0
- package/dist/web/assets/subset-shared.chunk-Lb-_zEfd.js +1 -0
- package/dist/web/assets/{subset-worker.chunk-DN4FBbFb.js → subset-worker.chunk-Cm3Hfued.js} +1 -1
- package/dist/web/assets/{sv-SE-XGPEYMSR-BwnaFVSC.js → sv-SE-XGPEYMSR-BxQJQlZm.js} +1 -1
- package/dist/web/assets/{ta-IN-2NMHFXQM-Digwj7d-.js → ta-IN-2NMHFXQM-CLstIlbu.js} +1 -1
- package/dist/web/assets/{th-TH-HPSO5L25-Ck3FgDpQ.js → th-TH-HPSO5L25-C1Fw6Lrv.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-Sxfm8qnw.js → timeline-definition-PNZ67QCA-DSwgdcbw.js} +1 -1
- package/dist/web/assets/{tr-TR-DEFEU3FU-CZntbglt.js → tr-TR-DEFEU3FU-yIjSKsYZ.js} +1 -1
- package/dist/web/assets/{treeView-BLDUP644-Cx8t5kj4.js → treeView-BLDUP644-qRIad0RL.js} +1 -1
- package/dist/web/assets/{treemap-LRROVOQU-Dfh_IQDp.js → treemap-LRROVOQU-DkzmeI5b.js} +1 -1
- package/dist/web/assets/{uk-UA-QMV73CPH-B0wjKowt.js → uk-UA-QMV73CPH-Cxu2wJjR.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-BMEOJ0gl.js → vennDiagram-CIIHVFJN-C9IbTAeZ.js} +1 -1
- package/dist/web/assets/{vi-VN-M7AON7JQ-Dj0BUYxm.js → vi-VN-M7AON7JQ-CVlx5WSz.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-EJTnFNq7.js → wardley-L42UT6IY-BoetX5iL.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-BJjdV56L.js → wardleyDiagram-YWT4CUSO-BnSF5czb.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-CBQ88pV0.js → xychartDiagram-2RQKCTM6-CnGeJixh.js} +1 -1
- package/dist/web/assets/{zh-CN-LNUGB5OW-K1_YaWy1.js → zh-CN-LNUGB5OW-Cnl39PI9.js} +1 -1
- package/dist/web/assets/{zh-HK-E62DVLB3-DCU_gXiJ.js → zh-HK-E62DVLB3-D0hO_t0Q.js} +1 -1
- package/dist/web/assets/{zh-TW-RAJ6MFWO-1ApXhCfP.js → zh-TW-RAJ6MFWO-Cq71LAZa.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/architecture-7EHR7CIX-BW08P3jh.js +0 -1
- package/dist/web/assets/channel-jlcmNkDM.js +0 -1
- package/dist/web/assets/chunk-QZHKN3VN-B2hsWqvV.js +0 -1
- package/dist/web/assets/chunk-WU5MYG2G-ad-_f9bA.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-pgvxJ5K2.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-pgvxJ5K2.js +0 -1
- package/dist/web/assets/eventmodeling-FCH6USID-3Z5bJ280.js +0 -1
- package/dist/web/assets/image-GAAHSSAO-Ufj_572K.js +0 -1
- package/dist/web/assets/percentages-BXMCSKIN-BRi-lUYV.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Z9e_4KHc.js +0 -1
- package/dist/web/assets/subset-shared.chunk-CnjPFGnW.js +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -20,12 +20,65 @@ function isElementEndpoint(endpoint) {
|
|
|
20
20
|
}
|
|
21
21
|
function cloneScene(scene) {
|
|
22
22
|
return {
|
|
23
|
-
elements: scene.elements.map(
|
|
23
|
+
elements: scene.elements.map(cloneElement),
|
|
24
24
|
appState: { ...scene.appState },
|
|
25
25
|
files: { ...scene.files },
|
|
26
26
|
version: scene.version
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
+
function cloneElement(element) {
|
|
30
|
+
return {
|
|
31
|
+
...element,
|
|
32
|
+
groupIds: [...element.groupIds],
|
|
33
|
+
roundness: element.roundness ? { ...element.roundness } : null,
|
|
34
|
+
boundElements: element.boundElements ? element.boundElements.map((bound) => ({ ...bound })) : null,
|
|
35
|
+
points: element.points ? element.points.map(([x, y]) => [x, y]) : void 0,
|
|
36
|
+
startBinding: element.startBinding ? { ...element.startBinding } : element.startBinding,
|
|
37
|
+
endBinding: element.endBinding ? { ...element.endBinding } : element.endBinding,
|
|
38
|
+
lastCommittedPoint: element.lastCommittedPoint ? [element.lastCommittedPoint[0], element.lastCommittedPoint[1]] : element.lastCommittedPoint,
|
|
39
|
+
customData: element.customData ? { ...element.customData } : element.customData
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/shared/colors.ts
|
|
44
|
+
var NAMED_COLORS = /* @__PURE__ */ new Set([
|
|
45
|
+
"black",
|
|
46
|
+
"white",
|
|
47
|
+
"red",
|
|
48
|
+
"green",
|
|
49
|
+
"blue",
|
|
50
|
+
"yellow",
|
|
51
|
+
"orange",
|
|
52
|
+
"purple",
|
|
53
|
+
"pink",
|
|
54
|
+
"brown",
|
|
55
|
+
"gray",
|
|
56
|
+
"grey",
|
|
57
|
+
"cyan",
|
|
58
|
+
"magenta",
|
|
59
|
+
"lime",
|
|
60
|
+
"navy",
|
|
61
|
+
"teal",
|
|
62
|
+
"transparent"
|
|
63
|
+
]);
|
|
64
|
+
function isCanvasColor(value) {
|
|
65
|
+
const color = value.trim().toLowerCase();
|
|
66
|
+
if (NAMED_COLORS.has(color)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (/^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(color)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (/^rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i.test(color)) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
if (/^hsla?\(\s*-?\d+(?:deg)?\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i.test(
|
|
76
|
+
color
|
|
77
|
+
)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
29
82
|
|
|
30
83
|
// src/plugins/excalidraw/adapter.ts
|
|
31
84
|
var SUPPORTED_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -106,6 +159,52 @@ function normalizeRoughness(roughness) {
|
|
|
106
159
|
|
|
107
160
|
// src/plugins/excalidraw/elements.ts
|
|
108
161
|
import { randomUUID } from "crypto";
|
|
162
|
+
|
|
163
|
+
// src/plugins/excalidraw/textMetrics.ts
|
|
164
|
+
var DEFAULT_FONT_SIZE = 20;
|
|
165
|
+
var EXCALIDRAW_LINE_HEIGHT = 1.25;
|
|
166
|
+
function measureTextBounds(text, style) {
|
|
167
|
+
const fontSize = style?.fontSize ?? DEFAULT_FONT_SIZE;
|
|
168
|
+
const lineHeight = fontSize * EXCALIDRAW_LINE_HEIGHT;
|
|
169
|
+
const lines = splitLines(text);
|
|
170
|
+
const widestLine = Math.max(...lines.map((line) => measureLineWidth(line, fontSize)), 0);
|
|
171
|
+
return {
|
|
172
|
+
width: Math.max(40, Math.ceil(widestLine)),
|
|
173
|
+
height: Math.max(lineHeight, Math.ceil(lines.length * lineHeight)),
|
|
174
|
+
fontSize,
|
|
175
|
+
lineHeight: EXCALIDRAW_LINE_HEIGHT
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function splitLines(text) {
|
|
179
|
+
return text.split(/\r\n|\r|\n/);
|
|
180
|
+
}
|
|
181
|
+
function measureLineWidth(line, fontSize) {
|
|
182
|
+
let width = 0;
|
|
183
|
+
for (const character of line) {
|
|
184
|
+
width += fontSize * characterWeight(character);
|
|
185
|
+
}
|
|
186
|
+
return width;
|
|
187
|
+
}
|
|
188
|
+
function characterWeight(character) {
|
|
189
|
+
if (character === " " || character === " ") {
|
|
190
|
+
return 0.35;
|
|
191
|
+
}
|
|
192
|
+
if (/^[ilI.,:;|!']$/.test(character)) {
|
|
193
|
+
return 0.32;
|
|
194
|
+
}
|
|
195
|
+
if (/^[mwMW@#%&]$/.test(character)) {
|
|
196
|
+
return 0.9;
|
|
197
|
+
}
|
|
198
|
+
if (/^[A-Z0-9]$/.test(character)) {
|
|
199
|
+
return 0.68;
|
|
200
|
+
}
|
|
201
|
+
if ((character.codePointAt(0) ?? 0) > 11904) {
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
return 0.58;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/plugins/excalidraw/elements.ts
|
|
109
208
|
var DEFAULT_WIDTH = 160;
|
|
110
209
|
var DEFAULT_HEIGHT = 80;
|
|
111
210
|
var ORDER_KEY_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
@@ -144,18 +243,20 @@ function buildElement(spec, options = {}) {
|
|
|
144
243
|
locked: false
|
|
145
244
|
};
|
|
146
245
|
if (type === "text") {
|
|
246
|
+
const text = options.text ?? spec.text ?? "";
|
|
247
|
+
const bounds = measureTextBounds(text, spec.style);
|
|
147
248
|
return {
|
|
148
249
|
...base,
|
|
149
|
-
width: spec.width ??
|
|
150
|
-
height: spec.height ??
|
|
151
|
-
text
|
|
152
|
-
originalText:
|
|
153
|
-
fontSize:
|
|
250
|
+
width: spec.width ?? bounds.width,
|
|
251
|
+
height: spec.height ?? bounds.height,
|
|
252
|
+
text,
|
|
253
|
+
originalText: text,
|
|
254
|
+
fontSize: bounds.fontSize,
|
|
154
255
|
fontFamily: 1,
|
|
155
256
|
textAlign: spec.style?.textAlign ?? "center",
|
|
156
257
|
verticalAlign: "middle",
|
|
157
258
|
containerId: options.containerId ?? spec.containerId ?? null,
|
|
158
|
-
lineHeight:
|
|
259
|
+
lineHeight: bounds.lineHeight,
|
|
159
260
|
autoResize: true
|
|
160
261
|
};
|
|
161
262
|
}
|
|
@@ -339,9 +440,12 @@ var canvasObjectTypeSchema = z.enum([
|
|
|
339
440
|
"text",
|
|
340
441
|
"frame"
|
|
341
442
|
]);
|
|
443
|
+
var colorSchema = z.string().refine(isCanvasColor, {
|
|
444
|
+
message: "Invalid canvas color"
|
|
445
|
+
});
|
|
342
446
|
var styleSchema = z.object({
|
|
343
|
-
strokeColor:
|
|
344
|
-
backgroundColor:
|
|
447
|
+
strokeColor: colorSchema.optional(),
|
|
448
|
+
backgroundColor: colorSchema.optional(),
|
|
345
449
|
fillStyle: z.enum(["hachure", "cross-hatch", "solid"]).optional(),
|
|
346
450
|
strokeWidth: z.union([z.literal(1), z.literal(2), z.literal(4)]).optional(),
|
|
347
451
|
strokeStyle: z.enum(["solid", "dashed", "dotted"]).optional(),
|
|
@@ -355,13 +459,13 @@ var endpointSchema = z.union([
|
|
|
355
459
|
z.object({ x: z.number(), y: z.number() })
|
|
356
460
|
]);
|
|
357
461
|
var pointSchema = z.array(z.number()).length(2);
|
|
358
|
-
var pointsSchema = z.array(pointSchema);
|
|
462
|
+
var pointsSchema = z.array(pointSchema).min(2);
|
|
359
463
|
var createObjectShape = {
|
|
360
464
|
type: canvasObjectTypeSchema,
|
|
361
465
|
x: z.number(),
|
|
362
466
|
y: z.number(),
|
|
363
|
-
width: z.number().optional(),
|
|
364
|
-
height: z.number().optional(),
|
|
467
|
+
width: z.number().positive().optional(),
|
|
468
|
+
height: z.number().positive().optional(),
|
|
365
469
|
text: z.string().optional(),
|
|
366
470
|
points: pointsSchema.optional(),
|
|
367
471
|
style: styleSchema.optional(),
|
|
@@ -374,8 +478,8 @@ var updateObjectShape = {
|
|
|
374
478
|
id: z.string(),
|
|
375
479
|
x: z.number().optional(),
|
|
376
480
|
y: z.number().optional(),
|
|
377
|
-
width: z.number().optional(),
|
|
378
|
-
height: z.number().optional(),
|
|
481
|
+
width: z.number().positive().optional(),
|
|
482
|
+
height: z.number().positive().optional(),
|
|
379
483
|
text: z.string().optional(),
|
|
380
484
|
points: pointsSchema.optional(),
|
|
381
485
|
style: styleSchema.optional(),
|
|
@@ -387,6 +491,7 @@ var updateObjectShape = {
|
|
|
387
491
|
|
|
388
492
|
// src/plugins/excalidraw/flowchart.ts
|
|
389
493
|
function planFlowchart(input) {
|
|
494
|
+
validateFlowchart(input);
|
|
390
495
|
const direction = input.direction ?? "LR";
|
|
391
496
|
const spacingX = input.spacingX ?? 220;
|
|
392
497
|
const spacingY = input.spacingY ?? 140;
|
|
@@ -419,9 +524,10 @@ function planFlowchart(input) {
|
|
|
419
524
|
}
|
|
420
525
|
function assignLevels(input) {
|
|
421
526
|
const nodeIds = new Set(input.nodes.map((node) => node.id));
|
|
527
|
+
const backEdges = findBackEdges(input);
|
|
422
528
|
const indegrees = new Map(input.nodes.map((node) => [node.id, 0]));
|
|
423
529
|
for (const edge of input.edges) {
|
|
424
|
-
if (nodeIds.has(edge.to)) {
|
|
530
|
+
if (nodeIds.has(edge.to) && !backEdges.has(edgeKey(edge))) {
|
|
425
531
|
indegrees.set(edge.to, (indegrees.get(edge.to) ?? 0) + 1);
|
|
426
532
|
}
|
|
427
533
|
}
|
|
@@ -433,6 +539,9 @@ function assignLevels(input) {
|
|
|
433
539
|
for (let pass = 0; pass < input.nodes.length; pass += 1) {
|
|
434
540
|
let changed = false;
|
|
435
541
|
for (const edge of input.edges) {
|
|
542
|
+
if (backEdges.has(edgeKey(edge))) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
436
545
|
const fromLevel = levels.get(edge.from);
|
|
437
546
|
if (fromLevel === void 0 || !nodeIds.has(edge.to)) {
|
|
438
547
|
continue;
|
|
@@ -465,14 +574,62 @@ function groupLevels(nodes, levels) {
|
|
|
465
574
|
function peerOffset(index, count, spacing) {
|
|
466
575
|
return (index - (count - 1) / 2) * spacing;
|
|
467
576
|
}
|
|
577
|
+
function validateFlowchart(input) {
|
|
578
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
579
|
+
for (const node of input.nodes) {
|
|
580
|
+
if (nodeIds.has(node.id)) {
|
|
581
|
+
throw new Error(`Duplicate flowchart node id: ${node.id}`);
|
|
582
|
+
}
|
|
583
|
+
nodeIds.add(node.id);
|
|
584
|
+
}
|
|
585
|
+
for (const edge of input.edges) {
|
|
586
|
+
if (!nodeIds.has(edge.from) || !nodeIds.has(edge.to)) {
|
|
587
|
+
throw new Error(`Flowchart edge references missing node: ${edge.from} -> ${edge.to}`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function findBackEdges(input) {
|
|
592
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
593
|
+
for (const edge of input.edges) {
|
|
594
|
+
adjacency.set(edge.from, [...adjacency.get(edge.from) ?? [], edge]);
|
|
595
|
+
}
|
|
596
|
+
const visited = /* @__PURE__ */ new Set();
|
|
597
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
598
|
+
const backEdges = /* @__PURE__ */ new Set();
|
|
599
|
+
const visit = (nodeId) => {
|
|
600
|
+
if (visiting.has(nodeId)) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (visited.has(nodeId)) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
visiting.add(nodeId);
|
|
607
|
+
for (const edge of adjacency.get(nodeId) ?? []) {
|
|
608
|
+
if (visiting.has(edge.to)) {
|
|
609
|
+
backEdges.add(edgeKey(edge));
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
visit(edge.to);
|
|
613
|
+
}
|
|
614
|
+
visiting.delete(nodeId);
|
|
615
|
+
visited.add(nodeId);
|
|
616
|
+
};
|
|
617
|
+
for (const node of input.nodes) {
|
|
618
|
+
visit(node.id);
|
|
619
|
+
}
|
|
620
|
+
return backEdges;
|
|
621
|
+
}
|
|
622
|
+
function edgeKey(edge) {
|
|
623
|
+
return `${edge.from}\0${edge.to}`;
|
|
624
|
+
}
|
|
468
625
|
|
|
469
626
|
// src/plugins/excalidraw/tools.ts
|
|
470
627
|
var shapeInput = {
|
|
471
628
|
x: z2.number(),
|
|
472
629
|
y: z2.number(),
|
|
473
|
-
width: z2.number(),
|
|
474
|
-
height: z2.number(),
|
|
475
|
-
text: z2.string().optional(),
|
|
630
|
+
width: z2.number().positive(),
|
|
631
|
+
height: z2.number().positive(),
|
|
632
|
+
text: z2.string().min(1).optional(),
|
|
476
633
|
style: styleSchema.optional()
|
|
477
634
|
};
|
|
478
635
|
function registerExcalidrawTools(server, context) {
|
|
@@ -538,7 +695,7 @@ function registerExcalidrawTools(server, context) {
|
|
|
538
695
|
inputSchema: {
|
|
539
696
|
x: z2.number().optional(),
|
|
540
697
|
y: z2.number().optional(),
|
|
541
|
-
text: z2.string(),
|
|
698
|
+
text: z2.string().min(1),
|
|
542
699
|
containerId: z2.string().optional(),
|
|
543
700
|
style: styleSchema.optional()
|
|
544
701
|
}
|
|
@@ -567,9 +724,9 @@ function registerExcalidrawTools(server, context) {
|
|
|
567
724
|
inputSchema: {
|
|
568
725
|
x: z2.number(),
|
|
569
726
|
y: z2.number(),
|
|
570
|
-
width: z2.number(),
|
|
571
|
-
height: z2.number(),
|
|
572
|
-
name: z2.string().optional(),
|
|
727
|
+
width: z2.number().positive(),
|
|
728
|
+
height: z2.number().positive(),
|
|
729
|
+
name: z2.string().min(1).optional(),
|
|
573
730
|
childIds: z2.array(z2.string()).optional()
|
|
574
731
|
}
|
|
575
732
|
},
|
|
@@ -595,13 +752,51 @@ function registerExcalidrawTools(server, context) {
|
|
|
595
752
|
"group_objects",
|
|
596
753
|
{
|
|
597
754
|
description: "Group existing Excalidraw elements.",
|
|
755
|
+
inputSchema: {
|
|
756
|
+
ids: z2.array(z2.string()).min(2)
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
async ({ ids }) => {
|
|
760
|
+
try {
|
|
761
|
+
return textResult(context.controller.mutateScene((scene) => groupElements(scene, ids)));
|
|
762
|
+
} catch (error) {
|
|
763
|
+
return errorResult(error);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
);
|
|
767
|
+
server.registerTool(
|
|
768
|
+
"ungroup_objects",
|
|
769
|
+
{
|
|
770
|
+
description: "Remove one group, or all groups, from existing Excalidraw elements.",
|
|
771
|
+
inputSchema: {
|
|
772
|
+
ids: z2.array(z2.string()).min(1),
|
|
773
|
+
groupId: z2.string().optional()
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
async ({ ids, groupId }) => {
|
|
777
|
+
try {
|
|
778
|
+
return textResult(
|
|
779
|
+
context.controller.mutateScene((scene) => ungroupElements(scene, ids, groupId))
|
|
780
|
+
);
|
|
781
|
+
} catch (error) {
|
|
782
|
+
return errorResult(error);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
);
|
|
786
|
+
server.registerTool(
|
|
787
|
+
"remove_from_frame",
|
|
788
|
+
{
|
|
789
|
+
description: "Clear the frame assignment from existing Excalidraw elements.",
|
|
598
790
|
inputSchema: {
|
|
599
791
|
ids: z2.array(z2.string()).min(1)
|
|
600
792
|
}
|
|
601
793
|
},
|
|
602
794
|
async ({ ids }) => {
|
|
603
|
-
|
|
604
|
-
|
|
795
|
+
try {
|
|
796
|
+
return textResult(context.controller.mutateScene((scene) => removeFromFrame(scene, ids)));
|
|
797
|
+
} catch (error) {
|
|
798
|
+
return errorResult(error);
|
|
799
|
+
}
|
|
605
800
|
}
|
|
606
801
|
);
|
|
607
802
|
server.registerTool(
|
|
@@ -612,14 +807,14 @@ function registerExcalidrawTools(server, context) {
|
|
|
612
807
|
nodes: z2.array(
|
|
613
808
|
z2.object({
|
|
614
809
|
id: z2.string(),
|
|
615
|
-
label: z2.string(),
|
|
810
|
+
label: z2.string().min(1),
|
|
616
811
|
shape: z2.enum(["rectangle", "ellipse", "diamond"]).optional(),
|
|
617
812
|
x: z2.number().optional(),
|
|
618
813
|
y: z2.number().optional()
|
|
619
814
|
})
|
|
620
815
|
).min(1),
|
|
621
816
|
edges: z2.array(
|
|
622
|
-
z2.object({ from: z2.string(), to: z2.string(), label: z2.string().optional() })
|
|
817
|
+
z2.object({ from: z2.string(), to: z2.string(), label: z2.string().min(1).optional() })
|
|
623
818
|
),
|
|
624
819
|
direction: z2.enum(["TB", "LR"]).optional(),
|
|
625
820
|
spacingX: z2.number().positive().optional(),
|
|
@@ -730,8 +925,15 @@ function getObject(scene, id) {
|
|
|
730
925
|
return element ? toCanvasObject(element) : void 0;
|
|
731
926
|
}
|
|
732
927
|
function createObject(scene, spec) {
|
|
928
|
+
validateCreateSpec(spec);
|
|
929
|
+
if (spec.containerId && !findElement(scene, spec.containerId)) {
|
|
930
|
+
throw new Error(`Object not found: ${spec.containerId}`);
|
|
931
|
+
}
|
|
733
932
|
const element = buildElementWithBindings(scene, spec);
|
|
734
933
|
scene.elements.push(element);
|
|
934
|
+
if (spec.type === "text" && spec.containerId) {
|
|
935
|
+
addBoundElementById(scene, spec.containerId, { id: element.id, type: "text" });
|
|
936
|
+
}
|
|
735
937
|
if (spec.text && canCreateBoundLabel(spec.type)) {
|
|
736
938
|
const label = createBoundLabel(element, spec.text, spec.style);
|
|
737
939
|
scene.elements.push(label);
|
|
@@ -747,6 +949,7 @@ function updateObject(scene, id, patch) {
|
|
|
747
949
|
if (!element) {
|
|
748
950
|
return void 0;
|
|
749
951
|
}
|
|
952
|
+
validateUpdatePatch(element, patch);
|
|
750
953
|
if (patch.x !== void 0) {
|
|
751
954
|
element.x = patch.x;
|
|
752
955
|
}
|
|
@@ -772,25 +975,54 @@ function updateObject(scene, id, patch) {
|
|
|
772
975
|
}
|
|
773
976
|
if (patch.style) {
|
|
774
977
|
applyStyle(element, patch.style);
|
|
978
|
+
if (element.type === "text") {
|
|
979
|
+
resizeTextElement(element);
|
|
980
|
+
} else {
|
|
981
|
+
applyTextStyleToBoundLabels(scene, element, patch.style);
|
|
982
|
+
}
|
|
775
983
|
}
|
|
776
984
|
if (patch.text !== void 0) {
|
|
777
985
|
if (element.type === "text") {
|
|
778
986
|
element.text = patch.text;
|
|
779
987
|
element.originalText = patch.text;
|
|
988
|
+
resizeTextElement(element);
|
|
989
|
+
if (element.containerId) {
|
|
990
|
+
const container = findElement(scene, element.containerId);
|
|
991
|
+
if (container) {
|
|
992
|
+
relayoutBoundLabels(scene, container);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
780
995
|
} else {
|
|
781
996
|
upsertContainerLabel(scene, element, patch.text, patch.style);
|
|
782
997
|
}
|
|
783
998
|
}
|
|
784
999
|
touchElement(element);
|
|
1000
|
+
syncBoundElements(scene, element);
|
|
785
1001
|
return toCanvasObject(element);
|
|
786
1002
|
}
|
|
787
1003
|
function deleteObjects(scene, ids) {
|
|
788
1004
|
const idSet = new Set(ids);
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
(
|
|
792
|
-
|
|
793
|
-
|
|
1005
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
1006
|
+
for (const element of scene.elements) {
|
|
1007
|
+
if (idSet.has(element.id) || idSet.has(element.containerId ?? "")) {
|
|
1008
|
+
deleted.add(element.id);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (deleted.size === 0) {
|
|
1012
|
+
return [];
|
|
1013
|
+
}
|
|
1014
|
+
scene.elements = scene.elements.filter((element) => !deleted.has(element.id));
|
|
1015
|
+
for (const element of scene.elements) {
|
|
1016
|
+
if (!element.boundElements) {
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
const kept = element.boundElements.filter((bound) => !deleted.has(bound.id));
|
|
1020
|
+
if (kept.length !== element.boundElements.length) {
|
|
1021
|
+
element.boundElements = kept.length > 0 ? kept : null;
|
|
1022
|
+
touchElement(element);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return [...deleted];
|
|
794
1026
|
}
|
|
795
1027
|
function clear(scene) {
|
|
796
1028
|
scene.elements = [];
|
|
@@ -869,6 +1101,7 @@ function upsertContainerLabel(scene, container, text, style) {
|
|
|
869
1101
|
if (style) {
|
|
870
1102
|
applyStyle(existing, style);
|
|
871
1103
|
}
|
|
1104
|
+
positionBoundLabel(existing, container);
|
|
872
1105
|
touchElement(existing);
|
|
873
1106
|
return;
|
|
874
1107
|
}
|
|
@@ -896,24 +1129,21 @@ function createBoundLabel(container, text, style) {
|
|
|
896
1129
|
return label;
|
|
897
1130
|
}
|
|
898
1131
|
function labelPosition(container, text, style) {
|
|
899
|
-
const
|
|
900
|
-
const lineHeight = fontSize * 1.25;
|
|
1132
|
+
const measured = measureTextBounds(text, style);
|
|
901
1133
|
if (isLinearElement(container)) {
|
|
902
1134
|
const midpoint = linearMidpoint(container);
|
|
903
|
-
const width = Math.max(40, text.length * fontSize * 0.6);
|
|
904
|
-
const height = lineHeight;
|
|
905
1135
|
return {
|
|
906
|
-
x: midpoint.x - width / 2,
|
|
907
|
-
y: midpoint.y - height / 2,
|
|
908
|
-
width,
|
|
909
|
-
height
|
|
1136
|
+
x: midpoint.x - measured.width / 2,
|
|
1137
|
+
y: midpoint.y - measured.height / 2,
|
|
1138
|
+
width: measured.width,
|
|
1139
|
+
height: measured.height
|
|
910
1140
|
};
|
|
911
1141
|
}
|
|
912
1142
|
return {
|
|
913
1143
|
x: container.x,
|
|
914
|
-
y: container.y + container.height / 2 -
|
|
1144
|
+
y: container.y + container.height / 2 - measured.height / 2,
|
|
915
1145
|
width: Math.max(40, container.width),
|
|
916
|
-
height:
|
|
1146
|
+
height: measured.height
|
|
917
1147
|
};
|
|
918
1148
|
}
|
|
919
1149
|
function isLinearElement(element) {
|
|
@@ -956,15 +1186,39 @@ function setFrameOnChildren(scene, childIds, frameId) {
|
|
|
956
1186
|
return updated;
|
|
957
1187
|
}
|
|
958
1188
|
function groupElements(scene, ids) {
|
|
1189
|
+
const elements = resolveElements(scene, ids);
|
|
1190
|
+
if (elements.length < 2) {
|
|
1191
|
+
throw new Error("At least two existing objects are required to create a group");
|
|
1192
|
+
}
|
|
959
1193
|
const groupId = randomUUID2();
|
|
960
|
-
for (const
|
|
961
|
-
|
|
962
|
-
if (element && !element.groupIds.includes(groupId)) {
|
|
1194
|
+
for (const element of elements) {
|
|
1195
|
+
if (!element.groupIds.includes(groupId)) {
|
|
963
1196
|
element.groupIds = [...element.groupIds, groupId];
|
|
964
1197
|
touchElement(element);
|
|
965
1198
|
}
|
|
966
1199
|
}
|
|
967
|
-
return groupId;
|
|
1200
|
+
return { groupId, ids: elements.map((element) => element.id) };
|
|
1201
|
+
}
|
|
1202
|
+
function ungroupElements(scene, ids, groupId) {
|
|
1203
|
+
const elements = resolveElements(scene, ids);
|
|
1204
|
+
for (const element of elements) {
|
|
1205
|
+
const nextGroupIds = groupId ? element.groupIds.filter((candidate) => candidate !== groupId) : [];
|
|
1206
|
+
if (nextGroupIds.length !== element.groupIds.length) {
|
|
1207
|
+
element.groupIds = nextGroupIds;
|
|
1208
|
+
touchElement(element);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
return { ids: elements.map((element) => element.id), groupId };
|
|
1212
|
+
}
|
|
1213
|
+
function removeFromFrame(scene, ids) {
|
|
1214
|
+
const elements = resolveElements(scene, ids);
|
|
1215
|
+
for (const element of elements) {
|
|
1216
|
+
if (element.frameId !== null) {
|
|
1217
|
+
element.frameId = null;
|
|
1218
|
+
touchElement(element);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return { ids: elements.map((element) => element.id) };
|
|
968
1222
|
}
|
|
969
1223
|
function applyStyle(element, style) {
|
|
970
1224
|
if (style.strokeColor !== void 0) {
|
|
@@ -989,13 +1243,184 @@ function applyStyle(element, style) {
|
|
|
989
1243
|
element.opacity = style.opacity;
|
|
990
1244
|
}
|
|
991
1245
|
if (style.fontSize !== void 0) {
|
|
1246
|
+
if (element.type !== "text") {
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
992
1249
|
element.fontSize = style.fontSize;
|
|
993
|
-
element.height = style.fontSize * 1.25;
|
|
994
1250
|
}
|
|
995
1251
|
if (style.textAlign !== void 0) {
|
|
1252
|
+
if (element.type !== "text") {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
996
1255
|
element.textAlign = style.textAlign;
|
|
997
1256
|
}
|
|
998
1257
|
}
|
|
1258
|
+
function validateCreateSpec(spec) {
|
|
1259
|
+
validateStyle(spec.style);
|
|
1260
|
+
validateText(spec.type, spec.text);
|
|
1261
|
+
validateDimensions(spec.type, spec.width, spec.height);
|
|
1262
|
+
if ((spec.type === "line" || spec.type === "arrow") && spec.points) {
|
|
1263
|
+
validateLinearPoints(spec.points);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
function validateUpdatePatch(element, patch) {
|
|
1267
|
+
validateStyle(patch.style);
|
|
1268
|
+
validateText(element.type, patch.text);
|
|
1269
|
+
validateDimensions(element.type, patch.width, patch.height);
|
|
1270
|
+
if (patch.points) {
|
|
1271
|
+
validateLinearPoints(patch.points);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function validateStyle(style) {
|
|
1275
|
+
for (const [field, value] of [
|
|
1276
|
+
["strokeColor", style?.strokeColor],
|
|
1277
|
+
["backgroundColor", style?.backgroundColor]
|
|
1278
|
+
]) {
|
|
1279
|
+
if (value !== void 0 && !isCanvasColor(value)) {
|
|
1280
|
+
throw new Error(`Invalid ${field}: ${value}`);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function validateText(type, text) {
|
|
1285
|
+
if (text !== void 0 && text.trim().length === 0 && type !== "frame") {
|
|
1286
|
+
throw new Error("Text must not be empty");
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function validateDimensions(type, width, height) {
|
|
1290
|
+
if (type === "line" || type === "arrow" || type === "text") {
|
|
1291
|
+
if (width !== void 0 && height !== void 0 && width === 0 && height === 0) {
|
|
1292
|
+
throw new Error("Linear geometry must not be zero length");
|
|
1293
|
+
}
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
if (width !== void 0 && width <= 0) {
|
|
1297
|
+
throw new Error("Width must be greater than zero");
|
|
1298
|
+
}
|
|
1299
|
+
if (height !== void 0 && height <= 0) {
|
|
1300
|
+
throw new Error("Height must be greater than zero");
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function validateLinearPoints(points) {
|
|
1304
|
+
if (points.length < 2) {
|
|
1305
|
+
throw new Error("Line and arrow points must contain at least two points");
|
|
1306
|
+
}
|
|
1307
|
+
const [firstX, firstY] = points[0] ?? [0, 0];
|
|
1308
|
+
if (points.every(([x, y]) => x === firstX && y === firstY)) {
|
|
1309
|
+
throw new Error("Line and arrow points must not be zero length");
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function resizeTextElement(element) {
|
|
1313
|
+
const bounds = measureTextBounds(element.text ?? "", styleFromElement(element));
|
|
1314
|
+
element.width = bounds.width;
|
|
1315
|
+
element.height = bounds.height;
|
|
1316
|
+
element.fontSize = bounds.fontSize;
|
|
1317
|
+
element.lineHeight = bounds.lineHeight;
|
|
1318
|
+
}
|
|
1319
|
+
function syncBoundElements(scene, element) {
|
|
1320
|
+
relayoutBoundLabels(scene, element);
|
|
1321
|
+
rerouteBoundArrows(scene, element);
|
|
1322
|
+
}
|
|
1323
|
+
function relayoutBoundLabels(scene, container) {
|
|
1324
|
+
for (const label of scene.elements.filter(
|
|
1325
|
+
(candidate) => candidate.type === "text" && candidate.containerId === container.id
|
|
1326
|
+
)) {
|
|
1327
|
+
positionBoundLabel(label, container);
|
|
1328
|
+
touchElement(label);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
function applyTextStyleToBoundLabels(scene, container, style) {
|
|
1332
|
+
if (style.fontSize === void 0 && style.textAlign === void 0) {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
for (const label of scene.elements.filter(
|
|
1336
|
+
(candidate) => candidate.type === "text" && candidate.containerId === container.id
|
|
1337
|
+
)) {
|
|
1338
|
+
applyStyle(label, {
|
|
1339
|
+
fontSize: style.fontSize,
|
|
1340
|
+
textAlign: style.textAlign
|
|
1341
|
+
});
|
|
1342
|
+
resizeTextElement(label);
|
|
1343
|
+
positionBoundLabel(label, container);
|
|
1344
|
+
touchElement(label);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
function positionBoundLabel(label, container) {
|
|
1348
|
+
const position = labelPosition(container, label.text ?? "", styleFromElement(label));
|
|
1349
|
+
label.x = position.x;
|
|
1350
|
+
label.y = position.y;
|
|
1351
|
+
label.width = position.width;
|
|
1352
|
+
label.height = position.height;
|
|
1353
|
+
label.autoResize = false;
|
|
1354
|
+
}
|
|
1355
|
+
function rerouteBoundArrows(scene, changedElement) {
|
|
1356
|
+
const arrowIds = new Set(
|
|
1357
|
+
(changedElement.boundElements ?? []).filter((bound) => bound.type === "arrow").map((bound) => bound.id)
|
|
1358
|
+
);
|
|
1359
|
+
for (const candidate of scene.elements) {
|
|
1360
|
+
if (isLinearElement(candidate) && (candidate.startBinding?.elementId === changedElement.id || candidate.endBinding?.elementId === changedElement.id)) {
|
|
1361
|
+
arrowIds.add(candidate.id);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
for (const arrowId of arrowIds) {
|
|
1365
|
+
const arrow = findElement(scene, arrowId);
|
|
1366
|
+
if (!arrow || !isLinearElement(arrow)) {
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
rerouteArrow(scene, arrow);
|
|
1370
|
+
relayoutBoundLabels(scene, arrow);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
function rerouteArrow(scene, arrow) {
|
|
1374
|
+
const startTarget = arrow.startBinding ? findElement(scene, arrow.startBinding.elementId) : void 0;
|
|
1375
|
+
const endTarget = arrow.endBinding ? findElement(scene, arrow.endBinding.elementId) : void 0;
|
|
1376
|
+
if (!startTarget && !endTarget) {
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
const currentStart = absoluteStartPoint(arrow);
|
|
1380
|
+
const currentEnd = absoluteEndPoint(arrow);
|
|
1381
|
+
const startReference = endTarget ? centerPoint(endTarget) : currentEnd;
|
|
1382
|
+
const endReference = startTarget ? centerPoint(startTarget) : currentStart;
|
|
1383
|
+
const startPoint = startTarget ? edgePoint(startTarget, startReference.x, startReference.y) : currentStart;
|
|
1384
|
+
const endPoint = endTarget ? edgePoint(endTarget, endReference.x, endReference.y) : currentEnd;
|
|
1385
|
+
arrow.x = startPoint.x;
|
|
1386
|
+
arrow.y = startPoint.y;
|
|
1387
|
+
arrow.points = [
|
|
1388
|
+
[0, 0],
|
|
1389
|
+
[endPoint.x - startPoint.x, endPoint.y - startPoint.y]
|
|
1390
|
+
];
|
|
1391
|
+
arrow.width = linearWidth2(arrow.points);
|
|
1392
|
+
arrow.height = linearHeight2(arrow.points);
|
|
1393
|
+
touchElement(arrow);
|
|
1394
|
+
}
|
|
1395
|
+
function absoluteStartPoint(element) {
|
|
1396
|
+
const first = element.points?.[0] ?? [0, 0];
|
|
1397
|
+
return { x: element.x + first[0], y: element.y + first[1] };
|
|
1398
|
+
}
|
|
1399
|
+
function absoluteEndPoint(element) {
|
|
1400
|
+
const points = element.points ?? [[0, 0]];
|
|
1401
|
+
const last = points[points.length - 1] ?? [0, 0];
|
|
1402
|
+
return { x: element.x + last[0], y: element.y + last[1] };
|
|
1403
|
+
}
|
|
1404
|
+
function styleFromElement(element) {
|
|
1405
|
+
return {
|
|
1406
|
+
strokeColor: element.strokeColor,
|
|
1407
|
+
backgroundColor: element.backgroundColor,
|
|
1408
|
+
fillStyle: element.fillStyle,
|
|
1409
|
+
strokeWidth: element.strokeWidth,
|
|
1410
|
+
strokeStyle: element.strokeStyle,
|
|
1411
|
+
roughness: element.roughness,
|
|
1412
|
+
opacity: element.opacity,
|
|
1413
|
+
fontSize: element.fontSize,
|
|
1414
|
+
textAlign: element.textAlign
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
function resolveElements(scene, ids) {
|
|
1418
|
+
const missingIds = ids.filter((id) => !findElement(scene, id));
|
|
1419
|
+
if (missingIds.length > 0) {
|
|
1420
|
+
throw new Error(`Object not found: ${missingIds.join(", ")}`);
|
|
1421
|
+
}
|
|
1422
|
+
return ids.map((id) => findElement(scene, id)).filter((element) => Boolean(element));
|
|
1423
|
+
}
|
|
999
1424
|
function linearWidth2(points) {
|
|
1000
1425
|
const xs = points.map(([x]) => x);
|
|
1001
1426
|
return Math.max(...xs) - Math.min(...xs);
|
|
@@ -1132,6 +1557,21 @@ function registerBaselineTools(server, context) {
|
|
|
1132
1557
|
}
|
|
1133
1558
|
}
|
|
1134
1559
|
);
|
|
1560
|
+
server.registerTool(
|
|
1561
|
+
"set_canvas_background",
|
|
1562
|
+
{
|
|
1563
|
+
description: "Set the canvas background color.",
|
|
1564
|
+
inputSchema: {
|
|
1565
|
+
color: colorSchema
|
|
1566
|
+
}
|
|
1567
|
+
},
|
|
1568
|
+
async ({ color }) => {
|
|
1569
|
+
context.controller.mutateScene((scene) => {
|
|
1570
|
+
scene.appState.viewBackgroundColor = color;
|
|
1571
|
+
});
|
|
1572
|
+
return textResult2({ viewBackgroundColor: color });
|
|
1573
|
+
}
|
|
1574
|
+
);
|
|
1135
1575
|
server.registerTool(
|
|
1136
1576
|
"open_canvas",
|
|
1137
1577
|
{
|
|
@@ -1209,6 +1649,57 @@ function registerBaselineTools(server, context) {
|
|
|
1209
1649
|
}
|
|
1210
1650
|
}
|
|
1211
1651
|
);
|
|
1652
|
+
server.registerTool(
|
|
1653
|
+
"select_objects",
|
|
1654
|
+
{
|
|
1655
|
+
description: "Select existing objects in the connected browser.",
|
|
1656
|
+
inputSchema: {
|
|
1657
|
+
ids: z3.array(z3.string())
|
|
1658
|
+
}
|
|
1659
|
+
},
|
|
1660
|
+
async ({ ids }) => {
|
|
1661
|
+
try {
|
|
1662
|
+
const selectedIds = [];
|
|
1663
|
+
const missingIds = [];
|
|
1664
|
+
for (const id of ids) {
|
|
1665
|
+
if (context.controller.getObject(id)) {
|
|
1666
|
+
selectedIds.push(id);
|
|
1667
|
+
} else {
|
|
1668
|
+
missingIds.push(id);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
const result = await context.requestSetSelection(selectedIds);
|
|
1672
|
+
return textResult2({
|
|
1673
|
+
selectedIds: result.selectedIds,
|
|
1674
|
+
missingIds
|
|
1675
|
+
});
|
|
1676
|
+
} catch (error) {
|
|
1677
|
+
return errorResult2(error);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
);
|
|
1681
|
+
server.registerTool(
|
|
1682
|
+
"undo",
|
|
1683
|
+
{
|
|
1684
|
+
description: "Undo the most recent authoritative scene change.",
|
|
1685
|
+
inputSchema: {}
|
|
1686
|
+
},
|
|
1687
|
+
async () => {
|
|
1688
|
+
const undone = context.controller.undo();
|
|
1689
|
+
return textResult2({ version: context.controller.currentVersion(), undone });
|
|
1690
|
+
}
|
|
1691
|
+
);
|
|
1692
|
+
server.registerTool(
|
|
1693
|
+
"redo",
|
|
1694
|
+
{
|
|
1695
|
+
description: "Redo the most recently undone authoritative scene change.",
|
|
1696
|
+
inputSchema: {}
|
|
1697
|
+
},
|
|
1698
|
+
async () => {
|
|
1699
|
+
const redone = context.controller.redo();
|
|
1700
|
+
return textResult2({ version: context.controller.currentVersion(), redone });
|
|
1701
|
+
}
|
|
1702
|
+
);
|
|
1212
1703
|
}
|
|
1213
1704
|
function textResult2(value) {
|
|
1214
1705
|
return {
|
|
@@ -1241,7 +1732,8 @@ function buildMcpServer(options) {
|
|
|
1241
1732
|
workspace: options.workspace,
|
|
1242
1733
|
clientsConnected: options.clientsConnected,
|
|
1243
1734
|
requestExport: options.requestExport,
|
|
1244
|
-
requestSelection: options.requestSelection
|
|
1735
|
+
requestSelection: options.requestSelection,
|
|
1736
|
+
requestSetSelection: options.requestSetSelection
|
|
1245
1737
|
});
|
|
1246
1738
|
options.plugin.registerTools(server, {
|
|
1247
1739
|
controller: options.controller
|
|
@@ -1356,6 +1848,10 @@ var CanvasController = class {
|
|
|
1356
1848
|
txDepth = 0;
|
|
1357
1849
|
txDirty = false;
|
|
1358
1850
|
txOrigin;
|
|
1851
|
+
txSnapshot;
|
|
1852
|
+
undoStack = [];
|
|
1853
|
+
redoStack = [];
|
|
1854
|
+
maxHistory = 50;
|
|
1359
1855
|
get canvasName() {
|
|
1360
1856
|
return this.plugin.name;
|
|
1361
1857
|
}
|
|
@@ -1404,12 +1900,17 @@ var CanvasController = class {
|
|
|
1404
1900
|
return JSON.stringify(this.plugin.serialize(this.scene), null, 2);
|
|
1405
1901
|
}
|
|
1406
1902
|
deserialize(raw) {
|
|
1903
|
+
const before = cloneScene(this.scene);
|
|
1407
1904
|
const currentVersion = this.scene.version;
|
|
1408
1905
|
this.scene = this.plugin.deserialize(raw);
|
|
1409
1906
|
this.scene.version = currentVersion;
|
|
1907
|
+
this.pushUndo(before);
|
|
1908
|
+
this.redoStack.length = 0;
|
|
1410
1909
|
this.bumpAndNotify();
|
|
1411
1910
|
}
|
|
1412
1911
|
replaceFromBrowser(elements, appState, files, origin) {
|
|
1912
|
+
this.pushUndo(cloneScene(this.scene));
|
|
1913
|
+
this.redoStack.length = 0;
|
|
1413
1914
|
this.scene.elements = elements;
|
|
1414
1915
|
this.scene.appState = {
|
|
1415
1916
|
viewBackgroundColor: appState?.viewBackgroundColor ?? this.scene.appState.viewBackgroundColor
|
|
@@ -1418,24 +1919,73 @@ var CanvasController = class {
|
|
|
1418
1919
|
this.bumpAndNotify(origin);
|
|
1419
1920
|
}
|
|
1420
1921
|
mutateScene(mutator, origin) {
|
|
1922
|
+
if (this.txDepth > 0) {
|
|
1923
|
+
const result2 = mutator(this.scene);
|
|
1924
|
+
this.bumpAndNotify(origin);
|
|
1925
|
+
return result2;
|
|
1926
|
+
}
|
|
1927
|
+
const before = cloneScene(this.scene);
|
|
1421
1928
|
const result = mutator(this.scene);
|
|
1929
|
+
this.pushUndo(before);
|
|
1930
|
+
this.redoStack.length = 0;
|
|
1422
1931
|
this.bumpAndNotify(origin);
|
|
1423
1932
|
return result;
|
|
1424
1933
|
}
|
|
1425
1934
|
transaction(fn, origin) {
|
|
1935
|
+
const isOuterTransaction = this.txDepth === 0;
|
|
1936
|
+
if (isOuterTransaction) {
|
|
1937
|
+
this.txSnapshot = cloneScene(this.scene);
|
|
1938
|
+
}
|
|
1426
1939
|
this.txDepth += 1;
|
|
1940
|
+
let failed = false;
|
|
1427
1941
|
try {
|
|
1428
1942
|
return fn();
|
|
1943
|
+
} catch (error) {
|
|
1944
|
+
failed = true;
|
|
1945
|
+
if (isOuterTransaction && this.txSnapshot) {
|
|
1946
|
+
this.scene = this.txSnapshot;
|
|
1947
|
+
this.txDirty = false;
|
|
1948
|
+
this.txOrigin = void 0;
|
|
1949
|
+
}
|
|
1950
|
+
throw error;
|
|
1429
1951
|
} finally {
|
|
1430
1952
|
this.txDepth -= 1;
|
|
1431
|
-
if (this.txDepth === 0 &&
|
|
1953
|
+
if (this.txDepth === 0 && failed) {
|
|
1954
|
+
this.txSnapshot = void 0;
|
|
1955
|
+
} else if (this.txDepth === 0 && this.txDirty) {
|
|
1432
1956
|
const txOrigin = this.txOrigin;
|
|
1957
|
+
const snapshot = this.txSnapshot;
|
|
1433
1958
|
this.txDirty = false;
|
|
1434
1959
|
this.txOrigin = void 0;
|
|
1960
|
+
this.txSnapshot = void 0;
|
|
1961
|
+
if (snapshot) {
|
|
1962
|
+
this.pushUndo(snapshot);
|
|
1963
|
+
this.redoStack.length = 0;
|
|
1964
|
+
}
|
|
1435
1965
|
this.commit(txOrigin ?? origin);
|
|
1966
|
+
} else if (this.txDepth === 0) {
|
|
1967
|
+
this.txSnapshot = void 0;
|
|
1436
1968
|
}
|
|
1437
1969
|
}
|
|
1438
1970
|
}
|
|
1971
|
+
undo() {
|
|
1972
|
+
const previous = this.undoStack.pop();
|
|
1973
|
+
if (!previous) {
|
|
1974
|
+
return false;
|
|
1975
|
+
}
|
|
1976
|
+
this.redoStack.push(cloneScene(this.scene));
|
|
1977
|
+
this.restoreScene(previous);
|
|
1978
|
+
return true;
|
|
1979
|
+
}
|
|
1980
|
+
redo() {
|
|
1981
|
+
const next = this.redoStack.pop();
|
|
1982
|
+
if (!next) {
|
|
1983
|
+
return false;
|
|
1984
|
+
}
|
|
1985
|
+
this.undoStack.push(cloneScene(this.scene));
|
|
1986
|
+
this.restoreScene(next);
|
|
1987
|
+
return true;
|
|
1988
|
+
}
|
|
1439
1989
|
bumpAndNotify(origin) {
|
|
1440
1990
|
if (this.txDepth > 0) {
|
|
1441
1991
|
this.txDirty = true;
|
|
@@ -1448,6 +1998,18 @@ var CanvasController = class {
|
|
|
1448
1998
|
this.scene.version += 1;
|
|
1449
1999
|
this.listener?.(this.getSnapshot(), origin);
|
|
1450
2000
|
}
|
|
2001
|
+
pushUndo(scene) {
|
|
2002
|
+
this.undoStack.push(cloneScene(scene));
|
|
2003
|
+
if (this.undoStack.length > this.maxHistory) {
|
|
2004
|
+
this.undoStack.shift();
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
restoreScene(scene) {
|
|
2008
|
+
const currentVersion = this.scene.version;
|
|
2009
|
+
this.scene = cloneScene(scene);
|
|
2010
|
+
this.scene.version = currentVersion;
|
|
2011
|
+
this.commit();
|
|
2012
|
+
}
|
|
1451
2013
|
};
|
|
1452
2014
|
|
|
1453
2015
|
// src/server/workspace.ts
|
|
@@ -1520,6 +2082,7 @@ var WsBridge = class {
|
|
|
1520
2082
|
clients = /* @__PURE__ */ new Map();
|
|
1521
2083
|
pendingExports = /* @__PURE__ */ new Map();
|
|
1522
2084
|
pendingSelections = /* @__PURE__ */ new Map();
|
|
2085
|
+
pendingSelectionSets = /* @__PURE__ */ new Map();
|
|
1523
2086
|
syncOrder = 0;
|
|
1524
2087
|
wss;
|
|
1525
2088
|
attach(server, path5 = "/ws") {
|
|
@@ -1588,6 +2151,31 @@ var WsBridge = class {
|
|
|
1588
2151
|
client.socket.send(JSON.stringify(request));
|
|
1589
2152
|
});
|
|
1590
2153
|
}
|
|
2154
|
+
requestSetSelection(selectedIds, options = {}) {
|
|
2155
|
+
const client = this.mostRecentlySyncedClient((candidate) => candidate.supportsSelectionSet);
|
|
2156
|
+
if (!client) {
|
|
2157
|
+
if (this.connectedClientCount() > 0) {
|
|
2158
|
+
return Promise.reject(
|
|
2159
|
+
new Error("No browser canvas client supports programmatic selection")
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
return Promise.reject(new Error("No browser canvas client is connected"));
|
|
2163
|
+
}
|
|
2164
|
+
const id = randomUUID4();
|
|
2165
|
+
const request = {
|
|
2166
|
+
type: "selection:set",
|
|
2167
|
+
id,
|
|
2168
|
+
selectedIds
|
|
2169
|
+
};
|
|
2170
|
+
return new Promise((resolve, reject) => {
|
|
2171
|
+
const timer = setTimeout(() => {
|
|
2172
|
+
this.pendingSelectionSets.delete(id);
|
|
2173
|
+
reject(new Error("Selection set timed out"));
|
|
2174
|
+
}, options.timeoutMs ?? 5e3);
|
|
2175
|
+
this.pendingSelectionSets.set(id, { resolve, reject, timer });
|
|
2176
|
+
client.socket.send(JSON.stringify(request));
|
|
2177
|
+
});
|
|
2178
|
+
}
|
|
1591
2179
|
close() {
|
|
1592
2180
|
for (const { socket } of this.clients.values()) {
|
|
1593
2181
|
socket.close();
|
|
@@ -1598,7 +2186,8 @@ var WsBridge = class {
|
|
|
1598
2186
|
this.clients.set(socket, {
|
|
1599
2187
|
socket,
|
|
1600
2188
|
lastSyncedOrder: 0,
|
|
1601
|
-
lastSyncedVersion: -1
|
|
2189
|
+
lastSyncedVersion: -1,
|
|
2190
|
+
supportsSelectionSet: false
|
|
1602
2191
|
});
|
|
1603
2192
|
socket.on("message", (data) => this.handleMessage(socket, data.toString()));
|
|
1604
2193
|
socket.on("close", () => {
|
|
@@ -1612,6 +2201,10 @@ var WsBridge = class {
|
|
|
1612
2201
|
return;
|
|
1613
2202
|
}
|
|
1614
2203
|
if (message.type === "hello") {
|
|
2204
|
+
const client = this.clients.get(socket);
|
|
2205
|
+
if (client) {
|
|
2206
|
+
client.supportsSelectionSet = Boolean(message.capabilities?.selectionSet);
|
|
2207
|
+
}
|
|
1615
2208
|
this.sendScene(socket, this.controller.getSnapshot());
|
|
1616
2209
|
return;
|
|
1617
2210
|
}
|
|
@@ -1655,6 +2248,20 @@ var WsBridge = class {
|
|
|
1655
2248
|
} else {
|
|
1656
2249
|
pending.reject(new Error(message.message));
|
|
1657
2250
|
}
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
if (message.type === "selection:set:result" || message.type === "selection:set:error") {
|
|
2254
|
+
const pending = this.pendingSelectionSets.get(message.id);
|
|
2255
|
+
if (!pending) {
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
clearTimeout(pending.timer);
|
|
2259
|
+
this.pendingSelectionSets.delete(message.id);
|
|
2260
|
+
if (message.type === "selection:set:result") {
|
|
2261
|
+
pending.resolve({ selectedIds: message.selectedIds });
|
|
2262
|
+
} else {
|
|
2263
|
+
pending.reject(new Error(message.message));
|
|
2264
|
+
}
|
|
1658
2265
|
}
|
|
1659
2266
|
}
|
|
1660
2267
|
sendScene(socket, snapshot) {
|
|
@@ -1680,8 +2287,8 @@ var WsBridge = class {
|
|
|
1680
2287
|
client.lastSyncedOrder = ++this.syncOrder;
|
|
1681
2288
|
client.lastSyncedVersion = version;
|
|
1682
2289
|
}
|
|
1683
|
-
mostRecentlySyncedClient() {
|
|
1684
|
-
return [...this.clients.values()].filter((candidate) => candidate.socket.readyState === WebSocket.OPEN).sort((left, right) => right.lastSyncedOrder - left.lastSyncedOrder)[0];
|
|
2290
|
+
mostRecentlySyncedClient(predicate = () => true) {
|
|
2291
|
+
return [...this.clients.values()].filter((candidate) => candidate.socket.readyState === WebSocket.OPEN && predicate(candidate)).sort((left, right) => right.lastSyncedOrder - left.lastSyncedOrder)[0];
|
|
1685
2292
|
}
|
|
1686
2293
|
};
|
|
1687
2294
|
|
|
@@ -1702,7 +2309,8 @@ async function startHttpServer(options) {
|
|
|
1702
2309
|
allowedHosts: allowedHostsFor(options.host, port),
|
|
1703
2310
|
clientsConnected: () => bridge.connectedClientCount(),
|
|
1704
2311
|
requestExport: (exportOptions) => bridge.requestExport(exportOptions),
|
|
1705
|
-
requestSelection: (selectionOptions) => bridge.requestSelection(selectionOptions)
|
|
2312
|
+
requestSelection: (selectionOptions) => bridge.requestSelection(selectionOptions),
|
|
2313
|
+
requestSetSelection: (selectedIds, selectionOptions) => bridge.requestSetSelection(selectedIds, selectionOptions)
|
|
1706
2314
|
});
|
|
1707
2315
|
const server = createServer(app);
|
|
1708
2316
|
bridge.attach(server);
|