agentic-ui-kit 0.4.0 → 0.5.1
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/README.md +6 -0
- package/dist/core.js +11 -4
- package/dist/index.js +510 -218
- package/dist/{ui-markup-DtMYer7I.js → src-Ce7xTfGU.js} +73 -9
- package/dist/styles.css +1 -1
- package/dist/types/core/src/index.d.ts +2 -0
- package/dist/types/core/src/replaces.d.ts +23 -0
- package/dist/types/core/src/types.d.ts +2 -0
- package/dist/types/core/src/ui-markup.d.ts +6 -0
- package/dist/types/core/src/ui-tree.d.ts +6 -0
- package/dist/types/core/src/visualize.d.ts +7 -1
- package/dist/types/core/src/widget-loader.d.ts +19 -0
- package/dist/types/react/src/components/effort-slider.d.ts +24 -0
- package/dist/types/react/src/components/replacements.d.ts +29 -0
- package/dist/types/react/src/components/widget-frame.d.ts +7 -2
- package/dist/types/react/src/index.d.ts +3 -0
- package/dist/types/react/src/primitives/code-block.d.ts +3 -1
- package/dist/types/react/src/primitives/copy-button.d.ts +5 -0
- package/dist/types/react/src/primitives/icons.d.ts +7 -0
- package/dist/types/react/src/primitives/labels.d.ts +7 -0
- package/dist/types/react/src/views/chat-view.d.ts +10 -2
- package/dist/types/react/src/views/sidebar.d.ts +3 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as awaitingToolCalls, C as widgetLoaderMessages, D as toBlocks, F as settleToolCall, L as salvageJson, O as activityTokens, T as readEventStream, _ as replacedId, b as resolveReplaces, d as isStateRef, f as matchesSchema, g as uiActionSchema, i as parseUiMarkup, k as applyEvent } from "./src-Ce7xTfGU.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__ */
|
|
575
|
-
className: "
|
|
576
|
-
children:
|
|
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,112 @@ 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
|
+
const titleOf = (p) => {
|
|
1286
|
+
const t = (p.input ?? p.partialInput)?.title;
|
|
1287
|
+
return typeof t === "string" && t ? t : void 0;
|
|
1288
|
+
};
|
|
1289
|
+
/** Ids and titles of the calls seen so far, to resolve `replaces` (an id, or a title as a fallback). */
|
|
1290
|
+
function seen() {
|
|
1291
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1292
|
+
const titles = /* @__PURE__ */ new Map();
|
|
1293
|
+
return {
|
|
1294
|
+
resolve: (call) => {
|
|
1295
|
+
const r = replaces(call);
|
|
1296
|
+
return r === void 0 ? void 0 : resolveReplaces(r, ids, titles);
|
|
1297
|
+
},
|
|
1298
|
+
add: (call) => {
|
|
1299
|
+
ids.add(call.id);
|
|
1300
|
+
const t = titleOf(call);
|
|
1301
|
+
if (t) titles.set(t, call.id);
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
/** One reply's chains: each call's first call (where the chain is drawn) and each first call's chain, in order. */
|
|
1306
|
+
function chainsOf(message) {
|
|
1307
|
+
const first = /* @__PURE__ */ new Map();
|
|
1308
|
+
const chains = /* @__PURE__ */ new Map();
|
|
1309
|
+
const known = seen();
|
|
1310
|
+
for (const call of callsOf(message)) {
|
|
1311
|
+
const r = known.resolve(call);
|
|
1312
|
+
known.add(call);
|
|
1313
|
+
const root = r !== void 0 && first.has(r) ? first.get(r) : call.id;
|
|
1314
|
+
first.set(call.id, root);
|
|
1315
|
+
chains.set(root, [...chains.get(root) ?? [], call]);
|
|
1316
|
+
}
|
|
1317
|
+
return {
|
|
1318
|
+
first,
|
|
1319
|
+
chains
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
/** Calls replaced by a finished call in a later reply: replaced id → the newest such call. */
|
|
1323
|
+
function laterReplacements(messages) {
|
|
1324
|
+
const later = /* @__PURE__ */ new Map();
|
|
1325
|
+
const known = seen();
|
|
1326
|
+
for (const m of messages) {
|
|
1327
|
+
if (m.role !== "assistant") continue;
|
|
1328
|
+
const calls = callsOf(m);
|
|
1329
|
+
const own = new Set(calls.map((c) => c.id));
|
|
1330
|
+
for (const call of calls) {
|
|
1331
|
+
const r = known.resolve(call);
|
|
1332
|
+
if (r !== void 0 && !own.has(r) && isReady(call)) later.set(r, call);
|
|
1333
|
+
}
|
|
1334
|
+
calls.forEach(known.add);
|
|
1335
|
+
}
|
|
1336
|
+
return later;
|
|
1337
|
+
}
|
|
1338
|
+
const LaterContext = createContext(/* @__PURE__ */ new Map());
|
|
1339
|
+
function LaterReplacements({ value, children }) {
|
|
1340
|
+
return /* @__PURE__ */ jsx(LaterContext.Provider, {
|
|
1341
|
+
value,
|
|
1342
|
+
children
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
/** The call in a later reply that replaces any call of this chain, if there is one. */
|
|
1346
|
+
function useLaterReplacement(chain) {
|
|
1347
|
+
const later = useContext(LaterContext);
|
|
1348
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
1349
|
+
const by = later.get(chain[i].id);
|
|
1350
|
+
if (by) return by;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
/** The DOM id of a drawn tool call, for the "Updated below" link. */
|
|
1354
|
+
const toolAnchor = (id) => `chat-tool-${id}`;
|
|
1355
|
+
/** Draws a chain: the newest finished call (dimmed while a newer one streams), or a link to its later update. */
|
|
1356
|
+
function ToolChain({ chain, draw }) {
|
|
1357
|
+
const labels = useLabels();
|
|
1358
|
+
const later = useLaterReplacement(chain);
|
|
1359
|
+
const shown = chain.findLast(isReady) ?? chain[0];
|
|
1360
|
+
const updating = chain.indexOf(shown) < chain.length - 1 && chain.at(-1).status !== "error";
|
|
1361
|
+
if (later) return /* @__PURE__ */ jsx(Button, {
|
|
1362
|
+
variant: "ghost",
|
|
1363
|
+
size: "sm",
|
|
1364
|
+
className: "self-start text-chat-muted data-hovered:text-chat-fg",
|
|
1365
|
+
onPress: () => document.getElementById(toolAnchor(later.id))?.scrollIntoView({
|
|
1366
|
+
behavior: "smooth",
|
|
1367
|
+
block: "center"
|
|
1368
|
+
}),
|
|
1369
|
+
children: labels.visualUpdated
|
|
1370
|
+
});
|
|
1371
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1372
|
+
id: toolAnchor(chain[0].id),
|
|
1373
|
+
"aria-busy": updating || void 0,
|
|
1374
|
+
className: updating ? "opacity-60 transition-opacity" : "transition-opacity",
|
|
1375
|
+
children: [/* @__PURE__ */ jsx("div", { children: draw(shown) }, shown.id), updating && /* @__PURE__ */ jsx("p", {
|
|
1376
|
+
className: "chat-shimmer mt-1 font-chat-sans text-(length:--chat-ui-font-size-sm) text-chat-muted",
|
|
1377
|
+
children: labels.visualUpdating
|
|
1378
|
+
})]
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1250
1381
|
// src/components/widget-frame.tsx
|
|
1251
1382
|
const WidgetHostContext = createContext({});
|
|
1252
1383
|
/** Sets what widgets below may do. ChatView and ChatTranscript set it from their `widgets` prop. */
|
|
@@ -1362,6 +1493,8 @@ const BOOT = `(() => {
|
|
|
1362
1493
|
new ResizeObserver(() => {
|
|
1363
1494
|
const h = Math.ceil(root.getBoundingClientRect().height);
|
|
1364
1495
|
if (h !== last) { last = h; post("ui/notifications/size-changed", { height: h }); }
|
|
1496
|
+
// The frame grows to fit, a moment behind; only content past the cap gets a scrollbar.
|
|
1497
|
+
document.documentElement.style.overflowY = h > ${WIDGET_MAX_HEIGHT} ? "auto" : "hidden";
|
|
1365
1498
|
}).observe(root);
|
|
1366
1499
|
const seen = new Set();
|
|
1367
1500
|
const report = (text) => { if (!seen.has(text) && seen.size < 10) { seen.add(text); post("notifications/message", { level: "error", logger: "widget", data: text }); } };
|
|
@@ -1374,6 +1507,7 @@ function fragmentDoc(csp, theme) {
|
|
|
1374
1507
|
<meta http-equiv="Content-Security-Policy" content="${csp}">
|
|
1375
1508
|
<style>
|
|
1376
1509
|
:root { ${Object.entries(theme.vars).map(([k, v]) => `${k}: ${v};`).join(" ")} color-scheme: ${theme.scheme}; }
|
|
1510
|
+
html { overflow: hidden; }
|
|
1377
1511
|
html, body { margin: 0; background: transparent; color: var(--chat-fg); font-family: var(--chat-font-sans);
|
|
1378
1512
|
font-size: var(--chat-ui-font-size); line-height: 1.5; }
|
|
1379
1513
|
#widget { display: flow-root; }
|
|
@@ -1415,8 +1549,10 @@ function watchTheme(el, onChange) {
|
|
|
1415
1549
|
media?.removeEventListener?.("change", onChange);
|
|
1416
1550
|
};
|
|
1417
1551
|
}
|
|
1418
|
-
function WidgetFrame({ html, document: isDocument = false, streaming = false, title, loadingMessages, allow, csp: request, onPrompt, onOpenLink, onCallTool, onError, toolInput, toolResult, cancelled, className }) {
|
|
1552
|
+
function WidgetFrame({ html, document: isDocument = false, streaming = false, title, loadingMessages, allow, csp: request, onPrompt, onOpenLink, onCallTool, onError, toolInput, toolResult, cancelled, frameUrl: ownFrameUrl, className }) {
|
|
1419
1553
|
const labels = useLabels();
|
|
1554
|
+
const hostFrameUrl = useWidgetHost().frameUrl;
|
|
1555
|
+
const frameUrl = ownFrameUrl ?? hostFrameUrl;
|
|
1420
1556
|
const frame = useRef(null);
|
|
1421
1557
|
const host = useRef(null);
|
|
1422
1558
|
const [ready, setReady] = useState(false);
|
|
@@ -1438,7 +1574,8 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
|
|
|
1438
1574
|
onCallTool,
|
|
1439
1575
|
onError,
|
|
1440
1576
|
policy,
|
|
1441
|
-
effective
|
|
1577
|
+
effective,
|
|
1578
|
+
doc
|
|
1442
1579
|
});
|
|
1443
1580
|
latest.current = {
|
|
1444
1581
|
onPrompt,
|
|
@@ -1446,7 +1583,16 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
|
|
|
1446
1583
|
onCallTool,
|
|
1447
1584
|
onError,
|
|
1448
1585
|
policy,
|
|
1449
|
-
effective
|
|
1586
|
+
effective,
|
|
1587
|
+
doc
|
|
1588
|
+
};
|
|
1589
|
+
const generation = useRef({
|
|
1590
|
+
doc,
|
|
1591
|
+
n: 0
|
|
1592
|
+
});
|
|
1593
|
+
if (generation.current.doc !== doc) generation.current = {
|
|
1594
|
+
doc,
|
|
1595
|
+
n: generation.current.n + 1
|
|
1450
1596
|
};
|
|
1451
1597
|
const send = (message) => frame.current?.contentWindow?.postMessage({
|
|
1452
1598
|
jsonrpc: "2.0",
|
|
@@ -1496,6 +1642,14 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
|
|
|
1496
1642
|
});
|
|
1497
1643
|
const onMessage = async (e) => {
|
|
1498
1644
|
if (!frame.current || e.source !== frame.current.contentWindow) return;
|
|
1645
|
+
if (e.data?.type === widgetLoaderMessages.ready) {
|
|
1646
|
+
const html = latest.current.doc;
|
|
1647
|
+
if (html) frame.current.contentWindow?.postMessage({
|
|
1648
|
+
type: widgetLoaderMessages.document,
|
|
1649
|
+
html
|
|
1650
|
+
}, "*");
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1499
1653
|
const m = e.data ?? {};
|
|
1500
1654
|
if (typeof m !== "object" || typeof m.method !== "string") return;
|
|
1501
1655
|
const { onPrompt, onOpenLink, onCallTool, effective } = latest.current;
|
|
@@ -1614,13 +1768,13 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
|
|
|
1614
1768
|
ref: frame,
|
|
1615
1769
|
title: title ?? labels.widget,
|
|
1616
1770
|
sandbox: "allow-scripts",
|
|
1617
|
-
srcDoc: doc,
|
|
1771
|
+
...frameUrl ? { src: frameUrl } : { srcDoc: doc },
|
|
1618
1772
|
style: {
|
|
1619
1773
|
height: height || (streaming ? 120 : 0),
|
|
1620
1774
|
transition: "height 150ms ease-out"
|
|
1621
1775
|
},
|
|
1622
1776
|
className: "block w-full border-0 bg-transparent"
|
|
1623
|
-
})]
|
|
1777
|
+
}, frameUrl ? generation.current.n : "srcdoc")]
|
|
1624
1778
|
});
|
|
1625
1779
|
}
|
|
1626
1780
|
/** Pulls the host's abilities from context into frame props. */
|
|
@@ -2388,10 +2542,14 @@ function ActivityGroup({ parts, defaultExpanded = false, className }) {
|
|
|
2388
2542
|
}) : /* @__PURE__ */ jsx(Disclosure, {
|
|
2389
2543
|
isExpanded: expanded,
|
|
2390
2544
|
onExpandedChange: setExpanded,
|
|
2391
|
-
summary: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2392
|
-
live,
|
|
2393
|
-
|
|
2394
|
-
|
|
2545
|
+
summary: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2546
|
+
/* @__PURE__ */ jsx(Lead, { live }),
|
|
2547
|
+
/* @__PURE__ */ jsx(Live, {
|
|
2548
|
+
live,
|
|
2549
|
+
children: activityHeading(parts, views, labels)
|
|
2550
|
+
}),
|
|
2551
|
+
after
|
|
2552
|
+
] }),
|
|
2395
2553
|
summaryClassName: "text-(length:--chat-activity-font-size)",
|
|
2396
2554
|
panelClassName: "pt-3",
|
|
2397
2555
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -2401,6 +2559,17 @@ function ActivityGroup({ parts, defaultExpanded = false, className }) {
|
|
|
2401
2559
|
})
|
|
2402
2560
|
});
|
|
2403
2561
|
}
|
|
2562
|
+
/** The host's mark left of the summary (`icons.activity` while working, `icons.activityDone` after), if set. */
|
|
2563
|
+
function Lead({ live }) {
|
|
2564
|
+
const icons = useIcons();
|
|
2565
|
+
const icon = live ? icons.activity : icons.activityDone;
|
|
2566
|
+
if (!icon) return null;
|
|
2567
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2568
|
+
"aria-hidden": true,
|
|
2569
|
+
className: "chat-activity-icon mr-1.5 inline-flex size-[1.15em] shrink-0 items-center self-center [&_svg]:size-full",
|
|
2570
|
+
children: icon
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2404
2573
|
const SHIMMER_MS = 2e3;
|
|
2405
2574
|
/**
|
|
2406
2575
|
* Sweeps a highlight across a label while its step is running. The animation starts at the phase of a
|
|
@@ -2425,10 +2594,14 @@ function SingleStep({ part, live, after, expanded, onExpandedChange }) {
|
|
|
2425
2594
|
return /* @__PURE__ */ jsx(Disclosure, {
|
|
2426
2595
|
isExpanded: expanded,
|
|
2427
2596
|
onExpandedChange,
|
|
2428
|
-
summary: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2429
|
-
live,
|
|
2430
|
-
|
|
2431
|
-
|
|
2597
|
+
summary: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2598
|
+
/* @__PURE__ */ jsx(Lead, { live }),
|
|
2599
|
+
/* @__PURE__ */ jsx(Live, {
|
|
2600
|
+
live,
|
|
2601
|
+
children: summary
|
|
2602
|
+
}),
|
|
2603
|
+
after
|
|
2604
|
+
] }),
|
|
2432
2605
|
summaryClassName: "text-(length:--chat-activity-font-size)",
|
|
2433
2606
|
panelClassName: "pt-3",
|
|
2434
2607
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -2729,21 +2902,6 @@ function formatRelativeTime(iso, now = Date.now(), labels = defaultLabels) {
|
|
|
2729
2902
|
function messageText(message) {
|
|
2730
2903
|
return message.parts.map((p) => p.type === "text" ? p.text : p.type === "custom" ? p.text ?? "" : "").filter(Boolean).join("\n\n");
|
|
2731
2904
|
}
|
|
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
2905
|
const timeClass = "px-1 font-chat-sans text-(length:--chat-ui-font-size-sm) leading-4 text-chat-subtle";
|
|
2748
2906
|
/** The user's turn: a bubble on the right, with time, retry, edit and copy under it on hover. */
|
|
2749
2907
|
function UserMessage({ message, onRetry, onEdit, actions, copy = true, time = true, className }) {
|
|
@@ -2838,6 +2996,7 @@ function AssistantMessage({ message, onRetry, onFeedback, onReadAloud, renderPar
|
|
|
2838
2996
|
const streaming = message.status === "streaming";
|
|
2839
2997
|
const views = useToolViews();
|
|
2840
2998
|
const blocks = toBlocks(message.parts, (part) => views[part.name]?.Block !== void 0);
|
|
2999
|
+
const { first, chains } = chainsOf(message);
|
|
2841
3000
|
return /* @__PURE__ */ jsxs("div", {
|
|
2842
3001
|
className: clsx("group/message flex flex-col gap-4", className),
|
|
2843
3002
|
children: [
|
|
@@ -2852,8 +3011,14 @@ function AssistantMessage({ message, onRetry, onFeedback, onReadAloud, renderPar
|
|
|
2852
3011
|
})] }, block.id);
|
|
2853
3012
|
}
|
|
2854
3013
|
if (block.kind === "tool") {
|
|
2855
|
-
|
|
2856
|
-
return /* @__PURE__ */ jsx(
|
|
3014
|
+
if (first.get(block.part.id) !== block.part.id) return null;
|
|
3015
|
+
return /* @__PURE__ */ jsx(ToolChain, {
|
|
3016
|
+
chain: chains.get(block.part.id) ?? [block.part],
|
|
3017
|
+
draw: (part) => {
|
|
3018
|
+
const ToolBlock = (views[part.name] ?? views[block.part.name]).Block;
|
|
3019
|
+
return /* @__PURE__ */ jsx(ToolBlock, { part });
|
|
3020
|
+
}
|
|
3021
|
+
}, block.id);
|
|
2857
3022
|
}
|
|
2858
3023
|
const part = block.part;
|
|
2859
3024
|
if (part.type === "text") return /* @__PURE__ */ jsx(Markdown, {
|
|
@@ -3251,6 +3416,115 @@ function AttachButton({ children, label, icon, className, menuClassName }) {
|
|
|
3251
3416
|
children
|
|
3252
3417
|
})] });
|
|
3253
3418
|
}
|
|
3419
|
+
// src/components/effort-slider.tsx
|
|
3420
|
+
/**
|
|
3421
|
+
* Effort as a slider with one stop per level, lowest (faster) on the left: title and the current level on
|
|
3422
|
+
* top, the level's description under them, "Faster" and "Smarter" under the track. Arrow keys move a stop.
|
|
3423
|
+
*/
|
|
3424
|
+
function EffortSlider({ efforts, value, onChange, label, className }) {
|
|
3425
|
+
const labels = useLabels();
|
|
3426
|
+
const id = useId();
|
|
3427
|
+
const index = Math.max(0, efforts.findIndex((e) => e.id === value));
|
|
3428
|
+
const current = efforts[index];
|
|
3429
|
+
const last = Math.max(1, efforts.length - 1);
|
|
3430
|
+
const at = (i) => `${i / last * 100}%`;
|
|
3431
|
+
if (!current) return null;
|
|
3432
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3433
|
+
className: clsx("flex w-full flex-col gap-1 font-chat-sans", className),
|
|
3434
|
+
children: [
|
|
3435
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3436
|
+
className: "flex items-center gap-2",
|
|
3437
|
+
children: [
|
|
3438
|
+
/* @__PURE__ */ jsx("label", {
|
|
3439
|
+
htmlFor: id,
|
|
3440
|
+
className: "flex-1 text-(length:--chat-ui-font-size) font-medium text-chat-fg",
|
|
3441
|
+
children: label ?? labels.effort
|
|
3442
|
+
}),
|
|
3443
|
+
current.recommended && /* @__PURE__ */ jsx(Badge, { children: labels.recommended }),
|
|
3444
|
+
current.warning && /* @__PURE__ */ jsx(Badge, {
|
|
3445
|
+
tone: "warning",
|
|
3446
|
+
children: current.warning
|
|
3447
|
+
}),
|
|
3448
|
+
/* @__PURE__ */ jsx("output", {
|
|
3449
|
+
htmlFor: id,
|
|
3450
|
+
className: "text-(length:--chat-ui-font-size) text-chat-fg",
|
|
3451
|
+
children: current.label
|
|
3452
|
+
})
|
|
3453
|
+
]
|
|
3454
|
+
}),
|
|
3455
|
+
current.description && /* @__PURE__ */ jsx("p", {
|
|
3456
|
+
className: "text-(length:--chat-ui-font-size-sm) leading-4 text-chat-muted",
|
|
3457
|
+
children: current.description
|
|
3458
|
+
}),
|
|
3459
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3460
|
+
className: "relative mx-2 mt-2 h-6",
|
|
3461
|
+
children: [
|
|
3462
|
+
/* @__PURE__ */ jsx("div", { className: "absolute top-1/2 right-0 left-0 h-1 -translate-y-1/2 rounded-full bg-chat-border-strong" }),
|
|
3463
|
+
/* @__PURE__ */ jsx("div", {
|
|
3464
|
+
className: "absolute top-1/2 left-0 h-1 -translate-y-1/2 rounded-full bg-chat-accent",
|
|
3465
|
+
style: { width: at(index) }
|
|
3466
|
+
}),
|
|
3467
|
+
efforts.map((e, i) => /* @__PURE__ */ jsx("span", {
|
|
3468
|
+
"aria-hidden": true,
|
|
3469
|
+
className: clsx("absolute top-1/2 size-1.5 -translate-1/2 rounded-full", i <= index ? "bg-chat-accent-fg/70" : "bg-chat-subtle"),
|
|
3470
|
+
style: { left: at(i) }
|
|
3471
|
+
}, e.id)),
|
|
3472
|
+
/* @__PURE__ */ jsx("span", {
|
|
3473
|
+
"aria-hidden": true,
|
|
3474
|
+
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",
|
|
3475
|
+
style: { left: at(index) }
|
|
3476
|
+
}),
|
|
3477
|
+
/* @__PURE__ */ jsx("input", {
|
|
3478
|
+
id,
|
|
3479
|
+
type: "range",
|
|
3480
|
+
min: 0,
|
|
3481
|
+
max: efforts.length - 1,
|
|
3482
|
+
step: 1,
|
|
3483
|
+
value: index,
|
|
3484
|
+
"aria-valuetext": current.label,
|
|
3485
|
+
onChange: (e) => {
|
|
3486
|
+
const next = efforts[Number(e.target.value)];
|
|
3487
|
+
if (next && next.id !== value) onChange(next.id);
|
|
3488
|
+
},
|
|
3489
|
+
onKeyDown: (e) => {
|
|
3490
|
+
const step = e.key === "ArrowRight" || e.key === "ArrowUp" ? 1 : e.key === "ArrowLeft" || e.key === "ArrowDown" ? -1 : 0;
|
|
3491
|
+
const next = step ? efforts[index + step] : void 0;
|
|
3492
|
+
if (next) {
|
|
3493
|
+
e.preventDefault();
|
|
3494
|
+
onChange(next.id);
|
|
3495
|
+
}
|
|
3496
|
+
},
|
|
3497
|
+
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"
|
|
3498
|
+
})
|
|
3499
|
+
]
|
|
3500
|
+
}),
|
|
3501
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3502
|
+
className: "flex justify-between text-(length:--chat-ui-font-size-sm) leading-4 text-chat-subtle",
|
|
3503
|
+
children: [/* @__PURE__ */ jsx("span", { children: labels.effortFaster }), /* @__PURE__ */ jsx("span", { children: labels.effortSmarter })]
|
|
3504
|
+
})
|
|
3505
|
+
]
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
/** A composer pill ("◎ Medium") that opens the effort slider above it, like chatgpt.com's Think button. */
|
|
3509
|
+
function EffortButton({ icon, children, className, ...slider }) {
|
|
3510
|
+
const current = slider.efforts.find((e) => e.id === slider.value) ?? slider.efforts[0];
|
|
3511
|
+
const labels = useLabels();
|
|
3512
|
+
return /* @__PURE__ */ jsxs(DialogTrigger, { children: [/* @__PURE__ */ jsxs(Button, {
|
|
3513
|
+
variant: "ghost",
|
|
3514
|
+
size: "sm",
|
|
3515
|
+
className: clsx("text-chat-muted data-hovered:text-chat-fg", className),
|
|
3516
|
+
"aria-label": `${labels.effort}: ${current?.label ?? ""}`,
|
|
3517
|
+
children: [icon, children ?? current?.label]
|
|
3518
|
+
}), /* @__PURE__ */ jsx(Popover, {
|
|
3519
|
+
placement: "top end",
|
|
3520
|
+
className: "w-72 p-3",
|
|
3521
|
+
children: /* @__PURE__ */ jsx(Dialog, {
|
|
3522
|
+
"aria-label": labels.effort,
|
|
3523
|
+
className: "outline-none",
|
|
3524
|
+
children: /* @__PURE__ */ jsx(EffortSlider, { ...slider })
|
|
3525
|
+
})
|
|
3526
|
+
})] });
|
|
3527
|
+
}
|
|
3254
3528
|
// src/components/model-picker.tsx
|
|
3255
3529
|
/** "Opus 5.5 High ⌄": the models, an Effort submenu for models that have levels, and More models. */
|
|
3256
3530
|
function ModelPicker({ models, model, onModelChange, effort, onEffortChange, effortNote, footnote, className }) {
|
|
@@ -3371,6 +3645,7 @@ function MessageList({ messages, onRetry, onEdit, onFeedback, onReadAloud, actio
|
|
|
3371
3645
|
const content = useRef(null);
|
|
3372
3646
|
const stuck = useRef(start === "bottom");
|
|
3373
3647
|
const labels = useLabels();
|
|
3648
|
+
const later = useMemo(() => laterReplacements(messages), [messages]);
|
|
3374
3649
|
const [atBottom, setAtBottom] = useState(true);
|
|
3375
3650
|
const toBottom = useCallback((behavior = "auto") => {
|
|
3376
3651
|
const el = scroller.current;
|
|
@@ -3420,22 +3695,25 @@ function MessageList({ messages, onRetry, onEdit, onFeedback, onReadAloud, actio
|
|
|
3420
3695
|
role: "log",
|
|
3421
3696
|
"aria-label": labels.conversation,
|
|
3422
3697
|
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,
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3698
|
+
children: [header, /* @__PURE__ */ jsx(LaterReplacements, {
|
|
3699
|
+
value: later,
|
|
3700
|
+
children: messages.map((m) => m.role === "user" ? /* @__PURE__ */ jsx(UserMessage, {
|
|
3701
|
+
message: m,
|
|
3702
|
+
...onEdit && { onEdit: () => onEdit(m.id) },
|
|
3703
|
+
...actions?.extra && { actions: actions.extra(m) },
|
|
3704
|
+
...actions?.copy === false && { copy: false },
|
|
3705
|
+
...actions?.time === false && { time: false }
|
|
3706
|
+
}, m.id) : /* @__PURE__ */ jsx(AssistantMessage, {
|
|
3707
|
+
message: m,
|
|
3708
|
+
...onRetry && { onRetry: () => onRetry(m.id) },
|
|
3709
|
+
...onFeedback && { onFeedback: (f) => onFeedback(m.id, f) },
|
|
3710
|
+
...onReadAloud && { onReadAloud: () => onReadAloud(m.id) },
|
|
3711
|
+
...actions?.extra && { actions: actions.extra(m) },
|
|
3712
|
+
...actions?.copy === false && { copy: false },
|
|
3713
|
+
...actions?.time === false && { time: false },
|
|
3714
|
+
...renderPart && { renderPart }
|
|
3715
|
+
}, m.id))
|
|
3716
|
+
})]
|
|
3439
3717
|
}), footer && /* @__PURE__ */ jsx("div", {
|
|
3440
3718
|
className: "sticky bottom-0",
|
|
3441
3719
|
children: footer
|
|
@@ -3732,7 +4010,7 @@ const densityVars = {
|
|
|
3732
4010
|
}
|
|
3733
4011
|
};
|
|
3734
4012
|
/** 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 }) {
|
|
4013
|
+
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
4014
|
const style = {
|
|
3737
4015
|
...densityVars[density],
|
|
3738
4016
|
...contentWidth && { "--chat-content-width": contentWidth },
|
|
@@ -3747,14 +4025,17 @@ function ChatView({ messages, busy = false, onSend, onStop, approvals, onApprove
|
|
|
3747
4025
|
const [body, short] = useShortPane();
|
|
3748
4026
|
const labels = useLabels();
|
|
3749
4027
|
const pending = onApprove && approvals?.[0];
|
|
3750
|
-
const box = (variant) =>
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
4028
|
+
const box = (variant) => {
|
|
4029
|
+
const element = /* @__PURE__ */ jsx(Composer, {
|
|
4030
|
+
variant: density === "inline" || short && !empty ? "compact" : variant,
|
|
4031
|
+
busy,
|
|
4032
|
+
onSubmit: onSend,
|
|
4033
|
+
...onStop && { onStop },
|
|
4034
|
+
...composer,
|
|
4035
|
+
...pending && { placeholder: labels.replyDirectly }
|
|
4036
|
+
});
|
|
4037
|
+
return renderComposer ? renderComposer(element, variant === "centered" ? "empty" : "docked") : element;
|
|
4038
|
+
};
|
|
3758
4039
|
return /* @__PURE__ */ jsxs("div", {
|
|
3759
4040
|
"data-chat-density": density,
|
|
3760
4041
|
style,
|
|
@@ -3822,7 +4103,7 @@ function EmptyState({ greeting, icon, children, footer }) {
|
|
|
3822
4103
|
/* @__PURE__ */ jsxs("h1", {
|
|
3823
4104
|
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
4105
|
children: [icon && /* @__PURE__ */ jsx("span", {
|
|
3825
|
-
className: "flex shrink-0 text-chat-accent [&_svg]:size-[1em]",
|
|
4106
|
+
className: "flex shrink-0 text-[var(--chat-empty-icon,var(--chat-accent))] [&_svg]:size-[1em]",
|
|
3826
4107
|
children: icon
|
|
3827
4108
|
}), greeting]
|
|
3828
4109
|
}),
|
|
@@ -3843,12 +4124,13 @@ function ChatTranscript({ messages, renderPart, widgets, header, notice, footer,
|
|
|
3843
4124
|
...contentPadding && { "--chat-content-padding": contentPadding }
|
|
3844
4125
|
};
|
|
3845
4126
|
const host = useMemo(() => {
|
|
3846
|
-
const { allow, readResource } = { ...widgets };
|
|
4127
|
+
const { allow, readResource, frameUrl } = { ...widgets };
|
|
3847
4128
|
const onOpenLink = widgets && "onOpenLink" in widgets ? widgets.onOpenLink : openInNewTab;
|
|
3848
4129
|
return {
|
|
3849
4130
|
...allow && { allow },
|
|
3850
4131
|
...onOpenLink && { onOpenLink },
|
|
3851
|
-
...readResource && { readResource }
|
|
4132
|
+
...readResource && { readResource },
|
|
4133
|
+
...frameUrl && { frameUrl }
|
|
3852
4134
|
};
|
|
3853
4135
|
}, [widgets]);
|
|
3854
4136
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -3943,17 +4225,27 @@ function SidebarToggle({ drawerOnly = false, className }) {
|
|
|
3943
4225
|
}
|
|
3944
4226
|
// src/views/sidebar.tsx
|
|
3945
4227
|
/** 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 }) {
|
|
4228
|
+
function Sidebar({ brand, brandIcon, headerActions, nav, children, footer, className }) {
|
|
3947
4229
|
const { collapsed } = useSidebar();
|
|
3948
4230
|
return /* @__PURE__ */ jsxs("div", {
|
|
3949
4231
|
className: clsx("flex h-full flex-col font-chat-sans", collapsed ? "w-13" : "w-full", className),
|
|
3950
4232
|
children: [
|
|
4233
|
+
collapsed && brandIcon && /* @__PURE__ */ jsx("div", {
|
|
4234
|
+
className: "flex h-12 shrink-0 items-center justify-center [&_svg]:size-5",
|
|
4235
|
+
children: brandIcon
|
|
4236
|
+
}),
|
|
3951
4237
|
/* @__PURE__ */ jsxs("div", {
|
|
3952
4238
|
className: clsx("flex h-12 shrink-0 items-center gap-1 px-2", collapsed && "justify-center"),
|
|
3953
4239
|
children: [
|
|
3954
|
-
!collapsed && /* @__PURE__ */
|
|
3955
|
-
className: "min-w-0 flex-1
|
|
3956
|
-
children:
|
|
4240
|
+
!collapsed && /* @__PURE__ */ jsxs("div", {
|
|
4241
|
+
className: "flex min-w-0 flex-1 items-center gap-2 px-1.5 font-chat-serif text-lg text-chat-fg",
|
|
4242
|
+
children: [brandIcon && /* @__PURE__ */ jsx("span", {
|
|
4243
|
+
className: "flex shrink-0 [&_svg]:size-5",
|
|
4244
|
+
children: brandIcon
|
|
4245
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4246
|
+
className: "min-w-0 truncate",
|
|
4247
|
+
children: brand
|
|
4248
|
+
})]
|
|
3957
4249
|
}),
|
|
3958
4250
|
!collapsed && headerActions,
|
|
3959
4251
|
/* @__PURE__ */ jsx(SidebarToggle, {})
|
|
@@ -4053,4 +4345,4 @@ function SidebarUser({ name, detail, avatarUrl, menu }) {
|
|
|
4053
4345
|
children: menu
|
|
4054
4346
|
})] });
|
|
4055
4347
|
}
|
|
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 };
|
|
4348
|
+
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 };
|