@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,462 @@
|
|
|
1
|
+
// Pretui — ToolGate: the decision a tool call waits on, asked inside its row.
|
|
2
|
+
import Component from '@glimmer/component';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { on } from '@ember/modifier';
|
|
5
|
+
import { fn } from '@ember/helper';
|
|
6
|
+
import { guidFor } from '@ember/object/internals';
|
|
7
|
+
import { modifier } from 'ember-modifier';
|
|
8
|
+
import { StateGlyph } from './state-glyph';
|
|
9
|
+
|
|
10
|
+
export interface ToolGateAction {
|
|
11
|
+
/** the button's words: 'Allow once', 'Keep 40', 'Revert'. On a
|
|
12
|
+
* destructive gate the primary answer names the act it runs: 'Delete 3
|
|
13
|
+
* files', never 'OK' or 'Allow' */
|
|
14
|
+
label: string;
|
|
15
|
+
/** 'primary' (the one filled button), 'secondary' (outlined), or 'quiet'
|
|
16
|
+
* (text). Default 'secondary' */
|
|
17
|
+
kind?: 'primary' | 'secondary' | 'quiet';
|
|
18
|
+
/** a stable name for the answer ('apply', 'always', 'deny'): what a host
|
|
19
|
+
* finds it by, and its test hook. Default: the label */
|
|
20
|
+
id?: string;
|
|
21
|
+
/** the action; a returned promise keeps the gate busy until it settles */
|
|
22
|
+
onAct: () => unknown;
|
|
23
|
+
/** what the button reads while its action runs. Default: label and "…" */
|
|
24
|
+
busyLabel?: string;
|
|
25
|
+
/** an "always" answer: what it grants is said under it before it is
|
|
26
|
+
* given ("Allows realm.fs.delete in showcase/scratch/ for this session"),
|
|
27
|
+
* and a destructive gate never offers one */
|
|
28
|
+
always?: boolean;
|
|
29
|
+
/** the decline: the gate asks why (optional) and hands the reason to
|
|
30
|
+
* `onDecline` instead of running `onAct` */
|
|
31
|
+
declines?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ToolGateSignature {
|
|
35
|
+
Args: {
|
|
36
|
+
/** the question, in one sentence: "This waits for your click." */
|
|
37
|
+
prompt: string;
|
|
38
|
+
/** why it asks, or what the click allows; a quieter second line */
|
|
39
|
+
detail?: string;
|
|
40
|
+
/** the answers, most likely first. Omit when the decision is someone
|
|
41
|
+
* else's (@waitingOn) */
|
|
42
|
+
actions?: ToolGateAction[];
|
|
43
|
+
/** the decision belongs to this person: the gate says so and offers
|
|
44
|
+
* no buttons. "Rupert" → "Waiting on Rupert" */
|
|
45
|
+
waitingOn?: string;
|
|
46
|
+
/** 'attention' (default) for a question, 'conflict' when the call found
|
|
47
|
+
* that something moved since it asked */
|
|
48
|
+
tone?: 'attention' | 'conflict';
|
|
49
|
+
/** the call cannot be undone: the gate says so in words, draws its
|
|
50
|
+
* primary answer in the destructive colour (so that answer's label must
|
|
51
|
+
* name the act: 'Delete 3 files'), and drops any "always" answer, since
|
|
52
|
+
* a standing yes to a delete is not a yes the reader can see the cost
|
|
53
|
+
* of */
|
|
54
|
+
destructive?: boolean;
|
|
55
|
+
/** what an "always" answer grants, said under the answers while it is
|
|
56
|
+
* focused or hovered: "realm.fs.delete in showcase/scratch/" */
|
|
57
|
+
scope?: string;
|
|
58
|
+
/** the decline's reason, optional: asked when an answer marked
|
|
59
|
+
* `declines` is pressed, then handed here */
|
|
60
|
+
onDecline?: (reason: string) => unknown;
|
|
61
|
+
/** the call was allowed by a standing answer: the gate is a receipt,
|
|
62
|
+
* "Allowed for this session", with no buttons */
|
|
63
|
+
auto?: string;
|
|
64
|
+
/** the question ended without an answer: 'canceled' (the run went on
|
|
65
|
+
* without it) or 'expired' (nobody answered in time). The gate says so,
|
|
66
|
+
* with no buttons */
|
|
67
|
+
resolution?: 'canceled' | 'expired';
|
|
68
|
+
/** the reader cannot answer here (a read-only transcript, a teammate's
|
|
69
|
+
* request): the question shows, its answers do not */
|
|
70
|
+
readonly?: boolean;
|
|
71
|
+
};
|
|
72
|
+
/** what the reader decides about: a picker, the files a preview staged */
|
|
73
|
+
Blocks: { default: [] };
|
|
74
|
+
Element: HTMLDivElement;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The in-row gate. One question, its answers, and one busy state for all
|
|
79
|
+
* of them, so a decision is made once:
|
|
80
|
+
*
|
|
81
|
+
* ```hbs
|
|
82
|
+
* <ToolRow @name='run-realm-code' @state='ready' @framed={{true}}>
|
|
83
|
+
* <:gate>
|
|
84
|
+
* <ToolGate @prompt='This waits for your click.'
|
|
85
|
+
* @actions={{array (hash label='Allow once' kind='primary' onAct=this.allow)
|
|
86
|
+
* (hash label='Always for this session' onAct=this.always)
|
|
87
|
+
* (hash label='Deny' kind='quiet' onAct=this.deny)}} />
|
|
88
|
+
* </:gate>
|
|
89
|
+
* </ToolRow>
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export class ToolGate extends Component<ToolGateSignature> {
|
|
93
|
+
/** the label of the action in flight; every answer waits on it */
|
|
94
|
+
@tracked busy: string | undefined;
|
|
95
|
+
@tracked declining = false;
|
|
96
|
+
@tracked reason = '';
|
|
97
|
+
readonly promptId = guidFor(this) + '-prompt';
|
|
98
|
+
/** the declining answer, which takes focus back when the reader leaves
|
|
99
|
+
* the reason field with Back or Escape */
|
|
100
|
+
private returnTo: ToolGateAction | undefined;
|
|
101
|
+
|
|
102
|
+
get actions(): ToolGateAction[] {
|
|
103
|
+
if (
|
|
104
|
+
this.args.waitingOn ||
|
|
105
|
+
this.args.auto ||
|
|
106
|
+
this.args.resolution ||
|
|
107
|
+
this.args.readonly
|
|
108
|
+
) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
let actions = this.args.actions ?? [];
|
|
112
|
+
return this.args.destructive ? actions.filter((a) => !a.always) : actions;
|
|
113
|
+
}
|
|
114
|
+
get hasAlways(): boolean {
|
|
115
|
+
return Boolean(this.args.scope) && this.actions.some((a) => a.always);
|
|
116
|
+
}
|
|
117
|
+
get prompt(): string {
|
|
118
|
+
if (this.args.resolution) {
|
|
119
|
+
let how = this.args.resolution === 'expired' ? 'Expired' : 'Canceled';
|
|
120
|
+
return `${how} · ${this.args.prompt}`;
|
|
121
|
+
}
|
|
122
|
+
return this.args.waitingOn
|
|
123
|
+
? `Waiting on ${this.args.waitingOn}`
|
|
124
|
+
: this.args.prompt;
|
|
125
|
+
}
|
|
126
|
+
labelOf = (action: ToolGateAction): string =>
|
|
127
|
+
this.busy === action.label
|
|
128
|
+
? (action.busyLabel ?? `${action.label}…`)
|
|
129
|
+
: action.label;
|
|
130
|
+
kindOf = (action: ToolGateAction): string => action.kind ?? 'secondary';
|
|
131
|
+
isBusy = (action: ToolGateAction): boolean => this.busy === action.label;
|
|
132
|
+
|
|
133
|
+
act = async (action: ToolGateAction) => {
|
|
134
|
+
if (this.busy) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (action.declines && this.args.onDecline) {
|
|
138
|
+
this.returnTo = action;
|
|
139
|
+
this.declining = true;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.busy = action.label;
|
|
143
|
+
try {
|
|
144
|
+
await action.onAct();
|
|
145
|
+
} finally {
|
|
146
|
+
if (!this.isDestroying && !this.isDestroyed) {
|
|
147
|
+
this.busy = undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
typeReason = (event: Event) =>
|
|
153
|
+
(this.reason = (event.target as HTMLInputElement).value);
|
|
154
|
+
cancelDecline = () => {
|
|
155
|
+
this.declining = false;
|
|
156
|
+
this.reason = '';
|
|
157
|
+
};
|
|
158
|
+
/** the reason field takes focus as it appears: the answer that opened it
|
|
159
|
+
* has just left the page */
|
|
160
|
+
focusOnInsert = modifier((element: HTMLElement) => {
|
|
161
|
+
element.focus();
|
|
162
|
+
});
|
|
163
|
+
/** the declining answer takes focus back as it reappears after Back or
|
|
164
|
+
* Escape */
|
|
165
|
+
refocus = modifier((element: HTMLElement, [action]: [ToolGateAction]) => {
|
|
166
|
+
if (this.returnTo?.label === action.label && !this.declining) {
|
|
167
|
+
this.returnTo = undefined;
|
|
168
|
+
element.focus();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
confirmDecline = async () => {
|
|
172
|
+
if (this.busy) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.busy = 'decline';
|
|
176
|
+
try {
|
|
177
|
+
await this.args.onDecline?.(this.reason.trim());
|
|
178
|
+
} finally {
|
|
179
|
+
if (!this.isDestroying && !this.isDestroyed) {
|
|
180
|
+
this.busy = undefined;
|
|
181
|
+
this.declining = false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
reasonKey = (e: Event) => {
|
|
186
|
+
let event = e as KeyboardEvent;
|
|
187
|
+
if (event.key === 'Enter') {
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
this.confirmDecline();
|
|
190
|
+
} else if (event.key === 'Escape') {
|
|
191
|
+
// the field's own Escape: a host listening at the document must not
|
|
192
|
+
// also close what holds the gate
|
|
193
|
+
event.stopPropagation();
|
|
194
|
+
this.cancelDecline();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
<template>
|
|
199
|
+
<div
|
|
200
|
+
class='pretui-toolgate'
|
|
201
|
+
role='group'
|
|
202
|
+
aria-labelledby={{this.promptId}}
|
|
203
|
+
aria-busy={{if this.busy 'true'}}
|
|
204
|
+
data-tone={{if @tone @tone 'attention'}}
|
|
205
|
+
data-routed={{if @waitingOn 'true'}}
|
|
206
|
+
data-destructive={{if @destructive 'true'}}
|
|
207
|
+
data-auto={{if @auto 'true'}}
|
|
208
|
+
data-resolution={{@resolution}}
|
|
209
|
+
data-test-pretui-tool-gate
|
|
210
|
+
...attributes
|
|
211
|
+
>
|
|
212
|
+
<p class='pretui-toolgate-prompt' id={{this.promptId}}>
|
|
213
|
+
{{#if @auto}}<span
|
|
214
|
+
class='pretui-toolgate-auto'
|
|
215
|
+
data-test-pretui-tool-gate-auto
|
|
216
|
+
><StateGlyph
|
|
217
|
+
class='pretui-toolgate-auto-mark'
|
|
218
|
+
@glyph='done'
|
|
219
|
+
/>{{@auto}}</span>{{else}}{{this.prompt}}{{/if}}
|
|
220
|
+
{{#if @destructive}}<span
|
|
221
|
+
class='pretui-toolgate-destructive'
|
|
222
|
+
data-test-pretui-tool-gate-destructive
|
|
223
|
+
>This cannot be undone.</span>{{/if}}
|
|
224
|
+
{{#if @detail}}<span
|
|
225
|
+
class='pretui-toolgate-detail'
|
|
226
|
+
data-test-pretui-tool-gate-detail
|
|
227
|
+
>{{@detail}}</span>{{/if}}
|
|
228
|
+
</p>
|
|
229
|
+
{{yield}}
|
|
230
|
+
{{#if this.declining}}
|
|
231
|
+
<div class='pretui-toolgate-decline' data-test-pretui-tool-gate-decline>
|
|
232
|
+
<input
|
|
233
|
+
class='pretui-toolgate-reason'
|
|
234
|
+
aria-label='Why not? (optional)'
|
|
235
|
+
placeholder='Why not? (optional)'
|
|
236
|
+
value={{this.reason}}
|
|
237
|
+
{{on 'input' this.typeReason}}
|
|
238
|
+
{{on 'keydown' this.reasonKey}}
|
|
239
|
+
{{this.focusOnInsert}}
|
|
240
|
+
data-test-pretui-tool-gate-reason
|
|
241
|
+
/>
|
|
242
|
+
<button
|
|
243
|
+
type='button'
|
|
244
|
+
class='pretui-toolgate-action'
|
|
245
|
+
data-kind='primary'
|
|
246
|
+
aria-disabled={{if this.busy 'true'}}
|
|
247
|
+
{{on 'click' this.confirmDecline}}
|
|
248
|
+
data-test-pretui-tool-gate-decline-confirm
|
|
249
|
+
>Decline</button>
|
|
250
|
+
<button
|
|
251
|
+
type='button'
|
|
252
|
+
class='pretui-toolgate-action'
|
|
253
|
+
data-kind='quiet'
|
|
254
|
+
{{on 'click' this.cancelDecline}}
|
|
255
|
+
data-test-pretui-tool-gate-decline-back
|
|
256
|
+
>Back</button>
|
|
257
|
+
</div>
|
|
258
|
+
{{else if this.actions.length}}
|
|
259
|
+
<div class='pretui-toolgate-actions'>
|
|
260
|
+
{{#each this.actions as |answer|}}
|
|
261
|
+
<button
|
|
262
|
+
type='button'
|
|
263
|
+
class='pretui-toolgate-action'
|
|
264
|
+
data-kind={{this.kindOf answer}}
|
|
265
|
+
data-busy={{if (this.isBusy answer) 'true'}}
|
|
266
|
+
aria-disabled={{if this.busy 'true'}}
|
|
267
|
+
aria-describedby={{this.promptId}}
|
|
268
|
+
data-tool-gate-action={{answer.id}}
|
|
269
|
+
{{on 'click' (fn this.act answer)}}
|
|
270
|
+
{{this.refocus answer}}
|
|
271
|
+
data-test-pretui-tool-gate-action={{if
|
|
272
|
+
answer.id
|
|
273
|
+
answer.id
|
|
274
|
+
answer.label
|
|
275
|
+
}}
|
|
276
|
+
>{{this.labelOf answer}}</button>
|
|
277
|
+
{{/each}}
|
|
278
|
+
</div>
|
|
279
|
+
{{#if this.hasAlways}}
|
|
280
|
+
<p
|
|
281
|
+
class='pretui-toolgate-scope'
|
|
282
|
+
data-test-pretui-tool-gate-scope
|
|
283
|
+
>“Always” allows {{@scope}}</p>
|
|
284
|
+
{{/if}}
|
|
285
|
+
{{/if}}
|
|
286
|
+
</div>
|
|
287
|
+
<style scoped>
|
|
288
|
+
@layer PretComponent {
|
|
289
|
+
/* the gate sits on the row's text edge, under its lines: a question
|
|
290
|
+
and its answers, with no box of its own (the row is the box) */
|
|
291
|
+
.pretui-toolgate {
|
|
292
|
+
display: grid;
|
|
293
|
+
gap: var(--boxel-sp-xs);
|
|
294
|
+
padding-block: var(--boxel-sp-4xs) var(--boxel-sp-6xs);
|
|
295
|
+
padding-inline-start: calc(
|
|
296
|
+
var(--pretui-toolrow-disc, var(--boxel-sp)) +
|
|
297
|
+
var(--pretui-toolrow-gap, var(--boxel-sp-xs))
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
.pretui-toolgate-prompt {
|
|
301
|
+
margin: 0;
|
|
302
|
+
font-size: var(--boxel-ui-label-font-size);
|
|
303
|
+
line-height: var(--boxel-ui-label-line-height);
|
|
304
|
+
font-weight: 600;
|
|
305
|
+
color: var(--foreground);
|
|
306
|
+
}
|
|
307
|
+
/* a conflict needs the reader: the prompt stays ink-1 and a dot
|
|
308
|
+
before it takes the attention state (the mark, never the text) */
|
|
309
|
+
.pretui-toolgate[data-tone='conflict'] .pretui-toolgate-prompt::before {
|
|
310
|
+
content: '';
|
|
311
|
+
display: inline-block;
|
|
312
|
+
width: 0.375rem;
|
|
313
|
+
height: 0.375rem;
|
|
314
|
+
margin-inline-end: var(--boxel-sp-2xs);
|
|
315
|
+
border-radius: 50%;
|
|
316
|
+
vertical-align: 0.1em;
|
|
317
|
+
background-color: var(--attention);
|
|
318
|
+
}
|
|
319
|
+
.pretui-toolgate[data-routed] .pretui-toolgate-prompt {
|
|
320
|
+
font-weight: 400;
|
|
321
|
+
color: var(--muted-foreground);
|
|
322
|
+
}
|
|
323
|
+
.pretui-toolgate[data-resolution] .pretui-toolgate-prompt {
|
|
324
|
+
font-weight: 400;
|
|
325
|
+
color: var(--muted-foreground);
|
|
326
|
+
}
|
|
327
|
+
.pretui-toolgate-destructive {
|
|
328
|
+
display: block;
|
|
329
|
+
font-weight: 600;
|
|
330
|
+
color: var(--destructive-ink);
|
|
331
|
+
}
|
|
332
|
+
.pretui-toolgate-auto {
|
|
333
|
+
font-weight: 400;
|
|
334
|
+
color: var(--muted-foreground);
|
|
335
|
+
}
|
|
336
|
+
.pretui-toolgate-auto-mark {
|
|
337
|
+
display: inline-grid;
|
|
338
|
+
margin-inline-end: var(--boxel-sp-4xs);
|
|
339
|
+
vertical-align: -0.1em;
|
|
340
|
+
}
|
|
341
|
+
.pretui-toolgate-scope {
|
|
342
|
+
margin: 0;
|
|
343
|
+
font-size: var(--boxel-caption-font-size);
|
|
344
|
+
line-height: var(--boxel-caption-line-height);
|
|
345
|
+
color: var(--subtle-foreground);
|
|
346
|
+
}
|
|
347
|
+
.pretui-toolgate-decline {
|
|
348
|
+
display: flex;
|
|
349
|
+
flex-wrap: wrap;
|
|
350
|
+
gap: var(--boxel-sp-xs);
|
|
351
|
+
align-items: center;
|
|
352
|
+
}
|
|
353
|
+
.pretui-toolgate-reason {
|
|
354
|
+
flex: 1 1 10rem;
|
|
355
|
+
min-width: 0;
|
|
356
|
+
height: 1.75rem;
|
|
357
|
+
padding: 0 var(--boxel-sp-xs);
|
|
358
|
+
border: 0;
|
|
359
|
+
border-radius: var(--boxel-border-radius-sm);
|
|
360
|
+
background-color: var(--field);
|
|
361
|
+
box-shadow: inset 0 0 0 1px var(--input);
|
|
362
|
+
font-family: inherit;
|
|
363
|
+
font-size: var(--boxel-ui-label-font-size);
|
|
364
|
+
line-height: var(--boxel-ui-label-line-height);
|
|
365
|
+
color: var(--foreground);
|
|
366
|
+
}
|
|
367
|
+
.pretui-toolgate-reason::placeholder {
|
|
368
|
+
color: var(--subtle-foreground);
|
|
369
|
+
}
|
|
370
|
+
.pretui-toolgate-reason:focus {
|
|
371
|
+
outline: 2px solid var(--ring);
|
|
372
|
+
outline-offset: 1px;
|
|
373
|
+
}
|
|
374
|
+
.pretui-toolgate-detail {
|
|
375
|
+
display: block;
|
|
376
|
+
font-size: var(--boxel-caption-font-size);
|
|
377
|
+
line-height: var(--boxel-caption-line-height);
|
|
378
|
+
font-weight: 400;
|
|
379
|
+
color: var(--muted-foreground);
|
|
380
|
+
}
|
|
381
|
+
.pretui-toolgate-actions {
|
|
382
|
+
display: flex;
|
|
383
|
+
flex-wrap: wrap;
|
|
384
|
+
align-items: center;
|
|
385
|
+
gap: var(--boxel-sp-xs);
|
|
386
|
+
}
|
|
387
|
+
/* answers: the one answer is --primary; the rest are outlined
|
|
388
|
+
(secondary) or quiet text */
|
|
389
|
+
.pretui-toolgate-action {
|
|
390
|
+
min-height: 1.75rem;
|
|
391
|
+
padding: 0 var(--boxel-sp-sm);
|
|
392
|
+
border: 0;
|
|
393
|
+
border-radius: var(--boxel-border-radius-sm);
|
|
394
|
+
background-color: transparent;
|
|
395
|
+
box-shadow: inset 0 0 0 1px var(--border-strong);
|
|
396
|
+
font-family: inherit;
|
|
397
|
+
font-size: var(--boxel-ui-label-font-size);
|
|
398
|
+
line-height: var(--boxel-ui-label-line-height);
|
|
399
|
+
font-weight: 600;
|
|
400
|
+
color: var(--foreground);
|
|
401
|
+
cursor: pointer;
|
|
402
|
+
white-space: nowrap;
|
|
403
|
+
}
|
|
404
|
+
.pretui-toolgate-action[data-kind='primary'] {
|
|
405
|
+
box-shadow: none;
|
|
406
|
+
background-color: var(--primary);
|
|
407
|
+
color: var(--primary-foreground);
|
|
408
|
+
}
|
|
409
|
+
/* the answer that runs an act that cannot be undone looks like one */
|
|
410
|
+
.pretui-toolgate[data-destructive]
|
|
411
|
+
.pretui-toolgate-action[data-kind='primary'] {
|
|
412
|
+
background-color: var(--destructive);
|
|
413
|
+
color: var(--destructive-foreground);
|
|
414
|
+
}
|
|
415
|
+
.pretui-toolgate-action[data-kind='quiet'] {
|
|
416
|
+
box-shadow: none;
|
|
417
|
+
font-weight: 400;
|
|
418
|
+
color: var(--muted-foreground);
|
|
419
|
+
}
|
|
420
|
+
.pretui-toolgate-action:hover:not([aria-disabled='true']) {
|
|
421
|
+
background-color: var(--hover);
|
|
422
|
+
}
|
|
423
|
+
.pretui-toolgate-action[data-kind='quiet']:hover:not(
|
|
424
|
+
[aria-disabled='true']
|
|
425
|
+
) {
|
|
426
|
+
background-color: transparent;
|
|
427
|
+
color: var(--foreground);
|
|
428
|
+
}
|
|
429
|
+
.pretui-toolgate-action[data-kind='primary']:hover:not(
|
|
430
|
+
[aria-disabled='true']
|
|
431
|
+
) {
|
|
432
|
+
background-color: color-mix(
|
|
433
|
+
in srgb,
|
|
434
|
+
var(--primary) 88%,
|
|
435
|
+
var(--foreground)
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
.pretui-toolgate[data-destructive]
|
|
439
|
+
.pretui-toolgate-action[data-kind='primary']:hover:not(
|
|
440
|
+
[aria-disabled='true']
|
|
441
|
+
) {
|
|
442
|
+
background-color: color-mix(
|
|
443
|
+
in srgb,
|
|
444
|
+
var(--destructive) 88%,
|
|
445
|
+
var(--foreground)
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
.pretui-toolgate-action:focus-visible {
|
|
449
|
+
outline: 2px solid var(--ring);
|
|
450
|
+
outline-offset: 2px;
|
|
451
|
+
}
|
|
452
|
+
.pretui-toolgate-action[aria-disabled='true'] {
|
|
453
|
+
cursor: default;
|
|
454
|
+
opacity: 0.6;
|
|
455
|
+
}
|
|
456
|
+
.pretui-toolgate-action[data-busy] {
|
|
457
|
+
opacity: 1;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
</style>
|
|
461
|
+
</template>
|
|
462
|
+
}
|