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