chalkbridge 1.0.4 → 1.1.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.
Files changed (153) hide show
  1. package/dist/{chunk-U2ZMSGNS.js → chunk-XDK5UUTO.js} +243 -9
  2. package/dist/cli.js +538 -86
  3. package/dist/index.js +1 -1
  4. package/guides/icons.md +22 -3
  5. package/guides/layout.md +42 -3
  6. package/package.json +1 -1
  7. package/ui-dist/assets/{abnfDiagram-VCTEODGH-C_cqCKun.js → abnfDiagram-VCTEODGH-BCawxiKB.js} +1 -1
  8. package/ui-dist/assets/{ar-SA-G6X2FPQ2-CEKRHtmX.js → ar-SA-G6X2FPQ2-UCm9FftA.js} +1 -1
  9. package/ui-dist/assets/architecture-7GRP2DOG-CiyYoVgW.js +1 -0
  10. package/ui-dist/assets/{architectureDiagram-5GKGNRK7-BuWVonvB.js → architectureDiagram-5GKGNRK7-DIw1pNEk.js} +1 -1
  11. package/ui-dist/assets/{az-AZ-76LH7QW2-mn2Y6BCY.js → az-AZ-76LH7QW2-B5LEIxkk.js} +1 -1
  12. package/ui-dist/assets/{bg-BG-XCXSNQG7-CraLx0fE.js → bg-BG-XCXSNQG7-2bpThpZL.js} +1 -1
  13. package/ui-dist/assets/{blockDiagram-NRAW4CY4-83XozRPe.js → blockDiagram-NRAW4CY4-BGr2WGp_.js} +1 -1
  14. package/ui-dist/assets/{bn-BD-2XOGV67Q-CsTrJsPu.js → bn-BD-2XOGV67Q-Drz-y_cX.js} +1 -1
  15. package/ui-dist/assets/{c4Diagram-UCG6FXSJ-DTdS8B8G.js → c4Diagram-UCG6FXSJ-vbm87bpC.js} +1 -1
  16. package/ui-dist/assets/{ca-ES-6MX7JW3Y-DD8cv_ze.js → ca-ES-6MX7JW3Y-D89p_FZ-.js} +1 -1
  17. package/ui-dist/assets/channel-DtgwkzBZ.js +1 -0
  18. package/ui-dist/assets/{chunk-75Z2AOVW-DQDqkKP0.js → chunk-75Z2AOVW-B7kf5jVz.js} +1 -1
  19. package/ui-dist/assets/{chunk-DU6HZSFF-_YwywVCK.js → chunk-DU6HZSFF-BlGPS6B6.js} +1 -1
  20. package/ui-dist/assets/{chunk-EIO257PC-CLwbKeso.js → chunk-EIO257PC-nzXnwORk.js} +1 -1
  21. package/ui-dist/assets/{chunk-F27PBJKO-BwhryBf6.js → chunk-F27PBJKO-B7qepoj_.js} +1 -1
  22. package/ui-dist/assets/{chunk-G27WJ6UU-BixSJdhz.js → chunk-G27WJ6UU-CSjcvmOf.js} +1 -1
  23. package/ui-dist/assets/{chunk-GMAD6QVW-Bp7aUQup.js → chunk-GMAD6QVW-CtaZAO5u.js} +1 -1
  24. package/ui-dist/assets/{chunk-JQ64N6SF-Dt5bAScf.js → chunk-JQ64N6SF-CuhtdTse.js} +1 -1
  25. package/ui-dist/assets/{chunk-LCL6LL3I-B5MsvPPp.js → chunk-LCL6LL3I-DMfA3a_s.js} +1 -1
  26. package/ui-dist/assets/{chunk-OBVCFTLP-DBqcHzNF.js → chunk-OBVCFTLP-BoBuso2l.js} +1 -1
  27. package/ui-dist/assets/{chunk-P2QGCYS3-TMRACTMO.js → chunk-P2QGCYS3-BZHK87sP.js} +1 -1
  28. package/ui-dist/assets/{chunk-POPQ4Y6H-BsmX_MmQ.js → chunk-POPQ4Y6H-Dk_v51-0.js} +1 -1
  29. package/ui-dist/assets/{chunk-PWAF6VOD-g4MniF8u.js → chunk-PWAF6VOD-DQTvHbxy.js} +1 -1
  30. package/ui-dist/assets/{chunk-R7TYR2AO-S7THFl9x.js → chunk-R7TYR2AO-CQ_StUs1.js} +1 -1
  31. package/ui-dist/assets/{chunk-RHFEMEQ7-CZYull6D.js → chunk-RHFEMEQ7-BTgGEQit.js} +1 -1
  32. package/ui-dist/assets/{chunk-SVP7TREG-DCss0vmb.js → chunk-SVP7TREG-lkVXeHxJ.js} +1 -1
  33. package/ui-dist/assets/{chunk-TEH6E4GO-B-6h5v_5.js → chunk-TEH6E4GO-DIEC-0PO.js} +1 -1
  34. package/ui-dist/assets/classDiagram-DTDB5LWJ-B83TJF6p.js +1 -0
  35. package/ui-dist/assets/classDiagram-v2-JRS7N3AN-B83TJF6p.js +1 -0
  36. package/ui-dist/assets/{cs-CZ-2BRQDIVT-C9fIM1z5.js → cs-CZ-2BRQDIVT-CmSaQCAW.js} +1 -1
  37. package/ui-dist/assets/{cynefin-OW5HDTMX-CtdCt-Da.js → cynefin-OW5HDTMX-B6f8qooM.js} +1 -1
  38. package/ui-dist/assets/{cynefinDiagram-5FMLGOSQ-CF9L7X85.js → cynefinDiagram-5FMLGOSQ-DFu8uB2c.js} +1 -1
  39. package/ui-dist/assets/{da-DK-5WZEPLOC-DXGP9-TU.js → da-DK-5WZEPLOC-DZ68cfJh.js} +1 -1
  40. package/ui-dist/assets/{dagre-3AP2YEHR-Bt0GOj3f.js → dagre-3AP2YEHR-CuUE3cEY.js} +1 -1
  41. package/ui-dist/assets/{de-DE-XR44H4JA-ChR8kF4h.js → de-DE-XR44H4JA-ClBUqbwZ.js} +1 -1
  42. package/ui-dist/assets/{diagram-S7CK7UJ4-D2NIL20U.js → diagram-S7CK7UJ4-DjLndaJK.js} +1 -1
  43. package/ui-dist/assets/{diagram-UQ7AKVKN-Dty3qvRq.js → diagram-UQ7AKVKN-wLhxEBwN.js} +1 -1
  44. package/ui-dist/assets/{diagram-VSXAHHWV-znpNlomZ.js → diagram-VSXAHHWV-CVek4gvW.js} +1 -1
  45. package/ui-dist/assets/{diagram-VX7I27RA-DMKYB58y.js → diagram-VX7I27RA-CXyuaDuW.js} +1 -1
  46. package/ui-dist/assets/{diagram-Z3DM3KII-BLsAgfm1.js → diagram-Z3DM3KII-BGk8dw5c.js} +1 -1
  47. package/ui-dist/assets/{dist-Biz3NhE4.js → dist-DyDCC61R.js} +4 -4
  48. package/ui-dist/assets/{ebnfDiagram-PWID7BFC-sN1WpIzH.js → ebnfDiagram-PWID7BFC-Bq5wdWGX.js} +1 -1
  49. package/ui-dist/assets/{el-GR-BZB4AONW-BMi-5oTn.js → el-GR-BZB4AONW-oCuJWxYE.js} +1 -1
  50. package/ui-dist/assets/{en-B4ZKOASM-DTTcVPyw.js → en-B4ZKOASM-DevQWFhD.js} +1 -1
  51. package/ui-dist/assets/{erDiagram-SSCWMZ5O-qLib13wp.js → erDiagram-SSCWMZ5O-CXZW4I_b.js} +1 -1
  52. package/ui-dist/assets/{es-ES-U4NZUMDT-DSk-whe2.js → es-ES-U4NZUMDT-DgzMLG1J.js} +1 -1
  53. package/ui-dist/assets/{eu-ES-A7QVB2H4-Dlhf1HMz.js → eu-ES-A7QVB2H4-1tA3vosZ.js} +1 -1
  54. package/ui-dist/assets/eventmodeling-NTZA5JFV-Bh0QsTUi.js +1 -0
  55. package/ui-dist/assets/{fa-IR-HGAKTJCU-C4NL1SUQ.js → fa-IR-HGAKTJCU-BVbN6Ifb.js} +1 -1
  56. package/ui-dist/assets/{fi-FI-Z5N7JZ37-BPDGFbjy.js → fi-FI-Z5N7JZ37-DsGMdP7T.js} +1 -1
  57. package/ui-dist/assets/flowDiagram-A5DVABFB-Chb6hSSG.js +1 -0
  58. package/ui-dist/assets/{fr-FR-RHASNOE6-BKM44_qc.js → fr-FR-RHASNOE6-BEZa94DA.js} +1 -1
  59. package/ui-dist/assets/{ganttDiagram-EL5Y4UJY-Cm_9m3AA.js → ganttDiagram-EL5Y4UJY-BZxyNgRP.js} +1 -1
  60. package/ui-dist/assets/{gitGraph-4MIJSDKK-B53Ccobw.js → gitGraph-4MIJSDKK-Cz7F-_6d.js} +1 -1
  61. package/ui-dist/assets/{gitGraphDiagram-WWUBYQGX-CshbIfrd.js → gitGraphDiagram-WWUBYQGX-77gOaAOD.js} +1 -1
  62. package/ui-dist/assets/{gl-ES-HMX3MZ6V-CYq-Wgeo.js → gl-ES-HMX3MZ6V-DfSDz_sy.js} +1 -1
  63. package/ui-dist/assets/{he-IL-6SHJWFNN-CejOCh7c.js → he-IL-6SHJWFNN-DT8nqxK2.js} +1 -1
  64. package/ui-dist/assets/{hi-IN-IWLTKZ5I-DdZM6com.js → hi-IN-IWLTKZ5I-BtL1eizk.js} +1 -1
  65. package/ui-dist/assets/{hu-HU-A5ZG7DT2-DcLFRMuO.js → hu-HU-A5ZG7DT2-5FwjCLbT.js} +1 -1
  66. package/ui-dist/assets/{id-ID-SAP4L64H-1x9JAf0A.js → id-ID-SAP4L64H-DXfUbYET.js} +1 -1
  67. package/ui-dist/assets/image-GAAHSSAO-AgKiDvHK.js +1 -0
  68. package/ui-dist/assets/{index-CzWe1zeF.js → index-Boxo043A.js} +8 -8
  69. package/ui-dist/assets/{info-A6RAGUB7-Cge9ecX_.js → info-A6RAGUB7-DIe8iET8.js} +1 -1
  70. package/ui-dist/assets/{infoDiagram-RXCK75RN-VS1BTI_H.js → infoDiagram-RXCK75RN-Dik-rzpk.js} +1 -1
  71. package/ui-dist/assets/{ishikawaDiagram-5VMMS53U-Cbf1xbxj.js → ishikawaDiagram-5VMMS53U-Ccb-hHW6.js} +1 -1
  72. package/ui-dist/assets/{it-IT-JPQ66NNP-DgmQi0B_.js → it-IT-JPQ66NNP-6_sPHBVO.js} +1 -1
  73. package/ui-dist/assets/{ja-JP-DBVTYXUO-BpmMfXE3.js → ja-JP-DBVTYXUO-DgY_5_c8.js} +1 -1
  74. package/ui-dist/assets/{journeyDiagram-EYS64GPL-C8RWIfHV.js → journeyDiagram-EYS64GPL-CgzxmpyV.js} +1 -1
  75. package/ui-dist/assets/{kaa-6HZHGXH3-CczLR43o.js → kaa-6HZHGXH3-glmGx672.js} +1 -1
  76. package/ui-dist/assets/{kab-KAB-ZGHBKWFO-Cx74DVre.js → kab-KAB-ZGHBKWFO--G7baVL0.js} +1 -1
  77. package/ui-dist/assets/{kanban-definition-3QL26DDD-R8ng9q7k.js → kanban-definition-3QL26DDD-D6TnbrdQ.js} +1 -1
  78. package/ui-dist/assets/{kk-KZ-P5N5QNE5-BPvg9KTT.js → kk-KZ-P5N5QNE5-D3Vn0qgF.js} +1 -1
  79. package/ui-dist/assets/{km-KH-HSX4SM5Z-Blgo7Zit.js → km-KH-HSX4SM5Z-CAHt3o8M.js} +1 -1
  80. package/ui-dist/assets/{ko-KR-MTYHY66A-DZdo-KOi.js → ko-KR-MTYHY66A-N3tCiPap.js} +1 -1
  81. package/ui-dist/assets/{ku-TR-6OUDTVRD-CTpgWpW_.js → ku-TR-6OUDTVRD-CSZFwS--.js} +1 -1
  82. package/ui-dist/assets/{line-Baf15v0x.js → line-BhVZwKwh.js} +1 -1
  83. package/ui-dist/assets/{lt-LT-XHIRWOB4-DKNkbcsj.js → lt-LT-XHIRWOB4-DuSlkfc-.js} +1 -1
  84. package/ui-dist/assets/{lv-LV-5QDEKY6T-DwnZRxkH.js → lv-LV-5QDEKY6T-COuWGNLd.js} +1 -1
  85. package/ui-dist/assets/{mermaid-parser.core-0V2gZHuN.js → mermaid-parser.core-DlqbfUOM.js} +3 -3
  86. package/ui-dist/assets/{mindmap-definition-FBJOCRG2-7hOTFX4B.js → mindmap-definition-FBJOCRG2-D9wLjZXa.js} +1 -1
  87. package/ui-dist/assets/{mr-IN-CRQNXWMA-C-BFvdUk.js → mr-IN-CRQNXWMA-Bv4WHAzM.js} +1 -1
  88. package/ui-dist/assets/{my-MM-5M5IBNSE-BFs64Lrm.js → my-MM-5M5IBNSE-BNltBCCh.js} +1 -1
  89. package/ui-dist/assets/{nb-NO-T6EIAALU-CHGzOQzI.js → nb-NO-T6EIAALU-DgsGabVE.js} +1 -1
  90. package/ui-dist/assets/{nl-NL-IS3SIHDZ-CmrNJ43t.js → nl-NL-IS3SIHDZ-MPMxu0cH.js} +1 -1
  91. package/ui-dist/assets/{nn-NO-6E72VCQL-Cmx8XlD1.js → nn-NO-6E72VCQL-CY-NT6bD.js} +1 -1
  92. package/ui-dist/assets/{oc-FR-POXYY2M6-D6ZK0W24.js → oc-FR-POXYY2M6-BCWtA2dJ.js} +1 -1
  93. package/ui-dist/assets/{pa-IN-N4M65BXN-CoFjVv1P.js → pa-IN-N4M65BXN-CU1D5Lce.js} +1 -1
  94. package/ui-dist/assets/{packet-AYTQ26CC-auec35xT.js → packet-AYTQ26CC-DCMIIosG.js} +1 -1
  95. package/ui-dist/assets/{pegDiagram-XKGWAZYB-NMMkeAIs.js → pegDiagram-XKGWAZYB-CpU2aj-A.js} +1 -1
  96. package/ui-dist/assets/percentages-BXMCSKIN-CMz17NMy.js +1 -0
  97. package/ui-dist/assets/{pie-WAS4IAKB-Brhoju7F.js → pie-WAS4IAKB-BEVkH0em.js} +1 -1
  98. package/ui-dist/assets/{pieDiagram-E7YTZNPT-CbaXV2ep.js → pieDiagram-E7YTZNPT-D-jyX8nJ.js} +1 -1
  99. package/ui-dist/assets/{pl-PL-T2D74RX3-CtZh2-fJ.js → pl-PL-T2D74RX3-CwNWACCb.js} +1 -1
  100. package/ui-dist/assets/{pt-BR-5N22H2LF-Dwnm2Lle.js → pt-BR-5N22H2LF-DCs1W5P0.js} +1 -1
  101. package/ui-dist/assets/{pt-PT-UZXXM6DQ-Cb08TgqC.js → pt-PT-UZXXM6DQ-Bm9Hlh9S.js} +1 -1
  102. package/ui-dist/assets/{quadrantDiagram-AXDQQJYC-CXEPKL_-.js → quadrantDiagram-AXDQQJYC-BqzJD4zL.js} +1 -1
  103. package/ui-dist/assets/{radar-RG4KPBEZ-CTDsgRBl.js → radar-RG4KPBEZ-B6T3SZ4z.js} +1 -1
  104. package/ui-dist/assets/{railroad-74A4TZTK-Bad-j95k.js → railroad-74A4TZTK-BPKCaP4g.js} +1 -1
  105. package/ui-dist/assets/railroad-abnf-HS5TGJTU-RL2kk25t.js +1 -0
  106. package/ui-dist/assets/railroad-ebnf-LZEXJU2U-NY4mALmg.js +1 -0
  107. package/ui-dist/assets/railroad-peg-WCYAUIDC-BNAC9MGy.js +1 -0
  108. package/ui-dist/assets/{railroadDiagram-O6MQD6OU-CJiZ6mmg.js → railroadDiagram-O6MQD6OU-CzTuUKE-.js} +1 -1
  109. package/ui-dist/assets/{requirementDiagram-EFPCY7ZU-T8QMEZu6.js → requirementDiagram-EFPCY7ZU-Bly8_reu.js} +1 -1
  110. package/ui-dist/assets/{ro-RO-JPDTUUEW-D6H165NT.js → ro-RO-JPDTUUEW-DbKkWzmT.js} +1 -1
  111. package/ui-dist/assets/{ru-RU-B4JR7IUQ-CBp0xVXG.js → ru-RU-B4JR7IUQ-C7y8hptt.js} +1 -1
  112. package/ui-dist/assets/{sankeyDiagram-P5KCCOFB-Z9s0vmSt.js → sankeyDiagram-P5KCCOFB-BNe1jsYh.js} +1 -1
  113. package/ui-dist/assets/{sequenceDiagram-WJ2MYXX4-BulR70nw.js → sequenceDiagram-WJ2MYXX4-jpU3VDzA.js} +1 -1
  114. package/ui-dist/assets/{si-LK-N5RQ5JYF-10AxMH9e.js → si-LK-N5RQ5JYF-DsyN9eZC.js} +1 -1
  115. package/ui-dist/assets/{sk-SK-C5VTKIMK-BXvCxb46.js → sk-SK-C5VTKIMK-C3GmutL2.js} +1 -1
  116. package/ui-dist/assets/{sl-SI-NN7IZMDC-BI5_PFXZ.js → sl-SI-NN7IZMDC-C6dxM6aa.js} +1 -1
  117. package/ui-dist/assets/{stateDiagram-HBIQ2CUA-Dam0DwKS.js → stateDiagram-HBIQ2CUA-B2ZRnydN.js} +1 -1
  118. package/ui-dist/assets/stateDiagram-v2-4QOOHH4V-BUscqu-N.js +1 -0
  119. package/ui-dist/assets/subset-shared.chunk-CRXudW0i.js +1 -0
  120. package/ui-dist/assets/{subset-worker.chunk-BaDYCnkg.js → subset-worker.chunk-WItGSQq_.js} +1 -1
  121. package/ui-dist/assets/{sv-SE-XGPEYMSR-dVM3morJ.js → sv-SE-XGPEYMSR-BPU1z_vz.js} +1 -1
  122. package/ui-dist/assets/{swimlanes-XN3QIQJK-txWA6rWV.js → swimlanes-XN3QIQJK-BuYIN15M.js} +1 -1
  123. package/ui-dist/assets/swimlanesDiagram-VK2B7HYN-Cn_HC7uw.js +8 -0
  124. package/ui-dist/assets/{ta-IN-2NMHFXQM-CWJY03pN.js → ta-IN-2NMHFXQM-AJVbdGTO.js} +1 -1
  125. package/ui-dist/assets/{th-TH-HPSO5L25-CR6GhLgj.js → th-TH-HPSO5L25-B1vvYNmv.js} +1 -1
  126. package/ui-dist/assets/{timeline-definition-24CTP7MA-D8m_1UrT.js → timeline-definition-24CTP7MA-Bld3Oj9i.js} +1 -1
  127. package/ui-dist/assets/{tr-TR-DEFEU3FU-BI8tDJSm.js → tr-TR-DEFEU3FU-C_emJDca.js} +1 -1
  128. package/ui-dist/assets/{treeView-Q6P3EWNA-CWJmpg6A.js → treeView-Q6P3EWNA-CQEi50Re.js} +1 -1
  129. package/ui-dist/assets/{treemap-WGGIJYW6-CvClsvN5.js → treemap-WGGIJYW6-D3gRwGbr.js} +1 -1
  130. package/ui-dist/assets/{uk-UA-QMV73CPH-D3Uc38wP.js → uk-UA-QMV73CPH-B69Ddo0p.js} +1 -1
  131. package/ui-dist/assets/{vennDiagram-4TSXK5OY-C5iS8F8o.js → vennDiagram-4TSXK5OY-BWtR81LD.js} +1 -1
  132. package/ui-dist/assets/{vi-VN-M7AON7JQ-B6BZpyS4.js → vi-VN-M7AON7JQ-ByM0OkIF.js} +1 -1
  133. package/ui-dist/assets/{wardley-WFR3VGLG-CdqB7_9-.js → wardley-WFR3VGLG-DpBUmo8s.js} +1 -1
  134. package/ui-dist/assets/{wardleyDiagram-VM6X3IG4-DAHyWwbW.js → wardleyDiagram-VM6X3IG4-n39Dh9vZ.js} +1 -1
  135. package/ui-dist/assets/{xychartDiagram-S5SC5T6Z-ilyFTLQ_.js → xychartDiagram-S5SC5T6Z-D8-lcdGr.js} +1 -1
  136. package/ui-dist/assets/{zh-CN-LNUGB5OW-B0PN355q.js → zh-CN-LNUGB5OW-g_S93sCp.js} +1 -1
  137. package/ui-dist/assets/{zh-HK-E62DVLB3-DUEHDaPB.js → zh-HK-E62DVLB3-B3J_lyrF.js} +1 -1
  138. package/ui-dist/assets/{zh-TW-RAJ6MFWO-Ci4YMClQ.js → zh-TW-RAJ6MFWO-mYLP5aqQ.js} +1 -1
  139. package/ui-dist/index.html +1 -1
  140. package/ui-dist/assets/architecture-7GRP2DOG-fqG4giDT.js +0 -1
  141. package/ui-dist/assets/channel-BOrwYBHk.js +0 -1
  142. package/ui-dist/assets/classDiagram-DTDB5LWJ-Dm2kcSSm.js +0 -1
  143. package/ui-dist/assets/classDiagram-v2-JRS7N3AN-Dm2kcSSm.js +0 -1
  144. package/ui-dist/assets/eventmodeling-NTZA5JFV-BN91GLT5.js +0 -1
  145. package/ui-dist/assets/flowDiagram-A5DVABFB-DBrXJ-Jf.js +0 -1
  146. package/ui-dist/assets/image-GAAHSSAO-BgehxCDd.js +0 -1
  147. package/ui-dist/assets/percentages-BXMCSKIN-BBuUnwM-.js +0 -1
  148. package/ui-dist/assets/railroad-abnf-HS5TGJTU-CbvNSNpm.js +0 -1
  149. package/ui-dist/assets/railroad-ebnf-LZEXJU2U-DdltjnE0.js +0 -1
  150. package/ui-dist/assets/railroad-peg-WCYAUIDC-CgbxToYh.js +0 -1
  151. package/ui-dist/assets/stateDiagram-v2-4QOOHH4V-CSjCWsxW.js +0 -1
  152. package/ui-dist/assets/subset-shared.chunk-Du7WrYQy.js +0 -1
  153. package/ui-dist/assets/swimlanesDiagram-VK2B7HYN-CU4h0g7a.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, DataUrl, Index, 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-U2ZMSGNS.js';
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, measureLine, wrapLines, KIND_ABBREVIATION, catalogUnavailable, FrameId, IconItem, ExcalidrawFile } from './chunk-XDK5UUTO.js';
3
3
  import './chunk-7D4SUZUM.js';
