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/README.md +6 -0
- package/dist/core.js +11 -4
- package/dist/index.js +457 -211
- package/dist/styles.css +1 -1
- package/dist/types/core/src/index.d.ts +1 -0
- package/dist/types/core/src/replaces.d.ts +15 -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/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/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/dist/{ui-markup-DtMYer7I.js → ui-markup-CG9Lednh.js} +33 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
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__ */
|
|
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,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: [
|
|
2392
|
-
live,
|
|
2393
|
-
|
|
2394
|
-
|
|
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: [
|
|
2429
|
-
live,
|
|
2430
|
-
|
|
2431
|
-
|
|
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
|
-
|
|
2856
|
-
return /* @__PURE__ */ jsx(
|
|
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,
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
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) =>
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
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__ */
|
|
3955
|
-
className: "min-w-0 flex-1
|
|
3956
|
-
children:
|
|
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 };
|