@artooi/ag-ui-web-component 0.38.0 → 0.40.0
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/CHANGELOG.md +515 -1
- package/README.md +232 -56
- package/dist/ag-ui-web-component.bundle.js +370 -139
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +17 -14
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/activity_registry.d.ts +46 -0
- package/dist/core/activity_registry.d.ts.map +1 -0
- package/dist/core/ag_ui_chat.d.ts +23 -10
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +28 -0
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/core/client_seed.d.ts +27 -0
- package/dist/core/client_seed.d.ts.map +1 -0
- package/dist/core/read_max_tool_rounds.d.ts +9 -0
- package/dist/core/read_max_tool_rounds.d.ts.map +1 -0
- package/dist/core/run_handlers.d.ts +80 -0
- package/dist/core/run_handlers.d.ts.map +1 -0
- package/dist/core/storage_scope.d.ts +110 -0
- package/dist/core/storage_scope.d.ts.map +1 -0
- package/dist/core/tool_outcome.d.ts +2 -3
- package/dist/core/tool_outcome.d.ts.map +1 -1
- package/dist/core/utils.d.ts +46 -0
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/index.js +16268 -14747
- package/dist/index.js.map +4 -4
- package/dist/skills/skill_catalog.d.ts +67 -0
- package/dist/skills/skill_catalog.d.ts.map +1 -0
- package/dist/skills/skill_name_from.d.ts +12 -0
- package/dist/skills/skill_name_from.d.ts.map +1 -0
- package/dist/tools/tool_catalog.d.ts +79 -0
- package/dist/tools/tool_catalog.d.ts.map +1 -0
- package/dist/tools/tool_dispatch.d.ts +104 -0
- package/dist/tools/tool_dispatch.d.ts.map +1 -0
- package/dist/ui/composer/attachment_tray.d.ts.map +1 -1
- package/dist/ui/composer/auto_grow.d.ts +10 -0
- package/dist/ui/composer/auto_grow.d.ts.map +1 -0
- package/dist/ui/composer/composer_attachments.d.ts +57 -0
- package/dist/ui/composer/composer_attachments.d.ts.map +1 -0
- package/dist/ui/composer/composer_voice.d.ts +48 -0
- package/dist/ui/composer/composer_voice.d.ts.map +1 -0
- package/dist/ui/composer/voice_input.d.ts.map +1 -1
- package/dist/ui/excerpts/transcript_quote_offer.d.ts +57 -0
- package/dist/ui/excerpts/transcript_quote_offer.d.ts.map +1 -0
- package/dist/ui/fill_ui_string.d.ts +32 -0
- package/dist/ui/fill_ui_string.d.ts.map +1 -0
- package/dist/ui/history/conversation_history.d.ts +203 -0
- package/dist/ui/history/conversation_history.d.ts.map +1 -0
- package/dist/ui/history/relative_time.d.ts.map +1 -1
- package/dist/ui/history/thread_drawer.d.ts +0 -14
- package/dist/ui/history/thread_drawer.d.ts.map +1 -1
- package/dist/ui/interrupts/confirmation_card.d.ts.map +1 -1
- package/dist/ui/interrupts/pending_decision.d.ts +23 -0
- package/dist/ui/interrupts/pending_decision.d.ts.map +1 -0
- package/dist/ui/placement/is_collapsible_placement.d.ts +18 -0
- package/dist/ui/placement/is_collapsible_placement.d.ts.map +1 -0
- package/dist/ui/placement/is_draggable_placement.d.ts +9 -0
- package/dist/ui/placement/is_draggable_placement.d.ts.map +1 -0
- package/dist/ui/placement/launcher_drag.d.ts +6 -0
- package/dist/ui/placement/launcher_drag.d.ts.map +1 -1
- package/dist/ui/placement/panel_placement.d.ts +119 -0
- package/dist/ui/placement/panel_placement.d.ts.map +1 -0
- package/dist/ui/progress/run_announcer.d.ts +41 -0
- package/dist/ui/progress/run_announcer.d.ts.map +1 -0
- package/dist/ui/progress/subagent_progress.d.ts +58 -0
- package/dist/ui/progress/subagent_progress.d.ts.map +1 -0
- package/dist/ui/progress/tool_call_card.d.ts.map +1 -1
- package/dist/ui/shell/adopt_styles.d.ts +23 -0
- package/dist/ui/shell/adopt_styles.d.ts.map +1 -0
- package/dist/ui/shell/glyph_slot.d.ts +7 -0
- package/dist/ui/shell/glyph_slot.d.ts.map +1 -0
- package/dist/ui/shell/header_button.d.ts +10 -0
- package/dist/ui/shell/header_button.d.ts.map +1 -0
- package/dist/ui/shell/icon_element.d.ts +12 -0
- package/dist/ui/shell/icon_element.d.ts.map +1 -0
- package/dist/ui/shell/is_unread_badge_enabled.d.ts +10 -0
- package/dist/ui/shell/is_unread_badge_enabled.d.ts.map +1 -0
- package/dist/ui/shell/read_launcher_icon_url.d.ts +8 -0
- package/dist/ui/shell/read_launcher_icon_url.d.ts.map +1 -0
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/transcript/answer_actions.d.ts +39 -0
- package/dist/ui/transcript/answer_actions.d.ts.map +1 -0
- package/dist/ui/transcript/answer_stream.d.ts +56 -0
- package/dist/ui/transcript/answer_stream.d.ts.map +1 -0
- package/dist/ui/transcript/render_or_warn.d.ts +16 -0
- package/dist/ui/transcript/render_or_warn.d.ts.map +1 -0
- package/dist/ui/transcript/starter_chips.d.ts +21 -0
- package/dist/ui/transcript/starter_chips.d.ts.map +1 -0
- package/dist/ui/transcript/transcript.d.ts +171 -0
- package/dist/ui/transcript/transcript.d.ts.map +1 -0
- package/dist/ui/ui_strings.d.ts +29 -1
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +17 -15
- package/src/core/activity_registry.ts +133 -0
- package/src/core/ag_ui_chat.ts +945 -4419
- package/src/core/agui_client.ts +172 -16
- package/src/core/client_seed.ts +27 -0
- package/src/core/read_max_tool_rounds.ts +13 -0
- package/src/core/remote_conversation_store.ts +1 -1
- package/src/core/run_handlers.ts +501 -0
- package/src/core/storage_scope.ts +279 -0
- package/src/core/tool_outcome.ts +5 -3
- package/src/core/utils.ts +109 -0
- package/src/skills/skill_catalog.ts +166 -0
- package/src/skills/skill_name_from.ts +19 -0
- package/src/tools/tool_catalog.ts +354 -0
- package/src/tools/tool_dispatch.ts +521 -0
- package/src/ui/composer/attachment_tray.ts +6 -5
- package/src/ui/composer/auto_grow.ts +12 -0
- package/src/ui/composer/composer_attachments.ts +324 -0
- package/src/ui/composer/composer_voice.ts +93 -0
- package/src/ui/composer/voice_input.ts +4 -4
- package/src/ui/excerpts/transcript_quote_offer.ts +198 -0
- package/src/ui/fill_ui_string.ts +41 -0
- package/src/ui/history/conversation_history.ts +699 -0
- package/src/ui/history/relative_time.ts +5 -4
- package/src/ui/history/thread_drawer.ts +3 -50
- package/src/ui/interrupts/confirmation_card.ts +4 -2
- package/src/ui/interrupts/pending_decision.ts +34 -0
- package/src/ui/placement/is_collapsible_placement.ts +19 -0
- package/src/ui/placement/is_draggable_placement.ts +23 -0
- package/src/ui/placement/launcher_drag.ts +104 -89
- package/src/ui/placement/panel_placement.ts +1130 -0
- package/src/ui/progress/run_announcer.ts +67 -0
- package/src/ui/progress/subagent_progress.ts +201 -0
- package/src/ui/progress/tool_call_card.ts +2 -0
- package/src/ui/shell/adopt_styles.ts +28 -0
- package/src/ui/shell/glyph_slot.ts +12 -0
- package/src/ui/shell/header_button.ts +21 -0
- package/src/ui/shell/icon_element.ts +33 -0
- package/src/ui/shell/is_unread_badge_enabled.ts +11 -0
- package/src/ui/shell/read_launcher_icon_url.ts +9 -0
- package/src/ui/styles.ts +301 -70
- package/src/ui/transcript/answer_actions.ts +139 -0
- package/src/ui/transcript/answer_stream.ts +123 -0
- package/src/ui/transcript/render_or_warn.ts +22 -0
- package/src/ui/transcript/starter_chips.ts +42 -0
- package/src/ui/transcript/transcript.ts +429 -0
- package/src/ui/ui_strings.ts +38 -1
- package/src/version.ts +1 -1
package/src/ui/styles.ts
CHANGED
|
@@ -38,6 +38,12 @@ export const STYLES = `
|
|
|
38
38
|
--_header-fg: var(--ag-ui-header-fg, #ffffff);
|
|
39
39
|
--_border: var(--ag-ui-border, #e2e2ec);
|
|
40
40
|
--_radius: var(--ag-ui-radius, 12px);
|
|
41
|
+
/* The panel's own frame, which is the one corner a placement can put against
|
|
42
|
+
the edge of the viewport. The placements that do (page, sidebar, side, full,
|
|
43
|
+
and all but embedded on a small viewport) square this and leave --_radius alone, so
|
|
44
|
+
the answer well and the controls inside the panel stay as round as the
|
|
45
|
+
theme says: none of them meets that edge. */
|
|
46
|
+
--_panel-radius: var(--ag-ui-panel-radius, var(--_radius));
|
|
41
47
|
|
|
42
48
|
/* Body text and raised chrome, read only by the code-block copy button.
|
|
43
49
|
The defaults restate what it inherits, so a host that sets neither sees
|
|
@@ -84,6 +90,7 @@ export const STYLES = `
|
|
|
84
90
|
--_tool-icon-done: var(--ag-ui-tool-icon-done, "✓");
|
|
85
91
|
--_tool-icon-error: var(--ag-ui-tool-icon-error, "✕");
|
|
86
92
|
--_tool-icon-declined: var(--ag-ui-tool-icon-declined, "⊘");
|
|
93
|
+
--_tool-icon-interrupted: var(--ag-ui-tool-icon-interrupted, "◌");
|
|
87
94
|
|
|
88
95
|
/* Disclosure marks on every expandable row. Tokenised for the same reason
|
|
89
96
|
the status icons above are: a host re-theming one set and not the other
|
|
@@ -95,6 +102,7 @@ export const STYLES = `
|
|
|
95
102
|
/* Answer well (opt-in via data-answer-well) — boxes a whole assistant turn. */
|
|
96
103
|
--_well-bg: var(--ag-ui-well-bg, transparent);
|
|
97
104
|
--_well-border: var(--ag-ui-well-border, var(--_border));
|
|
105
|
+
--_well-radius: var(--ag-ui-well-radius, var(--_radius));
|
|
98
106
|
|
|
99
107
|
/* Surface — set --ag-ui-shadow: none for a flush, embedded panel. */
|
|
100
108
|
--_shadow: var(--ag-ui-shadow, 0 12px 32px rgba(20, 20, 50, 0.18));
|
|
@@ -172,7 +180,24 @@ export const STYLES = `
|
|
|
172
180
|
keyboard changes no viewport-percentage length -- not vh, not dvh, not svh
|
|
173
181
|
-- so a full-bleed panel on a phone has to be told the height rather than
|
|
174
182
|
deriving it. The value to publish there is the visual viewport's. */
|
|
175
|
-
--_viewport-height: var(
|
|
183
|
+
--_viewport-height: var(
|
|
184
|
+
--ag-ui-viewport-height,
|
|
185
|
+
calc(
|
|
186
|
+
min(var(--_visual-viewport-inset-top) + var(--_visual-viewport-height), 100dvh - var(--_viewport-inset-bottom)) -
|
|
187
|
+
var(--_viewport-inset-top) - var(--_keyboard-inset-top)
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
/* That default is the host's box cut to the part of the screen the user can
|
|
191
|
+
see: from the top the panel starts at, the host's reserved top moved down
|
|
192
|
+
by the keyboard inset below, to whichever is further up, the bottom of the
|
|
193
|
+
visible area or the host's reserved bottom. Where nothing has measured, the
|
|
194
|
+
visible area is the layout viewport with no pan, and this is the host's
|
|
195
|
+
box exactly.
|
|
196
|
+
|
|
197
|
+
Cut rather than replaced. A measured height used as the whole answer kept
|
|
198
|
+
a bar the host reserved at the top in the position and out of the height,
|
|
199
|
+
so the panel ran that far past the bottom of the visible area, with its
|
|
200
|
+
composer behind the keyboard. */
|
|
176
201
|
/* The measured height of the part of the screen the user can actually see,
|
|
177
202
|
written by the element from the visual viewport and falling back to the
|
|
178
203
|
layout viewport where nothing has measured yet.
|
|
@@ -185,11 +210,15 @@ export const STYLES = `
|
|
|
185
210
|
|
|
186
211
|
Separate from the token above so a host that states the usable height
|
|
187
212
|
outright still wins: the element writes this one inline, and an inline
|
|
188
|
-
value would otherwise outrank the host's own rule.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
213
|
+
value would otherwise outrank the host's own rule.
|
|
214
|
+
|
|
215
|
+
The fallback is dvh rather than vh because the browser's own bars do move
|
|
216
|
+
it, where a keyboard does not. iOS Safari resolves vh to the viewport with
|
|
217
|
+
its bars collapsed, so on a page that does not scroll them away a panel
|
|
218
|
+
sized from it ran under the address bar by the bars' height -- 40px on
|
|
219
|
+
the phone measured -- with a docked composer underneath. dvh is the
|
|
220
|
+
viewport with the bars as they are. */
|
|
221
|
+
--_visual-viewport-height: var(--ag-ui-visual-viewport-height, 100dvh);
|
|
193
222
|
/* How much of the layout viewport is hidden below the visible one, measured
|
|
194
223
|
and written by the element alongside the height above.
|
|
195
224
|
|
|
@@ -204,6 +233,24 @@ export const STYLES = `
|
|
|
204
233
|
A host wanting no keyboard lift at all sets --ag-ui-keyboard-inset: 0px. */
|
|
205
234
|
--_keyboard-inset: var(--ag-ui-keyboard-inset, var(--_visual-viewport-inset-bottom));
|
|
206
235
|
--_visual-viewport-inset-bottom: var(--ag-ui-visual-viewport-inset-bottom, 0px);
|
|
236
|
+
/* How far a panel anchored at the top moves down for a keyboard, below the
|
|
237
|
+
top the host reserved. With the same two-token shape as the lift above;
|
|
238
|
+
0px keeps the panel at the host's top.
|
|
239
|
+
|
|
240
|
+
To show the field being typed into, a browser pans the visible area down
|
|
241
|
+
the layout viewport, and a fixed element stays against the layout top, so
|
|
242
|
+
a panel that did not move showed only its lower part, from the pan down,
|
|
243
|
+
with its header off the screen and an empty band under it. It moves by
|
|
244
|
+
what the pan goes past the host's reserved top, not by the whole pan: the
|
|
245
|
+
pan scrolls a bar reserved there away with the rest of the page rather
|
|
246
|
+
than pushing it down, and adding the two put the panel a whole bar below
|
|
247
|
+
the visible area. A corner panel anchored at the bottom does not use this,
|
|
248
|
+
because the band below already accounts for the pan. */
|
|
249
|
+
--_keyboard-inset-top: var(--ag-ui-keyboard-inset-top, max(0px, var(--_visual-viewport-inset-top) - var(--_viewport-inset-top)));
|
|
250
|
+
/* How much of the layout viewport is hidden above the visible one: how far
|
|
251
|
+
the browser panned. Measured and written by the element with the two
|
|
252
|
+
above. */
|
|
253
|
+
--_visual-viewport-inset-top: var(--ag-ui-visual-viewport-inset-top, 0px);
|
|
207
254
|
--_viewport-width: var(
|
|
208
255
|
--ag-ui-viewport-width,
|
|
209
256
|
calc(100vw - var(--_viewport-inset-left) - var(--_viewport-inset-right))
|
|
@@ -254,11 +301,14 @@ export const STYLES = `
|
|
|
254
301
|
--_max-height: var(--ag-ui-max-height, calc(var(--_viewport-height) - var(--_edge-gutter)));
|
|
255
302
|
/* Reading-column width for placement="page" (full-bleed, centred content). */
|
|
256
303
|
--_content-max-width: var(--ag-ui-content-max-width, 820px);
|
|
304
|
+
/* The greeting over an empty full-page conversation. The size scales with
|
|
305
|
+
the viewport between a phone and a desktop rather than stepping at a
|
|
306
|
+
breakpoint. */
|
|
307
|
+
--_greeting-font: var(--ag-ui-greeting-font, var(--_font));
|
|
308
|
+
--_greeting-size: var(--ag-ui-greeting-size, clamp(1.5rem, 4vw, 2.25rem));
|
|
257
309
|
/* Slim rail the sidebar placement collapses to. Only that placement reads
|
|
258
310
|
it, but it is declared here so every alias has a default in one place. */
|
|
259
311
|
--_rail-width: var(--ag-ui-rail-width, 52px);
|
|
260
|
-
/* Width of the docked conversation list on a full-page chat. */
|
|
261
|
-
--_threads-rail-width: var(--ag-ui-threads-rail-width, 280px);
|
|
262
312
|
|
|
263
313
|
position: var(--_position);
|
|
264
314
|
inset: var(--_inset);
|
|
@@ -337,23 +387,24 @@ export const STYLES = `
|
|
|
337
387
|
}
|
|
338
388
|
|
|
339
389
|
:host([placement="side"]) {
|
|
340
|
-
--_inset: var(--ag-ui-inset, var(--_viewport-inset-top) var(--_viewport-inset-right) var(--_viewport-inset-bottom) auto);
|
|
390
|
+
--_inset: var(--ag-ui-inset, calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right) var(--_viewport-inset-bottom) auto);
|
|
341
391
|
--_width: var(--ag-ui-width, 420px);
|
|
342
392
|
--_height: var(--ag-ui-height, var(--_viewport-height));
|
|
343
393
|
--_max-height: var(--ag-ui-max-height, var(--_viewport-height));
|
|
344
|
-
--
|
|
394
|
+
--_panel-radius: var(--ag-ui-panel-radius, 0);
|
|
345
395
|
}
|
|
346
396
|
|
|
347
397
|
:host([placement="full"]) {
|
|
348
398
|
--_inset: var(
|
|
349
399
|
--ag-ui-inset,
|
|
350
|
-
var(--_viewport-inset-top)
|
|
400
|
+
calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right)
|
|
401
|
+
var(--_viewport-inset-bottom) var(--_viewport-inset-left)
|
|
351
402
|
);
|
|
352
403
|
--_width: var(--ag-ui-width, var(--_viewport-width));
|
|
353
404
|
--_height: var(--ag-ui-height, var(--_viewport-height));
|
|
354
405
|
--_max-width: var(--ag-ui-max-width, var(--_viewport-width));
|
|
355
406
|
--_max-height: var(--ag-ui-max-height, var(--_viewport-height));
|
|
356
|
-
--
|
|
407
|
+
--_panel-radius: var(--ag-ui-panel-radius, 0);
|
|
357
408
|
}
|
|
358
409
|
|
|
359
410
|
/* Page: full-bleed background with a centred reading column capped at
|
|
@@ -364,13 +415,14 @@ export const STYLES = `
|
|
|
364
415
|
:host([placement="page"]) {
|
|
365
416
|
--_inset: var(
|
|
366
417
|
--ag-ui-inset,
|
|
367
|
-
var(--_viewport-inset-top)
|
|
418
|
+
calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right)
|
|
419
|
+
var(--_viewport-inset-bottom) var(--_viewport-inset-left)
|
|
368
420
|
);
|
|
369
421
|
--_width: var(--ag-ui-width, var(--_viewport-width));
|
|
370
422
|
--_height: var(--ag-ui-height, var(--_viewport-height));
|
|
371
423
|
--_max-width: var(--ag-ui-max-width, var(--_viewport-width));
|
|
372
424
|
--_max-height: var(--ag-ui-max-height, var(--_viewport-height));
|
|
373
|
-
--
|
|
425
|
+
--_panel-radius: var(--ag-ui-panel-radius, 0);
|
|
374
426
|
}
|
|
375
427
|
|
|
376
428
|
:host([placement="page"]) .messages {
|
|
@@ -430,7 +482,7 @@ export const STYLES = `
|
|
|
430
482
|
:host([placement=""]:not([data-small-viewport="off"])) {
|
|
431
483
|
--_inset: var(
|
|
432
484
|
--ag-ui-inset,
|
|
433
|
-
var(--_viewport-inset-top) var(--_viewport-inset-right)
|
|
485
|
+
calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right)
|
|
434
486
|
calc(var(--_viewport-inset-bottom) + var(--_keyboard-inset))
|
|
435
487
|
var(--_viewport-inset-left)
|
|
436
488
|
);
|
|
@@ -438,7 +490,7 @@ export const STYLES = `
|
|
|
438
490
|
--_height: var(--ag-ui-height, var(--_viewport-height));
|
|
439
491
|
--_max-width: var(--ag-ui-max-width, var(--_viewport-width));
|
|
440
492
|
--_max-height: var(--ag-ui-max-height, var(--_viewport-height));
|
|
441
|
-
--
|
|
493
|
+
--_panel-radius: var(--ag-ui-panel-radius, 0);
|
|
442
494
|
--_shadow: var(--ag-ui-shadow, none);
|
|
443
495
|
}
|
|
444
496
|
|
|
@@ -460,16 +512,20 @@ export const STYLES = `
|
|
|
460
512
|
--ag-ui-position: static (and place this element in your own layout) for a
|
|
461
513
|
host-managed push instead. */
|
|
462
514
|
:host([placement="sidebar"]) {
|
|
463
|
-
--_inset: var(--ag-ui-inset, var(--_viewport-inset-top) var(--_viewport-inset-right) var(--_viewport-inset-bottom) auto);
|
|
515
|
+
--_inset: var(--ag-ui-inset, calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right) var(--_viewport-inset-bottom) auto);
|
|
464
516
|
--_width: var(--ag-ui-width, 420px);
|
|
465
517
|
--_height: var(--ag-ui-height, var(--_viewport-height));
|
|
466
518
|
--_max-height: var(--ag-ui-max-height, var(--_viewport-height));
|
|
467
|
-
--
|
|
519
|
+
--_panel-radius: var(--ag-ui-panel-radius, 0);
|
|
468
520
|
transition: width var(--_motion) var(--_ease);
|
|
469
521
|
}
|
|
470
522
|
|
|
471
523
|
:host([placement="sidebar"][data-side="left"]) {
|
|
472
|
-
--_inset: var(
|
|
524
|
+
--_inset: var(
|
|
525
|
+
--ag-ui-inset,
|
|
526
|
+
calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) auto var(--_viewport-inset-bottom)
|
|
527
|
+
var(--_viewport-inset-left)
|
|
528
|
+
);
|
|
473
529
|
}
|
|
474
530
|
|
|
475
531
|
/* The docked panel is pinned to the edge it docks against rather than filling
|
|
@@ -742,7 +798,7 @@ export const STYLES = `
|
|
|
742
798
|
min-height: 0;
|
|
743
799
|
background: var(--_bg);
|
|
744
800
|
border: 1px solid var(--_border);
|
|
745
|
-
border-radius: var(--
|
|
801
|
+
border-radius: var(--_panel-radius);
|
|
746
802
|
box-shadow: var(--_shadow);
|
|
747
803
|
overflow: hidden;
|
|
748
804
|
}
|
|
@@ -957,48 +1013,6 @@ export const STYLES = `
|
|
|
957
1013
|
display: none;
|
|
958
1014
|
}
|
|
959
1015
|
|
|
960
|
-
/* A docked conversation list: beside the transcript rather than over it.
|
|
961
|
-
|
|
962
|
-
A full-page chat is the one surface with width to spare, and covering the
|
|
963
|
-
conversation to show the list of conversations is the wrong trade there --
|
|
964
|
-
it hides the thing you are trying to get back to. Everywhere else the panel
|
|
965
|
-
is a few hundred pixels wide and a docked list would leave a column of
|
|
966
|
-
transcript narrower than the messages in it, so this is the only placement
|
|
967
|
-
that gets it.
|
|
968
|
-
|
|
969
|
-
The transcript is moved by padding on the shell rather than by making the
|
|
970
|
-
list a flex sibling: the drawer is the last child of the panel, and no
|
|
971
|
-
selector reaches backwards from it to the rows that have to shift. That is
|
|
972
|
-
also why the state is stamped on the host. */
|
|
973
|
-
:host([data-threads-docked]) .drawer {
|
|
974
|
-
/* Not a scrim: the page behind it is still the user's to work in. */
|
|
975
|
-
pointer-events: none;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
:host([data-threads-docked]) .drawer-backdrop {
|
|
979
|
-
display: none;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
:host([data-threads-docked]) .drawer-panel {
|
|
983
|
-
width: var(--_threads-rail-width);
|
|
984
|
-
pointer-events: auto;
|
|
985
|
-
border-inline-end: 1px solid var(--_border);
|
|
986
|
-
box-shadow: none;
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
:host([data-threads-docked]) .messages,
|
|
990
|
-
:host([data-threads-docked]) .input-row,
|
|
991
|
-
:host([data-threads-docked]) .skill-chips,
|
|
992
|
-
:host([data-threads-docked]) .attachment-tray {
|
|
993
|
-
padding-inline-start: calc(
|
|
994
|
-
var(--_threads-rail-width) + max(var(--_pad), (100% - var(--_content-max-width)) / 2)
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
:host([data-threads-docked]) .header {
|
|
999
|
-
padding-inline-start: calc(var(--_threads-rail-width) + var(--_pad));
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
1016
|
/* The page placement has no collapsed state, so it offers no control for one.
|
|
1003
1017
|
A dedicated route has no "away" to go to: shrinking it left a strip of
|
|
1004
1018
|
application chrome fixed over a route that no longer had an owner, and this
|
|
@@ -1176,6 +1190,162 @@ export const STYLES = `
|
|
|
1176
1190
|
display: none;
|
|
1177
1191
|
}
|
|
1178
1192
|
|
|
1193
|
+
/* The greeting layout: a greeting over an empty conversation, and the
|
|
1194
|
+
composer centred beneath it until the conversation has something in it.
|
|
1195
|
+
|
|
1196
|
+
On by default for the page placement, where a conversation is the whole
|
|
1197
|
+
route; data-greeting="off" turns it off there. An embedded panel opts in
|
|
1198
|
+
with any other data-greeting value. No other placement has it: a panel
|
|
1199
|
+
opened from a launcher is already mid-task, and a greeting in a corner
|
|
1200
|
+
panel costs space and says little.
|
|
1201
|
+
|
|
1202
|
+
The greeting itself renders under every placement and is hidden here, so a
|
|
1203
|
+
placement switch at runtime needs nothing from script. */
|
|
1204
|
+
.greeting {
|
|
1205
|
+
display: none;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
:host([placement="page"]:not([data-greeting="off"])) .greeting,
|
|
1209
|
+
:host([placement="embedded"][data-greeting]:not([data-greeting="off"])) .greeting {
|
|
1210
|
+
display: block;
|
|
1211
|
+
margin-block-end: calc(var(--_space) * 2);
|
|
1212
|
+
font-family: var(--_greeting-font);
|
|
1213
|
+
font-size: var(--_greeting-size);
|
|
1214
|
+
font-weight: 500;
|
|
1215
|
+
line-height: 1.2;
|
|
1216
|
+
color: var(--_fg);
|
|
1217
|
+
overflow-wrap: anywhere;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/* Centring is two equal flexible boxes either side of the composer rows: the
|
|
1221
|
+
transcript above, which already grows, and this one below. Equal zero bases
|
|
1222
|
+
make the two halves the same height whatever they hold, so the rows sit in
|
|
1223
|
+
the middle of the panel below its header, and stay there as a draft, an
|
|
1224
|
+
attachment tray or a hint grows them, because growth splits into both
|
|
1225
|
+
halves at once.
|
|
1226
|
+
|
|
1227
|
+
A pseudo element rather than a node, so no host layout and nothing that
|
|
1228
|
+
counts children can see it. It exists whether or not the conversation is
|
|
1229
|
+
empty and only its growth changes, because a box that appears and
|
|
1230
|
+
disappears is a box nothing can ever transition.
|
|
1231
|
+
|
|
1232
|
+
Only the rule that generates it names the placements. The one that grows it
|
|
1233
|
+
needs nothing but the state: where the first rule does not apply there is no
|
|
1234
|
+
box to grow. The box is empty, so its automatic basis is already zero, the
|
|
1235
|
+
same as the transcript's; nothing here writes the flex shorthand, because
|
|
1236
|
+
this selector is the more specific of the two and a shorthand would win
|
|
1237
|
+
over the growth. */
|
|
1238
|
+
:host([placement="page"]:not([data-greeting="off"])) .chat::after,
|
|
1239
|
+
:host([placement="embedded"][data-greeting]:not([data-greeting="off"])) .chat::after {
|
|
1240
|
+
content: "";
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/* Not while a restore is in flight: a conversation a remote store is still
|
|
1244
|
+
fetching is more likely to have messages than not, and centring it first
|
|
1245
|
+
would drop the composer the moment they land. */
|
|
1246
|
+
:host([data-empty]:not([data-restoring])) .chat::after {
|
|
1247
|
+
flex-grow: 1;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/* Only the send that leaves the empty state travels. The element arms this for
|
|
1251
|
+
that one change and disarms it whenever the transcript is cleared, so a
|
|
1252
|
+
restored conversation, a thread switch and a new chat all snap. Growth rather
|
|
1253
|
+
than a transform, because the rows between the transcript and the composer
|
|
1254
|
+
move with it, and growth is continuous where a reorder is not. Reduced motion
|
|
1255
|
+
needs no rule of its own: the motion token already collapses to a frame
|
|
1256
|
+
there.
|
|
1257
|
+
|
|
1258
|
+
The rule over the composer fades in over the same stretch. Appearing at once,
|
|
1259
|
+
it was drawn across the page at the composer's destination while the
|
|
1260
|
+
composer was still on its way there. */
|
|
1261
|
+
:host([data-composer-settling]) .chat::after {
|
|
1262
|
+
transition: flex-grow var(--_motion) var(--_ease);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
:host([data-composer-settling]) .input-row {
|
|
1266
|
+
transition: border-color var(--_motion) var(--_ease);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/* The greeting and whatever the host slotted sit at the foot of the upper
|
|
1270
|
+
half, directly over the composer, rather than floating in its middle. The
|
|
1271
|
+
region is hidden once the conversation has content, so this needs no state
|
|
1272
|
+
of its own. */
|
|
1273
|
+
:host([placement="page"]:not([data-greeting="off"]):not([data-restoring])) .empty,
|
|
1274
|
+
:host([placement="embedded"][data-greeting]:not([data-greeting="off"]):not([data-restoring])) .empty {
|
|
1275
|
+
margin: auto auto 0;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/* The rule over the composer separates it from a transcript. Over nothing it
|
|
1279
|
+
is a line drawn across the middle of the page. */
|
|
1280
|
+
:host([placement="page"][data-empty]:not([data-greeting="off"]):not([data-restoring])) .input-row,
|
|
1281
|
+
:host([placement="embedded"][data-greeting][data-empty]:not([data-greeting="off"]):not([data-restoring])) .input-row {
|
|
1282
|
+
border-block-start-color: transparent;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/* On a phone the composer stays at the foot and the greeting takes the room
|
|
1286
|
+
above it, which is the other way round from everything above.
|
|
1287
|
+
|
|
1288
|
+
Centring is a shape for a screen with room to spare: the composer reads as
|
|
1289
|
+
the one thing on the page, and whichever way the rows grow there is space
|
|
1290
|
+
left on both sides of them. A phone has no space to spare, and the keyboard
|
|
1291
|
+
is what takes it -- opening one leaves a 426px-tall visible area with the
|
|
1292
|
+
composer halfway up it and an empty band underneath, which is the half of
|
|
1293
|
+
the screen a thumb is already resting on. Docked, the field sits directly
|
|
1294
|
+
over the keyboard, where every chat on the platform puts it, and the
|
|
1295
|
+
greeting keeps the rest.
|
|
1296
|
+
|
|
1297
|
+
Both rules restate the selector they override, with the breakpoint's own
|
|
1298
|
+
opt-out added: this is part of the shape a host keeps its desktop layout
|
|
1299
|
+
instead of, so it goes the same way as the rest of that shape. Restating
|
|
1300
|
+
rather than adding conditions to the originals keeps each of those rules
|
|
1301
|
+
readable as one decision, and source order settles the pair. */
|
|
1302
|
+
@media (max-width: 600px) {
|
|
1303
|
+
:host([data-empty]:not([data-restoring]):not([data-small-viewport="off"])) .chat::after {
|
|
1304
|
+
flex-grow: 0;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/* The empty region fills the transcript instead of being centred in it as a
|
|
1308
|
+
block, so the two things in it can go to different places: the prompts the
|
|
1309
|
+
host offers to the foot, against the composer, and the greeting to the
|
|
1310
|
+
middle of what they leave.
|
|
1311
|
+
|
|
1312
|
+
A prompt chip is a way into the conversation. Against the field it starts,
|
|
1313
|
+
it reads as one; under the greeting halfway up the panel, with the field
|
|
1314
|
+
at the foot, it reads as decoration next to something else. Nothing here
|
|
1315
|
+
moves them on a screen where the composer is still centred: there the
|
|
1316
|
+
whole region hangs at the foot of the upper half, directly over the
|
|
1317
|
+
composer, and the prompts are already against it.
|
|
1318
|
+
|
|
1319
|
+
Auto block margins on the greeting are what centre it, and they take the
|
|
1320
|
+
free space whether or not there are prompts below to leave any, so a host
|
|
1321
|
+
that offers none gets a greeting in the middle of the transcript rather
|
|
1322
|
+
than one clinging to the composer.
|
|
1323
|
+
The region declares a display here, which outranks the rule collapsing a
|
|
1324
|
+
hidden one, so it says it is not hidden itself. Nothing sets the two
|
|
1325
|
+
apart today -- the transcript writes the hidden property and the host's
|
|
1326
|
+
data-empty from one expression -- and that is exactly why: a rule holding
|
|
1327
|
+
because of how a method in another file happens to be written is a rule
|
|
1328
|
+
holding by luck. */
|
|
1329
|
+
:host([placement="page"][data-empty]:not([data-greeting="off"]):not([data-restoring]):not([data-small-viewport="off"])) .empty:not([hidden]),
|
|
1330
|
+
:host([placement="embedded"][data-greeting][data-empty]:not([data-greeting="off"]):not([data-restoring]):not([data-small-viewport="off"])) .empty:not([hidden]) {
|
|
1331
|
+
margin: 0;
|
|
1332
|
+
flex: 1;
|
|
1333
|
+
display: flex;
|
|
1334
|
+
flex-direction: column;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
:host([placement="page"][data-empty]:not([data-greeting="off"]):not([data-restoring]):not([data-small-viewport="off"])) .greeting,
|
|
1338
|
+
:host([placement="embedded"][data-greeting][data-empty]:not([data-greeting="off"]):not([data-restoring]):not([data-small-viewport="off"])) .greeting {
|
|
1339
|
+
margin-block: auto;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
/* Nor does the greeting paint while a restore is in flight. Hidden rather than
|
|
1344
|
+
removed, so it keeps its place and nothing around it moves when it shows. */
|
|
1345
|
+
:host([data-restoring]) .greeting {
|
|
1346
|
+
visibility: hidden;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1179
1349
|
/* ── Answer group / well ─────────────────────────────────────────
|
|
1180
1350
|
One .answer per assistant turn wraps the streamed text, its tool cards,
|
|
1181
1351
|
and the pending indicator so a whole answer reads (and can be boxed) as one
|
|
@@ -1194,7 +1364,7 @@ export const STYLES = `
|
|
|
1194
1364
|
padding: var(--_pad);
|
|
1195
1365
|
background: var(--_well-bg);
|
|
1196
1366
|
border: 1px solid var(--_well-border);
|
|
1197
|
-
border-radius: var(--
|
|
1367
|
+
border-radius: var(--_well-radius);
|
|
1198
1368
|
}
|
|
1199
1369
|
|
|
1200
1370
|
.message {
|
|
@@ -1591,6 +1761,13 @@ export const STYLES = `
|
|
|
1591
1761
|
color: var(--_muted);
|
|
1592
1762
|
}
|
|
1593
1763
|
|
|
1764
|
+
/* Not finished: muted like declined, since nothing failed, but its own glyph,
|
|
1765
|
+
since nobody refused it either. */
|
|
1766
|
+
.tool-call[data-status="interrupted"] .tool-call-icon::before {
|
|
1767
|
+
content: var(--_tool-icon-interrupted);
|
|
1768
|
+
color: var(--_muted);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1594
1771
|
@media (prefers-reduced-motion: reduce) {
|
|
1595
1772
|
.tool-call[data-status="pending"] .tool-call-icon {
|
|
1596
1773
|
animation: none;
|
|
@@ -1629,7 +1806,8 @@ export const STYLES = `
|
|
|
1629
1806
|
color: var(--_danger);
|
|
1630
1807
|
}
|
|
1631
1808
|
|
|
1632
|
-
.tool-call[data-status="declined"] .tool-call-status
|
|
1809
|
+
.tool-call[data-status="declined"] .tool-call-status,
|
|
1810
|
+
.tool-call[data-status="interrupted"] .tool-call-status {
|
|
1633
1811
|
color: var(--_muted);
|
|
1634
1812
|
}
|
|
1635
1813
|
|
|
@@ -1645,6 +1823,17 @@ export const STYLES = `
|
|
|
1645
1823
|
gap: 3px;
|
|
1646
1824
|
}
|
|
1647
1825
|
|
|
1826
|
+
/* A call with no arguments drops the region rather than framing an empty
|
|
1827
|
+
object, and a settled card drops the result region until it has one -- both
|
|
1828
|
+
by setting the hidden property, and an author display beats the user-agent
|
|
1829
|
+
rule for that attribute. Without this the region kept laying out: 42px of
|
|
1830
|
+
card holding the ARGUMENTS heading over nothing, on every call the agent
|
|
1831
|
+
made with no arguments, in the display mode that shows arguments by
|
|
1832
|
+
default. */
|
|
1833
|
+
.tool-call-section[hidden] {
|
|
1834
|
+
display: none;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1648
1837
|
/* The heading that tells the two payloads apart. Without it the arguments and
|
|
1649
1838
|
the result were one run of text and a reader had to guess the boundary. */
|
|
1650
1839
|
.tool-call-section-label {
|
|
@@ -2315,8 +2504,8 @@ export const STYLES = `
|
|
|
2315
2504
|
display: none;
|
|
2316
2505
|
}
|
|
2317
2506
|
|
|
2318
|
-
/* The mic button's mount point; filled only once
|
|
2319
|
-
control. */
|
|
2507
|
+
/* The mic button's mount point; filled only once ComposerVoice.wire mounts
|
|
2508
|
+
the control, which it skips unless transcription is configured. */
|
|
2320
2509
|
.voice-slot {
|
|
2321
2510
|
display: contents;
|
|
2322
2511
|
}
|
|
@@ -2993,6 +3182,15 @@ export const STYLES = `
|
|
|
2993
3182
|
padding: 10px 12px;
|
|
2994
3183
|
}
|
|
2995
3184
|
|
|
3185
|
+
/* The row is hidden whenever the host offers no skills, which is most elements
|
|
3186
|
+
most of the time, and an author display beats the user-agent rule for the
|
|
3187
|
+
hidden attribute. Without this it kept its padding: 20px of panel between
|
|
3188
|
+
the transcript and the composer, under every placement, reading as the gap
|
|
3189
|
+
under whatever the transcript ends with. */
|
|
3190
|
+
.skill-chips[hidden] {
|
|
3191
|
+
display: none;
|
|
3192
|
+
}
|
|
3193
|
+
|
|
2996
3194
|
.skill-chip {
|
|
2997
3195
|
border: 1px solid var(--_border);
|
|
2998
3196
|
border-radius: 999px;
|
|
@@ -3050,6 +3248,14 @@ export const STYLES = `
|
|
|
3050
3248
|
box-shadow: 0 8px 24px rgba(20, 20, 50, 0.16);
|
|
3051
3249
|
}
|
|
3052
3250
|
|
|
3251
|
+
/* Closed nearly all of the time and mounted on every element. The display
|
|
3252
|
+
above beats the user-agent rule for the hidden attribute, so without this a
|
|
3253
|
+
closed palette still painted its margin, border and shadow as a line over
|
|
3254
|
+
the composer. */
|
|
3255
|
+
.skill-palette[hidden] {
|
|
3256
|
+
display: none;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3053
3259
|
.skill-item {
|
|
3054
3260
|
display: flex;
|
|
3055
3261
|
flex-direction: column;
|
|
@@ -3096,17 +3302,26 @@ export const STYLES = `
|
|
|
3096
3302
|
inset: 0;
|
|
3097
3303
|
z-index: 5;
|
|
3098
3304
|
display: flex;
|
|
3099
|
-
|
|
3305
|
+
/* Visible at once on the way in. Opening moves focus to the list in the
|
|
3306
|
+
same task that unhides it, and an element still hidden cannot take focus:
|
|
3307
|
+
a transition on visibility starts at its first value, so easing it in
|
|
3308
|
+
left the drawer hidden at exactly that instant and the focus call was
|
|
3309
|
+
dropped at every placement. Reduced motion shortens the transition
|
|
3310
|
+
without removing it, so it had the same first frame. */
|
|
3311
|
+
transition: visibility 0s;
|
|
3100
3312
|
}
|
|
3101
3313
|
|
|
3102
3314
|
/* Closed. The overlay keeps its box (display, not none) so the backdrop and
|
|
3103
3315
|
panel inside it stay rendered and can transition both ways; visibility is
|
|
3104
3316
|
what takes the whole subtree out of the tab order, the a11y tree and hit
|
|
3105
|
-
testing at rest
|
|
3317
|
+
testing at rest. On the way out it is delayed rather than eased, so it
|
|
3318
|
+
flips only once the slide has finished. A transition is read from the
|
|
3319
|
+
state being entered, which is what lets the two directions differ. */
|
|
3106
3320
|
.drawer[hidden] {
|
|
3107
3321
|
display: flex;
|
|
3108
3322
|
visibility: hidden;
|
|
3109
3323
|
pointer-events: none;
|
|
3324
|
+
transition: visibility 0s var(--_motion);
|
|
3110
3325
|
}
|
|
3111
3326
|
|
|
3112
3327
|
.drawer[hidden] .drawer-backdrop {
|
|
@@ -3135,20 +3350,29 @@ export const STYLES = `
|
|
|
3135
3350
|
/* Scrolling past the end of this must not scroll the page behind it. */
|
|
3136
3351
|
overscroll-behavior: contain;
|
|
3137
3352
|
transform-origin: top center;
|
|
3353
|
+
/* Visibility flips at once on the way in, for the drawer's reason: opening
|
|
3354
|
+
focuses the panel in the same task that unhides it, and an element still
|
|
3355
|
+
hidden cannot take focus. The fade and the scale still ease in. */
|
|
3138
3356
|
transition:
|
|
3139
3357
|
opacity var(--_motion) var(--_ease),
|
|
3140
3358
|
transform var(--_motion) var(--_ease),
|
|
3141
|
-
visibility
|
|
3359
|
+
visibility 0s;
|
|
3142
3360
|
}
|
|
3143
3361
|
|
|
3144
3362
|
/* Same idiom as the drawer: laid out at rest, hidden by visibility, so the
|
|
3145
|
-
popover can animate open and closed.
|
|
3363
|
+
popover can animate open and closed. The whole list is restated because a
|
|
3364
|
+
transition is read from the state being entered; only visibility differs,
|
|
3365
|
+
delayed rather than eased so it flips once the exit has played. */
|
|
3146
3366
|
.checkpoints[hidden] {
|
|
3147
3367
|
display: flex;
|
|
3148
3368
|
visibility: hidden;
|
|
3149
3369
|
pointer-events: none;
|
|
3150
3370
|
opacity: 0;
|
|
3151
3371
|
transform: scale(0.96) translateY(-6px);
|
|
3372
|
+
transition:
|
|
3373
|
+
opacity var(--_motion) var(--_ease),
|
|
3374
|
+
transform var(--_motion) var(--_ease),
|
|
3375
|
+
visibility 0s var(--_motion);
|
|
3152
3376
|
}
|
|
3153
3377
|
|
|
3154
3378
|
.checkpoints-title {
|
|
@@ -3346,6 +3570,13 @@ export const STYLES = `
|
|
|
3346
3570
|
padding: 0 var(--_pad) var(--_space);
|
|
3347
3571
|
}
|
|
3348
3572
|
|
|
3573
|
+
/* Empty whenever no run is in flight, and hidden then. Same reason as the
|
|
3574
|
+
palette: the display above would otherwise keep the bottom padding as dead
|
|
3575
|
+
space over the composer. */
|
|
3576
|
+
.queued[hidden] {
|
|
3577
|
+
display: none;
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3349
3580
|
.queued-chip {
|
|
3350
3581
|
max-width: 100%;
|
|
3351
3582
|
overflow: hidden;
|