chalkbridge 1.0.1 → 1.0.4

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/CHANGELOG.md +47 -0
  2. package/README.md +16 -0
  3. package/dist/{chunk-SHJT5BF3.js → chunk-U2ZMSGNS.js} +22 -6
  4. package/dist/cli.js +238 -25
  5. package/dist/index.js +1 -1
  6. package/package.json +1 -1
  7. package/ui-dist/assets/{abnfDiagram-VCTEODGH-BJtEDuNS.js → abnfDiagram-VCTEODGH-C_cqCKun.js} +1 -1
  8. package/ui-dist/assets/{ar-SA-G6X2FPQ2-B6obDqD5.js → ar-SA-G6X2FPQ2-CEKRHtmX.js} +1 -1
  9. package/ui-dist/assets/architecture-7GRP2DOG-fqG4giDT.js +1 -0
  10. package/ui-dist/assets/{architectureDiagram-5GKGNRK7-CvbQe1_v.js → architectureDiagram-5GKGNRK7-BuWVonvB.js} +1 -1
  11. package/ui-dist/assets/{az-AZ-76LH7QW2-CfV9ZZWy.js → az-AZ-76LH7QW2-mn2Y6BCY.js} +1 -1
  12. package/ui-dist/assets/{bg-BG-XCXSNQG7-DUA8YPCl.js → bg-BG-XCXSNQG7-CraLx0fE.js} +1 -1
  13. package/ui-dist/assets/{blockDiagram-NRAW4CY4-DocsEmiD.js → blockDiagram-NRAW4CY4-83XozRPe.js} +1 -1
  14. package/ui-dist/assets/{bn-BD-2XOGV67Q-DvE_tajm.js → bn-BD-2XOGV67Q-CsTrJsPu.js} +1 -1
  15. package/ui-dist/assets/{c4Diagram-UCG6FXSJ-Tx_g_cO9.js → c4Diagram-UCG6FXSJ-DTdS8B8G.js} +1 -1
  16. package/ui-dist/assets/{ca-ES-6MX7JW3Y-DHdtT9lh.js → ca-ES-6MX7JW3Y-DD8cv_ze.js} +1 -1
  17. package/ui-dist/assets/channel-BOrwYBHk.js +1 -0
  18. package/ui-dist/assets/{chunk-75Z2AOVW-LAZTrfqO.js → chunk-75Z2AOVW-DQDqkKP0.js} +1 -1
  19. package/ui-dist/assets/{chunk-DU6HZSFF-DYQVLGqe.js → chunk-DU6HZSFF-_YwywVCK.js} +1 -1
  20. package/ui-dist/assets/{chunk-EIO257PC-B41AYx2_.js → chunk-EIO257PC-CLwbKeso.js} +1 -1
  21. package/ui-dist/assets/{chunk-F27PBJKO-BTHkH7Vo.js → chunk-F27PBJKO-BwhryBf6.js} +1 -1
  22. package/ui-dist/assets/{chunk-G27WJ6UU-BBBWLXXO.js → chunk-G27WJ6UU-BixSJdhz.js} +1 -1
  23. package/ui-dist/assets/{chunk-GMAD6QVW-BkvPd3CL.js → chunk-GMAD6QVW-Bp7aUQup.js} +1 -1
  24. package/ui-dist/assets/{chunk-JQ64N6SF-CKrtUr61.js → chunk-JQ64N6SF-Dt5bAScf.js} +1 -1
  25. package/ui-dist/assets/{chunk-LCL6LL3I-B4K72jRj.js → chunk-LCL6LL3I-B5MsvPPp.js} +1 -1
  26. package/ui-dist/assets/{chunk-OBVCFTLP-CMCEQnr_.js → chunk-OBVCFTLP-DBqcHzNF.js} +1 -1
  27. package/ui-dist/assets/{chunk-P2QGCYS3-BNpg6XHk.js → chunk-P2QGCYS3-TMRACTMO.js} +1 -1
  28. package/ui-dist/assets/{chunk-POPQ4Y6H-BaAoqHUX.js → chunk-POPQ4Y6H-BsmX_MmQ.js} +1 -1
  29. package/ui-dist/assets/{chunk-PWAF6VOD-CGQ4p70E.js → chunk-PWAF6VOD-g4MniF8u.js} +1 -1
  30. package/ui-dist/assets/{chunk-R7TYR2AO-B2S7-r2z.js → chunk-R7TYR2AO-S7THFl9x.js} +1 -1
  31. package/ui-dist/assets/{chunk-RHFEMEQ7-C_ziruXk.js → chunk-RHFEMEQ7-CZYull6D.js} +1 -1
  32. package/ui-dist/assets/{chunk-SVP7TREG-FJEPEsgF.js → chunk-SVP7TREG-DCss0vmb.js} +1 -1
  33. package/ui-dist/assets/{chunk-TEH6E4GO-BvKV-yBX.js → chunk-TEH6E4GO-B-6h5v_5.js} +1 -1
  34. package/ui-dist/assets/classDiagram-DTDB5LWJ-Dm2kcSSm.js +1 -0
  35. package/ui-dist/assets/classDiagram-v2-JRS7N3AN-Dm2kcSSm.js +1 -0
  36. package/ui-dist/assets/{cs-CZ-2BRQDIVT-BamYs5wC.js → cs-CZ-2BRQDIVT-C9fIM1z5.js} +1 -1
  37. package/ui-dist/assets/{cynefin-OW5HDTMX-Cj_nyyy3.js → cynefin-OW5HDTMX-CtdCt-Da.js} +1 -1
  38. package/ui-dist/assets/{cynefinDiagram-5FMLGOSQ-B3Rm8-dE.js → cynefinDiagram-5FMLGOSQ-CF9L7X85.js} +1 -1
  39. package/ui-dist/assets/{da-DK-5WZEPLOC-B8baJSus.js → da-DK-5WZEPLOC-DXGP9-TU.js} +1 -1
  40. package/ui-dist/assets/{dagre-3AP2YEHR-BesdyiUo.js → dagre-3AP2YEHR-Bt0GOj3f.js} +1 -1
  41. package/ui-dist/assets/{de-DE-XR44H4JA-DKPtPOlk.js → de-DE-XR44H4JA-ChR8kF4h.js} +1 -1
  42. package/ui-dist/assets/{diagram-S7CK7UJ4-jeIUYTnQ.js → diagram-S7CK7UJ4-D2NIL20U.js} +1 -1
  43. package/ui-dist/assets/{diagram-UQ7AKVKN-BmAlHtLI.js → diagram-UQ7AKVKN-Dty3qvRq.js} +1 -1
  44. package/ui-dist/assets/{diagram-VSXAHHWV-B-S5XoGb.js → diagram-VSXAHHWV-znpNlomZ.js} +1 -1
  45. package/ui-dist/assets/{diagram-VX7I27RA--KrWarId.js → diagram-VX7I27RA-DMKYB58y.js} +1 -1
  46. package/ui-dist/assets/{diagram-Z3DM3KII-B7MnKinD.js → diagram-Z3DM3KII-BLsAgfm1.js} +1 -1
  47. package/ui-dist/assets/{dist-C2o0r0se.js → dist-Biz3NhE4.js} +4 -4
  48. package/ui-dist/assets/{ebnfDiagram-PWID7BFC-C20WACav.js → ebnfDiagram-PWID7BFC-sN1WpIzH.js} +1 -1
  49. package/ui-dist/assets/{el-GR-BZB4AONW-FSgeiYUW.js → el-GR-BZB4AONW-BMi-5oTn.js} +1 -1
  50. package/ui-dist/assets/{en-B4ZKOASM-BAWBMIE-.js → en-B4ZKOASM-DTTcVPyw.js} +1 -1
  51. package/ui-dist/assets/{erDiagram-SSCWMZ5O-DU4Qz6qu.js → erDiagram-SSCWMZ5O-qLib13wp.js} +1 -1
  52. package/ui-dist/assets/{es-ES-U4NZUMDT-CZ9XqqCk.js → es-ES-U4NZUMDT-DSk-whe2.js} +1 -1
  53. package/ui-dist/assets/{eu-ES-A7QVB2H4-CNagAo_o.js → eu-ES-A7QVB2H4-Dlhf1HMz.js} +1 -1
  54. package/ui-dist/assets/eventmodeling-NTZA5JFV-BN91GLT5.js +1 -0
  55. package/ui-dist/assets/{fa-IR-HGAKTJCU-CGpX3K56.js → fa-IR-HGAKTJCU-C4NL1SUQ.js} +1 -1
  56. package/ui-dist/assets/{fi-FI-Z5N7JZ37-D8ErPXQZ.js → fi-FI-Z5N7JZ37-BPDGFbjy.js} +1 -1
  57. package/ui-dist/assets/flowDiagram-A5DVABFB-DBrXJ-Jf.js +1 -0
  58. package/ui-dist/assets/{fr-FR-RHASNOE6-CgP9qmF-.js → fr-FR-RHASNOE6-BKM44_qc.js} +1 -1
  59. package/ui-dist/assets/{ganttDiagram-EL5Y4UJY-CNqGfKGy.js → ganttDiagram-EL5Y4UJY-Cm_9m3AA.js} +1 -1
  60. package/ui-dist/assets/{gitGraph-4MIJSDKK-Cjg2l4tr.js → gitGraph-4MIJSDKK-B53Ccobw.js} +1 -1
  61. package/ui-dist/assets/{gitGraphDiagram-WWUBYQGX-C1RblGyv.js → gitGraphDiagram-WWUBYQGX-CshbIfrd.js} +1 -1
  62. package/ui-dist/assets/{gl-ES-HMX3MZ6V-D64w31ga.js → gl-ES-HMX3MZ6V-CYq-Wgeo.js} +1 -1
  63. package/ui-dist/assets/{he-IL-6SHJWFNN-prTy1rRE.js → he-IL-6SHJWFNN-CejOCh7c.js} +1 -1
  64. package/ui-dist/assets/{hi-IN-IWLTKZ5I-uvdg8ovV.js → hi-IN-IWLTKZ5I-DdZM6com.js} +1 -1
  65. package/ui-dist/assets/{hu-HU-A5ZG7DT2-OIhzhh7g.js → hu-HU-A5ZG7DT2-DcLFRMuO.js} +1 -1
  66. package/ui-dist/assets/{id-ID-SAP4L64H-Bs8DbFR5.js → id-ID-SAP4L64H-1x9JAf0A.js} +1 -1
  67. package/ui-dist/assets/image-GAAHSSAO-BgehxCDd.js +1 -0
  68. package/ui-dist/assets/{index-CknJbzCM.js → index-CzWe1zeF.js} +8 -8
  69. package/ui-dist/assets/{info-A6RAGUB7-DdLeprH9.js → info-A6RAGUB7-Cge9ecX_.js} +1 -1
  70. package/ui-dist/assets/{infoDiagram-RXCK75RN-B1aPG_BB.js → infoDiagram-RXCK75RN-VS1BTI_H.js} +1 -1
  71. package/ui-dist/assets/{ishikawaDiagram-5VMMS53U-ChG5EVuT.js → ishikawaDiagram-5VMMS53U-Cbf1xbxj.js} +1 -1
  72. package/ui-dist/assets/{it-IT-JPQ66NNP-CVy3c_Ht.js → it-IT-JPQ66NNP-DgmQi0B_.js} +1 -1
  73. package/ui-dist/assets/{ja-JP-DBVTYXUO-EcX4mq4z.js → ja-JP-DBVTYXUO-BpmMfXE3.js} +1 -1
  74. package/ui-dist/assets/{journeyDiagram-EYS64GPL-BZ4Ytc1U.js → journeyDiagram-EYS64GPL-C8RWIfHV.js} +1 -1
  75. package/ui-dist/assets/{kaa-6HZHGXH3-BwP9az-p.js → kaa-6HZHGXH3-CczLR43o.js} +1 -1
  76. package/ui-dist/assets/{kab-KAB-ZGHBKWFO-By-AIEWs.js → kab-KAB-ZGHBKWFO-Cx74DVre.js} +1 -1
  77. package/ui-dist/assets/{kanban-definition-3QL26DDD-Dm8bYrkM.js → kanban-definition-3QL26DDD-R8ng9q7k.js} +1 -1
  78. package/ui-dist/assets/{kk-KZ-P5N5QNE5-DYNjxBd-.js → kk-KZ-P5N5QNE5-BPvg9KTT.js} +1 -1
  79. package/ui-dist/assets/{km-KH-HSX4SM5Z-DS2ZYG-3.js → km-KH-HSX4SM5Z-Blgo7Zit.js} +1 -1
  80. package/ui-dist/assets/{ko-KR-MTYHY66A-DOz4vl1z.js → ko-KR-MTYHY66A-DZdo-KOi.js} +1 -1
  81. package/ui-dist/assets/{ku-TR-6OUDTVRD-CxEEWxJq.js → ku-TR-6OUDTVRD-CTpgWpW_.js} +1 -1
  82. package/ui-dist/assets/{line-B65qaCJb.js → line-Baf15v0x.js} +1 -1
  83. package/ui-dist/assets/{lt-LT-XHIRWOB4-CkgB2O8i.js → lt-LT-XHIRWOB4-DKNkbcsj.js} +1 -1
  84. package/ui-dist/assets/{lv-LV-5QDEKY6T-BUDeq4Hb.js → lv-LV-5QDEKY6T-DwnZRxkH.js} +1 -1
  85. package/ui-dist/assets/{mermaid-parser.core-C60rNywA.js → mermaid-parser.core-0V2gZHuN.js} +3 -3
  86. package/ui-dist/assets/{mindmap-definition-FBJOCRG2-DAu41qFN.js → mindmap-definition-FBJOCRG2-7hOTFX4B.js} +1 -1
  87. package/ui-dist/assets/{mr-IN-CRQNXWMA-DtuuryqC.js → mr-IN-CRQNXWMA-C-BFvdUk.js} +1 -1
  88. package/ui-dist/assets/{my-MM-5M5IBNSE-CC79-XBH.js → my-MM-5M5IBNSE-BFs64Lrm.js} +1 -1
  89. package/ui-dist/assets/{nb-NO-T6EIAALU-CKNIbK-0.js → nb-NO-T6EIAALU-CHGzOQzI.js} +1 -1
  90. package/ui-dist/assets/{nl-NL-IS3SIHDZ-B3dYZhvS.js → nl-NL-IS3SIHDZ-CmrNJ43t.js} +1 -1
  91. package/ui-dist/assets/{nn-NO-6E72VCQL-CdPrOnP4.js → nn-NO-6E72VCQL-Cmx8XlD1.js} +1 -1
  92. package/ui-dist/assets/{oc-FR-POXYY2M6-DUmzGjKc.js → oc-FR-POXYY2M6-D6ZK0W24.js} +1 -1
  93. package/ui-dist/assets/{pa-IN-N4M65BXN-DH39ZTHj.js → pa-IN-N4M65BXN-CoFjVv1P.js} +1 -1
  94. package/ui-dist/assets/{packet-AYTQ26CC-DdR4jEOZ.js → packet-AYTQ26CC-auec35xT.js} +1 -1
  95. package/ui-dist/assets/{pegDiagram-XKGWAZYB-Dx1lj8MV.js → pegDiagram-XKGWAZYB-NMMkeAIs.js} +1 -1
  96. package/ui-dist/assets/percentages-BXMCSKIN-BBuUnwM-.js +1 -0
  97. package/ui-dist/assets/{pie-WAS4IAKB-cVrV21tR.js → pie-WAS4IAKB-Brhoju7F.js} +1 -1
  98. package/ui-dist/assets/{pieDiagram-E7YTZNPT-CcvgLYQz.js → pieDiagram-E7YTZNPT-CbaXV2ep.js} +1 -1
  99. package/ui-dist/assets/{pl-PL-T2D74RX3-BNYpL8df.js → pl-PL-T2D74RX3-CtZh2-fJ.js} +1 -1
  100. package/ui-dist/assets/{pt-BR-5N22H2LF-RWmGHl_8.js → pt-BR-5N22H2LF-Dwnm2Lle.js} +1 -1
  101. package/ui-dist/assets/{pt-PT-UZXXM6DQ-smq6YmTY.js → pt-PT-UZXXM6DQ-Cb08TgqC.js} +1 -1
  102. package/ui-dist/assets/{quadrantDiagram-AXDQQJYC-C4xDsL0E.js → quadrantDiagram-AXDQQJYC-CXEPKL_-.js} +1 -1
  103. package/ui-dist/assets/{radar-RG4KPBEZ-DYaemlGZ.js → radar-RG4KPBEZ-CTDsgRBl.js} +1 -1
  104. package/ui-dist/assets/{railroad-74A4TZTK-BbRg5RPZ.js → railroad-74A4TZTK-Bad-j95k.js} +1 -1
  105. package/ui-dist/assets/railroad-abnf-HS5TGJTU-CbvNSNpm.js +1 -0
  106. package/ui-dist/assets/railroad-ebnf-LZEXJU2U-DdltjnE0.js +1 -0
  107. package/ui-dist/assets/railroad-peg-WCYAUIDC-CgbxToYh.js +1 -0
  108. package/ui-dist/assets/{railroadDiagram-O6MQD6OU-CR1fbGhX.js → railroadDiagram-O6MQD6OU-CJiZ6mmg.js} +1 -1
  109. package/ui-dist/assets/{requirementDiagram-EFPCY7ZU-CFp52i96.js → requirementDiagram-EFPCY7ZU-T8QMEZu6.js} +1 -1
  110. package/ui-dist/assets/{ro-RO-JPDTUUEW-BOjKaxCz.js → ro-RO-JPDTUUEW-D6H165NT.js} +1 -1
  111. package/ui-dist/assets/{ru-RU-B4JR7IUQ-DOK2ZlyX.js → ru-RU-B4JR7IUQ-CBp0xVXG.js} +1 -1
  112. package/ui-dist/assets/{sankeyDiagram-P5KCCOFB-CZWdGNnS.js → sankeyDiagram-P5KCCOFB-Z9s0vmSt.js} +1 -1
  113. package/ui-dist/assets/{sequenceDiagram-WJ2MYXX4-D4X03yFA.js → sequenceDiagram-WJ2MYXX4-BulR70nw.js} +1 -1
  114. package/ui-dist/assets/{si-LK-N5RQ5JYF-BtKkEPfN.js → si-LK-N5RQ5JYF-10AxMH9e.js} +1 -1
  115. package/ui-dist/assets/{sk-SK-C5VTKIMK-DehBWGkI.js → sk-SK-C5VTKIMK-BXvCxb46.js} +1 -1
  116. package/ui-dist/assets/{sl-SI-NN7IZMDC-ClR46UYx.js → sl-SI-NN7IZMDC-BI5_PFXZ.js} +1 -1
  117. package/ui-dist/assets/{stateDiagram-HBIQ2CUA-Bg1Uy2K-.js → stateDiagram-HBIQ2CUA-Dam0DwKS.js} +1 -1
  118. package/ui-dist/assets/stateDiagram-v2-4QOOHH4V-CSjCWsxW.js +1 -0
  119. package/ui-dist/assets/subset-shared.chunk-Du7WrYQy.js +1 -0
  120. package/ui-dist/assets/{subset-worker.chunk-BLppPGco.js → subset-worker.chunk-BaDYCnkg.js} +1 -1
  121. package/ui-dist/assets/{sv-SE-XGPEYMSR-Cq54FLpH.js → sv-SE-XGPEYMSR-dVM3morJ.js} +1 -1
  122. package/ui-dist/assets/{swimlanes-XN3QIQJK-Ca4vLBMp.js → swimlanes-XN3QIQJK-txWA6rWV.js} +1 -1
  123. package/ui-dist/assets/swimlanesDiagram-VK2B7HYN-CU4h0g7a.js +8 -0
  124. package/ui-dist/assets/{ta-IN-2NMHFXQM-SCh3n0lB.js → ta-IN-2NMHFXQM-CWJY03pN.js} +1 -1
  125. package/ui-dist/assets/{th-TH-HPSO5L25-BotlAjVv.js → th-TH-HPSO5L25-CR6GhLgj.js} +1 -1
  126. package/ui-dist/assets/{timeline-definition-24CTP7MA-CjHCKN4P.js → timeline-definition-24CTP7MA-D8m_1UrT.js} +1 -1
  127. package/ui-dist/assets/{tr-TR-DEFEU3FU-B4u6Au2y.js → tr-TR-DEFEU3FU-BI8tDJSm.js} +1 -1
  128. package/ui-dist/assets/{treeView-Q6P3EWNA-CKMtikLc.js → treeView-Q6P3EWNA-CWJmpg6A.js} +1 -1
  129. package/ui-dist/assets/{treemap-WGGIJYW6-B51xYeX7.js → treemap-WGGIJYW6-CvClsvN5.js} +1 -1
  130. package/ui-dist/assets/{uk-UA-QMV73CPH-CtQkKgv_.js → uk-UA-QMV73CPH-D3Uc38wP.js} +1 -1
  131. package/ui-dist/assets/{vennDiagram-4TSXK5OY-CSH3IvAD.js → vennDiagram-4TSXK5OY-C5iS8F8o.js} +1 -1
  132. package/ui-dist/assets/{vi-VN-M7AON7JQ-CjZyv0HH.js → vi-VN-M7AON7JQ-B6BZpyS4.js} +1 -1
  133. package/ui-dist/assets/{wardley-WFR3VGLG-wnJkewtF.js → wardley-WFR3VGLG-CdqB7_9-.js} +1 -1
  134. package/ui-dist/assets/{wardleyDiagram-VM6X3IG4-BJuisjH-.js → wardleyDiagram-VM6X3IG4-DAHyWwbW.js} +1 -1
  135. package/ui-dist/assets/{xychartDiagram-S5SC5T6Z-hiDu0XBA.js → xychartDiagram-S5SC5T6Z-ilyFTLQ_.js} +1 -1
  136. package/ui-dist/assets/{zh-CN-LNUGB5OW-DyyyDDxj.js → zh-CN-LNUGB5OW-B0PN355q.js} +1 -1
  137. package/ui-dist/assets/{zh-HK-E62DVLB3-Ch2pxiFR.js → zh-HK-E62DVLB3-DUEHDaPB.js} +1 -1
  138. package/ui-dist/assets/{zh-TW-RAJ6MFWO-CSf1_B6H.js → zh-TW-RAJ6MFWO-Ci4YMClQ.js} +1 -1
  139. package/ui-dist/index.html +1 -1
  140. package/ui-dist/assets/architecture-7GRP2DOG-8nO9N-PA.js +0 -1
  141. package/ui-dist/assets/channel-uzdT5KPV.js +0 -1
  142. package/ui-dist/assets/classDiagram-DTDB5LWJ-CodHh4yt.js +0 -1
  143. package/ui-dist/assets/classDiagram-v2-JRS7N3AN-CodHh4yt.js +0 -1
  144. package/ui-dist/assets/eventmodeling-NTZA5JFV-CLxW34Y6.js +0 -1
  145. package/ui-dist/assets/flowDiagram-A5DVABFB-mMONVpxe.js +0 -1
  146. package/ui-dist/assets/image-GAAHSSAO-D1P4Y62B.js +0 -1
  147. package/ui-dist/assets/percentages-BXMCSKIN-d5p7s-wT.js +0 -1
  148. package/ui-dist/assets/railroad-abnf-HS5TGJTU-BoiCOpxv.js +0 -1
  149. package/ui-dist/assets/railroad-ebnf-LZEXJU2U-ArFqgLSZ.js +0 -1
  150. package/ui-dist/assets/railroad-peg-WCYAUIDC-BAks8GCx.js +0 -1
  151. package/ui-dist/assets/stateDiagram-v2-4QOOHH4V-Ci4MD4Pt.js +0 -1
  152. package/ui-dist/assets/subset-shared.chunk-DQ-JqEaX.js +0 -1
  153. package/ui-dist/assets/swimlanesDiagram-VK2B7HYN-BKiafpMh.js +0 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # chalkbridge
