@cardstack/boxel-cli 0.11.0-unstable.1 → 0.11.0-unstable.3

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 (177) hide show
  1. package/bundled-realms/pretui/components/agent-mark.gts +98 -0
  2. package/bundled-realms/pretui/components/agent-question.gts +340 -37
  3. package/bundled-realms/pretui/components/answer-footer.gts +249 -0
  4. package/bundled-realms/pretui/components/card-embed.gts +523 -0
  5. package/bundled-realms/pretui/components/card-pills.gts +725 -0
  6. package/bundled-realms/pretui/components/code-block.gts +87 -45
  7. package/bundled-realms/pretui/components/collapsed-turn.gts +133 -12
  8. package/bundled-realms/pretui/components/compaction-chip.gts +81 -67
  9. package/bundled-realms/pretui/components/composer-cap.gts +516 -0
  10. package/bundled-realms/pretui/components/context-ring.gts +108 -0
  11. package/bundled-realms/pretui/components/diff-block.gts +1002 -31
  12. package/bundled-realms/pretui/components/diff-stat.gts +102 -0
  13. package/bundled-realms/pretui/components/jump-chip.gts +86 -53
  14. package/bundled-realms/pretui/components/markdown-stream.gts +799 -0
  15. package/bundled-realms/pretui/components/markdown-surface.gts +71 -0
  16. package/bundled-realms/pretui/components/plan-mark.gts +147 -0
  17. package/bundled-realms/pretui/components/prose.gts +185 -8
  18. package/bundled-realms/pretui/components/prose.md +6 -8
  19. package/bundled-realms/pretui/components/quick-look.gts +377 -0
  20. package/bundled-realms/pretui/components/reactions.gts +136 -0
  21. package/bundled-realms/pretui/components/recommendation.gts +67 -36
  22. package/bundled-realms/pretui/components/result-card.gts +58 -22
  23. package/bundled-realms/pretui/components/segmented-control.gts +13 -8
  24. package/bundled-realms/pretui/components/session-prep.gts +12 -11
  25. package/bundled-realms/pretui/components/sliding-highlight.gts +8 -3
  26. package/bundled-realms/pretui/components/speaker-label.gts +132 -0
  27. package/bundled-realms/pretui/components/state-glyph.gts +121 -0
  28. package/bundled-realms/pretui/components/step-list.gts +62 -60
  29. package/bundled-realms/pretui/components/stick-to-bottom.gts +1150 -0
  30. package/bundled-realms/pretui/components/system-line.gts +143 -0
  31. package/bundled-realms/pretui/components/task-checklist.gts +184 -0
  32. package/bundled-realms/pretui/components/thinking.gts +63 -94
  33. package/bundled-realms/pretui/components/tool-change-table.gts +285 -0
  34. package/bundled-realms/pretui/components/tool-gate.gts +462 -0
  35. package/bundled-realms/pretui/components/tool-group.gts +920 -0
  36. package/bundled-realms/pretui/components/tool-icon.ts +64 -0
  37. package/bundled-realms/pretui/components/tool-meta.gts +82 -0
  38. package/bundled-realms/pretui/components/tool-plan.gts +197 -0
  39. package/bundled-realms/pretui/components/tool-row.gts +2087 -0
  40. package/bundled-realms/pretui/components/turn-changes.gts +227 -0
  41. package/bundled-realms/pretui/components/user-bubble.gts +575 -0
  42. package/bundled-realms/pretui/components/work-item.gts +185 -48
  43. package/bundled-realms/pretui/docs/scroll-anchoring-protocol.md +136 -0
  44. package/bundled-realms/pretui/internal/markdown-projection.ts +438 -0
  45. package/bundled-realms/pretui/internal/mount-queue.ts +79 -0
  46. package/bundled-realms/pretui/internal/tool-phrases.ts +168 -0
  47. package/bundled-realms/pretui/package.json +2 -1
  48. package/bundled-test-harness/assets/{ai-module-creation-test-beRNwH-e.js → ai-module-creation-test-B6FQKqIl.js} +1 -1
  49. package/bundled-test-harness/assets/{app-QAuw0l8v.js → app-KpPqdyX3.js} +4 -4
  50. package/bundled-test-harness/assets/{binary-upload-dedupe-test-bI8T0QLl.js → binary-upload-dedupe-test-DN00y1tM.js} +1 -1
  51. package/bundled-test-harness/assets/{can-invoke-test-DVaU_yRT.js → can-invoke-test-Bl-EUFM3.js} +1 -1
  52. package/bundled-test-harness/assets/{card-chooser-paging-test-DnxMnXKR.js → card-chooser-paging-test-BNuZRHX3.js} +1 -1
  53. package/bundled-test-harness/assets/{card-chooser-test-DUSnLr-u.js → card-chooser-test-wKfPOOT4.js} +1 -1
  54. package/bundled-test-harness/assets/{card-def-field-def-relationships-test-GBUr3tFK.js → card-def-field-def-relationships-test-CduQ6zan.js} +1 -1
  55. package/bundled-test-harness/assets/{card-delete-test-CQveO1VC.js → card-delete-test-DKxolj5o.js} +1 -1
  56. package/bundled-test-harness/assets/{check-correctness-test-D2M2p66N.js → check-correctness-test-B-kgHztp.js} +1 -1
  57. package/bundled-test-harness/assets/choreo-BEHW-NOc.js +2 -0
  58. package/bundled-test-harness/assets/{choreo-CJfBb_EY.js → choreo-C2cpCfhA.js} +164 -13
  59. package/bundled-test-harness/assets/{choreo-feature-reel-test-D2OrygZv.js → choreo-feature-reel-test-CPI_6nkT.js} +2 -2
  60. package/bundled-test-harness/assets/{choreo-gallery-w07xqtO-.js → choreo-gallery-BxA9cyzF.js} +3 -3
  61. package/bundled-test-harness/assets/{choreo-gallery-build-order-test-yQg4JER6.js → choreo-gallery-build-order-test-CLVoseAJ.js} +1 -1
  62. package/bundled-test-harness/assets/{choreo-gallery-compositor-test-C2THCXJN.js → choreo-gallery-compositor-test-BpT5Ia6B.js} +1 -1
  63. package/bundled-test-harness/assets/{choreo-gallery-cover-test-D4T_Tk4s.js → choreo-gallery-cover-test-DL_fdSsh.js} +1 -1
  64. package/bundled-test-harness/assets/{choreo-gallery-crossing-stage-test-tHUPJ-Hy.js → choreo-gallery-crossing-stage-test-BcKHhpOK.js} +2 -2
  65. package/bundled-test-harness/assets/{choreo-gallery-demos-alive-test-BOtBOnEZ.js → choreo-gallery-demos-alive-test-KufLhXG8.js} +1 -1
  66. package/bundled-test-harness/assets/{choreo-gallery-drift-test-D_6U1Z2c.js → choreo-gallery-drift-test-Cx58rYIA.js} +1 -1
  67. package/bundled-test-harness/assets/{choreo-gallery-escort-test-DDBU0n4C.js → choreo-gallery-escort-test-Wz-emmID.js} +1 -1
  68. package/bundled-test-harness/assets/{choreo-gallery-films-test-CgW1NSdr.js → choreo-gallery-films-test-BFrVs5WW.js} +2 -2
  69. package/bundled-test-harness/assets/{choreo-gallery-filter-test-DUIsNwdO.js → choreo-gallery-filter-test-DCKQkhwe.js} +1 -1
  70. package/bundled-test-harness/assets/{choreo-gallery-fold-test-Chzlxvdr.js → choreo-gallery-fold-test-UtpDgpG9.js} +1 -1
  71. package/bundled-test-harness/assets/{choreo-gallery-guide-pages-test-DOWnqALT.js → choreo-gallery-guide-pages-test-rXDU9JLM.js} +1 -1
  72. package/bundled-test-harness/assets/{choreo-gallery-guides-test-Dra2J4LJ.js → choreo-gallery-guides-test-DfVFdIuU.js} +3 -3
  73. package/bundled-test-harness/assets/{choreo-gallery-hang-test-zzn1eiB-.js → choreo-gallery-hang-test-Cj7MbkFf.js} +1 -1
  74. package/bundled-test-harness/assets/{choreo-gallery-highlight-test-CW99hncQ.js → choreo-gallery-highlight-test-D6CF3IYZ.js} +2 -2
  75. package/bundled-test-harness/assets/{choreo-gallery-inline-edit-test-CYFm1kUz.js → choreo-gallery-inline-edit-test-LfHWsbqc.js} +1 -1
  76. package/bundled-test-harness/assets/{choreo-gallery-interruption-test-CYIBrKV9.js → choreo-gallery-interruption-test-CvQQAgTp.js} +2 -2
  77. package/bundled-test-harness/assets/{choreo-gallery-lone-stack-test-DL8R3SG7.js → choreo-gallery-lone-stack-test-D7Zci7fm.js} +1 -1
  78. package/bundled-test-harness/assets/{choreo-gallery-navigation-test-Dcv04kDx.js → choreo-gallery-navigation-test-DB1EnBEW.js} +1 -1
  79. package/bundled-test-harness/assets/{choreo-gallery-playhead-transport-test-Cfz7GsXl.js → choreo-gallery-playhead-transport-test-CtBlc6AJ.js} +1 -1
  80. package/bundled-test-harness/assets/{choreo-gallery-poplayout-subtree-test-CwoklBkW.js → choreo-gallery-poplayout-subtree-test-BQbmjsvI.js} +1 -1
  81. package/bundled-test-harness/assets/{choreo-gallery-presence-modes-test-BDCIZzJ0.js → choreo-gallery-presence-modes-test-CIdXVG1K.js} +1 -1
  82. package/bundled-test-harness/assets/{choreo-gallery-presentation-test-DlyiHwFf.js → choreo-gallery-presentation-test-C_rPFp29.js} +1 -1
  83. package/bundled-test-harness/assets/{choreo-gallery-reduced-motion-test-DVz5SKNR.js → choreo-gallery-reduced-motion-test-CH5w8Lw-.js} +2 -2
  84. package/bundled-test-harness/assets/{choreo-gallery-sheet-standalone-test-CpD_-BvS.js → choreo-gallery-sheet-standalone-test-CDgI4mGX.js} +1 -1
  85. package/bundled-test-harness/assets/{choreo-gallery-sheet-test-BjvAKHDL.js → choreo-gallery-sheet-test-5lsFX7Rk.js} +1 -1
  86. package/bundled-test-harness/assets/{choreo-gallery-site-test-oq_7y_PN.js → choreo-gallery-site-test-DbMa2xeC.js} +2 -2
  87. package/bundled-test-harness/assets/{choreo-gallery-stage-Bzr4_8Xd.js → choreo-gallery-stage-b3Sk86hp.js} +2 -2
  88. package/bundled-test-harness/assets/{choreo-gallery-stage-keyboard-test-a83d2euJ.js → choreo-gallery-stage-keyboard-test-ds6fT5Eg.js} +1 -1
  89. package/bundled-test-harness/assets/{choreo-gallery-stages-test-C9Sl1kH6.js → choreo-gallery-stages-test-ahioKCct.js} +2 -2
  90. package/bundled-test-harness/assets/{choreo-gallery-subdivision-test-BVVnkOwk.js → choreo-gallery-subdivision-test-g7anTDhT.js} +1 -1
  91. package/bundled-test-harness/assets/{choreo-gallery-table-plan-test-B0YdYEA0.js → choreo-gallery-table-plan-test-Bozc4uUf.js} +1 -1
  92. package/bundled-test-harness/assets/{choreo-gallery-theater-test-BLA147xw.js → choreo-gallery-theater-test-kE535d-b.js} +3 -3
  93. package/bundled-test-harness/assets/{choreo-gallery-tuning-test-DSXiNzCh.js → choreo-gallery-tuning-test-6rHkQBnZ.js} +1 -1
  94. package/bundled-test-harness/assets/{choreo-gallery-widget-room-test-BI574PdO.js → choreo-gallery-widget-room-test-0Vq-uTo1.js} +2 -2
  95. package/bundled-test-harness/assets/{choreo-gallery-wires-test-B6Mz2WZl.js → choreo-gallery-wires-test-CdtnAWYO.js} +1 -1
  96. package/bundled-test-harness/assets/clip-BLfuHi8g.js +2 -0
  97. package/bundled-test-harness/assets/{clip-C_pXjVvY.js → clip-q1QE5f73.js} +1 -1
  98. package/bundled-test-harness/assets/{codeblocks-test-AdolhdCA.js → codeblocks-test-BDD7qK_V.js} +1 -1
  99. package/bundled-test-harness/assets/{compaction-test-DvyAQq56.js → compaction-test-BweolkhG.js} +1 -1
  100. package/bundled-test-harness/assets/{container-DIF_9lZ9.css → container-B5B6u-xm.css} +29 -22
  101. package/bundled-test-harness/assets/{container-DvvqmNpg.js → container-BnJjIM6J.js} +9 -6
  102. package/bundled-test-harness/assets/{create-listing-modal-test-D95JfQab.js → create-listing-modal-test-FvcAKpIa.js} +1 -1
  103. package/bundled-test-harness/assets/{debug-message-test-BcNtj7Wr.js → debug-message-test-CJ18Evt7.js} +1 -1
  104. package/bundled-test-harness/assets/{externals-6ZWftYIb.js → externals-BWjhmCD4.js} +12 -12
  105. package/bundled-test-harness/assets/{fallback-banner-live-delete-test-5lnN1pv2.js → fallback-banner-live-delete-test-C2H3BrT-.js} +1 -1
  106. package/bundled-test-harness/assets/{fallback-banner-test-DFb_m_Kq.js → fallback-banner-test-C10aYjkj.js} +1 -1
  107. package/bundled-test-harness/assets/{fallback-models-test-p3YIwj1L.js → fallback-models-test-BxoPiaGF.js} +1 -1
  108. package/bundled-test-harness/assets/{file-attachment-test-CmyjNWRR.js → file-attachment-test-DeKyzYaz.js} +1 -1
  109. package/bundled-test-harness/assets/film-B68PjRlr.js +2 -0
  110. package/bundled-test-harness/assets/{film-D7vtJO5q.js → film-CeUpnHN_.js} +6 -6
  111. package/bundled-test-harness/assets/{film-DN0A8JHx.js → film-DBZqyqW4.js} +6 -6
  112. package/bundled-test-harness/assets/{general-test-CX-TFDuX.js → general-test-BewzrpoB.js} +1 -1
  113. package/bundled-test-harness/assets/{glimmer-motion-and-choreo-in-cards-test-BHte2t9G.js → glimmer-motion-and-choreo-in-cards-test-HA-INN3d.js} +14 -14
  114. package/bundled-test-harness/assets/{glimmer-motion-and-choreo-test-BPNVeIio.js → glimmer-motion-and-choreo-test-BZ0ZuuAw.js} +3 -3
  115. package/bundled-test-harness/assets/{index-query-engine-test-Dz7Q4Xmk.js → index-query-engine-test-BtI8Y2LR.js} +1 -1
  116. package/bundled-test-harness/assets/{instance-filter-matcher-test-BoLU3bVg.js → instance-filter-matcher-test-BHXHGHjE.js} +1 -1
  117. package/bundled-test-harness/assets/{main-Dlgp8c7v.js → main-CqAHZ2kK.js} +1 -1
  118. package/bundled-test-harness/assets/{message-tool-test-CfjRSJvt.js → message-tool-test-NMaPJFsd.js} +1 -1
  119. package/bundled-test-harness/assets/{model-cost-tier-test-mZG84Uso.js → model-cost-tier-test-BgbfN2LH.js} +1 -1
  120. package/bundled-test-harness/assets/{operation-lowering-test-B0KpI1xu.js → operation-lowering-test-C4IaOjsA.js} +1 -1
  121. package/bundled-test-harness/assets/{operator-mode-card-chooser-test-KWS8lq16.js → operator-mode-card-chooser-test-Bwe47Rn7.js} +1 -1
  122. package/bundled-test-harness/assets/{operator-mode-links-test-CViFt0Ws.js → operator-mode-links-test-BU4jkC-B.js} +1 -1
  123. package/bundled-test-harness/assets/{operator-mode-test-D0gZM2yw.js → operator-mode-test-Bh0rGM9z.js} +1 -1
  124. package/bundled-test-harness/assets/{operator-mode-ui-test-CEvHJ9DS.js → operator-mode-ui-test-whgUw0ju.js} +1 -1
  125. package/bundled-test-harness/assets/{overlay-menu-items-test-Pw80RXsr.js → overlay-menu-items-test-Cwn1vr8q.js} +1 -1
  126. package/bundled-test-harness/assets/{overlays-BnOM360u.js → overlays-BOzLbVDK.js} +1 -1
  127. package/bundled-test-harness/assets/{overlays-kBVfa4i8.js → overlays-C_PuGDGh.js} +1 -1
  128. package/bundled-test-harness/assets/{past-sessions-test-CW0-Bjav.js → past-sessions-test-Bh24N-Kz.js} +1 -1
  129. package/bundled-test-harness/assets/{permission-checking-realm-test-D5uXQypS.js → permission-checking-realm-test-DKnnAy0f.js} +1 -1
  130. package/bundled-test-harness/assets/{plate-CFvCDv2t.js → plate-BL7UtyKE.js} +1 -1
  131. package/bundled-test-harness/assets/plate-CqG3igCJ.js +2 -0
  132. package/bundled-test-harness/assets/player-BJfsgdSI.js +2 -0
  133. package/bundled-test-harness/assets/{player-DzuYjrTU.js → player-Da_CIsp_.js} +1 -1
  134. package/bundled-test-harness/assets/{realm-config-policy-standing-test-Btatheni.js → realm-config-policy-standing-test-DngdYypq.js} +1 -1
  135. package/bundled-test-harness/assets/{realm-config-routing-rule-edit-test-Dh5zW13o.js → realm-config-routing-rule-edit-test-_rkFU8SN.js} +1 -1
  136. package/bundled-test-harness/assets/{realm-config-settings-test-BJa8gv_C.js → realm-config-settings-test-CR9NH0Zn.js} +1 -1
  137. package/bundled-test-harness/assets/{reasoning-test-2Zp37cQB.js → reasoning-test-0lH10kRl.js} +1 -1
  138. package/bundled-test-harness/assets/{room-message-test-BsrON13e.js → room-message-test-C-PddE4Y.js} +1 -1
  139. package/bundled-test-harness/assets/{run-BMyvn_UL.js → run-D4n4LPcK.js} +96 -6
  140. package/bundled-test-harness/assets/{run-realm-code-BxktQf6R.js → run-realm-code-C8N7DtN-.js} +1 -1
  141. package/bundled-test-harness/assets/{run-realm-code-test-7grpTos-.js → run-realm-code-test-DBdp147q.js} +1 -1
  142. package/bundled-test-harness/assets/{schema-generation-test-CM9ggQji.js → schema-generation-test-CB1Hb7GQ.js} +1 -1
  143. package/bundled-test-harness/assets/{scrolling-test-BER00htB.js → scrolling-test-3PhVin8h.js} +1 -1
  144. package/bundled-test-harness/assets/{searchable-option-test-BBr5BPow.js → searchable-option-test-BAdOiL4U.js} +1 -1
  145. package/bundled-test-harness/assets/{sending-test-BJGobs2E.js → sending-test-KSmu1tSJ.js} +1 -1
  146. package/bundled-test-harness/assets/{skills-test-GPOsHRN6.js → skills-test-D-dlcFpd.js} +1 -1
  147. package/bundled-test-harness/assets/{src-Df148w_b.js → src-B_DEhPze.js} +4 -4
  148. package/bundled-test-harness/assets/{src-Cf2wv-Le.js → src-BoTgwqUA.js} +2 -2
  149. package/bundled-test-harness/assets/{store-test-QvvHimzH.js → store-test-CEW7xQjZ.js} +1 -1
  150. package/bundled-test-harness/assets/{test-support-rNHuieEc.js → test-support-FXDPpIl3.js} +1 -1
  151. package/bundled-test-harness/assets/{test-support-C6N_2X-l.js → test-support-VX6ukQNR.js} +1 -1
  152. package/bundled-test-harness/assets/{tests-CmsQ-Z_3.js → tests-QYqqa6nv.js} +84 -84
  153. package/bundled-test-harness/assets/{text-input-validator-test-CYYH2N0L.js → text-input-validator-test-D8iNOWep.js} +1 -1
  154. package/bundled-test-harness/assets/{titles-B1pTaIfP.js → titles-DFJj5O0E.js} +1 -1
  155. package/bundled-test-harness/assets/titles-xGlgu7ys.js +2 -0
  156. package/bundled-test-harness/assets/{to-device-streaming-test-Ctqa0bVp.js → to-device-streaming-test-trCX-505.js} +1 -1
  157. package/bundled-test-harness/assets/{token-usage-test-BQVbK3X0.js → token-usage-test-C6sULooH.js} +1 -1
  158. package/bundled-test-harness/assets/{tools-DU8Rc15s.js → tools-UJfD-_al.js} +1 -1
  159. package/bundled-test-harness/assets/{tools-test-B13yvEKE.js → tools-test-Brav31AV.js} +1 -1
  160. package/bundled-test-harness/assets/{worker-DHBEBihX.js → worker-9ph9_Lpv.js} +31 -26
  161. package/bundled-test-harness/index.html +8 -8
  162. package/bundled-test-harness/tests/index.html +8 -8
  163. package/bundled-types/choreo/beacons.ts +16 -4
  164. package/bundled-types/choreo/changeset.ts +25 -5
  165. package/bundled-types/choreo/choreo.gts +245 -3
  166. package/bundled-types/choreo/far.ts +39 -0
  167. package/bundled-types/choreo/index.ts +2 -0
  168. package/bundled-types/choreo/run.ts +104 -1
  169. package/bundled-types/host-app/lib/realm-runner/worker.ts +8 -51
  170. package/bundled-types/runtime-common/realm-runner-script.d.ts +3 -0
  171. package/package.json +1 -1
  172. package/bundled-test-harness/assets/choreo-B3Z9QhDH.js +0 -2
  173. package/bundled-test-harness/assets/clip-BjcXuTwN.js +0 -2
  174. package/bundled-test-harness/assets/film-DMDPP7ma.js +0 -2
  175. package/bundled-test-harness/assets/plate-66qnQ-M9.js +0 -2
  176. package/bundled-test-harness/assets/player-pygg1PwA.js +0 -2
  177. package/bundled-test-harness/assets/titles-Ccpcl-jq.js +0 -2
