@cardstack/boxel-cli 0.11.0-unstable.2 → 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 (106) 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-CtezUCip.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/{codeblocks-test-AdolhdCA.js → codeblocks-test-BDD7qK_V.js} +1 -1
  58. package/bundled-test-harness/assets/{compaction-test-DvyAQq56.js → compaction-test-BweolkhG.js} +1 -1
  59. package/bundled-test-harness/assets/{container-DIF_9lZ9.css → container-B5B6u-xm.css} +29 -22
  60. package/bundled-test-harness/assets/{container-DvvqmNpg.js → container-BnJjIM6J.js} +9 -6
  61. package/bundled-test-harness/assets/{create-listing-modal-test-D95JfQab.js → create-listing-modal-test-FvcAKpIa.js} +1 -1
  62. package/bundled-test-harness/assets/{debug-message-test-BcNtj7Wr.js → debug-message-test-CJ18Evt7.js} +1 -1
  63. package/bundled-test-harness/assets/{externals-DCnB4XSS.js → externals-BWjhmCD4.js} +1 -1
  64. package/bundled-test-harness/assets/{fallback-banner-live-delete-test-5lnN1pv2.js → fallback-banner-live-delete-test-C2H3BrT-.js} +1 -1
  65. package/bundled-test-harness/assets/{fallback-banner-test-DFb_m_Kq.js → fallback-banner-test-C10aYjkj.js} +1 -1
  66. package/bundled-test-harness/assets/{fallback-models-test-p3YIwj1L.js → fallback-models-test-BxoPiaGF.js} +1 -1
  67. package/bundled-test-harness/assets/{file-attachment-test-CmyjNWRR.js → file-attachment-test-DeKyzYaz.js} +1 -1
  68. package/bundled-test-harness/assets/{general-test-CX-TFDuX.js → general-test-BewzrpoB.js} +1 -1
  69. package/bundled-test-harness/assets/{index-query-engine-test-CY2tAkot.js → index-query-engine-test-BtI8Y2LR.js} +1 -1
  70. package/bundled-test-harness/assets/{instance-filter-matcher-test-CMBTh8xn.js → instance-filter-matcher-test-BHXHGHjE.js} +1 -1
  71. package/bundled-test-harness/assets/{main-BOZbZVem.js → main-CqAHZ2kK.js} +1 -1
  72. package/bundled-test-harness/assets/{message-tool-test-CfjRSJvt.js → message-tool-test-NMaPJFsd.js} +1 -1
  73. package/bundled-test-harness/assets/{model-cost-tier-test-mZG84Uso.js → model-cost-tier-test-BgbfN2LH.js} +1 -1
  74. package/bundled-test-harness/assets/{operation-lowering-test-CHnjKSJb.js → operation-lowering-test-C4IaOjsA.js} +1 -1
  75. package/bundled-test-harness/assets/{operator-mode-card-chooser-test-KWS8lq16.js → operator-mode-card-chooser-test-Bwe47Rn7.js} +1 -1
  76. package/bundled-test-harness/assets/{operator-mode-links-test-CViFt0Ws.js → operator-mode-links-test-BU4jkC-B.js} +1 -1
  77. package/bundled-test-harness/assets/{operator-mode-test-D0gZM2yw.js → operator-mode-test-Bh0rGM9z.js} +1 -1
  78. package/bundled-test-harness/assets/{operator-mode-ui-test-CEvHJ9DS.js → operator-mode-ui-test-whgUw0ju.js} +1 -1
  79. package/bundled-test-harness/assets/{overlay-menu-items-test-Pw80RXsr.js → overlay-menu-items-test-Cwn1vr8q.js} +1 -1
  80. package/bundled-test-harness/assets/{past-sessions-test-CW0-Bjav.js → past-sessions-test-Bh24N-Kz.js} +1 -1
  81. package/bundled-test-harness/assets/{permission-checking-realm-test-D5uXQypS.js → permission-checking-realm-test-DKnnAy0f.js} +1 -1
  82. package/bundled-test-harness/assets/{realm-config-policy-standing-test-Btatheni.js → realm-config-policy-standing-test-DngdYypq.js} +1 -1
  83. package/bundled-test-harness/assets/{realm-config-routing-rule-edit-test-Dh5zW13o.js → realm-config-routing-rule-edit-test-_rkFU8SN.js} +1 -1
  84. package/bundled-test-harness/assets/{realm-config-settings-test-BJa8gv_C.js → realm-config-settings-test-CR9NH0Zn.js} +1 -1
  85. package/bundled-test-harness/assets/{reasoning-test-2Zp37cQB.js → reasoning-test-0lH10kRl.js} +1 -1
  86. package/bundled-test-harness/assets/{room-message-test-BsrON13e.js → room-message-test-C-PddE4Y.js} +1 -1
  87. package/bundled-test-harness/assets/{run-realm-code-BxktQf6R.js → run-realm-code-C8N7DtN-.js} +1 -1
  88. package/bundled-test-harness/assets/{run-realm-code-test-7grpTos-.js → run-realm-code-test-DBdp147q.js} +1 -1
  89. package/bundled-test-harness/assets/{schema-generation-test-CM9ggQji.js → schema-generation-test-CB1Hb7GQ.js} +1 -1
  90. package/bundled-test-harness/assets/{scrolling-test-BER00htB.js → scrolling-test-3PhVin8h.js} +1 -1
  91. package/bundled-test-harness/assets/{searchable-option-test-Ctf8r3HZ.js → searchable-option-test-BAdOiL4U.js} +1 -1
  92. package/bundled-test-harness/assets/{sending-test-BJGobs2E.js → sending-test-KSmu1tSJ.js} +1 -1
  93. package/bundled-test-harness/assets/{skills-test-GPOsHRN6.js → skills-test-D-dlcFpd.js} +1 -1
  94. package/bundled-test-harness/assets/{store-test-QvvHimzH.js → store-test-CEW7xQjZ.js} +1 -1
  95. package/bundled-test-harness/assets/{tests-DoTEpvcF.js → tests-QYqqa6nv.js} +47 -47
  96. package/bundled-test-harness/assets/{text-input-validator-test-CYYH2N0L.js → text-input-validator-test-D8iNOWep.js} +1 -1
  97. package/bundled-test-harness/assets/{to-device-streaming-test-Ctqa0bVp.js → to-device-streaming-test-trCX-505.js} +1 -1
  98. package/bundled-test-harness/assets/{token-usage-test-BQVbK3X0.js → token-usage-test-C6sULooH.js} +1 -1
  99. package/bundled-test-harness/assets/{tools-DU8Rc15s.js → tools-UJfD-_al.js} +1 -1
  100. package/bundled-test-harness/assets/{tools-test-B13yvEKE.js → tools-test-Brav31AV.js} +1 -1
  101. package/bundled-test-harness/assets/{worker-DHBEBihX.js → worker-9ph9_Lpv.js} +31 -26
  102. package/bundled-test-harness/index.html +8 -8
  103. package/bundled-test-harness/tests/index.html +8 -8
  104. package/bundled-types/host-app/lib/realm-runner/worker.ts +8 -51
  105. package/bundled-types/runtime-common/realm-runner-script.d.ts +3 -0
  106. package/package.json +1 -1
