@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.
- package/bundled-realms/pretui/components/agent-mark.gts +98 -0
- package/bundled-realms/pretui/components/agent-question.gts +340 -37
- package/bundled-realms/pretui/components/answer-footer.gts +249 -0
- package/bundled-realms/pretui/components/card-embed.gts +523 -0
- package/bundled-realms/pretui/components/card-pills.gts +725 -0
- package/bundled-realms/pretui/components/code-block.gts +87 -45
- package/bundled-realms/pretui/components/collapsed-turn.gts +133 -12
- package/bundled-realms/pretui/components/compaction-chip.gts +81 -67
- package/bundled-realms/pretui/components/composer-cap.gts +516 -0
- package/bundled-realms/pretui/components/context-ring.gts +108 -0
- package/bundled-realms/pretui/components/diff-block.gts +1002 -31
- package/bundled-realms/pretui/components/diff-stat.gts +102 -0
- package/bundled-realms/pretui/components/jump-chip.gts +86 -53
- package/bundled-realms/pretui/components/markdown-stream.gts +799 -0
- package/bundled-realms/pretui/components/markdown-surface.gts +71 -0
- package/bundled-realms/pretui/components/plan-mark.gts +147 -0
- package/bundled-realms/pretui/components/prose.gts +185 -8
- package/bundled-realms/pretui/components/prose.md +6 -8
- package/bundled-realms/pretui/components/quick-look.gts +377 -0
- package/bundled-realms/pretui/components/reactions.gts +136 -0
- package/bundled-realms/pretui/components/recommendation.gts +67 -36
- package/bundled-realms/pretui/components/result-card.gts +58 -22
- package/bundled-realms/pretui/components/segmented-control.gts +13 -8
- package/bundled-realms/pretui/components/session-prep.gts +12 -11
- package/bundled-realms/pretui/components/sliding-highlight.gts +8 -3
- package/bundled-realms/pretui/components/speaker-label.gts +132 -0
- package/bundled-realms/pretui/components/state-glyph.gts +121 -0
- package/bundled-realms/pretui/components/step-list.gts +62 -60
- package/bundled-realms/pretui/components/stick-to-bottom.gts +1150 -0
- package/bundled-realms/pretui/components/system-line.gts +143 -0
- package/bundled-realms/pretui/components/task-checklist.gts +184 -0
- package/bundled-realms/pretui/components/thinking.gts +63 -94
- package/bundled-realms/pretui/components/tool-change-table.gts +285 -0
- package/bundled-realms/pretui/components/tool-gate.gts +462 -0
- package/bundled-realms/pretui/components/tool-group.gts +920 -0
- package/bundled-realms/pretui/components/tool-icon.ts +64 -0
- package/bundled-realms/pretui/components/tool-meta.gts +82 -0
- package/bundled-realms/pretui/components/tool-plan.gts +197 -0
- package/bundled-realms/pretui/components/tool-row.gts +2087 -0
- package/bundled-realms/pretui/components/turn-changes.gts +227 -0
- package/bundled-realms/pretui/components/user-bubble.gts +575 -0
- package/bundled-realms/pretui/components/work-item.gts +185 -48
- package/bundled-realms/pretui/docs/scroll-anchoring-protocol.md +136 -0
- package/bundled-realms/pretui/internal/markdown-projection.ts +438 -0
- package/bundled-realms/pretui/internal/mount-queue.ts +79 -0
- package/bundled-realms/pretui/internal/tool-phrases.ts +168 -0
- package/bundled-realms/pretui/package.json +2 -1
- package/bundled-test-harness/assets/{ai-module-creation-test-beRNwH-e.js → ai-module-creation-test-B6FQKqIl.js} +1 -1
- package/bundled-test-harness/assets/{app-CtezUCip.js → app-KpPqdyX3.js} +4 -4
- package/bundled-test-harness/assets/{binary-upload-dedupe-test-bI8T0QLl.js → binary-upload-dedupe-test-DN00y1tM.js} +1 -1
- package/bundled-test-harness/assets/{can-invoke-test-DVaU_yRT.js → can-invoke-test-Bl-EUFM3.js} +1 -1
- package/bundled-test-harness/assets/{card-chooser-paging-test-DnxMnXKR.js → card-chooser-paging-test-BNuZRHX3.js} +1 -1
- package/bundled-test-harness/assets/{card-chooser-test-DUSnLr-u.js → card-chooser-test-wKfPOOT4.js} +1 -1
- package/bundled-test-harness/assets/{card-def-field-def-relationships-test-GBUr3tFK.js → card-def-field-def-relationships-test-CduQ6zan.js} +1 -1
- package/bundled-test-harness/assets/{card-delete-test-CQveO1VC.js → card-delete-test-DKxolj5o.js} +1 -1
- package/bundled-test-harness/assets/{check-correctness-test-D2M2p66N.js → check-correctness-test-B-kgHztp.js} +1 -1
- package/bundled-test-harness/assets/{codeblocks-test-AdolhdCA.js → codeblocks-test-BDD7qK_V.js} +1 -1
- package/bundled-test-harness/assets/{compaction-test-DvyAQq56.js → compaction-test-BweolkhG.js} +1 -1
- package/bundled-test-harness/assets/{container-DIF_9lZ9.css → container-B5B6u-xm.css} +29 -22
- package/bundled-test-harness/assets/{container-DvvqmNpg.js → container-BnJjIM6J.js} +9 -6
- package/bundled-test-harness/assets/{create-listing-modal-test-D95JfQab.js → create-listing-modal-test-FvcAKpIa.js} +1 -1
- package/bundled-test-harness/assets/{debug-message-test-BcNtj7Wr.js → debug-message-test-CJ18Evt7.js} +1 -1
- package/bundled-test-harness/assets/{externals-DCnB4XSS.js → externals-BWjhmCD4.js} +1 -1
- package/bundled-test-harness/assets/{fallback-banner-live-delete-test-5lnN1pv2.js → fallback-banner-live-delete-test-C2H3BrT-.js} +1 -1
- package/bundled-test-harness/assets/{fallback-banner-test-DFb_m_Kq.js → fallback-banner-test-C10aYjkj.js} +1 -1
- package/bundled-test-harness/assets/{fallback-models-test-p3YIwj1L.js → fallback-models-test-BxoPiaGF.js} +1 -1
- package/bundled-test-harness/assets/{file-attachment-test-CmyjNWRR.js → file-attachment-test-DeKyzYaz.js} +1 -1
- package/bundled-test-harness/assets/{general-test-CX-TFDuX.js → general-test-BewzrpoB.js} +1 -1
- package/bundled-test-harness/assets/{index-query-engine-test-CY2tAkot.js → index-query-engine-test-BtI8Y2LR.js} +1 -1
- package/bundled-test-harness/assets/{instance-filter-matcher-test-CMBTh8xn.js → instance-filter-matcher-test-BHXHGHjE.js} +1 -1
- package/bundled-test-harness/assets/{main-BOZbZVem.js → main-CqAHZ2kK.js} +1 -1
- package/bundled-test-harness/assets/{message-tool-test-CfjRSJvt.js → message-tool-test-NMaPJFsd.js} +1 -1
- package/bundled-test-harness/assets/{model-cost-tier-test-mZG84Uso.js → model-cost-tier-test-BgbfN2LH.js} +1 -1
- package/bundled-test-harness/assets/{operation-lowering-test-CHnjKSJb.js → operation-lowering-test-C4IaOjsA.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-card-chooser-test-KWS8lq16.js → operator-mode-card-chooser-test-Bwe47Rn7.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-links-test-CViFt0Ws.js → operator-mode-links-test-BU4jkC-B.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-test-D0gZM2yw.js → operator-mode-test-Bh0rGM9z.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-ui-test-CEvHJ9DS.js → operator-mode-ui-test-whgUw0ju.js} +1 -1
- package/bundled-test-harness/assets/{overlay-menu-items-test-Pw80RXsr.js → overlay-menu-items-test-Cwn1vr8q.js} +1 -1
- package/bundled-test-harness/assets/{past-sessions-test-CW0-Bjav.js → past-sessions-test-Bh24N-Kz.js} +1 -1
- package/bundled-test-harness/assets/{permission-checking-realm-test-D5uXQypS.js → permission-checking-realm-test-DKnnAy0f.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-policy-standing-test-Btatheni.js → realm-config-policy-standing-test-DngdYypq.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-routing-rule-edit-test-Dh5zW13o.js → realm-config-routing-rule-edit-test-_rkFU8SN.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-settings-test-BJa8gv_C.js → realm-config-settings-test-CR9NH0Zn.js} +1 -1
- package/bundled-test-harness/assets/{reasoning-test-2Zp37cQB.js → reasoning-test-0lH10kRl.js} +1 -1
- package/bundled-test-harness/assets/{room-message-test-BsrON13e.js → room-message-test-C-PddE4Y.js} +1 -1
- package/bundled-test-harness/assets/{run-realm-code-BxktQf6R.js → run-realm-code-C8N7DtN-.js} +1 -1
- package/bundled-test-harness/assets/{run-realm-code-test-7grpTos-.js → run-realm-code-test-DBdp147q.js} +1 -1
- package/bundled-test-harness/assets/{schema-generation-test-CM9ggQji.js → schema-generation-test-CB1Hb7GQ.js} +1 -1
- package/bundled-test-harness/assets/{scrolling-test-BER00htB.js → scrolling-test-3PhVin8h.js} +1 -1
- package/bundled-test-harness/assets/{searchable-option-test-Ctf8r3HZ.js → searchable-option-test-BAdOiL4U.js} +1 -1
- package/bundled-test-harness/assets/{sending-test-BJGobs2E.js → sending-test-KSmu1tSJ.js} +1 -1
- package/bundled-test-harness/assets/{skills-test-GPOsHRN6.js → skills-test-D-dlcFpd.js} +1 -1
- package/bundled-test-harness/assets/{store-test-QvvHimzH.js → store-test-CEW7xQjZ.js} +1 -1
- package/bundled-test-harness/assets/{tests-DoTEpvcF.js → tests-QYqqa6nv.js} +47 -47
- package/bundled-test-harness/assets/{text-input-validator-test-CYYH2N0L.js → text-input-validator-test-D8iNOWep.js} +1 -1
- package/bundled-test-harness/assets/{to-device-streaming-test-Ctqa0bVp.js → to-device-streaming-test-trCX-505.js} +1 -1
- package/bundled-test-harness/assets/{token-usage-test-BQVbK3X0.js → token-usage-test-C6sULooH.js} +1 -1
- package/bundled-test-harness/assets/{tools-DU8Rc15s.js → tools-UJfD-_al.js} +1 -1
- package/bundled-test-harness/assets/{tools-test-B13yvEKE.js → tools-test-Brav31AV.js} +1 -1
- package/bundled-test-harness/assets/{worker-DHBEBihX.js → worker-9ph9_Lpv.js} +31 -26
- package/bundled-test-harness/index.html +8 -8
- package/bundled-test-harness/tests/index.html +8 -8
- package/bundled-types/host-app/lib/realm-runner/worker.ts +8 -51
- package/bundled-types/runtime-common/realm-runner-script.d.ts +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
// Pretui — ComposerCap: the dark cap on the composer's top edge. One status row, what the agent does now; the messages queued behind it; then the box.
|
|
2
|
+
import Component from '@glimmer/component';
|
|
3
|
+
import { hash } from '@ember/helper';
|
|
4
|
+
import { on } from '@ember/modifier';
|
|
5
|
+
import type { ComponentLike } from '@glint/template';
|
|
6
|
+
import ArrowDownIcon from '@cardstack/boxel-icons/arrow-down';
|
|
7
|
+
import ChevronDownIcon from '@cardstack/boxel-icons/chevron-down';
|
|
8
|
+
import ChevronUpIcon from '@cardstack/boxel-icons/chevron-up';
|
|
9
|
+
import ListIcon from '@cardstack/boxel-icons/list';
|
|
10
|
+
import PencilIcon from '@cardstack/boxel-icons/pencil';
|
|
11
|
+
import PlayerStopIcon from '@cardstack/boxel-icons/player-stop';
|
|
12
|
+
import RotateCcwIcon from '@cardstack/boxel-icons/rotate-ccw';
|
|
13
|
+
import XIcon from '@cardstack/boxel-icons/x';
|
|
14
|
+
import { AgentMark } from './agent-mark';
|
|
15
|
+
|
|
16
|
+
export type CapTone = 'working' | 'waiting' | 'problem' | 'done' | 'idle';
|
|
17
|
+
export type CapButtonKind = 'primary' | 'secondary' | 'quiet' | 'icon';
|
|
18
|
+
export type CapGlyph =
|
|
19
|
+
| 'stop'
|
|
20
|
+
| 'retry'
|
|
21
|
+
| 'jump'
|
|
22
|
+
| 'edit'
|
|
23
|
+
| 'remove'
|
|
24
|
+
| 'collapse'
|
|
25
|
+
| 'expand';
|
|
26
|
+
|
|
27
|
+
export interface CapButtonSignature {
|
|
28
|
+
Args: {
|
|
29
|
+
/** primary is the moment's one action, in solid --primary. Default 'secondary' */
|
|
30
|
+
kind?: CapButtonKind;
|
|
31
|
+
/** a glyph before the words, or alone for kind 'icon' */
|
|
32
|
+
glyph?: CapGlyph;
|
|
33
|
+
/** the click is running: the button holds its size and greys */
|
|
34
|
+
busy?: boolean;
|
|
35
|
+
};
|
|
36
|
+
Blocks: { default: [] };
|
|
37
|
+
Element: HTMLButtonElement;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** A button sized for the cap's row: 1.75rem in the status row, 1.5rem in a queued row. */
|
|
41
|
+
export class CapButton extends Component<CapButtonSignature> {
|
|
42
|
+
<template>
|
|
43
|
+
<button
|
|
44
|
+
type='button'
|
|
45
|
+
class='pretui-cap-button'
|
|
46
|
+
data-kind={{if @kind @kind 'secondary'}}
|
|
47
|
+
data-busy={{if @busy 'true'}}
|
|
48
|
+
aria-busy={{if @busy 'true'}}
|
|
49
|
+
...attributes
|
|
50
|
+
>{{#if @glyph}}<CapGlyphIcon @glyph={{@glyph}} />{{/if}}{{yield}}</button>
|
|
51
|
+
</template>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type CapIcon = ComponentLike<{ Element: SVGSVGElement }>;
|
|
55
|
+
|
|
56
|
+
/** each glyph is a boxel-icons mark, so the cap draws its icons the way
|
|
57
|
+
* the rest of the kit does and a theme recolours them all */
|
|
58
|
+
const CAP_ICONS: Record<CapGlyph, CapIcon> = {
|
|
59
|
+
stop: PlayerStopIcon as CapIcon,
|
|
60
|
+
retry: RotateCcwIcon as CapIcon,
|
|
61
|
+
jump: ArrowDownIcon as CapIcon,
|
|
62
|
+
edit: PencilIcon as CapIcon,
|
|
63
|
+
remove: XIcon as CapIcon,
|
|
64
|
+
collapse: ChevronUpIcon as CapIcon,
|
|
65
|
+
expand: ChevronDownIcon as CapIcon,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
class CapGlyphIcon extends Component<{ Args: { glyph: CapGlyph } }> {
|
|
69
|
+
get icon(): CapIcon {
|
|
70
|
+
return CAP_ICONS[this.args.glyph];
|
|
71
|
+
}
|
|
72
|
+
<template>
|
|
73
|
+
<this.icon
|
|
74
|
+
class='pretui-cap-glyph'
|
|
75
|
+
data-glyph={{@glyph}}
|
|
76
|
+
width='12'
|
|
77
|
+
height='12'
|
|
78
|
+
aria-hidden='true'
|
|
79
|
+
/>
|
|
80
|
+
</template>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface CapStatusSignature {
|
|
84
|
+
Args: {
|
|
85
|
+
/** what kind of moment: working animates the agent's mark; waiting is the
|
|
86
|
+
attention dot; problem the destructive dot; done the success dot;
|
|
87
|
+
idle the still mark, dimmed. Default 'working' */
|
|
88
|
+
tone?: CapTone;
|
|
89
|
+
/** the status in a few words: "Generating results", "Run 3 tool calls" */
|
|
90
|
+
title: string;
|
|
91
|
+
/** what qualifies it, quieter: "3 calls", "1 of 3" */
|
|
92
|
+
detail?: string;
|
|
93
|
+
/** three breathing dots after the title, for a reply still arriving */
|
|
94
|
+
dots?: boolean;
|
|
95
|
+
/** let the words wrap: an instruction ("You need a minimum of…") */
|
|
96
|
+
wrap?: boolean;
|
|
97
|
+
};
|
|
98
|
+
Blocks: { default: [] };
|
|
99
|
+
Element: HTMLDivElement;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The cap's one status row. Its actions are the block, right-aligned. */
|
|
103
|
+
export class CapStatus extends Component<CapStatusSignature> {
|
|
104
|
+
get tone(): CapTone {
|
|
105
|
+
return this.args.tone ?? 'working';
|
|
106
|
+
}
|
|
107
|
+
get showsMark() {
|
|
108
|
+
return this.tone === 'working' || this.tone === 'idle';
|
|
109
|
+
}
|
|
110
|
+
get working() {
|
|
111
|
+
return this.tone === 'working';
|
|
112
|
+
}
|
|
113
|
+
<template>
|
|
114
|
+
<div
|
|
115
|
+
class='pretui-cap-row pretui-cap-status'
|
|
116
|
+
data-tone={{this.tone}}
|
|
117
|
+
data-wrap={{if @wrap 'true'}}
|
|
118
|
+
data-test-pretui-cap-status={{this.tone}}
|
|
119
|
+
...attributes
|
|
120
|
+
>
|
|
121
|
+
<span class='pretui-cap-lead' aria-hidden='true'>
|
|
122
|
+
{{#if this.showsMark}}
|
|
123
|
+
<AgentMark @working={{this.working}} @size={{18}} />
|
|
124
|
+
{{else}}
|
|
125
|
+
<span class='pretui-cap-dot'></span>
|
|
126
|
+
{{/if}}
|
|
127
|
+
</span>
|
|
128
|
+
{{! the status region holds the words only, so a change announces the
|
|
129
|
+
status and not the row's buttons }}
|
|
130
|
+
<span
|
|
131
|
+
class='pretui-cap-text'
|
|
132
|
+
role='status'
|
|
133
|
+
aria-atomic='true'
|
|
134
|
+
data-test-pretui-cap-status-text
|
|
135
|
+
><span class='pretui-cap-title'>{{@title}}</span>{{#if @dots}}<span
|
|
136
|
+
class='pretui-cap-dots'
|
|
137
|
+
aria-hidden='true'
|
|
138
|
+
><span>.</span><span>.</span><span>.</span></span>{{/if}}{{#if
|
|
139
|
+
@detail
|
|
140
|
+
}}<span class='pretui-cap-detail'> · {{@detail}}</span>{{/if}}</span>
|
|
141
|
+
<span class='pretui-cap-actions'>{{yield}}</span>
|
|
142
|
+
</div>
|
|
143
|
+
</template>
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface CapQueuedSignature {
|
|
147
|
+
Args: {
|
|
148
|
+
/** where it stands in line: "Next", "Then" */
|
|
149
|
+
order?: string;
|
|
150
|
+
text: string;
|
|
151
|
+
/** pull it back into the box to change it */
|
|
152
|
+
onEdit?: () => void;
|
|
153
|
+
/** take it out of the queue */
|
|
154
|
+
onRemove?: () => void;
|
|
155
|
+
};
|
|
156
|
+
Element: HTMLDivElement;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** A message waiting behind the running turn, in send order under the status. */
|
|
160
|
+
export class CapQueued extends Component<CapQueuedSignature> {
|
|
161
|
+
edit = () => this.args.onEdit?.();
|
|
162
|
+
remove = () => this.args.onRemove?.();
|
|
163
|
+
<template>
|
|
164
|
+
<div
|
|
165
|
+
class='pretui-cap-row pretui-cap-queued'
|
|
166
|
+
data-test-pretui-cap-queued
|
|
167
|
+
...attributes
|
|
168
|
+
>
|
|
169
|
+
<span class='pretui-cap-lead' aria-hidden='true'><ListIcon
|
|
170
|
+
class='pretui-cap-glyph'
|
|
171
|
+
width='13'
|
|
172
|
+
height='13'
|
|
173
|
+
/></span>
|
|
174
|
+
<span class='pretui-cap-text'>{{#if @order}}<span
|
|
175
|
+
class='pretui-cap-detail'
|
|
176
|
+
>{{@order}} · </span>{{/if}}{{@text}}</span>
|
|
177
|
+
<span class='pretui-cap-actions'>
|
|
178
|
+
{{#if @onEdit}}
|
|
179
|
+
<CapButton
|
|
180
|
+
@kind='icon'
|
|
181
|
+
@glyph='edit'
|
|
182
|
+
aria-label='Edit queued message'
|
|
183
|
+
{{on 'click' this.edit}}
|
|
184
|
+
data-test-pretui-cap-queued-edit
|
|
185
|
+
/>
|
|
186
|
+
{{/if}}
|
|
187
|
+
{{#if @onRemove}}
|
|
188
|
+
<CapButton
|
|
189
|
+
@kind='icon'
|
|
190
|
+
@glyph='remove'
|
|
191
|
+
aria-label='Remove queued message'
|
|
192
|
+
{{on 'click' this.remove}}
|
|
193
|
+
data-test-pretui-cap-queued-remove
|
|
194
|
+
/>
|
|
195
|
+
{{/if}}
|
|
196
|
+
</span>
|
|
197
|
+
</div>
|
|
198
|
+
</template>
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface CapSectionSignature {
|
|
202
|
+
Blocks: { default: [] };
|
|
203
|
+
Element: HTMLDivElement;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Room under the status for what belongs to it: a script's plan. */
|
|
207
|
+
const CapSection = <template>
|
|
208
|
+
<div class='pretui-cap-section' ...attributes>{{yield}}</div>
|
|
209
|
+
</template> satisfies import('@ember/component/template-only').TemplateOnlyComponent<CapSectionSignature>;
|
|
210
|
+
|
|
211
|
+
export interface ComposerCapSignature {
|
|
212
|
+
Args: {
|
|
213
|
+
/** the group's accessible name. Default 'Assistant status' */
|
|
214
|
+
label?: string;
|
|
215
|
+
};
|
|
216
|
+
Blocks: {
|
|
217
|
+
default: [
|
|
218
|
+
{
|
|
219
|
+
Status: typeof CapStatus;
|
|
220
|
+
Queued: typeof CapQueued;
|
|
221
|
+
Section: typeof CapSection;
|
|
222
|
+
Button: typeof CapButton;
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
};
|
|
226
|
+
Element: HTMLDivElement;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The dark cap on the composer's top edge. Read down, the panel is a
|
|
231
|
+
* timeline: the transcript (done), the cap's status (now), its queue (next),
|
|
232
|
+
* the box (what you are still writing). One status at a time, the most
|
|
233
|
+
* urgent: a decision, a problem, work, idle.
|
|
234
|
+
*/
|
|
235
|
+
export class ComposerCap extends Component<ComposerCapSignature> {
|
|
236
|
+
<template>
|
|
237
|
+
<div
|
|
238
|
+
class='pretui-cap'
|
|
239
|
+
role='group'
|
|
240
|
+
aria-label={{if @label @label 'Assistant status'}}
|
|
241
|
+
data-test-pretui-composer-cap
|
|
242
|
+
...attributes
|
|
243
|
+
>
|
|
244
|
+
{{yield
|
|
245
|
+
(hash
|
|
246
|
+
Status=CapStatus Queued=CapQueued Section=CapSection Button=CapButton
|
|
247
|
+
)
|
|
248
|
+
}}
|
|
249
|
+
</div>
|
|
250
|
+
<style scoped>
|
|
251
|
+
@layer PretComponent {
|
|
252
|
+
.pretui-cap {
|
|
253
|
+
--pretui-cap-ink: var(--foreground);
|
|
254
|
+
/* a row's least height, which its buttons fill */
|
|
255
|
+
--_cap-row-h: 1.75rem;
|
|
256
|
+
display: flex;
|
|
257
|
+
flex-direction: column;
|
|
258
|
+
margin-inline: var(--pretui-cap-inset, var(--boxel-sp-sm));
|
|
259
|
+
border-radius: var(--boxel-border-radius-lg)
|
|
260
|
+
var(--boxel-border-radius-lg) 0 0;
|
|
261
|
+
background-color: var(
|
|
262
|
+
--pretui-cap-surface,
|
|
263
|
+
color-mix(in oklch, var(--foreground) 8%, var(--background))
|
|
264
|
+
);
|
|
265
|
+
box-shadow: 0 0 0 1px var(--pretui-cap-edge, var(--border-strong));
|
|
266
|
+
color: var(--pretui-cap-ink);
|
|
267
|
+
font-family: var(--font-sans);
|
|
268
|
+
container: pretui-cap / inline-size;
|
|
269
|
+
}
|
|
270
|
+
/* a narrow cap keeps the moment's primary and drops what it can
|
|
271
|
+
reach another way */
|
|
272
|
+
@container pretui-cap (width < 20rem) {
|
|
273
|
+
.pretui-cap :deep([data-cap-optional]) {
|
|
274
|
+
display: none;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/* every row is UI size; a queued row is the compact one */
|
|
278
|
+
.pretui-cap :deep(.pretui-cap-row) {
|
|
279
|
+
display: flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
gap: var(--boxel-sp-xs);
|
|
282
|
+
min-height: var(--_cap-row-h);
|
|
283
|
+
padding: 0 var(--boxel-sp-4xs) 0 var(--boxel-sp-xs);
|
|
284
|
+
font-size: var(--boxel-ui-label-font-size);
|
|
285
|
+
line-height: var(--boxel-ui-label-line-height);
|
|
286
|
+
font-weight: 400;
|
|
287
|
+
min-width: 0;
|
|
288
|
+
}
|
|
289
|
+
.pretui-cap > :deep(* + *) {
|
|
290
|
+
box-shadow: inset 0 1px 0 var(--pretui-cap-edge, var(--border-strong));
|
|
291
|
+
}
|
|
292
|
+
/* the status is the row you read first: one step stronger than the
|
|
293
|
+
queue by weight (its title is 600) and height, never by size */
|
|
294
|
+
.pretui-cap :deep(.pretui-cap-status) {
|
|
295
|
+
min-height: 2.25rem;
|
|
296
|
+
padding: 0 var(--boxel-sp-4xs) 0 var(--boxel-sp-xs);
|
|
297
|
+
}
|
|
298
|
+
.pretui-cap :deep(.pretui-cap-status[data-wrap]) {
|
|
299
|
+
align-items: flex-start;
|
|
300
|
+
padding-block: var(--boxel-sp-xs);
|
|
301
|
+
}
|
|
302
|
+
.pretui-cap :deep(.pretui-cap-status[data-wrap] .pretui-cap-lead) {
|
|
303
|
+
margin-top: 0;
|
|
304
|
+
}
|
|
305
|
+
.pretui-cap :deep(.pretui-cap-status[data-wrap] .pretui-cap-actions) {
|
|
306
|
+
margin-top: calc(-1 * var(--boxel-sp-4xs));
|
|
307
|
+
}
|
|
308
|
+
.pretui-cap :deep(.pretui-cap-lead) {
|
|
309
|
+
flex: none;
|
|
310
|
+
display: inline-flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
justify-content: center;
|
|
313
|
+
width: 1.125rem;
|
|
314
|
+
height: 1.125rem;
|
|
315
|
+
}
|
|
316
|
+
.pretui-cap :deep(.pretui-cap-queued .pretui-cap-lead) {
|
|
317
|
+
width: 1rem;
|
|
318
|
+
height: 1rem;
|
|
319
|
+
color: var(--subtle-foreground);
|
|
320
|
+
}
|
|
321
|
+
.pretui-cap
|
|
322
|
+
:deep(.pretui-cap-status[data-tone='idle'] .pretui-cap-lead) {
|
|
323
|
+
opacity: 0.55;
|
|
324
|
+
}
|
|
325
|
+
.pretui-cap :deep(.pretui-cap-dot) {
|
|
326
|
+
width: 0.5rem;
|
|
327
|
+
height: 0.5rem;
|
|
328
|
+
border-radius: 50%;
|
|
329
|
+
background-color: currentColor;
|
|
330
|
+
box-shadow: 0 0 0 0.1875rem
|
|
331
|
+
color-mix(in oklch, currentColor 20%, transparent);
|
|
332
|
+
transition: color 160ms ease;
|
|
333
|
+
}
|
|
334
|
+
/* a state colours the mark only: waiting on you is attention, a
|
|
335
|
+
problem is failed, done and live work are live */
|
|
336
|
+
.pretui-cap
|
|
337
|
+
:deep(.pretui-cap-status[data-tone='waiting'] .pretui-cap-lead) {
|
|
338
|
+
color: var(--attention-ink);
|
|
339
|
+
}
|
|
340
|
+
.pretui-cap
|
|
341
|
+
:deep(.pretui-cap-status[data-tone='problem'] .pretui-cap-lead) {
|
|
342
|
+
color: var(--destructive-ink);
|
|
343
|
+
}
|
|
344
|
+
.pretui-cap
|
|
345
|
+
:deep(.pretui-cap-status[data-tone='done'] .pretui-cap-lead) {
|
|
346
|
+
color: var(--primary-ink);
|
|
347
|
+
}
|
|
348
|
+
/* the title holds its words; the detail gives way first */
|
|
349
|
+
.pretui-cap :deep(.pretui-cap-text) {
|
|
350
|
+
flex: 1 1 auto;
|
|
351
|
+
display: flex;
|
|
352
|
+
align-items: baseline;
|
|
353
|
+
min-width: 0;
|
|
354
|
+
overflow: hidden;
|
|
355
|
+
white-space: nowrap;
|
|
356
|
+
}
|
|
357
|
+
.pretui-cap :deep(.pretui-cap-title) {
|
|
358
|
+
flex: 0 1 auto;
|
|
359
|
+
min-width: 0;
|
|
360
|
+
overflow: hidden;
|
|
361
|
+
text-overflow: ellipsis;
|
|
362
|
+
}
|
|
363
|
+
.pretui-cap :deep(.pretui-cap-dots) {
|
|
364
|
+
flex: none;
|
|
365
|
+
}
|
|
366
|
+
.pretui-cap :deep(.pretui-cap-text > .pretui-cap-detail) {
|
|
367
|
+
flex: 0 4 auto;
|
|
368
|
+
min-width: 0;
|
|
369
|
+
overflow: hidden;
|
|
370
|
+
text-overflow: ellipsis;
|
|
371
|
+
white-space: pre;
|
|
372
|
+
}
|
|
373
|
+
.pretui-cap :deep(.pretui-cap-status[data-wrap] .pretui-cap-text) {
|
|
374
|
+
display: block;
|
|
375
|
+
white-space: normal;
|
|
376
|
+
overflow-wrap: anywhere;
|
|
377
|
+
color: var(--muted-foreground);
|
|
378
|
+
}
|
|
379
|
+
.pretui-cap :deep(.pretui-cap-status[data-wrap] .pretui-cap-title) {
|
|
380
|
+
font-weight: 400;
|
|
381
|
+
}
|
|
382
|
+
.pretui-cap :deep(.pretui-cap-title) {
|
|
383
|
+
font-weight: 600;
|
|
384
|
+
}
|
|
385
|
+
.pretui-cap :deep(.pretui-cap-queued .pretui-cap-text) {
|
|
386
|
+
color: var(--muted-foreground);
|
|
387
|
+
}
|
|
388
|
+
.pretui-cap :deep(.pretui-cap-detail) {
|
|
389
|
+
color: var(--muted-foreground);
|
|
390
|
+
font-weight: 400;
|
|
391
|
+
font-variant-numeric: tabular-nums;
|
|
392
|
+
}
|
|
393
|
+
.pretui-cap :deep(.pretui-cap-dots span) {
|
|
394
|
+
animation: pretui-cap-blink 1.4s infinite both;
|
|
395
|
+
opacity: 0.4;
|
|
396
|
+
}
|
|
397
|
+
.pretui-cap :deep(.pretui-cap-dots span:nth-child(2)) {
|
|
398
|
+
animation-delay: 0.2s;
|
|
399
|
+
}
|
|
400
|
+
.pretui-cap :deep(.pretui-cap-dots span:nth-child(3)) {
|
|
401
|
+
animation-delay: 0.4s;
|
|
402
|
+
}
|
|
403
|
+
@keyframes pretui-cap-blink {
|
|
404
|
+
0%,
|
|
405
|
+
80%,
|
|
406
|
+
100% {
|
|
407
|
+
opacity: 0.4;
|
|
408
|
+
}
|
|
409
|
+
40% {
|
|
410
|
+
opacity: 1;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
.pretui-cap :deep(.pretui-cap-actions) {
|
|
414
|
+
flex: none;
|
|
415
|
+
display: flex;
|
|
416
|
+
align-items: center;
|
|
417
|
+
gap: var(--boxel-sp-4xs);
|
|
418
|
+
}
|
|
419
|
+
.pretui-cap :deep(.pretui-cap-actions:empty) {
|
|
420
|
+
display: none;
|
|
421
|
+
}
|
|
422
|
+
.pretui-cap :deep(.pretui-cap-section) {
|
|
423
|
+
padding: var(--boxel-sp-4xs) var(--boxel-sp-sm) var(--boxel-sp-xs)
|
|
424
|
+
var(--boxel-sp-xl);
|
|
425
|
+
}
|
|
426
|
+
.pretui-cap :deep(.pretui-cap-button) {
|
|
427
|
+
display: inline-flex;
|
|
428
|
+
align-items: center;
|
|
429
|
+
justify-content: center;
|
|
430
|
+
gap: var(--boxel-sp-4xs);
|
|
431
|
+
height: 1.5rem;
|
|
432
|
+
min-width: 1.5rem;
|
|
433
|
+
padding: 0 var(--boxel-sp-xs);
|
|
434
|
+
border: 0;
|
|
435
|
+
border-radius: var(--boxel-border-radius-sm);
|
|
436
|
+
background-color: transparent;
|
|
437
|
+
color: var(--muted-foreground);
|
|
438
|
+
font-family: var(--font-sans);
|
|
439
|
+
font-size: var(--boxel-ui-label-font-size);
|
|
440
|
+
line-height: var(--boxel-ui-label-line-height);
|
|
441
|
+
font-weight: 600;
|
|
442
|
+
white-space: nowrap;
|
|
443
|
+
cursor: pointer;
|
|
444
|
+
}
|
|
445
|
+
.pretui-cap :deep(.pretui-cap-status .pretui-cap-button) {
|
|
446
|
+
height: var(--_cap-row-h);
|
|
447
|
+
min-width: var(--_cap-row-h);
|
|
448
|
+
padding: 0 var(--boxel-sp-sm);
|
|
449
|
+
}
|
|
450
|
+
.pretui-cap
|
|
451
|
+
:deep(.pretui-cap-status .pretui-cap-button[data-kind='icon']) {
|
|
452
|
+
padding: 0;
|
|
453
|
+
}
|
|
454
|
+
.pretui-cap :deep(.pretui-cap-button[data-kind='icon']) {
|
|
455
|
+
padding: 0;
|
|
456
|
+
}
|
|
457
|
+
/* the moment's one action */
|
|
458
|
+
.pretui-cap :deep(.pretui-cap-button[data-kind='primary']) {
|
|
459
|
+
background-color: var(--primary);
|
|
460
|
+
color: var(--primary-foreground);
|
|
461
|
+
}
|
|
462
|
+
.pretui-cap
|
|
463
|
+
:deep(.pretui-cap-button[data-kind='primary']:hover:not(:disabled)) {
|
|
464
|
+
background-color: color-mix(
|
|
465
|
+
in oklch,
|
|
466
|
+
var(--primary) 88%,
|
|
467
|
+
var(--pretui-cap-ink)
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
.pretui-cap :deep(.pretui-cap-button[data-kind='secondary']) {
|
|
471
|
+
box-shadow: inset 0 0 0 1px var(--border-strong);
|
|
472
|
+
color: var(--pretui-cap-ink);
|
|
473
|
+
}
|
|
474
|
+
.pretui-cap :deep(.pretui-cap-button[data-kind='quiet']),
|
|
475
|
+
.pretui-cap :deep(.pretui-cap-button[data-kind='icon']) {
|
|
476
|
+
color: var(--muted-foreground);
|
|
477
|
+
}
|
|
478
|
+
.pretui-cap
|
|
479
|
+
:deep(.pretui-cap-button[data-kind='quiet']:hover:not(:disabled)),
|
|
480
|
+
.pretui-cap
|
|
481
|
+
:deep(.pretui-cap-button[data-kind='icon']:hover:not(:disabled)) {
|
|
482
|
+
color: var(--pretui-cap-ink);
|
|
483
|
+
background-color: color-mix(
|
|
484
|
+
in oklch,
|
|
485
|
+
var(--pretui-cap-ink) 8%,
|
|
486
|
+
transparent
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
.pretui-cap :deep(.pretui-cap-button:disabled),
|
|
490
|
+
.pretui-cap :deep(.pretui-cap-button[data-busy]) {
|
|
491
|
+
cursor: default;
|
|
492
|
+
background-color: color-mix(
|
|
493
|
+
in oklch,
|
|
494
|
+
var(--pretui-cap-ink) 10%,
|
|
495
|
+
transparent
|
|
496
|
+
);
|
|
497
|
+
color: var(--subtle-foreground);
|
|
498
|
+
box-shadow: none;
|
|
499
|
+
}
|
|
500
|
+
.pretui-cap :deep(.pretui-cap-button:focus-visible) {
|
|
501
|
+
outline: 2px solid var(--ring);
|
|
502
|
+
outline-offset: 1px;
|
|
503
|
+
}
|
|
504
|
+
.pretui-cap :deep(.pretui-cap-glyph) {
|
|
505
|
+
flex: none;
|
|
506
|
+
}
|
|
507
|
+
@media (prefers-reduced-motion: reduce) {
|
|
508
|
+
.pretui-cap :deep(.pretui-cap-dots span) {
|
|
509
|
+
animation: none;
|
|
510
|
+
opacity: 0.7;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
</style>
|
|
515
|
+
</template>
|
|
516
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Pretui — ContextRing: how much of the model's context the conversation uses, as a small ring.
|
|
2
|
+
import Component from '@glimmer/component';
|
|
3
|
+
import { htmlSafe } from '@ember/template';
|
|
4
|
+
|
|
5
|
+
export interface ContextRingSignature {
|
|
6
|
+
Args: {
|
|
7
|
+
/** tokens the conversation uses */
|
|
8
|
+
used: number;
|
|
9
|
+
/** the model's context window, in tokens */
|
|
10
|
+
limit: number;
|
|
11
|
+
/** the share at which the ring warns. Default 0.8 */
|
|
12
|
+
warnAt?: number;
|
|
13
|
+
/** the ring alone: the percentage moves to its tooltip, for a tight
|
|
14
|
+
* composer bar */
|
|
15
|
+
compact?: boolean;
|
|
16
|
+
};
|
|
17
|
+
Element: HTMLSpanElement;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function compact(n: number): string {
|
|
21
|
+
return n >= 1000 ? `${Math.round(n / 100) / 10}k` : String(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A small ring and its percentage, beside the model picker: how full the
|
|
26
|
+
* context is, so a reader sees compaction coming before it happens. The
|
|
27
|
+
* ring turns to the attention colour past @warnAt, and its name says
|
|
28
|
+
* "nearly full".
|
|
29
|
+
*/
|
|
30
|
+
export class ContextRing extends Component<ContextRingSignature> {
|
|
31
|
+
get share(): number {
|
|
32
|
+
let { used, limit } = this.args;
|
|
33
|
+
return limit > 0 ? Math.min(Math.max(used / limit, 0), 1) : 0;
|
|
34
|
+
}
|
|
35
|
+
get percent(): number {
|
|
36
|
+
return Math.round(this.share * 100);
|
|
37
|
+
}
|
|
38
|
+
get warns(): boolean {
|
|
39
|
+
return this.share >= (this.args.warnAt ?? 0.8);
|
|
40
|
+
}
|
|
41
|
+
get ringStyle() {
|
|
42
|
+
return htmlSafe(`--pretui-ring-share: ${this.share * 360}deg`);
|
|
43
|
+
}
|
|
44
|
+
/** the accessible name and tooltip; past @warnAt it says so in words, so
|
|
45
|
+
* the warning never rests on the ring's colour alone */
|
|
46
|
+
get label(): string {
|
|
47
|
+
let warning = this.warns ? ', nearly full' : '';
|
|
48
|
+
return `Context ${this.percent}% used${warning}: ${compact(this.args.used)} of ${compact(this.args.limit)} tokens`;
|
|
49
|
+
}
|
|
50
|
+
<template>
|
|
51
|
+
<span
|
|
52
|
+
class='pretui-contextring'
|
|
53
|
+
role='img'
|
|
54
|
+
aria-label={{this.label}}
|
|
55
|
+
title={{this.label}}
|
|
56
|
+
data-warn={{if this.warns 'true'}}
|
|
57
|
+
data-test-pretui-context-ring
|
|
58
|
+
...attributes
|
|
59
|
+
>
|
|
60
|
+
<span
|
|
61
|
+
class='pretui-contextring-ring'
|
|
62
|
+
style={{this.ringStyle}}
|
|
63
|
+
aria-hidden='true'
|
|
64
|
+
></span>
|
|
65
|
+
{{#unless @compact}}<span
|
|
66
|
+
class='pretui-contextring-pct'
|
|
67
|
+
aria-hidden='true'
|
|
68
|
+
>{{this.percent}}%</span>{{/unless}}
|
|
69
|
+
</span>
|
|
70
|
+
<style scoped>
|
|
71
|
+
@layer PretComponent {
|
|
72
|
+
.pretui-contextring {
|
|
73
|
+
display: inline-flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: var(--boxel-sp-4xs);
|
|
76
|
+
font-size: var(--boxel-caption-font-size);
|
|
77
|
+
line-height: var(--boxel-caption-line-height);
|
|
78
|
+
font-variant-numeric: tabular-nums;
|
|
79
|
+
color: var(--muted-foreground);
|
|
80
|
+
}
|
|
81
|
+
.pretui-contextring-ring {
|
|
82
|
+
--_contextring-ink: var(--muted-foreground);
|
|
83
|
+
width: 0.875rem;
|
|
84
|
+
height: 0.875rem;
|
|
85
|
+
border-radius: 50%;
|
|
86
|
+
background: conic-gradient(
|
|
87
|
+
var(--_contextring-ink) var(--pretui-ring-share),
|
|
88
|
+
var(--border-strong) 0
|
|
89
|
+
);
|
|
90
|
+
-webkit-mask: radial-gradient(
|
|
91
|
+
circle,
|
|
92
|
+
transparent 0.25rem,
|
|
93
|
+
black 0.28125rem
|
|
94
|
+
);
|
|
95
|
+
mask: radial-gradient(circle, transparent 0.25rem, black 0.28125rem);
|
|
96
|
+
}
|
|
97
|
+
/* past the warning share the ring (the mark) takes the attention
|
|
98
|
+
ink, and the digits step up to the full foreground */
|
|
99
|
+
.pretui-contextring[data-warn] {
|
|
100
|
+
color: var(--foreground);
|
|
101
|
+
}
|
|
102
|
+
.pretui-contextring[data-warn] .pretui-contextring-ring {
|
|
103
|
+
--_contextring-ink: var(--attention-ink);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
107
|
+
</template>
|
|
108
|
+
}
|