@@ -206,6 +206,35 @@ interface Signature {
206
206
  /** outline the region and its participants; console.table each run */
207
207
  debug?: boolean;
208
208
  id?: string;
209
+ /**
210
+ * Sit this region out of render passes: while true it takes no snapshot
211
+ * and joins no barrier, so it plays nothing and costs nothing, and its
212
+ * participants stay registered and its block stays rendered. A page with
213
+ * many regions of which few can change at once (a message per region in
214
+ * a long transcript) keeps the rest idle; the pass that sets it back to
215
+ * false is the first one the region watches again.
216
+ */
217
+ idle?: boolean;
218
+ /**
219
+ * Wire only what can be seen: the region measures and animates only the
220
+ * participants inside its scroller's view, or close enough to it
221
+ * (`@inViewMargin` views beyond each edge, default 1) that a move may
222
+ * bring them on screen. The rest are left to layout: no snapshot, no
223
+ * measuring, no motion. A participant coming within the margin is wired
224
+ * and one leaving it unwired, and neither counts as an arrival or a
225
+ * removal. A pass that moves something farther than the margin is a
226
+ * jump, not a change, and lands as layout puts it: animating it would
227
+ * fly blocks past ones that were never wired.
228
+ */
229
+ inView?: boolean;
230
+ inViewMargin?: number;
231
+ /**
232
+ * The most animation time, in ms, one painted frame may advance: a run
233
+ * behind a long frame slows down instead of skipping ahead, so nothing
234
+ * jumps on a busy page. Unset, a frame advances by the time since the
235
+ * last one (up to 40 ms).
236
+ */
237
+ maxFrameStep?: number;
209
238
  /**
210
239
  * A `c.Camera3D` cue moved the shot. Called every frame it changes —
211
240
  * scrubs included, since the pose is sampled from the score — with a
@@ -353,6 +382,29 @@ function reducesMotion(root: Element): boolean {
353
382
  );
354
383
  }
355
384
 
385
+ /** whether an element scrolls its content on either axis */
386
+ function scrolls(el: Element): boolean {
387
+ const { overflowX, overflowY } = getComputedStyle(el);
388
+ return [overflowX, overflowY].some(
389
+ (o) => o === 'auto' || o === 'scroll' || o === 'overlay',
390
+ );
391
+ }
392
+
393
+ /** the nearest ancestor that scrolls vertically: what `@inView` calls the view */
394
+ function scrollerOf(el: Element): HTMLElement | null {
395
+ for (let p = el.parentElement; p; p = p.parentElement) {
396
+ const overflow = getComputedStyle(p).overflowY;
397
+ if (
398
+ overflow === 'auto' ||
399
+ overflow === 'scroll' ||
400
+ overflow === 'overlay'
401
+ ) {
402
+ return p;
403
+ }
404
+ }
405
+ return null;
406
+ }
407
+
356
408
  /**
357
409
  * The barrier books one post-render callback for every region taking part in a
358
410
  * pass. `joinPass(this)` is where the region's PassHost contract (measurePass /
@@ -392,6 +444,9 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
392
444
  private arrived = new Set<ChoreoNode>();
393
445
  /** destroyed before the pass that removed them has been processed */
394
446
  private claimed = new Set<ChoreoNode>();
447
+ /** every beacon name this region's score has read, so a pass measures
448
+ * those beacons and not the whole page's */
449
+ readonly beaconNames = new Set<string>();
395
450
  /** removed participants this region is keeping alive in its orphan layer */
396
451
  private orphans = new Set<ChoreoNode>();
397
452
  /** participants whose Presence is letting them go: removed once, not on every pass while they leave */
@@ -443,6 +498,7 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
443
498
  this.run = undefined;
444
499
  // a quiet region must not take its pause to the grave
445
500
  this.unquiet();
501
+ this.viewObserver?.disconnect();
446
502
  for (const node of [...this.orphans, ...this.claimed]) {
447
503
  this.drop(node);
448
504
  }
@@ -474,6 +530,7 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
474
530
  register(node: ChoreoNode) {
475
531
  this.participants.add(node);
476
532
  this.arrived.add(node);
533
+ this.watchView(node);
477
534
  if (this.args.debug && node.element) {
478
535
  (node.element as HTMLElement).style.outline = '1px dotted #16a34a';
479
536
  // the seam lints (§5.3): inside a region the timeline is the only
@@ -495,9 +552,120 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
495
552
  // a destroyed participant may still be claimed (below); the sets that decide that survive until drop()
496
553
  this.participants.delete(node);
497
554
  this.arrived.delete(node);
555
+ this.unwatchView(node);
498
556
  };
499
557
  }
500
558
 
559
+ /** sat out a pass since the last one it watched (`@idle`) */
560
+ private slept = false;
561
+
562
+ /* ---- @inView: what is wired ---- */
563
+
564
+ /** participants inside the view or its margin, as the observer last saw them */
565
+ private nearView = new Set<ChoreoNode>();
566
+ /** what the last pass animated: kept wired until a pass lets it go, so a
567
+ * block that leaves the margin mid-flight is not cut loose halfway */
568
+ private flying = new Set<ChoreoNode>();
569
+ private viewNodes = new WeakMap<Element, ChoreoNode>();
570
+ private viewObserver?: IntersectionObserver;
571
+ private viewRoot: HTMLElement | null = null;
572
+ /** the `@inViewMargin` the current observer was made with */
573
+ private viewMarginUsed?: number;
574
+
575
+ private get viewMargin() {
576
+ return this.args.inViewMargin ?? 1;
577
+ }
578
+
579
+ private watchView(node: ChoreoNode) {
580
+ const el = node.element;
581
+ if (!this.args.inView || !el) {
582
+ return;
583
+ }
584
+ if (!this.viewObserver) {
585
+ this.viewRoot = this.element ? scrollerOf(this.element) : null;
586
+ this.viewMarginUsed = this.viewMargin;
587
+ this.viewObserver = new IntersectionObserver(
588
+ (entries) => {
589
+ for (const entry of entries) {
590
+ const seen = this.viewNodes.get(entry.target);
591
+ if (!seen) {
592
+ continue;
593
+ }
594
+ if (entry.isIntersecting) {
595
+ this.nearView.add(seen);
596
+ } else {
597
+ this.nearView.delete(seen);
598
+ }
599
+ }
600
+ },
601
+ {
602
+ root: this.viewRoot,
603
+ rootMargin: `${this.viewMargin * 100}% 0px`,
604
+ },
605
+ );
606
+ }
607
+ this.viewNodes.set(el, node);
608
+ this.viewObserver.observe(el);
609
+ }
610
+
611
+ /**
612
+ * `@inView` and `@inViewMargin` can change after participants registered:
613
+ * switched on (or given a new margin), the region observes every current
614
+ * participant and wires the ones already near the view now (the observer
615
+ * reports only after the next frame); switched off, it lets the observer
616
+ * go and wires everything again.
617
+ */
618
+ private syncViewWatch() {
619
+ if (
620
+ this.args.inView &&
621
+ this.viewObserver &&
622
+ this.viewMarginUsed !== this.viewMargin
623
+ ) {
624
+ // the observer's margin is fixed when it is made: a new margin makes a
625
+ // new observer
626
+ this.viewObserver.disconnect();
627
+ this.viewObserver = undefined;
628
+ this.nearView.clear();
629
+ }
630
+ if (this.args.inView && !this.viewObserver) {
631
+ for (const node of this.participants) {
632
+ this.watchView(node);
633
+ }
634
+ if (this.viewObserver) {
635
+ const near = this.nearViewBand();
636
+ for (const node of this.participants) {
637
+ const r = node.element?.getBoundingClientRect();
638
+ if (r && r.bottom >= near.top && r.top <= near.bottom) {
639
+ this.nearView.add(node);
640
+ }
641
+ }
642
+ }
643
+ } else if (!this.args.inView && this.viewObserver) {
644
+ this.viewObserver.disconnect();
645
+ this.viewObserver = undefined;
646
+ this.viewRoot = null;
647
+ this.nearView.clear();
648
+ this.flying.clear();
649
+ }
650
+ }
651
+
652
+ private unwatchView(node: ChoreoNode) {
653
+ this.nearView.delete(node);
654
+ if (node.element) {
655
+ this.viewObserver?.unobserve(node.element);
656
+ }
657
+ }
658
+
659
+ /** the view and its margin, in viewport px; read once per pass */
660
+ private nearViewBand() {
661
+ const r = this.viewRoot
662
+ ? this.viewRoot.getBoundingClientRect()
663
+ : { bottom: window.innerHeight, top: 0 };
664
+ const height = r.bottom - r.top;
665
+ const margin = height * this.viewMargin;
666
+ return { bottom: r.bottom + margin, margin, top: r.top - margin };
667
+ }
668
+
501
669
  claim(node: ChoreoNode) {
502
670
  if (this.orphans.has(node)) {
503
671
  return true;
@@ -520,8 +688,20 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
520
688
  // (`layout` / `layoutId`) snapshot here too, so `{{motion layout=true}}`
521
689
  // works inside a <Choreo> without a <LayoutGroup> wrapped around it.
522
690
  snapshotOnRender();
691
+ if (this.args.idle) {
692
+ this.slept = true;
693
+ return undefined;
694
+ }
695
+ if (this.slept) {
696
+ // what arrived while the region sat out was never watched arriving:
697
+ // waking must not replay it. This pass's own arrivals register after
698
+ // this, as its modifiers install.
699
+ this.slept = false;
700
+ this.arrived.clear();
701
+ }
523
702
  if (!this.passPending) {
524
703
  this.passPending = true;
704
+ this.syncViewWatch();
525
705
  this.snapshot();
526
706
  // every region announces here, while Glimmer is still rendering, so the
527
707
  // barrier knows the whole cast before anybody measures
@@ -534,9 +714,14 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
534
714
  private snapshot() {
535
715
  this.snapshots.clear();
536
716
  this.rootSnapshot = this.element?.getBoundingClientRect();
717
+ const wired = this.args.inView ? this.nearView : undefined;
537
718
  for (const node of this.participants) {
538
719
  const el = node.element;
539
- if (el?.isConnected && !this.orphans.has(node)) {
720
+ if (
721
+ el?.isConnected &&
722
+ !this.orphans.has(node) &&
723
+ (!wired || wired.has(node) || this.flying.has(node))
724
+ ) {
540
725
  this.snapshots.set(node, measure(el));
541
726
  }
542
727
  }
@@ -601,6 +786,7 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
601
786
  role: node.role,
602
787
  type,
603
788
  });
789
+ const near = this.args.inView ? this.nearViewBand() : undefined;
604
790
  const candidates = new Set([
605
791
  ...this.participants,
606
792
  ...this.claimed,
@@ -617,6 +803,15 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
617
803
  const prior = this.orphanBounds.get(node);
618
804
  removed.push(sprite(node, 'removed', prior));
619
805
  } else if (this.arrived.has(node)) {
806
+ if (near) {
807
+ const r = el.getBoundingClientRect();
808
+ if (r.bottom < near.top || r.top > near.bottom) {
809
+ // it lands out of reach of the view: layout places it
810
+ continue;
811
+ }
812
+ // wired now: the observer reports only after the next frame
813
+ this.nearView.add(node);
814
+ }
620
815
  inserted.push(
621
816
  sprite(node, 'inserted', undefined, bounds(measure(el), after)),
622
817
  );
@@ -667,6 +862,24 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
667
862
  };
668
863
  }
669
864
  }
865
+ // a jump: something moved farther than the margin, past blocks that
866
+ // were never wired. It lands as layout puts it; what was leaving goes now.
867
+ if (near && kept.some((s) => Math.abs(s.delta?.y ?? 0) > near.margin)) {
868
+ for (const s of removed) {
869
+ this.drop(s.node);
870
+ }
871
+ inserted.length = 0;
872
+ kept.length = 0;
873
+ removed.length = 0;
874
+ }
875
+ if (near) {
876
+ this.flying = new Set(
877
+ [
878
+ ...inserted,
879
+ ...kept.filter((s) => s.delta && (s.delta.x || s.delta.y)),
880
+ ].map((s) => s.node),
881
+ );
882
+ }
670
883
  this.arrived.clear();
671
884
  this.snapshots.clear();
672
885
  const claimed = [...this.claimed];
@@ -725,7 +938,10 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
725
938
  [...kept, ...inserted.filter((s) => s.type === 'kept')],
726
939
  // measured in the same window as `final`, and re-measured every pass: a
727
940
  // beacon can move without this region rendering at all
728
- measureBeacons(after),
941
+ // the beacons this score has asked for before, measured now; a name it
942
+ // asks for the first time is measured when the step reads it (Glimmer
943
+ // reads a step's args as it compiles, after this)
944
+ measureBeacons(after, this.beaconNames),
729
945
  measureZoom,
730
946
  // the frame's own size in the same final layout, local pixels — the
731
947
  // camera's centre reference, so aim terms are internally consistent
@@ -733,7 +949,11 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
733
949
  height: after.height / measureZoom,
734
950
  width: after.width / measureZoom,
735
951
  },
736
- groundOf(root),
952
+ () => groundOf(root),
953
+ (name) => {
954
+ this.beaconNames.add(name);
955
+ return measureBeacons(after, [name]).get(name) ?? null;
956
+ },
737
957
  );
738
958
  this.changeset = changeset;
739
959
 
@@ -860,6 +1080,9 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
860
1080
  // aloft, kept-motion cannot justify a recompile either: the score it
861
1081
  // would produce is the degraded one above.
862
1082
  (this.orphans.size > 0 || !changeset.sprites({ type: 'moved' }).length) &&
1083
+ // …and only while the layout it compiled in still stands: a reflow or
1084
+ // a scroll under a leaver still aloft is a real move, not noise
1085
+ this.layoutOf() === this.layoutPrint &&
863
1086
  // …but an EDITED timeline is never noise: a changed tree replays,
864
1087
  // exactly as it would with no orphans aloft (the build-order
865
1088
  // transport's delay nudge, made while a leaver is frozen mid-park)
@@ -1010,6 +1233,7 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
1010
1233
  // arriving, and every one it can see on its way out. A region in that
1011
1234
  // state is not watching a departure, it is inside one — see `park`.
1012
1235
  doomed: !kept.length && !inserted.length && removed.length > 0,
1236
+ maxFrameStep: this.args.maxFrameStep,
1013
1237
  });
