@cloudbase/agent-react-ui 1.0.1-alpha.32
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 +123 -0
- package/components.json +21 -0
- package/dist/index.css +4241 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +2169 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2182 -0
- package/dist/index.mjs.map +1 -0
- package/example/.env.sample +2 -0
- package/example/App.tsx +368 -0
- package/example/app.css +1 -0
- package/example/index.html +12 -0
- package/example/main.tsx +9 -0
- package/example/vite.config.ts +34 -0
- package/package.json +75 -0
- package/postcss.config.cjs +3 -0
- package/src/components/ai-elements/agent.tsx +140 -0
- package/src/components/ai-elements/artifact.tsx +147 -0
- package/src/components/ai-elements/attachments.tsx +421 -0
- package/src/components/ai-elements/audio-player.tsx +228 -0
- package/src/components/ai-elements/canvas.tsx +22 -0
- package/src/components/ai-elements/chain-of-thought.tsx +228 -0
- package/src/components/ai-elements/checkpoint.tsx +71 -0
- package/src/components/ai-elements/code-block.tsx +532 -0
- package/src/components/ai-elements/commit.tsx +448 -0
- package/src/components/ai-elements/confirmation.tsx +176 -0
- package/src/components/ai-elements/connection.tsx +28 -0
- package/src/components/ai-elements/context.tsx +408 -0
- package/src/components/ai-elements/controls.tsx +18 -0
- package/src/components/ai-elements/conversation.tsx +100 -0
- package/src/components/ai-elements/edge.tsx +140 -0
- package/src/components/ai-elements/environment-variables.tsx +295 -0
- package/src/components/ai-elements/file-tree.tsx +258 -0
- package/src/components/ai-elements/image.tsx +24 -0
- package/src/components/ai-elements/inline-citation.tsx +287 -0
- package/src/components/ai-elements/message.tsx +336 -0
- package/src/components/ai-elements/mic-selector.tsx +370 -0
- package/src/components/ai-elements/model-selector.tsx +211 -0
- package/src/components/ai-elements/node.tsx +71 -0
- package/src/components/ai-elements/open-in-chat.tsx +365 -0
- package/src/components/ai-elements/package-info.tsx +233 -0
- package/src/components/ai-elements/panel.tsx +15 -0
- package/src/components/ai-elements/persona.tsx +270 -0
- package/src/components/ai-elements/plan.tsx +142 -0
- package/src/components/ai-elements/prompt-input.tsx +1263 -0
- package/src/components/ai-elements/queue.tsx +274 -0
- package/src/components/ai-elements/reasoning.tsx +193 -0
- package/src/components/ai-elements/sandbox.tsx +126 -0
- package/src/components/ai-elements/schema-display.tsx +458 -0
- package/src/components/ai-elements/shimmer.tsx +64 -0
- package/src/components/ai-elements/snippet.tsx +139 -0
- package/src/components/ai-elements/sources.tsx +77 -0
- package/src/components/ai-elements/speech-input.tsx +301 -0
- package/src/components/ai-elements/stack-trace.tsx +482 -0
- package/src/components/ai-elements/suggestion.tsx +53 -0
- package/src/components/ai-elements/task.tsx +87 -0
- package/src/components/ai-elements/terminal.tsx +261 -0
- package/src/components/ai-elements/test-results.tsx +485 -0
- package/src/components/ai-elements/tool.tsx +174 -0
- package/src/components/ai-elements/toolbar.tsx +16 -0
- package/src/components/ai-elements/transcription.tsx +124 -0
- package/src/components/ai-elements/voice-selector.tsx +479 -0
- package/src/components/ai-elements/web-preview.tsx +263 -0
- package/src/components/chat/Chat.tsx +178 -0
- package/src/components/chat/Input.tsx +98 -0
- package/src/components/chat/Message.tsx +276 -0
- package/src/components/chat/index.ts +2 -0
- package/src/components/index.ts +1 -0
- package/src/components/ui/accordion.tsx +64 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/carousel.tsx +239 -0
- package/src/components/ui/collapsible.tsx +31 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/hover-card.tsx +42 -0
- package/src/components/ui/input-group.tsx +168 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +33 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/css/global.css +123 -0
- package/src/css/index.css +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-copy-to-clipboard.ts +31 -0
- package/src/index.ts +4 -0
- package/src/lib/utils.ts +6 -0
- package/src/locales/context.ts +8 -0
- package/src/locales/hooks.ts +20 -0
- package/src/locales/index.ts +3 -0
- package/src/locales/langs/en.ts +17 -0
- package/src/locales/langs/index.ts +12 -0
- package/src/locales/langs/zh-cn.ts +18 -0
- package/tsconfig.json +21 -0
- package/tsup.config.ts +21 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2182 @@
|
|
|
1
|
+
// src/components/ui/button.tsx
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { Slot } from "radix-ui";
|
|
4
|
+
|
|
5
|
+
// src/lib/utils.ts
|
|
6
|
+
import { clsx } from "clsx";
|
|
7
|
+
import { twMerge } from "tailwind-merge";
|
|
8
|
+
function cn(...inputs) {
|
|
9
|
+
return twMerge(clsx(inputs));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/components/ui/button.tsx
|
|
13
|
+
import { jsx } from "react/jsx-runtime";
|
|
14
|
+
var buttonVariants = cva(
|
|
15
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
variant: {
|
|
19
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
20
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
21
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
22
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
23
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
24
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
25
|
+
},
|
|
26
|
+
size: {
|
|
27
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
28
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
29
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
30
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
31
|
+
icon: "size-9",
|
|
32
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
33
|
+
"icon-sm": "size-8",
|
|
34
|
+
"icon-lg": "size-10"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: "default",
|
|
39
|
+
size: "default"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
function Button({
|
|
44
|
+
className,
|
|
45
|
+
variant = "default",
|
|
46
|
+
size = "default",
|
|
47
|
+
asChild = false,
|
|
48
|
+
...props
|
|
49
|
+
}) {
|
|
50
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
Comp,
|
|
53
|
+
{
|
|
54
|
+
"data-slot": "button",
|
|
55
|
+
"data-variant": variant,
|
|
56
|
+
"data-size": size,
|
|
57
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
58
|
+
...props
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/components/ui/hover-card.tsx
|
|
64
|
+
import { HoverCard as HoverCardPrimitive } from "radix-ui";
|
|
65
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
66
|
+
|
|
67
|
+
// src/components/ai-elements/attachments.tsx
|
|
68
|
+
import {
|
|
69
|
+
FileTextIcon,
|
|
70
|
+
GlobeIcon,
|
|
71
|
+
ImageIcon,
|
|
72
|
+
Music2Icon,
|
|
73
|
+
PaperclipIcon,
|
|
74
|
+
VideoIcon,
|
|
75
|
+
XIcon
|
|
76
|
+
} from "lucide-react";
|
|
77
|
+
import { createContext, useContext, useMemo } from "react";
|
|
78
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
79
|
+
var getMediaCategory = (data) => {
|
|
80
|
+
if (data.type === "source-document") {
|
|
81
|
+
return "source";
|
|
82
|
+
}
|
|
83
|
+
const mediaType = data.mediaType ?? "";
|
|
84
|
+
if (mediaType.startsWith("image/")) {
|
|
85
|
+
return "image";
|
|
86
|
+
}
|
|
87
|
+
if (mediaType.startsWith("video/")) {
|
|
88
|
+
return "video";
|
|
89
|
+
}
|
|
90
|
+
if (mediaType.startsWith("audio/")) {
|
|
91
|
+
return "audio";
|
|
92
|
+
}
|
|
93
|
+
if (mediaType.startsWith("application/") || mediaType.startsWith("text/")) {
|
|
94
|
+
return "document";
|
|
95
|
+
}
|
|
96
|
+
return "unknown";
|
|
97
|
+
};
|
|
98
|
+
var AttachmentsContext = createContext(null);
|
|
99
|
+
var AttachmentContext = createContext(null);
|
|
100
|
+
var useAttachmentsContext = () => useContext(AttachmentsContext) ?? { variant: "grid" };
|
|
101
|
+
var useAttachmentContext = () => {
|
|
102
|
+
const ctx = useContext(AttachmentContext);
|
|
103
|
+
if (!ctx) {
|
|
104
|
+
throw new Error("Attachment components must be used within <Attachment>");
|
|
105
|
+
}
|
|
106
|
+
return ctx;
|
|
107
|
+
};
|
|
108
|
+
var Attachments = ({
|
|
109
|
+
variant = "grid",
|
|
110
|
+
className,
|
|
111
|
+
children,
|
|
112
|
+
...props
|
|
113
|
+
}) => {
|
|
114
|
+
const contextValue = useMemo(() => ({ variant }), [variant]);
|
|
115
|
+
return /* @__PURE__ */ jsx3(AttachmentsContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx3(
|
|
116
|
+
"div",
|
|
117
|
+
{
|
|
118
|
+
className: cn(
|
|
119
|
+
"flex items-start",
|
|
120
|
+
variant === "list" ? "flex-col gap-2" : "flex-wrap gap-2",
|
|
121
|
+
variant === "grid" && "ml-auto w-fit",
|
|
122
|
+
className
|
|
123
|
+
),
|
|
124
|
+
...props,
|
|
125
|
+
children
|
|
126
|
+
}
|
|
127
|
+
) });
|
|
128
|
+
};
|
|
129
|
+
var Attachment = ({
|
|
130
|
+
data,
|
|
131
|
+
onRemove,
|
|
132
|
+
className,
|
|
133
|
+
children,
|
|
134
|
+
...props
|
|
135
|
+
}) => {
|
|
136
|
+
const { variant } = useAttachmentsContext();
|
|
137
|
+
const mediaCategory = getMediaCategory(data);
|
|
138
|
+
const contextValue = useMemo(
|
|
139
|
+
() => ({ data, mediaCategory, onRemove, variant }),
|
|
140
|
+
[data, mediaCategory, onRemove, variant]
|
|
141
|
+
);
|
|
142
|
+
return /* @__PURE__ */ jsx3(AttachmentContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx3(
|
|
143
|
+
"div",
|
|
144
|
+
{
|
|
145
|
+
className: cn(
|
|
146
|
+
"group relative",
|
|
147
|
+
variant === "grid" && "size-24 overflow-hidden rounded-lg",
|
|
148
|
+
variant === "inline" && [
|
|
149
|
+
"flex h-8 cursor-pointer select-none items-center gap-1.5",
|
|
150
|
+
"rounded-md border border-border px-1.5",
|
|
151
|
+
"font-medium text-sm transition-all",
|
|
152
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50"
|
|
153
|
+
],
|
|
154
|
+
variant === "list" && [
|
|
155
|
+
"flex w-full items-center gap-3 rounded-lg border p-3",
|
|
156
|
+
"hover:bg-accent/50"
|
|
157
|
+
],
|
|
158
|
+
className
|
|
159
|
+
),
|
|
160
|
+
...props,
|
|
161
|
+
children
|
|
162
|
+
}
|
|
163
|
+
) });
|
|
164
|
+
};
|
|
165
|
+
var AttachmentPreview = ({
|
|
166
|
+
fallbackIcon,
|
|
167
|
+
className,
|
|
168
|
+
...props
|
|
169
|
+
}) => {
|
|
170
|
+
const { data, mediaCategory, variant } = useAttachmentContext();
|
|
171
|
+
const iconSize = variant === "inline" ? "size-3" : "size-4";
|
|
172
|
+
const renderImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */ jsx3(
|
|
173
|
+
"img",
|
|
174
|
+
{
|
|
175
|
+
alt: filename || "Image",
|
|
176
|
+
className: "size-full object-cover",
|
|
177
|
+
height: 96,
|
|
178
|
+
src: url,
|
|
179
|
+
width: 96
|
|
180
|
+
}
|
|
181
|
+
) : /* @__PURE__ */ jsx3(
|
|
182
|
+
"img",
|
|
183
|
+
{
|
|
184
|
+
alt: filename || "Image",
|
|
185
|
+
className: "size-full rounded object-cover",
|
|
186
|
+
height: 20,
|
|
187
|
+
src: url,
|
|
188
|
+
width: 20
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
const renderIcon = (Icon) => /* @__PURE__ */ jsx3(Icon, { className: cn(iconSize, "text-muted-foreground") });
|
|
192
|
+
const renderContent = () => {
|
|
193
|
+
if (mediaCategory === "image" && data.type === "file" && data.url) {
|
|
194
|
+
return renderImage(data.url, data.filename, variant === "grid");
|
|
195
|
+
}
|
|
196
|
+
if (mediaCategory === "video" && data.type === "file" && data.url) {
|
|
197
|
+
return /* @__PURE__ */ jsx3("video", { className: "size-full object-cover", muted: true, src: data.url });
|
|
198
|
+
}
|
|
199
|
+
const iconMap = {
|
|
200
|
+
image: ImageIcon,
|
|
201
|
+
video: VideoIcon,
|
|
202
|
+
audio: Music2Icon,
|
|
203
|
+
source: GlobeIcon,
|
|
204
|
+
document: FileTextIcon,
|
|
205
|
+
unknown: PaperclipIcon
|
|
206
|
+
};
|
|
207
|
+
const Icon = iconMap[mediaCategory];
|
|
208
|
+
return fallbackIcon ?? renderIcon(Icon);
|
|
209
|
+
};
|
|
210
|
+
return /* @__PURE__ */ jsx3(
|
|
211
|
+
"div",
|
|
212
|
+
{
|
|
213
|
+
className: cn(
|
|
214
|
+
"flex shrink-0 items-center justify-center overflow-hidden",
|
|
215
|
+
variant === "grid" && "size-full bg-muted",
|
|
216
|
+
variant === "inline" && "size-5 rounded bg-background",
|
|
217
|
+
variant === "list" && "size-12 rounded bg-muted",
|
|
218
|
+
className
|
|
219
|
+
),
|
|
220
|
+
...props,
|
|
221
|
+
children: renderContent()
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
var AttachmentRemove = ({
|
|
226
|
+
label = "Remove",
|
|
227
|
+
className,
|
|
228
|
+
children,
|
|
229
|
+
...props
|
|
230
|
+
}) => {
|
|
231
|
+
const { onRemove, variant } = useAttachmentContext();
|
|
232
|
+
if (!onRemove) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return /* @__PURE__ */ jsxs(
|
|
236
|
+
Button,
|
|
237
|
+
{
|
|
238
|
+
"aria-label": label,
|
|
239
|
+
className: cn(
|
|
240
|
+
variant === "grid" && [
|
|
241
|
+
"absolute top-2 right-2 size-6 rounded-full p-0",
|
|
242
|
+
"bg-background/80 backdrop-blur-sm",
|
|
243
|
+
"opacity-0 transition-opacity group-hover:opacity-100",
|
|
244
|
+
"hover:bg-background",
|
|
245
|
+
"[&>svg]:size-3"
|
|
246
|
+
],
|
|
247
|
+
variant === "inline" && [
|
|
248
|
+
"size-5 rounded p-0",
|
|
249
|
+
"opacity-0 transition-opacity group-hover:opacity-100",
|
|
250
|
+
"[&>svg]:size-2.5"
|
|
251
|
+
],
|
|
252
|
+
variant === "list" && ["size-8 shrink-0 rounded p-0", "[&>svg]:size-4"],
|
|
253
|
+
className
|
|
254
|
+
),
|
|
255
|
+
onClick: (e) => {
|
|
256
|
+
e.stopPropagation();
|
|
257
|
+
onRemove();
|
|
258
|
+
},
|
|
259
|
+
type: "button",
|
|
260
|
+
variant: "ghost",
|
|
261
|
+
...props,
|
|
262
|
+
children: [
|
|
263
|
+
children ?? /* @__PURE__ */ jsx3(XIcon, {}),
|
|
264
|
+
/* @__PURE__ */ jsx3("span", { className: "sr-only", children: label })
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/components/ui/command.tsx
|
|
271
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
272
|
+
import { SearchIcon } from "lucide-react";
|
|
273
|
+
|
|
274
|
+
// src/components/ui/dialog.tsx
|
|
275
|
+
import { XIcon as XIcon2 } from "lucide-react";
|
|
276
|
+
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
277
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
278
|
+
|
|
279
|
+
// src/components/ui/command.tsx
|
|
280
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
281
|
+
|
|
282
|
+
// src/components/ui/dropdown-menu.tsx
|
|
283
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
|
|
284
|
+
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
|
|
285
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
286
|
+
|
|
287
|
+
// src/components/ui/input-group.tsx
|
|
288
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
289
|
+
|
|
290
|
+
// src/components/ui/input.tsx
|
|
291
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
292
|
+
|
|
293
|
+
// src/components/ui/textarea.tsx
|
|
294
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
295
|
+
function Textarea({ className, ...props }) {
|
|
296
|
+
return /* @__PURE__ */ jsx8(
|
|
297
|
+
"textarea",
|
|
298
|
+
{
|
|
299
|
+
"data-slot": "textarea",
|
|
300
|
+
className: cn(
|
|
301
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
302
|
+
className
|
|
303
|
+
),
|
|
304
|
+
...props
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/components/ui/input-group.tsx
|
|
310
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
311
|
+
function InputGroup({ className, ...props }) {
|
|
312
|
+
return /* @__PURE__ */ jsx9(
|
|
313
|
+
"div",
|
|
314
|
+
{
|
|
315
|
+
"data-slot": "input-group",
|
|
316
|
+
role: "group",
|
|
317
|
+
className: cn(
|
|
318
|
+
"group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
|
|
319
|
+
"h-9 min-w-0 has-[>textarea]:h-auto",
|
|
320
|
+
// Variants based on alignment.
|
|
321
|
+
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
322
|
+
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
323
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
324
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
|
|
325
|
+
// Focus state.
|
|
326
|
+
"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
|
|
327
|
+
// Error state.
|
|
328
|
+
"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
|
329
|
+
className
|
|
330
|
+
),
|
|
331
|
+
...props
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
var inputGroupAddonVariants = cva2(
|
|
336
|
+
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
|
|
337
|
+
{
|
|
338
|
+
variants: {
|
|
339
|
+
align: {
|
|
340
|
+
"inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
341
|
+
"inline-end": "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
|
|
342
|
+
"block-start": "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
|
|
343
|
+
"block-end": "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5"
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
defaultVariants: {
|
|
347
|
+
align: "inline-start"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
function InputGroupAddon({
|
|
352
|
+
className,
|
|
353
|
+
align = "inline-start",
|
|
354
|
+
...props
|
|
355
|
+
}) {
|
|
356
|
+
return /* @__PURE__ */ jsx9(
|
|
357
|
+
"div",
|
|
358
|
+
{
|
|
359
|
+
role: "group",
|
|
360
|
+
"data-slot": "input-group-addon",
|
|
361
|
+
"data-align": align,
|
|
362
|
+
className: cn(inputGroupAddonVariants({ align }), className),
|
|
363
|
+
onClick: (e) => {
|
|
364
|
+
if (e.target.closest("button")) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
368
|
+
},
|
|
369
|
+
...props
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
var inputGroupButtonVariants = cva2(
|
|
374
|
+
"text-sm shadow-none flex gap-2 items-center",
|
|
375
|
+
{
|
|
376
|
+
variants: {
|
|
377
|
+
size: {
|
|
378
|
+
xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
|
|
379
|
+
sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
|
|
380
|
+
"icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
|
|
381
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0"
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
defaultVariants: {
|
|
385
|
+
size: "xs"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
function InputGroupButton({
|
|
390
|
+
className,
|
|
391
|
+
type = "button",
|
|
392
|
+
variant = "ghost",
|
|
393
|
+
size = "xs",
|
|
394
|
+
...props
|
|
395
|
+
}) {
|
|
396
|
+
return /* @__PURE__ */ jsx9(
|
|
397
|
+
Button,
|
|
398
|
+
{
|
|
399
|
+
type,
|
|
400
|
+
"data-size": size,
|
|
401
|
+
variant,
|
|
402
|
+
className: cn(inputGroupButtonVariants({ size }), className),
|
|
403
|
+
...props
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
function InputGroupTextarea({
|
|
408
|
+
className,
|
|
409
|
+
...props
|
|
410
|
+
}) {
|
|
411
|
+
return /* @__PURE__ */ jsx9(
|
|
412
|
+
Textarea,
|
|
413
|
+
{
|
|
414
|
+
"data-slot": "input-group-control",
|
|
415
|
+
className: cn(
|
|
416
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
417
|
+
className
|
|
418
|
+
),
|
|
419
|
+
...props
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/components/ui/select.tsx
|
|
425
|
+
import { CheckIcon as CheckIcon2, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
426
|
+
import { Select as SelectPrimitive } from "radix-ui";
|
|
427
|
+
import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
428
|
+
|
|
429
|
+
// src/components/ui/spinner.tsx
|
|
430
|
+
import { Loader2Icon } from "lucide-react";
|
|
431
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
432
|
+
function Spinner({ className, ...props }) {
|
|
433
|
+
return /* @__PURE__ */ jsx11(
|
|
434
|
+
Loader2Icon,
|
|
435
|
+
{
|
|
436
|
+
role: "status",
|
|
437
|
+
"aria-label": "Loading",
|
|
438
|
+
className: cn("size-4 animate-spin", className),
|
|
439
|
+
...props
|
|
440
|
+
}
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/components/ai-elements/prompt-input.tsx
|
|
445
|
+
import {
|
|
446
|
+
CornerDownLeftIcon,
|
|
447
|
+
ImageIcon as ImageIcon2,
|
|
448
|
+
PlusIcon,
|
|
449
|
+
SquareIcon,
|
|
450
|
+
XIcon as XIcon3
|
|
451
|
+
} from "lucide-react";
|
|
452
|
+
import { nanoid } from "nanoid";
|
|
453
|
+
import {
|
|
454
|
+
Children,
|
|
455
|
+
createContext as createContext2,
|
|
456
|
+
useCallback,
|
|
457
|
+
useContext as useContext2,
|
|
458
|
+
useEffect,
|
|
459
|
+
useMemo as useMemo2,
|
|
460
|
+
useRef,
|
|
461
|
+
useState
|
|
462
|
+
} from "react";
|
|
463
|
+
import { Fragment, jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
464
|
+
var PromptInputController = createContext2(
|
|
465
|
+
null
|
|
466
|
+
);
|
|
467
|
+
var ProviderAttachmentsContext = createContext2(
|
|
468
|
+
null
|
|
469
|
+
);
|
|
470
|
+
var useOptionalPromptInputController = () => useContext2(PromptInputController);
|
|
471
|
+
var useOptionalProviderAttachments = () => useContext2(ProviderAttachmentsContext);
|
|
472
|
+
function PromptInputProvider({
|
|
473
|
+
initialInput: initialTextInput = "",
|
|
474
|
+
children
|
|
475
|
+
}) {
|
|
476
|
+
const [textInput, setTextInput] = useState(initialTextInput);
|
|
477
|
+
const clearInput = useCallback(() => setTextInput(""), []);
|
|
478
|
+
const [attachmentFiles, setAttachmentFiles] = useState([]);
|
|
479
|
+
const fileInputRef = useRef(null);
|
|
480
|
+
const openRef = useRef(() => void 0);
|
|
481
|
+
const add = useCallback((files) => {
|
|
482
|
+
const incoming = Array.from(files);
|
|
483
|
+
if (incoming.length === 0) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
setAttachmentFiles(
|
|
487
|
+
(prev) => prev.concat(
|
|
488
|
+
incoming.map((file) => ({
|
|
489
|
+
id: nanoid(),
|
|
490
|
+
type: "file",
|
|
491
|
+
url: URL.createObjectURL(file),
|
|
492
|
+
mediaType: file.type,
|
|
493
|
+
filename: file.name
|
|
494
|
+
}))
|
|
495
|
+
)
|
|
496
|
+
);
|
|
497
|
+
}, []);
|
|
498
|
+
const remove = useCallback((id) => {
|
|
499
|
+
setAttachmentFiles((prev) => {
|
|
500
|
+
const found = prev.find((f) => f.id === id);
|
|
501
|
+
if (found?.url) {
|
|
502
|
+
URL.revokeObjectURL(found.url);
|
|
503
|
+
}
|
|
504
|
+
return prev.filter((f) => f.id !== id);
|
|
505
|
+
});
|
|
506
|
+
}, []);
|
|
507
|
+
const clear = useCallback(() => {
|
|
508
|
+
setAttachmentFiles((prev) => {
|
|
509
|
+
for (const f of prev) {
|
|
510
|
+
if (f.url) {
|
|
511
|
+
URL.revokeObjectURL(f.url);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return [];
|
|
515
|
+
});
|
|
516
|
+
}, []);
|
|
517
|
+
const attachmentsRef = useRef(attachmentFiles);
|
|
518
|
+
attachmentsRef.current = attachmentFiles;
|
|
519
|
+
useEffect(
|
|
520
|
+
() => () => {
|
|
521
|
+
for (const f of attachmentsRef.current) {
|
|
522
|
+
if (f.url) {
|
|
523
|
+
URL.revokeObjectURL(f.url);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
[]
|
|
528
|
+
);
|
|
529
|
+
const openFileDialog = useCallback(() => {
|
|
530
|
+
openRef.current?.();
|
|
531
|
+
}, []);
|
|
532
|
+
const attachments = useMemo2(
|
|
533
|
+
() => ({
|
|
534
|
+
files: attachmentFiles,
|
|
535
|
+
add,
|
|
536
|
+
remove,
|
|
537
|
+
clear,
|
|
538
|
+
openFileDialog,
|
|
539
|
+
fileInputRef
|
|
540
|
+
}),
|
|
541
|
+
[attachmentFiles, add, remove, clear, openFileDialog]
|
|
542
|
+
);
|
|
543
|
+
const __registerFileInput = useCallback(
|
|
544
|
+
(ref, open) => {
|
|
545
|
+
fileInputRef.current = ref.current;
|
|
546
|
+
openRef.current = open;
|
|
547
|
+
},
|
|
548
|
+
[]
|
|
549
|
+
);
|
|
550
|
+
const controller = useMemo2(
|
|
551
|
+
() => ({
|
|
552
|
+
textInput: {
|
|
553
|
+
value: textInput,
|
|
554
|
+
setInput: setTextInput,
|
|
555
|
+
clear: clearInput
|
|
556
|
+
},
|
|
557
|
+
attachments,
|
|
558
|
+
__registerFileInput
|
|
559
|
+
}),
|
|
560
|
+
[textInput, clearInput, attachments, __registerFileInput]
|
|
561
|
+
);
|
|
562
|
+
return /* @__PURE__ */ jsx12(PromptInputController.Provider, { value: controller, children: /* @__PURE__ */ jsx12(ProviderAttachmentsContext.Provider, { value: attachments, children }) });
|
|
563
|
+
}
|
|
564
|
+
var LocalAttachmentsContext = createContext2(null);
|
|
565
|
+
var usePromptInputAttachments = () => {
|
|
566
|
+
const provider = useOptionalProviderAttachments();
|
|
567
|
+
const local = useContext2(LocalAttachmentsContext);
|
|
568
|
+
const context = local ?? provider;
|
|
569
|
+
if (!context) {
|
|
570
|
+
throw new Error(
|
|
571
|
+
"usePromptInputAttachments must be used within a PromptInput or PromptInputProvider"
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
return context;
|
|
575
|
+
};
|
|
576
|
+
var LocalReferencedSourcesContext = createContext2(null);
|
|
577
|
+
var PromptInput = ({
|
|
578
|
+
className,
|
|
579
|
+
accept,
|
|
580
|
+
multiple,
|
|
581
|
+
globalDrop,
|
|
582
|
+
syncHiddenInput,
|
|
583
|
+
maxFiles,
|
|
584
|
+
maxFileSize,
|
|
585
|
+
onError,
|
|
586
|
+
onSubmit,
|
|
587
|
+
children,
|
|
588
|
+
...props
|
|
589
|
+
}) => {
|
|
590
|
+
const controller = useOptionalPromptInputController();
|
|
591
|
+
const usingProvider = !!controller;
|
|
592
|
+
const inputRef = useRef(null);
|
|
593
|
+
const formRef = useRef(null);
|
|
594
|
+
const [items, setItems] = useState([]);
|
|
595
|
+
const files = usingProvider ? controller.attachments.files : items;
|
|
596
|
+
const [referencedSources, setReferencedSources] = useState([]);
|
|
597
|
+
const filesRef = useRef(files);
|
|
598
|
+
filesRef.current = files;
|
|
599
|
+
const openFileDialogLocal = useCallback(() => {
|
|
600
|
+
inputRef.current?.click();
|
|
601
|
+
}, []);
|
|
602
|
+
const matchesAccept = useCallback(
|
|
603
|
+
(f) => {
|
|
604
|
+
if (!accept || accept.trim() === "") {
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
const patterns = accept.split(",").map((s) => s.trim()).filter(Boolean);
|
|
608
|
+
return patterns.some((pattern) => {
|
|
609
|
+
if (pattern.endsWith("/*")) {
|
|
610
|
+
const prefix = pattern.slice(0, -1);
|
|
611
|
+
return f.type.startsWith(prefix);
|
|
612
|
+
}
|
|
613
|
+
return f.type === pattern;
|
|
614
|
+
});
|
|
615
|
+
},
|
|
616
|
+
[accept]
|
|
617
|
+
);
|
|
618
|
+
const addLocal = useCallback(
|
|
619
|
+
(fileList) => {
|
|
620
|
+
const incoming = Array.from(fileList);
|
|
621
|
+
const accepted = incoming.filter((f) => matchesAccept(f));
|
|
622
|
+
if (incoming.length && accepted.length === 0) {
|
|
623
|
+
onError?.({
|
|
624
|
+
code: "accept",
|
|
625
|
+
message: "No files match the accepted types."
|
|
626
|
+
});
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
|
|
630
|
+
const sized = accepted.filter(withinSize);
|
|
631
|
+
if (accepted.length > 0 && sized.length === 0) {
|
|
632
|
+
onError?.({
|
|
633
|
+
code: "max_file_size",
|
|
634
|
+
message: "All files exceed the maximum size."
|
|
635
|
+
});
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
setItems((prev) => {
|
|
639
|
+
const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : void 0;
|
|
640
|
+
const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
|
|
641
|
+
if (typeof capacity === "number" && sized.length > capacity) {
|
|
642
|
+
onError?.({
|
|
643
|
+
code: "max_files",
|
|
644
|
+
message: "Too many files. Some were not added."
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
const next = [];
|
|
648
|
+
for (const file of capped) {
|
|
649
|
+
next.push({
|
|
650
|
+
id: nanoid(),
|
|
651
|
+
type: "file",
|
|
652
|
+
url: URL.createObjectURL(file),
|
|
653
|
+
mediaType: file.type,
|
|
654
|
+
filename: file.name
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
return prev.concat(next);
|
|
658
|
+
});
|
|
659
|
+
},
|
|
660
|
+
[matchesAccept, maxFiles, maxFileSize, onError]
|
|
661
|
+
);
|
|
662
|
+
const removeLocal = useCallback(
|
|
663
|
+
(id) => setItems((prev) => {
|
|
664
|
+
const found = prev.find((file) => file.id === id);
|
|
665
|
+
if (found?.url) {
|
|
666
|
+
URL.revokeObjectURL(found.url);
|
|
667
|
+
}
|
|
668
|
+
return prev.filter((file) => file.id !== id);
|
|
669
|
+
}),
|
|
670
|
+
[]
|
|
671
|
+
);
|
|
672
|
+
const addWithProviderValidation = useCallback(
|
|
673
|
+
(fileList) => {
|
|
674
|
+
const incoming = Array.from(fileList);
|
|
675
|
+
const accepted = incoming.filter((f) => matchesAccept(f));
|
|
676
|
+
if (incoming.length && accepted.length === 0) {
|
|
677
|
+
onError?.({
|
|
678
|
+
code: "accept",
|
|
679
|
+
message: "No files match the accepted types."
|
|
680
|
+
});
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
|
|
684
|
+
const sized = accepted.filter(withinSize);
|
|
685
|
+
if (accepted.length > 0 && sized.length === 0) {
|
|
686
|
+
onError?.({
|
|
687
|
+
code: "max_file_size",
|
|
688
|
+
message: "All files exceed the maximum size."
|
|
689
|
+
});
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const currentCount = files.length;
|
|
693
|
+
const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - currentCount) : void 0;
|
|
694
|
+
const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
|
|
695
|
+
if (typeof capacity === "number" && sized.length > capacity) {
|
|
696
|
+
onError?.({
|
|
697
|
+
code: "max_files",
|
|
698
|
+
message: "Too many files. Some were not added."
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
if (capped.length > 0) {
|
|
702
|
+
controller?.attachments.add(capped);
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
[matchesAccept, maxFileSize, maxFiles, onError, files.length, controller]
|
|
706
|
+
);
|
|
707
|
+
const clearAttachments = useCallback(
|
|
708
|
+
() => usingProvider ? controller?.attachments.clear() : setItems((prev) => {
|
|
709
|
+
for (const file of prev) {
|
|
710
|
+
if (file.url) {
|
|
711
|
+
URL.revokeObjectURL(file.url);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return [];
|
|
715
|
+
}),
|
|
716
|
+
[usingProvider, controller]
|
|
717
|
+
);
|
|
718
|
+
const clearReferencedSources = useCallback(
|
|
719
|
+
() => setReferencedSources([]),
|
|
720
|
+
[]
|
|
721
|
+
);
|
|
722
|
+
const add = usingProvider ? addWithProviderValidation : addLocal;
|
|
723
|
+
const remove = usingProvider ? controller.attachments.remove : removeLocal;
|
|
724
|
+
const openFileDialog = usingProvider ? controller.attachments.openFileDialog : openFileDialogLocal;
|
|
725
|
+
const clear = useCallback(() => {
|
|
726
|
+
clearAttachments();
|
|
727
|
+
clearReferencedSources();
|
|
728
|
+
}, [clearAttachments, clearReferencedSources]);
|
|
729
|
+
useEffect(() => {
|
|
730
|
+
if (!usingProvider) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
controller.__registerFileInput(inputRef, () => inputRef.current?.click());
|
|
734
|
+
}, [usingProvider, controller]);
|
|
735
|
+
useEffect(() => {
|
|
736
|
+
if (syncHiddenInput && inputRef.current && files.length === 0) {
|
|
737
|
+
inputRef.current.value = "";
|
|
738
|
+
}
|
|
739
|
+
}, [files, syncHiddenInput]);
|
|
740
|
+
useEffect(() => {
|
|
741
|
+
const form = formRef.current;
|
|
742
|
+
if (!form) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
if (globalDrop) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const onDragOver = (e) => {
|
|
749
|
+
if (e.dataTransfer?.types?.includes("Files")) {
|
|
750
|
+
e.preventDefault();
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
const onDrop = (e) => {
|
|
754
|
+
if (e.dataTransfer?.types?.includes("Files")) {
|
|
755
|
+
e.preventDefault();
|
|
756
|
+
}
|
|
757
|
+
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
|
|
758
|
+
add(e.dataTransfer.files);
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
form.addEventListener("dragover", onDragOver);
|
|
762
|
+
form.addEventListener("drop", onDrop);
|
|
763
|
+
return () => {
|
|
764
|
+
form.removeEventListener("dragover", onDragOver);
|
|
765
|
+
form.removeEventListener("drop", onDrop);
|
|
766
|
+
};
|
|
767
|
+
}, [add, globalDrop]);
|
|
768
|
+
useEffect(() => {
|
|
769
|
+
if (!globalDrop) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const onDragOver = (e) => {
|
|
773
|
+
if (e.dataTransfer?.types?.includes("Files")) {
|
|
774
|
+
e.preventDefault();
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
const onDrop = (e) => {
|
|
778
|
+
if (e.dataTransfer?.types?.includes("Files")) {
|
|
779
|
+
e.preventDefault();
|
|
780
|
+
}
|
|
781
|
+
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
|
|
782
|
+
add(e.dataTransfer.files);
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
document.addEventListener("dragover", onDragOver);
|
|
786
|
+
document.addEventListener("drop", onDrop);
|
|
787
|
+
return () => {
|
|
788
|
+
document.removeEventListener("dragover", onDragOver);
|
|
789
|
+
document.removeEventListener("drop", onDrop);
|
|
790
|
+
};
|
|
791
|
+
}, [add, globalDrop]);
|
|
792
|
+
useEffect(
|
|
793
|
+
() => () => {
|
|
794
|
+
if (!usingProvider) {
|
|
795
|
+
for (const f of filesRef.current) {
|
|
796
|
+
if (f.url) {
|
|
797
|
+
URL.revokeObjectURL(f.url);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
},
|
|
802
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- cleanup only on unmount; filesRef always current
|
|
803
|
+
[usingProvider]
|
|
804
|
+
);
|
|
805
|
+
const handleChange = (event) => {
|
|
806
|
+
if (event.currentTarget.files) {
|
|
807
|
+
add(event.currentTarget.files);
|
|
808
|
+
}
|
|
809
|
+
event.currentTarget.value = "";
|
|
810
|
+
};
|
|
811
|
+
const convertBlobUrlToDataUrl = async (url) => {
|
|
812
|
+
try {
|
|
813
|
+
const response = await fetch(url);
|
|
814
|
+
const blob = await response.blob();
|
|
815
|
+
return new Promise((resolve) => {
|
|
816
|
+
const reader = new FileReader();
|
|
817
|
+
reader.onloadend = () => resolve(reader.result);
|
|
818
|
+
reader.onerror = () => resolve(null);
|
|
819
|
+
reader.readAsDataURL(blob);
|
|
820
|
+
});
|
|
821
|
+
} catch {
|
|
822
|
+
return null;
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
const attachmentsCtx = useMemo2(
|
|
826
|
+
() => ({
|
|
827
|
+
files: files.map((item) => ({ ...item, id: item.id })),
|
|
828
|
+
add,
|
|
829
|
+
remove,
|
|
830
|
+
clear: clearAttachments,
|
|
831
|
+
openFileDialog,
|
|
832
|
+
fileInputRef: inputRef
|
|
833
|
+
}),
|
|
834
|
+
[files, add, remove, clearAttachments, openFileDialog]
|
|
835
|
+
);
|
|
836
|
+
const refsCtx = useMemo2(
|
|
837
|
+
() => ({
|
|
838
|
+
sources: referencedSources,
|
|
839
|
+
add: (incoming) => {
|
|
840
|
+
const array = Array.isArray(incoming) ? incoming : [incoming];
|
|
841
|
+
setReferencedSources(
|
|
842
|
+
(prev) => prev.concat(array.map((s) => ({ ...s, id: nanoid() })))
|
|
843
|
+
);
|
|
844
|
+
},
|
|
845
|
+
remove: (id) => {
|
|
846
|
+
setReferencedSources((prev) => prev.filter((s) => s.id !== id));
|
|
847
|
+
},
|
|
848
|
+
clear: clearReferencedSources
|
|
849
|
+
}),
|
|
850
|
+
[referencedSources, clearReferencedSources]
|
|
851
|
+
);
|
|
852
|
+
const handleSubmit = (event) => {
|
|
853
|
+
event.preventDefault();
|
|
854
|
+
const form = event.currentTarget;
|
|
855
|
+
const text = usingProvider ? controller.textInput.value : (() => {
|
|
856
|
+
const formData = new FormData(form);
|
|
857
|
+
return formData.get("message") || "";
|
|
858
|
+
})();
|
|
859
|
+
if (!usingProvider) {
|
|
860
|
+
form.reset();
|
|
861
|
+
}
|
|
862
|
+
Promise.all(
|
|
863
|
+
files.map(async ({ id, ...item }) => {
|
|
864
|
+
if (item.url?.startsWith("blob:")) {
|
|
865
|
+
const dataUrl = await convertBlobUrlToDataUrl(item.url);
|
|
866
|
+
return {
|
|
867
|
+
...item,
|
|
868
|
+
url: dataUrl ?? item.url
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
return item;
|
|
872
|
+
})
|
|
873
|
+
).then((convertedFiles) => {
|
|
874
|
+
try {
|
|
875
|
+
const result = onSubmit({ text, files: convertedFiles }, event);
|
|
876
|
+
if (result instanceof Promise) {
|
|
877
|
+
result.then(() => {
|
|
878
|
+
clear();
|
|
879
|
+
if (usingProvider) {
|
|
880
|
+
controller.textInput.clear();
|
|
881
|
+
}
|
|
882
|
+
}).catch(() => {
|
|
883
|
+
});
|
|
884
|
+
} else {
|
|
885
|
+
clear();
|
|
886
|
+
if (usingProvider) {
|
|
887
|
+
controller.textInput.clear();
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
} catch {
|
|
891
|
+
}
|
|
892
|
+
}).catch(() => {
|
|
893
|
+
});
|
|
894
|
+
};
|
|
895
|
+
const inner = /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
896
|
+
/* @__PURE__ */ jsx12(
|
|
897
|
+
"input",
|
|
898
|
+
{
|
|
899
|
+
accept,
|
|
900
|
+
"aria-label": "Upload files",
|
|
901
|
+
className: "hidden",
|
|
902
|
+
multiple,
|
|
903
|
+
onChange: handleChange,
|
|
904
|
+
ref: inputRef,
|
|
905
|
+
title: "Upload files",
|
|
906
|
+
type: "file"
|
|
907
|
+
}
|
|
908
|
+
),
|
|
909
|
+
/* @__PURE__ */ jsx12(
|
|
910
|
+
"form",
|
|
911
|
+
{
|
|
912
|
+
className: cn("w-full", className),
|
|
913
|
+
onSubmit: handleSubmit,
|
|
914
|
+
ref: formRef,
|
|
915
|
+
...props,
|
|
916
|
+
children: /* @__PURE__ */ jsx12(InputGroup, { className: "overflow-hidden", children })
|
|
917
|
+
}
|
|
918
|
+
)
|
|
919
|
+
] });
|
|
920
|
+
const withReferencedSources = /* @__PURE__ */ jsx12(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
|
|
921
|
+
return /* @__PURE__ */ jsx12(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
|
|
922
|
+
};
|
|
923
|
+
var PromptInputBody = ({
|
|
924
|
+
className,
|
|
925
|
+
...props
|
|
926
|
+
}) => /* @__PURE__ */ jsx12("div", { className: cn("contents", className), ...props });
|
|
927
|
+
var PromptInputTextarea = ({
|
|
928
|
+
onChange,
|
|
929
|
+
onKeyDown,
|
|
930
|
+
className,
|
|
931
|
+
placeholder = "What would you like to know?",
|
|
932
|
+
...props
|
|
933
|
+
}) => {
|
|
934
|
+
const controller = useOptionalPromptInputController();
|
|
935
|
+
const attachments = usePromptInputAttachments();
|
|
936
|
+
const [isComposing, setIsComposing] = useState(false);
|
|
937
|
+
const handleKeyDown = (e) => {
|
|
938
|
+
onKeyDown?.(e);
|
|
939
|
+
if (e.defaultPrevented) {
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
if (e.key === "Enter") {
|
|
943
|
+
if (isComposing || e.nativeEvent.isComposing) {
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (e.shiftKey) {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
e.preventDefault();
|
|
950
|
+
const form = e.currentTarget.form;
|
|
951
|
+
const submitButton = form?.querySelector(
|
|
952
|
+
'button[type="submit"]'
|
|
953
|
+
);
|
|
954
|
+
if (submitButton?.disabled) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
form?.requestSubmit();
|
|
958
|
+
}
|
|
959
|
+
if (e.key === "Backspace" && e.currentTarget.value === "" && attachments.files.length > 0) {
|
|
960
|
+
e.preventDefault();
|
|
961
|
+
const lastAttachment = attachments.files[attachments.files.length - 1];
|
|
962
|
+
if (lastAttachment) {
|
|
963
|
+
attachments.remove(lastAttachment.id);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
const handlePaste = (event) => {
|
|
968
|
+
const items = event.clipboardData?.items;
|
|
969
|
+
if (!items) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const files = [];
|
|
973
|
+
for (const item of items) {
|
|
974
|
+
if (item.kind === "file") {
|
|
975
|
+
const file = item.getAsFile();
|
|
976
|
+
if (file) {
|
|
977
|
+
files.push(file);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (files.length > 0) {
|
|
982
|
+
event.preventDefault();
|
|
983
|
+
attachments.add(files);
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
const controlledProps = controller ? {
|
|
987
|
+
value: controller.textInput.value,
|
|
988
|
+
onChange: (e) => {
|
|
989
|
+
controller.textInput.setInput(e.currentTarget.value);
|
|
990
|
+
onChange?.(e);
|
|
991
|
+
}
|
|
992
|
+
} : {
|
|
993
|
+
onChange
|
|
994
|
+
};
|
|
995
|
+
return /* @__PURE__ */ jsx12(
|
|
996
|
+
InputGroupTextarea,
|
|
997
|
+
{
|
|
998
|
+
className: cn("field-sizing-content max-h-48 min-h-16", className),
|
|
999
|
+
name: "message",
|
|
1000
|
+
onCompositionEnd: () => setIsComposing(false),
|
|
1001
|
+
onCompositionStart: () => setIsComposing(true),
|
|
1002
|
+
onKeyDown: handleKeyDown,
|
|
1003
|
+
onPaste: handlePaste,
|
|
1004
|
+
placeholder,
|
|
1005
|
+
...props,
|
|
1006
|
+
...controlledProps
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
};
|
|
1010
|
+
var PromptInputFooter = ({
|
|
1011
|
+
className,
|
|
1012
|
+
...props
|
|
1013
|
+
}) => /* @__PURE__ */ jsx12(
|
|
1014
|
+
InputGroupAddon,
|
|
1015
|
+
{
|
|
1016
|
+
align: "block-end",
|
|
1017
|
+
className: cn("justify-between gap-1", className),
|
|
1018
|
+
...props
|
|
1019
|
+
}
|
|
1020
|
+
);
|
|
1021
|
+
var PromptInputTools = ({
|
|
1022
|
+
className,
|
|
1023
|
+
...props
|
|
1024
|
+
}) => /* @__PURE__ */ jsx12("div", { className: cn("flex items-center gap-1", className), ...props });
|
|
1025
|
+
var PromptInputSubmit = ({
|
|
1026
|
+
className,
|
|
1027
|
+
variant = "default",
|
|
1028
|
+
size = "icon-sm",
|
|
1029
|
+
status,
|
|
1030
|
+
onStop,
|
|
1031
|
+
onClick,
|
|
1032
|
+
children,
|
|
1033
|
+
...props
|
|
1034
|
+
}) => {
|
|
1035
|
+
const isGenerating = status === "submitted" || status === "streaming";
|
|
1036
|
+
let Icon = /* @__PURE__ */ jsx12(CornerDownLeftIcon, { className: "size-4" });
|
|
1037
|
+
if (status === "submitted") {
|
|
1038
|
+
Icon = /* @__PURE__ */ jsx12(Spinner, {});
|
|
1039
|
+
} else if (status === "streaming") {
|
|
1040
|
+
Icon = /* @__PURE__ */ jsx12(SquareIcon, { className: "size-4" });
|
|
1041
|
+
} else if (status === "error") {
|
|
1042
|
+
Icon = /* @__PURE__ */ jsx12(XIcon3, { className: "size-4" });
|
|
1043
|
+
}
|
|
1044
|
+
const handleClick = (e) => {
|
|
1045
|
+
if (isGenerating && onStop) {
|
|
1046
|
+
e.preventDefault();
|
|
1047
|
+
onStop();
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
onClick?.(e);
|
|
1051
|
+
};
|
|
1052
|
+
return /* @__PURE__ */ jsx12(
|
|
1053
|
+
InputGroupButton,
|
|
1054
|
+
{
|
|
1055
|
+
"aria-label": isGenerating ? "Stop" : "Submit",
|
|
1056
|
+
className: cn(className),
|
|
1057
|
+
onClick: handleClick,
|
|
1058
|
+
size,
|
|
1059
|
+
type: isGenerating && onStop ? "button" : "submit",
|
|
1060
|
+
variant,
|
|
1061
|
+
...props,
|
|
1062
|
+
children: children ?? Icon
|
|
1063
|
+
}
|
|
1064
|
+
);
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
// src/locales/context.ts
|
|
1068
|
+
import { createContext as createContext3 } from "react";
|
|
1069
|
+
var LocaleContext = createContext3({
|
|
1070
|
+
locale: "zh-CN"
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
// src/locales/hooks.ts
|
|
1074
|
+
import { useContext as useContext3, useMemo as useMemo3 } from "react";
|
|
1075
|
+
|
|
1076
|
+
// src/locales/langs/zh-cn.ts
|
|
1077
|
+
var zhCN = {
|
|
1078
|
+
chat: {
|
|
1079
|
+
emptyTitle: "\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u5230\u4F60\uFF1F"
|
|
1080
|
+
},
|
|
1081
|
+
input: {
|
|
1082
|
+
placeholder: "\u8BF7\u8F93\u5165\u6D88\u606F...",
|
|
1083
|
+
disclaimer: "\u5185\u5BB9\u7531 AI \u751F\u6210\uFF0C\u4EC5\u4F9B\u53C2\u8003"
|
|
1084
|
+
},
|
|
1085
|
+
message: {
|
|
1086
|
+
copy: "\u590D\u5236",
|
|
1087
|
+
copied: "\u5DF2\u590D\u5236",
|
|
1088
|
+
copyToClipboard: "\u590D\u5236",
|
|
1089
|
+
paused: "\u5DF2\u6682\u505C\u751F\u6210"
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
// src/locales/langs/en.ts
|
|
1094
|
+
var en = {
|
|
1095
|
+
chat: {
|
|
1096
|
+
emptyTitle: "How can I help you?"
|
|
1097
|
+
},
|
|
1098
|
+
input: {
|
|
1099
|
+
placeholder: "Type your message...",
|
|
1100
|
+
disclaimer: "AI-generated content, for reference only"
|
|
1101
|
+
},
|
|
1102
|
+
message: {
|
|
1103
|
+
copy: "Copy",
|
|
1104
|
+
copied: "Copied!",
|
|
1105
|
+
copyToClipboard: "Copy to clipboard",
|
|
1106
|
+
paused: "Generation paused"
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
// src/locales/langs/index.ts
|
|
1111
|
+
var locales = {
|
|
1112
|
+
"zh-CN": zhCN,
|
|
1113
|
+
"en-US": en
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
// src/locales/hooks.ts
|
|
1117
|
+
function useLocale(name) {
|
|
1118
|
+
const { locale: localeCode } = useContext3(LocaleContext);
|
|
1119
|
+
const locale = useMemo3(() => {
|
|
1120
|
+
const fullLocale = locales[localeCode] ?? locales["zh-CN"];
|
|
1121
|
+
return name ? fullLocale[name] : fullLocale;
|
|
1122
|
+
}, [localeCode, name]);
|
|
1123
|
+
return { locale, localeCode };
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// src/components/chat/Input.tsx
|
|
1127
|
+
import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1128
|
+
var PromptInputAttachmentsDisplay = () => {
|
|
1129
|
+
const attachments = usePromptInputAttachments();
|
|
1130
|
+
if (attachments.files.length === 0) {
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
return /* @__PURE__ */ jsx13(Attachments, { variant: "inline", children: attachments.files.map((attachment) => /* @__PURE__ */ jsxs7(
|
|
1134
|
+
Attachment,
|
|
1135
|
+
{
|
|
1136
|
+
data: attachment,
|
|
1137
|
+
onRemove: () => attachments.remove(attachment.id),
|
|
1138
|
+
children: [
|
|
1139
|
+
/* @__PURE__ */ jsx13(AttachmentPreview, {}),
|
|
1140
|
+
/* @__PURE__ */ jsx13(AttachmentRemove, {})
|
|
1141
|
+
]
|
|
1142
|
+
},
|
|
1143
|
+
attachment.id
|
|
1144
|
+
)) });
|
|
1145
|
+
};
|
|
1146
|
+
function Input2({
|
|
1147
|
+
onSend,
|
|
1148
|
+
onStop,
|
|
1149
|
+
placeholder,
|
|
1150
|
+
streaming = false
|
|
1151
|
+
}) {
|
|
1152
|
+
const { locale } = useLocale("input");
|
|
1153
|
+
const status = streaming ? "streaming" : "ready";
|
|
1154
|
+
const handleSubmit = (message) => {
|
|
1155
|
+
const text = message.text.trim();
|
|
1156
|
+
const hasText = Boolean(text);
|
|
1157
|
+
const hasAttachments = Boolean(message.files?.length);
|
|
1158
|
+
if (!(hasText || hasAttachments)) {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
onSend?.({
|
|
1162
|
+
...message,
|
|
1163
|
+
text
|
|
1164
|
+
});
|
|
1165
|
+
};
|
|
1166
|
+
return /* @__PURE__ */ jsx13("div", { className: "pb-4", children: /* @__PURE__ */ jsxs7(PromptInputProvider, { children: [
|
|
1167
|
+
/* @__PURE__ */ jsxs7(PromptInput, { globalDrop: true, multiple: true, onSubmit: handleSubmit, children: [
|
|
1168
|
+
/* @__PURE__ */ jsx13(PromptInputAttachmentsDisplay, {}),
|
|
1169
|
+
/* @__PURE__ */ jsx13(PromptInputBody, { children: /* @__PURE__ */ jsx13(
|
|
1170
|
+
PromptInputTextarea,
|
|
1171
|
+
{
|
|
1172
|
+
placeholder: placeholder ?? locale.placeholder
|
|
1173
|
+
}
|
|
1174
|
+
) }),
|
|
1175
|
+
/* @__PURE__ */ jsxs7(PromptInputFooter, { children: [
|
|
1176
|
+
/* @__PURE__ */ jsx13(PromptInputTools, {}),
|
|
1177
|
+
/* @__PURE__ */ jsx13(PromptInputSubmit, { status, onStop })
|
|
1178
|
+
] })
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ jsx13("div", { className: "text-center text-xs opacity-50 mt-3", children: locale.disclaimer })
|
|
1181
|
+
] }) });
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// src/components/chat/Chat.tsx
|
|
1185
|
+
import { useChat } from "@cloudbase/agent-react-core";
|
|
1186
|
+
|
|
1187
|
+
// src/components/ui/button-group.tsx
|
|
1188
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
1189
|
+
import { Slot as Slot2 } from "radix-ui";
|
|
1190
|
+
|
|
1191
|
+
// src/components/ui/separator.tsx
|
|
1192
|
+
import { Separator as SeparatorPrimitive } from "radix-ui";
|
|
1193
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1194
|
+
|
|
1195
|
+
// src/components/ui/button-group.tsx
|
|
1196
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1197
|
+
var buttonGroupVariants = cva3(
|
|
1198
|
+
"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
|
|
1199
|
+
{
|
|
1200
|
+
variants: {
|
|
1201
|
+
orientation: {
|
|
1202
|
+
horizontal: "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
|
|
1203
|
+
vertical: "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none"
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
defaultVariants: {
|
|
1207
|
+
orientation: "horizontal"
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
);
|
|
1211
|
+
|
|
1212
|
+
// src/components/ui/tooltip.tsx
|
|
1213
|
+
import { Tooltip as TooltipPrimitive } from "radix-ui";
|
|
1214
|
+
import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1215
|
+
function TooltipProvider({
|
|
1216
|
+
delayDuration = 0,
|
|
1217
|
+
...props
|
|
1218
|
+
}) {
|
|
1219
|
+
return /* @__PURE__ */ jsx16(
|
|
1220
|
+
TooltipPrimitive.Provider,
|
|
1221
|
+
{
|
|
1222
|
+
"data-slot": "tooltip-provider",
|
|
1223
|
+
delayDuration,
|
|
1224
|
+
...props
|
|
1225
|
+
}
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
function Tooltip({
|
|
1229
|
+
...props
|
|
1230
|
+
}) {
|
|
1231
|
+
return /* @__PURE__ */ jsx16(TooltipProvider, { children: /* @__PURE__ */ jsx16(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) });
|
|
1232
|
+
}
|
|
1233
|
+
function TooltipTrigger({
|
|
1234
|
+
...props
|
|
1235
|
+
}) {
|
|
1236
|
+
return /* @__PURE__ */ jsx16(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
1237
|
+
}
|
|
1238
|
+
function TooltipContent({
|
|
1239
|
+
className,
|
|
1240
|
+
sideOffset = 0,
|
|
1241
|
+
children,
|
|
1242
|
+
...props
|
|
1243
|
+
}) {
|
|
1244
|
+
return /* @__PURE__ */ jsx16(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs8(
|
|
1245
|
+
TooltipPrimitive.Content,
|
|
1246
|
+
{
|
|
1247
|
+
"data-slot": "tooltip-content",
|
|
1248
|
+
sideOffset,
|
|
1249
|
+
className: cn(
|
|
1250
|
+
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
1251
|
+
className
|
|
1252
|
+
),
|
|
1253
|
+
...props,
|
|
1254
|
+
children: [
|
|
1255
|
+
children,
|
|
1256
|
+
/* @__PURE__ */ jsx16(TooltipPrimitive.Arrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
|
|
1257
|
+
]
|
|
1258
|
+
}
|
|
1259
|
+
) });
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// src/components/ai-elements/message.tsx
|
|
1263
|
+
import { cjk } from "@streamdown/cjk";
|
|
1264
|
+
import { code } from "@streamdown/code";
|
|
1265
|
+
import { math } from "@streamdown/math";
|
|
1266
|
+
import { mermaid } from "@streamdown/mermaid";
|
|
1267
|
+
import { ChevronLeftIcon, ChevronRightIcon as ChevronRightIcon2 } from "lucide-react";
|
|
1268
|
+
import { createContext as createContext4, memo, useContext as useContext4, useEffect as useEffect2, useState as useState2 } from "react";
|
|
1269
|
+
import { Streamdown } from "streamdown";
|
|
1270
|
+
import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1271
|
+
var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx17(
|
|
1272
|
+
"div",
|
|
1273
|
+
{
|
|
1274
|
+
className: cn(
|
|
1275
|
+
"group flex w-full max-w-[95%] flex-col gap-2",
|
|
1276
|
+
from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
|
|
1277
|
+
className
|
|
1278
|
+
),
|
|
1279
|
+
...props
|
|
1280
|
+
}
|
|
1281
|
+
);
|
|
1282
|
+
var MessageContent = ({
|
|
1283
|
+
children,
|
|
1284
|
+
className,
|
|
1285
|
+
...props
|
|
1286
|
+
}) => /* @__PURE__ */ jsx17(
|
|
1287
|
+
"div",
|
|
1288
|
+
{
|
|
1289
|
+
className: cn(
|
|
1290
|
+
"is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",
|
|
1291
|
+
"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
|
|
1292
|
+
"group-[.is-assistant]:text-foreground",
|
|
1293
|
+
className
|
|
1294
|
+
),
|
|
1295
|
+
...props,
|
|
1296
|
+
children
|
|
1297
|
+
}
|
|
1298
|
+
);
|
|
1299
|
+
var MessageActions = ({
|
|
1300
|
+
className,
|
|
1301
|
+
children,
|
|
1302
|
+
...props
|
|
1303
|
+
}) => /* @__PURE__ */ jsx17("div", { className: cn("flex items-center gap-1", className), ...props, children });
|
|
1304
|
+
var MessageAction = ({
|
|
1305
|
+
tooltip,
|
|
1306
|
+
children,
|
|
1307
|
+
label,
|
|
1308
|
+
variant = "ghost",
|
|
1309
|
+
size = "icon-sm",
|
|
1310
|
+
...props
|
|
1311
|
+
}) => {
|
|
1312
|
+
const button = /* @__PURE__ */ jsxs9(Button, { size, type: "button", variant, ...props, children: [
|
|
1313
|
+
children,
|
|
1314
|
+
/* @__PURE__ */ jsx17("span", { className: "sr-only", children: label || tooltip })
|
|
1315
|
+
] });
|
|
1316
|
+
if (tooltip) {
|
|
1317
|
+
return /* @__PURE__ */ jsx17(TooltipProvider, { children: /* @__PURE__ */ jsxs9(Tooltip, { children: [
|
|
1318
|
+
/* @__PURE__ */ jsx17(TooltipTrigger, { asChild: true, children: button }),
|
|
1319
|
+
/* @__PURE__ */ jsx17(TooltipContent, { children: /* @__PURE__ */ jsx17("p", { children: tooltip }) })
|
|
1320
|
+
] }) });
|
|
1321
|
+
}
|
|
1322
|
+
return button;
|
|
1323
|
+
};
|
|
1324
|
+
var MessageBranchContext = createContext4(
|
|
1325
|
+
null
|
|
1326
|
+
);
|
|
1327
|
+
var MessageResponse = memo(
|
|
1328
|
+
({ className, ...props }) => /* @__PURE__ */ jsx17(
|
|
1329
|
+
Streamdown,
|
|
1330
|
+
{
|
|
1331
|
+
className: cn(
|
|
1332
|
+
"size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
|
|
1333
|
+
className
|
|
1334
|
+
),
|
|
1335
|
+
plugins: { code, mermaid, math, cjk },
|
|
1336
|
+
...props
|
|
1337
|
+
}
|
|
1338
|
+
),
|
|
1339
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children
|
|
1340
|
+
);
|
|
1341
|
+
MessageResponse.displayName = "MessageResponse";
|
|
1342
|
+
|
|
1343
|
+
// src/components/ui/badge.tsx
|
|
1344
|
+
import { cva as cva4 } from "class-variance-authority";
|
|
1345
|
+
import { Slot as Slot3 } from "radix-ui";
|
|
1346
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1347
|
+
var badgeVariants = cva4(
|
|
1348
|
+
"inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
|
1349
|
+
{
|
|
1350
|
+
variants: {
|
|
1351
|
+
variant: {
|
|
1352
|
+
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
1353
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
1354
|
+
destructive: "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
1355
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
1356
|
+
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
1357
|
+
link: "text-primary underline-offset-4 [a&]:hover:underline"
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
defaultVariants: {
|
|
1361
|
+
variant: "default"
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
);
|
|
1365
|
+
function Badge({
|
|
1366
|
+
className,
|
|
1367
|
+
variant = "default",
|
|
1368
|
+
asChild = false,
|
|
1369
|
+
...props
|
|
1370
|
+
}) {
|
|
1371
|
+
const Comp = asChild ? Slot3.Root : "span";
|
|
1372
|
+
return /* @__PURE__ */ jsx18(
|
|
1373
|
+
Comp,
|
|
1374
|
+
{
|
|
1375
|
+
"data-slot": "badge",
|
|
1376
|
+
"data-variant": variant,
|
|
1377
|
+
className: cn(badgeVariants({ variant }), className),
|
|
1378
|
+
...props
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// src/components/ui/collapsible.tsx
|
|
1384
|
+
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
|
1385
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
1386
|
+
function Collapsible({
|
|
1387
|
+
...props
|
|
1388
|
+
}) {
|
|
1389
|
+
return /* @__PURE__ */ jsx19(CollapsiblePrimitive.Root, { "data-slot": "collapsible", ...props });
|
|
1390
|
+
}
|
|
1391
|
+
function CollapsibleTrigger({
|
|
1392
|
+
...props
|
|
1393
|
+
}) {
|
|
1394
|
+
return /* @__PURE__ */ jsx19(
|
|
1395
|
+
CollapsiblePrimitive.CollapsibleTrigger,
|
|
1396
|
+
{
|
|
1397
|
+
"data-slot": "collapsible-trigger",
|
|
1398
|
+
...props
|
|
1399
|
+
}
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
function CollapsibleContent({
|
|
1403
|
+
...props
|
|
1404
|
+
}) {
|
|
1405
|
+
return /* @__PURE__ */ jsx19(
|
|
1406
|
+
CollapsiblePrimitive.CollapsibleContent,
|
|
1407
|
+
{
|
|
1408
|
+
"data-slot": "collapsible-content",
|
|
1409
|
+
...props
|
|
1410
|
+
}
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// src/components/ai-elements/tool.tsx
|
|
1415
|
+
import {
|
|
1416
|
+
CheckCircleIcon,
|
|
1417
|
+
ChevronDownIcon as ChevronDownIcon2,
|
|
1418
|
+
CircleIcon as CircleIcon2,
|
|
1419
|
+
ClockIcon,
|
|
1420
|
+
WrenchIcon,
|
|
1421
|
+
XCircleIcon
|
|
1422
|
+
} from "lucide-react";
|
|
1423
|
+
import { isValidElement } from "react";
|
|
1424
|
+
|
|
1425
|
+
// src/components/ai-elements/code-block.tsx
|
|
1426
|
+
import { CheckIcon as CheckIcon3, CopyIcon } from "lucide-react";
|
|
1427
|
+
import {
|
|
1428
|
+
createContext as createContext5,
|
|
1429
|
+
memo as memo2,
|
|
1430
|
+
useContext as useContext5,
|
|
1431
|
+
useEffect as useEffect3,
|
|
1432
|
+
useMemo as useMemo4,
|
|
1433
|
+
useRef as useRef2,
|
|
1434
|
+
useState as useState3
|
|
1435
|
+
} from "react";
|
|
1436
|
+
import {
|
|
1437
|
+
createHighlighter
|
|
1438
|
+
} from "shiki";
|
|
1439
|
+
import { jsx as jsx20, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1440
|
+
var isItalic = (fontStyle) => fontStyle && fontStyle & 1;
|
|
1441
|
+
var isBold = (fontStyle) => fontStyle && fontStyle & 2;
|
|
1442
|
+
var isUnderline = (fontStyle) => (
|
|
1443
|
+
// biome-ignore lint/suspicious/noBitwiseOperators: shiki bitflag check
|
|
1444
|
+
fontStyle && fontStyle & 4
|
|
1445
|
+
);
|
|
1446
|
+
var addKeysToTokens = (lines) => lines.map((line, lineIdx) => ({
|
|
1447
|
+
key: `line-${lineIdx}`,
|
|
1448
|
+
tokens: line.map((token, tokenIdx) => ({
|
|
1449
|
+
token,
|
|
1450
|
+
key: `line-${lineIdx}-${tokenIdx}`
|
|
1451
|
+
}))
|
|
1452
|
+
}));
|
|
1453
|
+
var TokenSpan = ({ token }) => /* @__PURE__ */ jsx20(
|
|
1454
|
+
"span",
|
|
1455
|
+
{
|
|
1456
|
+
className: "dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)]",
|
|
1457
|
+
style: {
|
|
1458
|
+
color: token.color,
|
|
1459
|
+
backgroundColor: token.bgColor,
|
|
1460
|
+
...token.htmlStyle,
|
|
1461
|
+
fontStyle: isItalic(token.fontStyle) ? "italic" : void 0,
|
|
1462
|
+
fontWeight: isBold(token.fontStyle) ? "bold" : void 0,
|
|
1463
|
+
textDecoration: isUnderline(token.fontStyle) ? "underline" : void 0
|
|
1464
|
+
},
|
|
1465
|
+
children: token.content
|
|
1466
|
+
}
|
|
1467
|
+
);
|
|
1468
|
+
var LineSpan = ({
|
|
1469
|
+
keyedLine,
|
|
1470
|
+
showLineNumbers
|
|
1471
|
+
}) => /* @__PURE__ */ jsx20("span", { className: showLineNumbers ? LINE_NUMBER_CLASSES : "block", children: keyedLine.tokens.length === 0 ? "\n" : keyedLine.tokens.map(({ token, key }) => /* @__PURE__ */ jsx20(TokenSpan, { token }, key)) });
|
|
1472
|
+
var CodeBlockContext = createContext5({
|
|
1473
|
+
code: ""
|
|
1474
|
+
});
|
|
1475
|
+
var highlighterCache = /* @__PURE__ */ new Map();
|
|
1476
|
+
var tokensCache = /* @__PURE__ */ new Map();
|
|
1477
|
+
var subscribers = /* @__PURE__ */ new Map();
|
|
1478
|
+
var getTokensCacheKey = (code2, language) => {
|
|
1479
|
+
const start = code2.slice(0, 100);
|
|
1480
|
+
const end = code2.length > 100 ? code2.slice(-100) : "";
|
|
1481
|
+
return `${language}:${code2.length}:${start}:${end}`;
|
|
1482
|
+
};
|
|
1483
|
+
var getHighlighter = (language) => {
|
|
1484
|
+
const cached = highlighterCache.get(language);
|
|
1485
|
+
if (cached) {
|
|
1486
|
+
return cached;
|
|
1487
|
+
}
|
|
1488
|
+
const highlighterPromise = createHighlighter({
|
|
1489
|
+
themes: ["github-light", "github-dark"],
|
|
1490
|
+
langs: [language]
|
|
1491
|
+
});
|
|
1492
|
+
highlighterCache.set(language, highlighterPromise);
|
|
1493
|
+
return highlighterPromise;
|
|
1494
|
+
};
|
|
1495
|
+
var createRawTokens = (code2) => ({
|
|
1496
|
+
tokens: code2.split("\n").map(
|
|
1497
|
+
(line) => line === "" ? [] : [
|
|
1498
|
+
{
|
|
1499
|
+
content: line,
|
|
1500
|
+
color: "inherit"
|
|
1501
|
+
}
|
|
1502
|
+
]
|
|
1503
|
+
),
|
|
1504
|
+
fg: "inherit",
|
|
1505
|
+
bg: "transparent"
|
|
1506
|
+
});
|
|
1507
|
+
function highlightCode(code2, language, callback) {
|
|
1508
|
+
const tokensCacheKey = getTokensCacheKey(code2, language);
|
|
1509
|
+
const cached = tokensCache.get(tokensCacheKey);
|
|
1510
|
+
if (cached) {
|
|
1511
|
+
return cached;
|
|
1512
|
+
}
|
|
1513
|
+
if (callback) {
|
|
1514
|
+
if (!subscribers.has(tokensCacheKey)) {
|
|
1515
|
+
subscribers.set(tokensCacheKey, /* @__PURE__ */ new Set());
|
|
1516
|
+
}
|
|
1517
|
+
subscribers.get(tokensCacheKey)?.add(callback);
|
|
1518
|
+
}
|
|
1519
|
+
getHighlighter(language).then((highlighter) => {
|
|
1520
|
+
const availableLangs = highlighter.getLoadedLanguages();
|
|
1521
|
+
const langToUse = availableLangs.includes(language) ? language : "text";
|
|
1522
|
+
const result = highlighter.codeToTokens(code2, {
|
|
1523
|
+
lang: langToUse,
|
|
1524
|
+
themes: {
|
|
1525
|
+
light: "github-light",
|
|
1526
|
+
dark: "github-dark"
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
const tokenized = {
|
|
1530
|
+
tokens: result.tokens,
|
|
1531
|
+
fg: result.fg ?? "inherit",
|
|
1532
|
+
bg: result.bg ?? "transparent"
|
|
1533
|
+
};
|
|
1534
|
+
tokensCache.set(tokensCacheKey, tokenized);
|
|
1535
|
+
const subs = subscribers.get(tokensCacheKey);
|
|
1536
|
+
if (subs) {
|
|
1537
|
+
for (const sub of subs) {
|
|
1538
|
+
sub(tokenized);
|
|
1539
|
+
}
|
|
1540
|
+
subscribers.delete(tokensCacheKey);
|
|
1541
|
+
}
|
|
1542
|
+
}).catch((error) => {
|
|
1543
|
+
console.error("Failed to highlight code:", error);
|
|
1544
|
+
subscribers.delete(tokensCacheKey);
|
|
1545
|
+
});
|
|
1546
|
+
return null;
|
|
1547
|
+
}
|
|
1548
|
+
var LINE_NUMBER_CLASSES = cn(
|
|
1549
|
+
"block",
|
|
1550
|
+
"before:content-[counter(line)]",
|
|
1551
|
+
"before:inline-block",
|
|
1552
|
+
"before:[counter-increment:line]",
|
|
1553
|
+
"before:w-8",
|
|
1554
|
+
"before:mr-4",
|
|
1555
|
+
"before:text-right",
|
|
1556
|
+
"before:text-muted-foreground/50",
|
|
1557
|
+
"before:font-mono",
|
|
1558
|
+
"before:select-none"
|
|
1559
|
+
);
|
|
1560
|
+
var CodeBlockBody = memo2(
|
|
1561
|
+
({
|
|
1562
|
+
tokenized,
|
|
1563
|
+
showLineNumbers,
|
|
1564
|
+
className
|
|
1565
|
+
}) => {
|
|
1566
|
+
const preStyle = useMemo4(
|
|
1567
|
+
() => ({
|
|
1568
|
+
backgroundColor: tokenized.bg,
|
|
1569
|
+
color: tokenized.fg
|
|
1570
|
+
}),
|
|
1571
|
+
[tokenized.bg, tokenized.fg]
|
|
1572
|
+
);
|
|
1573
|
+
const keyedLines = useMemo4(
|
|
1574
|
+
() => addKeysToTokens(tokenized.tokens),
|
|
1575
|
+
[tokenized.tokens]
|
|
1576
|
+
);
|
|
1577
|
+
return /* @__PURE__ */ jsx20(
|
|
1578
|
+
"pre",
|
|
1579
|
+
{
|
|
1580
|
+
className: cn(
|
|
1581
|
+
"dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)] m-0 p-4 text-sm",
|
|
1582
|
+
className
|
|
1583
|
+
),
|
|
1584
|
+
style: preStyle,
|
|
1585
|
+
children: /* @__PURE__ */ jsx20(
|
|
1586
|
+
"code",
|
|
1587
|
+
{
|
|
1588
|
+
className: cn(
|
|
1589
|
+
"font-mono text-sm",
|
|
1590
|
+
showLineNumbers && "[counter-increment:line_0] [counter-reset:line]"
|
|
1591
|
+
),
|
|
1592
|
+
children: keyedLines.map((keyedLine) => /* @__PURE__ */ jsx20(
|
|
1593
|
+
LineSpan,
|
|
1594
|
+
{
|
|
1595
|
+
keyedLine,
|
|
1596
|
+
showLineNumbers
|
|
1597
|
+
},
|
|
1598
|
+
keyedLine.key
|
|
1599
|
+
))
|
|
1600
|
+
}
|
|
1601
|
+
)
|
|
1602
|
+
}
|
|
1603
|
+
);
|
|
1604
|
+
},
|
|
1605
|
+
(prevProps, nextProps) => prevProps.tokenized === nextProps.tokenized && prevProps.showLineNumbers === nextProps.showLineNumbers && prevProps.className === nextProps.className
|
|
1606
|
+
);
|
|
1607
|
+
var CodeBlockContainer = ({
|
|
1608
|
+
className,
|
|
1609
|
+
language,
|
|
1610
|
+
style,
|
|
1611
|
+
...props
|
|
1612
|
+
}) => /* @__PURE__ */ jsx20(
|
|
1613
|
+
"div",
|
|
1614
|
+
{
|
|
1615
|
+
className: cn(
|
|
1616
|
+
"group relative w-full overflow-hidden rounded-md border bg-background text-foreground",
|
|
1617
|
+
className
|
|
1618
|
+
),
|
|
1619
|
+
"data-language": language,
|
|
1620
|
+
style: {
|
|
1621
|
+
contentVisibility: "auto",
|
|
1622
|
+
containIntrinsicSize: "auto 200px",
|
|
1623
|
+
...style
|
|
1624
|
+
},
|
|
1625
|
+
...props
|
|
1626
|
+
}
|
|
1627
|
+
);
|
|
1628
|
+
var CodeBlockContent = ({
|
|
1629
|
+
code: code2,
|
|
1630
|
+
language,
|
|
1631
|
+
showLineNumbers = false
|
|
1632
|
+
}) => {
|
|
1633
|
+
const rawTokens = useMemo4(() => createRawTokens(code2), [code2]);
|
|
1634
|
+
const [tokenized, setTokenized] = useState3(
|
|
1635
|
+
() => highlightCode(code2, language) ?? rawTokens
|
|
1636
|
+
);
|
|
1637
|
+
useEffect3(() => {
|
|
1638
|
+
setTokenized(highlightCode(code2, language) ?? rawTokens);
|
|
1639
|
+
highlightCode(code2, language, setTokenized);
|
|
1640
|
+
}, [code2, language, rawTokens]);
|
|
1641
|
+
return /* @__PURE__ */ jsx20("div", { className: "relative overflow-auto", children: /* @__PURE__ */ jsx20(CodeBlockBody, { showLineNumbers, tokenized }) });
|
|
1642
|
+
};
|
|
1643
|
+
var CodeBlock = ({
|
|
1644
|
+
code: code2,
|
|
1645
|
+
language,
|
|
1646
|
+
showLineNumbers = false,
|
|
1647
|
+
className,
|
|
1648
|
+
children,
|
|
1649
|
+
...props
|
|
1650
|
+
}) => /* @__PURE__ */ jsx20(CodeBlockContext.Provider, { value: { code: code2 }, children: /* @__PURE__ */ jsxs10(CodeBlockContainer, { className, language, ...props, children: [
|
|
1651
|
+
children,
|
|
1652
|
+
/* @__PURE__ */ jsx20(
|
|
1653
|
+
CodeBlockContent,
|
|
1654
|
+
{
|
|
1655
|
+
code: code2,
|
|
1656
|
+
language,
|
|
1657
|
+
showLineNumbers
|
|
1658
|
+
}
|
|
1659
|
+
)
|
|
1660
|
+
] }) });
|
|
1661
|
+
|
|
1662
|
+
// src/components/ai-elements/tool.tsx
|
|
1663
|
+
import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1664
|
+
var Tool = ({ className, ...props }) => /* @__PURE__ */ jsx21(
|
|
1665
|
+
Collapsible,
|
|
1666
|
+
{
|
|
1667
|
+
className: cn("group not-prose mb-4 w-full rounded-md border", className),
|
|
1668
|
+
...props
|
|
1669
|
+
}
|
|
1670
|
+
);
|
|
1671
|
+
var getStatusBadge = (status) => {
|
|
1672
|
+
const labels = {
|
|
1673
|
+
"input-streaming": "Pending",
|
|
1674
|
+
"input-available": "Running",
|
|
1675
|
+
"approval-requested": "Awaiting Approval",
|
|
1676
|
+
"approval-responded": "Responded",
|
|
1677
|
+
"output-available": "Completed",
|
|
1678
|
+
"output-error": "Error",
|
|
1679
|
+
"output-denied": "Denied"
|
|
1680
|
+
};
|
|
1681
|
+
const icons = {
|
|
1682
|
+
"input-streaming": /* @__PURE__ */ jsx21(CircleIcon2, { className: "size-4" }),
|
|
1683
|
+
"input-available": /* @__PURE__ */ jsx21(ClockIcon, { className: "size-4 animate-pulse" }),
|
|
1684
|
+
"approval-requested": /* @__PURE__ */ jsx21(ClockIcon, { className: "size-4 text-yellow-600" }),
|
|
1685
|
+
"approval-responded": /* @__PURE__ */ jsx21(CheckCircleIcon, { className: "size-4 text-blue-600" }),
|
|
1686
|
+
"output-available": /* @__PURE__ */ jsx21(CheckCircleIcon, { className: "size-4 text-green-600" }),
|
|
1687
|
+
"output-error": /* @__PURE__ */ jsx21(XCircleIcon, { className: "size-4 text-red-600" }),
|
|
1688
|
+
"output-denied": /* @__PURE__ */ jsx21(XCircleIcon, { className: "size-4 text-orange-600" })
|
|
1689
|
+
};
|
|
1690
|
+
return /* @__PURE__ */ jsxs11(Badge, { className: "gap-1.5 rounded-full text-xs", variant: "secondary", children: [
|
|
1691
|
+
icons[status],
|
|
1692
|
+
labels[status]
|
|
1693
|
+
] });
|
|
1694
|
+
};
|
|
1695
|
+
var ToolHeader = ({
|
|
1696
|
+
className,
|
|
1697
|
+
title,
|
|
1698
|
+
type,
|
|
1699
|
+
state,
|
|
1700
|
+
toolName,
|
|
1701
|
+
...props
|
|
1702
|
+
}) => {
|
|
1703
|
+
const derivedName = type === "dynamic-tool" ? toolName : type.split("-").slice(1).join("-");
|
|
1704
|
+
return /* @__PURE__ */ jsxs11(
|
|
1705
|
+
CollapsibleTrigger,
|
|
1706
|
+
{
|
|
1707
|
+
className: cn(
|
|
1708
|
+
"flex w-full items-center justify-between gap-4 p-3",
|
|
1709
|
+
className
|
|
1710
|
+
),
|
|
1711
|
+
...props,
|
|
1712
|
+
children: [
|
|
1713
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
|
|
1714
|
+
/* @__PURE__ */ jsx21(WrenchIcon, { className: "size-4 text-muted-foreground" }),
|
|
1715
|
+
/* @__PURE__ */ jsx21("span", { className: "font-medium text-sm", children: title ?? derivedName }),
|
|
1716
|
+
getStatusBadge(state)
|
|
1717
|
+
] }),
|
|
1718
|
+
/* @__PURE__ */ jsx21(ChevronDownIcon2, { className: "size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })
|
|
1719
|
+
]
|
|
1720
|
+
}
|
|
1721
|
+
);
|
|
1722
|
+
};
|
|
1723
|
+
var ToolContent = ({ className, ...props }) => /* @__PURE__ */ jsx21(
|
|
1724
|
+
CollapsibleContent,
|
|
1725
|
+
{
|
|
1726
|
+
className: cn(
|
|
1727
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 p-4 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1728
|
+
className
|
|
1729
|
+
),
|
|
1730
|
+
...props
|
|
1731
|
+
}
|
|
1732
|
+
);
|
|
1733
|
+
var ToolInput = ({ className, input, ...props }) => /* @__PURE__ */ jsxs11("div", { className: cn("space-y-2 overflow-hidden", className), ...props, children: [
|
|
1734
|
+
/* @__PURE__ */ jsx21("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: "Parameters" }),
|
|
1735
|
+
/* @__PURE__ */ jsx21("div", { className: "rounded-md bg-muted/50", children: /* @__PURE__ */ jsx21(CodeBlock, { code: JSON.stringify(input, null, 2), language: "json" }) })
|
|
1736
|
+
] });
|
|
1737
|
+
var ToolOutput = ({
|
|
1738
|
+
className,
|
|
1739
|
+
output,
|
|
1740
|
+
errorText,
|
|
1741
|
+
...props
|
|
1742
|
+
}) => {
|
|
1743
|
+
if (!(output || errorText)) {
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
let Output = /* @__PURE__ */ jsx21("div", { children: output });
|
|
1747
|
+
if (typeof output === "object" && !isValidElement(output)) {
|
|
1748
|
+
Output = /* @__PURE__ */ jsx21(CodeBlock, { code: JSON.stringify(output, null, 2), language: "json" });
|
|
1749
|
+
} else if (typeof output === "string") {
|
|
1750
|
+
Output = /* @__PURE__ */ jsx21(CodeBlock, { code: output, language: "json" });
|
|
1751
|
+
}
|
|
1752
|
+
return /* @__PURE__ */ jsxs11("div", { className: cn("space-y-2", className), ...props, children: [
|
|
1753
|
+
/* @__PURE__ */ jsx21("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: errorText ? "Error" : "Result" }),
|
|
1754
|
+
/* @__PURE__ */ jsxs11(
|
|
1755
|
+
"div",
|
|
1756
|
+
{
|
|
1757
|
+
className: cn(
|
|
1758
|
+
"overflow-x-auto rounded-md text-xs [&_table]:w-full",
|
|
1759
|
+
errorText ? "bg-destructive/10 text-destructive" : "bg-muted/50 text-foreground"
|
|
1760
|
+
),
|
|
1761
|
+
children: [
|
|
1762
|
+
errorText && /* @__PURE__ */ jsx21("div", { children: errorText }),
|
|
1763
|
+
Output
|
|
1764
|
+
]
|
|
1765
|
+
}
|
|
1766
|
+
)
|
|
1767
|
+
] });
|
|
1768
|
+
};
|
|
1769
|
+
|
|
1770
|
+
// src/hooks/use-copy-to-clipboard.ts
|
|
1771
|
+
import { useState as useState4 } from "react";
|
|
1772
|
+
function useCopyToClipboard({
|
|
1773
|
+
timeout = 2e3
|
|
1774
|
+
} = {}) {
|
|
1775
|
+
const [isCopied, setIsCopied] = useState4(false);
|
|
1776
|
+
const copyToClipboard = (value) => {
|
|
1777
|
+
if (typeof window === "undefined" || !navigator.clipboard?.writeText) {
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
if (!value) {
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
navigator.clipboard.writeText(value).then(() => {
|
|
1784
|
+
setIsCopied(true);
|
|
1785
|
+
setTimeout(() => {
|
|
1786
|
+
setIsCopied(false);
|
|
1787
|
+
}, timeout);
|
|
1788
|
+
});
|
|
1789
|
+
};
|
|
1790
|
+
return { isCopied, copyToClipboard };
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
// src/components/chat/Message.tsx
|
|
1794
|
+
import { CheckIcon as CheckIcon4, CopyIcon as CopyIcon2 } from "lucide-react";
|
|
1795
|
+
import { memo as memo3 } from "react";
|
|
1796
|
+
import { jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1797
|
+
var CopyAction = memo3(({ content }) => {
|
|
1798
|
+
const { locale } = useLocale("message");
|
|
1799
|
+
const { isCopied, copyToClipboard } = useCopyToClipboard();
|
|
1800
|
+
return /* @__PURE__ */ jsx22(
|
|
1801
|
+
MessageAction,
|
|
1802
|
+
{
|
|
1803
|
+
label: locale.copy,
|
|
1804
|
+
onClick: () => copyToClipboard(content),
|
|
1805
|
+
tooltip: isCopied ? locale.copied : locale.copyToClipboard,
|
|
1806
|
+
children: isCopied ? /* @__PURE__ */ jsx22(CheckIcon4, { className: "size-4" }) : /* @__PURE__ */ jsx22(CopyIcon2, { className: "size-4" })
|
|
1807
|
+
}
|
|
1808
|
+
);
|
|
1809
|
+
});
|
|
1810
|
+
CopyAction.displayName = "CopyAction";
|
|
1811
|
+
var mapToolStatus = (status) => {
|
|
1812
|
+
const statusMap = {
|
|
1813
|
+
pending: "input-streaming",
|
|
1814
|
+
ready: "input-available",
|
|
1815
|
+
executing: "input-available",
|
|
1816
|
+
completed: "output-available",
|
|
1817
|
+
failed: "output-error"
|
|
1818
|
+
};
|
|
1819
|
+
return statusMap[status];
|
|
1820
|
+
};
|
|
1821
|
+
var ToolCallRenderer = memo3(
|
|
1822
|
+
({ toolPart, actions, onRespond }) => {
|
|
1823
|
+
const action = actions?.[toolPart.name];
|
|
1824
|
+
if (action && (action.type === "client-tool" && action.render || action.type === "tool-call")) {
|
|
1825
|
+
const respond = (result) => {
|
|
1826
|
+
onRespond?.(toolPart.toolCallId, result);
|
|
1827
|
+
};
|
|
1828
|
+
const render = action.render;
|
|
1829
|
+
const rendered = render?.({
|
|
1830
|
+
toolCall: toolPart,
|
|
1831
|
+
respond
|
|
1832
|
+
});
|
|
1833
|
+
if (typeof rendered === "string") {
|
|
1834
|
+
return /* @__PURE__ */ jsx22("div", { children: rendered });
|
|
1835
|
+
}
|
|
1836
|
+
return rendered;
|
|
1837
|
+
}
|
|
1838
|
+
const state = mapToolStatus(toolPart.status);
|
|
1839
|
+
return /* @__PURE__ */ jsxs12(Tool, { children: [
|
|
1840
|
+
/* @__PURE__ */ jsx22(
|
|
1841
|
+
ToolHeader,
|
|
1842
|
+
{
|
|
1843
|
+
type: "dynamic-tool",
|
|
1844
|
+
state,
|
|
1845
|
+
toolName: toolPart.name
|
|
1846
|
+
}
|
|
1847
|
+
),
|
|
1848
|
+
/* @__PURE__ */ jsxs12(ToolContent, { children: [
|
|
1849
|
+
/* @__PURE__ */ jsx22(ToolInput, { input: toolPart.args }),
|
|
1850
|
+
(toolPart.status === "completed" || toolPart.status === "failed") && /* @__PURE__ */ jsx22(
|
|
1851
|
+
ToolOutput,
|
|
1852
|
+
{
|
|
1853
|
+
output: toolPart.result,
|
|
1854
|
+
errorText: toolPart.error?.message
|
|
1855
|
+
}
|
|
1856
|
+
)
|
|
1857
|
+
] })
|
|
1858
|
+
] });
|
|
1859
|
+
}
|
|
1860
|
+
);
|
|
1861
|
+
ToolCallRenderer.displayName = "ToolCallRenderer";
|
|
1862
|
+
function ChatMessage({
|
|
1863
|
+
messages,
|
|
1864
|
+
isLoading = false,
|
|
1865
|
+
actions,
|
|
1866
|
+
onRespond
|
|
1867
|
+
}) {
|
|
1868
|
+
const { locale } = useLocale("message");
|
|
1869
|
+
const visibleMessages = messages.filter(
|
|
1870
|
+
(message) => message.role === "user" || message.role === "assistant"
|
|
1871
|
+
);
|
|
1872
|
+
const lastVisibleMessage = visibleMessages[visibleMessages.length - 1];
|
|
1873
|
+
const shouldRenderAssistantPlaceholder = isLoading && (!lastVisibleMessage || lastVisibleMessage.role === "user");
|
|
1874
|
+
return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-4", children: [
|
|
1875
|
+
visibleMessages.map((message) => {
|
|
1876
|
+
const role = message.role;
|
|
1877
|
+
const isLoadingAssistantMessage = isLoading && role === "assistant" && message.id === lastVisibleMessage?.id;
|
|
1878
|
+
const allTextContent = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
1879
|
+
const hasAssistantText = role === "assistant" && allTextContent.trim().length > 0;
|
|
1880
|
+
const isPausedAssistantNoText = role === "assistant" && Boolean(message.paused) && !hasAssistantText;
|
|
1881
|
+
const copyContent = allTextContent || (isPausedAssistantNoText ? locale.paused : "");
|
|
1882
|
+
return /* @__PURE__ */ jsxs12(Message, { from: role, children: [
|
|
1883
|
+
message.parts.map((part) => {
|
|
1884
|
+
if (part.type === "text") {
|
|
1885
|
+
const textPart = part;
|
|
1886
|
+
return /* @__PURE__ */ jsx22(MessageContent, { children: role === "assistant" ? /* @__PURE__ */ jsx22(MessageResponse, { children: textPart.text }) : textPart.text }, textPart.id);
|
|
1887
|
+
}
|
|
1888
|
+
if (part.type === "tool") {
|
|
1889
|
+
const toolPart = part;
|
|
1890
|
+
return /* @__PURE__ */ jsx22(
|
|
1891
|
+
ToolCallRenderer,
|
|
1892
|
+
{
|
|
1893
|
+
toolPart,
|
|
1894
|
+
actions,
|
|
1895
|
+
onRespond
|
|
1896
|
+
},
|
|
1897
|
+
toolPart.id
|
|
1898
|
+
);
|
|
1899
|
+
}
|
|
1900
|
+
return null;
|
|
1901
|
+
}),
|
|
1902
|
+
isLoadingAssistantMessage && /* @__PURE__ */ jsx22(MessageContent, { children: /* @__PURE__ */ jsx22("div", { className: "inline-flex items-center text-muted-foreground", children: /* @__PURE__ */ jsx22(SvgSpinners3DotsFade, { className: "text-xl" }) }) }),
|
|
1903
|
+
isPausedAssistantNoText && !isLoadingAssistantMessage && /* @__PURE__ */ jsx22(MessageContent, { children: /* @__PURE__ */ jsx22("div", { className: "text-muted-foreground", children: locale.paused }) }),
|
|
1904
|
+
role === "assistant" && copyContent && !isLoadingAssistantMessage && /* @__PURE__ */ jsx22(MessageActions, { children: /* @__PURE__ */ jsx22(CopyAction, { content: copyContent }) })
|
|
1905
|
+
] }, message.id);
|
|
1906
|
+
}),
|
|
1907
|
+
shouldRenderAssistantPlaceholder && /* @__PURE__ */ jsx22(Message, { from: "assistant", children: /* @__PURE__ */ jsx22(MessageContent, { children: /* @__PURE__ */ jsx22("div", { className: "inline-flex items-center text-muted-foreground", children: /* @__PURE__ */ jsx22(SvgSpinners3DotsFade, { className: "text-xl" }) }) }) })
|
|
1908
|
+
] });
|
|
1909
|
+
}
|
|
1910
|
+
function SvgSpinners3DotsFade(props) {
|
|
1911
|
+
return /* @__PURE__ */ jsxs12(
|
|
1912
|
+
"svg",
|
|
1913
|
+
{
|
|
1914
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1915
|
+
width: "1em",
|
|
1916
|
+
height: "1em",
|
|
1917
|
+
viewBox: "0 0 24 24",
|
|
1918
|
+
...props,
|
|
1919
|
+
children: [
|
|
1920
|
+
/* @__PURE__ */ jsx22("circle", { cx: "4", cy: "12", r: "3", fill: "currentColor", children: /* @__PURE__ */ jsx22(
|
|
1921
|
+
"animate",
|
|
1922
|
+
{
|
|
1923
|
+
id: "dotFadeFirst",
|
|
1924
|
+
fill: "freeze",
|
|
1925
|
+
attributeName: "opacity",
|
|
1926
|
+
begin: "0;dotFadeThird.end-0.25s",
|
|
1927
|
+
dur: "0.75s",
|
|
1928
|
+
values: "1;.2"
|
|
1929
|
+
}
|
|
1930
|
+
) }),
|
|
1931
|
+
/* @__PURE__ */ jsx22("circle", { cx: "12", cy: "12", r: "3", fill: "currentColor", opacity: ".4", children: /* @__PURE__ */ jsx22(
|
|
1932
|
+
"animate",
|
|
1933
|
+
{
|
|
1934
|
+
fill: "freeze",
|
|
1935
|
+
attributeName: "opacity",
|
|
1936
|
+
begin: "dotFadeFirst.begin+0.15s",
|
|
1937
|
+
dur: "0.75s",
|
|
1938
|
+
values: "1;.2"
|
|
1939
|
+
}
|
|
1940
|
+
) }),
|
|
1941
|
+
/* @__PURE__ */ jsx22("circle", { cx: "20", cy: "12", r: "3", fill: "currentColor", opacity: ".3", children: /* @__PURE__ */ jsx22(
|
|
1942
|
+
"animate",
|
|
1943
|
+
{
|
|
1944
|
+
id: "dotFadeThird",
|
|
1945
|
+
fill: "freeze",
|
|
1946
|
+
attributeName: "opacity",
|
|
1947
|
+
begin: "dotFadeFirst.begin+0.3s",
|
|
1948
|
+
dur: "0.75s",
|
|
1949
|
+
values: "1;.2"
|
|
1950
|
+
}
|
|
1951
|
+
) })
|
|
1952
|
+
]
|
|
1953
|
+
}
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
// src/components/chat/Chat.tsx
|
|
1958
|
+
import { v4 as uuidv4 } from "uuid";
|
|
1959
|
+
import { useMemo as useMemo5 } from "react";
|
|
1960
|
+
|
|
1961
|
+
// src/components/ui/scroll-area.tsx
|
|
1962
|
+
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
|
|
1963
|
+
import { jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1964
|
+
function ScrollArea({
|
|
1965
|
+
className,
|
|
1966
|
+
children,
|
|
1967
|
+
...props
|
|
1968
|
+
}) {
|
|
1969
|
+
return /* @__PURE__ */ jsxs13(
|
|
1970
|
+
ScrollAreaPrimitive.Root,
|
|
1971
|
+
{
|
|
1972
|
+
"data-slot": "scroll-area",
|
|
1973
|
+
className: cn("relative", className),
|
|
1974
|
+
...props,
|
|
1975
|
+
children: [
|
|
1976
|
+
/* @__PURE__ */ jsx23(
|
|
1977
|
+
ScrollAreaPrimitive.Viewport,
|
|
1978
|
+
{
|
|
1979
|
+
"data-slot": "scroll-area-viewport",
|
|
1980
|
+
className: "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1",
|
|
1981
|
+
children
|
|
1982
|
+
}
|
|
1983
|
+
),
|
|
1984
|
+
/* @__PURE__ */ jsx23(ScrollBar, {}),
|
|
1985
|
+
/* @__PURE__ */ jsx23(ScrollAreaPrimitive.Corner, {})
|
|
1986
|
+
]
|
|
1987
|
+
}
|
|
1988
|
+
);
|
|
1989
|
+
}
|
|
1990
|
+
function ScrollBar({
|
|
1991
|
+
className,
|
|
1992
|
+
orientation = "vertical",
|
|
1993
|
+
...props
|
|
1994
|
+
}) {
|
|
1995
|
+
return /* @__PURE__ */ jsx23(
|
|
1996
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
1997
|
+
{
|
|
1998
|
+
"data-slot": "scroll-area-scrollbar",
|
|
1999
|
+
orientation,
|
|
2000
|
+
className: cn(
|
|
2001
|
+
"flex touch-none p-px transition-colors select-none",
|
|
2002
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
|
|
2003
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
|
|
2004
|
+
className
|
|
2005
|
+
),
|
|
2006
|
+
...props,
|
|
2007
|
+
children: /* @__PURE__ */ jsx23(
|
|
2008
|
+
ScrollAreaPrimitive.ScrollAreaThumb,
|
|
2009
|
+
{
|
|
2010
|
+
"data-slot": "scroll-area-thumb",
|
|
2011
|
+
className: "bg-border relative flex-1 rounded-full"
|
|
2012
|
+
}
|
|
2013
|
+
)
|
|
2014
|
+
}
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
// src/components/ai-elements/suggestion.tsx
|
|
2019
|
+
import { jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2020
|
+
var Suggestions = ({
|
|
2021
|
+
className,
|
|
2022
|
+
children,
|
|
2023
|
+
...props
|
|
2024
|
+
}) => /* @__PURE__ */ jsxs14(ScrollArea, { className: "w-full overflow-x-auto whitespace-nowrap", ...props, children: [
|
|
2025
|
+
/* @__PURE__ */ jsx24("div", { className: cn("flex w-max flex-nowrap items-center gap-2", className), children }),
|
|
2026
|
+
/* @__PURE__ */ jsx24(ScrollBar, { className: "hidden", orientation: "horizontal" })
|
|
2027
|
+
] });
|
|
2028
|
+
var Suggestion = ({
|
|
2029
|
+
suggestion,
|
|
2030
|
+
onClick,
|
|
2031
|
+
className,
|
|
2032
|
+
variant = "outline",
|
|
2033
|
+
size = "sm",
|
|
2034
|
+
children,
|
|
2035
|
+
...props
|
|
2036
|
+
}) => {
|
|
2037
|
+
const handleClick = () => {
|
|
2038
|
+
onClick?.(suggestion);
|
|
2039
|
+
};
|
|
2040
|
+
return /* @__PURE__ */ jsx24(
|
|
2041
|
+
Button,
|
|
2042
|
+
{
|
|
2043
|
+
className: cn("cursor-pointer rounded-full px-4", className),
|
|
2044
|
+
onClick: handleClick,
|
|
2045
|
+
size,
|
|
2046
|
+
type: "button",
|
|
2047
|
+
variant,
|
|
2048
|
+
...props,
|
|
2049
|
+
children: children || suggestion
|
|
2050
|
+
}
|
|
2051
|
+
);
|
|
2052
|
+
};
|
|
2053
|
+
|
|
2054
|
+
// src/components/chat/Chat.tsx
|
|
2055
|
+
import { Fragment as Fragment2, jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2056
|
+
function AgKitUI({
|
|
2057
|
+
className = "h-full min-h-0 mx-auto max-w-225 flex flex-col overflow-hidden px-4",
|
|
2058
|
+
containerClassName,
|
|
2059
|
+
messagesClassName,
|
|
2060
|
+
inputClassName,
|
|
2061
|
+
emptyTitleClassName,
|
|
2062
|
+
inputPlaceholder,
|
|
2063
|
+
suggestions,
|
|
2064
|
+
onSuggestionClick,
|
|
2065
|
+
locale = "zh-CN"
|
|
2066
|
+
}) {
|
|
2067
|
+
const contextValue = useMemo5(() => ({ locale }), [locale]);
|
|
2068
|
+
return /* @__PURE__ */ jsx25(LocaleContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx25(
|
|
2069
|
+
AgKitUIContent,
|
|
2070
|
+
{
|
|
2071
|
+
className,
|
|
2072
|
+
containerClassName,
|
|
2073
|
+
messagesClassName,
|
|
2074
|
+
inputClassName,
|
|
2075
|
+
emptyTitleClassName,
|
|
2076
|
+
inputPlaceholder,
|
|
2077
|
+
suggestions,
|
|
2078
|
+
onSuggestionClick
|
|
2079
|
+
}
|
|
2080
|
+
) });
|
|
2081
|
+
}
|
|
2082
|
+
function AgKitUIContent({
|
|
2083
|
+
className,
|
|
2084
|
+
containerClassName,
|
|
2085
|
+
messagesClassName,
|
|
2086
|
+
inputClassName,
|
|
2087
|
+
emptyTitleClassName,
|
|
2088
|
+
inputPlaceholder,
|
|
2089
|
+
suggestions,
|
|
2090
|
+
onSuggestionClick
|
|
2091
|
+
}) {
|
|
2092
|
+
const { locale } = useLocale("chat");
|
|
2093
|
+
const { uiMessages, streaming, sendMessage, actions, abort } = useChat();
|
|
2094
|
+
const handleRespond = (toolCallId, result) => {
|
|
2095
|
+
sendMessage({
|
|
2096
|
+
id: uuidv4(),
|
|
2097
|
+
role: "tool",
|
|
2098
|
+
content: result,
|
|
2099
|
+
toolCallId
|
|
2100
|
+
});
|
|
2101
|
+
};
|
|
2102
|
+
const visibleMessages = uiMessages.filter(
|
|
2103
|
+
(message) => message.role === "user" || message.role === "assistant"
|
|
2104
|
+
);
|
|
2105
|
+
const isEmpty = visibleMessages.length === 0;
|
|
2106
|
+
const inputNode = /* @__PURE__ */ jsxs15("div", { className: inputClassName, children: [
|
|
2107
|
+
isEmpty && suggestions && suggestions.length > 0 && /* @__PURE__ */ jsx25(Suggestions, { className: "mb-3", children: suggestions.map((suggestion) => suggestion.trim()).filter(Boolean).map((suggestion, index) => /* @__PURE__ */ jsx25(
|
|
2108
|
+
Suggestion,
|
|
2109
|
+
{
|
|
2110
|
+
suggestion,
|
|
2111
|
+
variant: "ghost",
|
|
2112
|
+
className: "max-w-full border border-border/80 text-muted-foreground",
|
|
2113
|
+
disabled: streaming,
|
|
2114
|
+
onClick: (value) => {
|
|
2115
|
+
onSuggestionClick?.(value);
|
|
2116
|
+
sendMessage(value);
|
|
2117
|
+
}
|
|
2118
|
+
},
|
|
2119
|
+
`${suggestion}-${index}`
|
|
2120
|
+
)) }),
|
|
2121
|
+
/* @__PURE__ */ jsx25(
|
|
2122
|
+
Input2,
|
|
2123
|
+
{
|
|
2124
|
+
placeholder: inputPlaceholder,
|
|
2125
|
+
streaming,
|
|
2126
|
+
onStop: () => {
|
|
2127
|
+
if (!streaming) return;
|
|
2128
|
+
abort();
|
|
2129
|
+
},
|
|
2130
|
+
onSend: (message) => {
|
|
2131
|
+
sendMessage(message.text);
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
)
|
|
2135
|
+
] });
|
|
2136
|
+
return /* @__PURE__ */ jsx25(
|
|
2137
|
+
"div",
|
|
2138
|
+
{
|
|
2139
|
+
className: cn(
|
|
2140
|
+
"bg-background text-foreground h-full min-h-0 w-full",
|
|
2141
|
+
containerClassName
|
|
2142
|
+
),
|
|
2143
|
+
children: /* @__PURE__ */ jsx25("div", { className: cn(className), children: isEmpty ? /* @__PURE__ */ jsx25("div", { className: "flex min-h-0 flex-1 items-center justify-center", children: /* @__PURE__ */ jsxs15("div", { className: "w-full", children: [
|
|
2144
|
+
/* @__PURE__ */ jsx25(
|
|
2145
|
+
"div",
|
|
2146
|
+
{
|
|
2147
|
+
className: cn(
|
|
2148
|
+
"mb-5 text-center text-xl font-medium text-foreground",
|
|
2149
|
+
emptyTitleClassName
|
|
2150
|
+
),
|
|
2151
|
+
children: locale.emptyTitle
|
|
2152
|
+
}
|
|
2153
|
+
),
|
|
2154
|
+
inputNode
|
|
2155
|
+
] }) }) : /* @__PURE__ */ jsxs15(Fragment2, { children: [
|
|
2156
|
+
/* @__PURE__ */ jsx25(
|
|
2157
|
+
"div",
|
|
2158
|
+
{
|
|
2159
|
+
className: cn(
|
|
2160
|
+
"min-h-0 flex-1 overflow-y-auto py-4",
|
|
2161
|
+
messagesClassName
|
|
2162
|
+
),
|
|
2163
|
+
children: /* @__PURE__ */ jsx25(
|
|
2164
|
+
ChatMessage,
|
|
2165
|
+
{
|
|
2166
|
+
messages: uiMessages,
|
|
2167
|
+
isLoading: streaming,
|
|
2168
|
+
actions,
|
|
2169
|
+
onRespond: handleRespond
|
|
2170
|
+
}
|
|
2171
|
+
)
|
|
2172
|
+
}
|
|
2173
|
+
),
|
|
2174
|
+
inputNode
|
|
2175
|
+
] }) })
|
|
2176
|
+
}
|
|
2177
|
+
);
|
|
2178
|
+
}
|
|
2179
|
+
export {
|
|
2180
|
+
AgKitUI
|
|
2181
|
+
};
|
|
2182
|
+
//# sourceMappingURL=index.mjs.map
|