4
4
  import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
5
5
  import { homedir } from 'node:os';
@@ -2025,6 +2025,109 @@ function createExportBroker(options, deps) {
2025
2025
  };
2026
2026
  }
2027
2027
 
2028
+ // src/domain/scene/overlaps.ts
2029
+ var MIN_OVERLAP_PX = 2;
2030
+ var MAX_REPORTED_OVERLAPS = 12;
2031
+ function routeMidpoint(arrow) {
2032
+ const points = arrow.points;
2033
+ const lengths = points.slice(1).map((p, i) => {
2034
+ const prev = points[i];
2035
+ const [px2, py] = p;
2036
+ if (prev === void 0 || px2 === void 0 || py === void 0) return 0;
2037
+ const [qx, qy] = prev;
2038
+ if (qx === void 0 || qy === void 0) return 0;
2039
+ return Math.hypot(px2 - qx, py - qy);
2040
+ });
2041
+ let want = lengths.reduce((sum, l) => sum + l, 0) / 2;
2042
+ for (let i = 0; i < lengths.length; i++) {
2043
+ const from = points[i];
2044
+ const to = points[i + 1];
2045
+ const length = lengths[i];
2046
+ if (from === void 0 || to === void 0 || length === void 0) continue;
2047
+ const [fx, fy] = from;
2048
+ const [tx, ty] = to;
2049
+ if (fx === void 0 || fy === void 0 || tx === void 0 || ty === void 0) continue;
2050
+ if (want > length && i < lengths.length - 1) {
2051
+ want -= length;
2052
+ continue;
2053
+ }
2054
+ const k = length === 0 ? 0 : want / length;
2055
+ return { x: arrow.x + fx + (tx - fx) * k, y: arrow.y + fy + (ty - fy) * k };
2056
+ }
2057
+ return { x: arrow.x, y: arrow.y };
2058
+ }
2059
+ function renderedRect(element, byId) {
2060
+ if (element.type !== "text" || element.containerId === null) return element;
2061
+ const container = byId.get(element.containerId);
2062
+ if (container === void 0 || container.type !== "arrow") return element;
2063
+ const mid = routeMidpoint(container);
2064
+ return {
2065
+ x: mid.x - element.width / 2,
2066
+ y: mid.y - element.height / 2,
2067
+ width: element.width,
2068
+ height: element.height
2069
+ };
2070
+ }
2071
+ var intersection = (a, b) => ({
2072
+ width: Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x),
2073
+ height: Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y)
2074
+ });
2075
+ function rectsOverlap(a, b) {
2076
+ const { width, height } = intersection(a, b);
2077
+ return width >= MIN_OVERLAP_PX && height >= MIN_OVERLAP_PX;
2078
+ }
2079
+ function frameOf(element, byId) {
2080
+ if (element.frameId !== null) return element.frameId;
2081
+ if (element.type !== "text" || element.containerId === null) return null;
2082
+ return byId.get(element.containerId)?.frameId ?? null;
2083
+ }
2084
+ function nested(a, b, byId) {
2085
+ if (a.type === "frame" && frameOf(b, byId) === frameIdOf(a.id)) return true;
2086
+ if (b.type === "frame" && frameOf(a, byId) === frameIdOf(b.id)) return true;
2087
+ if (a.type === "text" && a.containerId === b.id && b.type !== "arrow") return true;
2088
+ if (b.type === "text" && b.containerId === a.id && a.type !== "arrow") return true;
2089
+ return false;
2090
+ }
2091
+ var participates = (element) => !element.isDeleted && element.type !== "arrow" && element.type !== "line" && // Frames are regions, not shapes. One is 600 px across, so nearly everything near it
2092
+ // intersects its area — and unlike a box it does not obscure what it covers, because a
2093
+ // frame only clips its *own* children. Reporting non-children put a warning on every
2094
+ // framed diagram, which is the noise this file exists to avoid. The case that genuinely
2095
+ // hides content — a child outside the frame holding it — is prevented rather than
2096
+ // reported: `frames.refitFrames` grows the frame on every path that can move a child.
2097
+ element.type !== "frame" && element.width > 0 && element.height > 0;
2098
+ function wouldCollide(candidate, elements, ignore = /* @__PURE__ */ new Set()) {
2099
+ const byId = new Map(elements.map((element) => [element.id, element]));
2100
+ return elements.filter(participates).filter((element) => !ignore.has(element.id)).some((element) => rectsOverlap(candidate, renderedRect(element, byId)));
2101
+ }
2102
+ function findOverlaps(elements) {
2103
+ const byId = new Map(elements.map((element) => [element.id, element]));
2104
+ const live = elements.filter(participates);
2105
+ const found = [];
2106
+ for (let i = 0; i < live.length; i++) {
2107
+ const a = live[i];
2108
+ if (a === void 0) continue;
2109
+ for (let j = i + 1; j < live.length; j++) {
2110
+ const b = live[j];
2111
+ if (b === void 0) continue;
2112
+ if (nested(a, b, byId)) continue;
2113
+ const { width, height } = intersection(renderedRect(a, byId), renderedRect(b, byId));
2114
+ if (width < MIN_OVERLAP_PX || height < MIN_OVERLAP_PX) continue;
2115
+ found.push({ a: a.id, b: b.id, width: Math.round(width), height: Math.round(height) });
2116
+ }
2117
+ }
2118
+ return found.sort(
2119
+ (p, q) => q.width * q.height - p.width * p.height || (p.a < q.a ? -1 : p.a > q.a ? 1 : 0) || (p.b < q.b ? -1 : p.b > q.b ? 1 : 0)
2120
+ );
2121
+ }
2122
+ function overlapWarning(elements) {
2123
+ const found = findOverlaps(elements);
2124
+ const worst = found[0];
2125
+ if (worst === void 0) return null;
2126
+ const size = `${Math.round(worst.width)}x${Math.round(worst.height)}`;
2127
+ const count2 = found.length === 1 ? "1 overlap" : `${found.length} overlaps`;
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
+ }
2130
+
2028
2131
  // src/domain/scene/observe.ts
