@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
@@ -0,0 +1,438 @@
1
+ // Pretui — the streaming projection behind MarkdownStream: a fence-aware
2
+ // block splitter, an incremental projector, and a tail healer. Pure
3
+ // functions, no DOM, no markdown engine (Law 9): turning a block's markdown
4
+ // into HTML is the caller's renderer's job. What this module decides is
5
+ // where blocks begin and end, which of them are finished, and how to make a
6
+ // half-written tail render sensibly. The split is line-based, so the kit
7
+ // carries no markdown parser.
8
+
9
+ export type BlockKind = 'text' | 'code' | 'directive';
10
+ export type BlockMode = 'frozen' | 'live';
11
+ /** `markdown` is CommonMark/GFM; `bfm` is Boxel Flavored Markdown, which
12
+ * adds `$`/`$$` math, `:card[…]`/`:file[…]` references (inline and, with
13
+ * `::`, block) and treats Mermaid fences as diagrams. */
14
+ export type MarkdownFlavor = 'markdown' | 'bfm';
15
+
16
+ export interface ProjectedBlock {
17
+ /** stable across updates: the block's start offset plus its kind */
18
+ key: string;
19
+ /** offset of the block's first character in the full text */
20
+ start: number;
21
+ kind: BlockKind;
22
+ /** the block's markdown exactly as written */
23
+ raw: string;
24
+ mode: BlockMode;
25
+ /** code blocks: the source between the fences */
26
+ source?: string;
27
+ /** code blocks: the info string's first word */
28
+ language?: string;
29
+ /** code blocks: whether the closing fence has arrived */
30
+ complete?: boolean;
31
+ /** directive blocks */
32
+ directive?: Directive;
33
+ /** bfm text blocks: a `$$` display-math block whose closing `$$` has not
34
+ * arrived. It must not reach the renderer half-written. */
35
+ pendingMath?: boolean;
36
+ }
37
+
38
+ export interface Directive {
39
+ name: string;
40
+ /** the text inside the brackets before the first `|` */
41
+ body: string;
42
+ /** the `|`-separated arguments after it, trimmed */
43
+ args: string[];
44
+ /** false while the closing `]` has not arrived (bfm references only) */
45
+ complete: boolean;
46
+ }
47
+
48
+ export interface Projection {
49
+ text: string;
50
+ blocks: ProjectedBlock[];
51
+ }
52
+
53
+ const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
54
+ const DIRECTIVE = /^::([A-Za-z][\w-]*)\[(.*)\]\s*$/;
55
+ const HEADING = /^ {0,3}#{1,6}(\s|$)/;
56
+ const LIST_ITEM = /^ {0,3}([-*+]|\d{1,9}[.)])\s/;
57
+ const CONTINUATION = /^( {2,}|\t)\S/;
58
+ const MATH_OPEN = /^ {0,3}\$\$/;
59
+ const PARTIAL_REF = /^::(card|file)\[([^\]\n]*)$/;
60
+
61
+ /** Parse `::name[body | arg | arg]`. */
62
+ export function parseDirective(line: string): Directive | undefined {
63
+ let match = DIRECTIVE.exec(line);
64
+ if (!match) {
65
+ return undefined;
66
+ }
67
+ let [body = '', ...args] = match[2]!.split('|').map((part) => part.trim());
68
+ return { name: match[1]!, body, args, complete: true };
69
+ }
70
+
71
+ interface Line {
72
+ text: string;
73
+ start: number;
74
+ }
75
+
76
+ function linesOf(text: string, offset: number): Line[] {
77
+ let lines: Line[] = [];
78
+ let start = 0;
79
+ while (start <= text.length) {
80
+ let end = text.indexOf('\n', start);
81
+ if (end === -1) {
82
+ if (start < text.length) {
83
+ lines.push({ text: text.slice(start), start: offset + start });
84
+ }
85
+ break;
86
+ }
87
+ lines.push({ text: text.slice(start, end), start: offset + start });
88
+ start = end + 1;
89
+ }
90
+ return lines;
91
+ }
92
+
93
+ function isBlank(line: string): boolean {
94
+ return line.trim() === '';
95
+ }
96
+
97
+ /**
98
+ * Split `text` (which starts at `offset` in the full message) into blocks.
99
+ * Every block is returned `frozen`; the projector decides which is live.
100
+ */
101
+ export function splitBlocks(
102
+ text: string,
103
+ offset = 0,
104
+ flavor: MarkdownFlavor = 'markdown',
105
+ ): ProjectedBlock[] {
106
+ let bfm = flavor === 'bfm';
107
+ let lines = linesOf(text, offset);
108
+ let blocks: ProjectedBlock[] = [];
109
+ let i = 0;
110
+ let sliceRaw = (from: number, to: number) =>
111
+ text.slice(from - offset, to - offset);
112
+
113
+ while (i < lines.length) {
114
+ let line = lines[i]!;
115
+ if (isBlank(line.text)) {
116
+ i++;
117
+ continue;
118
+ }
119
+
120
+ let fence = FENCE_OPEN.exec(line.text);
121
+ if (fence) {
122
+ let marker = fence[1]!;
123
+ let close = new RegExp(
124
+ `^ {0,3}${marker[0] === '`' ? '`' : '~'}{${marker.length},}\\s*$`,
125
+ );
126
+ let j = i + 1;
127
+ while (j < lines.length && !close.test(lines[j]!.text)) {
128
+ j++;
129
+ }
130
+ let complete = j < lines.length;
131
+ let bodyLines = lines.slice(i + 1, j).map((l) => l.text);
132
+ let last = complete ? lines[j]! : lines[lines.length - 1]!;
133
+ let end = last.start + last.text.length;
134
+ // an open fence's source keeps its trailing newline state as typed, so
135
+ // the code grows by exactly the appended characters
136
+ let source = complete
137
+ ? bodyLines.join('\n')
138
+ : text.slice(
139
+ Math.min(line.start + line.text.length + 1, offset + text.length) -
140
+ offset,
141
+ );
142
+ blocks.push({
143
+ key: `${line.start}:code`,
144
+ start: line.start,
145
+ kind: 'code',
146
+ raw: sliceRaw(line.start, end),
147
+ mode: 'frozen',
148
+ source,
149
+ language: fence[2]!.trim().split(/\s+/, 1)[0] || undefined,
150
+ complete,
151
+ });
152
+ i = complete ? j + 1 : lines.length;
153
+ continue;
154
+ }
155
+
156
+ // bfm display math: `$$` to the line ending in `$$`, blank lines included,
157
+ // so a formula is never cut into separately rendered pieces
158
+ if (bfm && MATH_OPEN.test(line.text)) {
159
+ let single =
160
+ line.text.trim().length > 2 && line.text.trim().endsWith('$$');
161
+ let j = i;
162
+ if (!single) {
163
+ j = i + 1;
164
+ while (j < lines.length && !lines[j]!.text.trim().endsWith('$$')) {
165
+ j++;
166
+ }
167
+ }
168
+ let closed = j < lines.length;
169
+ let last = closed ? lines[j]! : lines[lines.length - 1]!;
170
+ blocks.push({
171
+ key: `${line.start}:text`,
172
+ start: line.start,
173
+ kind: 'text',
174
+ raw: sliceRaw(line.start, last.start + last.text.length),
175
+ mode: 'frozen',
176
+ pendingMath: !closed,
177
+ });
178
+ i = closed ? j + 1 : lines.length;
179
+ continue;
180
+ }
181
+
182
+ // a bfm block reference still arriving: the last line, no closing `]`
183
+ let partial =
184
+ bfm && i === lines.length - 1 ? PARTIAL_REF.exec(line.text) : null;
185
+ if (partial) {
186
+ let [body = '', ...args] = partial[2]!.split('|').map((p) => p.trim());
187
+ blocks.push({
188
+ key: `${line.start}:directive`,
189
+ start: line.start,
190
+ kind: 'directive',
191
+ raw: line.text,
192
+ mode: 'frozen',
193
+ directive: { name: partial[1]!, body, args, complete: false },
194
+ });
195
+ i++;
196
+ continue;
197
+ }
198
+
199
+ let directive = parseDirective(line.text);
200
+ if (directive) {
201
+ blocks.push({
202
+ key: `${line.start}:directive`,
203
+ start: line.start,
204
+ kind: 'directive',
205
+ raw: line.text,
206
+ mode: 'frozen',
207
+ directive,
208
+ });
209
+ i++;
210
+ continue;
211
+ }
212
+
213
+ if (HEADING.test(line.text)) {
214
+ blocks.push({
215
+ key: `${line.start}:text`,
216
+ start: line.start,
217
+ kind: 'text',
218
+ raw: line.text,
219
+ mode: 'frozen',
220
+ });
221
+ i++;
222
+ continue;
223
+ }
224
+
225
+ // A text block runs to a blank line, a fence, a directive or a heading.
226
+ // A list keeps going across a blank line when the next line is another
227
+ // item or an indented continuation, so a loose list stays one block.
228
+ let isList = LIST_ITEM.test(line.text);
229
+ let j = i + 1;
230
+ while (j < lines.length) {
231
+ let next = lines[j]!.text;
232
+ if (
233
+ FENCE_OPEN.test(next) ||
234
+ DIRECTIVE.test(next) ||
235
+ HEADING.test(next) ||
236
+ (bfm && (MATH_OPEN.test(next) || PARTIAL_REF.test(next)))
237
+ ) {
238
+ break;
239
+ }
240
+ if (isBlank(next)) {
241
+ let after = lines[j + 1]?.text;
242
+ if (
243
+ isList &&
244
+ after !== undefined &&
245
+ (LIST_ITEM.test(after) || CONTINUATION.test(after))
246
+ ) {
247
+ j++;
248
+ continue;
249
+ }
250
+ break;
251
+ }
252
+ j++;
253
+ }
254
+ let last = lines[j - 1]!;
255
+ blocks.push({
256
+ key: `${line.start}:text`,
257
+ start: line.start,
258
+ kind: 'text',
259
+ raw: sliceRaw(line.start, last.start + last.text.length),
260
+ mode: 'frozen',
261
+ });
262
+ i = j;
263
+ }
264
+ return blocks;
265
+ }
266
+
267
+ function markLive(
268
+ blocks: ProjectedBlock[],
269
+ streaming: boolean,
270
+ ): ProjectedBlock[] {
271
+ if (!streaming || blocks.length === 0) {
272
+ return blocks;
273
+ }
274
+ let last = blocks[blocks.length - 1]!;
275
+ // an open fence grows by appending; a closed one is finished
276
+ if (last.kind === 'code') {
277
+ return blocks;
278
+ }
279
+ return [...blocks.slice(0, -1), { ...last, mode: 'live' }];
280
+ }
281
+
282
+ /**
283
+ * Project `text` into blocks, reusing `previous` where the new text extends
284
+ * it: only the region from the previous last block onwards is re-split, so
285
+ * an update costs the tail, not the whole message.
286
+ */
287
+ export function project(
288
+ previous: Projection | undefined,
289
+ text: string,
290
+ streaming: boolean,
291
+ flavor: MarkdownFlavor = 'markdown',
292
+ ): Projection {
293
+ if (
294
+ previous &&
295
+ previous.blocks.length > 0 &&
296
+ text.startsWith(previous.text)
297
+ ) {
298
+ let tailStart = previous.blocks[previous.blocks.length - 1]!.start;
299
+ let kept = previous.blocks
300
+ .slice(0, -1)
301
+ .map((block) =>
302
+ block.mode === 'frozen' ? block : { ...block, mode: 'frozen' as const },
303
+ );
304
+ let tail = splitBlocks(text.slice(tailStart), tailStart, flavor);
305
+ return { text, blocks: markLive([...kept, ...tail], streaming) };
306
+ }
307
+ return { text, blocks: markLive(splitBlocks(text, 0, flavor), streaming) };
308
+ }
309
+
310
+ // ── healing ──────────────────────────────────────────────────────────────
311
+
312
+ const INLINE_CODE = /`[^`\n]*`/g;
313
+
314
+ /**
315
+ * Make a half-written block render sensibly: close unbalanced inline code
316
+ * and emphasis, and reduce an unterminated link to its text. Only ever
317
+ * applied to the live tail; the raw text is what freezes.
318
+ */
319
+ export function heal(src: string): string {
320
+ let out = src;
321
+
322
+ // an image or link whose URL is still arriving: keep the text, drop the rest
323
+ out = out.replace(/!\[([^\]\n]*)\]\([^)\n]*$/, '$1');
324
+ out = out.replace(/\[([^\]\n]*)\]\([^)\n]*$/, '$1');
325
+ out = out.replace(/!?\[([^\]\n]*)$/, '$1');
326
+
327
+ // an odd number of single backticks: the last code span is still open
328
+ let ticks = (out.match(/`/g) ?? []).length;
329
+ if (ticks % 2 === 1) {
330
+ return `${out}\``;
331
+ }
332
+
333
+ // emphasis markers outside code spans, closed innermost (latest) first
334
+ let bare = out.replace(INLINE_CODE, (span) => ' '.repeat(span.length));
335
+ let open: { marker: string; at: number }[] = [];
336
+ for (let marker of ['**', '__', '~~']) {
337
+ let positions: number[] = [];
338
+ let index = bare.indexOf(marker);
339
+ while (index !== -1) {
340
+ positions.push(index);
341
+ index = bare.indexOf(marker, index + marker.length);
342
+ }
343
+ if (positions.length % 2 === 1) {
344
+ open.push({ marker, at: positions[positions.length - 1]! });
345
+ }
346
+ }
347
+ // a single `*` that is not a list bullet and not part of `**`
348
+ let singles: number[] = [];
349
+ let withoutDoubles = bare.replace(/\*\*/g, ' ');
350
+ for (let i = 0; i < withoutDoubles.length; i++) {
351
+ if (withoutDoubles[i] !== '*') {
352
+ continue;
353
+ }
354
+ let lineStart = withoutDoubles.lastIndexOf('\n', i - 1) + 1;
355
+ let isBullet =
356
+ withoutDoubles.slice(lineStart, i).trim() === '' &&
357
+ withoutDoubles[i + 1] === ' ';
358
+ if (!isBullet) {
359
+ singles.push(i);
360
+ }
361
+ }
362
+ if (singles.length % 2 === 1) {
363
+ open.push({ marker: '*', at: singles[singles.length - 1]! });
364
+ }
365
+ open.sort((a, b) => b.at - a.at);
366
+ return out + open.map((o) => o.marker).join('');
367
+ }
368
+
369
+ /**
370
+ * BFM's additions to healing, applied before `heal`: an inline `:card[` or
371
+ * `:file[` reference whose `]` has not arrived renders as nothing yet (a
372
+ * half-written URL is neither text nor a card), and a lone unclosed `$` is
373
+ * escaped so it renders as a dollar sign rather than an open formula.
374
+ */
375
+ export function healBfm(src: string): string {
376
+ let out = src.replace(/(^|[^:]):(?:card|file)\[[^\]\n]*$/, '$1');
377
+ let bare = out.replace(INLINE_CODE, (span) => ' '.repeat(span.length));
378
+ let singles: number[] = [];
379
+ for (let i = 0; i < bare.length; i++) {
380
+ if (bare[i] !== '$' || bare[i - 1] === '\\') {
381
+ continue;
382
+ }
383
+ if (bare[i + 1] === '$' || bare[i - 1] === '$') {
384
+ continue;
385
+ }
386
+ singles.push(i);
387
+ }
388
+ if (singles.length % 2 === 1) {
389
+ let at = singles[singles.length - 1]!;
390
+ out = `${out.slice(0, at)}\\${out.slice(at)}`;
391
+ }
392
+ return out;
393
+ }
394
+
395
+ const DEFINITION = /^ {0,3}\[(\^?)[^\]\n]+\]:[ \t]*\S.*$/gm;
396
+
397
+ /**
398
+ * Every link-reference and footnote definition in the document, one per
399
+ * line. A block rendered on its own cannot see definitions that live in
400
+ * another block, so the renderer receives them as context.
401
+ */
402
+ export function collectDefinitions(text: string): string {
403
+ return (text.match(DEFINITION) ?? []).join('\n');
404
+ }
405
+
406
+ // ── links ────────────────────────────────────────────────────────────────
407
+
408
+ const PATH_LIKE =
409
+ /^(?:\.{0,2}\/)?[\w@.-]+(?:\/[\w@.-]+)*\.[A-Za-z0-9]{1,8}(?::\d+(?:-\d+)?)?(?:#L\d+(?:-L?\d+)?)?$/;
410
+
411
+ /** Whether inline code text looks like a file path worth resolving:
412
+ * `src/a.gts`, `a.gts:42`, `card.json#L4-L9`. */
413
+ export function isPathLike(text: string): boolean {
414
+ let trimmed = text.trim();
415
+ return trimmed.length > 2 && trimmed.length < 300 && PATH_LIKE.test(trimmed);
416
+ }
417
+
418
+ /** Whether an href leaves the app (and so needs no lookup). */
419
+ export function isExternalHref(href: string): boolean {
420
+ return /^(https?:|mailto:)/i.test(href);
421
+ }
422
+
423
+ // ── default rendering ────────────────────────────────────────────────────
424
+
425
+ function escapeHtml(text: string): string {
426
+ return text
427
+ .replace(/&/g, '&amp;')
428
+ .replace(/</g, '&lt;')
429
+ .replace(/>/g, '&gt;')
430
+ .replace(/"/g, '&quot;')
431
+ .replace(/'/g, '&#39;');
432
+ }
433
+
434
+ /** The renderer used when the caller supplies none: the text escaped, one
435
+ * paragraph per block, line breaks kept. Safe by construction. */
436
+ export function renderPlain(src: string): string {
437
+ return `<p>${escapeHtml(src).replace(/\n/g, '<br>')}</p>`;
438
+ }
@@ -0,0 +1,79 @@
1
+ // Pretui — the shared, bottom-first mount queue for heavy transcript bodies.
2
+ //
3
+ // A transcript renders its rows top to bottom, but the reader is at the
4
+ // bottom. Mounting every open tool body in document order makes the newest
5
+ // one (the one the reader is looking at) interactive last. So bodies are
6
+ // queued and mounted newest first: the queue is a stack, flushed after two
7
+ // animation frames (the first lets the rows themselves paint), one body per
8
+ // frame so no single frame pays for all of them.
9
+ //
10
+ // Cancelling marks an entry inactive instead of splicing it out, so a row
11
+ // that closes, or unmounts under virtualization, costs O(1) and the flush
12
+ // simply skips it.
13
+ //
14
+ // Timer-free: requestAnimationFrame only. Where it does not exist (the
15
+ // prerenderer), each mount runs on a microtask instead, so the prerendered
16
+ // output carries the mounted body and nothing waits on a frame that never
17
+ // comes. A microtask rather than a synchronous call, because the caller is
18
+ // usually mid-render and must not write tracked state there.
19
+
20
+ interface Entry {
21
+ active: boolean;
22
+ run: () => void;
23
+ }
24
+
25
+ const stack: Entry[] = [];
26
+ let frame: number | undefined;
27
+
28
+ function hasFrames(): boolean {
29
+ return typeof requestAnimationFrame === 'function';
30
+ }
31
+
32
+ function flush() {
33
+ while (stack.length > 0) {
34
+ let entry = stack.pop()!;
35
+ if (entry.active) {
36
+ entry.active = false;
37
+ frame = stack.length > 0 ? requestAnimationFrame(flush) : undefined;
38
+ entry.run();
39
+ return;
40
+ }
41
+ }
42
+ frame = undefined;
43
+ }
44
+
45
+ function schedule() {
46
+ if (frame !== undefined) {
47
+ return;
48
+ }
49
+ frame = requestAnimationFrame(() => {
50
+ frame = requestAnimationFrame(flush);
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Queue `run` to mount a heavy body. Returns a cancel function; calling it
56
+ * after `run` has happened is harmless.
57
+ */
58
+ export function scheduleMount(run: () => void): () => void {
59
+ let entry: Entry = { active: true, run };
60
+ if (!hasFrames()) {
61
+ queueMicrotask(() => {
62
+ if (entry.active) {
63
+ entry.active = false;
64
+ entry.run();
65
+ }
66
+ });
67
+ } else {
68
+ stack.push(entry);
69
+ schedule();
70
+ }
71
+ return () => {
72
+ entry.active = false;
73
+ };
74
+ }
75
+
76
+ /** How many mounts are still waiting. For tests and diagnostics. */
77
+ export function pendingMounts(): number {
78
+ return stack.filter((entry) => entry.active).length;
79
+ }
@@ -0,0 +1,168 @@
1
+ // Pretui — the phrase grammar for tool calls: what a run of calls DID, in
2
+ // words, rather than which tools it used. "Read 3 files, searched 2
3
+ // queries, and edited 1 file" instead of "Used 6 tools · Read, Search,
4
+ // Patch". While the run is still going, the phrase for the newest call reads
5
+ // in the present tense ("editing 1 file"); the rest are past.
6
+
7
+ export type ToolKind =
8
+ | 'read'
9
+ | 'search'
10
+ | 'write'
11
+ | 'command'
12
+ | 'web'
13
+ | 'agent'
14
+ | 'other';
15
+
16
+ export interface ToolPhrase {
17
+ /** present tense, while the call is running: 'Reading' */
18
+ active: string;
19
+ /** past tense, once it settled: 'Read' */
20
+ done: string;
21
+ /** the noun for one, and for several: 'file', 'files' */
22
+ one: string;
23
+ many: string;
24
+ }
25
+
26
+ export const KIND_PHRASES: Record<ToolKind, ToolPhrase> = {
27
+ read: { active: 'Reading', done: 'Read', one: 'file', many: 'files' },
28
+ search: {
29
+ active: 'Searching',
30
+ done: 'Searched',
31
+ one: 'query',
32
+ many: 'queries',
33
+ },
34
+ write: { active: 'Editing', done: 'Edited', one: 'file', many: 'files' },
35
+ command: { active: 'Running', done: 'Ran', one: 'command', many: 'commands' },
36
+ web: {
37
+ active: 'Researching',
38
+ done: 'Researched',
39
+ one: 'source',
40
+ many: 'sources',
41
+ },
42
+ agent: {
43
+ active: 'Delegating to',
44
+ done: 'Delegated to',
45
+ one: 'agent',
46
+ many: 'agents',
47
+ },
48
+ other: { active: 'Using', done: 'Used', one: 'tool', many: 'tools' },
49
+ };
50
+
51
+ export interface PhraseItem {
52
+ /** stable: identifies the phrase across updates */
53
+ key: string;
54
+ /** the verb in the right tense, lower-case unless it opens the receipt */
55
+ verb: string;
56
+ count: number;
57
+ noun: string;
58
+ /** present tense: this is what the run is doing now */
59
+ active: boolean;
60
+ /** the whole phrase as text */
61
+ text: string;
62
+ }
63
+
64
+ function phraseKey(phrase: ToolPhrase): string {
65
+ return `${phrase.done}|${phrase.one}`;
66
+ }
67
+
68
+ function lowerFirst(text: string): string {
69
+ return text ? text[0]!.toLowerCase() + text.slice(1) : text;
70
+ }
71
+
72
+ function upperFirst(text: string): string {
73
+ return text ? text[0]!.toUpperCase() + text.slice(1) : text;
74
+ }
75
+
76
+ /**
77
+ * Group `items` by phrase, in order of first appearance, counting each.
78
+ * `activeIndex` is the index of the call still running, if any: its phrase
79
+ * reads in the present tense.
80
+ */
81
+ export function phraseRun<T>(
82
+ items: T[],
83
+ phraseOf: (item: T) => ToolPhrase,
84
+ activeIndex?: number,
85
+ ): PhraseItem[] {
86
+ let order: string[] = [];
87
+ let groups = new Map<string, { phrase: ToolPhrase; count: number }>();
88
+ items.forEach((item) => {
89
+ let phrase = phraseOf(item);
90
+ let key = phraseKey(phrase);
91
+ let group = groups.get(key);
92
+ if (group) {
93
+ group.count++;
94
+ } else {
95
+ groups.set(key, { phrase, count: 1 });
96
+ order.push(key);
97
+ }
98
+ });
99
+ let activeKey =
100
+ activeIndex !== undefined && items[activeIndex] !== undefined
101
+ ? phraseKey(phraseOf(items[activeIndex]!))
102
+ : undefined;
103
+ return order.map((key, index) => {
104
+ let { phrase, count } = groups.get(key)!;
105
+ let active = key === activeKey;
106
+ let verb = active ? phrase.active : phrase.done;
107
+ verb = index === 0 ? upperFirst(verb) : lowerFirst(verb);
108
+ let noun = count === 1 ? phrase.one : phrase.many;
109
+ return { key, verb, count, noun, active, text: `${verb} ${count} ${noun}` };
110
+ });
111
+ }
112
+
113
+ // English, like every verb and noun around it: a list joined in the
114
+ // browser's locale would read "Read 3 files und edited 1 file". Change this
115
+ // together with the phrases when they are localised.
116
+ const LIST_LOCALE = 'en';
117
+
118
+ function formatter(): Intl.ListFormat | undefined {
119
+ return typeof Intl !== 'undefined' && 'ListFormat' in Intl
120
+ ? new Intl.ListFormat(LIST_LOCALE, { style: 'long', type: 'conjunction' })
121
+ : undefined;
122
+ }
123
+
124
+ /** The phrases as one list, in English. */
125
+ export function joinPhrases(texts: string[]): string {
126
+ if (texts.length < 2) {
127
+ return texts[0] ?? '';
128
+ }
129
+ let format = formatter();
130
+ if (format) {
131
+ return format.format(texts);
132
+ }
133
+ return `${texts.slice(0, -1).join(', ')} and ${texts[texts.length - 1]}`;
134
+ }
135
+
136
+ /**
137
+ * The literal separators the list puts between `count` items, so a
138
+ * template can lay the items out as components (a rolling count each) and
139
+ * still read as one grammatical list. separators[i] goes before item i; the
140
+ * first is ''.
141
+ */
142
+ export function listSeparators(count: number): string[] {
143
+ if (count <= 1) {
144
+ return count === 1 ? [''] : [];
145
+ }
146
+ let format = formatter();
147
+ let placeholders = Array.from({ length: count }, (_, i) => `\u0000${i}`);
148
+ if (!format || typeof format.formatToParts !== 'function') {
149
+ return placeholders.map((_, i) =>
150
+ i === 0 ? '' : i === count - 1 ? ' and ' : ', ',
151
+ );
152
+ }
153
+ let separators: string[] = [''];
154
+ let pending = '';
155
+ let seen = 0;
156
+ for (let part of format.formatToParts(placeholders)) {
157
+ if (part.type === 'element') {
158
+ if (seen > 0) {
159
+ separators.push(pending);
160
+ }
161
+ seen++;
162
+ pending = '';
163
+ } else {
164
+ pending += part.value;
165
+ }
166
+ }
167
+ return separators;
168
+ }
@@ -14,7 +14,8 @@
14
14
  "lint:usage-proof": "node scripts/usage-proof.mjs"
15
15
  },
16
16
  "dependencies": {
17
- "ember-modifier": "^4.1.0"
17
+ "ember-modifier": "^4.1.0",
18
+ "glimmer-motion": "workspace:*"
18
19
  },
19
20
  "devDependencies": {
20
21
  "@babel/plugin-proposal-decorators": "catalog:",
@@ -8,7 +8,7 @@ import { H as templateFactory } from "./index-DaCZcoCY-CgzbilMy.js";
8
8
  import "./component-BJO47HTh.js";
9
9
  import { t as Component } from "./dist-CkA1rgqO.js";
10
10
  import "./template-factory-ds9s0u8l.js";
11
- import { t as OperatorModeContainer } from "./container-DvvqmNpg.js";
11
+ import { t as OperatorModeContainer } from "./container-BnJjIM6J.js";
12
12
  import { d as click, i as findAll, o as waitFor } from "./dist-BOAdzfkP.js";
13
13
  import { t as require_qunit } from "./qunit-C-Foo4Nd.js";
14
14
  import { F as setupLocalIndexing, M as setupCardLogs, P as setupIntegrationTestRealm, lt as setupRenderingTest, ot as setupOperatorModeStateCleanup, t as setupMockMatrix } from "./mock-matrix-rxYRI9xw.js";