agentic-ui-kit 0.4.0 → 0.5.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/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { A as salvageJson, C as awaitingToolCalls, O as settleToolCall, S as applyEvent, b as toBlocks, d as isStateRef, f as matchesSchema, g as uiActionSchema, i as parseUiMarkup, v as readEventStream, x as activityTokens } from "./ui-markup-DtMYer7I.js";
2
+ import { C as toBlocks, E as awaitingToolCalls, N as salvageJson, T as applyEvent, _ as replacedId, d as isStateRef, f as matchesSchema, g as uiActionSchema, i as parseUiMarkup, j as settleToolCall, w as activityTokens, x as readEventStream } from "./ui-markup-CG9Lednh.js";
3
3
  import { clsx } from "clsx";
4
- import { Autocomplete, Button as Button$1, Dialog, Disclosure as Disclosure$1, DisclosurePanel, Focusable, Header, Heading, Input, Keyboard, Link, Menu as Menu$1, MenuItem as MenuItem$1, MenuSection as MenuSection$1, MenuTrigger, MenuTrigger as MenuTrigger$1, Modal as Modal$1, ModalOverlay, Popover as Popover$1, SearchField, Separator, SubmenuTrigger, Text, ToggleButton, ToggleButtonGroup, Tooltip as Tooltip$1, TooltipTrigger, useFilter } from "react-aria-components";
4
+ import { Autocomplete, Button as Button$1, Dialog, DialogTrigger, Disclosure as Disclosure$1, DisclosurePanel, Focusable, Header, Heading, Input, Keyboard, Link, Menu as Menu$1, MenuItem as MenuItem$1, MenuSection as MenuSection$1, MenuTrigger, MenuTrigger as MenuTrigger$1, Modal as Modal$1, ModalOverlay, Popover as Popover$1, SearchField, Separator, SubmenuTrigger, Text, ToggleButton, ToggleButtonGroup, Tooltip as Tooltip$1, TooltipTrigger, useFilter } from "react-aria-components";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { Component, Fragment as Fragment$1, createContext, createElement, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
6
7
  import { AlertTriangle, ArrowDown, ArrowUp, Check, ChevronDown, ChevronRight, ChevronsUpDown, Copy, FileText, Folder, FolderOpen, FolderPlus, Globe, Info, MoreHorizontal, PanelLeft, Pencil, Pin, PinOff, Plus, RefreshCw, Share, ShieldQuestion, Square, ThumbsDown, ThumbsUp, Trash2, Volume2, X } from "lucide-react";
7
- import { Component, Fragment as Fragment$1, createContext, createElement, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
8
8
  import { UNSAFE_PortalProvider } from "react-aria";
9
9
  import { createMathPlugin } from "@streamdown/math";
10
10
  import { Streamdown, defaultRehypePlugins } from "streamdown";
@@ -74,37 +74,6 @@ function Badge({ children, tone = "outline", icon, className }) {
74
74
  children: [icon, children]
75
75
  });
76
76
  }
77
- // src/primitives/segmented-control.tsx
78
- /**
79
- * One of a few options, side by side on a track, like claude.ai's Chat / Cowork switch. Themed with
80
- * `--chat-segment-track` (default: a shade darker than the surface it sits on) and `--chat-segment-selected`
81
- * (default `--chat-raised`).
82
- */
83
- function SegmentedControl({ label, options, value, onChange, className }) {
84
- return /* @__PURE__ */ jsx(ToggleButtonGroup, {
85
- "aria-label": label,
86
- selectionMode: "single",
87
- disallowEmptySelection: true,
88
- selectedKeys: [value],
89
- onSelectionChange: (keys) => {
90
- const next = [...keys][0];
91
- if (next !== void 0) onChange(next);
92
- },
93
- className: clsx("inline-flex shrink-0 items-center gap-0.5 rounded-chat-md p-0.5", "bg-[var(--chat-segment-track,color-mix(in_srgb,var(--chat-bg)_55%,transparent))]", className),
94
- children: options.map((o) => {
95
- const button = /* @__PURE__ */ jsxs(ToggleButton, {
96
- id: o.id,
97
- ...o.iconOnly && { "aria-label": o.label },
98
- className: clsx("inline-flex h-6 cursor-pointer items-center justify-center gap-1.5 rounded-[calc(var(--chat-radius-md)-2px)] font-chat-sans text-(length:--chat-ui-font-size) leading-5 text-chat-muted outline-none transition-colors select-none [&_svg]:size-4", o.iconOnly ? "w-7" : "px-2", "data-hovered:text-chat-fg data-focus-visible:ring-2 data-focus-visible:ring-chat-focus", "data-selected:bg-[var(--chat-segment-selected,var(--chat-raised))] data-selected:text-chat-fg data-selected:shadow-[inset_0_0_0_1px_var(--chat-border-strong)]"),
99
- children: [o.icon, !o.iconOnly && o.label]
100
- }, o.id);
101
- return o.iconOnly ? /* @__PURE__ */ jsx(Tooltip, {
102
- content: o.label,
103
- children: button
104
- }, o.id) : button;
105
- })
106
- });
107
- }
108
77
  // src/primitives/caret.tsx
109
78
  /** The small filled triangle claude.ai uses after expandable labels. Points right; rotate to point down. */
110
79
  function Caret({ className }) {
@@ -182,6 +151,176 @@ function IconSlot({ children, className }) {
182
151
  children
183
152
  });
184
153
  }