2029
2132
  var SHORT_ID_LENGTH = 4;
2030
2133
  var shortId2 = (id) => id.slice(0, SHORT_ID_LENGTH);
@@ -2131,6 +2234,7 @@ function observeScene(input) {
2131
2234
  const selected = new Set(input.selectedIds ?? []);
2132
2235
  const context = { labels, frames, selected, detail: input.detail };
2133
2236
  const rows = rowElements(scene.elements).filter((element) => element.type !== "frame");
2237
+ const overlaps = findOverlaps(scene.elements);
2134
2238
  return {
2135
2239
  sceneId: scene.id,
2136
2240
  name: scene.name,
@@ -2139,6 +2243,8 @@ function observeScene(input) {
2139
2243
  updatedAgoMs: Math.max(0, input.now - scene.updatedAt),
2140
2244
  frames: [...frames.values()],
2141
2245
  elements: rows.map((element) => describeElement(element, context)),
2246
+ overlaps: overlaps.slice(0, MAX_REPORTED_OVERLAPS),
2247
+ overlapCount: overlaps.length,
2142
2248
  detail: input.detail
2143
2249
  };
2144
2250
  }
@@ -3171,6 +3277,10 @@ function restoreSnapshot(deps) {
3171
3277
  sceneId: scene.id,
3172
3278
  sceneVersion: updated.sceneVersion,
3173
3279
  broadcastTo,
3280
+ // A restore puts back exactly what was saved, so any collision in it is the one the
3281
+ // snapshot was taken with. Worth saying: the reader is often restoring *because* a
3282
+ // layout went wrong, and silence here would read as "the old one was clean".
3283
+ overlapWarning: overlapWarning(snapshot.elements),
3174
3284
  snapshotId: snapshot.summary.id,
3175
3285
  label: snapshot.summary.label,
3176
3286
  restored: snapshot.elements.filter((element) => !element.isDeleted).length
@@ -3195,6 +3305,19 @@ function serviceSize() {
3195
3305
  const [width, height] = HOUSE.sizes.service;
3196
3306
  return { width, height };
3197
3307
  }
3308
+ var LINE_HEIGHT = 1.25;
3309
+ var quantise = (value) => Math.ceil(value / HOUSE.grid) * HOUSE.grid;
3310
+ function labelledSize(label, fontSize = HOUSE.fontSize) {
3311
+ const floor = serviceSize();
3312
+ if (label === void 0 || label === "") return floor;
3313
+ const padX = HOUSE.sizes.labelPadX * 2;
3314
+ const padY = HOUSE.sizes.labelPadY * 2;
3315
+ const oneLine = measureLine(label, fontSize) + padX;
3316
+ const width = Math.max(floor.width, quantise(Math.min(oneLine, HOUSE.sizes.maxBox)));
3317
+ const lines = wrapLines(label, fontSize, width - padX);
3318
+ const height = Math.max(floor.height, quantise(lines.length * fontSize * LINE_HEIGHT + padY));
3319
+ return { width, height };
3320
+ }
3198
3321
 
3199
3322
  // src/app/usecases/write/annotate-elements.ts
3200
3323
  var NOTE_GAP = 24;
@@ -3273,6 +3396,120 @@ function focusElements(deps) {
3273
3396
  return ok({ sceneId: scene.value.id, focused, broadcastTo });
3274
3397
  };
3275
3398
  }
3399
+
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
+ // src/domain/scene/frames.ts
3447
+ function boundsAround(children) {
3448
+ const pad2 = HOUSE.sizes.framePad;
3449
+ const left = Math.min(...children.map((c) => c.x));
3450
+ const top = Math.min(...children.map((c) => c.y));
3451
+ const right = Math.max(...children.map((c) => c.x + c.width));
3452
+ const bottom = Math.max(...children.map((c) => c.y + c.height));
3453
+ return {
3454
+ x: left - pad2,
3455
+ y: top - pad2,
3456
+ width: right - left + pad2 * 2,
3457
+ // Room at the top for the frame's own name, which renders above its border.
3458
+ height: bottom - top + pad2 * 2
3459
+ };
3460
+ }
3461
+ function childrenOf(scene, frame) {
3462
+ const id = frameIdOf(frame.id);
3463
+ return scene.filter(
3464
+ (element) => !element.isDeleted && element.id !== frame.id && element.frameId === id
3465
+ );
3466
+ }
3467
+ function releaseCrossingArrows(scene, frame) {
3468
+ const id = frameIdOf(frame.id);
3469
+ const inside = new Set(childrenOf(scene, frame).map((element) => element.id));
3470
+ const byId = new Map(scene.map((element) => [element.id, element]));
3471
+ return childrenOf(scene, frame).filter((element) => element.type === "arrow" || element.type === "line").filter((arrow) => {
3472
+ if (arrow.type !== "arrow") return true;
3473
+ const from = arrow.startBinding?.elementId;
3474
+ const to = arrow.endBinding?.elementId;
3475
+ if (from === void 0 || to === void 0) return true;
3476
+ return !(inside.has(from) && inside.has(to));
3477
+ }).filter((arrow) => byId.get(arrow.id)?.frameId === id).map((arrow) => ({ ...arrow, frameId: null }));
3478
+ }
3479
+ function withFrameChildren(scene, deltas) {
3480
+ const frames = scene.filter(
3481
+ (element) => element.type === "frame" && !element.isDeleted && deltas.has(element.id)
3482
+ );
3483
+ if (frames.length === 0) return deltas;
3484
+ const widened = new Map(deltas);
3485
+ for (const frame of frames) {
3486
+ const delta = deltas.get(frame.id);
3487
+ if (delta === void 0) continue;
3488
+ for (const child of childrenOf(scene, frame)) {
3489
+ if (widened.has(child.id)) continue;
3490
+ widened.set(child.id, delta);
3491
+ }
3492
+ }
3493
+ return widened;
3494
+ }
3495
+ function refitFrames(scene, moved) {
3496
+ if (moved.size === 0) return [];
3497
+ const current = scene.map((element) => moved.get(element.id) ?? element);
3498
+ const refitted = [];
3499
+ for (const element of current) {
3500
+ if (element.type !== "frame" || element.isDeleted) continue;
3501
+ const crossing = new Set(releaseCrossingArrows(current, element).map((a) => a.id));
3502
+ const children = childrenOf(current, element).filter((c) => !crossing.has(c.id));
3503
+ if (children.length === 0) continue;
3504
+ if (!children.some((child) => moved.has(child.id))) continue;
3505
+ const box = boundsAround(children);
3506
+ if (element.x === box.x && element.y === box.y && element.width === box.width && element.height === box.height) {
3507
+ continue;
3508
+ }
3509
+ refitted.push({ ...element, ...box });
3510
+ }
3511
+ return refitted;
3512
+ }
3276
3513
  function highestIndex(elements) {
3277
3514
  let highest = null;
3278
3515
  for (const element of elements) {
@@ -3337,7 +3574,7 @@ function edgePoint(from, to, gap) {
3337
3574
  const t = scale + push;
3338
3575
  return [cx + dx * t, cy + dy * t];
3339
3576
  }
3340
- function redrawOne(current, labels, arrow) {
3577
+ function redrawOne(current, labels, arrow, lane) {
3341
3578
  if (arrow.type !== "arrow") return [];
3342
3579
  const fromId = startOf(arrow);
3343
3580
  const toId = endOf(arrow);
@@ -3347,40 +3584,126 @@ function redrawOne(current, labels, arrow) {
3347
3584
  if (from === void 0 || to === void 0) return [];
3348
3585
  const startGap = arrow.startBinding?.gap ?? 4;
3349
3586
  const endGap = arrow.endBinding?.gap ?? 4;
3350
- const [sx, sy] = edgePoint(boxOf(from), boxOf(to), startGap);
3351
- const [ex, ey] = edgePoint(boxOf(to), boxOf(from), endGap);
3352
- const points = [point(0, 0), point(ex - sx, ey - sy)];
3587
+ const elbowed = arrow.elbowed === true;
3588
+ const [sx, sy, ex, ey] = elbowed ? facePoints(boxOf(from), boxOf(to), startGap, endGap, lane) : [
3589
+ ...edgePoint(boxOf(from), boxOf(to), startGap),
3590
+ ...edgePoint(boxOf(to), boxOf(from), endGap)
3591
+ ];
3592
+ const points = elbowed ? elbowRoute(ex - sx, ey - sy) : [point(0, 0), point(ex - sx, ey - sy)];
3593
+ const spans = points.reduce(
3594
+ (box, [px2, py]) => ({
3595
+ w: Math.max(box.w, Math.abs(px2)),
3596
+ h: Math.max(box.h, Math.abs(py))
3597
+ }),
3598
+ { w: 0, h: 0 }
3599
+ );
3353
3600
  const redrawn = {
3354
3601
  ...arrow,
3355
3602
  x: sx,
3356
3603
  y: sy,
3357
- width: Math.abs(ex - sx),
3358
- height: Math.abs(ey - sy),
3604
+ width: spans.w,
3605
+ height: spans.h,
3359
3606
  points,
3360
3607
  lastCommittedPoint: null
3361
3608
  };
3362
3609
  const label = labels.get(arrow.id);
3363
3610
  if (label === void 0) return [redrawn];
3364
- return [
3365
- redrawn,
3366
- {
3367
- ...label,
3368
- x: (sx + ex) / 2 - label.width / 2,
3369
- y: (sy + ey) / 2 - label.height / 2
3370
- }
3371
- ];
3611
+ const mid = routeMidpoint({ x: sx, y: sy, points });
3612
+ const at = { x: mid.x - label.width / 2, y: mid.y - label.height / 2 };
3613
+ return [redrawn, { ...label, ...at }];
3614
+ }
3615
+ function faceBetween(from, to) {
3616
+ const [fx, fy] = centre(from);
3617
+ const [tx, ty] = centre(to);
3618
+ const dx = tx - fx;
3619
+ const dy = ty - fy;
3620
+ if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? "right" : "left";
3621
+ return dy >= 0 ? "bottom" : "top";
3622
+ }
3623
+ var LANE_SPREAD = 44;
3624
+ function laneOffsets(arrows, current) {
3625
+ const groups = /* @__PURE__ */ new Map();
3626
+ const add = (nodeId, face, node, slot) => {
3627
+ const key = `${nodeId}|${face}`;
3628
+ const bucket = groups.get(key);
3629
+ if (bucket === void 0) groups.set(key, { face, node, slots: [slot] });
3630
+ else bucket.slots.push(slot);
3631
+ };
3632
+ const across = (face, box) => face === "right" || face === "left" ? centre(box)[1] : centre(box)[0];
3633
+ for (const arrow of arrows) {
3634
+ if (arrow.type !== "arrow" || arrow.elbowed !== true) continue;
3635
+ const fromId = startOf(arrow);
3636
+ const toId = endOf(arrow);
3637
+ if (fromId === null || toId === null) continue;
3638
+ const from = current.get(fromId);
3639
+ const to = current.get(toId);
3640
+ if (from === void 0 || to === void 0) continue;
3641
+ const outFace = faceBetween(boxOf(from), boxOf(to));
3642
+ const inFace = faceBetween(boxOf(to), boxOf(from));
3643
+ add(fromId, outFace, boxOf(from), {
3644
+ arrow: arrow.id,
3645
+ end: "start",
3646
+ along: across(outFace, boxOf(to))
3647
+ });
3648
+ add(toId, inFace, boxOf(to), {
3649
+ arrow: arrow.id,
3650
+ end: "end",
3651
+ along: across(inFace, boxOf(from))
3652
+ });
3653
+ }
3654
+ const offsets = /* @__PURE__ */ new Map();
3655
+ for (const { face, node, slots } of groups.values()) {
3656
+ if (slots.length < 2) continue;
3657
+ slots.sort((a, b) => a.along - b.along || (a.arrow < b.arrow ? -1 : 1));
3658
+ const span = face === "right" || face === "left" ? node.height : node.width;
3659
+ const spread = Math.min(LANE_SPREAD, span / (slots.length + 1));
3660
+ slots.forEach((slot, i) => {
3661
+ const shift = (i - (slots.length - 1) / 2) * spread;
3662
+ const so_far = offsets.get(slot.arrow) ?? { start: 0, end: 0 };
3663
+ offsets.set(slot.arrow, { ...so_far, [slot.end]: shift });
3664
+ });
3665
+ }
3666
+ return offsets;
3667
+ }
3668
+ function facePoints(from, to, startGap, endGap, lane) {
3669
+ const [fx, fy] = centre(from);
3670
+ const [tx, ty] = centre(to);
3671
+ const dx = tx - fx;
3672
+ const dy = ty - fy;
3673
+ if (Math.abs(dx) >= Math.abs(dy)) {
3674
+ const sx = dx >= 0 ? from.x + from.width + startGap : from.x - startGap;
3675
+ const ex = dx >= 0 ? to.x - endGap : to.x + to.width + endGap;
3676
+ return [sx, fy + lane.start, ex, ty + lane.end];
3677
+ }
3678
+ const sy = dy >= 0 ? from.y + from.height + startGap : from.y - startGap;
3679
+ const ey = dy >= 0 ? to.y - endGap : to.y + to.height + endGap;
3680
+ return [fx + lane.start, sy, tx + lane.end, ey];
3681
+ }
3682
+ function elbowRoute(dx, dy) {
3683
+ const ALIGNED = 1;
3684
+ if (Math.abs(dy) <= ALIGNED || Math.abs(dx) <= ALIGNED) {
3685
+ return [point(0, 0), point(dx, dy)];
3686
+ }
3687
+ if (Math.abs(dx) >= Math.abs(dy)) {
3688
+ const mid2 = dx / 2;
3689
+ return [point(0, 0), point(mid2, 0), point(mid2, dy), point(dx, dy)];
3690
+ }
3691
+ const mid = dy / 2;
3692
+ return [point(0, 0), point(0, mid), point(dx, mid), point(dx, dy)];
3372
3693
  }
3373
3694
  function redrawArrowsTouching(scene, moved) {
3374
3695
  const current = new Map(scene.map((element) => [element.id, moved.get(element.id) ?? element]));
3375
3696
  const labels = labelsByContainer([...current.values()]);
3376
3697
  const redrawn = [];
3698
+ const lanes = laneOffsets([...current.values()], current);
3699
+ const NO_LANE = { start: 0, end: 0 };
3377
3700
  for (const element of current.values()) {
3378
3701
  if (element.type !== "arrow") continue;
3379
3702
  const fromId = startOf(element);
3380
3703
  const toId = endOf(element);
3381
3704
  if (fromId === null || toId === null) continue;
3382
3705
  if (!moved.has(fromId) && !moved.has(toId)) continue;
3383
- redrawn.push(...redrawOne(current, labels, element));
3706
+ redrawn.push(...redrawOne(current, labels, element, lanes.get(element.id) ?? NO_LANE));
3384
3707
  }
3385
3708
  return redrawn;
3386
3709
  }
@@ -3388,13 +3711,16 @@ function redrawArrows(scene, ids) {
3388
3711
  const current = new Map(scene.map((element) => [element.id, element]));
3389
3712
  const labels = labelsByContainer(scene);
3390
3713
  const wanted = new Set(ids);
3391
- return [...current.values()].filter((element) => wanted.has(element.id)).flatMap((element) => redrawOne(current, labels, element));
3714
+ const lanes = laneOffsets([...current.values()], current);
3715
+ const NO_LANE = { start: 0, end: 0 };
3716
+ return [...current.values()].filter((element) => wanted.has(element.id)).flatMap((element) => redrawOne(current, labels, element, lanes.get(element.id) ?? NO_LANE));
3392
3717
  }
3393
3718
  function translate(scene, deltas) {
3719
+ const widened = withAnchoredElements(scene, withFrameChildren(scene, deltas));
3394
3720
  const labels = labelsByContainer(scene);
3395
3721
  const moved = /* @__PURE__ */ new Map();
3396
3722
  for (const element of scene) {
3397
- const delta = deltas.get(element.id);
3723
+ const delta = widened.get(element.id);
3398
3724
  if (delta === void 0) continue;
3399
3725
  const [dx, dy] = delta;
3400
3726
  if (dx === 0 && dy === 0) continue;
@@ -3404,7 +3730,10 @@ function translate(scene, deltas) {
3404
3730
  moved.set(label.id, { ...label, x: label.x + dx, y: label.y + dy });
3405
3731
  }
3406
3732
  }
3407
- return [...moved.values(), ...redrawArrowsTouching(scene, moved)];
3733
+ const changed = new Map(moved);
3734
+ for (const element of redrawArrowsTouching(scene, moved)) changed.set(element.id, element);
3735
+ for (const element of refitFrames(scene, moved)) changed.set(element.id, element);
3736
+ return [...changed.values()];
3408
3737
  }
3409
3738
  var alignments = {
3410
3739
  "align-left": (box, bounds) => [bounds.x - box.x, 0],
@@ -3545,18 +3874,11 @@ function layoutLayered(scene, targets, spacing) {
3545
3874
  if (!inSet.has(from) || !inSet.has(to) || from === to) continue;
3546
3875
  edges.push({ from, to });
3547
3876
  }
3548
- const nodes = targets.filter((element) => !isLinear(element));
3877
+ const nodes = targets.filter((element) => !isLinear(element) && !isAnchored(element));
3549
3878
  if (nodes.length === 0) return [];
3550
3879
  const incoming = /* @__PURE__ */ new Map();
3551
- const outgoing = /* @__PURE__ */ new Map();
3552
- for (const node of nodes) {
3553
- incoming.set(node.id, []);
3554
- outgoing.set(node.id, []);
3555
- }
3556
- for (const edge of edges) {
3557
- incoming.get(edge.to)?.push(edge.from);
3558
- outgoing.get(edge.from)?.push(edge.to);
3559
- }
3880
+ for (const node of nodes) incoming.set(node.id, []);
3881
+ for (const edge of edges) incoming.get(edge.to)?.push(edge.from);
3560
3882
  const rank = new Map(nodes.map((node) => [node.id, 0]));
3561
3883
  for (let pass = 0; pass < nodes.length; pass++) {
3562
3884
  let settled = true;
@@ -3570,57 +3892,101 @@ function layoutLayered(scene, targets, spacing) {
3570
3892
  }
3571
3893
  if (settled) break;
3572
3894
  }
3573
- const ranks = /* @__PURE__ */ new Map();
3895
+ const LANE_WIDTH = 0;
3896
+ const LANE_HEIGHT = 48;
3897
+ const slots = /* @__PURE__ */ new Map();
3898
+ const push = (r, slot) => {
3899
+ const bucket = slots.get(r);
3900
+ if (bucket === void 0) slots.set(r, [slot]);
3901
+ else bucket.push(slot);
3902
+ };
3574
3903
  for (const node of nodes) {
3575
- const r = rank.get(node.id) ?? 0;
3576
- const bucket = ranks.get(r);
3577
- if (bucket === void 0) ranks.set(r, [node]);
3578
- else bucket.push(node);
3904
+ push(rank.get(node.id) ?? 0, {
3905
+ key: node.id,
3906
+ box: boxOf(node),
3907
+ parents: incoming.get(node.id) ?? [],
3908
+ node
3909
+ });
3910
+ }
3911
+ for (const edge of edges) {
3912
+ const from = rank.get(edge.from);
3913
+ const to = rank.get(edge.to);
3914
+ if (from === void 0 || to === void 0) continue;
3915
+ let previous = edge.from;
3916
+ for (let r = Math.min(from, to) + 1; r < Math.max(from, to); r++) {
3917
+ const key = `lane:${edge.from}>${edge.to}@${r}`;
3918
+ push(r, {
3919
+ key,
3920
+ box: { x: 0, y: 0, width: LANE_WIDTH, height: LANE_HEIGHT },
3921
+ parents: [previous]
3922
+ });
3923
+ previous = key;
3924
+ }
3579
3925
  }
3580
3926
  const position = /* @__PURE__ */ new Map();
3581
- const rankNumbers = [...ranks.keys()].sort((a, b) => a - b);
3927
+ const rankNumbers = [...slots.keys()].sort((a, b) => a - b);
3582
3928
  for (const r of rankNumbers) {
3583
- const bucket = ranks.get(r) ?? [];
3584
- const scored = bucket.map((node) => {
3585
- const parents = (incoming.get(node.id) ?? []).map((id) => position.get(id)).filter((p) => p !== void 0);
3929
+ const bucket = slots.get(r) ?? [];
3930
+ const scored = bucket.map((slot) => {
3931
+ const parents = slot.parents.map((id) => position.get(id)).filter((p) => p !== void 0);
3586
3932
  const barycentre = parents.length === 0 ? Number.POSITIVE_INFINITY : parents.reduce((sum, p) => sum + p, 0) / parents.length;
3587
- return { node, barycentre };
3933
+ return { slot, barycentre };
3588
3934
  });
3589
3935
  scored.sort(
3590
- (a, b) => a.barycentre - b.barycentre || (a.node.id < b.node.id ? -1 : a.node.id > b.node.id ? 1 : 0)
3936
+ (a, b) => a.barycentre - b.barycentre || (a.slot.key < b.slot.key ? -1 : a.slot.key > b.slot.key ? 1 : 0)
3591
3937
  );
3592
- scored.forEach(({ node }, i) => {
3593
- position.set(node.id, i);
3938
+ scored.forEach(({ slot }, i) => {
3939
+ position.set(slot.key, i);
3594
3940
  });
3595
- ranks.set(
3941
+ slots.set(
3596
3942
  r,
3597
- scored.map((entry) => entry.node)
3943
+ scored.map((entry) => entry.slot)
3598
3944
  );
3599
3945
  }
3600
- const gapH = spacing ?? HOUSE.spacing.h;
3601
3946
  const gapV = HOUSE.spacing.v;
3947
+ const labelWidths = labelsByContainer(scene);
3948
+ const captionRoom = (column) => {
3949
+ const floor = spacing ?? HOUSE.spacing.h;
3950
+ let widest = 0;
3951
+ for (const edge of edges) {
3952
+ const from = rank.get(edge.from);
3953
+ const to = rank.get(edge.to);
3954
+ if (from === void 0 || to === void 0) continue;
3955
+ const low = Math.min(from, to);
3956
+ const high = Math.max(from, to);
3957
+ if (column < low || column >= high) continue;
3958
+ const arrow = scene.find(
3959
+ (element) => element.type === "arrow" && startOf(element) === edge.from && endOf(element) === edge.to
3960
+ );
3961
+ const caption = arrow === void 0 ? void 0 : labelWidths.get(arrow.id);
3962
+ if (caption !== void 0) widest = Math.max(widest, caption.width);
3963
+ }
3964
+ return Math.max(floor, widest + HOUSE.sizes.labelPadX * 2);
3965
+ };
3602
3966
  const anchor = boundsOfAll(nodes.map(boxOf));
3603
3967
  const columnWidths = rankNumbers.map(
3604
- (r) => Math.max(...(ranks.get(r) ?? []).map((node) => node.width))
3968
+ (r) => Math.max(...(slots.get(r) ?? []).map((slot) => slot.box.width))
3605
3969
  );
3606
3970
  const tallest = Math.max(
3607
3971
  ...rankNumbers.map(
3608
- (r) => (ranks.get(r) ?? []).reduce((sum, node) => sum + node.height + gapV, -gapV)
3972
+ (r) => (slots.get(r) ?? []).reduce((sum, slot) => sum + slot.box.height + gapV, -gapV)
3609
3973
  )
3610
3974
  );
3611
3975
  const deltas = /* @__PURE__ */ new Map();
3612
3976
  let cursorX = anchor.x;
3613
3977
  rankNumbers.forEach((r, column) => {
3614
- const bucket = ranks.get(r) ?? [];
3615
- const columnHeight = bucket.reduce((sum, node) => sum + node.height + gapV, -gapV);
3978
+ const bucket = slots.get(r) ?? [];
3979
+ const columnHeight = bucket.reduce((sum, slot) => sum + slot.box.height + gapV, -gapV);
3616
3980
  let cursorY = anchor.y + (tallest - columnHeight) / 2;
3617
3981
  const width = columnWidths[column] ?? 0;
3618
- for (const node of bucket) {
3619
- const x = cursorX + (width - node.width) / 2;
3620
- deltas.set(node.id, [x - node.x, cursorY - node.y]);
3621
- cursorY += node.height + gapV;
3982
+ for (const slot of bucket) {
3983
+ if (slot.node !== void 0) {
3984
+ const x = cursorX + (width - slot.box.width) / 2;
3985
+ deltas.set(slot.node.id, [x - slot.box.x, cursorY - slot.box.y]);
3986
+ }
3987
+ cursorY += slot.box.height + gapV;
3622
3988
  }
3623
- cursorX += width + gapH;
3989
+ cursorX += width + captionRoom(r);
3624
3990
  });
3625
3991
  return translate(scene, deltas);
3626
3992
  }
@@ -3644,6 +4010,7 @@ function arrangeElements(deps) {
3644
4010
  sceneId: applied.value.sceneId,
3645
4011
  sceneVersion: applied.value.sceneVersion,
3646
4012
  broadcastTo: applied.value.broadcastTo,
4013
+ overlapWarning: applied.value.overlapWarning,
3647
4014
  moved: [...applied.value.applied]
3648
4015
  });
3649
4016
  };
@@ -3809,7 +4176,7 @@ function explicit(input) {
3809
4176
  }
3810
4177
  return kept;
3811
4178
  }
3812
- var defaultSize = () => serviceSize();
4179
+ var defaultSize = (element) => labelledSize(element.label, element.fontSize ?? HOUSE.fontSize);
3813
4180
  function normalizeElements(input) {
3814
4181
  return input.elements.map((element, i) => {
3815
4182
  const fallback = input.ids[i];
@@ -3858,7 +4225,7 @@ function normalizeElements(input) {
3858
4225
  id,
3859
4226
  x: element.x,
3860
4227
  y: element.y,
3861
- ...defaultSize(),
4228
+ ...defaultSize(element),
3862
4229
  ...role,
3863
4230
  ...set,
3864
4231
  ...element.label === void 0 ? {} : { label: houseLabel(element.label) }
@@ -3901,20 +4268,6 @@ function createElements(deps) {
3901
4268
  }
3902
4269
 
3903
4270
  // src/app/usecases/write/frame-elements.ts
3904
- function boundsAround(children) {
3905
- const pad2 = HOUSE.sizes.framePad;
3906
- const left = Math.min(...children.map((c) => c.x));
3907
- const top = Math.min(...children.map((c) => c.y));
3908
- const right = Math.max(...children.map((c) => c.x + c.width));
3909
- const bottom = Math.max(...children.map((c) => c.y + c.height));
3910
- return {
3911
- x: left - pad2,
3912
- y: top - pad2,
3913
- width: right - left + pad2 * 2,
3914
- // Room at the top for the frame's own name, which renders above its border.
3915
- height: bottom - top + pad2 * 2
3916
- };
3917
- }
3918
4271
  function frameElements(deps) {
3919
4272
  return async (input) => {
3920
4273
  const scene = deps.write.resolve(input.sceneId);
@@ -3943,6 +4296,18 @@ function frameElements(deps) {
3943
4296
  if (!created.ok) return created;
3944
4297
  const committed = deps.write.append(scene.value, created.value);
3945
4298
  if (!committed.ok) return committed;
4299
+ const after = deps.write.resolve(input.sceneId);
4300
+ if (after.ok) {
4301
+ const built = after.value.elements.find((element) => element.id === elementId);
4302
+ if (built !== void 0) {
4303
+ const released = releaseCrossingArrows(after.value.elements, built);
4304
+ if (released.length > 0) {
4305
+ const freed = deps.write.apply(after.value, released);
4306
+ if (!freed.ok) return freed;
4307
+ return ok({ ...freed.value, frameId, children: named2.map((e) => e.id) });
4308
+ }
4309
+ }
4310
+ }
3946
4311
  return ok({ ...committed.value, frameId, children: named2.map((e) => e.id) });
3947
4312
  }
3948
4313
  if (input.frameId === void 0) {
@@ -3960,12 +4325,14 @@ function frameElements(deps) {
3960
4325
  if (members.length > 0) {
3961
4326
  changed.push({ ...frame, ...boundsAround(members) });
3962
4327
  }
4328
+ changed.push(...releaseCrossingArrows([...scene.value.elements, ...changed], frame));
3963
4329
  const applied = deps.write.apply(scene.value, changed);
3964
4330
  if (!applied.ok) return applied;
3965
4331
  return ok({
3966
4332
  sceneId: applied.value.sceneId,
3967
4333
  sceneVersion: applied.value.sceneVersion,
3968
4334
  broadcastTo: applied.value.broadcastTo,
4335
+ overlapWarning: applied.value.overlapWarning,
3969
4336
  frameId: input.frameId,
3970
4337
  children: members.map((element) => element.id)
3971
4338
  });
@@ -3991,6 +4358,7 @@ function groupElements(deps) {
3991
4358
  sceneId: applied.value.sceneId,
3992
4359
  sceneVersion: applied.value.sceneVersion,
3993
4360
  broadcastTo: applied.value.broadcastTo,
4361
+ overlapWarning: applied.value.overlapWarning,
3994
4362
  groupId,
3995
4363
  grouped: applied.value.applied
3996
4364
  });
@@ -4011,6 +4379,7 @@ function ungroupElements(deps) {
4011
4379
  sceneId: applied.value.sceneId,
4012
4380
  sceneVersion: applied.value.sceneVersion,
4013
4381
  broadcastTo: applied.value.broadcastTo,
4382
+ overlapWarning: applied.value.overlapWarning,
4014
4383
  ungrouped: applied.value.applied
4015
4384
  });
4016
4385
  };
@@ -4026,6 +4395,29 @@ var iconBox = (art, size) => {
4026
4395
  };
4027
4396
  };
4028
4397
  var svgDataUrl = (svg) => DataUrl.parse(`data:image/svg+xml;base64,${Buffer.from(svg, "utf8").toString("base64")}`);
4398
+ function placementOf(elements, input, box) {
4399
+ if (input.attachTo !== void 0) {
4400
+ const anchor = elements.find((element) => element.id === input.attachTo && !element.isDeleted);
4401
+ if (anchor === void 0) return fail(unknownElement(input.attachTo));
4402
+ const sides = input.placement !== void 0 ? [input.placement] : ["above", "below", "right", "left"];
4403
+ const ignore = /* @__PURE__ */ new Set([anchor.id]);
4404
+ for (const side of sides) {
4405
+ const at = anchoredPosition(anchor, box, side);
4406
+ const candidate = { ...at, width: box.width, height: box.height };
4407
+ if (sides.length === 1 || !wouldCollide(candidate, elements, ignore)) {
4408
+ return ok({ ...at, anchorIds: [anchor.id] });
4409
+ }
4410
+ }
4411
+ return ok({
4412
+ ...anchoredPosition(anchor, box, "above"),
4413
+ anchorIds: [anchor.id]
4414
+ });
4415
+ }
4416
+ if (input.x === void 0 || input.y === void 0) {
4417
+ return fail(iconNeedsAPlace(input.iconId));
4418
+ }
4419
+ return ok({ x: input.x, y: input.y, anchorIds: null });
4420
+ }
4029
4421
  function placeIcon(deps) {
4030
4422
  return async (input) => {
4031
4423
  const opened = await deps.icons();
@@ -4044,12 +4436,21 @@ function placeIcon(deps) {
4044
4436
  if (!resolved.ok) return resolved;
4045
4437
  const scene = resolved.value;
4046
4438
  const size = input.size ?? HOUSE.sizes.icon;
4047
- const chalk = { author: "claude", kind: "icon", iconId: icon.id };
4439
+ const box = icon.art.kind === "elements" ? { width: size, height: size } : iconBox(icon.art, size);
4440
+ const spot = placementOf(scene.elements, input, box);
4441
+ if (!spot.ok) return spot;
4442
+ const { x, y, anchorIds } = spot.value;
4443
+ const chalk = {
4444
+ author: "claude",
4445
+ kind: "icon",
4446
+ iconId: icon.id,
4447
+ ...anchorIds === null ? {} : { anchorIds }
4448
+ };
4048
4449
  if (icon.art.kind === "elements") {
4049
4450
  const ids = deps.idGen.elementIds(icon.art.elements.length);
4050
4451
  const placed = deps.placer.place({
4051
4452
  elements: icon.art.elements,
4052
- at: { x: input.x, y: input.y },
4453
+ at: { x, y },
4053
4454
  ids
4054
4455
  });
4055
4456
  const stamped = placed.map(
@@ -4066,7 +4467,6 @@ function placeIcon(deps) {
4066
4467
  iconId: icon.id
4067
4468
  });
4068
4469
  }
4069
- const box = iconBox(icon.art, size);
4070
4470
  const fileId = FileId.parse(deps.idGen.token());
4071
4471
  const file = {
4072
4472
  id: fileId,
@@ -4079,8 +4479,8 @@ function placeIcon(deps) {
4079
4479
  {
4080
4480
  type: "image",
4081
4481
  id: elementId,
4082
- x: input.x,
4083
- y: input.y,
4482
+ x,
4483
+ y,
4084
4484
  width: box.width,
4085
4485
  height: box.height,
4086
4486
  fileId,
@@ -4092,14 +4492,22 @@ function placeIcon(deps) {
4092
4492
  skeletons.push({
4093
4493
  type: "text",
4094
4494
  id: labelId,
4095
- x: input.x,
4096
- y: input.y + box.height + 8,
4495
+ ...captionBelow({ x, y }, box),
4097
4496
  text: input.label,
4098
4497
  width: box.width,
4099
4498
  fontSize: HOUSE.fontSize,
4100
4499
  fontFamily: HOUSE.fontFamily,
4101
4500
  textAlign: "center",
4102
- customData: { chalk: { author: "claude", kind: "note", iconId: icon.id } }
4501
+ // The caption carries the anchor too, or the glyph travels with its box and the
4502
+ // word it is captioned with stays behind.
4503
+ customData: {
4504
+ chalk: {
4505
+ author: "claude",
4506
+ kind: "note",
4507
+ iconId: icon.id,
4508
+ ...anchorIds === null ? {} : { anchorIds }
4509
+ }
4510
+ }
4103
4511
  });
4104
4512
  }
4105
4513
  const expanded = await deps.write.create(scene, skeletons);
@@ -4185,7 +4593,10 @@ function writeContext(deps) {
4185
4593
  return ok({
4186
4594
  sceneId: scene.id,
4187
4595
  sceneVersion: updated.sceneVersion,
4188
- broadcastTo: broadcast(scene, updated.sceneVersion, pushed, patch?.files)
4596
+ broadcastTo: broadcast(scene, updated.sceneVersion, pushed, patch?.files),
4597
+ // The scene as it now stands, not the batch: a new box buries whatever was
4598
+ // already underneath it, and the batch alone cannot see that.
4599
+ overlapWarning: overlapWarning([...kept, ...expansion.created])
4189
4600
  });
4190
4601
  },
4191
4602
  apply(scene, changed, options) {
@@ -4195,7 +4606,10 @@ function writeContext(deps) {
4195
4606
  sceneVersion: scene.sceneVersion,
4196
4607
  broadcastTo: 0,
4197
4608
  applied: [],
4198
- rejected: []
4609
+ rejected: [],
4610
+ // Nothing was written, so nothing was made worse. Staying quiet here keeps the
4611
+ // warning meaning "your write did this".
4612
+ overlapWarning: null
4199
4613
  });
4200
4614
  }
4201
4615
  deps.markPresence("writing");
@@ -4224,7 +4638,8 @@ function writeContext(deps) {
4224
4638
  sceneVersion: scene.sceneVersion,
4225
4639
  broadcastTo: 0,
4226
4640
  applied: [...merged.applied],
4227
- rejected
4641
+ rejected,
4642
+ overlapWarning: null
4228
4643
  });
4229
4644
  }
4230
4645
  const updated = deps.commit(scene.id, merged.elements);
@@ -4236,7 +4651,8 @@ function writeContext(deps) {
4236
4651
  sceneVersion: updated.sceneVersion,
4237
4652
  broadcastTo: broadcast(scene, updated.sceneVersion, pushed),
4238
4653
  applied: [...merged.applied],
4239
- rejected
4654
+ rejected,
4655
+ overlapWarning: overlapWarning(merged.elements)
4240
4656
  });
4241
4657
  }
4242
4658
  };
@@ -4378,6 +4794,19 @@ function updateElements(deps) {
4378
4794
  }
4379
4795
  }
4380
4796
  if (moved.size > 0) {
4797
+ const deltas = /* @__PURE__ */ new Map();
4798
+ for (const [id, next] of moved) {
4799
+ const before = byId.get(id);
4800
+ if (before !== void 0) deltas.set(id, [next.x - before.x, next.y - before.y]);
4801
+ }
4802
+ for (const [id, [dx, dy]] of withAnchoredElements(scene.value.elements, deltas)) {
4803
+ if (deltas.has(id) || dx === 0 && dy === 0) continue;
4804
+ const element = byId.get(id);
4805
+ if (element === void 0) continue;
4806
+ const followed = { ...element, x: element.x + dx, y: element.y + dy };
4807
+ moved.set(id, followed);
4808
+ knockOn.push(followed);
4809
+ }
4381
4810
  for (const [id, next] of moved) {
4382
4811
  const before = byId.get(id);
4383
4812
  const caption = captionOf(scene.value.elements, id);
@@ -4389,6 +4818,10 @@ function updateElements(deps) {
4389
4818
  });
4390
4819
  }
4391
4820
  knockOn.push(...redrawArrowsTouching(scene.value.elements, moved));
4821
+ const patched = new Set(changed.map((element) => element.id));
4822
+ knockOn.push(
4823
+ ...refitFrames(scene.value.elements, moved).filter((frame) => !patched.has(frame.id))
4824
+ );
4392
4825
  }
4393
4826
  for (const element of knockOn) {
4394
4827
  const before = byId.get(element.id);
@@ -4402,6 +4835,7 @@ function updateElements(deps) {
4402
4835
  sceneId: applied.value.sceneId,
4403
4836
  sceneVersion: applied.value.sceneVersion,
4404
4837
  broadcastTo: applied.value.broadcastTo,
4838
+ overlapWarning: applied.value.overlapWarning,
4405
4839
  // What the caller asked for, not everything that moved. A caption that followed its
4406
4840
  // box and an arrow that was redrawn are consequences; listing them would turn this
4407
4841
  // into "every element that changed" and leave the caller working out which of them
@@ -4455,6 +4889,7 @@ function deleteElements(deps) {
4455
4889
  sceneId: applied.value.sceneId,
4456
4890
  sceneVersion: applied.value.sceneVersion,
4457
4891
  broadcastTo: applied.value.broadcastTo,
4892
+ overlapWarning: applied.value.overlapWarning,
4458
4893
  deleted: onlyRequested(
4459
4894
  applied.value.applied,
4460
4895
  input.deletions.map((deletion) => deletion.id)
@@ -4509,7 +4944,7 @@ function readEnv(source) {
4509
4944
  }
4510
4945
 
4511
4946
  // src/composition/container.ts
4512
- var PACKAGE_VERSION = "1.0.4";
4947
+ var PACKAGE_VERSION = "1.1.0";
4513
4948
  function readRuntime() {
4514
4949
  return {
4515
4950
  env: process.env,
@@ -5117,8 +5552,22 @@ function renderScene(description) {
5117
5552
  }
5118
5553
  lines.push("elements:");
5119
5554
  for (const element of description.elements) lines.push(row(element, true));
5555
+ lines.push(...overlapLines(description));
5120
5556
  return lines.join("\n");
5121
5557
  }
5558
+ function overlapLines(description) {
5559
+ if (description.overlapCount === 0) return [];
5560
+ const shown = description.overlaps.length;
5561
+ const header = shown === description.overlapCount ? `overlaps: ${description.overlapCount}` : `overlaps: ${description.overlapCount} (worst ${shown} shown)`;
5562
+ return [
5563
+ header,
5564
+ ...description.overlaps.map(
5565
+ (overlap) => `! ${shortId2(overlap.a)} \xD7 ${shortId2(overlap.b)} ${px(overlap.width)}x${px(
5566
+ overlap.height
5567
+ )}`
5568
+ )
5569
+ ];
5570
+ }
5122
5571
  var arrowGlyph = (label) => label === null ? "->" : `-"${label}"->`;
5123
5572
  var insideLine = (edge) => ` ${shortId2(edge.from)} ${arrowGlyph(edge.label)} ${shortId2(edge.to)}`;
5124
5573
  var named = (id, label) => label === null ? shortId2(id) : `${shortId2(id)} "${label}"`;
@@ -5235,7 +5684,7 @@ var searchIconsTool = {
5235
5684
  };
5236
5685
  var placeIconTool = {
5237
5686
  name: "chalk_place_icon",
5238
- description: "Put an icon from the catalog on the canvas, at an id from chalk_search_icons. Give it a `label` \u2014 an unlabelled icon says a thing was here and not which thing. The icon id is recorded on the element, so chalk_describe_scene reports it as [icon:aws/lambda] rather than [image] and you can reason about it later without looking at pixels. Position it roughly and let chalk_arrange tidy up, as with everything else.",
5687
+ description: "Put an icon from the catalog on the canvas, at an id from chalk_search_icons. **Prefer `attachTo`** \u2014 the element the icon belongs to \u2014 over x/y: an attached icon travels with that element through every layout, while one at bare coordinates is near its box by coincidence and gets stranded the first time chalk_arrange runs. `placement` picks the side (above by default, or below/left/right/badge/inside). Give it a `label` unless it is attached to something already named \u2014 an unlabelled loose icon says a thing was here and not which thing. The icon id is recorded on the element, so chalk_describe_scene reports it as [icon:aws/lambda] rather than [image] and you can reason about it later without looking at pixels.",
5239
5688
  annotations: {
5240
5689
  title: "Place an icon",
5241
5690
  readOnlyHint: false,
@@ -5353,7 +5802,7 @@ var sceneSettingsTool = {
5353
5802
  // src/interfaces/mcp/tools/session.ts
5354
5803
  var screenshotTool = {
5355
5804
  name: "chalk_screenshot",
5356
- description: "Look at the canvas as an image. Use it when geometry matters and the description does not settle it \u2014 does this overlap, is that arrow crossing three boxes, does the layout read. Rendering happens in the user's browser, so the canvas has to be open; if no tab is connected the error says so. Prefer chalk_describe_scene for anything you can answer from structure: it is far smaller and it gives you ids you can act on.",
5805
+ description: "Look at the canvas as an image. Use it for questions only a picture answers \u2014 does the diagram read, does it look deliberate, is the visual weight right. Do NOT use it to find collisions: chalk_describe_scene reports every overlap with ids and sizes, and every write tool warns when it buried something. Rendering happens in the user's browser, so the canvas has to be open; if no tab is connected the error says so. Prefer chalk_describe_scene for anything answerable from structure \u2014 it is far smaller and it gives you ids you can act on.",
5357
5806
  annotations: {
5358
5807
  title: "Screenshot the canvas",
5359
5808
  readOnlyHint: true,
@@ -5509,7 +5958,7 @@ var createElementsTool = proxyTool({
5509
5958
  var connectTool = proxyTool({
5510
5959
  name: "chalk_connect",
5511
5960
  title: "Connect two elements",
5512
- 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.",
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. How the line runs, what each end looks like, and whether it is dashed all carry meaning here and are all settable \u2014 read chalk_read_me('arrows') before drawing a diagram's worth of them, or every edge comes out an identical straight solid line.",
5513
5962
  input: ConnectToolInput,
5514
5963
  output: ConnectOutput,
5515
5964
  call: (bridge) => bridge.connect
@@ -5853,6 +6302,9 @@ var STATUS = {
5853
6302
  // Phase 5.
5854
6303
  CATALOG_UNAVAILABLE: 500,
5855
6304
  UNKNOWN_ICON: 404,
6305
+ // 400: the icon exists and the request is malformed — it says nothing about where to
6306
+ // put the thing.
6307
+ ICON_NEEDS_A_PLACE: 400,
5856
6308
  UNKNOWN_PACK: 404,
5857
6309
  // 502, not 500: the failure is somebody else registry or somebody else archive, and
5858
6310
  // the distinction is what tells a user to check their network rather than file a bug.