2
2
 
3
+ ## 1.0.3
4
+
5
+ **An upgrade now actually arrives, a bridge can be stopped, and it ends when you are
6
+ done with it.**
7
+
8
+ (1.0.2 was built and verified but never published — everything below shipped as 1.0.3.)
9
+
10
+ The bridge is spawned detached and outlives the session that started it — deliberately,
11
+ because writes have to succeed with no browser connected. But `ensureBridge` reused
12
+ whatever was already listening without ever checking its version, so `npx -y chalkbridge
13
+ mcp` would fetch a new package, find the **old** bridge still answering, and reuse it.
14
+ Every fix would ship and none would arrive, silently. If 1.0.1 seemed not to change
15
+ anything for you, that is why.
16
+
17
+ - **A bridge from another build is replaced.** It is asked to stop first, so its pending
18
+ `.excalidraw` write still reaches the disk.
19
+ - **`chalkbridge stop`** ends the bridge for the current repo, flushing anything unsaved.
20
+ Until now the only options were a reboot or hunting the pid.
21
+ - **`POST /api/shutdown`** is what both use — a request rather than a signal, because on
22
+ Windows a signal from another process terminates outright and the flush never happens.
23
+ - **A pid fallback for bridges built before this release.** 1.0.0 and 1.0.1 have no
24
+ shutdown endpoint, so the polite path is unavailable for exactly the bridges an upgrade
25
+ has to replace. Without it, upgrading from either would leave the new build unable to
26
+ start at all. Found by running the published 1.0.1 against a local build.
27
+
28
+ **And it ends when you are done with it.** Until now a bridge ran until the machine
29
+ restarted — one per repo, forever — which is not what anybody expects after closing a chat
30
+ and a tab. It now exits when nothing is watching *and* nothing is asking, for 90 seconds:
31
+
32
+ | | |
33
+ |---|---|
34
+ | chat closed, tab closed | exits |
35
+ | tab refreshed | reconnects in about a second, so it stays |
36
+ | chat closed, tab still open | stays — somebody is looking at the canvas |
37
+ | Claude drawing with no tab open | every tool call counts, so it stays |
38
+ | two chats on one repo | activity keeps it alive |
39
+
40
+ One rule, no session counting. `CHALK_IDLE_TIMEOUT` tunes it and `0` restores the old
41
+ behaviour.
42
+
43
+ **A tool call now survives its own bridge.** That is what makes the above invisible: if a
44
+ bridge has given up, the next call re-runs the bootstrap — spawning one and reopening the
45
+ canvas — instead of failing. Previously a bridge lost mid-session meant every call failed
46
+ until Claude Code was restarted.
47
+
48
+ After this release, updating really is just restarting Claude Code.
49
+
3
50
  ## 1.0.1
