@artooi/ag-ui-web-component 0.23.0 → 0.24.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 +149 -36
- package/README.md +140 -9
- package/dist/ag-ui-web-component.bundle.js +113 -122
- package/dist/ag-ui-web-component.bundle.js.map +3 -3
- package/dist/constants.d.ts +71 -85
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +105 -127
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +24 -30
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/core/attachment.d.ts +9 -14
- package/dist/core/attachment.d.ts.map +1 -1
- package/dist/core/conversation_store.d.ts +38 -27
- package/dist/core/conversation_store.d.ts.map +1 -1
- package/dist/core/create_http_agent.d.ts +13 -15
- package/dist/core/create_http_agent.d.ts.map +1 -1
- package/dist/core/remote_conversation_store.d.ts +10 -9
- package/dist/core/remote_conversation_store.d.ts.map +1 -1
- package/dist/core/run_index.d.ts +11 -20
- package/dist/core/run_index.d.ts.map +1 -1
- package/dist/core/transcribe_audio.d.ts +8 -8
- package/dist/core/transcribe_audio.d.ts.map +1 -1
- package/dist/core/upload_attachment.d.ts +15 -18
- package/dist/core/upload_attachment.d.ts.map +1 -1
- package/dist/core/utils.d.ts +4 -6
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/dom/animations.d.ts +22 -30
- package/dist/dom/animations.d.ts.map +1 -1
- package/dist/dom/dom_driver.d.ts +7 -7
- package/dist/dom/native_setter.d.ts +2 -2
- package/dist/dom/native_setter.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +357 -383
- package/dist/index.js.map +2 -2
- package/dist/skills/fill_template.d.ts +4 -5
- package/dist/skills/fill_template.d.ts.map +1 -1
- package/dist/skills/parse_skills.d.ts.map +1 -1
- package/dist/skills/skill.d.ts +7 -8
- package/dist/skills/skill.d.ts.map +1 -1
- package/dist/tools/client_tool_registry.d.ts +2 -2
- package/dist/tools/page_action_tools.d.ts +7 -10
- package/dist/tools/page_action_tools.d.ts.map +1 -1
- package/dist/tools/page_state.d.ts +5 -8
- package/dist/tools/page_state.d.ts.map +1 -1
- package/dist/tools/route_map.d.ts +7 -10
- package/dist/tools/route_map.d.ts.map +1 -1
- package/dist/ui/approval_card.d.ts +15 -20
- package/dist/ui/approval_card.d.ts.map +1 -1
- package/dist/ui/attach_copy_buttons.d.ts +4 -10
- package/dist/ui/attach_copy_buttons.d.ts.map +1 -1
- package/dist/ui/attachment_chips.d.ts +7 -10
- package/dist/ui/attachment_chips.d.ts.map +1 -1
- package/dist/ui/attachment_tray.d.ts +6 -6
- package/dist/ui/checkpoint_menu.d.ts +7 -8
- package/dist/ui/checkpoint_menu.d.ts.map +1 -1
- package/dist/ui/confirmation_card.d.ts +10 -15
- package/dist/ui/confirmation_card.d.ts.map +1 -1
- package/dist/ui/question_card.d.ts +12 -15
- package/dist/ui/question_card.d.ts.map +1 -1
- package/dist/ui/relative_time.d.ts +5 -7
- package/dist/ui/relative_time.d.ts.map +1 -1
- package/dist/ui/render_markdown.d.ts +8 -8
- package/dist/ui/render_markdown.d.ts.map +1 -1
- package/dist/ui/resize_handle.d.ts +21 -34
- package/dist/ui/resize_handle.d.ts.map +1 -1
- package/dist/ui/run_notice.d.ts +5 -7
- package/dist/ui/run_notice.d.ts.map +1 -1
- package/dist/ui/skills_menu.d.ts +4 -5
- package/dist/ui/skills_menu.d.ts.map +1 -1
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/thoughts_block.d.ts +9 -11
- package/dist/ui/thoughts_block.d.ts.map +1 -1
- package/dist/ui/thread_drawer.d.ts +6 -5
- package/dist/ui/thread_drawer.d.ts.map +1 -1
- package/dist/ui/tool_call_card.d.ts +17 -25
- package/dist/ui/tool_call_card.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +6 -12
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/dist/ui/voice_input.d.ts +10 -11
- package/dist/ui/voice_input.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +74 -87
- package/src/core/ag_ui_chat.ts +313 -269
- package/src/core/agui_client.ts +60 -71
- package/src/core/attachment.ts +9 -14
- package/src/core/conversation_store.ts +55 -33
- package/src/core/create_http_agent.ts +18 -22
- package/src/core/remote_conversation_store.ts +28 -15
- package/src/core/run_index.ts +14 -23
- package/src/core/transcribe_audio.ts +9 -10
- package/src/core/upload_attachment.ts +18 -21
- package/src/core/utils.ts +4 -6
- package/src/dom/animations.ts +33 -43
- package/src/dom/dom_driver.ts +7 -7
- package/src/dom/native_setter.ts +11 -12
- package/src/index.ts +3 -0
- package/src/skills/fill_template.ts +4 -5
- package/src/skills/parse_skills.ts +3 -4
- package/src/skills/skill.ts +7 -8
- package/src/tools/client_tool_registry.ts +2 -2
- package/src/tools/page_action_tools.ts +12 -15
- package/src/tools/page_state.ts +5 -8
- package/src/tools/route_map.ts +15 -19
- package/src/ui/approval_card.ts +15 -20
- package/src/ui/attach_copy_buttons.ts +9 -18
- package/src/ui/attachment_chips.ts +7 -10
- package/src/ui/attachment_tray.ts +6 -6
- package/src/ui/checkpoint_menu.ts +7 -8
- package/src/ui/confirmation_card.ts +10 -15
- package/src/ui/question_card.ts +12 -15
- package/src/ui/relative_time.ts +5 -7
- package/src/ui/render_markdown.ts +25 -51
- package/src/ui/resize_handle.ts +25 -38
- package/src/ui/run_notice.ts +9 -12
- package/src/ui/skills_menu.ts +4 -5
- package/src/ui/styles.ts +92 -101
- package/src/ui/thoughts_block.ts +11 -13
- package/src/ui/thread_drawer.ts +6 -5
- package/src/ui/tool_call_card.ts +22 -32
- package/src/ui/ui_strings.ts +6 -12
- package/src/ui/voice_input.ts +10 -11
- package/src/version.ts +1 -1
package/src/ui/skills_menu.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { Skill } from "../skills/skill.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The two skill surfaces over a single catalog: a
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* {@link
|
|
8
|
-
* {@link onKeydown}, and acts on the {@link onPick} callback.
|
|
4
|
+
* The two skill surfaces over a single catalog: a chips row (the `chip: true`
|
|
5
|
+
* subset) and a slash-command palette (all skills, filtered). Both opt-in.
|
|
6
|
+
* Pure DOM; the host appends {@link chips} and {@link palette}, feeds input via
|
|
7
|
+
* {@link onInput} / {@link onKeydown}, and acts on {@link onPick}.
|
|
9
8
|
*/
|
|
10
9
|
export class SkillsMenu {
|
|
11
10
|
/** Chips row — append above the input. Hidden unless chips are enabled and present. */
|
package/src/ui/styles.ts
CHANGED
|
@@ -8,12 +8,11 @@ export const STYLES = `
|
|
|
8
8
|
only the alias is used by the rules below.
|
|
9
9
|
|
|
10
10
|
The indirection is what makes ancestor theming work. Declaring the public
|
|
11
|
-
name on :host would set it
|
|
12
|
-
always beats one inherited from an ancestor
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
inherited normally while a value aimed at the element still wins over it.
|
|
11
|
+
name on :host would set it on the host element, and a value on an element
|
|
12
|
+
always beats one inherited from an ancestor, so tokens put on a wrapper
|
|
13
|
+
would have no effect. Reading the public name with the default as a var()
|
|
14
|
+
fallback leaves it undeclared on the element, so an ancestor's value
|
|
15
|
+
inherits normally while one aimed at the element still wins.
|
|
17
16
|
|
|
18
17
|
Two invariants hold this together:
|
|
19
18
|
1. No rule outside this file's :host blocks may reference a public name
|
|
@@ -40,11 +39,9 @@ export const STYLES = `
|
|
|
40
39
|
--_border: var(--ag-ui-border, #e2e2ec);
|
|
41
40
|
--_radius: var(--ag-ui-radius, 12px);
|
|
42
41
|
|
|
43
|
-
/* Body text and raised chrome
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
transparent box. The defaults below restate exactly that, so this is a
|
|
47
|
-
rename with no repaint — see the note on .code-copy. */
|
|
42
|
+
/* Body text and raised chrome, read only by the code-block copy button.
|
|
43
|
+
The defaults restate what it inherits, so a host that sets neither sees
|
|
44
|
+
no repaint — see the note on .code-copy. */
|
|
48
45
|
--_text: var(--ag-ui-text, var(--_fg));
|
|
49
46
|
--_surface: var(--ag-ui-surface, transparent);
|
|
50
47
|
|
|
@@ -97,9 +94,9 @@ export const STYLES = `
|
|
|
97
94
|
--_launcher-inset: var(--ag-ui-launcher-inset, auto 0 0 auto);
|
|
98
95
|
|
|
99
96
|
/* Motion. One duration and two curves drive every collapse, expand and
|
|
100
|
-
slide-over, so the
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
slide-over, so the widget accelerates and settles as one thing. The
|
|
98
|
+
default curve decelerates into place; the pop curve overshoots slightly,
|
|
99
|
+
for something arriving. */
|
|
103
100
|
--_motion: var(--ag-ui-motion, 0.28s);
|
|
104
101
|
--_ease: var(--ag-ui-ease, cubic-bezier(0.32, 0.72, 0, 1));
|
|
105
102
|
--_ease-pop: var(--ag-ui-ease-pop, cubic-bezier(0.34, 1.36, 0.64, 1));
|
|
@@ -123,7 +120,7 @@ export const STYLES = `
|
|
|
123
120
|
/* Reading-column width for placement="page" (full-bleed, centred content). */
|
|
124
121
|
--_content-max-width: var(--ag-ui-content-max-width, 820px);
|
|
125
122
|
/* Slim rail the sidebar placement collapses to. Only that placement reads
|
|
126
|
-
it, but it is declared here so
|
|
123
|
+
it, but it is declared here so every alias has a default in one place. */
|
|
127
124
|
--_rail-width: var(--ag-ui-rail-width, 52px);
|
|
128
125
|
|
|
129
126
|
position: var(--_position);
|
|
@@ -215,11 +212,11 @@ export const STYLES = `
|
|
|
215
212
|
--_radius: var(--ag-ui-radius, 0);
|
|
216
213
|
}
|
|
217
214
|
|
|
218
|
-
/* Page: full-bleed background with a centred reading column
|
|
219
|
-
"full"
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
/* Page: full-bleed background with a centred reading column capped at
|
|
216
|
+
--ag-ui-content-max-width, where "full" is edge-to-edge and left-aligned.
|
|
217
|
+
The column comes from symmetric auto padding on the scroll area and
|
|
218
|
+
composer rather than a per-row wrapper, so user pills still right-align and
|
|
219
|
+
the assistant well spans the column. */
|
|
223
220
|
:host([placement="page"]) {
|
|
224
221
|
--_inset: var(--ag-ui-inset, 0);
|
|
225
222
|
--_width: var(--ag-ui-width, 100vw);
|
|
@@ -237,9 +234,8 @@ export const STYLES = `
|
|
|
237
234
|
padding-inline: max(12px, calc((100% - var(--_content-max-width)) / 2));
|
|
238
235
|
}
|
|
239
236
|
|
|
240
|
-
/* The rows between the message list and the composer
|
|
241
|
-
|
|
242
|
-
with the column too — chips are padding-based, the palette/hint/tray are
|
|
237
|
+
/* The rows between the message list and the composer line up with the column
|
|
238
|
+
too. Chips and tray are padding-based while palette and hint are
|
|
243
239
|
margin-based, so each gets its own inline axis nudged by the same gutter. */
|
|
244
240
|
:host([placement="page"]) .skill-chips,
|
|
245
241
|
:host([placement="page"]) .attachment-tray {
|
|
@@ -348,13 +344,12 @@ export const STYLES = `
|
|
|
348
344
|
visibility var(--_motion) var(--_ease);
|
|
349
345
|
}
|
|
350
346
|
|
|
351
|
-
/* The launcher grows out of the corner the panel shrank into.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
must stay reachable. */
|
|
347
|
+
/* The launcher grows out of the corner the panel shrank into. It stays laid
|
|
348
|
+
out at rest rather than display:none, which is what lets it animate both in
|
|
349
|
+
and out: an unrendered element has no before-change style to transition
|
|
350
|
+
from, and one flipping display to none cannot transition at all. visibility
|
|
351
|
+
keeps it unpaintable, untabbable and unclickable in between, so the expanded
|
|
352
|
+
panel's own controls underneath stay reachable. */
|
|
358
353
|
:host([collapsed]) .launcher {
|
|
359
354
|
opacity: 1;
|
|
360
355
|
transform: none;
|
|
@@ -515,20 +510,18 @@ export const STYLES = `
|
|
|
515
510
|
|
|
516
511
|
/* ── Collapse ───────────────────────────────────────────────────────────────
|
|
517
512
|
Collapsing shrinks the widget to the round floating launcher: the panel
|
|
518
|
-
scales
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
The host box keeps its expanded size
|
|
523
|
-
a dragged --ag-ui-width would
|
|
524
|
-
paints there once the panel is gone, so the box only has to stop
|
|
525
|
-
clicks: pointer events go to none
|
|
526
|
-
|
|
527
|
-
Two placements collapse
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
floating circle would escape the layout, and page is a full-screen route
|
|
531
|
-
with no corner to float in. */
|
|
513
|
+
scales toward the launcher's corner and fades, the launcher pops in from the
|
|
514
|
+
same point. Both halves are transform and opacity only, so the morph runs on
|
|
515
|
+
the compositor and never reflows the host page.
|
|
516
|
+
|
|
517
|
+
The host box keeps its expanded size, since animating it would animate
|
|
518
|
+
layout and a dragged --ag-ui-width would fight the launcher's own size.
|
|
519
|
+
Nothing paints there once the panel is gone, so the box only has to stop
|
|
520
|
+
swallowing clicks: pointer events go to none and the launcher takes them.
|
|
521
|
+
|
|
522
|
+
Two placements collapse differently: "sidebar" slides to its rail (below),
|
|
523
|
+
while "embedded" and "page" keep the header bar, having no corner for a
|
|
524
|
+
floating circle that would escape the host's layout. */
|
|
532
525
|
:host([collapsed]) {
|
|
533
526
|
pointer-events: none;
|
|
534
527
|
}
|
|
@@ -539,11 +532,11 @@ export const STYLES = `
|
|
|
539
532
|
visibility: hidden;
|
|
540
533
|
}
|
|
541
534
|
|
|
542
|
-
/* visibility
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
535
|
+
/* visibility keeps the panel out of the tab order and the a11y tree at rest
|
|
536
|
+
without display:none killing the transition. It interpolates so any progress
|
|
537
|
+
below 1 still counts as visible: the panel stays on screen for the whole
|
|
538
|
+
collapse, flips hidden exactly at the end, and on expand is visible from the
|
|
539
|
+
first frame. */
|
|
547
540
|
.chat {
|
|
548
541
|
transform-origin: bottom right;
|
|
549
542
|
transition:
|
|
@@ -570,10 +563,9 @@ export const STYLES = `
|
|
|
570
563
|
visibility: visible;
|
|
571
564
|
}
|
|
572
565
|
|
|
573
|
-
/* These two keep the header bar, so the launcher must stay out of the way
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
whatever the page happens to position. */
|
|
566
|
+
/* These two keep the header bar, so the launcher must stay out of the way: an
|
|
567
|
+
embedded host is position: static, which would let an absolutely-positioned
|
|
568
|
+
circle escape the layout and land against whatever the page positions. */
|
|
577
569
|
:host([collapsed]:is([placement="embedded"], [placement="page"])) .launcher {
|
|
578
570
|
visibility: hidden;
|
|
579
571
|
opacity: 0;
|
|
@@ -613,7 +605,7 @@ export const STYLES = `
|
|
|
613
605
|
and the pending indicator so a whole answer reads (and can be boxed) as one
|
|
614
606
|
unit. A flex column on the message-list gap, stretched to the list width so
|
|
615
607
|
its children keep their own left/right alignment. data-answer-well opts into
|
|
616
|
-
the bordered "well"; without it the
|
|
608
|
+
the bordered "well"; without it the turn renders as a flat stack. */
|
|
617
609
|
.answer {
|
|
618
610
|
display: flex;
|
|
619
611
|
flex-direction: column;
|
|
@@ -739,13 +731,9 @@ export const STYLES = `
|
|
|
739
731
|
position: relative;
|
|
740
732
|
}
|
|
741
733
|
|
|
742
|
-
/*
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
back to the initial transparent, and the hover/copied colour to the
|
|
746
|
-
inherited body colour. The defaults chosen upstream (transparent, and the
|
|
747
|
-
body foreground) reproduce that, so the rename repaints nothing. A raised
|
|
748
|
-
surface behind the button is a separate design question. */
|
|
734
|
+
/* The only reader of --ag-ui-surface and --ag-ui-text. Their defaults are
|
|
735
|
+
transparent and the body foreground, which is what this button rendered as
|
|
736
|
+
before either token existed; changing them repaints only this control. */
|
|
749
737
|
.code-copy {
|
|
750
738
|
position: absolute;
|
|
751
739
|
top: 4px;
|
|
@@ -791,12 +779,10 @@ export const STYLES = `
|
|
|
791
779
|
color: var(--_muted);
|
|
792
780
|
}
|
|
793
781
|
|
|
794
|
-
/* Markdown tables. table/thead/tbody/tr/th/td are all in
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
width-constrained, so without this the columns either crush or push the
|
|
799
|
-
layout sideways. */
|
|
782
|
+
/* Markdown tables. table/thead/tbody/tr/th/td are all in the sanitizer's
|
|
783
|
+
ALLOWED_TAGS, so an agent can emit one. A wide table must scroll inside its
|
|
784
|
+
own box rather than stretch the message: the bubble is width-constrained, so
|
|
785
|
+
without this the columns either crush or push the layout sideways. */
|
|
800
786
|
.message--assistant table {
|
|
801
787
|
display: block;
|
|
802
788
|
width: fit-content;
|
|
@@ -937,18 +923,28 @@ export const STYLES = `
|
|
|
937
923
|
color: var(--_tool-fg);
|
|
938
924
|
}
|
|
939
925
|
|
|
926
|
+
/* Wraps, because the name is the only flexible child and every badge the row
|
|
927
|
+
gains is taken out of it. An approved call adds a third fixed badge, which in
|
|
928
|
+
a sidebar-width panel left the name 37px and broke it mid-word. Badges drop to
|
|
929
|
+
their own row instead. */
|
|
940
930
|
.tool-call-head {
|
|
941
931
|
display: flex;
|
|
932
|
+
flex-wrap: wrap;
|
|
942
933
|
align-items: center;
|
|
943
934
|
justify-content: space-between;
|
|
944
935
|
gap: 8px;
|
|
945
936
|
}
|
|
946
937
|
|
|
947
938
|
.tool-call-name {
|
|
948
|
-
|
|
949
|
-
|
|
939
|
+
/* An auto basis, and a min-width floor rather than zero: the name may shrink,
|
|
940
|
+
but not below something readable, so wrapping moves a badge instead of
|
|
941
|
+
shredding a word. Breaking anywhere still applies to a name that cannot fit
|
|
942
|
+
on a line of its own, which is what keeps a long unbroken tool name inside
|
|
943
|
+
the card. */
|
|
944
|
+
flex: 1 1 auto;
|
|
945
|
+
min-width: 6ch;
|
|
950
946
|
font-weight: 600;
|
|
951
|
-
|
|
947
|
+
overflow-wrap: anywhere;
|
|
952
948
|
}
|
|
953
949
|
|
|
954
950
|
/* Leading status icon. Empty in the DOM — the glyph/spinner is drawn
|
|
@@ -1053,8 +1049,6 @@ export const STYLES = `
|
|
|
1053
1049
|
color: var(--_muted);
|
|
1054
1050
|
}
|
|
1055
1051
|
|
|
1056
|
-
/* The record of a human decision on a gated call. An approved call used to
|
|
1057
|
-
look exactly like one that was never gated. */
|
|
1058
1052
|
.skill-item-token {
|
|
1059
1053
|
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
1060
1054
|
font-size: 0.92em;
|
|
@@ -1062,6 +1056,8 @@ export const STYLES = `
|
|
|
1062
1056
|
margin-right: 6px;
|
|
1063
1057
|
}
|
|
1064
1058
|
|
|
1059
|
+
/* The lasting record of a human decision on a gated call — without it an
|
|
1060
|
+
approved call looks exactly like one that was never gated. */
|
|
1065
1061
|
.tool-call-decision {
|
|
1066
1062
|
flex: none;
|
|
1067
1063
|
font-size: 11px;
|
|
@@ -1084,10 +1080,8 @@ export const STYLES = `
|
|
|
1084
1080
|
}
|
|
1085
1081
|
|
|
1086
1082
|
/* Display modes are pure visibility over one DOM shape, selected from the host
|
|
1087
|
-
attribute rather than a value stamped on the card
|
|
1088
|
-
|
|
1089
|
-
re-read it, the way data-answer-well behaves. Baking the structure per mode
|
|
1090
|
-
meant the setting only reached cards created afterwards.
|
|
1083
|
+
attribute rather than a value stamped on the card at build time, so flipping
|
|
1084
|
+
data-tool-display re-styles cards already on screen. See ToolCallCard.
|
|
1091
1085
|
|
|
1092
1086
|
Default (no attribute) is the full mode: arguments always visible, result
|
|
1093
1087
|
behind the toggle. */
|
|
@@ -1184,20 +1178,19 @@ export const STYLES = `
|
|
|
1184
1178
|
}
|
|
1185
1179
|
|
|
1186
1180
|
/* Then the measurement corrects it. Which edges are held still belongs to the
|
|
1187
|
-
host's layout
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
embedded panel its host right-aligns. */
|
|
1181
|
+
host's layout, not to placement -- a floating panel a host right-aligns is
|
|
1182
|
+
anchored bottom-left -- so the element measures them and stamps a single
|
|
1183
|
+
hyphenated "<y>-<x>" token here. Equal specificity to the rules above, so
|
|
1184
|
+
source order is what lets the measured value win.
|
|
1185
|
+
|
|
1186
|
+
Two traps, both of which silently draw the grip on the corner that moves:
|
|
1187
|
+
|
|
1188
|
+
Never write these as [data-resize-anchor~="left"]. The stamped value is one
|
|
1189
|
+
hyphenated token and ~= matches whitespace-separated words, so it can never
|
|
1190
|
+
match.
|
|
1191
|
+
|
|
1192
|
+
Each rule must set both sides of its axis, not only the side it moves, or it
|
|
1193
|
+
cannot undo a placement guess that flipped the other way. */
|
|
1201
1194
|
:host([data-resize-anchor$="-left"]) .resize-handle {
|
|
1202
1195
|
left: auto;
|
|
1203
1196
|
right: 0;
|
|
@@ -1258,10 +1251,9 @@ export const STYLES = `
|
|
|
1258
1251
|
}
|
|
1259
1252
|
|
|
1260
1253
|
/* ── Composer ───────────────────────────────────────────────────────────────
|
|
1261
|
-
One surface owns the border, the background and the focus ring; the field
|
|
1262
|
-
its tool row sit inside it
|
|
1263
|
-
textarea's height
|
|
1264
|
-
and turned Send into a full-height slab. */
|
|
1254
|
+
One surface owns the border, the background and the focus ring; the field
|
|
1255
|
+
and its tool row sit inside it, rather than being siblings stretched to the
|
|
1256
|
+
textarea's height. */
|
|
1265
1257
|
.input-row {
|
|
1266
1258
|
display: flex;
|
|
1267
1259
|
padding: 12px;
|
|
@@ -1509,13 +1501,12 @@ export const STYLES = `
|
|
|
1509
1501
|
}
|
|
1510
1502
|
|
|
1511
1503
|
/* A chip carries its own text colour because it carries its own background.
|
|
1512
|
-
The same chip renders in two places with opposite inherited colours:
|
|
1513
|
-
composer tray it
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
Overridden below for an errored chip, which must keep its red. */
|
|
1504
|
+
The same chip renders in two places with opposite inherited colours: the
|
|
1505
|
+
composer tray gives it the panel's, a sent user bubble gives it the user
|
|
1506
|
+
foreground, which is white on the stock light theme and near-invisible
|
|
1507
|
+
against the chip. Pairing the colour with the background it belongs to makes
|
|
1508
|
+
both placements read alike. Overridden below for an errored chip, which
|
|
1509
|
+
keeps its red. */
|
|
1519
1510
|
.attachment-chip {
|
|
1520
1511
|
display: inline-flex;
|
|
1521
1512
|
align-items: center;
|
package/src/ui/thoughts_block.ts
CHANGED
|
@@ -4,15 +4,13 @@ import { DEFAULT_UI_STRINGS, type UiStrings } from "./ui_strings.js";
|
|
|
4
4
|
* A muted, collapsible "thinking" region for a reasoning model's streamed
|
|
5
5
|
* chain-of-thought.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* The header toggle lets the reader reopen it.
|
|
7
|
+
* Sits at the top of the current answer group. It opens expanded while the
|
|
8
|
+
* model reasons, {@link stream} replacing its body with the running buffer, and
|
|
9
|
+
* {@link collapse} folds it away once the answer's first text token arrives.
|
|
10
|
+
* The header toggle reopens it.
|
|
12
11
|
*
|
|
13
|
-
* Pure DOM
|
|
14
|
-
*
|
|
15
|
-
* chrome text is sourced from {@link UiStrings}.
|
|
12
|
+
* Pure DOM. The host inserts {@link element}; all visible chrome text comes
|
|
13
|
+
* from {@link UiStrings}.
|
|
16
14
|
*/
|
|
17
15
|
export class ThoughtsBlock {
|
|
18
16
|
/** The block's root element; insert this at the top of the answer group. */
|
|
@@ -30,8 +28,8 @@ export class ThoughtsBlock {
|
|
|
30
28
|
this.element = document.createElement("div");
|
|
31
29
|
this.element.className = "thoughts";
|
|
32
30
|
this.element.setAttribute("part", "thoughts");
|
|
33
|
-
//
|
|
34
|
-
//
|
|
31
|
+
// Lets CSS animate the header while the model is still reasoning; dropped
|
|
32
|
+
// on collapse.
|
|
35
33
|
this.element.setAttribute("data-streaming", "");
|
|
36
34
|
|
|
37
35
|
this.#toggle = document.createElement("button");
|
|
@@ -63,9 +61,9 @@ export class ThoughtsBlock {
|
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
/**
|
|
66
|
-
* Fold the region away
|
|
67
|
-
*
|
|
68
|
-
*
|
|
64
|
+
* Fold the region away when the answer's first text token arrives, flipping
|
|
65
|
+
* the header label to its settled form. Idempotent, since the per-token text
|
|
66
|
+
* handler calls it repeatedly.
|
|
69
67
|
*/
|
|
70
68
|
collapse(): void {
|
|
71
69
|
if (this.#collapsed) {
|
package/src/ui/thread_drawer.ts
CHANGED
|
@@ -17,13 +17,14 @@ export interface ThreadDrawerCallbacks {
|
|
|
17
17
|
/**
|
|
18
18
|
* The chat-history drawer: a slide-over listing the user's threads (title,
|
|
19
19
|
* relative time, preview), with select / new / rename / delete actions and an
|
|
20
|
-
* empty state. Pure DOM
|
|
20
|
+
* empty state. Pure DOM, like {@link SkillsMenu}: the host appends
|
|
21
21
|
* {@link element}, toggles it, feeds rows via {@link setThreads}, and acts on
|
|
22
|
-
* the callbacks.
|
|
23
|
-
* callback the host updates the store and calls {@link setThreads} to refresh.
|
|
22
|
+
* the callbacks.
|
|
24
23
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* A view only — it never mutates the store. After a callback the host updates
|
|
25
|
+
* the store and calls {@link setThreads} to refresh. All visible text comes
|
|
26
|
+
* from {@link UiStrings}; {@link setStrings} re-localizes a drawer built before
|
|
27
|
+
* its strings resolved.
|
|
27
28
|
*/
|
|
28
29
|
export class ThreadDrawer {
|
|
29
30
|
/** The drawer root (backdrop + panel). Append to the chat shell; hidden until opened. */
|
package/src/ui/tool_call_card.ts
CHANGED
|
@@ -41,29 +41,24 @@ function formatPayload(text: string): string {
|
|
|
41
41
|
/**
|
|
42
42
|
* A live tool-call card for the chat transcript.
|
|
43
43
|
*
|
|
44
|
-
* Construction renders a status icon, the tool name, a
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* the previous compact layout ran `args: {...}` and the result together in one
|
|
49
|
-
* `<pre>`, leaving no way to see where the call ended and the answer began.
|
|
44
|
+
* Construction renders a status icon, the tool name, a status pill, and a body
|
|
45
|
+
* of two separately-headed regions — arguments and result — each with its own
|
|
46
|
+
* `part`. {@link settle} fills in the result and flips the pill. Both payloads
|
|
47
|
+
* are pretty-printed and never concatenated into one block.
|
|
50
48
|
*
|
|
51
|
-
*
|
|
52
|
-
* shows
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* Visibility is selected from the host attribute rather than a value copied onto
|
|
57
|
-
* the card at construction, for the same reason.
|
|
49
|
+
* The card renders one DOM shape in every display mode and lets CSS decide what
|
|
50
|
+
* shows, selecting visibility from the host attribute rather than a value
|
|
51
|
+
* copied onto the card at construction. That is what lets `data-tool-display`
|
|
52
|
+
* be flipped on the host and re-read by every card already on screen; building
|
|
53
|
+
* a different structure per mode would leave existing cards stale.
|
|
58
54
|
*
|
|
59
|
-
* The leading icon carries no text
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
55
|
+
* The leading icon carries no text: the shadow CSS draws its glyph or spinner
|
|
56
|
+
* from the card's `data-status`, so a host themes it through the
|
|
57
|
+
* `--ag-ui-tool-icon-*` custom properties or the `tool-card-icon` part without
|
|
58
|
+
* the card reaching into the host stylesheet.
|
|
63
59
|
*
|
|
64
|
-
* Pure DOM
|
|
65
|
-
*
|
|
66
|
-
* `tool-card*` `part`s. All visible text is sourced from {@link UiStrings}.
|
|
60
|
+
* Pure DOM. The host appends {@link element} into its shadow root; all visible
|
|
61
|
+
* text comes from {@link UiStrings}.
|
|
67
62
|
*/
|
|
68
63
|
export class ToolCallCard {
|
|
69
64
|
/** The card's root element; append this into the message list. */
|
|
@@ -97,9 +92,8 @@ export class ToolCallCard {
|
|
|
97
92
|
head.className = "tool-call-head";
|
|
98
93
|
head.setAttribute("part", "tool-card-head");
|
|
99
94
|
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
// the glyphs stay themeable (`--ag-ui-tool-icon-*`) and the spin is real.
|
|
95
|
+
// Left empty in the DOM on purpose: the shadow CSS draws the spinner or
|
|
96
|
+
// settled mark from `data-status`, keeping the glyphs themeable.
|
|
103
97
|
const icon = document.createElement("span");
|
|
104
98
|
icon.className = "tool-call-icon";
|
|
105
99
|
icon.setAttribute("part", "tool-card-icon");
|
|
@@ -125,16 +119,15 @@ export class ToolCallCard {
|
|
|
125
119
|
|
|
126
120
|
const argsSection = this.#section("args", strings.argumentsLabel);
|
|
127
121
|
argsSection.body.textContent = JSON.stringify(args, null, 2);
|
|
128
|
-
//
|
|
129
|
-
// which is a frame around nothing. Drop the region instead.
|
|
122
|
+
// Drop the region rather than frame an empty object.
|
|
130
123
|
argsSection.root.hidden = Object.keys(args).length === 0;
|
|
131
124
|
|
|
132
125
|
const resultSection = this.#section("result", strings.resultLabel);
|
|
133
126
|
this.#resultSection = resultSection.root;
|
|
134
127
|
this.#resultLabel = resultSection.label;
|
|
135
128
|
this.#resultBody = resultSection.body;
|
|
136
|
-
// Nothing to show until `settle` supplies it
|
|
137
|
-
//
|
|
129
|
+
// Nothing to show until `settle` supplies it, or a pending card expands
|
|
130
|
+
// onto an empty region.
|
|
138
131
|
resultSection.root.hidden = true;
|
|
139
132
|
|
|
140
133
|
this.#toggle = document.createElement("button");
|
|
@@ -155,11 +148,8 @@ export class ToolCallCard {
|
|
|
155
148
|
|
|
156
149
|
/**
|
|
157
150
|
* Record that a human approved or declined this call, as a line in the card.
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* result saying so, while an approved one simply ran, making the transcript
|
|
161
|
-
* of a gated call byte-identical to one that was never gated. The prompt is
|
|
162
|
-
* gone once answered, so this is where the decision lives.
|
|
151
|
+
* The prompt disappears once answered, so this is the only lasting trace that
|
|
152
|
+
* the call was gated at all.
|
|
163
153
|
*/
|
|
164
154
|
recordDecision(kind: "approved" | "declined"): void {
|
|
165
155
|
this.element.setAttribute("data-decision", kind);
|
package/src/ui/ui_strings.ts
CHANGED
|
@@ -5,14 +5,9 @@
|
|
|
5
5
|
* or its `data-strings` JSON attribute) and the rest fall back to the English
|
|
6
6
|
* defaults.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* This module is one cohesive unit — the `UiStrings` shape, its `DEFAULT_UI_STRINGS`
|
|
13
|
-
* constant-like backing, and the {@link mergeUiStrings} merge over those defaults —
|
|
14
|
-
* so it bends the one-symbol-per-file rule the way `tool_call_card.ts` (class +
|
|
15
|
-
* its types) and `route_map.ts` (factory + its types) already do.
|
|
8
|
+
* Some values are templates carrying `{token}` placeholders the call site fills
|
|
9
|
+
* in; the token names are noted on each key, and a translation must keep them
|
|
10
|
+
* verbatim.
|
|
16
11
|
*/
|
|
17
12
|
export interface UiStrings {
|
|
18
13
|
// ── Header ────────────────────────────────────────────────────────────────
|
|
@@ -27,10 +22,9 @@ export interface UiStrings {
|
|
|
27
22
|
/** Expand affordance (the launcher, and the sidebar rail). */
|
|
28
23
|
expand: string;
|
|
29
24
|
/**
|
|
30
|
-
* The launcher's label while unread answers
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* needs.
|
|
25
|
+
* The launcher's label while unread answers wait behind it. Token: `{count}`.
|
|
26
|
+
* Replaces {@link expand} rather than appending to it, so a translation can
|
|
27
|
+
* order the two parts as its language needs.
|
|
34
28
|
*/
|
|
35
29
|
expandUnread: string;
|
|
36
30
|
/** Built-in header theme toggle (light ⇄ dark). */
|
package/src/ui/voice_input.ts
CHANGED
|
@@ -20,13 +20,13 @@ export interface VoiceInputOptions {
|
|
|
20
20
|
* `MediaRecorder`, then POSTs the clip through a {@link TranscribeHandler} and
|
|
21
21
|
* hands the transcript back via `onText`.
|
|
22
22
|
*
|
|
23
|
-
* Click to start recording
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* Click to start recording, which prompts for mic permission, and again to stop
|
|
24
|
+
* and transcribe. The button reflects its `idle` / `recording` / `transcribing`
|
|
25
|
+
* state on `data-state` for theming and is exposed as `part="voice-button"`; a
|
|
26
|
+
* capture or transcription failure returns it to idle and surfaces the message
|
|
27
|
+
* on its tooltip.
|
|
28
28
|
*
|
|
29
|
-
* Pure DOM
|
|
29
|
+
* Pure DOM. The host mounts {@link element} in the input row.
|
|
30
30
|
*/
|
|
31
31
|
export class VoiceInput {
|
|
32
32
|
/** The mic button; mount this in the composer. */
|
|
@@ -102,11 +102,10 @@ export class VoiceInput {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* Tear the control down
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* detached element.
|
|
105
|
+
* Tear the control down, for a host element removed mid-recording. Stops any
|
|
106
|
+
* live `MediaRecorder`, releases the mic tracks so the browser's recording
|
|
107
|
+
* indicator clears, and suppresses the pending transcription — a
|
|
108
|
+
* disconnected control must not fire `onText` into a detached element.
|
|
110
109
|
*/
|
|
111
110
|
dispose(): void {
|
|
112
111
|
this.#disposed = true;
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION: string = "0.
|
|
1
|
+
export const VERSION: string = "0.24.0";
|