@artooi/ag-ui-web-component 0.44.0 → 0.46.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 +182 -1
- package/README.md +245 -32
- package/dist/ag-ui-web-component.bundle.js +3696 -2798
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +4 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +28 -8
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/read_flag.d.ts +11 -0
- package/dist/core/read_flag.d.ts.map +1 -0
- package/dist/core/read_json_attribute.d.ts +15 -0
- package/dist/core/read_json_attribute.d.ts.map +1 -0
- package/dist/index.js +5084 -3481
- package/dist/index.js.map +4 -4
- package/dist/tools/tool_catalog.d.ts +5 -0
- package/dist/tools/tool_catalog.d.ts.map +1 -1
- package/dist/tools/tool_dispatch.d.ts.map +1 -1
- package/dist/ui/charts/chart_styles.d.ts +2 -0
- package/dist/ui/charts/chart_styles.d.ts.map +1 -0
- package/dist/ui/composer/attachment_styles.d.ts +2 -0
- package/dist/ui/composer/attachment_styles.d.ts.map +1 -0
- package/dist/ui/composer/build_composer.d.ts +30 -0
- package/dist/ui/composer/build_composer.d.ts.map +1 -0
- package/dist/ui/composer/composer_styles.d.ts +2 -0
- package/dist/ui/composer/composer_styles.d.ts.map +1 -0
- package/dist/ui/composer/skill_styles.d.ts +2 -0
- package/dist/ui/composer/skill_styles.d.ts.map +1 -0
- package/dist/ui/history/history_styles.d.ts +2 -0
- package/dist/ui/history/history_styles.d.ts.map +1 -0
- package/dist/ui/interrupts/approval_styles.d.ts +2 -0
- package/dist/ui/interrupts/approval_styles.d.ts.map +1 -0
- package/dist/ui/interrupts/confirmation_styles.d.ts +2 -0
- package/dist/ui/interrupts/confirmation_styles.d.ts.map +1 -0
- package/dist/ui/interrupts/question_styles.d.ts +2 -0
- package/dist/ui/interrupts/question_styles.d.ts.map +1 -0
- package/dist/ui/placement/collapse_styles.d.ts +2 -0
- package/dist/ui/placement/collapse_styles.d.ts.map +1 -0
- package/dist/ui/placement/embedded_styles.d.ts +2 -0
- package/dist/ui/placement/embedded_styles.d.ts.map +1 -0
- package/dist/ui/placement/launcher_styles.d.ts +2 -0
- package/dist/ui/placement/launcher_styles.d.ts.map +1 -0
- package/dist/ui/placement/page_styles.d.ts +2 -0
- package/dist/ui/placement/page_styles.d.ts.map +1 -0
- package/dist/ui/placement/panel_placement.d.ts.map +1 -1
- package/dist/ui/placement/preset_styles.d.ts +2 -0
- package/dist/ui/placement/preset_styles.d.ts.map +1 -0
- package/dist/ui/placement/resize_styles.d.ts +2 -0
- package/dist/ui/placement/resize_styles.d.ts.map +1 -0
- package/dist/ui/placement/sidebar_styles.d.ts +2 -0
- package/dist/ui/placement/sidebar_styles.d.ts.map +1 -0
- package/dist/ui/placement/small_viewport_styles.d.ts +2 -0
- package/dist/ui/placement/small_viewport_styles.d.ts.map +1 -0
- package/dist/ui/placement/surface_for.d.ts +74 -0
- package/dist/ui/placement/surface_for.d.ts.map +1 -0
- package/dist/ui/progress/run_notice_styles.d.ts +2 -0
- package/dist/ui/progress/run_notice_styles.d.ts.map +1 -0
- package/dist/ui/progress/thoughts_styles.d.ts +2 -0
- package/dist/ui/progress/thoughts_styles.d.ts.map +1 -0
- package/dist/ui/progress/tool_call_card.d.ts +8 -0
- package/dist/ui/progress/tool_call_card.d.ts.map +1 -1
- package/dist/ui/progress/tool_group_styles.d.ts +2 -0
- package/dist/ui/progress/tool_group_styles.d.ts.map +1 -0
- package/dist/ui/progress/tool_step_group.d.ts +47 -0
- package/dist/ui/progress/tool_step_group.d.ts.map +1 -0
- package/dist/ui/progress/tool_step_styles.d.ts +2 -0
- package/dist/ui/progress/tool_step_styles.d.ts.map +1 -0
- package/dist/ui/shell/adopt_styles.d.ts +9 -4
- package/dist/ui/shell/adopt_styles.d.ts.map +1 -1
- package/dist/ui/shell/build_header.d.ts +40 -0
- package/dist/ui/shell/build_header.d.ts.map +1 -0
- package/dist/ui/shell/build_launcher.d.ts +20 -0
- package/dist/ui/shell/build_launcher.d.ts.map +1 -0
- package/dist/ui/shell/frame_styles.d.ts +2 -0
- package/dist/ui/shell/frame_styles.d.ts.map +1 -0
- package/dist/ui/shell/theme_styles.d.ts +2 -0
- package/dist/ui/shell/theme_styles.d.ts.map +1 -0
- package/dist/ui/shell/token_styles.d.ts +2 -0
- package/dist/ui/shell/token_styles.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/themes/classic_styles.d.ts +2 -0
- package/dist/ui/themes/classic_styles.d.ts.map +1 -0
- package/dist/ui/themes/classic_tokens.d.ts +2 -0
- package/dist/ui/themes/classic_tokens.d.ts.map +1 -0
- package/dist/ui/themes/code_tokens.d.ts +2 -0
- package/dist/ui/themes/code_tokens.d.ts.map +1 -0
- package/dist/ui/themes/ledger_styles.d.ts +2 -0
- package/dist/ui/themes/ledger_styles.d.ts.map +1 -0
- package/dist/ui/themes/ledger_tokens.d.ts +2 -0
- package/dist/ui/themes/ledger_tokens.d.ts.map +1 -0
- package/dist/ui/themes/modern_styles.d.ts +2 -0
- package/dist/ui/themes/modern_styles.d.ts.map +1 -0
- package/dist/ui/themes/quiet_styles.d.ts +2 -0
- package/dist/ui/themes/quiet_styles.d.ts.map +1 -0
- package/dist/ui/themes/scheme_styles.d.ts +2 -0
- package/dist/ui/themes/scheme_styles.d.ts.map +1 -0
- package/dist/ui/themes/soft_styles.d.ts +2 -0
- package/dist/ui/themes/soft_styles.d.ts.map +1 -0
- package/dist/ui/themes/soft_tokens.d.ts +2 -0
- package/dist/ui/themes/soft_tokens.d.ts.map +1 -0
- package/dist/ui/themes/theme_hosts.d.ts +15 -0
- package/dist/ui/themes/theme_hosts.d.ts.map +1 -0
- package/dist/ui/transcript/attachment_chip_styles.d.ts +2 -0
- package/dist/ui/transcript/attachment_chip_styles.d.ts.map +1 -0
- package/dist/ui/transcript/build_transcript.d.ts +21 -0
- package/dist/ui/transcript/build_transcript.d.ts.map +1 -0
- package/dist/ui/transcript/greeting_styles.d.ts +2 -0
- package/dist/ui/transcript/greeting_styles.d.ts.map +1 -0
- package/dist/ui/transcript/groups_tool_steps.d.ts +13 -0
- package/dist/ui/transcript/groups_tool_steps.d.ts.map +1 -0
- package/dist/ui/transcript/message_action_styles.d.ts +2 -0
- package/dist/ui/transcript/message_action_styles.d.ts.map +1 -0
- package/dist/ui/transcript/message_styles.d.ts +2 -0
- package/dist/ui/transcript/message_styles.d.ts.map +1 -0
- package/dist/ui/transcript/transcript.d.ts +16 -1
- package/dist/ui/transcript/transcript.d.ts.map +1 -1
- package/dist/ui/transcript/transcript_styles.d.ts +2 -0
- package/dist/ui/transcript/transcript_styles.d.ts.map +1 -0
- package/dist/ui/ui_strings.d.ts +18 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/constants.ts +4 -1
- package/src/core/ag_ui_chat.ts +164 -299
- package/src/core/read_flag.ts +13 -0
- package/src/core/read_json_attribute.ts +30 -0
- package/src/tools/tool_catalog.ts +23 -10
- package/src/tools/tool_dispatch.ts +26 -11
- package/src/ui/charts/chart_block.ts +9 -9
- package/src/ui/charts/chart_styles.ts +63 -0
- package/src/ui/composer/attachment_styles.ts +71 -0
- package/src/ui/composer/build_composer.ts +112 -0
- package/src/ui/composer/composer_styles.ts +174 -0
- package/src/ui/composer/skill_styles.ts +120 -0
- package/src/ui/history/history_styles.ts +484 -0
- package/src/ui/interrupts/approval_styles.ts +52 -0
- package/src/ui/interrupts/confirmation_styles.ts +114 -0
- package/src/ui/interrupts/question_styles.ts +73 -0
- package/src/ui/placement/collapse_styles.ts +137 -0
- package/src/ui/placement/embedded_styles.ts +14 -0
- package/src/ui/placement/launcher_styles.ts +189 -0
- package/src/ui/placement/page_styles.ts +47 -0
- package/src/ui/placement/panel_placement.ts +3 -12
- package/src/ui/placement/preset_styles.ts +33 -0
- package/src/ui/placement/resize_styles.ts +186 -0
- package/src/ui/placement/sidebar_styles.ts +88 -0
- package/src/ui/placement/small_viewport_styles.ts +56 -0
- package/src/ui/placement/surface_for.ts +135 -0
- package/src/ui/progress/run_notice_styles.ts +52 -0
- package/src/ui/progress/thoughts_styles.ts +65 -0
- package/src/ui/progress/tool_call_card.ts +11 -0
- package/src/ui/progress/tool_group_styles.ts +232 -0
- package/src/ui/progress/tool_step_group.ts +269 -0
- package/src/ui/progress/tool_step_styles.ts +472 -0
- package/src/ui/shell/adopt_styles.ts +11 -5
- package/src/ui/shell/build_header.ts +108 -0
- package/src/ui/shell/build_launcher.ts +50 -0
- package/src/ui/shell/frame_styles.ts +119 -0
- package/src/ui/shell/theme_styles.ts +11 -0
- package/src/ui/shell/token_styles.ts +406 -0
- package/src/ui/styles.ts +104 -3774
- package/src/ui/themes/classic_styles.ts +32 -0
- package/src/ui/themes/classic_tokens.ts +50 -0
- package/src/ui/themes/code_tokens.ts +21 -0
- package/src/ui/themes/ledger_styles.ts +168 -0
- package/src/ui/themes/ledger_tokens.ts +32 -0
- package/src/ui/themes/modern_styles.ts +80 -0
- package/src/ui/themes/quiet_styles.ts +115 -0
- package/src/ui/themes/scheme_styles.ts +17 -0
- package/src/ui/themes/soft_styles.ts +148 -0
- package/src/ui/themes/soft_tokens.ts +28 -0
- package/src/ui/themes/theme_hosts.ts +14 -0
- package/src/ui/transcript/attachment_chip_styles.ts +116 -0
- package/src/ui/transcript/build_transcript.ts +67 -0
- package/src/ui/transcript/greeting_styles.ts +171 -0
- package/src/ui/transcript/groups_tool_steps.ts +16 -0
- package/src/ui/transcript/message_action_styles.ts +155 -0
- package/src/ui/transcript/message_styles.ts +262 -0
- package/src/ui/transcript/transcript.ts +109 -3
- package/src/ui/transcript/transcript_styles.ts +140 -0
- package/src/ui/ui_strings.ts +31 -0
- package/src/version.ts +1 -1
- package/dist/ui/placement/is_collapsible_placement.d.ts +0 -18
- package/dist/ui/placement/is_collapsible_placement.d.ts.map +0 -1
- package/dist/ui/placement/is_draggable_placement.d.ts +0 -9
- package/dist/ui/placement/is_draggable_placement.d.ts.map +0 -1
- package/src/ui/placement/is_collapsible_placement.ts +0 -19
- package/src/ui/placement/is_draggable_placement.ts +0 -23
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
// Tool steps: the per-call card in each display mode, its status glyphs, and
|
|
2
|
+
// the sub-agent progress a delegating card holds. The skill palette's token
|
|
3
|
+
// mark sits among them only because no rule moved in the split.
|
|
4
|
+
export const TOOL_STEP_STYLES: string = `
|
|
5
|
+
.tool-call {
|
|
6
|
+
align-self: flex-start;
|
|
7
|
+
max-width: 80%;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 6px;
|
|
12
|
+
font-size: 12px;
|
|
13
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
14
|
+
padding: 8px 10px;
|
|
15
|
+
border-radius: 8px;
|
|
16
|
+
background: var(--_tool-bg);
|
|
17
|
+
border: 1px solid var(--_border);
|
|
18
|
+
color: var(--_tool-fg);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Wraps, because the name is the only flexible child and every badge the row
|
|
22
|
+
gains is taken out of it. An approved call adds a third fixed badge, which in
|
|
23
|
+
a sidebar-width panel left the name 37px and broke it mid-word. Badges drop to
|
|
24
|
+
their own row instead. */
|
|
25
|
+
.tool-call-head {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: space-between;
|
|
30
|
+
gap: 8px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.tool-call-name {
|
|
34
|
+
/* An auto basis, and a min-width floor rather than zero: the name may shrink,
|
|
35
|
+
but not below something readable, so wrapping moves a badge instead of
|
|
36
|
+
shredding a word. Breaking anywhere still applies to a name that cannot fit
|
|
37
|
+
on a line of its own, which is what keeps a long unbroken tool name inside
|
|
38
|
+
the card. */
|
|
39
|
+
flex: 1 1 auto;
|
|
40
|
+
min-width: 6ch;
|
|
41
|
+
font-weight: 600;
|
|
42
|
+
overflow-wrap: anywhere;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Leading status icon. Empty in the DOM — the glyph/spinner is drawn
|
|
46
|
+
here from the card's data-status, so it stays themeable. */
|
|
47
|
+
.tool-call-icon {
|
|
48
|
+
flex: none;
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
display: inline-flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
width: 14px;
|
|
54
|
+
height: 14px;
|
|
55
|
+
font-size: 12px;
|
|
56
|
+
line-height: 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Pending: a real spinning ring. Speed is tunable; reduced motion stops it. */
|
|
60
|
+
.tool-call[data-status="pending"] .tool-call-icon {
|
|
61
|
+
border: 2px solid var(--_muted);
|
|
62
|
+
border-top-color: transparent;
|
|
63
|
+
border-radius: 50%;
|
|
64
|
+
animation: ag-ui-tool-spin var(--_tool-spin-duration) linear infinite;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes ag-ui-tool-spin {
|
|
68
|
+
to { transform: rotate(360deg); }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Deferred: no spinner, because nothing is spinning. A steady accent dot, since
|
|
72
|
+
the state is waiting-on-you rather than an outcome. */
|
|
73
|
+
.tool-call[data-status="deferred"] .tool-call-icon {
|
|
74
|
+
border-radius: 50%;
|
|
75
|
+
background: var(--_accent);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Settled: a themeable glyph coloured by outcome. */
|
|
79
|
+
.tool-call[data-status="done"] .tool-call-icon::before {
|
|
80
|
+
content: var(--_tool-icon-done);
|
|
81
|
+
color: var(--_success);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.tool-call[data-status="error"] .tool-call-icon::before {
|
|
85
|
+
content: var(--_tool-icon-error);
|
|
86
|
+
color: var(--_danger);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.tool-call[data-status="declined"] .tool-call-icon::before {
|
|
90
|
+
content: var(--_tool-icon-declined);
|
|
91
|
+
color: var(--_muted);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Not finished: muted like declined, since nothing failed, but its own glyph,
|
|
95
|
+
since nobody refused it either. */
|
|
96
|
+
.tool-call[data-status="interrupted"] .tool-call-icon::before {
|
|
97
|
+
content: var(--_tool-icon-interrupted);
|
|
98
|
+
color: var(--_muted);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@media (prefers-reduced-motion: reduce) {
|
|
102
|
+
.tool-call[data-status="pending"] .tool-call-icon {
|
|
103
|
+
animation: none;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Inline display mode: the lightest card. Drop the box chrome so the status row
|
|
108
|
+
reads as one line of the answer; the result toggle still expands below it. */
|
|
109
|
+
:host([data-tool-display="inline"]) .tool-call {
|
|
110
|
+
max-width: 100%;
|
|
111
|
+
background: transparent;
|
|
112
|
+
border: none;
|
|
113
|
+
padding: 2px 0;
|
|
114
|
+
gap: 2px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.tool-call-status {
|
|
118
|
+
flex: none;
|
|
119
|
+
padding: 1px 8px;
|
|
120
|
+
border-radius: 999px;
|
|
121
|
+
font-size: 11px;
|
|
122
|
+
font-weight: 600;
|
|
123
|
+
background: rgba(127, 127, 127, 0.16);
|
|
124
|
+
color: var(--_muted);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.tool-call[data-status="deferred"] .tool-call-status {
|
|
128
|
+
color: var(--_accent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.tool-call[data-status="done"] .tool-call-status {
|
|
132
|
+
color: var(--_success);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.tool-call[data-status="error"] .tool-call-status {
|
|
136
|
+
color: var(--_danger);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.tool-call[data-status="declined"] .tool-call-status,
|
|
140
|
+
.tool-call[data-status="interrupted"] .tool-call-status {
|
|
141
|
+
color: var(--_muted);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.tool-call-body {
|
|
145
|
+
display: flex;
|
|
146
|
+
flex-direction: column;
|
|
147
|
+
gap: 6px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.tool-call-section {
|
|
151
|
+
display: flex;
|
|
152
|
+
flex-direction: column;
|
|
153
|
+
gap: 3px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* A call with no arguments drops the region rather than framing an empty
|
|
157
|
+
object, and a settled card drops the result region until it has one -- both
|
|
158
|
+
by setting the hidden property, and an author display beats the user-agent
|
|
159
|
+
rule for that attribute. Without this the region kept laying out: 42px of
|
|
160
|
+
card holding the ARGUMENTS heading over nothing, on every call the agent
|
|
161
|
+
made with no arguments, in the display mode that shows arguments by
|
|
162
|
+
default. */
|
|
163
|
+
.tool-call-section[hidden] {
|
|
164
|
+
display: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* The heading that tells the two payloads apart. Without it the arguments and
|
|
168
|
+
the result were one run of text and a reader had to guess the boundary. */
|
|
169
|
+
.tool-call-section-label {
|
|
170
|
+
font-size: 10px;
|
|
171
|
+
font-weight: 700;
|
|
172
|
+
letter-spacing: 0.06em;
|
|
173
|
+
text-transform: uppercase;
|
|
174
|
+
color: var(--_muted);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.skill-item-token {
|
|
178
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
179
|
+
font-size: 0.92em;
|
|
180
|
+
color: var(--_accent);
|
|
181
|
+
margin-right: 6px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* The lasting record of a human decision on a gated call — without it an
|
|
185
|
+
approved call looks exactly like one that was never gated. */
|
|
186
|
+
.tool-call-decision {
|
|
187
|
+
flex: none;
|
|
188
|
+
font-size: 11px;
|
|
189
|
+
font-style: italic;
|
|
190
|
+
color: var(--_muted);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.tool-call-args,
|
|
194
|
+
.tool-call-result {
|
|
195
|
+
margin: 0;
|
|
196
|
+
padding: 6px 8px;
|
|
197
|
+
max-height: 160px;
|
|
198
|
+
overflow: auto;
|
|
199
|
+
background: var(--_bg);
|
|
200
|
+
border: 1px solid var(--_border);
|
|
201
|
+
border-radius: 6px;
|
|
202
|
+
white-space: pre-wrap;
|
|
203
|
+
word-break: break-word;
|
|
204
|
+
color: var(--_fg);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* A region a host formatter took over, marked by the card. Preformatted
|
|
208
|
+
whitespace is what makes the built-in block read as written, and it is the one
|
|
209
|
+
thing a host cannot want: a table inherits it as mangled cell spacing, and a
|
|
210
|
+
sentence as line breaks nobody typed.
|
|
211
|
+
|
|
212
|
+
Whitespace only. The card's own face, frame, padding and scroll cap stay,
|
|
213
|
+
because the card is one visual object -- the head row and the status pill are
|
|
214
|
+
monospaced too -- and a region that dropped the family would be the only part
|
|
215
|
+
of it wearing a different one. A host that wants that restyles the
|
|
216
|
+
tool-card-result part, which does not need the formatter at all. */
|
|
217
|
+
.tool-call-args[data-formatted],
|
|
218
|
+
.tool-call-result[data-formatted] {
|
|
219
|
+
white-space: normal;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Display modes are pure visibility over one DOM shape, selected from the host
|
|
223
|
+
attribute rather than a value stamped on the card at build time, so flipping
|
|
224
|
+
data-tool-display re-styles cards already on screen. See ToolCallCard.
|
|
225
|
+
|
|
226
|
+
Default (no attribute) is the full mode: arguments always visible, result
|
|
227
|
+
behind the toggle. */
|
|
228
|
+
.tool-call[data-expanded="false"] .tool-call-section--result {
|
|
229
|
+
display: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Compact: one toggle over both regions, so a settled card is a single line
|
|
233
|
+
until asked. */
|
|
234
|
+
:host([data-tool-display="compact"]) .tool-call[data-expanded="false"] .tool-call-section {
|
|
235
|
+
display: none;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Inline: the result only; the call's arguments are noise at this density. */
|
|
239
|
+
:host([data-tool-display="inline"]) .tool-call .tool-call-section--args {
|
|
240
|
+
display: none;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* Minimal: the status row and nothing else, so there is no toggle to press. */
|
|
244
|
+
:host([data-tool-display="minimal"]) .tool-call .tool-call-toggle,
|
|
245
|
+
:host([data-tool-display="minimal"]) .tool-call .tool-call-body {
|
|
246
|
+
display: none;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* A pending card has no result yet, and in the modes where the arguments are
|
|
250
|
+
hidden too there is nothing behind the toggle. Hide the control rather than
|
|
251
|
+
offer one that expands onto nothing. A deferred card is the same, and its
|
|
252
|
+
arguments are shown unconditionally by the rules below. */
|
|
253
|
+
.tool-call[data-status="pending"] .tool-call-toggle,
|
|
254
|
+
.tool-call[data-status="deferred"] .tool-call-toggle,
|
|
255
|
+
:host([data-tool-display="inline"]) .tool-call[data-status="pending"] .tool-call-toggle {
|
|
256
|
+
display: none;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* The approval prompt for a gated call, rendered inside that call's own card.
|
|
260
|
+
Empty on every card nobody is being asked about, so it collapses instead of
|
|
261
|
+
adding a gap to each one. */
|
|
262
|
+
.tool-call-approval:empty {
|
|
263
|
+
display: none;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.tool-call-approval {
|
|
267
|
+
margin-top: 8px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* A card that is asking a question shows what it is asking about, in every
|
|
271
|
+
display mode. Three gated calls of one tool ask the same words, so the
|
|
272
|
+
arguments are the only thing telling them apart, and a density setting must
|
|
273
|
+
not be able to hide the answer to "which one is this". */
|
|
274
|
+
:host([data-tool-display="minimal"]) .tool-call[data-status="deferred"] .tool-call-body {
|
|
275
|
+
display: flex;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* The arguments region only, never every section: the result region carries the
|
|
279
|
+
hidden attribute until a result exists, and a display value here overrides it,
|
|
280
|
+
framing an empty RESULT heading under the question. */
|
|
281
|
+
:host([data-tool-display="compact"]) .tool-call[data-status="deferred"] .tool-call-section--args,
|
|
282
|
+
:host([data-tool-display="inline"]) .tool-call[data-status="deferred"] .tool-call-section--args {
|
|
283
|
+
display: flex;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* A delegated sub-agent's progress, inside the card that delegated. Empty on
|
|
287
|
+
every card that delegated nothing, so it collapses rather than adding a gap
|
|
288
|
+
to each one -- the same shape the approval slot uses. */
|
|
289
|
+
.tool-call-subagent:empty {
|
|
290
|
+
display: none;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.tool-call-subagent {
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-direction: column;
|
|
296
|
+
gap: 4px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.subagent {
|
|
300
|
+
display: flex;
|
|
301
|
+
flex-direction: column;
|
|
302
|
+
gap: 4px;
|
|
303
|
+
min-width: 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* The collapsed row is the status and the expander at once, which is what keeps
|
|
307
|
+
a ten-step child one row until somebody opens it. Full width and left-aligned,
|
|
308
|
+
because it is a line of the card rather than a button on it. */
|
|
309
|
+
.subagent-row {
|
|
310
|
+
display: flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
gap: 6px;
|
|
313
|
+
width: 100%;
|
|
314
|
+
box-sizing: border-box;
|
|
315
|
+
padding: 2px 0;
|
|
316
|
+
border: none;
|
|
317
|
+
background: none;
|
|
318
|
+
font: inherit;
|
|
319
|
+
text-align: left;
|
|
320
|
+
color: var(--_muted);
|
|
321
|
+
cursor: pointer;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* Nothing behind the row yet -- a delegation that failed before calling
|
|
325
|
+
anything. Drop the affordances rather than offer a control that expands onto
|
|
326
|
+
an empty region, which is the refusal the card's own toggle already makes. */
|
|
327
|
+
.subagent-row:disabled {
|
|
328
|
+
cursor: default;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.subagent-row::after {
|
|
332
|
+
content: var(--_disclosure-collapsed);
|
|
333
|
+
flex: none;
|
|
334
|
+
margin-left: auto;
|
|
335
|
+
color: var(--_accent);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.subagent-row[aria-expanded="true"]::after {
|
|
339
|
+
content: var(--_disclosure-expanded);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.subagent-row:disabled::after {
|
|
343
|
+
display: none;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* Empty in the DOM; the glyph is drawn here from the panel's data-phase, so a
|
|
347
|
+
host re-themes it through the same tool-icon custom properties the card uses. */
|
|
348
|
+
.subagent-icon {
|
|
349
|
+
flex: none;
|
|
350
|
+
box-sizing: border-box;
|
|
351
|
+
display: inline-flex;
|
|
352
|
+
align-items: center;
|
|
353
|
+
justify-content: center;
|
|
354
|
+
width: 10px;
|
|
355
|
+
height: 10px;
|
|
356
|
+
font-size: 10px;
|
|
357
|
+
line-height: 1;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* Anything that is not a terminal phase is the child still working. Selected by
|
|
361
|
+
what it is not, so a phase this client has not heard of still spins rather
|
|
362
|
+
than rendering as a blank. */
|
|
363
|
+
.subagent[data-phase]:not([data-phase="finished"]):not([data-phase="failed"]) .subagent-icon {
|
|
364
|
+
border: 2px solid var(--_muted);
|
|
365
|
+
border-top-color: transparent;
|
|
366
|
+
border-radius: 50%;
|
|
367
|
+
animation: ag-ui-tool-spin var(--_tool-spin-duration) linear infinite;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.subagent[data-phase="finished"] .subagent-icon::before {
|
|
371
|
+
content: var(--_tool-icon-done);
|
|
372
|
+
color: var(--_success);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.subagent[data-phase="failed"] .subagent-icon::before {
|
|
376
|
+
content: var(--_tool-icon-error);
|
|
377
|
+
color: var(--_danger);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@media (prefers-reduced-motion: reduce) {
|
|
381
|
+
.subagent .subagent-icon {
|
|
382
|
+
animation: none;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* The server's own pre-rendered line. Shrinks and wraps rather than pushing the
|
|
387
|
+
chevron out of the card, which is what a fixed-width sibling in a flex row
|
|
388
|
+
does to a panel at sidebar width. */
|
|
389
|
+
.subagent-status {
|
|
390
|
+
flex: 1 1 auto;
|
|
391
|
+
min-width: 0;
|
|
392
|
+
overflow-wrap: anywhere;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* The child's own calls. Indented and ruled, so the nesting is visible without
|
|
396
|
+
a second card frame around it. */
|
|
397
|
+
.subagent-steps {
|
|
398
|
+
display: flex;
|
|
399
|
+
flex-direction: column;
|
|
400
|
+
gap: 2px;
|
|
401
|
+
margin-left: 4px;
|
|
402
|
+
padding-left: 10px;
|
|
403
|
+
border-left: 1px solid var(--_border);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.subagent-steps[hidden] {
|
|
407
|
+
display: none;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.subagent-step {
|
|
411
|
+
display: flex;
|
|
412
|
+
align-items: center;
|
|
413
|
+
gap: 6px;
|
|
414
|
+
min-width: 0;
|
|
415
|
+
color: var(--_muted);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.subagent-step-icon {
|
|
419
|
+
flex: none;
|
|
420
|
+
box-sizing: border-box;
|
|
421
|
+
display: inline-flex;
|
|
422
|
+
align-items: center;
|
|
423
|
+
justify-content: center;
|
|
424
|
+
width: 8px;
|
|
425
|
+
height: 8px;
|
|
426
|
+
font-size: 9px;
|
|
427
|
+
line-height: 1;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* No outcome yet: the wire says null while the call is in flight, and the
|
|
431
|
+
absence of the attribute is how that arrives here. A hollow ring, not a
|
|
432
|
+
spinner -- several can be on screen at once and the row above already spins. */
|
|
433
|
+
.subagent-step:not([data-ok]) .subagent-step-icon {
|
|
434
|
+
border: 1px solid var(--_muted);
|
|
435
|
+
border-radius: 50%;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.subagent-step[data-ok="true"] .subagent-step-icon::before {
|
|
439
|
+
content: var(--_tool-icon-done);
|
|
440
|
+
color: var(--_success);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.subagent-step[data-ok="false"] .subagent-step-icon::before {
|
|
444
|
+
content: var(--_tool-icon-error);
|
|
445
|
+
color: var(--_danger);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.subagent-step-name {
|
|
449
|
+
flex: 1 1 auto;
|
|
450
|
+
min-width: 0;
|
|
451
|
+
overflow-wrap: anywhere;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.tool-call-toggle {
|
|
455
|
+
align-self: flex-start;
|
|
456
|
+
border: none;
|
|
457
|
+
padding: 0;
|
|
458
|
+
background: none;
|
|
459
|
+
font: inherit;
|
|
460
|
+
font-weight: 600;
|
|
461
|
+
color: var(--_accent);
|
|
462
|
+
cursor: pointer;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.tool-call-toggle::before {
|
|
466
|
+
content: var(--_disclosure-collapsed) " ";
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.tool-call-toggle[aria-expanded="true"]::before {
|
|
470
|
+
content: var(--_disclosure-expanded) " ";
|
|
471
|
+
}
|
|
472
|
+
`;
|
|
@@ -16,13 +16,19 @@ import { STYLES } from "../styles.js";
|
|
|
16
16
|
* Per instance is no worse than the `<style>` element it replaces, which also
|
|
17
17
|
* parsed once per mount.
|
|
18
18
|
*
|
|
19
|
+
* `extra` is the element's `extraStyles`, adopted after the built-in sheet so
|
|
20
|
+
* a host's rules win at equal specificity. They are constructed sheets too, so
|
|
21
|
+
* they inherit the same CSP immunity.
|
|
22
|
+
*
|
|
19
23
|
* No fallback: constructible `CSSStyleSheet` is Chrome 73, Firefox 101 and
|
|
20
|
-
* Safari 16.4, all below this package's
|
|
21
|
-
* guard here would be code no supported browser can
|
|
22
|
-
* to keep it would be to exempt it from the coverage
|
|
24
|
+
* Safari 16.4, all below this package's browser floor (Chrome 123, Firefox
|
|
25
|
+
* 120, Safari 17.5). A guard here would be code no supported browser can
|
|
26
|
+
* reach, and the only way to keep it would be to exempt it from the coverage
|
|
27
|
+
* gate.
|
|
23
28
|
*/
|
|
24
|
-
export function adoptStyles(root: ShadowRoot):
|
|
29
|
+
export function adoptStyles(root: ShadowRoot, extra: readonly CSSStyleSheet[] = []): CSSStyleSheet {
|
|
25
30
|
const sheet = new CSSStyleSheet();
|
|
26
31
|
sheet.replaceSync(STYLES);
|
|
27
|
-
root.adoptedStyleSheets = [sheet];
|
|
32
|
+
root.adoptedStyleSheets = [sheet, ...extra];
|
|
33
|
+
return sheet;
|
|
28
34
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { UiStrings } from "../ui_strings.js";
|
|
2
|
+
import { headerButton } from "./header_button.js";
|
|
3
|
+
import { iconElement } from "./icon_element.js";
|
|
4
|
+
|
|
5
|
+
/** What {@link buildHeader} needs from the element that owns the header. */
|
|
6
|
+
export interface HeaderOptions {
|
|
7
|
+
/** The element itself, asked whether a host slotted an icon. */
|
|
8
|
+
readonly host: HTMLElement;
|
|
9
|
+
/** The title node, kept by the element so a later `title-text` can update it. */
|
|
10
|
+
readonly title: HTMLElement;
|
|
11
|
+
readonly titleText: string;
|
|
12
|
+
readonly iconUrl: string | null;
|
|
13
|
+
readonly strings: UiStrings;
|
|
14
|
+
/** Whether the server indexes runs, so there is anything to continue. */
|
|
15
|
+
readonly withCheckpoints: boolean;
|
|
16
|
+
/** The opt-in theme toggle, or `null` when the host did not ask for one. */
|
|
17
|
+
readonly themeToggle: HTMLButtonElement | null;
|
|
18
|
+
readonly signal: AbortSignal;
|
|
19
|
+
readonly onHistory: () => void;
|
|
20
|
+
readonly onCheckpoints: () => void;
|
|
21
|
+
readonly onNewChat: () => void;
|
|
22
|
+
readonly onCollapse: () => void;
|
|
23
|
+
readonly onToggleTheme: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The header, and the one control the element still needs a handle on. */
|
|
27
|
+
export interface BuiltHeader {
|
|
28
|
+
readonly header: HTMLElement;
|
|
29
|
+
/**
|
|
30
|
+
* The checkpoints button, built whether or not it is shown: the click-away
|
|
31
|
+
* handler for the checkpoints popover has to exclude it either way.
|
|
32
|
+
*/
|
|
33
|
+
readonly checkpoints: HTMLButtonElement;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build the panel's title bar: the optional icon, the title, the host's
|
|
38
|
+
* header-actions slot, and the controls.
|
|
39
|
+
*
|
|
40
|
+
* Every control calls back into the element's public methods, so a host
|
|
41
|
+
* chrome driving them imperatively takes exactly the path the built-in button
|
|
42
|
+
* takes.
|
|
43
|
+
*/
|
|
44
|
+
export function buildHeader(options: HeaderOptions): BuiltHeader {
|
|
45
|
+
const { strings } = options;
|
|
46
|
+
const header = document.createElement("div");
|
|
47
|
+
header.className = "header";
|
|
48
|
+
header.setAttribute("part", "header");
|
|
49
|
+
|
|
50
|
+
const title = options.title;
|
|
51
|
+
title.className = "header-title";
|
|
52
|
+
title.setAttribute("part", "title");
|
|
53
|
+
title.textContent = options.titleText;
|
|
54
|
+
|
|
55
|
+
// Optional header icon: a slot (any markup) with a `data-icon-url` <img>
|
|
56
|
+
// fallback. Rendered only when one of the two is provided, so the header has
|
|
57
|
+
// no phantom gap otherwise.
|
|
58
|
+
if (options.host.querySelector('[slot="icon"]') !== null || options.iconUrl !== null) {
|
|
59
|
+
header.append(iconElement("icon", "icon", null, options.iconUrl));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// A coarse slot for host-provided header actions, between title and controls.
|
|
63
|
+
const headerActions = document.createElement("slot");
|
|
64
|
+
headerActions.name = "header-actions";
|
|
65
|
+
|
|
66
|
+
const controls = document.createElement("div");
|
|
67
|
+
controls.className = "header-controls";
|
|
68
|
+
controls.setAttribute("part", "header-controls");
|
|
69
|
+
|
|
70
|
+
const history = headerButton("history", strings.chatHistory, "☰");
|
|
71
|
+
history.addEventListener("click", options.onHistory);
|
|
72
|
+
|
|
73
|
+
// ↺ rather than ⭯: the same idea in a glyph that has a font behind it in
|
|
74
|
+
// every browser. The obscure one rendered as an unreadable mark at 14px, and a
|
|
75
|
+
// header control nobody can name is one nobody presses.
|
|
76
|
+
const checkpoints = headerButton("checkpoints", strings.checkpoints, "↺");
|
|
77
|
+
checkpoints.addEventListener("click", options.onCheckpoints);
|
|
78
|
+
|
|
79
|
+
const newChat = headerButton("new", strings.newChat, "✚");
|
|
80
|
+
newChat.addEventListener("click", options.onNewChat);
|
|
81
|
+
|
|
82
|
+
const collapse = headerButton("collapse", strings.collapse, "—");
|
|
83
|
+
collapse.addEventListener("click", options.onCollapse);
|
|
84
|
+
|
|
85
|
+
// Only offered when the server actually indexes runs — without
|
|
86
|
+
// `data-runs-url` there is nothing to continue and the button would open
|
|
87
|
+
// a permanently empty panel.
|
|
88
|
+
if (options.withCheckpoints) {
|
|
89
|
+
controls.append(history, checkpoints, newChat);
|
|
90
|
+
} else {
|
|
91
|
+
controls.append(history, newChat);
|
|
92
|
+
}
|
|
93
|
+
// Optional built-in theme toggle: off unless the host opts in, so
|
|
94
|
+
// it never competes with a host-supplied switch in `slot="header-actions"`.
|
|
95
|
+
const toggle = options.themeToggle;
|
|
96
|
+
if (toggle !== null) {
|
|
97
|
+
toggle.type = "button";
|
|
98
|
+
toggle.className = "header-btn header-btn--theme";
|
|
99
|
+
toggle.setAttribute("part", "header-button theme-toggle");
|
|
100
|
+
toggle.title = strings.toggleTheme;
|
|
101
|
+
toggle.setAttribute("aria-label", strings.toggleTheme);
|
|
102
|
+
toggle.addEventListener("click", options.onToggleTheme, { signal: options.signal });
|
|
103
|
+
controls.append(toggle);
|
|
104
|
+
}
|
|
105
|
+
controls.append(collapse);
|
|
106
|
+
header.append(title, headerActions, controls);
|
|
107
|
+
return { header, checkpoints };
|
|
108
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ICON_LAUNCHER } from "../../constants.js";
|
|
2
|
+
import type { UiStrings } from "../ui_strings.js";
|
|
3
|
+
import { iconElement } from "./icon_element.js";
|
|
4
|
+
|
|
5
|
+
/** The launcher's parts, owned by the element, and what fills them. */
|
|
6
|
+
export interface LauncherOptions {
|
|
7
|
+
readonly launcher: HTMLButtonElement;
|
|
8
|
+
readonly badge: HTMLElement;
|
|
9
|
+
readonly railLabel: HTMLElement;
|
|
10
|
+
readonly titleText: string;
|
|
11
|
+
readonly iconUrl: string | null;
|
|
12
|
+
readonly strings: UiStrings;
|
|
13
|
+
readonly signal: AbortSignal;
|
|
14
|
+
readonly onOpen: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build what a collapsed widget shrinks to: a round floating button, or the
|
|
19
|
+
* slim edge rail under `placement="sidebar"` -- one element, shaped by the
|
|
20
|
+
* stylesheet. The element mounts it as a sibling of the panel, so it survives
|
|
21
|
+
* the panel being hidden.
|
|
22
|
+
*/
|
|
23
|
+
export function buildLauncher(options: LauncherOptions): void {
|
|
24
|
+
const { launcher, badge, railLabel } = options;
|
|
25
|
+
launcher.className = "launcher";
|
|
26
|
+
launcher.type = "button";
|
|
27
|
+
launcher.setAttribute("part", "launcher");
|
|
28
|
+
launcher.setAttribute("aria-label", options.strings.expand);
|
|
29
|
+
badge.className = "launcher-badge";
|
|
30
|
+
badge.setAttribute("part", "launcher-badge");
|
|
31
|
+
// The count is announced through the launcher's own label, so the badge is
|
|
32
|
+
// decoration to a screen reader rather than a second, context-free number.
|
|
33
|
+
badge.setAttribute("aria-hidden", "true");
|
|
34
|
+
badge.hidden = true;
|
|
35
|
+
// Only the edge rail shows this. A full-height column carrying one small
|
|
36
|
+
// icon reads as a coloured stripe rather than a way back into a
|
|
37
|
+
// conversation -- it is the widest collapsed state there is and the one
|
|
38
|
+
// that says least about itself. Written here and hidden in CSS everywhere
|
|
39
|
+
// else, because the launcher is one element shaped by placement.
|
|
40
|
+
railLabel.className = "rail-label";
|
|
41
|
+
railLabel.setAttribute("part", "rail-label");
|
|
42
|
+
railLabel.setAttribute("aria-hidden", "true");
|
|
43
|
+
railLabel.textContent = options.titleText;
|
|
44
|
+
launcher.replaceChildren(
|
|
45
|
+
iconElement("launcher", "launcher-icon", ICON_LAUNCHER, options.iconUrl),
|
|
46
|
+
railLabel,
|
|
47
|
+
badge,
|
|
48
|
+
);
|
|
49
|
+
launcher.addEventListener("click", options.onOpen, { signal: options.signal });
|
|
50
|
+
}
|