154
+ // src/primitives/labels.tsx
155
+ const plural = (count, one, many) => count === 1 ? one : many(count);
156
+ /** The English strings. */
157
+ const defaultLabels = {
158
+ thinking: "Thinking…",
159
+ thought: "Thought",
160
+ thoughtFor: (duration) => `Thought for ${duration}`,
161
+ waitingForYou: " · waiting for you",
162
+ usedTool: (name) => `used ${name}`,
163
+ joinSummaries: (phrases) => {
164
+ const text = phrases.join(", ");
165
+ return text.charAt(0).toUpperCase() + text.slice(1);
166
+ },
167
+ duration: (seconds) => seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`,
168
+ ranCommandLabel: "Ran a command",
169
+ ranCommands: (count) => plural(count, "ran a command", (n) => `ran ${n} commands`),
170
+ searchedWebLabel: "Searched the web",
171
+ readPageLabel: "Read page",
172
+ readPages: (count) => plural(count, "read a page", (n) => `read ${n} pages`),
173
+ searchedWeb: (count) => plural(count, "searched the web", (n) => `searched the web ${n} times`),
174
+ recalledMemoryLabel: "Recalled memory",
175
+ recalledMemory: () => "recalled memory",
176
+ output: "Output",
177
+ noOutput: "(no output)",
178
+ noResults: "No results",
179
+ reasoningHidden: "The model's reasoning is not shown.",
180
+ allowTool: (name) => `Allow ${name}?`,
181
+ allowAction: (label) => /* @__PURE__ */ jsxs(Fragment, { children: [
182
+ "Allow ",
183
+ label,
184
+ "?"
185
+ ] }),
186
+ allow: "Allow",
187
+ deny: "Deny",
188
+ sidebar: "Sidebar",
189
+ closeSidebar: "Close sidebar",
190
+ openSidebar: "Open sidebar",
191
+ expandSidebar: "Expand sidebar",
192
+ collapseSidebar: "Collapse sidebar",
193
+ context: "Context",
194
+ contextUsed: "Context used",
195
+ percentUsed: (percent) => `${percent}% used`,
196
+ freeSpace: "Free space",
197
+ tokenCount: (count) => `${count} tokens`,
198
+ replyPlaceholder: "Reply…",
199
+ replyDirectly: "Or reply directly…",
200
+ message: "Message",
201
+ stop: "Stop",
202
+ send: "Send",
203
+ addFiles: "Add files and more",
204
+ attachments: "Attachments",
205
+ editPastedText: "Edit pasted text",
206
+ viewPastedText: "View pasted text",
207
+ removeAttachment: (name) => `Remove ${name}`,
208
+ pastedText: "Pasted text",
209
+ pasted: "Pasted",
210
+ failed: "Failed",
211
+ file: "File",
212
+ uploadFailed: "Upload failed",
213
+ cancel: "Cancel",
214
+ close: "Close",
215
+ save: "Save",
216
+ chats: "Chats",
217
+ running: "Running",
218
+ unread: "Unread",
219
+ moreOptionsFor: (title) => `More options for ${title}`,
220
+ new: "New",
221
+ viewAll: "View all",
222
+ chatActions: "Chat actions",
223
+ pin: "Pin",
224
+ unpin: "Unpin",
225
+ rename: "Rename",
226
+ addToProject: "Add to project",
227
+ searchProjects: "Search or create a project",
228
+ projects: "Projects",
229
+ newProject: "New project",
230
+ shareChat: "Share chat",
231
+ delete: "Delete",
232
+ justNow: "just now",
233
+ copy: "Copy",
234
+ copied: "Copied",
235
+ retry: "Retry",
236
+ edit: "Edit",
237
+ readAloud: "Read aloud",
238
+ goodResponse: "Good response",
239
+ badResponse: "Bad response",
240
+ somethingWentWrong: "Something went wrong.",
241
+ conversation: "Conversation",
242
+ scrollToBottom: "Scroll to bottom",
243
+ showMore: "Show more",
244
+ showLess: "Show less",
245
+ model: "Model",
246
+ modelButton: (label) => `Model: ${label}`,
247
+ effort: "Effort",
248
+ effortNote: "Higher effort means more thorough responses, but takes longer and uses your limits faster.",
249
+ recommended: "Recommended",
250
+ effortFaster: "Faster",
251
+ effortSmarter: "Smarter",
252
+ moreModels: "More models",
253
+ widget: "Widget",
254
+ visualUpdated: "Updated below ↓",
255
+ visualUpdating: "Updating…",
256
+ drawingWidget: "Drawing"
257
+ };
258
+ const LabelsContext = createContext(defaultLabels);
259
+ /** Merges `labels` over the labels of the enclosing provider (or the defaults). */
260
+ function LabelsProvider({ labels, children }) {
261
+ const parent = useLabels();
262
+ const merged = useMemo(() => labels ? {
263
+ ...parent,
264
+ ...labels
265
+ } : parent, [parent, labels]);
266
+ return /* @__PURE__ */ jsx(LabelsContext.Provider, {
267
+ value: merged,
268
+ children
269
+ });
270
+ }
271
+ /** The active UI strings: the enclosing `ChatTheme`'s `labels` over the English defaults. */
272
+ function useLabels() {
273
+ return useContext(LabelsContext);
274
+ }
275
+ // src/primitives/copy-button.tsx
276
+ /** Copies `text`; shows a check for a moment after. */
277
+ function CopyButton({ text, className }) {
278
+ const icons = useIcons();
279
+ const labels = useLabels();
280
+ const [copied, setCopied] = useState(false);
281
+ return /* @__PURE__ */ jsx(IconButton, {
282
+ label: copied ? labels.copied : labels.copy,
283
+ size: "sm",
284
+ ...className && { className },
285
+ onPress: () => {
286
+ navigator.clipboard?.writeText(text);
287
+ setCopied(true);
288
+ setTimeout(() => setCopied(false), 1500);
289
+ },
290
+ children: copied ? icons.copied : icons.copy
291
+ });
292
+ }
293
+ // src/primitives/segmented-control.tsx
294
+ /**
295
+ * One of a few options, side by side on a track, like claude.ai's Chat / Cowork switch. Themed with
296
+ * `--chat-segment-track` (default: a shade darker than the surface it sits on) and `--chat-segment-selected`
297
+ * (default `--chat-raised`).
298
+ */
299
+ function SegmentedControl({ label, options, value, onChange, className }) {
300
+ return /* @__PURE__ */ jsx(ToggleButtonGroup, {
301
+ "aria-label": label,
302
+ selectionMode: "single",
303
+ disallowEmptySelection: true,
304
+ selectedKeys: [value],
305
+ onSelectionChange: (keys) => {
306
+ const next = [...keys][0];
307
+ if (next !== void 0) onChange(next);
308
+ },
309
+ className: clsx("inline-flex shrink-0 items-center gap-0.5 rounded-chat-md p-0.5", "bg-[var(--chat-segment-track,color-mix(in_srgb,var(--chat-bg)_55%,transparent))]", className),
310
+ children: options.map((o) => {
311
+ const button = /* @__PURE__ */ jsxs(ToggleButton, {
312
+ id: o.id,
313
+ ...o.iconOnly && { "aria-label": o.label },
314
+ className: clsx("inline-flex h-6 cursor-pointer items-center justify-center gap-1.5 rounded-[calc(var(--chat-radius-md)-2px)] font-chat-sans text-(length:--chat-ui-font-size) leading-5 text-chat-muted outline-none transition-colors select-none [&_svg]:size-4", o.iconOnly ? "w-7" : "px-2", "data-hovered:text-chat-fg data-focus-visible:ring-2 data-focus-visible:ring-chat-focus", "data-selected:bg-[var(--chat-segment-selected,var(--chat-raised))] data-selected:text-chat-fg data-selected:shadow-[inset_0_0_0_1px_var(--chat-border-strong)]"),
315
+ children: [o.icon, !o.iconOnly && o.label]
316
+ }, o.id);
317
+ return o.iconOnly ? /* @__PURE__ */ jsx(Tooltip, {
318
+ content: o.label,
319
+ children: button
320
+ }, o.id) : button;
321
+ })
322
+ });
323
+ }
185
324
  // src/primitives/disclosure.tsx
186
325
  /** A row that expands to show more, like "Recalled memory ▸". */
187
326
  function Disclosure({ summary, children, className, summaryClassName, panelClassName, indicator = "caret", trailing, ...props }) {
@@ -559,7 +698,7 @@ function useHighlight(code, language) {
559
698
  }
560
699
  // src/primitives/code-block.tsx
561
700
  /** Monospace code with Shiki highlighting, scrolling past a fixed height. */
562
- function CodeBlock({ code, language, label = language, variant = "plain", className }) {
701
+ function CodeBlock({ code, language, label = language, variant = "plain", copy = variant === "boxed", className }) {
563
702
  const result = useHighlight(code, language);
564
703
  const lines = result?.lines;
565
704
  const themeBackground = useCodeThemes().background === "theme" && variant === "boxed" && !!result;
@@ -571,9 +710,12 @@ function CodeBlock({ code, language, label = language, variant = "plain", classN
571
710
  "data-chat-code-bg": themeBackground ? "theme" : void 0,
572
711
  style: themeBackground ? result.vars : void 0,
573
712
  className: clsx(variant === "boxed" && "my-4 rounded-chat-md border border-chat-code-border bg-chat-code-bg px-4 py-3.5", className),
574
- children: [label && /* @__PURE__ */ jsx("div", {
575
- className: "mb-3 font-chat-sans text-(length:--chat-ui-font-size-sm) leading-4 text-chat-subtle",
576
- children: label
713
+ children: [(label || copy) && /* @__PURE__ */ jsxs("div", {
714
+ className: clsx("flex items-center gap-2 font-chat-sans text-(length:--chat-ui-font-size-sm) leading-4 text-chat-subtle", copy ? "-mt-1.5 -mr-2 mb-1.5" : "mb-3"),
715
+ children: [/* @__PURE__ */ jsx("span", {
716
+ className: "min-w-0 flex-1 truncate",
717
+ children: label
718
+ }), copy && /* @__PURE__ */ jsx(CopyButton, { text: code })]
577
719
  }), /* @__PURE__ */ jsx("pre", {
578
720
  className: clsx("overflow-auto font-chat-mono text-(length:--chat-code-font-size) leading-[1.75] whitespace-pre text-chat-fg", variant === "plain" && "max-h-56", "[scrollbar-color:var(--chat-border-strong)_transparent] [scrollbar-width:thin]"),
579
721
  children: /* @__PURE__ */ jsxs("code", { children: [highlighted ? highlighted.map((line, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [i > 0 && "\n", line.map((token, j) => /* @__PURE__ */ jsx("span", {
@@ -584,123 +726,6 @@ function CodeBlock({ code, language, label = language, variant = "plain", classN
584
726
  })]
585
727
  });
586
728
  }
587
- // src/primitives/labels.tsx
588
- const plural = (count, one, many) => count === 1 ? one : many(count);
589
- /** The English strings. */
590
- const defaultLabels = {
591
- thinking: "Thinking…",
592
- thought: "Thought",
593
- thoughtFor: (duration) => `Thought for ${duration}`,
594
- waitingForYou: " · waiting for you",
595
- usedTool: (name) => `used ${name}`,
596
- joinSummaries: (phrases) => {
597
- const text = phrases.join(", ");
598
- return text.charAt(0).toUpperCase() + text.slice(1);
599
- },
600
- duration: (seconds) => seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`,
601
- ranCommandLabel: "Ran a command",
602
- ranCommands: (count) => plural(count, "ran a command", (n) => `ran ${n} commands`),
603
- searchedWebLabel: "Searched the web",
604
- readPageLabel: "Read page",
605
- readPages: (count) => plural(count, "read a page", (n) => `read ${n} pages`),
606
- searchedWeb: (count) => plural(count, "searched the web", (n) => `searched the web ${n} times`),
607
- recalledMemoryLabel: "Recalled memory",
608
- recalledMemory: () => "recalled memory",
609
- output: "Output",
610
- noOutput: "(no output)",
611
- noResults: "No results",
612
- reasoningHidden: "The model's reasoning is not shown.",
613
- allowTool: (name) => `Allow ${name}?`,
614
- allowAction: (label) => /* @__PURE__ */ jsxs(Fragment, { children: [
615
- "Allow ",
616
- label,
617
- "?"
618
- ] }),
619
- allow: "Allow",
620
- deny: "Deny",
621
- sidebar: "Sidebar",
622
- closeSidebar: "Close sidebar",
623
- openSidebar: "Open sidebar",
624
- expandSidebar: "Expand sidebar",
625
- collapseSidebar: "Collapse sidebar",
626
- context: "Context",
627
- contextUsed: "Context used",
628
- percentUsed: (percent) => `${percent}% used`,
629
- freeSpace: "Free space",
630
- tokenCount: (count) => `${count} tokens`,
631
- replyPlaceholder: "Reply…",
632
- replyDirectly: "Or reply directly…",
633
- message: "Message",
634
- stop: "Stop",
635
- send: "Send",
636
- addFiles: "Add files and more",
637
- attachments: "Attachments",
638
- editPastedText: "Edit pasted text",
639
- viewPastedText: "View pasted text",
640
- removeAttachment: (name) => `Remove ${name}`,
641
- pastedText: "Pasted text",
642
- pasted: "Pasted",
643
- failed: "Failed",
644
- file: "File",
645
- uploadFailed: "Upload failed",
646
- cancel: "Cancel",
647
- close: "Close",
648
- save: "Save",
649
- chats: "Chats",
650
- running: "Running",
651
- unread: "Unread",
652
- moreOptionsFor: (title) => `More options for ${title}`,
653
- new: "New",
654
- viewAll: "View all",
655
- chatActions: "Chat actions",
656
- pin: "Pin",
657
- unpin: "Unpin",
658
- rename: "Rename",
659
- addToProject: "Add to project",
660
- searchProjects: "Search or create a project",
661
- projects: "Projects",
662
- newProject: "New project",
663
- shareChat: "Share chat",
664
- delete: "Delete",
665
- justNow: "just now",
666
- copy: "Copy",
667
- copied: "Copied",
668
- retry: "Retry",
669
- edit: "Edit",
670
- readAloud: "Read aloud",
671
- goodResponse: "Good response",
672
- badResponse: "Bad response",
673
- somethingWentWrong: "Something went wrong.",
674
- conversation: "Conversation",
675
- scrollToBottom: "Scroll to bottom",
676
- showMore: "Show more",
677
- showLess: "Show less",
678
- model: "Model",
679
- modelButton: (label) => `Model: ${label}`,
680
- effort: "Effort",
681
- effortNote: "Higher effort means more thorough responses, but takes longer and uses your limits faster.",
682
- recommended: "Recommended",
683
- moreModels: "More models",
684
- widget: "Widget",
685
- drawingWidget: "Drawing"
686
- };
687
- const LabelsContext = createContext(defaultLabels);
688
- /** Merges `labels` over the labels of the enclosing provider (or the defaults). */
689
- function LabelsProvider({ labels, children }) {
690
- const parent = useLabels();
691
- const merged = useMemo(() => labels ? {
692
- ...parent,
693
- ...labels
694
- } : parent, [parent, labels]);
695
- return /* @__PURE__ */ jsx(LabelsContext.Provider, {
696
- value: merged,
697
- children
698
- });
699
- }
700
- /** The active UI strings: the enclosing `ChatTheme`'s `labels` over the English defaults. */
701
- function useLabels() {
702
- return useContext(LabelsContext);
703
- }
704
729
  // src/primitives/chat-theme.tsx