1014
1238
 
1015
1239
  if (this.args.quiet) {
@@ -1082,6 +1306,24 @@ export class Choreo extends Component<Signature> implements ChoreoHost {
1082
1306
  `${el.offsetWidth},${el.offsetHeight}`,
1083
1307
  );
1084
1308
  }
1309
+ // A scroll moves every participant in page space and none of them in
1310
+ // the offsets above, so the scroll offset of the scroller the region
1311
+ // sits in is part of where its participants stand: a follow settled into
1312
+ // a pass mid-run is a real move, and the run is recompiled to carry it.
1313
+ // The walk stops at that scroller (the document's own scroll counts only
1314
+ // when no element scroller holds the region), so scrolling an outer
1315
+ // scroller or the page around it does not cancel the region's runs, for
1316
+ // the same reason the offset walk above stops at the region's edge.
1317
+ let sx = 0;
1318
+ let sy = 0;
1319
+ for (let a = root?.parentElement; a; a = a.parentElement) {
1320
+ sx += a.scrollLeft;
1321
+ sy += a.scrollTop;
1322
+ if (a !== document.documentElement && scrolls(a)) {
1323
+ break;
1324
+ }
1325
+ }
1326
+ parts.push(`scroll@${Math.round(sx)},${Math.round(sy)}`);
1085
1327
  return parts.join('|');