@@ -5,6 +5,7 @@ import { on } from '@ember/modifier';
5
5
  import { fn } from '@ember/helper';
6
6
  import { guidFor } from '@ember/object/internals';
7
7
  import { Collapse } from '../internal/agentic-chat';
8
+ import CheckIcon from '@cardstack/boxel-icons/check';
8
9
  import { Button } from './button';
9
10
  import { Meter } from './meter';
10
11
 
@@ -38,6 +39,9 @@ export interface RecommendationOption {
38
39
 
39
40
  export interface RecommendationSignature {
40
41
  Args: {
42
+ /** 'card' (default): its own raised surface. 'plain': no surface of its
43
+ * own, for a place that already frames it, such as a tool row */
44
+ surface?: 'card' | 'plain';
41
45
  /** what is being decided */
42
46
  question: string;
43
47
  /** the options, best first */
@@ -76,7 +80,9 @@ export class Recommendation extends Component<RecommendationSignature> {
76
80
  return this.args.selectedKey ?? this.innerKey ?? this.options[0]?.key ?? '';
77
81
  }
78
82
  get active(): RecommendationOption | undefined {
79
- return this.options.find((o) => o.key === this.activeKey) ?? this.options[0];
83
+ return (
84
+ this.options.find((o) => o.key === this.activeKey) ?? this.options[0]
85
+ );
80
86
  }
81
87
  get others(): RecommendationOption[] {
82
88
  return this.options.filter((o) => o.key !== this.activeKey);
@@ -113,7 +119,7 @@ export class Recommendation extends Component<RecommendationSignature> {
113
119
 
114
120
  accept = () => {
115
121
  let option = this.active;
116
- if (!option) {
122
+ if (!option || this.accepted) {
117
123
  return;
118
124
  }
119
125
  this.innerAccepted = true;
@@ -125,6 +131,7 @@ export class Recommendation extends Component<RecommendationSignature> {
125
131
  <template>
126
132
  <div
127
133
  class='pretui-reco'
134
+ data-surface={{if @surface @surface 'card'}}
128
135
  data-accepted={{if this.accepted 'true'}}
129
136
  data-test-pretui-recommendation
130
137
  ...attributes
@@ -194,11 +201,17 @@ export class Recommendation extends Component<RecommendationSignature> {
194
201
  {{/if}}
195
202
  <Button
196
203
  @size='s'
197
- @tone={{if this.accepted 'success' 'primary'}}
204
+ @tone={{if this.accepted 'neutral' 'primary'}}
205
+ @appearance={{if this.accepted 'plain' 'accent'}}
198
206
  @disabled={{unless this.active true}}
207
+ aria-disabled={{if this.accepted 'true'}}
199
208
  data-test-pretui-recommendation-accept
200
209
  {{on 'click' this.accept}}
201
- >{{this.acceptLabel}}</Button>
210
+ >{{#if this.accepted}}<CheckIcon
211
+ width='14'
212
+ height='14'
213
+ aria-hidden='true'
214
+ />{{/if}}{{this.acceptLabel}}</Button>
202
215
  </span>
203
216
  </div>
204
217
  </div>
@@ -208,43 +221,60 @@ export class Recommendation extends Component<RecommendationSignature> {
208
221
  .pretui-reco {
209
222
  width: 100%;
210
223
  overflow: hidden;
211
- border-radius: var(--radius-surface, 14px);
212
- background: var(--card);
224
+ border-radius: var(--boxel-border-radius-lg);
225
+ background-color: var(--card);
213
226
  box-shadow: var(
214
227
  --pretui-shadow-card,
215
228
  0 0 0 1px var(--border),
216
- 0 1px 2px rgb(0 0 0 / 0.08)
229
+ var(--shadow-xs)
217
230
  );
218
231
  container-type: inline-size;
219
232
  }
233
+ .pretui-reco[data-surface='plain'] {
234
+ border-radius: 0;
235
+ background-color: transparent;
236
+ box-shadow: none;
237
+ }
238
+ .pretui-reco[data-surface='plain'] .pretui-reco-head {
239
+ padding: 0 0 var(--boxel-sp-xs);
240
+ }
241
+ .pretui-reco[data-surface='plain'] .pretui-reco-foot {
242
+ padding: var(--boxel-sp-xs) 0 var(--boxel-sp-6xs);
243
+ background-color: transparent;
244
+ }
220
245
  .pretui-reco-head {
221
- padding: 12px 14px;
246
+ padding: var(--boxel-sp-sm);
222
247
  }
248
+ /* the card's title: reading size in 600, no tracking */
223
249
  .pretui-reco-question {
224
250
  margin: 0;
225
- font-size: 13px;
251
+ font-size: var(--boxel-body-font-size);
252
+ line-height: var(--boxel-body-line-height);
226
253
  font-weight: 600;
227
- letter-spacing: -0.01em;
254
+ color: var(--foreground);
228
255
  }
256
+ /* the recommendation is answer prose: reading type, full ink, the
257
+ reading measure */
229
258
  .pretui-reco-body {
230
- margin-top: 6px;
231
- min-height: 40px;
232
- max-width: 62ch;
233
- font-size: var(--text-ui-md, 12.5px);
234
- line-height: 1.6;
235
- color: var(--muted-foreground);
259
+ margin-top: var(--boxel-sp-4xs);
260
+ min-height: 2.5rem;
261
+ max-inline-size: 68ch;
262
+ font-size: var(--boxel-body-font-size);
263
+ line-height: var(--boxel-body-line-height);
264
+ color: var(--foreground);
236
265
  }
237
266
  .pretui-reco-drawer {
238
- background: var(--inset, var(--boxel-100));
267
+ background-color: var(--inset);
239
268
  box-shadow: 0 -1px 0 var(--border);
240
- padding: 8px;
269
+ padding: var(--boxel-sp-xs);
241
270
  }
242
271
  .pretui-reco-drawer-cap {
243
- margin: 0 0 4px;
244
- padding: 0 8px;
245
- font-size: 11px;
246
- font-weight: 500;
247
- color: var(--ink-3, var(--boxel-400));
272
+ margin: 0 0 var(--boxel-sp-4xs);
273
+ padding: 0 var(--boxel-sp-xs);
274
+ font-size: var(--boxel-caption-font-size);
275
+ line-height: var(--boxel-caption-line-height);
276
+ font-weight: 400;
277
+ color: var(--subtle-foreground);
248
278
  }
249
279
  .pretui-reco-drawer ul {
250
280
  margin: 0;
@@ -255,20 +285,21 @@ export class Recommendation extends Component<RecommendationSignature> {
255
285
  display: flex;
256
286
  width: 100%;
257
287
  align-items: center;
258
- gap: 10px;
259
- min-height: 34px;
260
- padding: 6px 8px;
288
+ gap: var(--boxel-sp-xs);
289
+ min-height: 2rem;
290
+ padding: var(--boxel-sp-4xs) var(--boxel-sp-xs);
261
291
  border: 0;
262
- border-radius: 8px;
263
- background: none;
264
- font: inherit;
265
- font-size: var(--text-ui-md, 12.5px);
292
+ border-radius: var(--boxel-border-radius-sm);
293
+ background-color: transparent;
294
+ font-family: inherit;
295
+ font-size: var(--boxel-ui-label-font-size);
296
+ line-height: var(--boxel-ui-label-line-height);
266
297
  text-align: left;
267
298
  color: var(--foreground);
268
299
  cursor: pointer;
269
300
  }
270
301
  .pretui-reco-alt:hover {
271
- background: var(--hover, var(--boxel-100));
302
+ background-color: var(--hover);
272
303
  }
273
304
  .pretui-reco-alt:focus-visible {
274
305
  outline: 2px solid var(--ring);
@@ -285,16 +316,16 @@ export class Recommendation extends Component<RecommendationSignature> {
285
316
  display: flex;
286
317
  align-items: center;
287
318
  justify-content: space-between;
288
- gap: 10px;
319
+ gap: var(--boxel-sp-xs);
289
320
  flex-wrap: wrap;
290
- padding: 8px 12px;
291
- background: var(--inset, var(--boxel-100));
321
+ padding: var(--boxel-sp-xs) var(--boxel-sp-sm);
322
+ background-color: var(--inset);
292
323
  box-shadow: 0 -1px 0 var(--border);
293
324
  }
294
325
  .pretui-reco-foot-actions {
295
326
  display: inline-flex;
296
327
  align-items: center;
297
- gap: 8px;
328
+ gap: var(--boxel-sp-xs);
298
329
  margin-left: auto;
299
330
  }
300
331
  .pretui-sr {
@@ -307,7 +338,7 @@ export class Recommendation extends Component<RecommendationSignature> {
307
338
  }
308
339
  @media (any-pointer: coarse) {
309
340
  .pretui-reco-alt {
310
- min-height: 44px;
341
+ min-height: 2.75rem;
311
342
  }
312
343
  }
313
344
  }
@@ -1,65 +1,101 @@
1
1
  // Pretui — ResultCard: the settled outcome of an agent run.
2
2
  import Component from '@glimmer/component';
3
+ import DotsIcon from '@cardstack/boxel-icons/dots';
3
4
  import { on } from '@ember/modifier';
4
5
 
5
6
  export interface ResultCardSignature {
6
- Args: { eyebrow?: string; title: string; lines?: string[]; onMenu?: () => void };
7
+ Args: {
8
+ eyebrow?: string;
9
+ title: string;
10
+ lines?: string[];
11
+ onMenu?: () => void;
12
+ };
7
13
  Element: HTMLDivElement;
8
14
  }
9
15
 
10
16
  // What you approve is card-shaped, never a JSON blob.
11
17
  export class ResultCard extends Component<ResultCardSignature> {
12
18
  menu = () => this.args.onMenu?.();
19
+ /** the menu button's name says whose actions it holds */
20
+ get menuLabel(): string {
21
+ return `More actions for ${this.args.title}`;
22
+ }
13
23
  <template>
14
24
  <div class='pretui-resultcard' data-test-pretui-result-card ...attributes>
15
25
  <div class='pretui-resultcard-head'>
16
26
  {{#if @eyebrow}}<span class='pretui-eyebrow'>{{@eyebrow}}</span>{{/if}}
17
27
  <span class='pretui-resultcard-title'>{{@title}}</span>
18
28
  {{#if @onMenu}}
19
- <button type='button' class='pretui-resultcard-menu' aria-label='More' {{on 'click' this.menu}}>⋯</button>
29
+ <button
30
+ type='button'
31
+ class='pretui-resultcard-menu'
32
+ aria-label={{this.menuLabel}}
33
+ {{on 'click' this.menu}}
34
+ data-test-pretui-result-card-menu
35
+ ><DotsIcon width='14' height='14' aria-hidden='true' /></button>
20
36
  {{/if}}
21
37
  </div>
22
- {{#each @lines as |line|}}<div class='pretui-resultcard-line'>{{line}}</div>{{/each}}
38
+ {{#each @lines as |line|}}<div
39
+ class='pretui-resultcard-line'
40
+ >{{line}}</div>{{/each}}
23
41
  </div>
24
42
  <style scoped>
25
43
  @layer PretComponent {
26
44
  .pretui-resultcard {
27
- background: var(--field, var(--boxel-light));
28
- border-radius: 8px;
45
+ background-color: var(--card);
46
+ color: var(--card-foreground);
47
+ border-radius: var(--boxel-border-radius-sm);
29
48
  box-shadow: var(--pretui-shadow-hairline, 0 0 0 1px var(--border));
30
- padding: 9px 11px;
49
+ padding: var(--boxel-sp-xs) var(--boxel-sp-sm);
31
50
  display: flex;
32
51
  flex-direction: column;
33
- gap: 3px;
52
+ gap: var(--boxel-sp-4xs);
34
53
  }
35
54
  .pretui-resultcard-head {
36
55
  display: flex;
37
56
  align-items: baseline;
38
- gap: 8px;
57
+ gap: var(--boxel-sp-xs);
39
58
  }
40
59
  .pretui-resultcard-title {
41
60
  font-weight: 600;
42
- font-size: var(--text-ui-md, 12.5px);
61
+ font-size: var(--boxel-ui-label-font-size);
62
+ line-height: var(--boxel-ui-label-line-height);
63
+ color: var(--foreground);
43
64
  }
65
+ /* a label, not a kicker: sans caption, no tracking, no caps */
44
66
  .pretui-eyebrow {
45
- font-family: var(--font-mono);
46
- font-size: var(--text-ui-xs, 11px);
47
- font-weight: 500;
48
- letter-spacing: var(--track-eyebrow, 0.08em);
49
- text-transform: uppercase;
50
- color: var(--muted-foreground);
67
+ font-size: var(--boxel-caption-font-size);
68
+ line-height: var(--boxel-caption-line-height);
69
+ font-weight: 400;
70
+ color: var(--subtle-foreground);
51
71
  }
72
+ /* 1.5rem square, the least a pointer target takes */
52
73
  .pretui-resultcard-menu {
53
- margin-left: auto;
54
- color: var(--ink-3, var(--boxel-400));
55
- cursor: pointer;
56
- letter-spacing: 0.1em;
74
+ display: inline-grid;
75
+ place-items: center;
76
+ flex: none;
77
+ align-self: center;
78
+ width: 1.5rem;
79
+ height: 1.5rem;
80
+ margin-inline-start: auto;
81
+ padding: 0;
57
82
  border: 0;
58
- background: none;
59
- font: inherit;
83
+ border-radius: var(--boxel-border-radius-xs);
84
+ background-color: transparent;
85
+ color: var(--subtle-foreground);
86
+ cursor: pointer;
87
+ }
88
+ .pretui-resultcard-menu:hover {
89
+ background-color: var(--hover);
90
+ color: var(--foreground);
91
+ }
92
+ .pretui-resultcard-menu:focus-visible {
93
+ outline: 2px solid var(--ring);
94
+ outline-offset: 1px;
60
95
  }
61
96
  .pretui-resultcard-line {
62
- font-size: var(--text-ui-sm, 11.5px);
97
+ font-size: var(--boxel-ui-label-font-size);
98
+ line-height: var(--boxel-ui-label-line-height);
63
99
  color: var(--muted-foreground);
64
100
  }
65
101
  }
@@ -51,17 +51,19 @@ export class SegmentedControl extends Component<SegmentedControlSignature> {
51
51
  get value() {
52
52
  return this.args.value ?? this.internal;
53
53
  }
54
- pick = (option: SegmentOption) => {
54
+ pick = (option: SegmentOption, event: Event) => {
55
55
  if (this.args.value === undefined) {
56
56
  this.internal = option.value;
57
57
  }
58
58
  emit([this.args.onValueChange, this.args.onChange], option.value);
59
59
  // controlled: the browser already moved the radio; move it back unless the
60
- // owner took the new value
60
+ // owner took the new value. The radios are found inside this control, so
61
+ // the lookup holds in a shadow root or another document too.
61
62
  if (this.args.value !== undefined && this.args.value !== option.value) {
62
- for (let radio of document.getElementsByName(
63
- this.name,
64
- ) as NodeListOf<HTMLInputElement>) {
63
+ let group = (event.target as HTMLElement).closest('[data-pretui-seg]');
64
+ for (let radio of group?.querySelectorAll<HTMLInputElement>(
65
+ '[data-pretui-seg-radio]',
66
+ ) ?? []) {
65
67
  radio.checked = radio.value === this.args.value;
66
68
  }
67
69
  }
@@ -72,6 +74,7 @@ export class SegmentedControl extends Component<SegmentedControlSignature> {
72
74
  class='pretui-seg'
73
75
  role='radiogroup'
74
76
  aria-label={{@label}}
77
+ data-pretui-seg
75
78
  {{slidingHighlight}}
76
79
  data-test-pretui-segmented
77
80
  ...attributes
@@ -89,6 +92,7 @@ export class SegmentedControl extends Component<SegmentedControlSignature> {
89
92
  value={{option.value}}
90
93
  checked={{this.isActive option}}
91
94
  disabled={{@disabled}}
95
+ data-pretui-seg-radio
92
96
  {{on 'change' (fn this.pick option)}}
93
97
  data-test-pretui-segmented-option={{option.value}}
94
98
  />
@@ -118,7 +122,9 @@ export class SegmentedControl extends Component<SegmentedControlSignature> {
118
122
  padding: var(--pretui-seg-inset);
119
123
  background-color: var(--inset);
120
124
  color: var(--foreground);
121
- border-radius: calc(var(--pretui-seg-radius) + var(--pretui-seg-inset));
125
+ border-radius: calc(
126
+ var(--pretui-seg-radius) + var(--pretui-seg-inset)
127
+ );
122
128
  box-shadow: 0 0 0 1px var(--border);
123
129
  }
124
130
  .pretui-seg:has(.pretui-seg-input:disabled) {
@@ -164,7 +170,7 @@ export class SegmentedControl extends Component<SegmentedControlSignature> {
164
170
  }
165
171
  /* the label sits on the pill's --card face */
166
172
  .pretui-seg-item[data-state='active'] {
167
- color: var(--card-foreground);
173
+ color: var(--pretui-seg-active-ink, var(--card-foreground));
168
174
  font-weight: var(--pretui-seg-active-weight);
169
175
  }
170
176
  /* a hidden bold copy under the label holds every segment at its bold
@@ -195,4 +201,3 @@ export class SegmentedControl extends Component<SegmentedControlSignature> {
195
201
  </style>
196
202
  </template>
197
203
  }
198
-
@@ -127,35 +127,36 @@ export class SessionPrep extends Component<SessionPrepSignature> {
127
127
  .pretui-prep {
128
128
  display: flex;
129
129
  flex-direction: column;
130
- gap: var(--space-4, 11px);
131
- padding: var(--space-4, 13px);
132
- border-radius: var(--radius-surface, 14px);
133
- background: var(--card);
130
+ gap: var(--boxel-sp-sm);
131
+ padding: var(--boxel-sp-sm) var(--boxel-sp);
132
+ border-radius: var(--boxel-border-radius-lg);
133
+ background-color: var(--card);
134
134
  box-shadow: var(
135
135
  --pretui-shadow-card,
136
136
  0 0 0 1px var(--border),
137
- 0 1px 2px rgb(0 0 0 / 0.08)
137
+ var(--shadow-xs)
138
138
  );
139
139
  }
140
140
  .pretui-prep-head {
141
141
  display: flex;
142
142
  align-items: center;
143
- gap: 10px;
143
+ gap: var(--boxel-sp-xs);
144
144
  flex-wrap: wrap;
145
145
  }
146
146
  .pretui-prep-title {
147
147
  margin: 0;
148
148
  flex: 1;
149
149
  min-width: 0;
150
- font-size: 13px;
150
+ font-size: var(--boxel-ui-label-font-size);
151
+ line-height: var(--boxel-ui-label-line-height);
151
152
  font-weight: 600;
152
- letter-spacing: -0.01em;
153
+ color: var(--foreground);
153
154
  }
154
155
  .pretui-prep-note {
155
156
  margin: 0;
156
- max-width: 62ch;
157
- font-size: var(--text-ui-sm, 11.5px);
158
- line-height: 1.6;
157
+ max-width: 68ch;
158
+ font-size: var(--boxel-ui-label-font-size);
159
+ line-height: var(--boxel-ui-label-line-height);
159
160
  color: var(--muted-foreground);
160
161
  }
161
162
  }
@@ -190,7 +190,10 @@ export class SlidingHighlight extends Component<SlidingHighlightSignature> {
190
190
  var(--pretui-highlight-y, 0px)
191
191
  );
192
192
  opacity: var(--pretui-highlight-on, 0);
193
- border-radius: var(--pretui-highlight-radius, var(--radius));
193
+ border-radius: var(
194
+ --pretui-highlight-radius,
195
+ var(--boxel-border-radius)
196
+ );
194
197
  pointer-events: none;
195
198
  z-index: 0;
196
199
  transition-property: transform, width, height, opacity;
@@ -205,8 +208,10 @@ export class SlidingHighlight extends Component<SlidingHighlightSignature> {
205
208
  cubic-bezier(0.23, 1, 0.32, 1)
206
209
  );
207
210
  }
208
- /* a strong edge, so the pill reads even when its track blends into
209
- the surface behind it */
211
+ /* the pill is a --card face on its --inset rail (the default
212
+ theme's card is the lighter of the two in light and dark mode);
213
+ a strong edge and a small shadow keep it raised under any theme,
214
+ even when the rail blends into the surface behind it */
210
215
  .pretui-highlight[data-variant='pill'] {
211
216
  background-color: var(--card);
212
217
  box-shadow:
@@ -0,0 +1,132 @@
1
+ // Pretui — SpeakerLabel: who said a message in a room with more than one person.
2
+ import Component from '@glimmer/component';
3
+ import { AgentMark } from './agent-mark';
4
+
5
+ export interface SpeakerLabelSignature {
6
+ Args: {
7
+ /** the speaker: 'Rupert', 'Assistant' */
8
+ name: string;
9
+ /** whose session carried it: an assistant answering Rupert's question
10
+ * in Chris's session is 'Assistant · via Chris' */
11
+ via?: string;
12
+ /** the speaker is the assistant, not a person */
13
+ agent?: boolean;
14
+ /** when, as the caller formats it: '2:04 PM' */
15
+ time?: string;
16
+ /** the assistant is working: its mark animates */
17
+ working?: boolean;
18
+ };
19
+ Blocks: {
20
+ /** the person's own avatar, in place of the initial: the host's profile
21
+ * mark, so a name in the transcript looks like it does everywhere else */
22
+ mark: [];
23
+ };
24
+ Element: HTMLDivElement;
25
+ }
26
+
27
+ /**
28
+ * A name above a message, only where the room has more than one voice. A
29
+ * person gets an initial; the assistant gets its own mark (AgentMark: the
30
+ * host's icon, animated while it works), so the two never read alike.
31
+ */
32
+ export class SpeakerLabel extends Component<SpeakerLabelSignature> {
33
+ get initial(): string {
34
+ return this.args.name.trim()[0] ?? '?';
35
+ }
36
+ <template>
37
+ <div
38
+ class='pretui-speaker'
39
+ data-agent={{if @agent 'true'}}
40
+ data-test-pretui-speaker-label
41
+ ...attributes
42
+ >
43
+ {{#if @agent}}
44
+ <AgentMark class='pretui-speaker-agent' @working={{@working}} />
45
+ {{else if (has-block 'mark')}}
46
+ <span class='pretui-speaker-avatar' aria-hidden='true'>{{yield
47
+ to='mark'
48
+ }}</span>
49
+ {{else}}
50
+ <span
51
+ class='pretui-speaker-mark'
52
+ aria-hidden='true'
53
+ >{{this.initial}}</span>
54
+ {{/if}}
55
+ <span class='pretui-speaker-name'>{{@name}}</span>
56
+ {{#if @via}}<span class='pretui-speaker-via' data-test-pretui-speaker-via>
57
+ via
58
+ {{@via}}</span>{{/if}}
59
+ {{#if @time}}<span class='pretui-speaker-time'>{{@time}}</span>{{/if}}
60
+ </div>
61
+ <style scoped>
62
+ @layer PretComponent {
63
+ .pretui-speaker {
64
+ display: flex;
65
+ align-items: center;
66
+ /* the mark is the thread's glyph column, its words one gap after,
67
+ as every mark in the transcript */
68
+ gap: var(--pretui-toolrow-gap, var(--boxel-sp-xs));
69
+ min-width: 0;
70
+ font-size: var(--boxel-ui-label-font-size);
71
+ line-height: var(--boxel-ui-label-line-height);
72
+ color: var(--muted-foreground);
73
+ }
74
+ /* the host's avatar, held to the mark's circle */
75
+ .pretui-speaker-avatar {
76
+ flex: none;
77
+ display: inline-flex;
78
+ width: var(--pretui-toolrow-disc, 1rem);
79
+ height: var(--pretui-toolrow-disc, 1rem);
80
+ overflow: hidden;
81
+ border-radius: 50%;
82
+ }
83
+ .pretui-speaker-mark {
84
+ flex: none;
85
+ display: grid;
86
+ place-items: center;
87
+ width: var(--pretui-toolrow-disc, 1rem);
88
+ height: var(--pretui-toolrow-disc, 1rem);
89
+ border-radius: 50%;
90
+ background-color: var(--inset);
91
+ box-shadow: inset 0 0 0 1px var(--border);
92
+ /* one initial, not a kicker: caption, set to the circle's height */
93
+ font-size: var(--boxel-caption-font-size);
94
+ line-height: var(--boxel-caption-line-height);
95
+ font-weight: 600;
96
+ text-transform: uppercase;
97
+ color: var(--foreground);
98
+ }
99
+ .pretui-speaker-name {
100
+ font-weight: 600;
101
+ color: var(--foreground);
102
+ white-space: nowrap;
103
+ overflow: hidden;
104
+ text-overflow: ellipsis;
105
+ }
106
+ .pretui-speaker-via,
107
+ .pretui-speaker-time {
108
+ white-space: nowrap;
109
+ }
110
+ /* the assistant's name is one short word and always reads; what
111
+ follows it (a time, or a turn's "Worked for 2m 55s · 6 calls")
112
+ gives way first */
113
+ .pretui-speaker[data-agent] .pretui-speaker-name {
114
+ flex: none;
115
+ }
116
+ .pretui-speaker[data-agent] .pretui-speaker-time {
117
+ min-width: 0;
118
+ overflow: hidden;
119
+ text-overflow: ellipsis;
120
+ }
121
+ .pretui-speaker-via::before,
122
+ .pretui-speaker-time::before {
123
+ content: '·';
124
+ margin-inline-end: var(--boxel-sp-xs);
125
+ }
126
+ .pretui-speaker-time {
127
+ font-variant-numeric: tabular-nums;
128
+ }
129
+ }
130
+ </style>
131
+ </template>
132
+ }