705
730
  /**
706
731
  * Applies a chat theme to part of a page. Menus, tooltips and dialogs opened inside render into this
@@ -1247,6 +1272,87 @@ const SidebarContext = createContext({
1247
1272
  dismiss: () => {}
1248
1273
  });
1249
1274
  const useSidebar = () => useContext(SidebarContext);
1275
+ // src/components/replacements.tsx
1276
+ /**
1277
+ * In-place updates of drawn visuals: a tool call whose input has `replaces: <id>` takes the place of that
1278
+ * earlier call. In the same reply the newest finished call is drawn where the first one was, and the old one
1279
+ * stays (dimmed) until the new one's input is complete. A call replaced from a later reply folds into a link.
1280
+ */
1281
+ /** Ready to take the old one's place: its input is complete and it didn't fail. */
1282
+ const isReady = (p) => p.status !== "streaming" && p.status !== "input" && p.status !== "error";
1283
+ const callsOf = (m) => m.parts.filter((p) => p.type === "tool_call");
1284
+ const replaces = (p) => replacedId(p.input ?? p.partialInput);
1285
+ /** One reply's chains: each call's first call (where the chain is drawn) and each first call's chain, in order. */
1286
+ function chainsOf(message) {
1287
+ const first = /* @__PURE__ */ new Map();
1288
+ const chains = /* @__PURE__ */ new Map();
1289
+ for (const call of callsOf(message)) {
1290
+ const r = replaces(call);
1291
+ const root = r !== void 0 && first.has(r) ? first.get(r) : call.id;
1292
+ first.set(call.id, root);
1293
+ chains.set(root, [...chains.get(root) ?? [], call]);
1294
+ }
1295
+ return {
1296
+ first,
1297
+ chains
1298
+ };
1299
+ }
1300
+ /** Calls replaced by a finished call in a later reply: replaced id → the newest such call. */
1301
+ function laterReplacements(messages) {
1302
+ const later = /* @__PURE__ */ new Map();
1303
+ for (const m of messages) {
1304
+ if (m.role !== "assistant") continue;
1305
+ const own = new Set(callsOf(m).map((c) => c.id));
1306
+ for (const call of callsOf(m)) {
1307
+ const r = replaces(call);
1308
+ if (r !== void 0 && !own.has(r) && isReady(call)) later.set(r, call);
1309
+ }
1310
+ }
1311
+ return later;
1312
+ }
1313
+ const LaterContext = createContext(/* @__PURE__ */ new Map());
1314
+ function LaterReplacements({ value, children }) {
1315
+ return /* @__PURE__ */ jsx(LaterContext.Provider, {
1316
+ value,
1317
+ children
1318
+ });
1319
+ }
1320
+ /** The call in a later reply that replaces any call of this chain, if there is one. */
1321
+ function useLaterReplacement(chain) {
1322
+ const later = useContext(LaterContext);
1323
+ for (let i = chain.length - 1; i >= 0; i--) {
1324
+ const by = later.get(chain[i].id);
1325
+ if (by) return by;
1326
+ }
1327
+ }
1328
+ /** The DOM id of a drawn tool call, for the "Updated below" link. */
1329
+ const toolAnchor = (id) => `chat-tool-${id}`;
1330
+ /** Draws a chain: the newest finished call (dimmed while a newer one streams), or a link to its later update. */
1331
+ function ToolChain({ chain, draw }) {
1332
+ const labels = useLabels();
1333
+ const later = useLaterReplacement(chain);
1334
+ const shown = chain.findLast(isReady) ?? chain[0];
1335
+ const updating = chain.indexOf(shown) < chain.length - 1 && chain.at(-1).status !== "error";
1336
+ if (later) return /* @__PURE__ */ jsx(Button, {
1337
+ variant: "ghost",
1338
+ size: "sm",
1339
+ className: "self-start text-chat-muted data-hovered:text-chat-fg",
1340
+ onPress: () => document.getElementById(toolAnchor(later.id))?.scrollIntoView({
1341
+ behavior: "smooth",
1342
+ block: "center"
1343
+ }),
1344
+ children: labels.visualUpdated
1345
+ });
1346
+ return /* @__PURE__ */ jsxs("div", {
1347
+ id: toolAnchor(chain[0].id),
1348
+ "aria-busy": updating || void 0,
1349
+ className: updating ? "opacity-60 transition-opacity" : "transition-opacity",
1350
+ children: [/* @__PURE__ */ jsx("div", { children: draw(shown) }, shown.id), updating && /* @__PURE__ */ jsx("p", {
1351
+ className: "chat-shimmer mt-1 font-chat-sans text-(length:--chat-ui-font-size-sm) text-chat-muted",
1352
+ children: labels.visualUpdating
1353
+ })]
1354
+ });
1355
+ }
1250
1356
  // src/components/widget-frame.tsx
