chalkbridge 1.1.0 → 1.1.1
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/dist/{chunk-XDK5UUTO.js → chunk-XFH76JS6.js} +66 -5
- package/dist/cli.js +879 -174
- package/dist/index.js +1 -1
- package/guides/layout.md +28 -1
- package/package.json +1 -1
- package/ui-dist/assets/{abnfDiagram-VCTEODGH-BCawxiKB.js → abnfDiagram-VCTEODGH-Bwcewgyl.js} +1 -1
- package/ui-dist/assets/{ar-SA-G6X2FPQ2-UCm9FftA.js → ar-SA-G6X2FPQ2-58dCrzRZ.js} +1 -1
- package/ui-dist/assets/architecture-7GRP2DOG-Df8gKqbg.js +1 -0
- package/ui-dist/assets/{architectureDiagram-5GKGNRK7-DIw1pNEk.js → architectureDiagram-5GKGNRK7-CMPSnIrP.js} +1 -1
- package/ui-dist/assets/{az-AZ-76LH7QW2-B5LEIxkk.js → az-AZ-76LH7QW2-CCpEVexR.js} +1 -1
- package/ui-dist/assets/{bg-BG-XCXSNQG7-2bpThpZL.js → bg-BG-XCXSNQG7-DDlArZMH.js} +1 -1
- package/ui-dist/assets/{blockDiagram-NRAW4CY4-BGr2WGp_.js → blockDiagram-NRAW4CY4-D7TrKIo5.js} +1 -1
- package/ui-dist/assets/{bn-BD-2XOGV67Q-Drz-y_cX.js → bn-BD-2XOGV67Q-BXyhMN4V.js} +1 -1
- package/ui-dist/assets/{c4Diagram-UCG6FXSJ-vbm87bpC.js → c4Diagram-UCG6FXSJ-CazStwlL.js} +1 -1
- package/ui-dist/assets/{ca-ES-6MX7JW3Y-D89p_FZ-.js → ca-ES-6MX7JW3Y-JHxjajfS.js} +1 -1
- package/ui-dist/assets/channel-C4uQlBVf.js +1 -0
- package/ui-dist/assets/{chunk-75Z2AOVW-B7kf5jVz.js → chunk-75Z2AOVW-BJsQGYsA.js} +1 -1
- package/ui-dist/assets/{chunk-DU6HZSFF-BlGPS6B6.js → chunk-DU6HZSFF-C1xw9RZQ.js} +1 -1
- package/ui-dist/assets/{chunk-EIO257PC-nzXnwORk.js → chunk-EIO257PC-BrhyYOZD.js} +1 -1
- package/ui-dist/assets/{chunk-F27PBJKO-B7qepoj_.js → chunk-F27PBJKO-f6XMRQIS.js} +1 -1
- package/ui-dist/assets/{chunk-G27WJ6UU-CSjcvmOf.js → chunk-G27WJ6UU-Cws9GsBU.js} +1 -1
- package/ui-dist/assets/{chunk-GMAD6QVW-CtaZAO5u.js → chunk-GMAD6QVW-CbhLp4Jt.js} +1 -1
- package/ui-dist/assets/{chunk-JQ64N6SF-CuhtdTse.js → chunk-JQ64N6SF-B5lJbHea.js} +1 -1
- package/ui-dist/assets/{chunk-LCL6LL3I-DMfA3a_s.js → chunk-LCL6LL3I-bGYRjzHM.js} +1 -1
- package/ui-dist/assets/{chunk-OBVCFTLP-BoBuso2l.js → chunk-OBVCFTLP-CxeUZPN4.js} +1 -1
- package/ui-dist/assets/{chunk-P2QGCYS3-BZHK87sP.js → chunk-P2QGCYS3-CZEugM1f.js} +1 -1
- package/ui-dist/assets/{chunk-POPQ4Y6H-Dk_v51-0.js → chunk-POPQ4Y6H-CoDq2Q_g.js} +1 -1
- package/ui-dist/assets/{chunk-PWAF6VOD-DQTvHbxy.js → chunk-PWAF6VOD-CX_HLotF.js} +1 -1
- package/ui-dist/assets/{chunk-R7TYR2AO-CQ_StUs1.js → chunk-R7TYR2AO-B4M26mv9.js} +1 -1
- package/ui-dist/assets/{chunk-RHFEMEQ7-BTgGEQit.js → chunk-RHFEMEQ7-CVXQBX33.js} +1 -1
- package/ui-dist/assets/{chunk-SVP7TREG-lkVXeHxJ.js → chunk-SVP7TREG-L7TJWT_o.js} +1 -1
- package/ui-dist/assets/{chunk-TEH6E4GO-DIEC-0PO.js → chunk-TEH6E4GO-Dct0SlmV.js} +1 -1
- package/ui-dist/assets/classDiagram-DTDB5LWJ-DRbrF0Ha.js +1 -0
- package/ui-dist/assets/classDiagram-v2-JRS7N3AN-DRbrF0Ha.js +1 -0
- package/ui-dist/assets/{cs-CZ-2BRQDIVT-CmSaQCAW.js → cs-CZ-2BRQDIVT-CvvJph-H.js} +1 -1
- package/ui-dist/assets/{cynefin-OW5HDTMX-B6f8qooM.js → cynefin-OW5HDTMX-Ei_Xgi2_.js} +1 -1
- package/ui-dist/assets/{cynefinDiagram-5FMLGOSQ-DFu8uB2c.js → cynefinDiagram-5FMLGOSQ-AFFSo_cm.js} +1 -1
- package/ui-dist/assets/{da-DK-5WZEPLOC-DZ68cfJh.js → da-DK-5WZEPLOC-BaezbVeN.js} +1 -1
- package/ui-dist/assets/{dagre-3AP2YEHR-CuUE3cEY.js → dagre-3AP2YEHR-B-FVEWH2.js} +1 -1
- package/ui-dist/assets/{de-DE-XR44H4JA-ClBUqbwZ.js → de-DE-XR44H4JA-BAGdMA-L.js} +1 -1
- package/ui-dist/assets/{diagram-S7CK7UJ4-DjLndaJK.js → diagram-S7CK7UJ4-eyl1yJj7.js} +1 -1
- package/ui-dist/assets/{diagram-UQ7AKVKN-wLhxEBwN.js → diagram-UQ7AKVKN-CugR7jCP.js} +1 -1
- package/ui-dist/assets/{diagram-VSXAHHWV-CVek4gvW.js → diagram-VSXAHHWV-Dz85hV5k.js} +1 -1
- package/ui-dist/assets/{diagram-VX7I27RA-CXyuaDuW.js → diagram-VX7I27RA-BYSNxzTB.js} +1 -1
- package/ui-dist/assets/{diagram-Z3DM3KII-BGk8dw5c.js → diagram-Z3DM3KII-BN4pFJaP.js} +1 -1
- package/ui-dist/assets/{dist-DyDCC61R.js → dist-CVqcfLn4.js} +4 -4
- package/ui-dist/assets/{ebnfDiagram-PWID7BFC-Bq5wdWGX.js → ebnfDiagram-PWID7BFC-Ddprc5Rv.js} +1 -1
- package/ui-dist/assets/{el-GR-BZB4AONW-oCuJWxYE.js → el-GR-BZB4AONW-C8EbIpNQ.js} +1 -1
- package/ui-dist/assets/{en-B4ZKOASM-DevQWFhD.js → en-B4ZKOASM-BZp0XUv6.js} +1 -1
- package/ui-dist/assets/{erDiagram-SSCWMZ5O-CXZW4I_b.js → erDiagram-SSCWMZ5O-BoMeaxjf.js} +1 -1
- package/ui-dist/assets/{es-ES-U4NZUMDT-DgzMLG1J.js → es-ES-U4NZUMDT-CA2eQinb.js} +1 -1
- package/ui-dist/assets/{eu-ES-A7QVB2H4-1tA3vosZ.js → eu-ES-A7QVB2H4-CcaNArBV.js} +1 -1
- package/ui-dist/assets/eventmodeling-NTZA5JFV-CSPN2jeA.js +1 -0
- package/ui-dist/assets/{fa-IR-HGAKTJCU-BVbN6Ifb.js → fa-IR-HGAKTJCU-BH7DgnZN.js} +1 -1
- package/ui-dist/assets/{fi-FI-Z5N7JZ37-DsGMdP7T.js → fi-FI-Z5N7JZ37-DWg6APJ2.js} +1 -1
- package/ui-dist/assets/flowDiagram-A5DVABFB-YrkFRLG9.js +1 -0
- package/ui-dist/assets/{fr-FR-RHASNOE6-BEZa94DA.js → fr-FR-RHASNOE6-qBJplDsT.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-EL5Y4UJY-BZxyNgRP.js → ganttDiagram-EL5Y4UJY-4wx5f9nv.js} +1 -1
- package/ui-dist/assets/{gitGraph-4MIJSDKK-Cz7F-_6d.js → gitGraph-4MIJSDKK-DDYlgTud.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-WWUBYQGX-77gOaAOD.js → gitGraphDiagram-WWUBYQGX-DAhZkdQa.js} +1 -1
- package/ui-dist/assets/{gl-ES-HMX3MZ6V-DfSDz_sy.js → gl-ES-HMX3MZ6V-CTvRbK-4.js} +1 -1
- package/ui-dist/assets/{he-IL-6SHJWFNN-DT8nqxK2.js → he-IL-6SHJWFNN-w2aMYZZd.js} +1 -1
- package/ui-dist/assets/{hi-IN-IWLTKZ5I-BtL1eizk.js → hi-IN-IWLTKZ5I-DX4ZRE2H.js} +1 -1
- package/ui-dist/assets/{hu-HU-A5ZG7DT2-5FwjCLbT.js → hu-HU-A5ZG7DT2-naHDPNOM.js} +1 -1
- package/ui-dist/assets/{id-ID-SAP4L64H-DXfUbYET.js → id-ID-SAP4L64H-D5QxdnZa.js} +1 -1
- package/ui-dist/assets/image-GAAHSSAO-C9O0QZ3x.js +1 -0
- package/ui-dist/assets/{index-Boxo043A.js → index-W8DLsYN7.js} +8 -8
- package/ui-dist/assets/{info-A6RAGUB7-DIe8iET8.js → info-A6RAGUB7-RXb_MEIH.js} +1 -1
- package/ui-dist/assets/{infoDiagram-RXCK75RN-Dik-rzpk.js → infoDiagram-RXCK75RN-DzaEiHqC.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-5VMMS53U-Ccb-hHW6.js → ishikawaDiagram-5VMMS53U-C9We7rRC.js} +1 -1
- package/ui-dist/assets/{it-IT-JPQ66NNP-6_sPHBVO.js → it-IT-JPQ66NNP-B0YDAONu.js} +1 -1
- package/ui-dist/assets/{ja-JP-DBVTYXUO-DgY_5_c8.js → ja-JP-DBVTYXUO-CjS5c0my.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-EYS64GPL-CgzxmpyV.js → journeyDiagram-EYS64GPL-BnolpCVx.js} +1 -1
- package/ui-dist/assets/{kaa-6HZHGXH3-glmGx672.js → kaa-6HZHGXH3-BCxOG4kc.js} +1 -1
- package/ui-dist/assets/{kab-KAB-ZGHBKWFO--G7baVL0.js → kab-KAB-ZGHBKWFO-D3QotDYI.js} +1 -1
- package/ui-dist/assets/{kanban-definition-3QL26DDD-D6TnbrdQ.js → kanban-definition-3QL26DDD-Cy9HKz-0.js} +1 -1
- package/ui-dist/assets/{kk-KZ-P5N5QNE5-D3Vn0qgF.js → kk-KZ-P5N5QNE5-BrSjotfG.js} +1 -1
- package/ui-dist/assets/{km-KH-HSX4SM5Z-CAHt3o8M.js → km-KH-HSX4SM5Z-fb8nqdkK.js} +1 -1
- package/ui-dist/assets/{ko-KR-MTYHY66A-N3tCiPap.js → ko-KR-MTYHY66A-BXj8eDyy.js} +1 -1
- package/ui-dist/assets/{ku-TR-6OUDTVRD-CSZFwS--.js → ku-TR-6OUDTVRD-COZArCj9.js} +1 -1
- package/ui-dist/assets/{line-BhVZwKwh.js → line-7U1712L5.js} +1 -1
- package/ui-dist/assets/{lt-LT-XHIRWOB4-DuSlkfc-.js → lt-LT-XHIRWOB4-C-HJQqop.js} +1 -1
- package/ui-dist/assets/{lv-LV-5QDEKY6T-COuWGNLd.js → lv-LV-5QDEKY6T-DLfr6jLA.js} +1 -1
- package/ui-dist/assets/{mermaid-parser.core-DlqbfUOM.js → mermaid-parser.core-DrLo74XG.js} +3 -3
- package/ui-dist/assets/{mindmap-definition-FBJOCRG2-D9wLjZXa.js → mindmap-definition-FBJOCRG2-Ba42gC8u.js} +1 -1
- package/ui-dist/assets/{mr-IN-CRQNXWMA-Bv4WHAzM.js → mr-IN-CRQNXWMA-B9soDV3M.js} +1 -1
- package/ui-dist/assets/{my-MM-5M5IBNSE-BNltBCCh.js → my-MM-5M5IBNSE-LZaFHx2a.js} +1 -1
- package/ui-dist/assets/{nb-NO-T6EIAALU-DgsGabVE.js → nb-NO-T6EIAALU-BwhFAqXH.js} +1 -1
- package/ui-dist/assets/{nl-NL-IS3SIHDZ-MPMxu0cH.js → nl-NL-IS3SIHDZ-D2FhUB0E.js} +1 -1
- package/ui-dist/assets/{nn-NO-6E72VCQL-CY-NT6bD.js → nn-NO-6E72VCQL-yn-NI0ZL.js} +1 -1
- package/ui-dist/assets/{oc-FR-POXYY2M6-BCWtA2dJ.js → oc-FR-POXYY2M6-DOhix0u-.js} +1 -1
- package/ui-dist/assets/{pa-IN-N4M65BXN-CU1D5Lce.js → pa-IN-N4M65BXN-9aoDSwWx.js} +1 -1
- package/ui-dist/assets/{packet-AYTQ26CC-DCMIIosG.js → packet-AYTQ26CC-DuyZkxuT.js} +1 -1
- package/ui-dist/assets/{pegDiagram-XKGWAZYB-CpU2aj-A.js → pegDiagram-XKGWAZYB-PH6THd_o.js} +1 -1
- package/ui-dist/assets/percentages-BXMCSKIN-BFxN7npZ.js +1 -0
- package/ui-dist/assets/{pie-WAS4IAKB-BEVkH0em.js → pie-WAS4IAKB-BdQJyLvD.js} +1 -1
- package/ui-dist/assets/{pieDiagram-E7YTZNPT-D-jyX8nJ.js → pieDiagram-E7YTZNPT-CPm011HM.js} +1 -1
- package/ui-dist/assets/{pl-PL-T2D74RX3-CwNWACCb.js → pl-PL-T2D74RX3-BO50j2s4.js} +1 -1
- package/ui-dist/assets/{pt-BR-5N22H2LF-DCs1W5P0.js → pt-BR-5N22H2LF-Cp8T2vmD.js} +1 -1
- package/ui-dist/assets/{pt-PT-UZXXM6DQ-Bm9Hlh9S.js → pt-PT-UZXXM6DQ-CdTZJ0Y5.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-AXDQQJYC-BqzJD4zL.js → quadrantDiagram-AXDQQJYC-Cm1Rlzhk.js} +1 -1
- package/ui-dist/assets/{radar-RG4KPBEZ-B6T3SZ4z.js → radar-RG4KPBEZ-48srnC1_.js} +1 -1
- package/ui-dist/assets/{railroad-74A4TZTK-BPKCaP4g.js → railroad-74A4TZTK-C6601ICA.js} +1 -1
- package/ui-dist/assets/railroad-abnf-HS5TGJTU-Dmq5JjW8.js +1 -0
- package/ui-dist/assets/railroad-ebnf-LZEXJU2U-DzMo_BK8.js +1 -0
- package/ui-dist/assets/railroad-peg-WCYAUIDC-LanIHXaC.js +1 -0
- package/ui-dist/assets/{railroadDiagram-O6MQD6OU-CzTuUKE-.js → railroadDiagram-O6MQD6OU-C8yR5TE7.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-EFPCY7ZU-Bly8_reu.js → requirementDiagram-EFPCY7ZU-DeleqP-5.js} +1 -1
- package/ui-dist/assets/{ro-RO-JPDTUUEW-DbKkWzmT.js → ro-RO-JPDTUUEW-C7SipvaK.js} +1 -1
- package/ui-dist/assets/{ru-RU-B4JR7IUQ-C7y8hptt.js → ru-RU-B4JR7IUQ-dNLeoFpA.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-P5KCCOFB-BNe1jsYh.js → sankeyDiagram-P5KCCOFB-CSJKN7--.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-WJ2MYXX4-jpU3VDzA.js → sequenceDiagram-WJ2MYXX4-B_C10dhq.js} +1 -1
- package/ui-dist/assets/{si-LK-N5RQ5JYF-DsyN9eZC.js → si-LK-N5RQ5JYF-DogInl3-.js} +1 -1
- package/ui-dist/assets/{sk-SK-C5VTKIMK-C3GmutL2.js → sk-SK-C5VTKIMK-2YnR2SrM.js} +1 -1
- package/ui-dist/assets/{sl-SI-NN7IZMDC-C6dxM6aa.js → sl-SI-NN7IZMDC-w1ecqU-U.js} +1 -1
- package/ui-dist/assets/{stateDiagram-HBIQ2CUA-B2ZRnydN.js → stateDiagram-HBIQ2CUA-BIYHmokV.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-4QOOHH4V-BEVYrEze.js +1 -0
- package/ui-dist/assets/subset-shared.chunk-zuCoK3Ax.js +1 -0
- package/ui-dist/assets/{subset-worker.chunk-WItGSQq_.js → subset-worker.chunk-Fq9aq3Pk.js} +1 -1
- package/ui-dist/assets/{sv-SE-XGPEYMSR-BPU1z_vz.js → sv-SE-XGPEYMSR-ZA1w16aZ.js} +1 -1
- package/ui-dist/assets/{swimlanes-XN3QIQJK-BuYIN15M.js → swimlanes-XN3QIQJK-Bsz-wFaq.js} +1 -1
- package/ui-dist/assets/swimlanesDiagram-VK2B7HYN-DVXdrFIL.js +8 -0
- package/ui-dist/assets/{ta-IN-2NMHFXQM-AJVbdGTO.js → ta-IN-2NMHFXQM-CanbC1P6.js} +1 -1
- package/ui-dist/assets/{th-TH-HPSO5L25-B1vvYNmv.js → th-TH-HPSO5L25-DyHWEX0G.js} +1 -1
- package/ui-dist/assets/{timeline-definition-24CTP7MA-Bld3Oj9i.js → timeline-definition-24CTP7MA-4TgC1OoH.js} +1 -1
- package/ui-dist/assets/{tr-TR-DEFEU3FU-C_emJDca.js → tr-TR-DEFEU3FU-D0W6rufc.js} +1 -1
- package/ui-dist/assets/{treeView-Q6P3EWNA-CQEi50Re.js → treeView-Q6P3EWNA-C9fqdzP-.js} +1 -1
- package/ui-dist/assets/{treemap-WGGIJYW6-D3gRwGbr.js → treemap-WGGIJYW6-BqNZy40L.js} +1 -1
- package/ui-dist/assets/{uk-UA-QMV73CPH-B69Ddo0p.js → uk-UA-QMV73CPH-C1iCWdAQ.js} +1 -1
- package/ui-dist/assets/{vennDiagram-4TSXK5OY-BWtR81LD.js → vennDiagram-4TSXK5OY-BQFCZXqB.js} +1 -1
- package/ui-dist/assets/{vi-VN-M7AON7JQ-ByM0OkIF.js → vi-VN-M7AON7JQ-BWynaJhA.js} +1 -1
- package/ui-dist/assets/{wardley-WFR3VGLG-DpBUmo8s.js → wardley-WFR3VGLG-cNQ06Oix.js} +1 -1
- package/ui-dist/assets/{wardleyDiagram-VM6X3IG4-n39Dh9vZ.js → wardleyDiagram-VM6X3IG4-BbLDW2ka.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-S5SC5T6Z-D8-lcdGr.js → xychartDiagram-S5SC5T6Z-r5VAgI1x.js} +1 -1
- package/ui-dist/assets/{zh-CN-LNUGB5OW-g_S93sCp.js → zh-CN-LNUGB5OW-BNvy0Mnj.js} +1 -1
- package/ui-dist/assets/{zh-HK-E62DVLB3-B3J_lyrF.js → zh-HK-E62DVLB3-Csv83LC3.js} +1 -1
- package/ui-dist/assets/{zh-TW-RAJ6MFWO-mYLP5aqQ.js → zh-TW-RAJ6MFWO-CW-6XIyO.js} +1 -1
- package/ui-dist/index.html +1 -1
- package/ui-dist/assets/architecture-7GRP2DOG-CiyYoVgW.js +0 -1
- package/ui-dist/assets/channel-DtgwkzBZ.js +0 -1
- package/ui-dist/assets/classDiagram-DTDB5LWJ-B83TJF6p.js +0 -1
- package/ui-dist/assets/classDiagram-v2-JRS7N3AN-B83TJF6p.js +0 -1
- package/ui-dist/assets/eventmodeling-NTZA5JFV-Bh0QsTUi.js +0 -1
- package/ui-dist/assets/flowDiagram-A5DVABFB-Chb6hSSG.js +0 -1
- package/ui-dist/assets/image-GAAHSSAO-AgKiDvHK.js +0 -1
- package/ui-dist/assets/percentages-BXMCSKIN-CMz17NMy.js +0 -1
- package/ui-dist/assets/railroad-abnf-HS5TGJTU-RL2kk25t.js +0 -1
- package/ui-dist/assets/railroad-ebnf-LZEXJU2U-NY4mALmg.js +0 -1
- package/ui-dist/assets/railroad-peg-WCYAUIDC-BNAC9MGy.js +0 -1
- package/ui-dist/assets/stateDiagram-v2-4QOOHH4V-BUscqu-N.js +0 -1
- package/ui-dist/assets/subset-shared.chunk-CRXudW0i.js +0 -1
- package/ui-dist/assets/swimlanesDiagram-VK2B7HYN-Cn_HC7uw.js +0 -8
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { SceneElement, PackName, PackKind, IconId, SceneId, Scene, SnapshotId, SERVER_MANAGED_FIELDS, BINDING_FIELDS, GUIDE_TOPICS, DetailLevel, createNodeSkeletonExpander, ok, fail, bridgeSpawnFailed, bridgeVersionMismatch, bridgeUnreachable, GuideTopic, storeUnavailable, describeThrown, unreadableSceneFile, pathOutsideRepo, packInstallFailed, sceneFileMissing, notAPack, PackRecipe, portUnavailable, DescribeSceneOutput, DescribeSceneInput, GetSelectionOutput, GetSelectionInput, GetElementsOutput, GetElementsInput, ListScenesOutput, ListScenesInput, SceneSettingsOutput, SceneSettingsInput, SearchIconsOutput, SearchIconsInput, ScreenshotOutput, ScreenshotInput, ReadMeOutput, ReadMeInput, PlaceIconOutput, PlaceIconInput, SnapshotOutput, SnapshotInput, RestoreSnapshotOutput, RestoreSnapshotInput, ExportSceneOutput, ExportSceneInput, ImportSceneOutput, ImportSceneInput, ExportImageOutput, ExportImageInput, InstallPackOutput, InstallPackInput, notARepo, ScreenshotResponse, RemovePackOutput, ListPacksOutput, RawElementsOutput, FocusOutput, EmbedOutput, AnnotateOutput, FrameOutput, UngroupOutput, GroupOutput, ArrangeOutput, DeleteElementsOutput, UpdateElementsOutput, ConnectOutput, CreateElementsOutput, HealthResponse, iconIdOf, unknownScene, unknownSnapshot, exportFailed, unknownIcon, HOUSE, FileId, ElementId, diskConflict, refusedFieldMessage, idCollision, expansionFailed, emptySelection, unknownElement, frameIdOf, GroupId, staleVersion, noBoundLabel, deepEqual, exportTimedOut, noBrowser, DebugElementRequest, CreateElementsInput, ConnectToolInput, UpdateElementsInput, DeleteElementsInput, ArrangeToolInput, GroupInput, UngroupInput, FrameInput, AnnotateInput, EmbedInput, FocusInput, RawElementsInput, ListPacksInput, RemovePackInput, iconNeedsAPlace, DataUrl, Index, ROUNDNESS, STALE_SELECTION_MS, noScenes, ClientMessage, assertNever, PROTOCOL_VERSION, ApiError, EXCALIDRAW_FILE_VERSION, EXCALIDRAW_FILE_TYPE, point, FLAG_GLYPH,
|
|
2
|
+
import { SceneElement, PackName, PackKind, IconId, SceneId, Scene, SnapshotId, SERVER_MANAGED_FIELDS, BINDING_FIELDS, GUIDE_TOPICS, DetailLevel, createNodeSkeletonExpander, ok, fail, bridgeSpawnFailed, bridgeVersionMismatch, bridgeUnreachable, GuideTopic, storeUnavailable, describeThrown, unreadableSceneFile, pathOutsideRepo, packInstallFailed, sceneFileMissing, notAPack, PackRecipe, portUnavailable, DescribeSceneOutput, DescribeSceneInput, GetSelectionOutput, GetSelectionInput, GetElementsOutput, GetElementsInput, ListScenesOutput, ListScenesInput, SceneSettingsOutput, SceneSettingsInput, SearchIconsOutput, SearchIconsInput, ScreenshotOutput, ScreenshotInput, ReadMeOutput, ReadMeInput, PlaceIconOutput, PlaceIconInput, SnapshotOutput, SnapshotInput, RestoreSnapshotOutput, RestoreSnapshotInput, ExportSceneOutput, ExportSceneInput, ImportSceneOutput, ImportSceneInput, ExportImageOutput, ExportImageInput, InstallPackOutput, InstallPackInput, notARepo, ScreenshotResponse, RemovePackOutput, ListPacksOutput, RawElementsOutput, FocusOutput, EmbedOutput, AnnotateOutput, FrameOutput, UngroupOutput, GroupOutput, ArrangeOutput, DeleteElementsOutput, UpdateElementsOutput, ConnectOutput, CreateElementsOutput, HealthResponse, iconIdOf, unknownScene, unknownSnapshot, exportFailed, unknownIcon, HOUSE, FileId, ElementId, diskConflict, refusedFieldMessage, idCollision, expansionFailed, emptySelection, unknownElement, measureLine, frameIdOf, GroupId, staleVersion, noBoundLabel, deepEqual, exportTimedOut, noBrowser, DebugElementRequest, CreateElementsInput, ConnectToolInput, UpdateElementsInput, DeleteElementsInput, ArrangeToolInput, GroupInput, UngroupInput, FrameInput, AnnotateInput, EmbedInput, FocusInput, RawElementsInput, ListPacksInput, RemovePackInput, iconNeedsAPlace, DataUrl, Index, wrapLines, ROUNDNESS, STALE_SELECTION_MS, noScenes, ClientMessage, assertNever, PROTOCOL_VERSION, ApiError, EXCALIDRAW_FILE_VERSION, EXCALIDRAW_FILE_TYPE, point, FLAG_GLYPH, KIND_ABBREVIATION, catalogUnavailable, FrameId, IconItem, ExcalidrawFile } from './chunk-XFH76JS6.js';
|
|
3
3
|
import './chunk-7D4SUZUM.js';
|
|
4
4
|
import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
@@ -2128,6 +2128,151 @@ function overlapWarning(elements) {
|
|
|
2128
2128
|
return `${count2} on the canvas \u2014 worst ${worst.a} \xD7 ${worst.b} (${size}). chalk_describe_scene lists them; chalk_arrange moves them apart`;
|
|
2129
2129
|
}
|
|
2130
2130
|
|
|
2131
|
+
// src/domain/scene/anchors.ts
|
|
2132
|
+
var ANCHOR_GAP = 12;
|
|
2133
|
+
var anchorsOf = (element) => element.customData?.chalk?.anchorIds ?? [];
|
|
2134
|
+
var isAnchored = (element) => anchorsOf(element).length > 0;
|
|
2135
|
+
function anchoredPosition(anchor, size, placement) {
|
|
2136
|
+
const midX = anchor.x + anchor.width / 2 - size.width / 2;
|
|
2137
|
+
const midY = anchor.y + anchor.height / 2 - size.height / 2;
|
|
2138
|
+
switch (placement) {
|
|
2139
|
+
case "above":
|
|
2140
|
+
return { x: midX, y: anchor.y - size.height - ANCHOR_GAP };
|
|
2141
|
+
case "below":
|
|
2142
|
+
return { x: midX, y: anchor.y + anchor.height + ANCHOR_GAP };
|
|
2143
|
+
case "left":
|
|
2144
|
+
return { x: anchor.x - size.width - ANCHOR_GAP, y: midY };
|
|
2145
|
+
case "right":
|
|
2146
|
+
return { x: anchor.x + anchor.width + ANCHOR_GAP, y: midY };
|
|
2147
|
+
case "badge":
|
|
2148
|
+
return { x: anchor.x + anchor.width - size.width / 2, y: anchor.y - size.height / 2 };
|
|
2149
|
+
case "inside":
|
|
2150
|
+
return { x: midX, y: midY };
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
var captionBelow = (position, size) => ({
|
|
2154
|
+
x: position.x,
|
|
2155
|
+
y: position.y + size.height + 8
|
|
2156
|
+
});
|
|
2157
|
+
function withAnchoredElements(scene, deltas) {
|
|
2158
|
+
const attached = scene.filter((element) => !element.isDeleted && isAnchored(element));
|
|
2159
|
+
if (attached.length === 0) return deltas;
|
|
2160
|
+
const widened = new Map(deltas);
|
|
2161
|
+
const present2 = new Set(scene.filter((element) => !element.isDeleted).map((e) => e.id));
|
|
2162
|
+
for (const element of attached) {
|
|
2163
|
+
if (widened.has(element.id)) continue;
|
|
2164
|
+
const live = anchorsOf(element).filter((id) => present2.has(id));
|
|
2165
|
+
if (live.length === 0) continue;
|
|
2166
|
+
const moves = live.map((id) => deltas.get(id));
|
|
2167
|
+
const [first] = moves;
|
|
2168
|
+
if (first === void 0) continue;
|
|
2169
|
+
if (!moves.every((move) => move !== void 0 && move[0] === first[0] && move[1] === first[1])) {
|
|
2170
|
+
continue;
|
|
2171
|
+
}
|
|
2172
|
+
widened.set(element.id, first);
|
|
2173
|
+
}
|
|
2174
|
+
return widened;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
// src/domain/scene/recommend.ts
|
|
2178
|
+
var ENOUGH_TO_MATTER = 5;
|
|
2179
|
+
var HUB_SHARE = 0.5;
|
|
2180
|
+
var HUB_MIN_DEGREE = 4;
|
|
2181
|
+
var RING_LABEL_WIDTH = 200;
|
|
2182
|
+
function recommendLayout(scene, targetIds) {
|
|
2183
|
+
const wanted = new Set(targetIds);
|
|
2184
|
+
const nodes = scene.filter(
|
|
2185
|
+
(element) => wanted.has(element.id) && !isLinear(element) && !isAnchored(element) && !element.isDeleted
|
|
2186
|
+
);
|
|
2187
|
+
if (nodes.length < ENOUGH_TO_MATTER) return null;
|
|
2188
|
+
const ids = new Set(nodes.map((node) => node.id));
|
|
2189
|
+
const degree = new Map(nodes.map((node) => [node.id, 0]));
|
|
2190
|
+
const out = new Map(nodes.map((node) => [node.id, []]));
|
|
2191
|
+
let edges = 0;
|
|
2192
|
+
for (const element of scene) {
|
|
2193
|
+
if (element.type !== "arrow" || element.isDeleted) continue;
|
|
2194
|
+
const from = startOf(element);
|
|
2195
|
+
const to = endOf(element);
|
|
2196
|
+
if (from === null || to === null || from === to) continue;
|
|
2197
|
+
if (!ids.has(from) || !ids.has(to)) continue;
|
|
2198
|
+
edges += 1;
|
|
2199
|
+
degree.set(from, (degree.get(from) ?? 0) + 1);
|
|
2200
|
+
degree.set(to, (degree.get(to) ?? 0) + 1);
|
|
2201
|
+
out.get(from)?.push(to);
|
|
2202
|
+
}
|
|
2203
|
+
if (edges === 0) {
|
|
2204
|
+
return {
|
|
2205
|
+
op: "grid",
|
|
2206
|
+
why: `${nodes.length} elements with no arrows between them \u2014 chalk_arrange(op: "grid") is the layout for a set rather than a graph`
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
const busiest = Math.max(...degree.values());
|
|
2210
|
+
const hubShare = busiest / (nodes.length - 1);
|
|
2211
|
+
const depth = longestChain(nodes, out);
|
|
2212
|
+
const widestLabel = Math.max(...nodes.map((node) => node.width));
|
|
2213
|
+
if (hubShare >= HUB_SHARE && busiest >= HUB_MIN_DEGREE) {
|
|
2214
|
+
const hub = nodes.find((node) => (degree.get(node.id) ?? 0) === busiest);
|
|
2215
|
+
const name = hub === void 0 ? "one element" : shortLabel(scene, hub);
|
|
2216
|
+
return widestLabel > RING_LABEL_WIDTH ? {
|
|
2217
|
+
op: "layout-tree",
|
|
2218
|
+
why: `${name} touches ${busiest} of ${nodes.length - 1} others and the labels are wide \u2014 chalk_arrange(op: "layout-tree") puts it on the left and fans the rest out; a ring this wide crosses its own middle`
|
|
2219
|
+
} : {
|
|
2220
|
+
op: "layout-radial",
|
|
2221
|
+
why: `${name} touches ${busiest} of ${nodes.length - 1} others \u2014 chalk_arrange(op: "layout-radial") puts it at the centre, which ranks cannot show`
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
return {
|
|
2225
|
+
op: "layout-layered",
|
|
2226
|
+
why: `${nodes.length} elements, ${edges} arrows, longest chain ${depth} \u2014 chalk_arrange(op: "layout-layered") ranks them by the direction of the arrows`
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
function longestChain(nodes, out) {
|
|
2230
|
+
const ON_STACK = 1;
|
|
2231
|
+
const DONE = 2;
|
|
2232
|
+
const state = /* @__PURE__ */ new Map();
|
|
2233
|
+
const depth = /* @__PURE__ */ new Map();
|
|
2234
|
+
const visit = (start) => {
|
|
2235
|
+
const stack = [{ node: start, cursor: 0 }];
|
|
2236
|
+
state.set(start, ON_STACK);
|
|
2237
|
+
while (stack.length > 0) {
|
|
2238
|
+
const frame = stack[stack.length - 1];
|
|
2239
|
+
if (frame === void 0) break;
|
|
2240
|
+
const kids = out.get(frame.node) ?? [];
|
|
2241
|
+
if (frame.cursor >= kids.length) {
|
|
2242
|
+
let best = 1;
|
|
2243
|
+
for (const kid2 of kids) {
|
|
2244
|
+
if (state.get(kid2) === DONE) best = Math.max(best, 1 + (depth.get(kid2) ?? 0));
|
|
2245
|
+
}
|
|
2246
|
+
depth.set(frame.node, best);
|
|
2247
|
+
state.set(frame.node, DONE);
|
|
2248
|
+
stack.pop();
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
const kid = kids[frame.cursor];
|
|
2252
|
+
frame.cursor += 1;
|
|
2253
|
+
if (kid === void 0 || state.has(kid)) continue;
|
|
2254
|
+
state.set(kid, ON_STACK);
|
|
2255
|
+
stack.push({ node: kid, cursor: 0 });
|
|
2256
|
+
}
|
|
2257
|
+
return depth.get(start) ?? 1;
|
|
2258
|
+
};
|
|
2259
|
+
let longest = 0;
|
|
2260
|
+
for (const node of [...nodes].sort((a, b) => a.id < b.id ? -1 : 1)) {
|
|
2261
|
+
if (!state.has(node.id)) visit(node.id);
|
|
2262
|
+
longest = Math.max(longest, depth.get(node.id) ?? 1);
|
|
2263
|
+
}
|
|
2264
|
+
return longest;
|
|
2265
|
+
}
|
|
2266
|
+
function shortLabel(scene, element) {
|
|
2267
|
+
const bound = (element.boundElements ?? []).find((b) => b.type === "text")?.id;
|
|
2268
|
+
const caption = bound === void 0 ? void 0 : scene.find((e) => e.id === bound);
|
|
2269
|
+
if (caption !== void 0 && caption.type === "text") {
|
|
2270
|
+
const text = (caption.originalText || caption.text).replace(/\n/g, " ").trim();
|
|
2271
|
+
if (text !== "") return `"${text}"`;
|
|
2272
|
+
}
|
|
2273
|
+
return String(element.id).slice(0, 4);
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2131
2276
|
// src/domain/scene/observe.ts
|
|
2132
2277
|
var SHORT_ID_LENGTH = 4;
|
|
2133
2278
|
var shortId2 = (id) => id.slice(0, SHORT_ID_LENGTH);
|
|
@@ -2245,6 +2390,12 @@ function observeScene(input) {
|
|
|
2245
2390
|
elements: rows.map((element) => describeElement(element, context)),
|
|
2246
2391
|
overlaps: overlaps.slice(0, MAX_REPORTED_OVERLAPS),
|
|
2247
2392
|
overlapCount: overlaps.length,
|
|
2393
|
+
// Over the rows, not the raw elements: a caption is part of the box it names, and
|
|
2394
|
+
// counting one as a node would halve every degree the advice is computed from.
|
|
2395
|
+
layoutAdvice: recommendLayout(
|
|
2396
|
+
scene.elements,
|
|
2397
|
+
rows.map((element) => element.id)
|
|
2398
|
+
)?.why ?? null,
|
|
2248
2399
|
detail: input.detail
|
|
2249
2400
|
};
|
|
2250
2401
|
}
|
|
@@ -3319,8 +3470,108 @@ function labelledSize(label, fontSize = HOUSE.fontSize) {
|
|
|
3319
3470
|
return { width, height };
|
|
3320
3471
|
}
|
|
3321
3472
|
|
|
3473
|
+
// src/domain/scene/skeleton.ts
|
|
3474
|
+
var isContainer = (type) => type === "rectangle" || type === "diamond" || type === "ellipse";
|
|
3475
|
+
function explicit(input) {
|
|
3476
|
+
const kept = {};
|
|
3477
|
+
for (const [key, value] of Object.entries(input)) {
|
|
3478
|
+
if (value === void 0) continue;
|
|
3479
|
+
if (key === "type" || key === "id" || key === "role" || key === "label") continue;
|
|
3480
|
+
kept[key] = value;
|
|
3481
|
+
}
|
|
3482
|
+
return kept;
|
|
3483
|
+
}
|
|
3484
|
+
var defaultSize = (element) => labelledSize(element.label, element.fontSize ?? HOUSE.fontSize);
|
|
3485
|
+
function noteWrap(text, fontSize, requested) {
|
|
3486
|
+
const column = requested ?? HOUSE.sizes.note;
|
|
3487
|
+
if (text === "") return { text };
|
|
3488
|
+
if (requested === void 0 && measureLine(text, fontSize) <= column) return { text };
|
|
3489
|
+
const lines = wrapLines(text, fontSize, column);
|
|
3490
|
+
return { text: lines.join("\n"), width: column, autoResize: false };
|
|
3491
|
+
}
|
|
3492
|
+
function normalizeElements(input) {
|
|
3493
|
+
return input.elements.map((element, i) => {
|
|
3494
|
+
const fallback = input.ids[i];
|
|
3495
|
+
if (fallback === void 0) {
|
|
3496
|
+
throw new Error(`normalizeElements needs one id per element: none at position ${i}`);
|
|
3497
|
+
}
|
|
3498
|
+
const id = element.id ?? fallback;
|
|
3499
|
+
const role = styleForRole(element.role ?? "compute");
|
|
3500
|
+
const set = explicit(element);
|
|
3501
|
+
if (element.type === "text") {
|
|
3502
|
+
const text = element.text ?? element.label ?? "";
|
|
3503
|
+
const skeleton2 = {
|
|
3504
|
+
type: "text",
|
|
3505
|
+
id,
|
|
3506
|
+
x: element.x,
|
|
3507
|
+
y: element.y,
|
|
3508
|
+
fontSize: HOUSE.fontSize,
|
|
3509
|
+
fontFamily: HOUSE.fontFamily,
|
|
3510
|
+
// A free-standing note is not a service box: it takes the role's ink but never
|
|
3511
|
+
// its fill, because a filled rectangle behind a caption reads as a shape.
|
|
3512
|
+
strokeColor: role.strokeColor,
|
|
3513
|
+
...set,
|
|
3514
|
+
// Last, because it owns `text`: `explicit` copies the caller's unwrapped string
|
|
3515
|
+
// across and this is what replaces it.
|
|
3516
|
+
...noteWrap(text, element.fontSize ?? HOUSE.fontSize, element.width)
|
|
3517
|
+
};
|
|
3518
|
+
return skeleton2;
|
|
3519
|
+
}
|
|
3520
|
+
if (element.type === "line") {
|
|
3521
|
+
const skeleton2 = {
|
|
3522
|
+
type: "line",
|
|
3523
|
+
id,
|
|
3524
|
+
x: element.x,
|
|
3525
|
+
y: element.y,
|
|
3526
|
+
strokeColor: role.strokeColor,
|
|
3527
|
+
strokeWidth: HOUSE.strokeWidth,
|
|
3528
|
+
roughness: HOUSE.roughness,
|
|
3529
|
+
...set,
|
|
3530
|
+
...element.label === void 0 ? {} : { label: houseLabel(element.label) }
|
|
3531
|
+
};
|
|
3532
|
+
return skeleton2;
|
|
3533
|
+
}
|
|
3534
|
+
if (!isContainer(element.type)) {
|
|
3535
|
+
throw new Error(`normalizeElements cannot build a ${element.type}`);
|
|
3536
|
+
}
|
|
3537
|
+
const skeleton = {
|
|
3538
|
+
type: element.type,
|
|
3539
|
+
id,
|
|
3540
|
+
x: element.x,
|
|
3541
|
+
y: element.y,
|
|
3542
|
+
...defaultSize(element),
|
|
3543
|
+
...role,
|
|
3544
|
+
...set,
|
|
3545
|
+
...element.label === void 0 ? {} : { label: houseLabel(element.label) }
|
|
3546
|
+
};
|
|
3547
|
+
return skeleton;
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
var LAYOUT_HINT_THRESHOLD = 4;
|
|
3551
|
+
function layoutHint(elements) {
|
|
3552
|
+
if (elements.length < LAYOUT_HINT_THRESHOLD) return null;
|
|
3553
|
+
const positions = new Set(elements.map((element) => `${element.x},${element.y}`));
|
|
3554
|
+
if (positions.size > 2) return null;
|
|
3555
|
+
return `${elements.length} elements with no explicit layout \u2014 chalk_arrange(op: "layout-layered") will place them`;
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3322
3558
|
// src/app/usecases/write/annotate-elements.ts
|
|
3323
3559
|
var NOTE_GAP = 24;
|
|
3560
|
+
var NOTE_FONT_SIZE = 16;
|
|
3561
|
+
var NOTE_SEARCH_STEP = 40;
|
|
3562
|
+
var NOTE_SEARCH_LIMIT = 24;
|
|
3563
|
+
function clearSpotBelow(scene, preferred, size) {
|
|
3564
|
+
for (let i = 0; i <= NOTE_SEARCH_LIMIT; i++) {
|
|
3565
|
+
const candidate = {
|
|
3566
|
+
x: preferred.x,
|
|
3567
|
+
y: preferred.y + i * NOTE_SEARCH_STEP,
|
|
3568
|
+
width: size.width,
|
|
3569
|
+
height: size.height
|
|
3570
|
+
};
|
|
3571
|
+
if (!wouldCollide(candidate, scene)) return { x: candidate.x, y: candidate.y };
|
|
3572
|
+
}
|
|
3573
|
+
return preferred;
|
|
3574
|
+
}
|
|
3324
3575
|
function annotateElements(deps) {
|
|
3325
3576
|
return async (input) => {
|
|
3326
3577
|
const scene = deps.write.resolve(input.sceneId);
|
|
@@ -3335,14 +3586,20 @@ function annotateElements(deps) {
|
|
|
3335
3586
|
if (anchors.length === 0) return fail(emptySelection("chalk_annotate"));
|
|
3336
3587
|
const left = Math.min(...anchors.map((element) => element.x));
|
|
3337
3588
|
const bottom = Math.max(...anchors.map((element) => element.y + element.height));
|
|
3589
|
+
const wrapped = noteWrap(input.text, NOTE_FONT_SIZE, void 0);
|
|
3590
|
+
const size = {
|
|
3591
|
+
width: wrapped.width ?? measureLine(wrapped.text, NOTE_FONT_SIZE),
|
|
3592
|
+
height: wrapped.text.split("\n").length * NOTE_FONT_SIZE * LINE_HEIGHT
|
|
3593
|
+
};
|
|
3594
|
+
const spot = clearSpotBelow(scene.value.elements, { x: left, y: bottom + NOTE_GAP }, size);
|
|
3338
3595
|
const skeleton = {
|
|
3339
3596
|
type: "text",
|
|
3340
3597
|
id: deps.idGen.elementId(),
|
|
3341
|
-
x:
|
|
3342
|
-
y:
|
|
3343
|
-
|
|
3598
|
+
x: spot.x,
|
|
3599
|
+
y: spot.y,
|
|
3600
|
+
...wrapped,
|
|
3344
3601
|
// A size below the labels, so a note reads as commentary rather than as content.
|
|
3345
|
-
fontSize:
|
|
3602
|
+
fontSize: NOTE_FONT_SIZE,
|
|
3346
3603
|
fontFamily: HOUSE.fontFamily,
|
|
3347
3604
|
strokeColor: styleForRole("external").strokeColor,
|
|
3348
3605
|
customData: {
|
|
@@ -3397,52 +3654,6 @@ function focusElements(deps) {
|
|
|
3397
3654
|
};
|
|
3398
3655
|
}
|
|
3399
3656
|
|
|
3400
|
-
// src/domain/scene/anchors.ts
|
|
3401
|
-
var ANCHOR_GAP = 12;
|
|
3402
|
-
var anchorsOf = (element) => element.customData?.chalk?.anchorIds ?? [];
|
|
3403
|
-
var isAnchored = (element) => anchorsOf(element).length > 0;
|
|
3404
|
-
function anchoredPosition(anchor, size, placement) {
|
|
3405
|
-
const midX = anchor.x + anchor.width / 2 - size.width / 2;
|
|
3406
|
-
const midY = anchor.y + anchor.height / 2 - size.height / 2;
|
|
3407
|
-
switch (placement) {
|
|
3408
|
-
case "above":
|
|
3409
|
-
return { x: midX, y: anchor.y - size.height - ANCHOR_GAP };
|
|
3410
|
-
case "below":
|
|
3411
|
-
return { x: midX, y: anchor.y + anchor.height + ANCHOR_GAP };
|
|
3412
|
-
case "left":
|
|
3413
|
-
return { x: anchor.x - size.width - ANCHOR_GAP, y: midY };
|
|
3414
|
-
case "right":
|
|
3415
|
-
return { x: anchor.x + anchor.width + ANCHOR_GAP, y: midY };
|
|
3416
|
-
case "badge":
|
|
3417
|
-
return { x: anchor.x + anchor.width - size.width / 2, y: anchor.y - size.height / 2 };
|
|
3418
|
-
case "inside":
|
|
3419
|
-
return { x: midX, y: midY };
|
|
3420
|
-
}
|
|
3421
|
-
}
|
|
3422
|
-
var captionBelow = (position, size) => ({
|
|
3423
|
-
x: position.x,
|
|
3424
|
-
y: position.y + size.height + 8
|
|
3425
|
-
});
|
|
3426
|
-
function withAnchoredElements(scene, deltas) {
|
|
3427
|
-
const attached = scene.filter((element) => !element.isDeleted && isAnchored(element));
|
|
3428
|
-
if (attached.length === 0) return deltas;
|
|
3429
|
-
const widened = new Map(deltas);
|
|
3430
|
-
const present2 = new Set(scene.filter((element) => !element.isDeleted).map((e) => e.id));
|
|
3431
|
-
for (const element of attached) {
|
|
3432
|
-
if (widened.has(element.id)) continue;
|
|
3433
|
-
const live = anchorsOf(element).filter((id) => present2.has(id));
|
|
3434
|
-
if (live.length === 0) continue;
|
|
3435
|
-
const moves = live.map((id) => deltas.get(id));
|
|
3436
|
-
const [first] = moves;
|
|
3437
|
-
if (first === void 0) continue;
|
|
3438
|
-
if (!moves.every((move) => move !== void 0 && move[0] === first[0] && move[1] === first[1])) {
|
|
3439
|
-
continue;
|
|
3440
|
-
}
|
|
3441
|
-
widened.set(element.id, first);
|
|
3442
|
-
}
|
|
3443
|
-
return widened;
|
|
3444
|
-
}
|
|
3445
|
-
|
|
3446
3657
|
// src/domain/scene/frames.ts
|
|
3447
3658
|
function boundsAround(children) {
|
|
3448
3659
|
const pad2 = HOUSE.sizes.framePad;
|
|
@@ -3532,6 +3743,176 @@ function betweenIndices(lower, upper, count2) {
|
|
|
3532
3743
|
return generateNKeysBetween(lower, upper, count2).map((key) => Index.parse(key));
|
|
3533
3744
|
}
|
|
3534
3745
|
|
|
3746
|
+
// src/domain/scene/route.ts
|
|
3747
|
+
var ROUTE_CLEARANCE = 30;
|
|
3748
|
+
var TURN_PENALTY = 60;
|
|
3749
|
+
var MAX_GRID = 64;
|
|
3750
|
+
var EPSILON = 1e-3;
|
|
3751
|
+
function segmentHitsRect(a, b, rect) {
|
|
3752
|
+
let t0 = 0;
|
|
3753
|
+
let t1 = 1;
|
|
3754
|
+
const dx = b[0] - a[0];
|
|
3755
|
+
const dy = b[1] - a[1];
|
|
3756
|
+
const left = rect.x + EPSILON;
|
|
3757
|
+
const right = rect.x + rect.width - EPSILON;
|
|
3758
|
+
const top = rect.y + EPSILON;
|
|
3759
|
+
const bottom = rect.y + rect.height - EPSILON;
|
|
3760
|
+
const edges = [
|
|
3761
|
+
[-dx, a[0] - left],
|
|
3762
|
+
[dx, right - a[0]],
|
|
3763
|
+
[-dy, a[1] - top],
|
|
3764
|
+
[dy, bottom - a[1]]
|
|
3765
|
+
];
|
|
3766
|
+
for (const edge of edges) {
|
|
3767
|
+
const den = edge[0];
|
|
3768
|
+
const num = edge[1];
|
|
3769
|
+
if (den === 0) {
|
|
3770
|
+
if (num < 0) return false;
|
|
3771
|
+
continue;
|
|
3772
|
+
}
|
|
3773
|
+
const t = num / den;
|
|
3774
|
+
if (den < 0) {
|
|
3775
|
+
if (t > t1) return false;
|
|
3776
|
+
if (t > t0) t0 = t;
|
|
3777
|
+
} else {
|
|
3778
|
+
if (t < t0) return false;
|
|
3779
|
+
if (t < t1) t1 = t;
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
return t1 - t0 > EPSILON;
|
|
3783
|
+
}
|
|
3784
|
+
function routeHitsAny(points, obstacles) {
|
|
3785
|
+
for (let i = 1; i < points.length; i++) {
|
|
3786
|
+
const a = points[i - 1];
|
|
3787
|
+
const b = points[i];
|
|
3788
|
+
if (a === void 0 || b === void 0) continue;
|
|
3789
|
+
for (const rect of obstacles) if (segmentHitsRect(a, b, rect)) return true;
|
|
3790
|
+
}
|
|
3791
|
+
return false;
|
|
3792
|
+
}
|
|
3793
|
+
function axis(ends, sides, clearance) {
|
|
3794
|
+
const values = new Set(ends);
|
|
3795
|
+
for (const [low, high] of sides) {
|
|
3796
|
+
values.add(low - clearance);
|
|
3797
|
+
values.add(high + clearance);
|
|
3798
|
+
}
|
|
3799
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
3800
|
+
if (sorted.length <= MAX_GRID) return sorted;
|
|
3801
|
+
const step = sorted.length / MAX_GRID;
|
|
3802
|
+
const kept = new Set(ends);
|
|
3803
|
+
for (let i = 0; i < MAX_GRID; i++) {
|
|
3804
|
+
const value = sorted[Math.floor(i * step)];
|
|
3805
|
+
if (value !== void 0) kept.add(value);
|
|
3806
|
+
}
|
|
3807
|
+
return [...kept].sort((a, b) => a - b);
|
|
3808
|
+
}
|
|
3809
|
+
function routeAround(start, end, obstacles, clearance = ROUTE_CLEARANCE) {
|
|
3810
|
+
if (obstacles.length === 0) return null;
|
|
3811
|
+
const xs = axis(
|
|
3812
|
+
[start[0], end[0]],
|
|
3813
|
+
obstacles.map((r) => [r.x, r.x + r.width]),
|
|
3814
|
+
clearance
|
|
3815
|
+
);
|
|
3816
|
+
const ys = axis(
|
|
3817
|
+
[start[1], end[1]],
|
|
3818
|
+
obstacles.map((r) => [r.y, r.y + r.height]),
|
|
3819
|
+
clearance
|
|
3820
|
+
);
|
|
3821
|
+
const xi = xs.indexOf(start[0]);
|
|
3822
|
+
const yi = ys.indexOf(start[1]);
|
|
3823
|
+
const xg = xs.indexOf(end[0]);
|
|
3824
|
+
const yg = ys.indexOf(end[1]);
|
|
3825
|
+
if (xi < 0 || yi < 0 || xg < 0 || yg < 0) return null;
|
|
3826
|
+
const width = xs.length;
|
|
3827
|
+
const index = (cx, cy) => cy * width + cx;
|
|
3828
|
+
const goal = index(xg, yg);
|
|
3829
|
+
const at = (cx, cy) => {
|
|
3830
|
+
const x = xs[cx];
|
|
3831
|
+
const y = ys[cy];
|
|
3832
|
+
return [x ?? 0, y ?? 0];
|
|
3833
|
+
};
|
|
3834
|
+
const passable = (a, b) => {
|
|
3835
|
+
for (const rect of obstacles) if (segmentHitsRect(a, b, rect)) return false;
|
|
3836
|
+
return true;
|
|
3837
|
+
};
|
|
3838
|
+
const total = width * ys.length;
|
|
3839
|
+
const best = new Float64Array(total).fill(Number.POSITIVE_INFINITY);
|
|
3840
|
+
const cameFrom = new Int32Array(total).fill(-1);
|
|
3841
|
+
const arrived = new Uint8Array(total);
|
|
3842
|
+
const done = new Uint8Array(total);
|
|
3843
|
+
const startAt = index(xi, yi);
|
|
3844
|
+
best[startAt] = 0;
|
|
3845
|
+
const heuristic = (cx, cy) => Math.abs((xs[cx] ?? 0) - end[0]) + Math.abs((ys[cy] ?? 0) - end[1]);
|
|
3846
|
+
for (; ; ) {
|
|
3847
|
+
let node = -1;
|
|
3848
|
+
let cheapest = Number.POSITIVE_INFINITY;
|
|
3849
|
+
for (let i = 0; i < total; i++) {
|
|
3850
|
+
if (done[i] === 1) continue;
|
|
3851
|
+
const score = best[i];
|
|
3852
|
+
if (score === void 0 || !Number.isFinite(score)) continue;
|
|
3853
|
+
const estimate = score + heuristic(i % width, Math.floor(i / width));
|
|
3854
|
+
if (estimate < cheapest) {
|
|
3855
|
+
cheapest = estimate;
|
|
3856
|
+
node = i;
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
if (node < 0) return null;
|
|
3860
|
+
if (node === goal) break;
|
|
3861
|
+
done[node] = 1;
|
|
3862
|
+
const cx = node % width;
|
|
3863
|
+
const cy = Math.floor(node / width);
|
|
3864
|
+
const here = at(cx, cy);
|
|
3865
|
+
const cost = best[node] ?? 0;
|
|
3866
|
+
const steps = [
|
|
3867
|
+
[cx - 1, cy, 1],
|
|
3868
|
+
[cx + 1, cy, 1],
|
|
3869
|
+
[cx, cy - 1, 2],
|
|
3870
|
+
[cx, cy + 1, 2]
|
|
3871
|
+
];
|
|
3872
|
+
for (const [nx, ny, axisOf] of steps) {
|
|
3873
|
+
if (nx < 0 || ny < 0 || nx >= width || ny >= ys.length) continue;
|
|
3874
|
+
const next = index(nx, ny);
|
|
3875
|
+
if (done[next] === 1) continue;
|
|
3876
|
+
const there = at(nx, ny);
|
|
3877
|
+
if (!passable(here, there)) continue;
|
|
3878
|
+
const length = Math.abs(there[0] - here[0]) + Math.abs(there[1] - here[1]);
|
|
3879
|
+
const turn = arrived[node] !== 0 && arrived[node] !== axisOf ? TURN_PENALTY : 0;
|
|
3880
|
+
const candidate = cost + length + turn;
|
|
3881
|
+
const known = best[next];
|
|
3882
|
+
if (known === void 0 || candidate < known) {
|
|
3883
|
+
best[next] = candidate;
|
|
3884
|
+
cameFrom[next] = node;
|
|
3885
|
+
arrived[next] = axisOf;
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
const path = [];
|
|
3890
|
+
for (let node = goal; node >= 0; node = cameFrom[node] ?? -1) {
|
|
3891
|
+
path.push(at(node % width, Math.floor(node / width)));
|
|
3892
|
+
if (node === startAt) break;
|
|
3893
|
+
}
|
|
3894
|
+
path.reverse();
|
|
3895
|
+
return simplify(path);
|
|
3896
|
+
}
|
|
3897
|
+
function simplify(points) {
|
|
3898
|
+
if (points.length < 3) return points;
|
|
3899
|
+
const out = [];
|
|
3900
|
+
for (let i = 0; i < points.length; i++) {
|
|
3901
|
+
const previous = out[out.length - 1];
|
|
3902
|
+
const current = points[i];
|
|
3903
|
+
const next = points[i + 1];
|
|
3904
|
+
if (current === void 0) continue;
|
|
3905
|
+
if (previous === void 0 || next === void 0) {
|
|
3906
|
+
out.push(current);
|
|
3907
|
+
continue;
|
|
3908
|
+
}
|
|
3909
|
+
const straightX = previous[0] === current[0] && current[0] === next[0];
|
|
3910
|
+
const straightY = previous[1] === current[1] && current[1] === next[1];
|
|
3911
|
+
if (!straightX && !straightY) out.push(current);
|
|
3912
|
+
}
|
|
3913
|
+
return out;
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3535
3916
|
// src/domain/scene/arrange.ts
|
|
3536
3917
|
var boxOf = (element) => ({
|
|
3537
3918
|
x: element.x,
|
|
@@ -3574,6 +3955,38 @@ function edgePoint(from, to, gap) {
|
|
|
3574
3955
|
const t = scale + push;
|
|
3575
3956
|
return [cx + dx * t, cy + dy * t];
|
|
3576
3957
|
}
|
|
3958
|
+
var ROUTABLE_OBSTACLE = /* @__PURE__ */ new Set(["rectangle", "diamond", "ellipse", "image", "embeddable"]);
|
|
3959
|
+
function obstaclesFor(current, fromId, toId) {
|
|
3960
|
+
const out = [];
|
|
3961
|
+
for (const element of current.values()) {
|
|
3962
|
+
if (element.id === fromId || element.id === toId) continue;
|
|
3963
|
+
if (element.isDeleted || !ROUTABLE_OBSTACLE.has(element.type)) continue;
|
|
3964
|
+
out.push(boxOf(element));
|
|
3965
|
+
}
|
|
3966
|
+
return out;
|
|
3967
|
+
}
|
|
3968
|
+
var NO_FAN = { start: 0, end: 0 };
|
|
3969
|
+
function stubFrom(box, at) {
|
|
3970
|
+
const [cx, cy] = centre(box);
|
|
3971
|
+
const horizontal = at[0] <= box.x || at[0] >= box.x + box.width;
|
|
3972
|
+
if (horizontal) return [at[0] + (at[0] < cx ? -ROUTE_CLEARANCE : ROUTE_CLEARANCE), at[1]];
|
|
3973
|
+
return [at[0], at[1] + (at[1] < cy ? -ROUTE_CLEARANCE : ROUTE_CLEARANCE)];
|
|
3974
|
+
}
|
|
3975
|
+
function anchorOn(box, adjacent, gap) {
|
|
3976
|
+
const [cx, cy] = centre(box);
|
|
3977
|
+
const dx = adjacent[0] - cx;
|
|
3978
|
+
const dy = adjacent[1] - cy;
|
|
3979
|
+
if (dx === 0 && dy === 0) return [cx, cy];
|
|
3980
|
+
const toSide = Math.min(
|
|
3981
|
+
dx === 0 ? Number.POSITIVE_INFINITY : box.width / 2 / Math.abs(dx),
|
|
3982
|
+
dy === 0 ? Number.POSITIVE_INFINITY : box.height / 2 / Math.abs(dy)
|
|
3983
|
+
);
|
|
3984
|
+
const bx = cx + dx * toSide;
|
|
3985
|
+
const by = cy + dy * toSide;
|
|
3986
|
+
const reach = Math.hypot(dx * toSide, dy * toSide);
|
|
3987
|
+
if (reach === 0) return [bx, by];
|
|
3988
|
+
return [bx + dx * toSide * gap / reach, by + dy * toSide * gap / reach];
|
|
3989
|
+
}
|
|
3577
3990
|
function redrawOne(current, labels, arrow, lane) {
|
|
3578
3991
|
if (arrow.type !== "arrow") return [];
|
|
3579
3992
|
const fromId = startOf(arrow);
|
|
@@ -3589,18 +4002,53 @@ function redrawOne(current, labels, arrow, lane) {
|
|
|
3589
4002
|
...edgePoint(boxOf(from), boxOf(to), startGap),
|
|
3590
4003
|
...edgePoint(boxOf(to), boxOf(from), endGap)
|
|
3591
4004
|
];
|
|
3592
|
-
const
|
|
3593
|
-
const
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
);
|
|
4005
|
+
const direct = elbowed ? elbowRoute(ex - sx, ey - sy) : [point(0, 0), point(ex - sx, ey - sy)];
|
|
4006
|
+
const obstacles = obstaclesFor(current, fromId, toId);
|
|
4007
|
+
const absolute = direct.map(([px2, py]) => [sx + px2, sy + py]);
|
|
4008
|
+
const detoured = routeHitsAny(absolute, obstacles);
|
|
4009
|
+
const [fsx, fsy, fex, fey] = detoured ? facePoints(boxOf(from), boxOf(to), startGap, endGap, NO_FAN) : [sx, sy, ex, ey];
|
|
4010
|
+
const stubStart = stubFrom(boxOf(from), [fsx, fsy]);
|
|
4011
|
+
const stubEnd = stubFrom(boxOf(to), [fex, fey]);
|
|
4012
|
+
const found = detoured ? routeAround(stubStart, stubEnd, obstacles) : null;
|
|
4013
|
+
const route = found === null ? null : simplify([[fsx, fsy], ...found, [fex, fey]]);
|
|
4014
|
+
const drawn = route ?? direct.map(([px2, py]) => [sx + px2, sy + py]);
|
|
4015
|
+
const settled = [...drawn];
|
|
4016
|
+
const secondFromStart = settled[1];
|
|
4017
|
+
const secondFromEnd = settled[settled.length - 2];
|
|
4018
|
+
if (secondFromStart !== void 0) {
|
|
4019
|
+
settled[0] = anchorOn(boxOf(from), secondFromStart, startGap);
|
|
4020
|
+
}
|
|
4021
|
+
if (secondFromEnd !== void 0) {
|
|
4022
|
+
settled[settled.length - 1] = anchorOn(boxOf(to), secondFromEnd, endGap);
|
|
4023
|
+
}
|
|
4024
|
+
const first = settled[0] ?? [sx, sy];
|
|
4025
|
+
const origin = [first[0], first[1]];
|
|
4026
|
+
const points = settled.map(([ax, ay]) => point(ax - origin[0], ay - origin[1]));
|
|
4027
|
+
const pxs = points.map(([px2]) => px2);
|
|
4028
|
+
const pys = points.map(([, py]) => py);
|
|
4029
|
+
const spans = {
|
|
4030
|
+
w: Math.max(...pxs) - Math.min(...pxs),
|
|
4031
|
+
h: Math.max(...pys) - Math.min(...pys)
|
|
4032
|
+
};
|
|
3600
4033
|
const redrawn = {
|
|
3601
4034
|
...arrow,
|
|
3602
|
-
|
|
3603
|
-
|
|
4035
|
+
// A detour is an orthogonal route, so it is drawn as one.
|
|
4036
|
+
//
|
|
4037
|
+
// This is not cosmetic. Excalidraw owns the endpoints of a *bound* arrow at paint
|
|
4038
|
+
// time: with `focus: 0` it re-derives where the line meets each box from the segment
|
|
4039
|
+
// adjacent to it, and a detour changes exactly that segment. The stored endpoints are
|
|
4040
|
+
// right — all twenty-nine measured on the boundary of the box they name — and the
|
|
4041
|
+
// canvas still drew several of them leaving from nowhere, because the renderer had
|
|
4042
|
+
// recomputed them from a first segment that now pointed the wrong way. An elbow arrow
|
|
4043
|
+
// is anchored differently and keeps the route it was given.
|
|
4044
|
+
//
|
|
4045
|
+
// The second reason is what it looks like. A right-angled detour rendered with
|
|
4046
|
+
// `roundness` comes out as a long S sweeping across the diagram — which is what the
|
|
4047
|
+
// bend at the bottom of the sample was. Square corners are what an orthogonal route
|
|
4048
|
+
// actually means.
|
|
4049
|
+
...route === null ? {} : { elbowed: true, roundness: null },
|
|
4050
|
+
x: origin[0],
|
|
4051
|
+
y: origin[1],
|
|
3604
4052
|
width: spans.w,
|
|
3605
4053
|
height: spans.h,
|
|
3606
4054
|
points,
|
|
@@ -3608,7 +4056,7 @@ function redrawOne(current, labels, arrow, lane) {
|
|
|
3608
4056
|
};
|
|
3609
4057
|
const label = labels.get(arrow.id);
|
|
3610
4058
|
if (label === void 0) return [redrawn];
|
|
3611
|
-
const mid = routeMidpoint({ x:
|
|
4059
|
+
const mid = routeMidpoint({ x: origin[0], y: origin[1], points });
|
|
3612
4060
|
const at = { x: mid.x - label.width / 2, y: mid.y - label.height / 2 };
|
|
3613
4061
|
return [redrawn, { ...label, ...at }];
|
|
3614
4062
|
}
|
|
@@ -3621,6 +4069,7 @@ function faceBetween(from, to) {
|
|
|
3621
4069
|
return dy >= 0 ? "bottom" : "top";
|
|
3622
4070
|
}
|
|
3623
4071
|
var LANE_SPREAD = 44;
|
|
4072
|
+
var LANE_INSET = 10;
|
|
3624
4073
|
function laneOffsets(arrows, current) {
|
|
3625
4074
|
const groups = /* @__PURE__ */ new Map();
|
|
3626
4075
|
const add = (nodeId, face, node, slot) => {
|
|
@@ -3656,7 +4105,8 @@ function laneOffsets(arrows, current) {
|
|
|
3656
4105
|
if (slots.length < 2) continue;
|
|
3657
4106
|
slots.sort((a, b) => a.along - b.along || (a.arrow < b.arrow ? -1 : 1));
|
|
3658
4107
|
const span = face === "right" || face === "left" ? node.height : node.width;
|
|
3659
|
-
const
|
|
4108
|
+
const usable = Math.max(0, span - LANE_INSET * 2);
|
|
4109
|
+
const spread = Math.min(LANE_SPREAD, usable / Math.max(1, slots.length - 1));
|
|
3660
4110
|
slots.forEach((slot, i) => {
|
|
3661
4111
|
const shift = (i - (slots.length - 1) / 2) * spread;
|
|
3662
4112
|
const so_far = offsets.get(slot.arrow) ?? { start: 0, end: 0 };
|
|
@@ -3745,14 +4195,14 @@ var alignments = {
|
|
|
3745
4195
|
"align-center-h": (box, bounds) => [0, bounds.y + bounds.height / 2 - (box.y + box.height / 2)],
|
|
3746
4196
|
"align-center-v": (box, bounds) => [bounds.x + bounds.width / 2 - (box.x + box.width / 2), 0]
|
|
3747
4197
|
};
|
|
3748
|
-
function distribute(boxes, spacing,
|
|
3749
|
-
const size =
|
|
3750
|
-
const order = boxes.map((box, i) => ({ box, i })).sort((a, b) => a.box[
|
|
4198
|
+
function distribute(boxes, spacing, axis2) {
|
|
4199
|
+
const size = axis2 === "x" ? "width" : "height";
|
|
4200
|
+
const order = boxes.map((box, i) => ({ box, i })).sort((a, b) => a.box[axis2] - b.box[axis2]);
|
|
3751
4201
|
const deltas = boxes.map(() => [0, 0]);
|
|
3752
|
-
let cursor = order[0]?.box[
|
|
4202
|
+
let cursor = order[0]?.box[axis2] ?? 0;
|
|
3753
4203
|
for (const { box, i } of order) {
|
|
3754
|
-
const delta = cursor - box[
|
|
3755
|
-
deltas[i] =
|
|
4204
|
+
const delta = cursor - box[axis2];
|
|
4205
|
+
deltas[i] = axis2 === "x" ? [delta, 0] : [0, delta];
|
|
3756
4206
|
cursor += box[size] + spacing;
|
|
3757
4207
|
}
|
|
3758
4208
|
return deltas;
|
|
@@ -3768,10 +4218,10 @@ function grid(boxes, options) {
|
|
|
3768
4218
|
return [anchor.x + column * cellW - box.x, anchor.y + rowIndex * cellH - box.y];
|
|
3769
4219
|
});
|
|
3770
4220
|
}
|
|
3771
|
-
function flip(boxes,
|
|
4221
|
+
function flip(boxes, axis2) {
|
|
3772
4222
|
const bounds = boundsOfAll(boxes);
|
|
3773
4223
|
return boxes.map((box) => {
|
|
3774
|
-
if (
|
|
4224
|
+
if (axis2 === "x") {
|
|
3775
4225
|
const mirrored2 = bounds.x + bounds.width - (box.x - bounds.x) - box.width;
|
|
3776
4226
|
return [mirrored2 - box.x, 0];
|
|
3777
4227
|
}
|
|
@@ -3832,6 +4282,12 @@ function arrangeGeometry(input) {
|
|
|
3832
4282
|
if (input.op === "layout-layered") {
|
|
3833
4283
|
return ok({ moved: layoutLayered(input.scene, targets, input.spacing) });
|
|
3834
4284
|
}
|
|
4285
|
+
if (input.op === "layout-radial") {
|
|
4286
|
+
return ok({ moved: layoutRadial(input.scene, targets, input.spacing) });
|
|
4287
|
+
}
|
|
4288
|
+
if (input.op === "layout-tree") {
|
|
4289
|
+
return ok({ moved: layoutTree(input.scene, targets, input.spacing) });
|
|
4290
|
+
}
|
|
3835
4291
|
const boxes = targets.map(boxOf);
|
|
3836
4292
|
const spacing = input.spacing ?? HOUSE.spacing.h;
|
|
3837
4293
|
const columns = input.columns ?? Math.ceil(Math.sqrt(targets.length));
|
|
@@ -3863,6 +4319,44 @@ function arrangeGeometry(input) {
|
|
|
3863
4319
|
});
|
|
3864
4320
|
return ok({ moved: translate(input.scene, byDelta) });
|
|
3865
4321
|
}
|
|
4322
|
+
function backEdgeIndices(nodeIds, edges) {
|
|
4323
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
4324
|
+
for (const id of nodeIds) outgoing.set(id, []);
|
|
4325
|
+
edges.forEach((edge, index) => {
|
|
4326
|
+
outgoing.get(edge.from)?.push(index);
|
|
4327
|
+
});
|
|
4328
|
+
const ON_STACK = 1;
|
|
4329
|
+
const DONE = 2;
|
|
4330
|
+
const state = /* @__PURE__ */ new Map();
|
|
4331
|
+
const back = /* @__PURE__ */ new Set();
|
|
4332
|
+
for (const root of [...nodeIds].sort()) {
|
|
4333
|
+
if (state.has(root)) continue;
|
|
4334
|
+
state.set(root, ON_STACK);
|
|
4335
|
+
const stack = [{ node: root, cursor: 0 }];
|
|
4336
|
+
while (stack.length > 0) {
|
|
4337
|
+
const frame = stack[stack.length - 1];
|
|
4338
|
+
if (frame === void 0) break;
|
|
4339
|
+
const fanOut = outgoing.get(frame.node) ?? [];
|
|
4340
|
+
if (frame.cursor >= fanOut.length) {
|
|
4341
|
+
state.set(frame.node, DONE);
|
|
4342
|
+
stack.pop();
|
|
4343
|
+
continue;
|
|
4344
|
+
}
|
|
4345
|
+
const index = fanOut[frame.cursor];
|
|
4346
|
+
frame.cursor += 1;
|
|
4347
|
+
if (index === void 0) continue;
|
|
4348
|
+
const head = edges[index]?.to;
|
|
4349
|
+
if (head === void 0) continue;
|
|
4350
|
+
const seen = state.get(head);
|
|
4351
|
+
if (seen === ON_STACK) back.add(index);
|
|
4352
|
+
else if (seen === void 0) {
|
|
4353
|
+
state.set(head, ON_STACK);
|
|
4354
|
+
stack.push({ node: head, cursor: 0 });
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
return back;
|
|
4359
|
+
}
|
|
3866
4360
|
function layoutLayered(scene, targets, spacing) {
|
|
3867
4361
|
const inSet = new Set(targets.map((element) => element.id));
|
|
3868
4362
|
const edges = [];
|
|
@@ -3876,13 +4370,18 @@ function layoutLayered(scene, targets, spacing) {
|
|
|
3876
4370
|
}
|
|
3877
4371
|
const nodes = targets.filter((element) => !isLinear(element) && !isAnchored(element));
|
|
3878
4372
|
if (nodes.length === 0) return [];
|
|
4373
|
+
const back = backEdgeIndices(
|
|
4374
|
+
nodes.map((node) => node.id),
|
|
4375
|
+
edges
|
|
4376
|
+
);
|
|
4377
|
+
const forward = edges.filter((_, index) => !back.has(index));
|
|
3879
4378
|
const incoming = /* @__PURE__ */ new Map();
|
|
3880
4379
|
for (const node of nodes) incoming.set(node.id, []);
|
|
3881
|
-
for (const edge of
|
|
4380
|
+
for (const edge of forward) incoming.get(edge.to)?.push(edge.from);
|
|
3882
4381
|
const rank = new Map(nodes.map((node) => [node.id, 0]));
|
|
3883
4382
|
for (let pass = 0; pass < nodes.length; pass++) {
|
|
3884
4383
|
let settled = true;
|
|
3885
|
-
for (const edge of
|
|
4384
|
+
for (const edge of forward) {
|
|
3886
4385
|
const from = rank.get(edge.from) ?? 0;
|
|
3887
4386
|
const to = rank.get(edge.to) ?? 0;
|
|
3888
4387
|
if (to < from + 1) {
|
|
@@ -3923,26 +4422,87 @@ function layoutLayered(scene, targets, spacing) {
|
|
|
3923
4422
|
previous = key;
|
|
3924
4423
|
}
|
|
3925
4424
|
}
|
|
3926
|
-
const position = /* @__PURE__ */ new Map();
|
|
3927
4425
|
const rankNumbers = [...slots.keys()].sort((a, b) => a - b);
|
|
4426
|
+
const childrenOf2 = /* @__PURE__ */ new Map();
|
|
3928
4427
|
for (const r of rankNumbers) {
|
|
3929
|
-
const
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
(a, b) => a.barycentre - b.barycentre || (a.slot.key < b.slot.key ? -1 : a.slot.key > b.slot.key ? 1 : 0)
|
|
3937
|
-
);
|
|
3938
|
-
scored.forEach(({ slot }, i) => {
|
|
3939
|
-
position.set(slot.key, i);
|
|
3940
|
-
});
|
|
3941
|
-
slots.set(
|
|
3942
|
-
r,
|
|
3943
|
-
scored.map((entry) => entry.slot)
|
|
3944
|
-
);
|
|
4428
|
+
for (const slot of slots.get(r) ?? []) {
|
|
4429
|
+
for (const parent of slot.parents) {
|
|
4430
|
+
const kids = childrenOf2.get(parent);
|
|
4431
|
+
if (kids === void 0) childrenOf2.set(parent, [slot.key]);
|
|
4432
|
+
else kids.push(slot.key);
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
3945
4435
|
}
|
|
4436
|
+
const indicesOf = (ordering) => {
|
|
4437
|
+
const at = /* @__PURE__ */ new Map();
|
|
4438
|
+
for (const bucket of ordering.values()) {
|
|
4439
|
+
bucket.forEach((slot, i) => {
|
|
4440
|
+
at.set(slot.key, i);
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4443
|
+
return at;
|
|
4444
|
+
};
|
|
4445
|
+
const crossings = (ordering) => {
|
|
4446
|
+
const at = indicesOf(ordering);
|
|
4447
|
+
let total = 0;
|
|
4448
|
+
for (const r of rankNumbers) {
|
|
4449
|
+
const pairs = [];
|
|
4450
|
+
for (const slot of ordering.get(r) ?? []) {
|
|
4451
|
+
const below = at.get(slot.key);
|
|
4452
|
+
if (below === void 0) continue;
|
|
4453
|
+
for (const parent of slot.parents) {
|
|
4454
|
+
const above = at.get(parent);
|
|
4455
|
+
if (above !== void 0) pairs.push([above, below]);
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
4459
|
+
for (let j = i + 1; j < pairs.length; j++) {
|
|
4460
|
+
const a = pairs[i];
|
|
4461
|
+
const b = pairs[j];
|
|
4462
|
+
if (a === void 0 || b === void 0) continue;
|
|
4463
|
+
if ((a[0] - b[0]) * (a[1] - b[1]) < 0) total += 1;
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
return total;
|
|
4468
|
+
};
|
|
4469
|
+
const sweep = (ordering, direction) => {
|
|
4470
|
+
const at = indicesOf(ordering);
|
|
4471
|
+
const next = /* @__PURE__ */ new Map();
|
|
4472
|
+
const order = direction === "down" ? rankNumbers : [...rankNumbers].reverse();
|
|
4473
|
+
for (const r of order) {
|
|
4474
|
+
const scored = (ordering.get(r) ?? []).map((slot) => {
|
|
4475
|
+
const adjacent = direction === "down" ? slot.parents : childrenOf2.get(slot.key) ?? [];
|
|
4476
|
+
const known = adjacent.map((key) => at.get(key)).filter((index) => index !== void 0);
|
|
4477
|
+
return {
|
|
4478
|
+
slot,
|
|
4479
|
+
barycentre: known.length === 0 ? Number.POSITIVE_INFINITY : known.reduce((sum, index) => sum + index, 0) / known.length
|
|
4480
|
+
};
|
|
4481
|
+
});
|
|
4482
|
+
scored.sort(
|
|
4483
|
+
(a, b) => a.barycentre - b.barycentre || (a.slot.key < b.slot.key ? -1 : a.slot.key > b.slot.key ? 1 : 0)
|
|
4484
|
+
);
|
|
4485
|
+
const settled = scored.map((entry) => entry.slot);
|
|
4486
|
+
settled.forEach((slot, i) => {
|
|
4487
|
+
at.set(slot.key, i);
|
|
4488
|
+
});
|
|
4489
|
+
next.set(r, settled);
|
|
4490
|
+
}
|
|
4491
|
+
return next;
|
|
4492
|
+
};
|
|
4493
|
+
const SWEEPS = 4;
|
|
4494
|
+
let current = new Map(rankNumbers.map((r) => [r, [...slots.get(r) ?? []]]));
|
|
4495
|
+
let best = current;
|
|
4496
|
+
let fewest = Number.POSITIVE_INFINITY;
|
|
4497
|
+
for (let round = 0; round < SWEEPS; round++) {
|
|
4498
|
+
current = sweep(current, round % 2 === 0 ? "down" : "up");
|
|
4499
|
+
const score = crossings(current);
|
|
4500
|
+
if (score < fewest) {
|
|
4501
|
+
fewest = score;
|
|
4502
|
+
best = current;
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
for (const r of rankNumbers) slots.set(r, [...best.get(r) ?? []]);
|
|
3946
4506
|
const gapV = HOUSE.spacing.v;
|
|
3947
4507
|
const labelWidths = labelsByContainer(scene);
|
|
3948
4508
|
const captionRoom = (column) => {
|
|
@@ -3990,6 +4550,202 @@ function layoutLayered(scene, targets, spacing) {
|
|
|
3990
4550
|
});
|
|
3991
4551
|
return translate(scene, deltas);
|
|
3992
4552
|
}
|
|
4553
|
+
var TAU = Math.PI * 2;
|
|
4554
|
+
var normalizeAngle = (radians) => (radians % TAU + TAU) % TAU;
|
|
4555
|
+
function meanAngle(angles) {
|
|
4556
|
+
let x = 0;
|
|
4557
|
+
let y = 0;
|
|
4558
|
+
for (const angle of angles) {
|
|
4559
|
+
x += Math.cos(angle);
|
|
4560
|
+
y += Math.sin(angle);
|
|
4561
|
+
}
|
|
4562
|
+
return Math.atan2(y, x);
|
|
4563
|
+
}
|
|
4564
|
+
var ringExtent = (element) => Math.max(element.width, element.height);
|
|
4565
|
+
function adjacency(scene, nodes) {
|
|
4566
|
+
const inSet = new Set(nodes.map((node) => node.id));
|
|
4567
|
+
const neighbours = /* @__PURE__ */ new Map();
|
|
4568
|
+
for (const node of nodes) neighbours.set(node.id, /* @__PURE__ */ new Set());
|
|
4569
|
+
for (const element of scene) {
|
|
4570
|
+
if (element.type !== "arrow") continue;
|
|
4571
|
+
const from = startOf(element);
|
|
4572
|
+
const to = endOf(element);
|
|
4573
|
+
if (from === null || to === null || from === to) continue;
|
|
4574
|
+
if (!inSet.has(from) || !inSet.has(to)) continue;
|
|
4575
|
+
neighbours.get(from)?.add(to);
|
|
4576
|
+
neighbours.get(to)?.add(from);
|
|
4577
|
+
}
|
|
4578
|
+
return neighbours;
|
|
4579
|
+
}
|
|
4580
|
+
function pickHub(neighbours, ordered) {
|
|
4581
|
+
const first = ordered[0];
|
|
4582
|
+
if (first === void 0) return void 0;
|
|
4583
|
+
const degree = (element) => neighbours.get(element.id)?.size ?? 0;
|
|
4584
|
+
let hub = first;
|
|
4585
|
+
for (const node of ordered) if (degree(node) > degree(hub)) hub = node;
|
|
4586
|
+
return hub;
|
|
4587
|
+
}
|
|
4588
|
+
function layoutRadial(scene, targets, spacing) {
|
|
4589
|
+
const nodes = targets.filter((element) => !isLinear(element) && !isAnchored(element));
|
|
4590
|
+
const ordered = [...nodes].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
4591
|
+
const neighbours = adjacency(scene, nodes);
|
|
4592
|
+
const hub = pickHub(neighbours, ordered);
|
|
4593
|
+
if (hub === void 0) return [];
|
|
4594
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
4595
|
+
const gap = spacing ?? HOUSE.spacing.h;
|
|
4596
|
+
const ringOf = /* @__PURE__ */ new Map([[hub.id, 0]]);
|
|
4597
|
+
const parentsOf = /* @__PURE__ */ new Map();
|
|
4598
|
+
let frontier = [hub.id];
|
|
4599
|
+
let depth = 0;
|
|
4600
|
+
while (frontier.length > 0) {
|
|
4601
|
+
depth += 1;
|
|
4602
|
+
const discovered = /* @__PURE__ */ new Map();
|
|
4603
|
+
for (const id of frontier) {
|
|
4604
|
+
for (const other of [...neighbours.get(id) ?? []].sort()) {
|
|
4605
|
+
if (ringOf.has(other)) continue;
|
|
4606
|
+
const found = discovered.get(other);
|
|
4607
|
+
if (found === void 0) discovered.set(other, [id]);
|
|
4608
|
+
else found.push(id);
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
for (const [child, parents] of discovered) {
|
|
4612
|
+
ringOf.set(child, depth);
|
|
4613
|
+
parentsOf.set(child, parents);
|
|
4614
|
+
}
|
|
4615
|
+
frontier = [...discovered.keys()];
|
|
4616
|
+
}
|
|
4617
|
+
for (const node of ordered) {
|
|
4618
|
+
if (!ringOf.has(node.id)) ringOf.set(node.id, depth + 1);
|
|
4619
|
+
}
|
|
4620
|
+
const rings = /* @__PURE__ */ new Map();
|
|
4621
|
+
for (const node of ordered) {
|
|
4622
|
+
const ring = ringOf.get(node.id) ?? 0;
|
|
4623
|
+
const bucket = rings.get(ring);
|
|
4624
|
+
if (bucket === void 0) rings.set(ring, [node.id]);
|
|
4625
|
+
else bucket.push(node.id);
|
|
4626
|
+
}
|
|
4627
|
+
const [cx, cy] = centre(boundsOfAll(nodes.map(boxOf)));
|
|
4628
|
+
const deltas = /* @__PURE__ */ new Map();
|
|
4629
|
+
deltas.set(hub.id, [cx - (hub.x + hub.width / 2), cy - (hub.y + hub.height / 2)]);
|
|
4630
|
+
const angleOf = /* @__PURE__ */ new Map([[hub.id, 0]]);
|
|
4631
|
+
let cleared = ringExtent(hub) / 2;
|
|
4632
|
+
for (const ring of [...rings.keys()].filter((r) => r > 0).sort((a, b) => a - b)) {
|
|
4633
|
+
const members = rings.get(ring) ?? [];
|
|
4634
|
+
if (members.length === 0) continue;
|
|
4635
|
+
const scored = members.map((id) => {
|
|
4636
|
+
const known = (parentsOf.get(id) ?? []).map((parent) => angleOf.get(parent)).filter((angle) => angle !== void 0);
|
|
4637
|
+
return {
|
|
4638
|
+
id,
|
|
4639
|
+
at: known.length === 0 ? Number.POSITIVE_INFINITY : normalizeAngle(meanAngle(known))
|
|
4640
|
+
};
|
|
4641
|
+
});
|
|
4642
|
+
scored.sort((a, b) => a.at - b.at || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
4643
|
+
const extents = members.map((id) => {
|
|
4644
|
+
const node = byId.get(id);
|
|
4645
|
+
return node === void 0 ? 0 : ringExtent(node);
|
|
4646
|
+
});
|
|
4647
|
+
const widest = Math.max(...extents);
|
|
4648
|
+
const seating = extents.reduce((sum, extent) => sum + extent + gap, 0) / TAU;
|
|
4649
|
+
const radius = Math.max(seating, cleared + widest / 2 + gap);
|
|
4650
|
+
scored.forEach(({ id }, i) => {
|
|
4651
|
+
const theta = -Math.PI / 2 + TAU * i / members.length;
|
|
4652
|
+
angleOf.set(id, normalizeAngle(theta));
|
|
4653
|
+
const node = byId.get(id);
|
|
4654
|
+
if (node === void 0) return;
|
|
4655
|
+
deltas.set(id, [
|
|
4656
|
+
cx + radius * Math.cos(theta) - node.width / 2 - node.x,
|
|
4657
|
+
cy + radius * Math.sin(theta) - node.height / 2 - node.y
|
|
4658
|
+
]);
|
|
4659
|
+
});
|
|
4660
|
+
cleared = radius + widest / 2;
|
|
4661
|
+
}
|
|
4662
|
+
return translate(scene, deltas);
|
|
4663
|
+
}
|
|
4664
|
+
function layoutTree(scene, targets, spacing) {
|
|
4665
|
+
const nodes = targets.filter((element) => !isLinear(element) && !isAnchored(element));
|
|
4666
|
+
const ordered = [...nodes].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
4667
|
+
const neighbours = adjacency(scene, nodes);
|
|
4668
|
+
const root = pickHub(neighbours, ordered);
|
|
4669
|
+
if (root === void 0) return [];
|
|
4670
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
4671
|
+
const gapH = spacing ?? HOUSE.spacing.h;
|
|
4672
|
+
const gapV = HOUSE.spacing.v;
|
|
4673
|
+
const depthOf = /* @__PURE__ */ new Map([[root.id, 0]]);
|
|
4674
|
+
const childrenOf2 = /* @__PURE__ */ new Map();
|
|
4675
|
+
const order = [];
|
|
4676
|
+
const stack = [root.id];
|
|
4677
|
+
const seen = /* @__PURE__ */ new Set([root.id]);
|
|
4678
|
+
while (stack.length > 0) {
|
|
4679
|
+
const id = stack.pop();
|
|
4680
|
+
if (id === void 0) continue;
|
|
4681
|
+
order.push(id);
|
|
4682
|
+
const kids = [];
|
|
4683
|
+
const adjacent = [...neighbours.get(id) ?? []].sort();
|
|
4684
|
+
for (const other of adjacent) {
|
|
4685
|
+
if (seen.has(other)) continue;
|
|
4686
|
+
seen.add(other);
|
|
4687
|
+
depthOf.set(other, (depthOf.get(id) ?? 0) + 1);
|
|
4688
|
+
kids.push(other);
|
|
4689
|
+
}
|
|
4690
|
+
childrenOf2.set(id, kids);
|
|
4691
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
4692
|
+
const kid = kids[i];
|
|
4693
|
+
if (kid !== void 0) stack.push(kid);
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
for (const node of ordered) {
|
|
4697
|
+
if (seen.has(node.id)) continue;
|
|
4698
|
+
seen.add(node.id);
|
|
4699
|
+
depthOf.set(node.id, 1);
|
|
4700
|
+
order.push(node.id);
|
|
4701
|
+
}
|
|
4702
|
+
const columns = /* @__PURE__ */ new Map();
|
|
4703
|
+
for (const id of order) {
|
|
4704
|
+
const d = depthOf.get(id) ?? 0;
|
|
4705
|
+
const bucket = columns.get(d);
|
|
4706
|
+
if (bucket === void 0) columns.set(d, [id]);
|
|
4707
|
+
else bucket.push(id);
|
|
4708
|
+
}
|
|
4709
|
+
const captions = labelsByContainer(scene);
|
|
4710
|
+
const crossing = /* @__PURE__ */ new Map();
|
|
4711
|
+
for (const element of scene) {
|
|
4712
|
+
if (element.type !== "arrow") continue;
|
|
4713
|
+
const from = startOf(element);
|
|
4714
|
+
const to = endOf(element);
|
|
4715
|
+
if (from === null || to === null) continue;
|
|
4716
|
+
const a = depthOf.get(from);
|
|
4717
|
+
const b = depthOf.get(to);
|
|
4718
|
+
if (a === void 0 || b === void 0 || a === b) continue;
|
|
4719
|
+
const caption = captions.get(element.id);
|
|
4720
|
+
if (caption === void 0) continue;
|
|
4721
|
+
const gap = Math.min(a, b);
|
|
4722
|
+
crossing.set(gap, Math.max(crossing.get(gap) ?? 0, caption.width));
|
|
4723
|
+
}
|
|
4724
|
+
const anchor = boundsOfAll(nodes.map(boxOf));
|
|
4725
|
+
const depths = [...columns.keys()].sort((a, b) => a - b);
|
|
4726
|
+
const columnX = /* @__PURE__ */ new Map();
|
|
4727
|
+
let cursorX = anchor.x;
|
|
4728
|
+
for (const d of depths) {
|
|
4729
|
+
columnX.set(d, cursorX);
|
|
4730
|
+
const widest = Math.max(...(columns.get(d) ?? []).map((id) => byId.get(id)?.width ?? 0));
|
|
4731
|
+
const caption = crossing.get(d) ?? 0;
|
|
4732
|
+
cursorX += widest + Math.max(gapH, caption === 0 ? 0 : caption + HOUSE.sizes.labelPadX * 2);
|
|
4733
|
+
}
|
|
4734
|
+
const columnHeight = (d) => (columns.get(d) ?? []).reduce((sum, id) => sum + (byId.get(id)?.height ?? 0) + gapV, -gapV);
|
|
4735
|
+
const tallest = Math.max(...depths.map(columnHeight));
|
|
4736
|
+
const deltas = /* @__PURE__ */ new Map();
|
|
4737
|
+
for (const d of depths) {
|
|
4738
|
+
let cursorY = anchor.y + (tallest - columnHeight(d)) / 2;
|
|
4739
|
+
for (const id of columns.get(d) ?? []) {
|
|
4740
|
+
const node = byId.get(id);
|
|
4741
|
+
if (node === void 0) continue;
|
|
4742
|
+
const x = columnX.get(d) ?? anchor.x;
|
|
4743
|
+
deltas.set(id, [x - node.x, cursorY - node.y]);
|
|
4744
|
+
cursorY += node.height + gapV;
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
return translate(scene, deltas);
|
|
4748
|
+
}
|
|
3993
4749
|
|
|
3994
4750
|
// src/app/usecases/write/arrange-elements.ts
|
|
3995
4751
|
function arrangeElements(deps) {
|
|
@@ -4165,82 +4921,6 @@ function createDebugElement(deps) {
|
|
|
4165
4921
|
};
|
|
4166
4922
|
}
|
|
4167
4923
|
|
|
4168
|
-
// src/domain/scene/skeleton.ts
|
|
4169
|
-
var isContainer = (type) => type === "rectangle" || type === "diamond" || type === "ellipse";
|
|
4170
|
-
function explicit(input) {
|
|
4171
|
-
const kept = {};
|
|
4172
|
-
for (const [key, value] of Object.entries(input)) {
|
|
4173
|
-
if (value === void 0) continue;
|
|
4174
|
-
if (key === "type" || key === "id" || key === "role" || key === "label") continue;
|
|
4175
|
-
kept[key] = value;
|
|
4176
|
-
}
|
|
4177
|
-
return kept;
|
|
4178
|
-
}
|
|
4179
|
-
var defaultSize = (element) => labelledSize(element.label, element.fontSize ?? HOUSE.fontSize);
|
|
4180
|
-
function normalizeElements(input) {
|
|
4181
|
-
return input.elements.map((element, i) => {
|
|
4182
|
-
const fallback = input.ids[i];
|
|
4183
|
-
if (fallback === void 0) {
|
|
4184
|
-
throw new Error(`normalizeElements needs one id per element: none at position ${i}`);
|
|
4185
|
-
}
|
|
4186
|
-
const id = element.id ?? fallback;
|
|
4187
|
-
const role = styleForRole(element.role ?? "compute");
|
|
4188
|
-
const set = explicit(element);
|
|
4189
|
-
if (element.type === "text") {
|
|
4190
|
-
const text = element.text ?? element.label ?? "";
|
|
4191
|
-
const skeleton2 = {
|
|
4192
|
-
type: "text",
|
|
4193
|
-
id,
|
|
4194
|
-
x: element.x,
|
|
4195
|
-
y: element.y,
|
|
4196
|
-
fontSize: HOUSE.fontSize,
|
|
4197
|
-
fontFamily: HOUSE.fontFamily,
|
|
4198
|
-
// A free-standing note is not a service box: it takes the role's ink but never
|
|
4199
|
-
// its fill, because a filled rectangle behind a caption reads as a shape.
|
|
4200
|
-
strokeColor: role.strokeColor,
|
|
4201
|
-
...set,
|
|
4202
|
-
text
|
|
4203
|
-
};
|
|
4204
|
-
return skeleton2;
|
|
4205
|
-
}
|
|
4206
|
-
if (element.type === "line") {
|
|
4207
|
-
const skeleton2 = {
|
|
4208
|
-
type: "line",
|
|
4209
|
-
id,
|
|
4210
|
-
x: element.x,
|
|
4211
|
-
y: element.y,
|
|
4212
|
-
strokeColor: role.strokeColor,
|
|
4213
|
-
strokeWidth: HOUSE.strokeWidth,
|
|
4214
|
-
roughness: HOUSE.roughness,
|
|
4215
|
-
...set,
|
|
4216
|
-
...element.label === void 0 ? {} : { label: houseLabel(element.label) }
|
|
4217
|
-
};
|
|
4218
|
-
return skeleton2;
|
|
4219
|
-
}
|
|
4220
|
-
if (!isContainer(element.type)) {
|
|
4221
|
-
throw new Error(`normalizeElements cannot build a ${element.type}`);
|
|
4222
|
-
}
|
|
4223
|
-
const skeleton = {
|
|
4224
|
-
type: element.type,
|
|
4225
|
-
id,
|
|
4226
|
-
x: element.x,
|
|
4227
|
-
y: element.y,
|
|
4228
|
-
...defaultSize(element),
|
|
4229
|
-
...role,
|
|
4230
|
-
...set,
|
|
4231
|
-
...element.label === void 0 ? {} : { label: houseLabel(element.label) }
|
|
4232
|
-
};
|
|
4233
|
-
return skeleton;
|
|
4234
|
-
});
|
|
4235
|
-
}
|
|
4236
|
-
var LAYOUT_HINT_THRESHOLD = 4;
|
|
4237
|
-
function layoutHint(elements) {
|
|
4238
|
-
if (elements.length < LAYOUT_HINT_THRESHOLD) return null;
|
|
4239
|
-
const positions = new Set(elements.map((element) => `${element.x},${element.y}`));
|
|
4240
|
-
if (positions.size > 2) return null;
|
|
4241
|
-
return `${elements.length} elements with no explicit layout \u2014 chalk_arrange(op: "layout-layered") will place them`;
|
|
4242
|
-
}
|
|
4243
|
-
|
|
4244
4924
|
// src/app/usecases/write/create-elements.ts
|
|
4245
4925
|
function createElements(deps) {
|
|
4246
4926
|
return async (input) => {
|
|
@@ -4742,6 +5422,24 @@ function rawElements(deps) {
|
|
|
4742
5422
|
};
|
|
4743
5423
|
}
|
|
4744
5424
|
|
|
5425
|
+
// src/domain/scene/captions.ts
|
|
5426
|
+
function fitCaption(container, caption) {
|
|
5427
|
+
if (caption.type !== "text") return caption;
|
|
5428
|
+
const source = caption.originalText === "" ? caption.text : caption.originalText;
|
|
5429
|
+
const inner = Math.max(1, container.width - HOUSE.sizes.labelPadX * 2);
|
|
5430
|
+
const lines = wrapLines(source, caption.fontSize, inner);
|
|
5431
|
+
const width = Math.max(...lines.map((line) => measureLine(line, caption.fontSize)));
|
|
5432
|
+
const height = lines.length * caption.fontSize * caption.lineHeight;
|
|
5433
|
+
return {
|
|
5434
|
+
...caption,
|
|
5435
|
+
text: lines.join("\n"),
|
|
5436
|
+
width,
|
|
5437
|
+
height,
|
|
5438
|
+
x: container.x + (container.width - width) / 2,
|
|
5439
|
+
y: container.y + (container.height - height) / 2
|
|
5440
|
+
};
|
|
5441
|
+
}
|
|
5442
|
+
|
|
4745
5443
|
// src/app/usecases/write/update-elements.ts
|
|
4746
5444
|
var missing = (id) => toRejection(id, unknownElement(id));
|
|
4747
5445
|
var onlyRequested = (landed, requested) => {
|
|
@@ -4811,11 +5509,14 @@ function updateElements(deps) {
|
|
|
4811
5509
|
const before = byId.get(id);
|
|
4812
5510
|
const caption = captionOf(scene.value.elements, id);
|
|
4813
5511
|
if (before === void 0 || caption === void 0) continue;
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
5512
|
+
const resized = next.width !== before.width || next.height !== before.height;
|
|
5513
|
+
knockOn.push(
|
|
5514
|
+
resized ? fitCaption(next, caption) : {
|
|
5515
|
+
...caption,
|
|
5516
|
+
x: caption.x + (next.x - before.x),
|
|
5517
|
+
y: caption.y + (next.y - before.y)
|
|
5518
|
+
}
|
|
5519
|
+
);
|
|
4819
5520
|
}
|
|
4820
5521
|
knockOn.push(...redrawArrowsTouching(scene.value.elements, moved));
|
|
4821
5522
|
const patched = new Set(changed.map((element) => element.id));
|
|
@@ -4944,7 +5645,7 @@ function readEnv(source) {
|
|
|
4944
5645
|
}
|
|
4945
5646
|
|
|
4946
5647
|
// src/composition/container.ts
|
|
4947
|
-
var PACKAGE_VERSION = "1.1.
|
|
5648
|
+
var PACKAGE_VERSION = "1.1.1";
|
|
4948
5649
|
function readRuntime() {
|
|
4949
5650
|
return {
|
|
4950
5651
|
env: process.env,
|
|
@@ -5552,9 +6253,13 @@ function renderScene(description) {
|
|
|
5552
6253
|
}
|
|
5553
6254
|
lines.push("elements:");
|
|
5554
6255
|
for (const element of description.elements) lines.push(row(element, true));
|
|
6256
|
+
lines.push(...adviceLines(description));
|
|
5555
6257
|
lines.push(...overlapLines(description));
|
|
5556
6258
|
return lines.join("\n");
|
|
5557
6259
|
}
|
|
6260
|
+
function adviceLines(description) {
|
|
6261
|
+
return description.layoutAdvice === null ? [] : [`layout: ${description.layoutAdvice}`];
|
|
6262
|
+
}
|
|
5558
6263
|
function overlapLines(description) {
|
|
5559
6264
|
if (description.overlapCount === 0) return [];
|
|
5560
6265
|
const shown = description.overlaps.length;
|
|
@@ -5958,7 +6663,7 @@ var createElementsTool = proxyTool({
|
|
|
5958
6663
|
var connectTool = proxyTool({
|
|
5959
6664
|
name: "chalk_connect",
|
|
5960
6665
|
title: "Connect two elements",
|
|
5961
|
-
description: "Draw an arrow between two elements. This is the ONLY way to make one: it binds both ends, so the arrow follows its boxes when the user drags them \u2014 an arrow made from coordinates comes loose the first time anybody tidies the diagram. Label it with what actually moves ('embeddings', 'POST /ingest', 'nightly'); an unlabelled arrow only says 'related', which the reader already assumed.
|
|
6666
|
+
description: "Draw an arrow between two elements. This is the ONLY way to make one: it binds both ends, so the arrow follows its boxes when the user drags them \u2014 an arrow made from coordinates comes loose the first time anybody tidies the diagram. Label it with what actually moves ('embeddings', 'POST /ingest', 'nightly'); an unlabelled arrow only says 'related', which the reader already assumed. `arrowType` is the one option worth knowing by name: it defaults to `round`, and `elbow` gives right-angled routing that suits an architecture diagram far better \u2014 pick one and keep it consistent across the canvas. Ends and `strokeStyle` carry meaning too (`dashed` for async is the modifier a reader most often wants) \u2014 read chalk_read_me('arrows') before drawing a diagram\u2019s worth of them, or every edge comes out an identical straight solid line.",
|
|
5962
6667
|
input: ConnectToolInput,
|
|
5963
6668
|
output: ConnectOutput,
|
|
5964
6669
|
call: (bridge) => bridge.connect
|
|
@@ -5984,7 +6689,7 @@ var deleteElementsTool = proxyTool({
|
|
|
5984
6689
|
var arrangeTool = proxyTool({
|
|
5985
6690
|
name: "chalk_arrange",
|
|
5986
6691
|
title: "Arrange elements",
|
|
5987
|
-
description: "Lay elements out. `layout-layered`
|
|
6692
|
+
description: "Lay elements out. Three of these read the arrows as well as the boxes and they answer different questions \u2014 picking the wrong one destroys the structure the right one would show. `layout-layered` ranks by arrow direction: reach for it when the diagram is a flow. `layout-radial` puts the most-connected box at the centre with the rest ringed around it. `layout-tree` puts it on the LEFT and fans the rest out rightwards, which reads better than a ring whenever the labels are wide \u2014 a ring has to be big enough to seat its members, and a big ring crosses its own middle. **chalk_describe_scene ends with a `layout:` line naming which of the three this graph wants and why** \u2014 read it rather than guessing. The rest are tidying: align\xD76, distribute-h/v, grid, flip\xD72, and four z-order ops. Arrange only what you added; running a layout over a diagram the user has arranged will move their work.",
|
|
5988
6693
|
input: ArrangeToolInput,
|
|
5989
6694
|
output: ArrangeOutput,
|
|
5990
6695
|
annotations: { idempotentHint: true },
|