4
51
 
5
52
  **Arrows are drawn where they belong, the first time.**
package/README.md CHANGED
@@ -53,10 +53,26 @@ reach loopback. Do not work around it by binding a wider address.
53
53
  ```bash
54
54
  chalkbridge health # what is running for this repository
55
55
  chalkbridge serve # start the bridge by hand
56
+ chalkbridge stop # end it, flushing anything unsaved
56
57
  chalkbridge icons list # what is in the machine-wide icon catalog
57
58
  chalkbridge icons install devicon
58
59
  ```
59
60
 
61
+ **The bridge is spawned detached and outlives your session** — deliberately, because
62
+ writes have to succeed with no browser connected. It ends when nothing is watching *and*
63
+ nothing is asking:
64
+
65
+ | | |
66
+ |---|---|
67
+ | chat closed, tab closed | exits after 90 seconds |
68
+ | tab refreshed | reconnects in about a second, so it stays |
69
+ | chat closed, tab still open | stays — you are still looking at the canvas |
70
+ | Claude drawing with no tab | every tool call counts, so it stays |
71
+
72
+ If it has already given up, the next tool call brings it back and reopens the canvas, so
73
+ you never have to think about it. `chalkbridge stop` ends one now; `CHALK_IDLE_TIMEOUT`
74
+ tunes the ninety seconds, and `0` keeps it up forever.
75
+
60
76
  `CHALK_NO_AUTOSTART=1` and `CHALK_NO_OPEN=1` turn off the two automatic behaviours;