1251
1357
  const WidgetHostContext = createContext({});
1252
1358
  /** Sets what widgets below may do. ChatView and ChatTranscript set it from their `widgets` prop. */
@@ -1362,6 +1468,8 @@ const BOOT = `(() => {
1362
1468
  new ResizeObserver(() => {
1363
1469
  const h = Math.ceil(root.getBoundingClientRect().height);
1364
1470
  if (h !== last) { last = h; post("ui/notifications/size-changed", { height: h }); }
1471
+ // The frame grows to fit, a moment behind; only content past the cap gets a scrollbar.
1472
+ document.documentElement.style.overflowY = h > ${WIDGET_MAX_HEIGHT} ? "auto" : "hidden";
1365
1473
  }).observe(root);
1366
1474
  const seen = new Set();
1367
1475
  const report = (text) => { if (!seen.has(text) && seen.size < 10) { seen.add(text); post("notifications/message", { level: "error", logger: "widget", data: text }); } };
@@ -1374,6 +1482,7 @@ function fragmentDoc(csp, theme) {
1374
1482
  <meta http-equiv="Content-Security-Policy" content="${csp}">
1375
1483
  <style>
1376
1484
  :root { ${Object.entries(theme.vars).map(([k, v]) => `${k}: ${v};`).join(" ")} color-scheme: ${theme.scheme}; }
1485
+ html { overflow: hidden; }
1377
1486
  html, body { margin: 0; background: transparent; color: var(--chat-fg); font-family: var(--chat-font-sans);
1378
1487
  font-size: var(--chat-ui-font-size); line-height: 1.5; }
1379
1488
  #widget { display: flow-root; }
@@ -2388,10 +2497,14 @@ function ActivityGroup({ parts, defaultExpanded = false, className }) {
2388
2497
  }) : /* @__PURE__ */ jsx(Disclosure, {
2389
2498
  isExpanded: expanded,
2390
2499
  onExpandedChange: setExpanded,
2391
- summary: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Live, {
2392
- live,
2393
- children: activityHeading(parts, views, labels)
2394
- }), after] }),
2500
+ summary: /* @__PURE__ */ jsxs(Fragment, { children: [
2501
+ /* @__PURE__ */ jsx(Lead, { live }),
2502
+ /* @__PURE__ */ jsx(Live, {
2503
+ live,
2504
+ children: activityHeading(parts, views, labels)
2505
+ }),
2506
+ after
2507
+ ] }),
2395
2508
  summaryClassName: "text-(length:--chat-activity-font-size)",
2396
2509
  panelClassName: "pt-3",
2397
2510
  children: /* @__PURE__ */ jsx("div", {
@@ -2401,6 +2514,17 @@ function ActivityGroup({ parts, defaultExpanded = false, className }) {
2401
2514
  })
2402
2515
  });
2403
2516
  }
2517
+ /** The host's mark left of the summary (`icons.activity` while working, `icons.activityDone` after), if set. */
2518
+ function Lead({ live }) {
2519
+ const icons = useIcons();
2520
+ const icon = live ? icons.activity : icons.activityDone;
2521
+ if (!icon) return null;
2522
+ return /* @__PURE__ */ jsx("span", {
2523
+ "aria-hidden": true,
2524
+ className: "chat-activity-icon mr-1.5 inline-flex size-[1.15em] shrink-0 items-center self-center [&_svg]:size-full",
2525
+ children: icon
2526
+ });
2527
+ }
2404
2528
  const SHIMMER_MS = 2e3;
2405
2529
  /**
2406
2530
  * Sweeps a highlight across a label while its step is running. The animation starts at the phase of a
@@ -2425,10 +2549,14 @@ function SingleStep({ part, live, after, expanded, onExpandedChange }) {
2425
2549
  return /* @__PURE__ */ jsx(Disclosure, {
2426
2550
  isExpanded: expanded,
2427
2551
  onExpandedChange,
2428
- summary: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Live, {
2429
- live,
2430
- children: summary
2431
- }), after] }),
2552
+ summary: /* @__PURE__ */ jsxs(Fragment, { children: [
2553
+ /* @__PURE__ */ jsx(Lead, { live }),
2554
+ /* @__PURE__ */ jsx(Live, {
2555
+ live,
2556
+ children: summary
2557
+ }),
2558
+ after
2559
+ ] }),
2432
2560
  summaryClassName: "text-(length:--chat-activity-font-size)",
2433
2561
  panelClassName: "pt-3",
2434
2562
  children: /* @__PURE__ */ jsx("div", {
@@ -2729,21 +2857,6 @@ function formatRelativeTime(iso, now = Date.now(), labels = defaultLabels) {
2729
2857
  function messageText(message) {
2730
2858
  return message.parts.map((p) => p.type === "text" ? p.text : p.type === "custom" ? p.text ?? "" : "").filter(Boolean).join("\n\n");
2731
2859
  }
2732
- function CopyButton({ text }) {
2733
- const icons = useIcons();
2734
- const labels = useLabels();
2735
- const [copied, setCopied] = useState(false);
2736
- return /* @__PURE__ */ jsx(IconButton, {
2737
- label: copied ? labels.copied : labels.copy,
2738
- size: "sm",
2739
- onPress: () => {
2740
- navigator.clipboard?.writeText(text);
2741
- setCopied(true);
2742
- setTimeout(() => setCopied(false), 1500);
2743
- },
2744
- children: copied ? icons.copied : icons.copy
2745
- });
2746
- }
2747
2860
  const timeClass = "px-1 font-chat-sans text-(length:--chat-ui-font-size-sm) leading-4 text-chat-subtle";
2748
2861
  /** The user's turn: a bubble on the right, with time, retry, edit and copy under it on hover. */
2749
2862
  function UserMessage({ message, onRetry, onEdit, actions, copy = true, time = true, className }) {
@@ -2838,6 +2951,7 @@ function AssistantMessage({ message, onRetry, onFeedback, onReadAloud, renderPar
2838
2951
  const streaming = message.status === "streaming";
2839
2952
  const views = useToolViews();
2840
2953
  const blocks = toBlocks(message.parts, (part) => views[part.name]?.Block !== void 0);
2954
+ const { first, chains } = chainsOf(message);
2841
2955
  return /* @__PURE__ */ jsxs("div", {
2842
2956
  className: clsx("group/message flex flex-col gap-4", className),
2843
2957
  children: [
@@ -2852,8 +2966,14 @@ function AssistantMessage({ message, onRetry, onFeedback, onReadAloud, renderPar
2852
2966
  })] }, block.id);
2853
2967
  }
2854
2968
  if (block.kind === "tool") {
2855
- const ToolBlock = views[block.part.name].Block;
2856
- return /* @__PURE__ */ jsx(ToolBlock, { part: block.part }, block.id);
2969
+ if (first.get(block.part.id) !== block.part.id) return null;
2970
+ return /* @__PURE__ */ jsx(ToolChain, {
2971
+ chain: chains.get(block.part.id) ?? [block.part],
2972
+ draw: (part) => {
2973
+ const ToolBlock = (views[part.name] ?? views[block.part.name]).Block;
2974
+ return /* @__PURE__ */ jsx(ToolBlock, { part });
2975
+ }
2976
+ }, block.id);
2857
2977
  }
2858
2978
  const part = block.part;
2859
2979
  if (part.type === "text") return /* @__PURE__ */ jsx(Markdown, {
@@ -3251,6 +3371,115 @@ function AttachButton({ children, label, icon, className, menuClassName }) {
3251
3371
  children
3252
3372
  })] });
3253
3373
  }
3374
+ // src/components/effort-slider.tsx
3375
+ /**
3376
+ * Effort as a slider with one stop per level, lowest (faster) on the left: title and the current level on
3377
+ * top, the level's description under them, "Faster" and "Smarter" under the track. Arrow keys move a stop.
3378
+ */
3379
+ function EffortSlider({ efforts, value, onChange, label, className }) {
3380
+ const labels = useLabels();
3381
+ const id = useId();
3382
+ const index = Math.max(0, efforts.findIndex((e) => e.id === value));
3383
+ const current = efforts[index];
3384
+ const last = Math.max(1, efforts.length - 1);
3385
+ const at = (i) => `${i / last * 100}%`;
3386
+ if (!current) return null;
3387
+ return /* @__PURE__ */ jsxs("div", {
3388
+ className: clsx("flex w-full flex-col gap-1 font-chat-sans", className),
3389
+ children: [
3390
+ /* @__PURE__ */ jsxs("div", {
3391
+ className: "flex items-center gap-2",
3392
+ children: [
3393
+ /* @__PURE__ */ jsx("label", {
3394
+ htmlFor: id,
3395
+ className: "flex-1 text-(length:--chat-ui-font-size) font-medium text-chat-fg",
3396
+ children: label ?? labels.effort
3397
+ }),
3398
+ current.recommended && /* @__PURE__ */ jsx(Badge, { children: labels.recommended }),
3399
+ current.warning && /* @__PURE__ */ jsx(Badge, {
3400
+ tone: "warning",
3401
+ children: current.warning
3402
+ }),
3403
+ /* @__PURE__ */ jsx("output", {
3404
+ htmlFor: id,
3405
+ className: "text-(length:--chat-ui-font-size) text-chat-fg",
3406
+ children: current.label
3407
+ })
3408
+ ]
3409
+ }),
3410
+ current.description && /* @__PURE__ */ jsx("p", {
3411
+ className: "text-(length:--chat-ui-font-size-sm) leading-4 text-chat-muted",
3412
+ children: current.description
3413
+ }),
3414
+ /* @__PURE__ */ jsxs("div", {
3415
+ className: "relative mx-2 mt-2 h-6",
3416
+ children: [
3417
+ /* @__PURE__ */ jsx("div", { className: "absolute top-1/2 right-0 left-0 h-1 -translate-y-1/2 rounded-full bg-chat-border-strong" }),
3418
+ /* @__PURE__ */ jsx("div", {
3419
+ className: "absolute top-1/2 left-0 h-1 -translate-y-1/2 rounded-full bg-chat-accent",
3420
+ style: { width: at(index) }
3421
+ }),
3422
+ efforts.map((e, i) => /* @__PURE__ */ jsx("span", {
3423
+ "aria-hidden": true,
3424
+ className: clsx("absolute top-1/2 size-1.5 -translate-1/2 rounded-full", i <= index ? "bg-chat-accent-fg/70" : "bg-chat-subtle"),
3425
+ style: { left: at(i) }
3426
+ }, e.id)),
3427
+ /* @__PURE__ */ jsx("span", {
3428
+ "aria-hidden": true,
3429
+ className: "pointer-events-none absolute top-1/2 size-4 -translate-1/2 rounded-full border-2 border-chat-accent bg-chat-fg shadow-sm peer-focus-visible:ring-2",
3430
+ style: { left: at(index) }
3431
+ }),
3432
+ /* @__PURE__ */ jsx("input", {
3433
+ id,
3434
+ type: "range",
3435
+ min: 0,
3436
+ max: efforts.length - 1,
3437
+ step: 1,
3438
+ value: index,
3439
+ "aria-valuetext": current.label,
3440
+ onChange: (e) => {
3441
+ const next = efforts[Number(e.target.value)];
3442
+ if (next && next.id !== value) onChange(next.id);
3443
+ },
3444
+ onKeyDown: (e) => {
3445
+ const step = e.key === "ArrowRight" || e.key === "ArrowUp" ? 1 : e.key === "ArrowLeft" || e.key === "ArrowDown" ? -1 : 0;
3446
+ const next = step ? efforts[index + step] : void 0;
3447
+ if (next) {
3448
+ e.preventDefault();
3449
+ onChange(next.id);
3450
+ }
3451
+ },
3452
+ className: "absolute -inset-x-2 inset-y-0 w-[calc(100%+1rem)] cursor-pointer rounded-full opacity-0 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-chat-focus [&::-webkit-slider-thumb]:opacity-0"
3453
+ })
3454
+ ]
3455
+ }),
3456
+ /* @__PURE__ */ jsxs("div", {
3457
+ className: "flex justify-between text-(length:--chat-ui-font-size-sm) leading-4 text-chat-subtle",
3458
+ children: [/* @__PURE__ */ jsx("span", { children: labels.effortFaster }), /* @__PURE__ */ jsx("span", { children: labels.effortSmarter })]
3459
+ })
3460
+ ]
3461
+ });
3462
+ }
3463
+ /** A composer pill ("◎ Medium") that opens the effort slider above it, like chatgpt.com's Think button. */
3464
+ function EffortButton({ icon, children, className, ...slider }) {
3465
+ const current = slider.efforts.find((e) => e.id === slider.value) ?? slider.efforts[0];
3466
+ const labels = useLabels();
3467
+ return /* @__PURE__ */ jsxs(DialogTrigger, { children: [/* @__PURE__ */ jsxs(Button, {
3468
+ variant: "ghost",
3469
+ size: "sm",
3470
+ className: clsx("text-chat-muted data-hovered:text-chat-fg", className),
3471
+ "aria-label": `${labels.effort}: ${current?.label ?? ""}`,
3472
+ children: [icon, children ?? current?.label]
3473
+ }), /* @__PURE__ */ jsx(Popover, {
3474
+ placement: "top end",
3475
+ className: "w-72 p-3",
3476
+ children: /* @__PURE__ */ jsx(Dialog, {
3477
+ "aria-label": labels.effort,
3478
+ className: "outline-none",
3479
+ children: /* @__PURE__ */ jsx(EffortSlider, { ...slider })
3480
+ })
3481
+ })] });
3482
+ }
3254
3483
  // src/components/model-picker.tsx
3255
3484
  /** "Opus 5.5 High ⌄": the models, an Effort submenu for models that have levels, and More models. */
3256
3485
  function ModelPicker({ models, model, onModelChange, effort, onEffortChange, effortNote, footnote, className }) {
@@ -3371,6 +3600,7 @@ function MessageList({ messages, onRetry, onEdit, onFeedback, onReadAloud, actio
3371
3600
  const content = useRef(null);
3372
3601
  const stuck = useRef(start === "bottom");
3373
3602
  const labels = useLabels();
3603
+ const later = useMemo(() => laterReplacements(messages), [messages]);
3374
3604
  const [atBottom, setAtBottom] = useState(true);
3375
3605
  const toBottom = useCallback((behavior = "auto") => {
3376
3606
  const el = scroller.current;
@@ -3420,22 +3650,25 @@ function MessageList({ messages, onRetry, onEdit, onFeedback, onReadAloud, actio
3420
3650
  role: "log",
3421
3651
  "aria-label": labels.conversation,
3422
3652
  className: "mx-auto flex w-full max-w-[calc(var(--chat-content-width)+2*var(--chat-gutter,var(--chat-content-padding)))] flex-1 flex-col gap-6 px-[var(--chat-gutter,var(--chat-content-padding))] pt-6 pb-4",
3423
- children: [header, messages.map((m) => m.role === "user" ? /* @__PURE__ */ jsx(UserMessage, {
3424
- message: m,
3425
- ...onEdit && { onEdit: () => onEdit(m.id) },
3426
- ...actions?.extra && { actions: actions.extra(m) },
3427
- ...actions?.copy === false && { copy: false },
3428
- ...actions?.time === false && { time: false }
3429
- }, m.id) : /* @__PURE__ */ jsx(AssistantMessage, {
3430
- message: m,
3431
- ...onRetry && { onRetry: () => onRetry(m.id) },
3432
- ...onFeedback && { onFeedback: (f) => onFeedback(m.id, f) },
3433
- ...onReadAloud && { onReadAloud: () => onReadAloud(m.id) },
3434
- ...actions?.extra && { actions: actions.extra(m) },
3435
- ...actions?.copy === false && { copy: false },
3436
- ...actions?.time === false && { time: false },
3437
- ...renderPart && { renderPart }
3438
- }, m.id))]
3653
+ children: [header, /* @__PURE__ */ jsx(LaterReplacements, {
3654
+ value: later,
3655
+ children: messages.map((m) => m.role === "user" ? /* @__PURE__ */ jsx(UserMessage, {
3656
+ message: m,
3657
+ ...onEdit && { onEdit: () => onEdit(m.id) },
3658
+ ...actions?.extra && { actions: actions.extra(m) },
3659
+ ...actions?.copy === false && { copy: false },
3660
+ ...actions?.time === false && { time: false }
3661
+ }, m.id) : /* @__PURE__ */ jsx(AssistantMessage, {
3662
+ message: m,
3663
+ ...onRetry && { onRetry: () => onRetry(m.id) },
3664
+ ...onFeedback && { onFeedback: (f) => onFeedback(m.id, f) },
3665
+ ...onReadAloud && { onReadAloud: () => onReadAloud(m.id) },
3666
+ ...actions?.extra && { actions: actions.extra(m) },
3667
+ ...actions?.copy === false && { copy: false },
3668
+ ...actions?.time === false && { time: false },
3669
+ ...renderPart && { renderPart }
3670
+ }, m.id))
3671
+ })]
3439
3672
  }), footer && /* @__PURE__ */ jsx("div", {
3440
3673
  className: "sticky bottom-0",
3441
3674
  children: footer
@@ -3732,7 +3965,7 @@ const densityVars = {
3732
3965
  }
3733
3966
  };
3734
3967
  /** A whole chat: header, the conversation, the docked composer. An empty chat shows `emptyState`. */
3735
- function ChatView({ messages, busy = false, onSend, onStop, approvals, onApprove, onRetry, onEdit, onFeedback, onReadAloud, messageActions, renderPart, widgets, header, composer, emptyState, disclaimer, density = "full", contentWidth, contentPadding, className }) {
3968
+ function ChatView({ messages, busy = false, onSend, onStop, approvals, onApprove, onRetry, onEdit, onFeedback, onReadAloud, messageActions, renderPart, widgets, header, composer, emptyState, disclaimer, renderComposer, density = "full", contentWidth, contentPadding, className }) {
3736
3969
  const style = {
3737
3970
  ...densityVars[density],
3738
3971
  ...contentWidth && { "--chat-content-width": contentWidth },
@@ -3747,14 +3980,17 @@ function ChatView({ messages, busy = false, onSend, onStop, approvals, onApprove
3747
3980
  const [body, short] = useShortPane();
3748
3981
  const labels = useLabels();
3749
3982
  const pending = onApprove && approvals?.[0];
3750
- const box = (variant) => /* @__PURE__ */ jsx(Composer, {
3751
- variant: density === "inline" || short && !empty ? "compact" : variant,
3752
- busy,
3753
- onSubmit: onSend,
3754
- ...onStop && { onStop },
3755
- ...composer,
3756
- ...pending && { placeholder: labels.replyDirectly }
3757
- });
3983
+ const box = (variant) => {
3984
+ const element = /* @__PURE__ */ jsx(Composer, {
3985
+ variant: density === "inline" || short && !empty ? "compact" : variant,
3986
+ busy,
3987
+ onSubmit: onSend,
3988
+ ...onStop && { onStop },
3989
+ ...composer,
3990
+ ...pending && { placeholder: labels.replyDirectly }
3991
+ });
3992
+ return renderComposer ? renderComposer(element, variant === "centered" ? "empty" : "docked") : element;
3993
+ };
3758
3994
  return /* @__PURE__ */ jsxs("div", {
3759
3995
  "data-chat-density": density,
3760
3996
  style,
@@ -3822,7 +4058,7 @@ function EmptyState({ greeting, icon, children, footer }) {
3822
4058
  /* @__PURE__ */ jsxs("h1", {
3823
4059
  className: "chat-empty-greeting flex items-center justify-center gap-3 text-center font-chat-serif text-[clamp(1.5rem,6cqw,2.5rem)] font-normal text-chat-fg",
3824
4060
  children: [icon && /* @__PURE__ */ jsx("span", {
3825
- className: "flex shrink-0 text-chat-accent [&_svg]:size-[1em]",
4061
+ className: "flex shrink-0 text-[var(--chat-empty-icon,var(--chat-accent))] [&_svg]:size-[1em]",
3826
4062
  children: icon
3827
4063
  }), greeting]
3828
4064
  }),
@@ -3943,17 +4179,27 @@ function SidebarToggle({ drawerOnly = false, className }) {
3943
4179
  }
3944
4180
  // src/views/sidebar.tsx
3945
4181
  /** The claude.ai sidebar frame. Collapses to an icon rail inside `AppShell`; a drawer on phones. */
3946
- function Sidebar({ brand, headerActions, nav, children, footer, className }) {
4182
+ function Sidebar({ brand, brandIcon, headerActions, nav, children, footer, className }) {
3947
4183
  const { collapsed } = useSidebar();
3948
4184
  return /* @__PURE__ */ jsxs("div", {
3949
4185
  className: clsx("flex h-full flex-col font-chat-sans", collapsed ? "w-13" : "w-full", className),
3950
4186
  children: [
4187
+ collapsed && brandIcon && /* @__PURE__ */ jsx("div", {
4188
+ className: "flex h-12 shrink-0 items-center justify-center [&_svg]:size-5",
4189
+ children: brandIcon
4190
+ }),
3951
4191
  /* @__PURE__ */ jsxs("div", {
3952
4192
  className: clsx("flex h-12 shrink-0 items-center gap-1 px-2", collapsed && "justify-center"),
3953
4193
  children: [
3954
- !collapsed && /* @__PURE__ */ jsx("div", {
3955
- className: "min-w-0 flex-1 truncate px-1.5 font-chat-serif text-lg text-chat-fg",
3956
- children: brand
4194
+ !collapsed && /* @__PURE__ */ jsxs("div", {
4195
+ className: "flex min-w-0 flex-1 items-center gap-2 px-1.5 font-chat-serif text-lg text-chat-fg",
4196
+ children: [brandIcon && /* @__PURE__ */ jsx("span", {
4197
+ className: "flex shrink-0 [&_svg]:size-5",
4198
+ children: brandIcon
4199
+ }), /* @__PURE__ */ jsx("span", {
4200
+ className: "min-w-0 truncate",
4201
+ children: brand
4202
+ })]
3957
4203
  }),
3958
4204
  !collapsed && headerActions,
3959
4205
  /* @__PURE__ */ jsx(SidebarToggle, {})
@@ -4053,4 +4299,4 @@ function SidebarUser({ name, detail, avatarUrl, menu }) {
4053
4299
  children: menu
4054
4300
  })] });
4055
4301
  }
4056
- export { ActivityGroup, AgentProvider, AgentRegistry, AppShell, ApprovalCard, AssistantMessage, AttachButton, AttachmentCard, AttachmentRow, Badge, Button, Caret, ChatHeader, ChatList, ChatNotice, ChatTheme, ChatTitleMenu, ChatTranscript, ChatView, CodeBlock, Composer, ContextMenu, ContextPanel, DRAWER_QUERY, Disclosure, Drawer, EmptyState, IconButton, Markdown, MarkdownLinksProvider, Menu, MenuItem, MenuNote, MenuSearch, MenuSection, MenuSeparator, MenuTrigger, MessageList, Modal, ModelPicker, Panel, PastedTextDialog, Popover, SegmentedControl, Sidebar, SidebarItem, SidebarToggle, SidebarUser, SubmenuTrigger, TokenCounter, ToolViewsProvider, Tooltip, UiComponentsProvider, UiMarkupBlock, UiTree, UserMessage, WIDGET_MAX_HEIGHT, WidgetFrame, WidgetHostProvider, WidgetPartView, activityHeading, activityLabel, commandTool, defaultIcons, defaultLabels, defaultToolViews, formatDuration, formatRelativeTime, formatTokens, htmlComponent, intersectAllow, memoryTool, messageText, openInNewTab, readTreeInput, readVisualizeInput, sseTransport, uiMarkupToolView, uiTreeToolView, useAgentContext, useAgentRegistry, useAgentSnapshot, useAgentTool, useAttachments, useChat, useChatTool, useCountUp, useIcons, useLabels, useMediaQuery, useSidebar, useSmoothText, useToolView, useToolViews, useUiAction, useUiMarkupLanguage, useUiState, useWidgetHost, userMessage, webFetchTool, webSearchTool, widgetCsp, widgetToolView };
4302
+ export { ActivityGroup, AgentProvider, AgentRegistry, AppShell, ApprovalCard, AssistantMessage, AttachButton, AttachmentCard, AttachmentRow, Badge, Button, Caret, ChatHeader, ChatList, ChatNotice, ChatTheme, ChatTitleMenu, ChatTranscript, ChatView, CodeBlock, Composer, ContextMenu, ContextPanel, CopyButton, DRAWER_QUERY, Disclosure, Drawer, EffortButton, EffortSlider, EmptyState, IconButton, Markdown, MarkdownLinksProvider, Menu, MenuItem, MenuNote, MenuSearch, MenuSection, MenuSeparator, MenuTrigger, MessageList, Modal, ModelPicker, Panel, PastedTextDialog, Popover, SegmentedControl, Sidebar, SidebarItem, SidebarToggle, SidebarUser, SubmenuTrigger, TokenCounter, ToolViewsProvider, Tooltip, UiComponentsProvider, UiMarkupBlock, UiTree, UserMessage, WIDGET_MAX_HEIGHT, WidgetFrame, WidgetHostProvider, WidgetPartView, activityHeading, activityLabel, commandTool, defaultIcons, defaultLabels, defaultToolViews, formatDuration, formatRelativeTime, formatTokens, htmlComponent, intersectAllow, memoryTool, messageText, openInNewTab, readTreeInput, readVisualizeInput, sseTransport, uiMarkupToolView, uiTreeToolView, useAgentContext, useAgentRegistry, useAgentSnapshot, useAgentTool, useAttachments, useChat, useChatTool, useCountUp, useIcons, useLabels, useMediaQuery, useSidebar, useSmoothText, useToolView, useToolViews, useUiAction, useUiMarkupLanguage, useUiState, useWidgetHost, userMessage, webFetchTool, webSearchTool, widgetCsp, widgetToolView };