1086
1328
  }
1087
1329
 
@@ -103,11 +103,50 @@ export function setBeforeMeasure(fn: () => void) {
103
103
  beforeMeasure = fn;
104
104
  }
105
105
 
106
+ /**
107
+ * SETTLE: state that the pass implies but that is not markup, settled after
108
+ * this pass's mounts and before any region measures. A scroll owner (a list
109
+ * that keeps its scroll at the newest content, or holds a row in place while
110
+ * rows above it change) holds its anchor here: a scroll position is state,
111
+ * so it lands in the same pass as the change that moved it, and every region
112
+ * measures the page as the reader will see it. An element the scroll kept
113
+ * still then reads as not moved and gets no motion; only what really moved
114
+ * on screen does.
115
+ *
116
+ * A settler only runs inside a barrier, and a barrier runs only when a
117
+ * region joins a pass: with no region mounted, every region idle, or motion
118
+ * off, no settler runs. So the owner sets its scroll through its own path as
119
+ * well (on render or on resize, as it would with no Choreo on the page), and
120
+ * the settler only makes that happen before regions measure. State never
121
+ * depends on a settler (ADR 007 §1.1).
122
+ *
123
+ * Settlers run in registration order; one that throws is reported and the
124
+ * pass goes on. Returns the unregister function.
125
+ */
126
+ const settlers = new Set<() => void>();
127
+ export function registerSettle(fn: () => void): () => void {
128
+ settlers.add(fn);
129
+ return () => {
130
+ settlers.delete(fn);
131
+ };
132
+ }
133
+
106
134
  function runBarrier() {
107
135
  booked = false;
108
136
  const hosts = [...intents];
109
137
  intents.clear();
110
138
  beforeMeasure();
139
+ for (const settle of [...settlers]) {
140
+ // a settler is app code: one that throws must not keep the regions that
141
+ // joined this pass from measuring, or they would stay pending for good
142
+ try {
143
+ settle();
144
+ } catch (e) {
145
+ queueMicrotask(() => {
146
+ throw e;
147
+ });
148
+ }
149
+ }
111
150
  const passes: Pass[] = [];
112
151
  for (const host of hosts) {
113
152
  const pass = host.measurePass();
@@ -11,10 +11,12 @@ export type { Arming, ArmingOptions, ArmingRegion } from './arming.ts';
11
11
  export { createArming } from './arming.ts';
12
12
  export { beacon } from './beacon.ts';
13
13
  export type { BeaconRef } from './beacons.ts';
14
+ export { registerBeacon } from './beacons.ts';
14
15
  export type { Changeset } from './changeset.ts';
15
16
  export type { ChoreoContext } from './choreo.gts';
16
17
  export { Choreo } from './choreo.gts';
17
18
  export { easeIn, easeInAndOut, easeOut } from './easings.ts';
19
+ export { registerSettle } from './far.ts';
18
20
  export type { GestureRef } from './gesture.ts';
19
21
  export {
20
22
  type ChoreoHost,
@@ -184,6 +184,13 @@ interface RunOptions {
184
184
  inherit?: Velocities;
185
185
  /** where the prior run's ambient loops stood, to carry them on from */
186
186
  loops?: LoopPhases;
187
+ /**
188
+ * The most animation time, in ms, one painted frame may advance. Unset,
189
+ * a frame advances by the time since the last one (the frame loop caps
190
+ * that at 40 ms). Set, a run behind a long frame slows down rather than
191
+ * skipping: it lands later, and no frame jumps.
192
+ */
193
+ maxFrameStep?: number;
187
194
  /** a camera step landed or the run ended: the frame's new resting state */
188
195
  onCamera?(state: CameraState): void;
189
196
  /**
@@ -396,6 +403,18 @@ interface Track {
396
403
  wire?: SVGPathElement;
397
404
  }
398
405
 
406
+ let warnedPrivateFields = false;
407
+ /** @maxFrameStep reads motion-dom's private animation fields; say once when they are gone */
408
+ function warnPrivateFieldsMoved() {
409
+ if (warnedPrivateFields) {
410
+ return;
411
+ }
412
+ warnedPrivateFields = true;
413
+ console.warn(
414
+ '@cardstack/choreo: motion-dom no longer exposes the animation fields @maxFrameStep reads (_animation, startTime, holdTime); a long frame will skip tweens ahead instead of slowing them',
415
+ );
416
+ }
417
+
399
418
  export class ChoreoRun implements Run {
400
419
  cues: Cue[];
401
420
  velocities: Velocities = new Map();
@@ -454,6 +473,17 @@ export class ChoreoRun implements Run {
454
473
  private ended = false;
455
474
  private resolveFinished!: () => void;
456
475
  private ticking = false;
476
+ /**
477
+ * The next tick is the first since the clock started (or resumed): it
478
+ * paints the run where it stands and advances nothing. The frame loop's
479
+ * delta on that tick is the time since ITS last frame, up to 40 ms, so a
480
+ * run started by a render pass would otherwise open with that whole step
481
+ * on its own clock: every cue the clock starts, gates, or drives frame by
482
+ * frame (a camera, a wire, a later cue's start) would begin that far in.
483
+ */
484
+ private freshClock = false;
485
+ /** the frame timestamp holdBack saw last, while a maxFrameStep applies */
486
+ private lastStamp: number | undefined;
457
487
  private autoTimer: ReturnType<typeof setTimeout> | undefined;
458
488
 
459
489
  constructor(compiled: Compiled, options: RunOptions) {
@@ -895,6 +925,12 @@ export class ChoreoRun implements Run {
895
925
  return;
896
926
  }
897
927
  this.ticking = true;
928
+ this.freshClock = true;
929
+ this.lastStamp = undefined;
930
+ if (this.options.maxFrameStep) {
931
+ // read runs before update: the animations tick after the hold-back
932
+ frame.read(this.holdBack, true);
933
+ }
898
934
  frame.update(this.tick, true);
899
935
  }
900
936
 
@@ -902,15 +938,82 @@ export class ChoreoRun implements Run {
902
938
  if (this.ticking) {
903
939
  this.ticking = false;
904
940
  cancelFrame(this.tick);
941
+ cancelFrame(this.holdBack);
905
942
  }
906
943
  }
907
944
 
945
+ /**
946
+ * maxFrameStep for the animations themselves. The run's own clock is
947
+ * capped in tick; the tweens and springs it started run on motion-dom's
948
+ * frame clock, which takes the real time since their start, so a long
949
+ * frame would still skip that much of them. Before they update, each one
950
+ * driven on the main thread has its start moved on by the time past the
951
+ * cap: it resumes from where the last frame left it, plus one step.
952
+ * Animations on the compositor (WAAPI) kept playing through the long
953
+ * frame and are left alone.
954
+ */
955
+ private holdBack = (data: FrameData) => {
956
+ const step = this.options.maxFrameStep;
957
+ const last = this.lastStamp;
958
+ this.lastStamp = data.timestamp;
959
+ if (!step) {
960
+ return;
961
+ }
962
+ // The first frame: an animation the render pass started takes its start
963
+ // from motion-dom's clock, which holds one time for a whole task, so a
964
+ // pass late in a long render would date it back to the task's start and
965
+ // its first frame would skip that far. Its start moves to this frame.
966
+ // After that: the time past the cap since the last frame.
967
+ for (const t of this.tracks) {
968
+ for (const c of t.controls ?? []) {
969
+ // the wrapper's resolved animation, read without the getter that
970
+ // flushes pending keyframes. These are motion-dom's private fields
971
+ // (VENDORED.md lists them for a bump to check): past the first frame
972
+ // every control has resolved, so a control without them means the
973
+ // fields moved and the cap no longer reaches the tweens.
974
+ const inner = (c as unknown as { _animation?: object })._animation as
975
+ | { holdTime?: number | null; startTime?: number | null }
976
+ | undefined;
977
+ if (
978
+ last !== undefined &&
979
+ (!inner ||
980
+ (!('animation' in inner) &&
981
+ !('startTime' in inner && 'holdTime' in inner)))
982
+ ) {
983
+ warnPrivateFieldsMoved();
984
+ }
985
+ if (
986
+ !inner ||
987
+ 'animation' in inner ||
988
+ typeof inner.startTime !== 'number' ||
989
+ inner.holdTime !== null
990
+ ) {
991
+ continue;
992
+ }
993
+ if (last === undefined) {
994
+ if (data.timestamp - inner.startTime > step) {
995
+ inner.startTime = data.timestamp;
996
+ }
997
+ } else if (data.timestamp - last > step) {
998
+ inner.startTime += data.timestamp - last - step;
999
+ }
1000
+ }
1001
+ }
1002
+ };
1003
+
908
1004
  private tick = (data: FrameData) => {
909
1005
  if (!this.playing || this.cancelled || this.ended) {
910
1006
  this.stopTicking();
911
1007
  return;
912
1008
  }
913
- const next = this.master + data.delta * this.rate;
1009
+ const step = this.options.maxFrameStep;
1010
+ const delta = this.freshClock
1011
+ ? 0
1012
+ : step
1013
+ ? Math.min(data.delta, step)
1014
+ : data.delta;
1015
+ this.freshClock = false;
1016
+ const next = this.master + delta * this.rate;
914
1017
  const gate = this.gates.find(
915
1018
  (g) => !g.opened && g.at >= this.master && g.at <= next,
916
1019
  );
@@ -5,6 +5,12 @@ import {
5
5
  shouldInterruptAfterDeadline,
6
6
  } from 'quickjs-emscripten';
7
7
 
8
+ import {
9
+ describeScriptError,
10
+ REALM_RUNNER_SCRIPT_FILE_NAME,
11
+ wrapRealmRunnerScript,
12
+ } from '@cardstack/runtime-common/realm-runner-script';
13
+
8
14
  import type {
9
15
  RealmRunnerCallMethod,
10
16
  RealmRunnerRequest,
@@ -106,55 +112,6 @@ const BOOTSTRAP = `
106
112
  })();
107
113
  `;
108
114
 
109
- const SCRIPT_FILE_NAME = 'realm-code.js';
110
- // The script runs inside `(async () => {\n … \n})()`, so its first line is
111
- // line 2 of what QuickJS evaluates.
112
- const WRAPPER_LINES = 1;
113
-
114
- // Turns an error from the script itself into a message the model can act on:
115
- // the error, the line of its own script it points at with the lines around
116
- // it, and, for the error that comes up most, what usually causes it. Errors
117
- // from realm calls and the time-limit interrupt keep their own message.
118
- function describeScriptError(dumped: unknown, code: string): string {
119
- let error = (dumped ?? {}) as {
120
- name?: string;
121
- message?: string;
122
- lineNumber?: number;
123
- };
124
- let message = error.message ?? String(dumped);
125
- let name = error.name ?? 'Error';
126
- if (
127
- !['SyntaxError', 'TypeError', 'ReferenceError', 'RangeError'].includes(name)
128
- ) {
129
- return message;
130
- }
131
- let lines = code.split('\n');
132
- let line =
133
- typeof error.lineNumber === 'number'
134
- ? error.lineNumber - WRAPPER_LINES
135
- : undefined;
136
- let parts = [
137
- `${name} in the script${line ? ` at line ${line}` : ''}: ${message}`,
138
- ];
139
- if (line && line >= 1 && line <= lines.length) {
140
- let from = Math.max(1, line - 2);
141
- let to = Math.min(lines.length, line + 1);
142
- let excerpt = [];
143
- for (let n = from; n <= to; n++) {
144
- excerpt.push(
145
- `${n === line ? '>' : ' '} ${n} | ${lines[n - 1].slice(0, 200)}`,
146
- );
147
- }
148
- parts.push(excerpt.join('\n'));
149
- }
150
- if (name === 'SyntaxError' && code.includes('`')) {
151
- parts.push(
152
- "If a file's content is inside a template literal, a backtick or ${ in that content ends the string or is interpolated. Escape them in the content as \\` and \\${, or build the content without a template literal.",
153
- );
154
- }
155
- return parts.join('\n');
156
- }
157
-
158
115
  const METHODS = new Set<RealmRunnerCallMethod>([
159
116
  'fs.readText',
160
117
  'fs.exists',
@@ -273,8 +230,8 @@ worker.onmessage = async (event: MessageEvent<RealmRunnerRequest>) => {
273
230
  context.unwrapResult(context.evalCode(BOOTSTRAP)).dispose();
274
231
 
275
232
  let evaluated = context.evalCode(
276
- `(async () => {\n${request.code}\n})()`,
277
- SCRIPT_FILE_NAME,
233
+ wrapRealmRunnerScript(request.code),
234
+ REALM_RUNNER_SCRIPT_FILE_NAME,
278
235
  );
279
236
  if (evaluated.error) {
280
237
  let dumped = context.dump(evaluated.error);
@@ -0,0 +1,3 @@
1
+ export declare function wrapRealmRunnerScript(code: string): string;
2
+ export declare const REALM_RUNNER_SCRIPT_FILE_NAME = "realm-code.js";
3
+ export declare function describeScriptError(dumped: unknown, code: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardstack/boxel-cli",
3
- "version": "0.11.0-unstable.1",
3
+ "version": "0.11.0-unstable.3",
4
4
  "license": "MIT",
5
5
  "description": "CLI tools for Boxel workspace management",
6
6
  "main": "./dist/index.js",
@@ -1,2 +0,0 @@
1
- import { t as Choreo } from "./choreo-CJfBb_EY.js";
2
- export { Choreo, Choreo as default };
@@ -1,2 +0,0 @@
1
- import { t as Clip } from "./clip-C_pXjVvY.js";
2
- export { Clip };
@@ -1,2 +0,0 @@
1
- import { r as TICK, t as Film } from "./film-D7vtJO5q.js";
2
- export { Film, TICK };
@@ -1,2 +0,0 @@
1
- import { t as Plate } from "./plate-CFvCDv2t.js";
2
- export { Plate };
@@ -1,2 +0,0 @@
1
- import { n as Menu, r as Player, t as Burst } from "./player-DzuYjrTU.js";
2
- export { Burst, Menu, Player };