@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
package/src/core/ag_ui_chat.ts
CHANGED
|
@@ -4,11 +4,7 @@ import {
|
|
|
4
4
|
CHART_ACTIVITY_TYPE,
|
|
5
5
|
COMPACTION_ACTIVITY_TYPE,
|
|
6
6
|
CUSTOM_AGENT_EVENT,
|
|
7
|
-
ICON_ATTACH,
|
|
8
|
-
ICON_LAUNCHER,
|
|
9
7
|
ICON_MOON,
|
|
10
|
-
ICON_SEND,
|
|
11
|
-
ICON_STOP,
|
|
12
8
|
ICON_SUN,
|
|
13
9
|
MESSAGE_ROLE,
|
|
14
10
|
STATE_EVENT,
|
|
@@ -37,6 +33,7 @@ import { renderChart } from "../ui/charts/chart_block.js";
|
|
|
37
33
|
import { chartSpecFrom } from "../ui/charts/chart_spec_from.js";
|
|
38
34
|
import { CHART_TOOL_NAME, createChartTool } from "../ui/charts/chart_tool.js";
|
|
39
35
|
import { autoGrow } from "../ui/composer/auto_grow.js";
|
|
36
|
+
import { buildComposer } from "../ui/composer/build_composer.js";
|
|
40
37
|
import { ComposerAttachments } from "../ui/composer/composer_attachments.js";
|
|
41
38
|
import { ComposerVoice } from "../ui/composer/composer_voice.js";
|
|
42
39
|
import { SkillsMenu } from "../ui/composer/skills_menu.js";
|
|
@@ -51,23 +48,23 @@ import { type ApprovalRenderer } from "../ui/interrupts/approval_card.js";
|
|
|
51
48
|
import { PendingDecision } from "../ui/interrupts/pending_decision.js";
|
|
52
49
|
// biome-ignore lint/style/useImportType: the emitted declaration file copies this form
|
|
53
50
|
import { type QuestionRenderer } from "../ui/interrupts/question_card.js";
|
|
54
|
-
import { isCollapsiblePlacement } from "../ui/placement/is_collapsible_placement.js";
|
|
55
|
-
import { isDraggablePlacement } from "../ui/placement/is_draggable_placement.js";
|
|
56
51
|
import { PanelPlacement } from "../ui/placement/panel_placement.js";
|
|
52
|
+
import { surfaceFor } from "../ui/placement/surface_for.js";
|
|
57
53
|
import { RunAnnouncer } from "../ui/progress/run_announcer.js";
|
|
58
54
|
import { renderRunNotice } from "../ui/progress/run_notice.js";
|
|
59
55
|
import { SubAgentProgress } from "../ui/progress/subagent_progress.js";
|
|
60
56
|
// biome-ignore lint/style/useImportType: the emitted declaration file copies this form
|
|
61
57
|
import { type ToolDisplayMode, type ToolPayloadFormatter } from "../ui/progress/tool_call_card.js";
|
|
62
58
|
import { adoptStyles } from "../ui/shell/adopt_styles.js";
|
|
63
|
-
import {
|
|
64
|
-
import {
|
|
59
|
+
import { buildHeader } from "../ui/shell/build_header.js";
|
|
60
|
+
import { buildLauncher } from "../ui/shell/build_launcher.js";
|
|
65
61
|
import { iconElement } from "../ui/shell/icon_element.js";
|
|
66
62
|
import { isUnreadBadgeEnabled } from "../ui/shell/is_unread_badge_enabled.js";
|
|
67
63
|
import { readLauncherIconUrl } from "../ui/shell/read_launcher_icon_url.js";
|
|
68
64
|
import { AnswerActions } from "../ui/transcript/answer_actions.js";
|
|
69
65
|
import { AnswerStream } from "../ui/transcript/answer_stream.js";
|
|
70
66
|
import { renderAttachmentChips } from "../ui/transcript/attachment_chips.js";
|
|
67
|
+
import { buildTranscript } from "../ui/transcript/build_transcript.js";
|
|
71
68
|
import { renderStarterChips } from "../ui/transcript/starter_chips.js";
|
|
72
69
|
import { renderSuggestionChips } from "../ui/transcript/suggestion_chips.js";
|
|
73
70
|
import { Transcript } from "../ui/transcript/transcript.js";
|
|
@@ -91,6 +88,8 @@ import type { SubmitDetail } from "./events/submit_detail.js";
|
|
|
91
88
|
import type { ToggleDetail } from "./events/toggle_detail.js";
|
|
92
89
|
import type { UnreadDetail } from "./events/unread_detail.js";
|
|
93
90
|
import type { MessageRole } from "./message_role.js";
|
|
91
|
+
import { readFlag } from "./read_flag.js";
|
|
92
|
+
import { readJsonAttribute } from "./read_json_attribute.js";
|
|
94
93
|
import { readMaxToolRounds } from "./read_max_tool_rounds.js";
|
|
95
94
|
import { RemoteConversationStore } from "./remote_conversation_store.js";
|
|
96
95
|
import { RunHandlers } from "./run_handlers.js";
|
|
@@ -145,9 +144,34 @@ function isCredentialsMode(value: string): value is RequestCredentials {
|
|
|
145
144
|
/** Per-tab persistence key for the collapsed state (survives MPA reloads). */
|
|
146
145
|
const COLLAPSED_KEY = "ag-ui-chat:collapsed";
|
|
147
146
|
|
|
148
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* Persistence key for the built-in theme toggle. It holds the user's scheme
|
|
149
|
+
* word only: the theme name belongs to the host's markup, and one saved value
|
|
150
|
+
* is restored on every page that shares the key.
|
|
151
|
+
*/
|
|
149
152
|
const THEME_KEY = "ag-ui-chat:theme";
|
|
150
153
|
|
|
154
|
+
/** The scheme words of the `theme` attribute; every other word names a theme. */
|
|
155
|
+
const SCHEME_WORDS: readonly string[] = ["light", "dark", "auto"];
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The last scheme word in a saved value, or `undefined` when it names none. A
|
|
159
|
+
* value saved before only the scheme was kept (`"soft dark"`) still gives up
|
|
160
|
+
* just its scheme.
|
|
161
|
+
*/
|
|
162
|
+
function savedScheme(saved: string): string | undefined {
|
|
163
|
+
return saved
|
|
164
|
+
.split(/\s+/)
|
|
165
|
+
.filter((word) => SCHEME_WORDS.includes(word))
|
|
166
|
+
.at(-1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** `theme`'s words with its scheme word, if any, replaced by `scheme`. */
|
|
170
|
+
function withScheme(theme: string, scheme: string): string {
|
|
171
|
+
const names = theme.split(/\s+/).filter((word) => word !== "" && !SCHEME_WORDS.includes(word));
|
|
172
|
+
return [...names, scheme].join(" ");
|
|
173
|
+
}
|
|
174
|
+
|
|
151
175
|
/**
|
|
152
176
|
* `<ag-ui-chat>` — a framework-free chat sidebar Web Component over AG-UI.
|
|
153
177
|
*
|
|
@@ -450,6 +474,10 @@ export class AgUiChat extends HTMLElement {
|
|
|
450
474
|
});
|
|
451
475
|
|
|
452
476
|
readonly #root: ShadowRoot;
|
|
477
|
+
/** The built-in stylesheet once rendered, kept so `extraStyles` can re-adopt beside it. */
|
|
478
|
+
#builtInSheet: CSSStyleSheet | null = null;
|
|
479
|
+
/** The host's own sheets, adopted after the built-in one. */
|
|
480
|
+
#extraStyles: readonly CSSStyleSheet[] = [];
|
|
453
481
|
/** The screen-reader-only status region the run and its tool calls report into. */
|
|
454
482
|
readonly #announcer = new RunAnnouncer();
|
|
455
483
|
/** Return-to-foot affordance, shown only once something has been missed. */
|
|
@@ -739,6 +767,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
739
767
|
askUser: () => this.askUser,
|
|
740
768
|
askUserRenderer: () => this.askUserRenderer,
|
|
741
769
|
decision: this.#decision,
|
|
770
|
+
setAsking: (callId, asking) => this.#transcript.setAsking(callId, asking),
|
|
742
771
|
ensureGroup: () => this.#transcript.ensureGroup(),
|
|
743
772
|
strings: () => this.#strings,
|
|
744
773
|
hidePending: () => this.#transcript.hidePending(),
|
|
@@ -792,6 +821,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
792
821
|
resolveTool: (name) => this.#tools.resolve(name),
|
|
793
822
|
toolSummaries: () => this.toolSummaries,
|
|
794
823
|
serverSummary: (name) => this.#tools.summary(name),
|
|
824
|
+
toolDisplay: () => this.toolDisplay,
|
|
795
825
|
// A thunk over the live property, not the property itself: the card keeps
|
|
796
826
|
// this for the life of the call, and the result region is filled when the
|
|
797
827
|
// tool settles -- which can be long after a host set the hook.
|
|
@@ -839,7 +869,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
839
869
|
root: this.#root,
|
|
840
870
|
connected: () => this.#connected,
|
|
841
871
|
collapsed: () => this.collapsed,
|
|
842
|
-
collapsible: () =>
|
|
872
|
+
collapsible: () => surfaceFor(this.getAttribute("placement")).collapseTo !== null,
|
|
843
873
|
strings: () => this.#strings,
|
|
844
874
|
readPreference: (base) => this.#storage.readPreference(base),
|
|
845
875
|
writePreference: (base, value) => this.#storage.writePreference(base, value),
|
|
@@ -912,8 +942,8 @@ export class AgUiChat extends HTMLElement {
|
|
|
912
942
|
hint: this.#composerHint,
|
|
913
943
|
strings: () => this.#strings,
|
|
914
944
|
context: () => this.skillContext(),
|
|
915
|
-
flag: (name) => this
|
|
916
|
-
readJsonAttribute: (name) =>
|
|
945
|
+
flag: (name) => readFlag(this, name),
|
|
946
|
+
readJsonAttribute: (name) => readJsonAttribute(this, name),
|
|
917
947
|
fetchInit: (url) => this.#fetchInit(url),
|
|
918
948
|
send: (content) => {
|
|
919
949
|
void this.sendMessage(content);
|
|
@@ -1055,7 +1085,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1055
1085
|
// not just stop offering it: the control is gone from the header the
|
|
1056
1086
|
// moment the attribute changes, so a panel collapsed under the previous
|
|
1057
1087
|
// placement would have no way back.
|
|
1058
|
-
if (
|
|
1088
|
+
if (surfaceFor(this.getAttribute("placement")).collapseTo === null && this.collapsed) {
|
|
1059
1089
|
this.setCollapsed(false);
|
|
1060
1090
|
}
|
|
1061
1091
|
// Placement also moves the panel, so the edges its layout holds still
|
|
@@ -1349,28 +1379,57 @@ export class AgUiChat extends HTMLElement {
|
|
|
1349
1379
|
|
|
1350
1380
|
/**
|
|
1351
1381
|
* How much detail tool-call cards show, from the `data-tool-display`
|
|
1352
|
-
* attribute (`minimal` / `inline` / `compact` / `full`).
|
|
1382
|
+
* attribute (`grouped` / `minimal` / `inline` / `compact` / `full`).
|
|
1383
|
+
* Defaults to `grouped`, for an absent attribute and for a value the
|
|
1384
|
+
* element does not know; `full` is the per-call card default before
|
|
1385
|
+
* `grouped` existed.
|
|
1353
1386
|
*
|
|
1354
|
-
*
|
|
1355
|
-
* restyles every card already in the
|
|
1356
|
-
*
|
|
1387
|
+
* The four card modes are applied by the shadow CSS from the attribute
|
|
1388
|
+
* itself, so changing between them restyles every card already in the
|
|
1389
|
+
* transcript. Whether calls fold into a step group is decided as each card
|
|
1390
|
+
* is built: switching to `grouped` folds only the calls drawn afterwards,
|
|
1391
|
+
* and switching away flattens the existing groups, again by CSS.
|
|
1357
1392
|
*/
|
|
1358
1393
|
get toolDisplay(): ToolDisplayMode {
|
|
1359
1394
|
const attr = this.getAttribute("data-tool-display");
|
|
1360
1395
|
if (
|
|
1361
1396
|
attr === TOOL_DISPLAY.INLINE ||
|
|
1362
1397
|
attr === TOOL_DISPLAY.MINIMAL ||
|
|
1363
|
-
attr === TOOL_DISPLAY.COMPACT
|
|
1398
|
+
attr === TOOL_DISPLAY.COMPACT ||
|
|
1399
|
+
attr === TOOL_DISPLAY.FULL
|
|
1364
1400
|
) {
|
|
1365
1401
|
return attr;
|
|
1366
1402
|
}
|
|
1367
|
-
return TOOL_DISPLAY.
|
|
1403
|
+
return TOOL_DISPLAY.GROUPED;
|
|
1368
1404
|
}
|
|
1369
1405
|
|
|
1370
1406
|
set toolDisplay(value: ToolDisplayMode) {
|
|
1371
1407
|
this.setAttribute("data-tool-display", value);
|
|
1372
1408
|
}
|
|
1373
1409
|
|
|
1410
|
+
/**
|
|
1411
|
+
* The host's own constructed stylesheets, adopted after the built-in one.
|
|
1412
|
+
*
|
|
1413
|
+
* The escape hatch for structure that neither the tokens nor the parts
|
|
1414
|
+
* reach. Per element, like the built-in sheet: two elements on one page can
|
|
1415
|
+
* carry different sheets. Constructed sheets are not subject to a
|
|
1416
|
+
* `style-src` policy, so this works under the same strict CSP the
|
|
1417
|
+
* built-in sheet does. Setting it re-adopts at once; the array is copied, so
|
|
1418
|
+
* mutating the one passed in changes nothing until it is set again.
|
|
1419
|
+
*
|
|
1420
|
+
* Unstable: reaches shadow class names, which may change in any minor.
|
|
1421
|
+
*/
|
|
1422
|
+
get extraStyles(): readonly CSSStyleSheet[] {
|
|
1423
|
+
return this.#extraStyles;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
set extraStyles(sheets: readonly CSSStyleSheet[]) {
|
|
1427
|
+
this.#extraStyles = [...sheets];
|
|
1428
|
+
if (this.#builtInSheet !== null) {
|
|
1429
|
+
this.#root.adoptedStyleSheets = [this.#builtInSheet, ...this.#extraStyles];
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1374
1433
|
connectedCallback(): void {
|
|
1375
1434
|
// A fresh signal per connection: disconnecting aborted the last one, and
|
|
1376
1435
|
// with it every listener the last connection added.
|
|
@@ -1405,12 +1464,14 @@ export class AgUiChat extends HTMLElement {
|
|
|
1405
1464
|
// Resolve the string table before rendering any chrome (defaults are the
|
|
1406
1465
|
// floor; `data-strings` then the `strings` property layer over them).
|
|
1407
1466
|
this.#strings = mergeUiStrings({ ...this.#readStringOverrides(), ...this.strings });
|
|
1408
|
-
// Restore
|
|
1409
|
-
// it never overrides a host that
|
|
1467
|
+
// Restore the scheme the built-in toggle persisted last visit onto the
|
|
1468
|
+
// markup's theme name (opt-in only, so it never overrides a host that
|
|
1469
|
+
// drives `theme` itself).
|
|
1410
1470
|
if (this.getAttribute("data-theme-toggle") !== null) {
|
|
1411
1471
|
const saved = this.#storage.readPreference(THEME_KEY);
|
|
1412
|
-
|
|
1413
|
-
|
|
1472
|
+
const scheme = saved === null ? undefined : savedScheme(saved);
|
|
1473
|
+
if (scheme !== undefined) {
|
|
1474
|
+
this.setAttribute("theme", withScheme(this.getAttribute("theme") ?? "", scheme));
|
|
1414
1475
|
}
|
|
1415
1476
|
}
|
|
1416
1477
|
this.#render();
|
|
@@ -1420,7 +1481,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1420
1481
|
// namespaced per instance but not per placement, so a tab that collapsed a
|
|
1421
1482
|
// floating panel and later loaded the same instance as a page would restore
|
|
1422
1483
|
// a state that placement has no way out of.
|
|
1423
|
-
if (
|
|
1484
|
+
if (surfaceFor(this.getAttribute("placement")).collapseTo !== null && this.#startsCollapsed()) {
|
|
1424
1485
|
this.setAttribute("collapsed", "");
|
|
1425
1486
|
}
|
|
1426
1487
|
this.#syncLauncher();
|
|
@@ -1558,53 +1619,9 @@ export class AgUiChat extends HTMLElement {
|
|
|
1558
1619
|
this.#announcer.dispose();
|
|
1559
1620
|
}
|
|
1560
1621
|
|
|
1561
|
-
/**
|
|
1562
|
-
* Read an opt-in flag attribute the way HTML reads a boolean attribute.
|
|
1563
|
-
*
|
|
1564
|
-
* Present means on: bare (`data-prompt-chips`), empty (`=""`), or any value
|
|
1565
|
-
* except the literal `"false"`. Comparing against `"true"` instead would make
|
|
1566
|
-
* the bare spelling every native boolean attribute uses silently disable the
|
|
1567
|
-
* feature it names. `="false"` still turns it off.
|
|
1568
|
-
*/
|
|
1569
|
-
#flag(name: string): boolean {
|
|
1570
|
-
const value = this.getAttribute(name);
|
|
1571
|
-
return value !== null && value !== "false";
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
/**
|
|
1575
|
-
* Parse a JSON-valued attribute, saying so when it will not parse.
|
|
1576
|
-
*
|
|
1577
|
-
* `null` for an absent attribute, and `null` again for one that is not JSON --
|
|
1578
|
-
* but not quietly the second time. Quoting JSON inside an HTML attribute is
|
|
1579
|
-
* fiddly, and the result of getting it wrong is indistinguishable from the
|
|
1580
|
-
* feature being switched off: no chips appear, or the strings stay English,
|
|
1581
|
-
* with nothing anywhere saying why. That is the same failure `data-paste-attach`
|
|
1582
|
-
* already reports for a value it cannot read.
|
|
1583
|
-
*
|
|
1584
|
-
* Console only. A page author's typo is not the reader's business, nothing the
|
|
1585
|
-
* reader did was refused, and the degraded widget is still perfectly usable.
|
|
1586
|
-
*/
|
|
1587
|
-
#readJsonAttribute(name: string): unknown {
|
|
1588
|
-
const raw = this.getAttribute(name);
|
|
1589
|
-
if (raw === null) {
|
|
1590
|
-
return null;
|
|
1591
|
-
}
|
|
1592
|
-
try {
|
|
1593
|
-
return JSON.parse(raw);
|
|
1594
|
-
} catch {
|
|
1595
|
-
console.warn(
|
|
1596
|
-
`<ag-ui-chat>: ${name} is not valid JSON, so it was ignored entirely and ` +
|
|
1597
|
-
"the built-in default is being used. Check the quoting -- JSON inside " +
|
|
1598
|
-
"an HTML attribute needs single quotes around the attribute value, or " +
|
|
1599
|
-
"its own double quotes escaped.",
|
|
1600
|
-
);
|
|
1601
|
-
return null;
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
1622
|
/** Parse the inline `data-strings` JSON overrides (empty when absent/malformed). */
|
|
1606
1623
|
#readStringOverrides(): Partial<UiStrings> {
|
|
1607
|
-
const parsed =
|
|
1624
|
+
const parsed = readJsonAttribute(this, "data-strings");
|
|
1608
1625
|
// A JSON number or string parses fine and overrides nothing. Not warned
|
|
1609
1626
|
// about separately: it is the same "this attribute did not take effect"
|
|
1610
1627
|
// as a parse failure, and the warning above already covers the spelling
|
|
@@ -1723,7 +1740,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1723
1740
|
* its own chrome.
|
|
1724
1741
|
*/
|
|
1725
1742
|
setCollapsed(collapsed: boolean, options: { readonly announce?: boolean } = {}): void {
|
|
1726
|
-
if (collapsed &&
|
|
1743
|
+
if (collapsed && surfaceFor(this.getAttribute("placement")).collapseTo === null) {
|
|
1727
1744
|
return;
|
|
1728
1745
|
}
|
|
1729
1746
|
// Announced before the state changes, so the notice is written into a
|
|
@@ -1800,7 +1817,8 @@ export class AgUiChat extends HTMLElement {
|
|
|
1800
1817
|
return stored === "1";
|
|
1801
1818
|
}
|
|
1802
1819
|
return (
|
|
1803
|
-
|
|
1820
|
+
surfaceFor(this.getAttribute("placement")).startsCollapsed &&
|
|
1821
|
+
!this.hasAttribute("data-start-open")
|
|
1804
1822
|
);
|
|
1805
1823
|
}
|
|
1806
1824
|
|
|
@@ -1838,15 +1856,21 @@ export class AgUiChat extends HTMLElement {
|
|
|
1838
1856
|
}
|
|
1839
1857
|
|
|
1840
1858
|
/**
|
|
1841
|
-
* Flip the `theme` attribute between `light` and `dark`
|
|
1842
|
-
* per tab. Bound to the optional built-in header theme
|
|
1843
|
-
* (`data-theme-toggle`)
|
|
1844
|
-
* `dark`
|
|
1859
|
+
* Flip the scheme word of the `theme` attribute between `light` and `dark`
|
|
1860
|
+
* and persist the choice per tab. Bound to the optional built-in header theme
|
|
1861
|
+
* toggle (`data-theme-toggle`). The theme name and any host words are kept:
|
|
1862
|
+
* `soft` becomes `soft dark`, and `soft dark` becomes `soft light`. Any
|
|
1863
|
+
* scheme but `dark` (including `auto`) flips to `dark` first.
|
|
1845
1864
|
*/
|
|
1846
1865
|
toggleTheme(): void {
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1866
|
+
// Only the scheme word flips, so a named theme survives the toggle:
|
|
1867
|
+
// "soft" becomes "soft dark", and "soft dark" becomes "soft light". And
|
|
1868
|
+
// only the scheme word is saved: the name is the host's, and a name saved
|
|
1869
|
+
// on one page would replace another page's theme on restore.
|
|
1870
|
+
const theme = this.getAttribute("theme") ?? "";
|
|
1871
|
+
const scheme = theme.split(/\s+/).includes("dark") ? "light" : "dark";
|
|
1872
|
+
this.setAttribute("theme", withScheme(theme, scheme));
|
|
1873
|
+
this.#storage.writePreference(THEME_KEY, scheme);
|
|
1850
1874
|
this.#syncThemeGlyph();
|
|
1851
1875
|
}
|
|
1852
1876
|
|
|
@@ -1964,7 +1988,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1964
1988
|
|
|
1965
1989
|
/** Reflect the current theme on the toggle: show the destination's glyph. */
|
|
1966
1990
|
#syncThemeGlyph(): void {
|
|
1967
|
-
const dark = this.getAttribute("theme")
|
|
1991
|
+
const dark = (this.getAttribute("theme") ?? "").split(/\s+/).includes("dark");
|
|
1968
1992
|
// Replaced wholesale rather than toggling a class: the two marks are
|
|
1969
1993
|
// different paths, not one path in two states, and the slot has to keep
|
|
1970
1994
|
// working so a host can still supply its own.
|
|
@@ -2238,96 +2262,44 @@ export class AgUiChat extends HTMLElement {
|
|
|
2238
2262
|
this.#chat.className = "chat";
|
|
2239
2263
|
this.#chat.setAttribute("part", "panel");
|
|
2240
2264
|
|
|
2241
|
-
const
|
|
2242
|
-
|
|
2243
|
-
header
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
this.
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
headerActions.name = "header-actions";
|
|
2263
|
-
|
|
2264
|
-
const controls = document.createElement("div");
|
|
2265
|
-
controls.className = "header-controls";
|
|
2266
|
-
controls.setAttribute("part", "header-controls");
|
|
2267
|
-
|
|
2268
|
-
// Both controls delegate to the public methods, so a host chrome driving
|
|
2269
|
-
// them imperatively takes exactly the path the built-in button takes.
|
|
2270
|
-
const history = headerButton("history", this.#strings.chatHistory, "☰");
|
|
2271
|
-
history.addEventListener("click", () => this.openThreads());
|
|
2272
|
-
|
|
2273
|
-
// ↺ rather than ⭯: the same idea in a glyph that has a font behind it in
|
|
2274
|
-
// every browser. The obscure one rendered as an unreadable mark at 14px, and a
|
|
2275
|
-
// header control nobody can name is one nobody presses.
|
|
2276
|
-
const checkpoints = headerButton("checkpoints", this.#strings.checkpoints, "↺");
|
|
2277
|
-
checkpoints.addEventListener("click", () => this.toggleCheckpoints());
|
|
2278
|
-
|
|
2279
|
-
const newChat = headerButton("new", this.#strings.newChat, "✚");
|
|
2280
|
-
newChat.addEventListener("click", () => this.newChat());
|
|
2281
|
-
|
|
2282
|
-
const collapse = headerButton("collapse", this.#strings.collapse, "—");
|
|
2283
|
-
collapse.addEventListener("click", () => this.toggleCollapsed());
|
|
2284
|
-
|
|
2285
|
-
// Only offered when the server actually indexes runs — without
|
|
2286
|
-
// `data-runs-url` there is nothing to continue and the button would open
|
|
2287
|
-
// a permanently empty panel. Asks the history's run index rather than
|
|
2288
|
-
// re-testing the attribute, so "configured" means one thing everywhere (an
|
|
2289
|
-
// empty value is unset, not a relative URL to the current page).
|
|
2290
|
-
if (this.#history.runs() !== null) {
|
|
2291
|
-
controls.append(history, checkpoints, newChat);
|
|
2292
|
-
} else {
|
|
2293
|
-
controls.append(history, newChat);
|
|
2294
|
-
}
|
|
2295
|
-
// Optional built-in theme toggle: off unless the host opts in, so
|
|
2296
|
-
// it never competes with a host-supplied switch in `slot="header-actions"`.
|
|
2297
|
-
if (this.getAttribute("data-theme-toggle") !== null) {
|
|
2298
|
-
this.#themeToggle.type = "button";
|
|
2299
|
-
this.#themeToggle.className = "header-btn header-btn--theme";
|
|
2300
|
-
this.#themeToggle.setAttribute("part", "header-button theme-toggle");
|
|
2301
|
-
this.#themeToggle.title = this.#strings.toggleTheme;
|
|
2302
|
-
this.#themeToggle.setAttribute("aria-label", this.#strings.toggleTheme);
|
|
2303
|
-
this.#themeToggle.addEventListener("click", () => this.toggleTheme(), { signal });
|
|
2265
|
+
const titleText = this.getAttribute("title-text") ?? this.#strings.title;
|
|
2266
|
+
const themed = this.getAttribute("data-theme-toggle") !== null;
|
|
2267
|
+
const { header, checkpoints } = buildHeader({
|
|
2268
|
+
host: this,
|
|
2269
|
+
title: this.#title,
|
|
2270
|
+
titleText,
|
|
2271
|
+
iconUrl: this.getAttribute("data-icon-url"),
|
|
2272
|
+
strings: this.#strings,
|
|
2273
|
+
// Asks the history's run index rather than re-testing the attribute, so
|
|
2274
|
+
// "configured" means one thing everywhere (an empty value is unset, not a
|
|
2275
|
+
// relative URL to the current page).
|
|
2276
|
+
withCheckpoints: this.#history.runs() !== null,
|
|
2277
|
+
themeToggle: themed ? this.#themeToggle : null,
|
|
2278
|
+
signal,
|
|
2279
|
+
onHistory: () => this.openThreads(),
|
|
2280
|
+
onCheckpoints: () => this.toggleCheckpoints(),
|
|
2281
|
+
onNewChat: () => this.newChat(),
|
|
2282
|
+
onCollapse: () => this.toggleCollapsed(),
|
|
2283
|
+
onToggleTheme: () => this.toggleTheme(),
|
|
2284
|
+
});
|
|
2285
|
+
if (themed) {
|
|
2304
2286
|
this.#syncThemeGlyph();
|
|
2305
|
-
controls.append(this.#themeToggle);
|
|
2306
2287
|
}
|
|
2307
|
-
controls.append(collapse);
|
|
2308
|
-
header.append(title, headerActions, controls);
|
|
2309
2288
|
|
|
2310
2289
|
// A panel is a window and a header is its title bar.
|
|
2311
2290
|
this.#placement.enablePanelDrag(header);
|
|
2312
2291
|
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
this.#messages.setAttribute("aria-live", "off");
|
|
2325
|
-
this.#messages.setAttribute("aria-label", this.#strings.conversation);
|
|
2326
|
-
|
|
2327
|
-
this.#jumpButton.className = "jump-latest";
|
|
2328
|
-
this.#jumpButton.type = "button";
|
|
2329
|
-
this.#jumpButton.setAttribute("part", "jump-latest");
|
|
2330
|
-
this.#jumpButton.textContent = this.#strings.jumpToLatest;
|
|
2292
|
+
buildTranscript({
|
|
2293
|
+
messages: this.#messages,
|
|
2294
|
+
jumpButton: this.#jumpButton,
|
|
2295
|
+
emptyWrap: this.#emptyWrap,
|
|
2296
|
+
greetingText: this.#greetingText,
|
|
2297
|
+
starters: renderStarterChips(this, this.#strings, (prompt) => {
|
|
2298
|
+
void this.sendMessage(prompt);
|
|
2299
|
+
}),
|
|
2300
|
+
strings: this.#strings,
|
|
2301
|
+
});
|
|
2302
|
+
this.#syncGreeting();
|
|
2331
2303
|
|
|
2332
2304
|
// The quote offer, and the transcript's settled selections it listens for.
|
|
2333
2305
|
this.#excerpts.mount(signal);
|
|
@@ -2338,82 +2310,25 @@ export class AgUiChat extends HTMLElement {
|
|
|
2338
2310
|
|
|
2339
2311
|
this.#announcer.mount();
|
|
2340
2312
|
|
|
2341
|
-
// Empty-state region: a host slot at the top of the list, hidden as soon as
|
|
2342
|
-
// anything renders.
|
|
2343
|
-
this.#emptyWrap.className = "empty";
|
|
2344
|
-
this.#emptyWrap.setAttribute("part", "empty");
|
|
2345
|
-
// The greeting heads the empty state, in a slot of its own: a host that
|
|
2346
|
-
// replaces the starters keeps the greeting, and a host that replaces the
|
|
2347
|
-
// greeting keeps the starters. A div rather than a heading, because this
|
|
2348
|
-
// sits inside somebody else's page, which owns the document outline.
|
|
2349
|
-
const greeting = document.createElement("div");
|
|
2350
|
-
greeting.className = "greeting";
|
|
2351
|
-
greeting.setAttribute("part", "greeting");
|
|
2352
|
-
const greetingSlot = document.createElement("slot");
|
|
2353
|
-
greetingSlot.name = "greeting";
|
|
2354
|
-
greetingSlot.append(this.#greetingText);
|
|
2355
|
-
greeting.append(greetingSlot);
|
|
2356
|
-
this.#syncGreeting();
|
|
2357
|
-
const emptySlot = document.createElement("slot");
|
|
2358
|
-
emptySlot.name = "empty";
|
|
2359
|
-
// Fallback content, so a host that slots its own gets exactly that and
|
|
2360
|
-
// nothing of ours: the starters live *inside* the slot rather than beside
|
|
2361
|
-
// it, which is the difference between an offer and an imposition.
|
|
2362
|
-
const starters = renderStarterChips(this, this.#strings, (prompt) => {
|
|
2363
|
-
void this.sendMessage(prompt);
|
|
2364
|
-
});
|
|
2365
|
-
if (starters !== null) {
|
|
2366
|
-
emptySlot.append(starters);
|
|
2367
|
-
}
|
|
2368
|
-
this.#emptyWrap.replaceChildren(greeting, emptySlot);
|
|
2369
|
-
this.#queuedRow.className = "queued";
|
|
2370
|
-
this.#queuedRow.setAttribute("part", "queued");
|
|
2371
|
-
this.#queuedRow.setAttribute("role", "group");
|
|
2372
|
-
this.#queuedRow.setAttribute("aria-label", this.#strings.queued);
|
|
2373
|
-
this.#queuedRow.hidden = true;
|
|
2374
2313
|
this.#messages.append(this.#emptyWrap);
|
|
2375
2314
|
// Stamped from the first frame, so a page that mounts empty is laid out as
|
|
2376
2315
|
// empty rather than switching to it on the first change.
|
|
2377
2316
|
this.#transcript.updateEmptyState();
|
|
2378
2317
|
|
|
2379
|
-
const inputRow =
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
this.#input.className = "input";
|
|
2394
|
-
this.#input.setAttribute("part", "input");
|
|
2395
|
-
this.#input.setAttribute("aria-label", this.#strings.message);
|
|
2396
|
-
this.#input.rows = 1;
|
|
2397
|
-
this.#input.placeholder = this.#strings.inputPlaceholder;
|
|
2398
|
-
this.#input.addEventListener("keydown", (event) => this.#onKeydown(event), { signal });
|
|
2399
|
-
this.#input.addEventListener("input", () => this.#onInput(), { signal });
|
|
2400
|
-
|
|
2401
|
-
// Icon-only, with both glyphs mounted at once and CSS showing the one the
|
|
2402
|
-
// state calls for — swapping a single glyph would leave a host that slotted
|
|
2403
|
-
// its own Send mark holding a stop icon mid-run.
|
|
2404
|
-
this.#send.className = "send";
|
|
2405
|
-
this.#send.type = "button";
|
|
2406
|
-
this.#send.setAttribute("part", "send");
|
|
2407
|
-
this.#send.replaceChildren(
|
|
2408
|
-
glyphSlot("icon-send", "send-send", ICON_SEND),
|
|
2409
|
-
glyphSlot("icon-stop", "send-stop", ICON_STOP),
|
|
2410
|
-
);
|
|
2411
|
-
this.#send.title = this.#strings.send;
|
|
2412
|
-
this.#send.setAttribute("aria-label", this.#strings.send);
|
|
2413
|
-
this.#send.dataset["state"] = "idle";
|
|
2414
|
-
this.#send.addEventListener(
|
|
2415
|
-
"click",
|
|
2416
|
-
() => {
|
|
2318
|
+
const inputRow = buildComposer({
|
|
2319
|
+
input: this.#input,
|
|
2320
|
+
send: this.#send,
|
|
2321
|
+
attachButton: this.#attachButton,
|
|
2322
|
+
fileInput: this.#fileInput,
|
|
2323
|
+
voiceSlot: this.#voiceSlot,
|
|
2324
|
+
hint: this.#composerHint,
|
|
2325
|
+
queuedRow: this.#queuedRow,
|
|
2326
|
+
attachSlot: this.#attachSlot,
|
|
2327
|
+
strings: this.#strings,
|
|
2328
|
+
signal,
|
|
2329
|
+
onKeydown: (event) => this.#onKeydown(event),
|
|
2330
|
+
onInput: () => this.#onInput(),
|
|
2331
|
+
onSend: () => {
|
|
2417
2332
|
// One button, two states: Send while idle, Stop while a run is in
|
|
2418
2333
|
// flight (no layout change).
|
|
2419
2334
|
if (this.#running) {
|
|
@@ -2422,53 +2337,21 @@ export class AgUiChat extends HTMLElement {
|
|
|
2422
2337
|
}
|
|
2423
2338
|
void this.#submit();
|
|
2424
2339
|
},
|
|
2425
|
-
|
|
2426
|
-
);
|
|
2427
|
-
|
|
2428
|
-
this.#composerHint.className = "skill-hint";
|
|
2429
|
-
this.#composerHint.setAttribute("part", "skill-hint");
|
|
2430
|
-
this.#composerHint.hidden = true;
|
|
2431
|
-
|
|
2432
|
-
// File-upload affordance: a paperclip button (hidden until
|
|
2433
|
-
// `data-attachments-url` is wired) opening a hidden multi-file input.
|
|
2434
|
-
// Drag-and-drop covers the whole shell (wired by ComposerAttachments).
|
|
2435
|
-
this.#attachButton.className = "attach-btn";
|
|
2436
|
-
this.#attachButton.type = "button";
|
|
2437
|
-
this.#attachButton.setAttribute("part", "attach-button");
|
|
2438
|
-
this.#attachButton.replaceChildren(glyphSlot("icon-attach", "attach-glyph", ICON_ATTACH));
|
|
2439
|
-
this.#attachButton.title = this.#strings.attachFiles;
|
|
2440
|
-
this.#attachButton.setAttribute("aria-label", this.#strings.attachFiles);
|
|
2441
|
-
this.#attachButton.hidden = true;
|
|
2442
|
-
this.#attachButton.addEventListener("click", () => this.#fileInput.click(), { signal });
|
|
2443
|
-
|
|
2444
|
-
this.#fileInput.className = "attach-input";
|
|
2445
|
-
this.#fileInput.type = "file";
|
|
2446
|
-
this.#fileInput.multiple = true;
|
|
2447
|
-
this.#fileInput.hidden = true;
|
|
2448
|
-
this.#fileInput.addEventListener("change", () => this.#attachments.onFilesPicked(), {
|
|
2449
|
-
signal,
|
|
2340
|
+
onFilesPicked: () => this.#attachments.onFilesPicked(),
|
|
2450
2341
|
});
|
|
2451
2342
|
|
|
2452
|
-
this.#attachSlot.className = "attachment-slot";
|
|
2453
|
-
|
|
2454
|
-
// Mic button mount point (kept empty until ComposerVoice mounts the control).
|
|
2455
|
-
this.#voiceSlot.className = "voice-slot";
|
|
2456
|
-
|
|
2457
2343
|
// A coarse footer slot below the composer.
|
|
2458
2344
|
const footer = document.createElement("slot");
|
|
2459
2345
|
footer.name = "footer";
|
|
2460
2346
|
|
|
2461
|
-
tools.append(this.#attachButton, this.#voiceSlot, this.#send);
|
|
2462
|
-
composer.append(this.#input, tools);
|
|
2463
|
-
inputRow.append(composer, this.#fileInput);
|
|
2464
|
-
// Composer surfaces sit just above the input: the skills palette (opens on
|
|
2465
|
-
// `/`), the chips, the hint saying what the composer still needs, and the
|
|
2466
|
-
// pending-attachments tray.
|
|
2467
2347
|
this.#messagesWrap.className = "messages-wrap";
|
|
2468
2348
|
// Sibling of the list inside a shared box, not a child of it: the
|
|
2469
2349
|
// affordance offering to scroll must not scroll away with the content.
|
|
2470
2350
|
this.#messagesWrap.replaceChildren(this.#messages, this.#jumpButton, this.#excerpts.button);
|
|
2471
2351
|
|
|
2352
|
+
// Composer surfaces sit just above the input: the skills palette (opens on
|
|
2353
|
+
// `/`), the chips, the hint saying what the composer still needs, the
|
|
2354
|
+
// queued messages and the pending-attachments tray.
|
|
2472
2355
|
this.#chat.replaceChildren(
|
|
2473
2356
|
header,
|
|
2474
2357
|
this.#messagesWrap,
|
|
@@ -2506,38 +2389,20 @@ export class AgUiChat extends HTMLElement {
|
|
|
2506
2389
|
{ signal },
|
|
2507
2390
|
);
|
|
2508
2391
|
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
// decoration to a screen reader rather than a second, context-free number.
|
|
2520
|
-
this.#badge.setAttribute("aria-hidden", "true");
|
|
2521
|
-
this.#badge.hidden = true;
|
|
2522
|
-
// Only the edge rail shows this. A full-height column carrying one small
|
|
2523
|
-
// icon reads as a coloured stripe rather than a way back into a
|
|
2524
|
-
// conversation -- it is the widest collapsed state there is and the one
|
|
2525
|
-
// that says least about itself. Written here and hidden in CSS everywhere
|
|
2526
|
-
// else, because the launcher is one element shaped by placement.
|
|
2527
|
-
this.#railLabel.className = "rail-label";
|
|
2528
|
-
this.#railLabel.setAttribute("part", "rail-label");
|
|
2529
|
-
this.#railLabel.setAttribute("aria-hidden", "true");
|
|
2530
|
-
this.#railLabel.textContent = this.getAttribute("title-text") ?? this.#strings.title;
|
|
2531
|
-
this.#launcher.replaceChildren(
|
|
2532
|
-
iconElement("launcher", "launcher-icon", ICON_LAUNCHER, readLauncherIconUrl(this)),
|
|
2533
|
-
this.#railLabel,
|
|
2534
|
-
this.#badge,
|
|
2535
|
-
);
|
|
2536
|
-
this.#launcher.addEventListener("click", () => this.setCollapsed(false), { signal });
|
|
2392
|
+
buildLauncher({
|
|
2393
|
+
launcher: this.#launcher,
|
|
2394
|
+
badge: this.#badge,
|
|
2395
|
+
railLabel: this.#railLabel,
|
|
2396
|
+
titleText,
|
|
2397
|
+
iconUrl: readLauncherIconUrl(this),
|
|
2398
|
+
strings: this.#strings,
|
|
2399
|
+
signal,
|
|
2400
|
+
onOpen: () => this.setCollapsed(false),
|
|
2401
|
+
});
|
|
2537
2402
|
this.#placement.enableLauncherDrag(signal);
|
|
2538
2403
|
|
|
2539
2404
|
this.#placement.mountResizeGrips(this.#chat);
|
|
2540
|
-
adoptStyles(this.#root);
|
|
2405
|
+
this.#builtInSheet = adoptStyles(this.#root, this.#extraStyles);
|
|
2541
2406
|
const probe = this.#placement.probe;
|
|
2542
2407
|
probe.className = "viewport-probe";
|
|
2543
2408
|
probe.setAttribute("aria-hidden", "true");
|