61
77
  `CHALK_PORT` pins the port; `CHALK_DATA_DIR` moves the icon catalog.
62
78
 
@@ -223,10 +223,16 @@ var BINDING_FIELDS = [
223
223
  "startBinding",
224
224
  "endBinding"
225
225
  ];
226
+ var DataUrl = z.custom(isNonEmptyString, {
227
+ message: "dataURL must be a non-empty string"
228
+ });
229
+ var FileMimeType = z.custom(isNonEmptyString, {
230
+ message: "mimeType must be a non-empty string"
231
+ });
226
232
  var SceneFile = z.object({
227
233
  id: FileId,
228
- mimeType: z.string(),
229
- dataURL: z.string(),
234
+ mimeType: FileMimeType,
235
+ dataURL: DataUrl,
230
236
  created: z.number(),
231
237
  lastRetrieved: z.number().optional()
232
238
  });
@@ -1248,6 +1254,14 @@ var bridgeUnreachable = (port) => port === null ? err("BRIDGE_UNREACHABLE", "no
1248
1254
  hint: "the bridge may have exited \u2014 `chalkbridge health` reports what is running",
1249
1255
  detail: { port }
1250
1256
  });
1257
+ var bridgeVersionMismatch = (running, ours, port) => err(
1258
+ "BRIDGE_VERSION_MISMATCH",
1259
+ `a Chalkbridge ${running} bridge is running on 127.0.0.1:${port} and this is ${ours}`,
1260
+ {
1261
+ hint: "it did not stop when asked \u2014 run `chalkbridge stop` in this repo, or end that process, and try again",
1262
+ detail: { running, ours, port }
1263
+ }
1264
+ );
1251
1265
  var portUnavailable = (from, tried) => err("PORT_UNAVAILABLE", `no free port between ${from} and ${from + tried - 1}`, {
1252
1266
  hint: "something is occupying the whole range \u2014 set CHALK_PORT to pick a different one",
1253
1267
  detail: { from, tried }
@@ -1449,6 +1463,8 @@ async function loadConverter() {
1449
1463
  uninstall();
1450
1464
  }
1451
1465
  }
1466
+ var NEEDS_STAND_IN = /* @__PURE__ */ new Set(["image", "frame", "embeddable"]);
1467
+ var standIn = (element) => NEEDS_STAND_IN.has(element.type) ? { ...element, type: "rectangle" } : element;
1452
1468
  function createNodeSkeletonExpander() {
1453
1469
  return {
1454
1470
  async expand(skeletons, context) {
@@ -1458,7 +1474,7 @@ function createNodeSkeletonExpander() {
1458
1474
  if (!loaded.ok) return loaded;
1459
1475
  let raw;
1460
1476
  try {
1461
- const convertible = (context ?? []).filter((element) => element.type !== "frame");
1477
+ const convertible = (context ?? []).map(standIn);
1462
1478
  raw = loaded.value([...convertible, ...skeletons], { regenerateIds: false });
1463
1479
  } catch (thrown) {
1464
1480
  return fail(expansionFailed(describeThrown(thrown)));
@@ -1495,6 +1511,6 @@ var stable = (element) => {
1495
1511
  return rest;
1496
1512
  };
1497
1513
 
1498
- export { AnnotateInput, AnnotateOutput, ApiError, ArrangeOutput, ArrangeToolInput, BINDING_FIELDS, ClientMessage, ConnectOutput, ConnectToolInput, CreateElementsInput, CreateElementsOutput, DebugElementRequest, DeleteElementsInput, DeleteElementsOutput, DescribeSceneInput, DescribeSceneOutput, DetailLevel, EXCALIDRAW_FILE_TYPE, EXCALIDRAW_FILE_VERSION, ElementId, EmbedInput, EmbedOutput, ExcalidrawFile, ExportImageInput, ExportImageOutput, ExportSceneInput, ExportSceneOutput, FLAG_GLYPH, FileId, FocusInput, FocusOutput, FrameId, FrameInput, FrameOutput, GUIDE_TOPICS, GetElementsInput, GetElementsOutput, GetSelectionInput, GetSelectionOutput, GroupId, GroupInput, GroupOutput, GuideTopic, HOUSE, HealthResponse, IconId, IconItem, ImportSceneInput, ImportSceneOutput, Index, InstallPackInput, InstallPackOutput, KIND_ABBREVIATION, ListPacksInput, ListPacksOutput, ListScenesInput, ListScenesOutput, PROTOCOL_VERSION, PackKind, PackName, PackRecipe, PlaceIconInput, PlaceIconOutput, ROUNDNESS, RawElementsInput, RawElementsOutput, ReadMeInput, ReadMeOutput, RemovePackInput, RemovePackOutput, RestoreSnapshotInput, RestoreSnapshotOutput, SERVER_MANAGED_FIELDS, STALE_SELECTION_MS, Scene, SceneElement, SceneId, SceneSettingsInput, SceneSettingsOutput, ScreenshotInput, ScreenshotOutput, ScreenshotResponse, SearchIconsInput, SearchIconsOutput, SnapshotId, SnapshotInput, SnapshotOutput, UngroupInput, UngroupOutput, UpdateElementsInput, UpdateElementsOutput, assertNever, bridgeSpawnFailed, bridgeUnreachable, catalogUnavailable, createNodeSkeletonExpander, deepEqual, describeThrown, diskConflict, emptySelection, expansionFailed, exportFailed, exportTimedOut, fail, frameIdOf, iconIdOf, idCollision, isFail, isOk, noBoundLabel, noBrowser, noScenes, notAPack, notARepo, ok, packInstallFailed, partition, pathOutsideRepo, point, portUnavailable, refusedFieldMessage, sceneFileMissing, staleVersion, storeUnavailable, unknownElement, unknownIcon, unknownScene, unknownSnapshot, unreadableSceneFile };
1499
- //# sourceMappingURL=chunk-SHJT5BF3.js.map
1500
- //# sourceMappingURL=chunk-SHJT5BF3.js.map
1514
+ export { AnnotateInput, AnnotateOutput, ApiError, ArrangeOutput, ArrangeToolInput, BINDING_FIELDS, ClientMessage, ConnectOutput, ConnectToolInput, CreateElementsInput, CreateElementsOutput, DataUrl, DebugElementRequest, DeleteElementsInput, DeleteElementsOutput, DescribeSceneInput, DescribeSceneOutput, DetailLevel, EXCALIDRAW_FILE_TYPE, EXCALIDRAW_FILE_VERSION, ElementId, EmbedInput, EmbedOutput, ExcalidrawFile, ExportImageInput, ExportImageOutput, ExportSceneInput, ExportSceneOutput, FLAG_GLYPH, FileId, FocusInput, FocusOutput, FrameId, FrameInput, FrameOutput, GUIDE_TOPICS, GetElementsInput, GetElementsOutput, GetSelectionInput, GetSelectionOutput, GroupId, GroupInput, GroupOutput, GuideTopic, HOUSE, HealthResponse, IconId, IconItem, ImportSceneInput, ImportSceneOutput, Index, InstallPackInput, InstallPackOutput, KIND_ABBREVIATION, ListPacksInput, ListPacksOutput, ListScenesInput, ListScenesOutput, PROTOCOL_VERSION, PackKind, PackName, PackRecipe, PlaceIconInput, PlaceIconOutput, ROUNDNESS, RawElementsInput, RawElementsOutput, ReadMeInput, ReadMeOutput, RemovePackInput, RemovePackOutput, RestoreSnapshotInput, RestoreSnapshotOutput, SERVER_MANAGED_FIELDS, STALE_SELECTION_MS, Scene, SceneElement, SceneId, SceneSettingsInput, SceneSettingsOutput, ScreenshotInput, ScreenshotOutput, ScreenshotResponse, SearchIconsInput, SearchIconsOutput, SnapshotId, SnapshotInput, SnapshotOutput, UngroupInput, UngroupOutput, UpdateElementsInput, UpdateElementsOutput, assertNever, bridgeSpawnFailed, bridgeUnreachable, bridgeVersionMismatch, catalogUnavailable, createNodeSkeletonExpander, deepEqual, describeThrown, diskConflict, emptySelection, expansionFailed, exportFailed, exportTimedOut, fail, frameIdOf, iconIdOf, idCollision, isFail, isOk, noBoundLabel, noBrowser, noScenes, notAPack, notARepo, ok, packInstallFailed, partition, pathOutsideRepo, point, portUnavailable, refusedFieldMessage, sceneFileMissing, staleVersion, storeUnavailable, unknownElement, unknownIcon, unknownScene, unknownSnapshot, unreadableSceneFile };
1515
+ //# sourceMappingURL=chunk-U2ZMSGNS.js.map
1516
+ //# sourceMappingURL=chunk-U2ZMSGNS.js.map
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, 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, 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-SHJT5BF3.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, 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';
3
3
  import './chunk-7D4SUZUM.js';
4
4
  import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
5
5
  import { homedir } from 'node:os';
@@ -1046,6 +1046,7 @@ var KNOWN_CODES = /* @__PURE__ */ new Set([
1046
1046
  "BRIDGE_SPAWN_FAILED",
1047
1047
  "BRIDGE_UNREACHABLE",
1048
1048
  "PORT_UNAVAILABLE",
1049
+ "BRIDGE_VERSION_MISMATCH",
1049
1050
  "STALE_VERSION",
1050
1051
  "ID_COLLISION",
1051
1052
  "UNKNOWN_ELEMENT",
@@ -1099,23 +1100,48 @@ function createBridgeClient() {
1099
1100
  return null;
1100
1101
  }
1101
1102
  },
1102
- connect(port) {
1103
- const base = `http://127.0.0.1:${port}`;
1104
- async function call(path, schema, init, timeoutMs = DEFAULT_API_TIMEOUT_MS) {
1105
- let response;
1103
+ async shutdown(port, timeoutMs = DEFAULT_HEALTH_TIMEOUT_MS) {
1104
+ try {
1105
+ await fetch(`http://127.0.0.1:${port}/api/shutdown`, {
1106
+ method: "POST",
1107
+ signal: AbortSignal.timeout(timeoutMs)
1108
+ });
1109
+ } catch {
1110
+ }
1111
+ const deadline = Date.now() + timeoutMs * 4;
1112
+ while (Date.now() < deadline) {
1113
+ if (await this.health(port, timeoutMs) === null) return true;
1114
+ await new Promise((resolve6) => setTimeout(resolve6, 100));
1115
+ }
1116
+ return false;
1117
+ },
1118
+ connect(port, reconnect) {
1119
+ let current = port;
1120
+ async function attempt(path, init, timeoutMs) {
1106
1121
  try {
1107
- response = await fetch(`${base}${path}`, {
1122
+ return await fetch(`http://127.0.0.1:${current}${path}`, {
1108
1123
  ...init,
1109
1124
  signal: AbortSignal.timeout(timeoutMs)
1110
1125
  });
1111
1126
  } catch {
1112
- return fail(bridgeUnreachable(port));
1127
+ return null;
1113
1128
  }
1129
+ }
1130
+ async function call(path, schema, init, timeoutMs = DEFAULT_API_TIMEOUT_MS) {
1131
+ let response = await attempt(path, init, timeoutMs);
1132
+ if (response === null && reconnect !== void 0) {
1133
+ const revived = await reconnect();
1134
+ if (revived !== null) {
1135
+ current = revived;
1136
+ response = await attempt(path, init, timeoutMs);
1137
+ }
1138
+ }
1139
+ if (response === null) return fail(bridgeUnreachable(current));
1114
1140
  const body = await response.json().catch(() => null);
1115
- if (!response.ok) return fail(toChalkError(body, port));
1141
+ if (!response.ok) return fail(toChalkError(body, current));
1116
1142
  const parsed = schema.safeParse(body);
1117
1143
  if (!parsed.success) {
1118
- return fail(bridgeUnreachable(port));
1144
+ return fail(bridgeUnreachable(current));
1119
1145
  }
1120
1146
  return ok(parsed.data);
1121
1147
  }
@@ -1726,6 +1752,24 @@ function createBridgeLauncher(options, deps) {
1726
1752
  await sleep(pollIntervalMs);
1727
1753
  }
1728
1754
  return fail(bridgeSpawnFailed(`it did not start listening within ${readyTimeoutMs}ms`));
1755
+ },
1756
+ async stop(pid) {
1757
+ try {
1758
+ process.kill(pid, "SIGTERM");
1759
+ } catch {
1760
+ return false;
1761
+ }
1762
+ const deadline = Date.now() + 5e3;
1763
+ while (Date.now() < deadline) {
1764
+ try {
1765
+ process.kill(pid, 0);
1766
+ } catch {
1767
+ return true;
1768
+ }
1769
+ await sleep(pollIntervalMs);
1770
+ }
1771
+ deps.logger.warn("a bridge did not exit after being signalled", { pid });
1772
+ return false;
1729
1773
  }
1730
1774
  };
1731
1775
  }
@@ -1734,6 +1778,48 @@ function createBridgeLauncher(options, deps) {
1734
1778
  function createClock() {
1735
1779
  return { now: () => Date.now() };
1736
1780
  }
1781
+
1782
+ // src/adapters/system/idle-watch.ts
1783
+ var intervalFor = (timeoutMs) => Math.max(1e3, Math.min(1e4, Math.floor(timeoutMs / 3)));
1784
+ function createIdleWatch(options, deps) {
1785
+ let lastActivity = deps.clock.now();
1786
+ let timer = null;
1787
+ let fired = false;
1788
+ return {
1789
+ touch() {
1790
+ lastActivity = deps.clock.now();
1791
+ },
1792
+ start({ isWatched, onIdle }) {
1793
+ const timeoutMs = options.timeoutMs;
1794
+ if (timeoutMs === null) {
1795
+ deps.logger.debug("idle shutdown is disabled", { reason: "CHALK_IDLE_TIMEOUT=0" });
1796
+ return;
1797
+ }
1798
+ if (timer !== null) return;
1799
+ const every = options.checkIntervalMs ?? intervalFor(timeoutMs);
1800
+ timer = setInterval(() => {
1801
+ if (fired) return;
1802
+ if (isWatched()) {
1803
+ lastActivity = deps.clock.now();
1804
+ return;
1805
+ }
1806
+ if (deps.clock.now() - lastActivity < timeoutMs) return;
1807
+ fired = true;
1808
+ deps.logger.info("idle \u2014 nothing watching and nothing asking", {
1809
+ idleMs: deps.clock.now() - lastActivity
1810
+ });
1811
+ onIdle();
1812
+ }, every);
1813
+ timer.unref?.();
1814
+ deps.logger.debug("watching for idle", { timeoutMs, every });
1815
+ },
1816
+ stop() {
1817
+ if (timer === null) return;
1818
+ clearInterval(timer);
1819
+ timer = null;
1820
+ }
1821
+ };
1822
+ }
1737
1823
  var ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
1738
1824
  var shortId = (length) => {
1739
1825
  let out = "";
@@ -2427,7 +2513,20 @@ function ensureBridge(deps) {
2427
2513
  const repoRoot = await deps.projectDir.findRepoRoot(options.cwd);
2428
2514
  if (!repoRoot.ok) return repoRoot;
2429
2515
  const existingPort = await deps.projectDir.readPort(repoRoot.value);
2430
- const existing = existingPort === null ? null : await deps.client.health(existingPort);
2516
+ let existing = existingPort === null ? null : await deps.client.health(existingPort);
2517
+ if (existing !== null && existingPort !== null && existing.version !== deps.version) {
2518
+ deps.logger.info("replacing a bridge from another build", {
2519
+ running: existing.version,
2520
+ ours: deps.version,
2521
+ port: existingPort
2522
+ });
2523
+ const stopped = await deps.client.shutdown(existingPort) || await deps.launcher.stop(existing.pid);
2524
+ if (!stopped) {
2525
+ return fail(bridgeVersionMismatch(existing.version, deps.version, existingPort));
2526
+ }
2527
+ await deps.projectDir.clearPort(repoRoot.value);
2528
+ existing = null;
2529
+ }
2431
2530
  let port = existing === null ? null : existingPort;
2432
2531
  let health = existing;
2433
2532
  let spawned = false;
@@ -3926,7 +4025,7 @@ var iconBox = (art, size) => {
3926
4025
  height: Math.round(art.height * scale)
3927
4026
  };
3928
4027
  };
3929
- var svgDataUrl = (svg) => `data:image/svg+xml;base64,${Buffer.from(svg, "utf8").toString("base64")}`;
4028
+ var svgDataUrl = (svg) => DataUrl.parse(`data:image/svg+xml;base64,${Buffer.from(svg, "utf8").toString("base64")}`);
3930
4029
  function placeIcon(deps) {
3931
4030
  return async (input) => {
3932
4031
  const opened = await deps.icons();
@@ -4028,12 +4127,13 @@ var toRejection = (id, error) => ({
4028
4127
  });
4029
4128
  function writeContext(deps) {
4030
4129
  const resolveScene2 = getScene({ store: deps.store });
4031
- const broadcast = (scene, sceneVersion, elements) => deps.broadcaster.broadcastToScene(scene.id, {
4130
+ const broadcast = (scene, sceneVersion, elements, files) => deps.broadcaster.broadcastToScene(scene.id, {
4032
4131
  t: "scene_patch",
4033
4132
  sceneId: scene.id,
4034
4133
  origin: "claude",
4035
4134
  sceneVersion,
4036
- elements: [...elements]
4135
+ elements: [...elements],
4136
+ ...files === void 0 ? {} : { files }
4037
4137
  });
4038
4138
  return {
4039
4139
  resolve(sceneId) {
@@ -4085,7 +4185,7 @@ function writeContext(deps) {
4085
4185
  return ok({
4086
4186
  sceneId: scene.id,
4087
4187
  sceneVersion: updated.sceneVersion,
4088
- broadcastTo: broadcast(scene, updated.sceneVersion, pushed)
4188
+ broadcastTo: broadcast(scene, updated.sceneVersion, pushed, patch?.files)
4089
4189
  });
4090
4190
  },
4091
4191
  apply(scene, changed, options) {
@@ -4371,6 +4471,7 @@ function printLine(line) {
4371
4471
  }
4372
4472
  var DEFAULT_PORT = 7180;
4373
4473
  var PORT_SCAN_RANGE = 32;
4474
+ var DEFAULT_IDLE_SECONDS = 90;
4374
4475
  var truthy = (value) => value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
4375
4476
  var EnvSchema = z.object({
4376
4477
  CHALK_PORT: z.coerce.number().int().min(1).max(65535).optional(),
@@ -4383,12 +4484,18 @@ var EnvSchema = z.object({
4383
4484
  * icon pack into the developer real data directory would be a test that changed the
4384
4485
  * machine it ran on.
4385
4486
  */
4386
- CHALK_DATA_DIR: z.string().min(1).optional()
4487
+ CHALK_DATA_DIR: z.string().min(1).optional(),
4488
+ /**
4489
+ * Seconds a bridge may sit with no browser client and no tool call before it exits.
4490
+ * `0` keeps it up forever, which is what every version before v1.0.2 did.
4491
+ */
4492
+ CHALK_IDLE_TIMEOUT: z.coerce.number().int().min(0).max(86400).optional()
4387
4493
  });
4388
4494
  function readEnv(source) {
4389
4495
  const parsed = EnvSchema.safeParse(source);
4390
4496
  const env = parsed.success ? parsed.data : {};
4391
4497
  const explicit2 = env.CHALK_PORT ?? null;
4498
+ const idleSeconds = env.CHALK_IDLE_TIMEOUT ?? DEFAULT_IDLE_SECONDS;
4392
4499
  return {
4393
4500
  listenFrom: explicit2 ?? DEFAULT_PORT,
4394
4501
  listenTries: explicit2 === null ? PORT_SCAN_RANGE : 1,
@@ -4396,12 +4503,13 @@ function readEnv(source) {
4396
4503
  autoStart: !truthy(env.CHALK_NO_AUTOSTART),
4397
4504
  autoOpen: !truthy(env.CHALK_NO_OPEN),
4398
4505
  dataDir: env.CHALK_DATA_DIR ?? null,
4506
+ idleTimeoutMs: idleSeconds === 0 ? null : idleSeconds * 1e3,
4399
4507
  logLevel: env.CHALK_LOG_LEVEL ?? "info"
4400
4508
  };
4401
4509
  }
4402
4510
 
4403
4511
  // src/composition/container.ts
4404
- var PACKAGE_VERSION = "1.0.1";
4512
+ var PACKAGE_VERSION = "1.0.4";
4405
4513
  function readRuntime() {
4406
4514
  return {
4407
4515
  env: process.env,
@@ -4465,6 +4573,7 @@ function buildCommandContext(runtime = readRuntime()) {
4465
4573
  if (!opened.ok) return opened;
4466
4574
  const store = opened.value;
4467
4575
  const broadcaster = createBroadcaster(idGen, logger);
4576
+ const idleWatch = createIdleWatch({ timeoutMs: config.idleTimeoutMs }, { clock, logger });
4468
4577
  const presenceTracker = createPresenceTracker();
4469
4578
  const expander = createNodeSkeletonExpander();
4470
4579
  const repoWriter = createRepoWriter({ repoRoot }, { store, logger });
@@ -4472,6 +4581,7 @@ function buildCommandContext(runtime = readRuntime()) {
4472
4581
  const diskWatcher = createDiskWatcher({ logger });
4473
4582
  const commit = commitScene({ store, repoWriter });
4474
4583
  const repoFiles = createRepoFiles({ repoRoot });
4584
+ let onShutdown = null;
4475
4585
  const exportBroker = createExportBroker({}, { broadcaster, idGen, logger });
4476
4586
  const globalPaths = globalPathsOf(runtime, config);
4477
4587
  let catalog = null;
@@ -4635,6 +4745,10 @@ function buildCommandContext(runtime = readRuntime()) {
4635
4745
  // logic in `app/`, and a lifecycle command has no more business holding a store
4636
4746
  // than a route does.
4637
4747
  sceneFilePaths: () => store.list().map((scene) => scene.repoPath).filter((path) => path !== null).map((path) => resolve(repoRoot, path)),
4748
+ idleWatch,
4749
+ onShutdownRequest: (handler) => {
4750
+ onShutdown = handler;
4751
+ },
4638
4752
  close: () => {
4639
4753
  repoWriter.cancel();
4640
4754
  exportBroker.abandon();
@@ -4675,6 +4789,7 @@ function buildCommandContext(runtime = readRuntime()) {
4675
4789
  importScene: usecases.importScene,
4676
4790
  broadcaster,
4677
4791
  identity,
4792
+ requestShutdown: () => onShutdown?.(),
4678
4793
  logger
4679
4794
  },
4680
4795
  ws: {
@@ -4698,24 +4813,42 @@ function buildCommandContext(runtime = readRuntime()) {
4698
4813
  { cliEntry: runtime.entryPath, execPath: runtime.execPath, env: runtime.env },
4699
4814
  { projectDir, client, logger }
4700
4815
  );
4816
+ const bootstrap = ensureBridge({
4817
+ // Compared against whatever is already listening: an upgrade that reused an
4818
+ // older detached bridge would ship every fix and deliver none of them.
4819
+ version: runtime.version,
4820
+ projectDir,
4821
+ client,
4822
+ launcher,
4823
+ opener: createBrowserOpener(logger),
4824
+ logger
4825
+ });
4701
4826
  return {
4702
4827
  logger,
4703
4828
  projectDir,
4704
4829
  client,
4830
+ launcher,
4705
4831
  // A7 — the tool handlers never see a port, a URL, or a `fetch`. They see the
4706
4832
  // bridge's read surface and a shelf of markdown, and nothing else exists to
4707
4833
  // tempt them into caching a scene (invariant 1).
4834
+ //
4835
+ // The `reconnect` argument is what makes a session survive its own bridge. One
4836
+ // now exits when nothing is watching and nothing is asking, so "the port this
4837
+ // session started with" stops being true the moment the user closes the tab and
4838
+ // walks away. Re-running the bootstrap puts a bridge *and a tab* back, which is
4839
+ // what the session's first call did, so a tool call after a break just works.
4708
4840
  createToolDeps: (port) => ({
4709
- bridge: client.connect(port),
4841
+ bridge: client.connect(port, async () => {
4842
+ const revived = await bootstrap({
4843
+ cwd: runtime.cwd,
4844
+ autoStart: config.autoStart,
4845
+ autoOpen: config.autoOpen
4846
+ });
4847
+ return revived.ok ? revived.value.port : null;
4848
+ }),
4710
4849
  guides: createGuideLibrary({ dir: resolveGuidesDir() }, { logger })
4711
4850
  }),
4712
- ensureBridge: ensureBridge({
4713
- projectDir,
4714
- client,
4715
- launcher,
4716
- opener: createBrowserOpener(logger),
4717
- logger
4718
- })
4851
+ ensureBridge: bootstrap
4719
4852
  };
4720
4853
  }
4721
4854
  };
@@ -5702,6 +5835,8 @@ var STATUS = {
5702
5835
  BRIDGE_SPAWN_FAILED: 500,
5703
5836
  BRIDGE_UNREACHABLE: 503,
5704
5837
  PORT_UNAVAILABLE: 500,
5838
+ // 409: something is there, it is simply not the build this process belongs to.
5839
+ BRIDGE_VERSION_MISMATCH: 409,
5705
5840
  // 409 for both: a stale version and a file that changed underneath us are the same
5706
5841
  // shape of problem — the caller's view of the world is out of date, and re-reading is
5707
5842
  // the way forward.
@@ -5960,6 +6095,20 @@ var sessionRoutes = [
5960
6095
  })
5961
6096
  ];
5962
6097
 
6098
+ // src/interfaces/http/routes/shutdown.ts
6099
+ var shutdownRoute = {
6100
+ name: "shutdown",
6101
+ register(app, deps) {
6102
+ app.post("/api/shutdown", async (_request, reply) => {
6103
+ const identity = deps.identity();
6104
+ deps.logger.info("shutdown requested over the API", { pid: identity.pid });
6105
+ await reply.send({ stopping: true, pid: identity.pid, version: identity.version });
6106
+ setTimeout(() => deps.requestShutdown(), 0);
6107
+ return reply;
6108
+ });
6109
+ }
6110
+ };
6111
+
5963
6112
  // src/interfaces/http/routes/write.ts
5964
6113
  var writeRoutes = [
5965
6114
  postRoute({
@@ -6045,6 +6194,7 @@ var ROUTES = [
6045
6194
  elementsRoute,
6046
6195
  settingsRoute,
6047
6196
  debugElementRoute,
6197
+ shutdownRoute,
6048
6198
  ...writeRoutes,
6049
6199
  ...sessionRoutes
6050
6200
  ];
@@ -6058,6 +6208,9 @@ async function createHttpServer(deps) {
6058
6208
  bodyLimit: 32 * 1024 * 1024
6059
6209
  });
6060
6210
  await app.register(websocket);
6211
+ app.addHook("onRequest", async () => {
6212
+ deps.touch();
6213
+ });
6061
6214
  for (const route of ROUTES) {
6062
6215
  route.register(app, deps.routes);
6063
6216
  deps.logger.debug("registered route", { route: route.name });
@@ -6067,6 +6220,7 @@ async function createHttpServer(deps) {
6067
6220
  const send = (message) => {
6068
6221
  socket.send(JSON.stringify(message));
6069
6222
  };
6223
+ deps.touch();
6070
6224
  const handle = deps.broadcaster.register(send);
6071
6225
  const session = { handle, send };
6072
6226
  socket.on("message", (data) => {
@@ -6147,7 +6301,8 @@ ${built.error.hint ?? ""}
6147
6301
  ws: services.ws,
6148
6302
  broadcaster: services.broadcaster,
6149
6303
  logger: services.logger,
6150
- uiDistPath: services.uiDistPath
6304
+ uiDistPath: services.uiDistPath,
6305
+ touch: () => services.idleWatch.touch()
6151
6306
  });
6152
6307
  const listening = await listenOnFirstFreePort(app, {
6153
6308
  host: "127.0.0.1",
@@ -6174,6 +6329,7 @@ ${built.error.hint ?? ""}
6174
6329
  await new Promise((resolve6) => {
6175
6330
  const shutdown = async (signal) => {
6176
6331
  services.logger.info("shutting down", { signal });
6332
+ services.idleWatch.stop();
6177
6333
  await services.diskWatcher.stop();
6178
6334
  const flushed = await services.repoWriter.flushAll();
6179
6335
  if (flushed.length > 0) {
@@ -6184,18 +6340,75 @@ ${built.error.hint ?? ""}
6184
6340
  services.close();
6185
6341
  resolve6();
6186
6342
  };
6343
+ services.idleWatch.start({
6344
+ isWatched: () => services.broadcaster.clientCount() > 0,
6345
+ onIdle: () => void shutdown("idle")
6346
+ });
6187
6347
  process.once("SIGINT", () => void shutdown("SIGINT"));
6188
6348
  process.once("SIGTERM", () => void shutdown("SIGTERM"));
6349
+ services.onShutdownRequest(() => void shutdown("api"));
6189
6350
  });
6190
6351
  return 0;
6191
6352
  }
6192
6353
  };
6193
6354
 
6355
+ // src/interfaces/cli/commands/stop.ts
6356
+ var stopCommand = {
6357
+ name: "stop",
6358
+ summary: "stop the bridge running for this repo, flushing anything unsaved",
6359
+ detail: [
6360
+ "Ends the bridge for the repository containing the working directory.",
6361
+ "",
6362
+ "The bridge flushes every pending .excalidraw write, stops watching the repo, and",
6363
+ "clears its port file on the way out \u2014 so nothing drawn in the last two seconds is",
6364
+ "lost. Nothing is started; the next Claude Code session starts a fresh one."
6365
+ ].join("\n"),
6366
+ async run(context) {
6367
+ const services = context.createClientServices({ prefix: "[stop]" });
6368
+ const repoRoot = await services.projectDir.findRepoRoot(context.runtime.cwd);
6369
+ if (!repoRoot.ok) {
6370
+ context.print(repoRoot.error.message);
6371
+ if (repoRoot.error.hint !== void 0) context.print(` ${repoRoot.error.hint}`);
6372
+ return 1;
6373
+ }
6374
+ const port = await services.projectDir.readPort(repoRoot.value);
6375
+ if (port === null) {
6376
+ context.print(`no bridge is running for ${repoRoot.value}`);
6377
+ return 0;
6378
+ }
6379
+ const health = await services.client.health(port);
6380
+ if (health === null) {
6381
+ await services.projectDir.clearPort(repoRoot.value);
6382
+ context.print(`nothing was listening on 127.0.0.1:${port} \u2014 cleared the stale port file`);
6383
+ return 0;
6384
+ }
6385
+ context.print(
6386
+ `stopping chalkbridge ${health.version} on 127.0.0.1:${port} (pid ${health.pid})`
6387
+ );
6388
+ if (await services.client.shutdown(port)) {
6389
+ context.print(" stopped \u2014 pending scene writes were flushed");
6390
+ return 0;
6391
+ }
6392
+ context.print(" it has no shutdown endpoint (built before v1.0.2) \u2014 ending it by pid");
6393
+ if (await services.launcher.stop(health.pid)) {
6394
+ await services.projectDir.clearPort(repoRoot.value);
6395
+ context.print(` stopped pid ${health.pid}`);
6396
+ return 0;
6397
+ }
6398
+ context.print(" it would not stop \u2014 end it yourself:");
6399
+ context.print(
6400
+ context.runtime.platform === "win32" ? ` taskkill /PID ${health.pid} /F` : ` kill ${health.pid}`
6401
+ );
6402
+ return 1;
6403
+ }
6404
+ };
6405
+
6194
6406
  // src/interfaces/cli/registry.ts
6195
6407
  var COMMANDS = [
6196
6408
  serveCommand,
6197
6409
  mcpCommand,
6198
6410
  healthCommand,
6411
+ stopCommand,
6199
6412
  iconsCommand
6200
6413
  ];
6201
6414
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { createNodeSkeletonExpander, fail, isFail, isOk, ok, partition } from './chunk-SHJT5BF3.js';
1
+ export { createNodeSkeletonExpander, fail, isFail, isOk, ok, partition } from './chunk-U2ZMSGNS.js';
2
2
  import './chunk-7D4SUZUM.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chalkbridge",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "type": "module",
5
5
  "description": "Self-hosted Excalidraw canvas for Claude Code to draw and reason on architecture diagrams.",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as t}from"./src-oBChb5qS.js";import"./chunk-DU6HZSFF-DYQVLGqe.js";import"./dist-C2o0r0se.js";import{m as n,t as r}from"./mermaid-parser.core-C60rNywA.js";import{n as i,r as a,t as o}from"./chunk-SVP7TREG-FJEPEsgF.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=n().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{o.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new r(n);let i=n.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
1
+ import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as t}from"./src-oBChb5qS.js";import"./chunk-DU6HZSFF-_YwywVCK.js";import"./dist-Biz3NhE4.js";import{m as n,t as r}from"./mermaid-parser.core-0V2gZHuN.js";import{n as i,r as a,t as o}from"./chunk-SVP7TREG-DCss0vmb.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=n().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{o.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new r(n);let i=n.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};