@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,102 @@
1
+ // Pretui — DiffStat: a change's size at a glance: +added −removed and a five-block bar.
2
+ import Component from '@glimmer/component';
3
+
4
+ export interface DiffStatSignature {
5
+ Args: {
6
+ added: number;
7
+ removed: number;
8
+ /** blocks in the bar. Default 5; 0 hides it */
9
+ blocks?: number;
10
+ };
11
+ Element: HTMLSpanElement;
12
+ }
13
+
14
+ /**
15
+ * "+24 −7 ▮▮▮▮▯": the counts, and a bar whose blocks split the change
16
+ * between additions and removals, as a code host's file list draws it.
17
+ */
18
+ export class DiffStat extends Component<DiffStatSignature> {
19
+ get bar(): ('add' | 'del' | 'none')[] {
20
+ let n = this.args.blocks ?? 5;
21
+ let { added, removed } = this.args;
22
+ let total = added + removed;
23
+ // a side with any change gets at least one block; together they never
24
+ // exceed the bar
25
+ let adds = total
26
+ ? Math.max(added ? 1 : 0, Math.round((added / total) * n))
27
+ : 0;
28
+ let dels = total
29
+ ? Math.max(removed ? 1 : 0, Math.round((removed / total) * n))
30
+ : 0;
31
+ while (adds + dels > n) {
32
+ adds > dels ? adds-- : dels--;
33
+ }
34
+ return Array.from({ length: n }, (_u, i) =>
35
+ i < adds ? 'add' : i < adds + dels ? 'del' : 'none',
36
+ );
37
+ }
38
+ get label(): string {
39
+ return `${this.args.added} added, ${this.args.removed} removed`;
40
+ }
41
+ <template>
42
+ <span
43
+ class='pretui-diffstat'
44
+ role='img'
45
+ aria-label={{this.label}}
46
+ data-test-pretui-diff-stat
47
+ ...attributes
48
+ >
49
+ <span
50
+ class='pretui-diffstat-n'
51
+ data-op='add'
52
+ aria-hidden='true'
53
+ >+{{@added}}</span>
54
+ <span
55
+ class='pretui-diffstat-n'
56
+ data-op='del'
57
+ aria-hidden='true'
58
+ >−{{@removed}}</span>
59
+ {{#if this.bar.length}}<span
60
+ class='pretui-diffstat-bar'
61
+ aria-hidden='true'
62
+ >{{#each this.bar as |block|}}<span
63
+ class='pretui-diffstat-block'
64
+ data-op={{block}}
65
+ ></span>{{/each}}</span>{{/if}}
66
+ </span>
67
+ <style scoped>
68
+ @layer PretComponent {
69
+ .pretui-diffstat {
70
+ display: inline-flex;
71
+ align-items: center;
72
+ gap: var(--boxel-sp-3xs);
73
+ font-family: var(--font-mono);
74
+ font-size: var(--boxel-font-size-2xs);
75
+ font-variant-numeric: tabular-nums;
76
+ }
77
+ .pretui-diffstat-n[data-op='add'] {
78
+ color: var(--success-ink);
79
+ }
80
+ .pretui-diffstat-n[data-op='del'] {
81
+ color: var(--destructive-ink);
82
+ }
83
+ .pretui-diffstat-bar {
84
+ display: inline-flex;
85
+ gap: 1px;
86
+ }
87
+ .pretui-diffstat-block {
88
+ width: 0.4375rem;
89
+ height: 0.4375rem;
90
+ border-radius: var(--boxel-border-radius-2xs);
91
+ background-color: var(--border-strong);
92
+ }
93
+ .pretui-diffstat-block[data-op='add'] {
94
+ background-color: var(--success);
95
+ }
96
+ .pretui-diffstat-block[data-op='del'] {
97
+ background-color: var(--destructive);
98
+ }
99
+ }
100
+ </style>
101
+ </template>
102
+ }
@@ -1,7 +1,9 @@
1
1
  // Pretui — JumpChip: scroll orientation for a growing stream, plus the scrollEdge measurement modifier it pairs with.
2
2
  import Component from '@glimmer/component';
3
+ import ArrowDownIcon from '@cardstack/boxel-icons/arrow-down';
3
4
  import { on } from '@ember/modifier';
4
5
  import { modifier } from 'ember-modifier';
6
+ import { VisuallyHidden } from './visually-hidden';
5
7
 
6
8
  // ── scrollEdge ───────────────────────────────────────────────────────────
7
9
  // The measurement JumpChip needs, and the reason the chip is not merely
@@ -38,7 +40,13 @@ export const scrollEdge = modifier(
38
40
  // ── JumpChip ─────────────────────────────────────────────────────────────
39
41
  // Scroll orientation for a stream that grows while you are reading it. Two
40
42
  // jobs in one control, distinguished by whether anything arrived: "3 new
41
- // messages" when it did, "Back to bottom" when it did not.
43
+ // messages" when it did, "Back to bottom" when it did not. The news is also
44
+ // said once through a polite status region, since a chip that appears at
45
+ // the edge of the frame is easy to miss and invisible to a screen reader.
46
+ //
47
+ // A reader who takes the jump from the keyboard keeps their focus: when the
48
+ // caller hides the chip in answer, it stays reachable (not inert) until
49
+ // focus leaves it, so focus never drops to the page body.
42
50
  //
43
51
  // Presentational by contract — the caller owns the scroller and decides when
44
52
  // the chip is warranted (`scrollEdge` above is the measurement, offered
@@ -51,8 +59,10 @@ export interface JumpChipSignature {
51
59
  /** whether the chip is offered at all */
52
60
  visible?: boolean;
53
61
  /**
54
- * items that arrived while the reader was away. Non-zero switches the
55
- * chip to its "new message" voice and shows the count.
62
+ * messages that arrived while the reader was away: count messages, not
63
+ * transcript rows (a tool call or a system line is not a message).
64
+ * Non-zero switches the chip to its "new messages" voice and shows the
65
+ * count.
56
66
  */
57
67
  count?: number;
58
68
  /** override the derived label entirely */
@@ -65,6 +75,35 @@ export interface JumpChipSignature {
65
75
  Element: HTMLDivElement;
66
76
  }
67
77
 
78
+ const PLURAL = new Intl.PluralRules('en');
79
+ const COUNT = new Intl.NumberFormat('en');
80
+
81
+ /** "1 new message", "3 new messages", "1,204 new messages" */
82
+ function newMessages(count: number): string {
83
+ let noun = PLURAL.select(count) === 'one' ? 'message' : 'messages';
84
+ return `${COUNT.format(count)} new ${noun}`;
85
+ }
86
+
87
+ // A hidden chip is inert, except while it holds keyboard focus: then inert
88
+ // waits for focus to leave, because putting it on the focused button would
89
+ // drop focus to the page body. Pointer focus does not hold it (the reader's
90
+ // next action is not on the chip).
91
+ const inertWhileHidden = modifier((el: HTMLElement, [visible]: [boolean]) => {
92
+ if (visible) {
93
+ el.inert = false;
94
+ return;
95
+ }
96
+ if (!el.querySelector(':focus-visible')) {
97
+ el.inert = true;
98
+ return;
99
+ }
100
+ let release = () => {
101
+ el.inert = true;
102
+ };
103
+ el.addEventListener('focusout', release, { once: true });
104
+ return () => el.removeEventListener('focusout', release);
105
+ });
106
+
68
107
  export class JumpChip extends Component<JumpChipSignature> {
69
108
  get count(): number {
70
109
  return this.args.count ?? 0;
@@ -83,10 +122,15 @@ export class JumpChip extends Component<JumpChipSignature> {
83
122
  return this.args.label;
84
123
  }
85
124
  if (this.isNews) {
86
- return this.count === 1 ? '1 new message' : this.count + ' new messages';
125
+ return newMessages(this.count);
87
126
  }
88
127
  return this.direction === 'up' ? 'Back to top' : 'Back to bottom';
89
128
  }
129
+ /** what the status region says: the news while the chip offers it,
130
+ * nothing otherwise */
131
+ get announcement(): string {
132
+ return this.visible && this.isNews ? this.label : '';
133
+ }
90
134
 
91
135
  jump = () => this.args.onJump?.();
92
136
 
@@ -96,29 +140,26 @@ export class JumpChip extends Component<JumpChipSignature> {
96
140
  data-visible={{if this.visible 'true'}}
97
141
  data-direction={{this.direction}}
98
142
  data-news={{if this.isNews 'true'}}
99
- inert={{unless this.visible true}}
143
+ {{inertWhileHidden this.visible}}
100
144
  data-test-pretui-jump-chip
101
145
  ...attributes
102
146
  >
103
147
  <button
104
148
  type='button'
105
149
  class='pretui-jump-btn'
106
- data-test-pretui-jump-chip-button
107
150
  {{on 'click' this.jump}}
151
+ data-test-pretui-jump-chip-button
108
152
  >
109
- <svg viewBox='0 0 24 24' aria-hidden='true' focusable='false'>
110
- <path
111
- d='M12 5v14M6 13l6 6 6-6'
112
- fill='none'
113
- stroke='currentColor'
114
- stroke-width='2.2'
115
- stroke-linecap='round'
116
- stroke-linejoin='round'
117
- />
118
- </svg>
153
+ <ArrowDownIcon width='13' height='13' aria-hidden='true' />
119
154
  <span>{{this.label}}</span>
120
155
  </button>
121
156
  </div>
157
+ {{! outside the chip, which is inert while hidden: an inert region is
158
+ not in the accessibility tree, so its change would not be heard }}
159
+ <VisuallyHidden
160
+ role='status'
161
+ data-test-pretui-jump-chip-status
162
+ >{{this.announcement}}</VisuallyHidden>
122
163
 
123
164
  <style scoped>
124
165
  @layer PretComponent {
@@ -127,7 +168,7 @@ export class JumpChip extends Component<JumpChipSignature> {
127
168
  .pretui-jump {
128
169
  position: absolute;
129
170
  inset-inline: 0;
130
- bottom: var(--pretui-jump-offset, 12px);
171
+ bottom: var(--pretui-jump-offset, var(--boxel-sp-sm));
131
172
  display: flex;
132
173
  justify-content: center;
133
174
  pointer-events: none;
@@ -135,41 +176,47 @@ export class JumpChip extends Component<JumpChipSignature> {
135
176
  }
136
177
  .pretui-jump[data-direction='up'] {
137
178
  bottom: auto;
138
- top: var(--pretui-jump-offset, 12px);
179
+ top: var(--pretui-jump-offset, var(--boxel-sp-sm));
139
180
  }
140
181
  .pretui-jump-btn {
141
182
  display: inline-flex;
142
183
  align-items: center;
143
- gap: 7px;
144
- min-height: 30px;
145
- padding: 0 13px;
184
+ gap: var(--boxel-sp-3xs);
185
+ min-height: 1.75rem;
186
+ padding: 0 var(--boxel-sp-sm);
146
187
  border: 0;
147
- border-radius: 999px;
148
- background: var(--card);
188
+ border-radius: var(--boxel-border-radius-sm);
189
+ background-color: var(--card);
149
190
  box-shadow: var(
150
191
  --pretui-shadow-overlay,
151
192
  0 0 0 1px var(--border),
152
- 0 8px 28px rgb(0 0 0 / 0.18)
193
+ var(--shadow-lg)
153
194
  );
154
- font: inherit;
155
- font-size: var(--text-ui-sm, 11.5px);
156
- font-weight: 500;
195
+ font-family: inherit;
196
+ font-size: var(--boxel-ui-label-font-size);
197
+ line-height: var(--boxel-ui-label-line-height);
198
+ font-weight: 600;
199
+ font-variant-numeric: tabular-nums;
200
+ white-space: nowrap;
157
201
  color: var(--foreground);
158
202
  cursor: pointer;
159
203
  pointer-events: auto;
160
204
  opacity: 0;
161
- transform: translateY(8px) scale(0.96);
205
+ transform: translateY(0.5rem) scale(0.96);
162
206
  transition:
163
207
  opacity 180ms ease-out,
164
208
  transform 220ms
165
209
  var(--pretui-ease-enter, cubic-bezier(0.22, 0.61, 0.25, 1));
166
210
  }
167
- .pretui-jump[data-visible] .pretui-jump-btn {
211
+ /* shown while offered, and while it holds the keyboard focus a jump
212
+ left on it, so a focused control never vanishes under the reader */
213
+ .pretui-jump[data-visible] .pretui-jump-btn,
214
+ .pretui-jump:focus-within .pretui-jump-btn {
168
215
  opacity: 1;
169
216
  transform: none;
170
217
  }
171
218
  /* hidden means hidden: no pointer events, no tab stop, no hit area */
172
- .pretui-jump:not([data-visible]) .pretui-jump-btn {
219
+ .pretui-jump:not([data-visible]):not(:focus-within) .pretui-jump-btn {
173
220
  pointer-events: none;
174
221
  visibility: hidden;
175
222
  transition:
@@ -177,45 +224,31 @@ export class JumpChip extends Component<JumpChipSignature> {
177
224
  transform 220ms ease-out,
178
225
  visibility 0s linear 220ms;
179
226
  }
227
+ /* --hover is translucent, so it lays over the card face rather than
228
+ replacing it: the chip floats over the transcript */
180
229
  .pretui-jump-btn:hover {
181
- background: var(--hover, var(--boxel-100));
230
+ background-image: linear-gradient(var(--hover), var(--hover));
182
231
  }
183
232
  .pretui-jump-btn:focus-visible {
184
233
  outline: 2px solid var(--ring);
185
234
  outline-offset: 2px;
186
235
  }
187
236
  .pretui-jump-btn > svg {
188
- width: 13px;
189
- height: 13px;
190
237
  flex: none;
191
- color: var(--ink-3, var(--boxel-400));
238
+ color: var(--subtle-foreground);
192
239
  }
193
240
  .pretui-jump[data-direction='up'] .pretui-jump-btn > svg {
194
241
  transform: rotate(180deg);
195
242
  }
196
- /* news is never colour alone — the count is in the label */
197
- .pretui-jump[data-news] .pretui-jump-btn {
198
- background: color-mix(
199
- in oklch,
200
- var(--primary) 12%,
201
- var(--card)
202
- );
203
- box-shadow:
204
- 0 0 0 1px
205
- color-mix(in oklch, var(--primary) 45%, var(--border)),
206
- 0 8px 28px rgb(0 0 0 / 0.18);
207
- }
243
+ /* news is never colour alone — the count is in the label; the
244
+ arrow (the mark) takes the live state, the chip stays the card */
208
245
  .pretui-jump[data-news] .pretui-jump-btn > svg {
209
- color: color-mix(
210
- in oklch,
211
- var(--foreground) 18%,
212
- var(--primary)
213
- );
246
+ color: var(--primary-ink);
214
247
  }
215
248
  @media (any-pointer: coarse) {
216
249
  .pretui-jump-btn {
217
- min-height: 44px;
218
- padding: 0 18px;
250
+ min-height: 2.75rem;
251
+ padding: 0 var(--boxel-sp);
219
252
  }
220
253
  }
221
254
  @media (prefers-reduced-motion: reduce) {