@rodrigocoliveira/agno-react 1.0.1 → 1.0.2
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 +361 -81
- package/dist/index.js +3 -3
- package/dist/ui.d.mts +891 -0
- package/dist/ui.d.ts +891 -0
- package/dist/ui.js +3625 -0
- package/dist/ui.mjs +3455 -0
- package/package.json +118 -12
package/dist/ui.mjs
ADDED
|
@@ -0,0 +1,3455 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/ui/lib/cn.ts
|
|
4
|
+
import { clsx } from "clsx";
|
|
5
|
+
import { twMerge } from "tailwind-merge";
|
|
6
|
+
function cn(...inputs) {
|
|
7
|
+
return twMerge(clsx(inputs));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/ui/lib/format-timestamp.ts
|
|
11
|
+
function formatSmartTimestamp(date) {
|
|
12
|
+
const now = /* @__PURE__ */ new Date();
|
|
13
|
+
const isToday = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
|
|
14
|
+
if (isToday) {
|
|
15
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
16
|
+
hour: "numeric",
|
|
17
|
+
minute: "2-digit",
|
|
18
|
+
hour12: true
|
|
19
|
+
}).format(date);
|
|
20
|
+
}
|
|
21
|
+
const isSameYear = date.getFullYear() === now.getFullYear();
|
|
22
|
+
if (isSameYear) {
|
|
23
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
24
|
+
month: "short",
|
|
25
|
+
day: "numeric"
|
|
26
|
+
}).format(date);
|
|
27
|
+
}
|
|
28
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
29
|
+
month: "short",
|
|
30
|
+
day: "numeric",
|
|
31
|
+
year: "numeric"
|
|
32
|
+
}).format(date);
|
|
33
|
+
}
|
|
34
|
+
function formatFullTimestamp(date) {
|
|
35
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
36
|
+
month: "short",
|
|
37
|
+
day: "numeric",
|
|
38
|
+
year: "numeric",
|
|
39
|
+
hour: "numeric",
|
|
40
|
+
minute: "2-digit",
|
|
41
|
+
hour12: true
|
|
42
|
+
}).format(date);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/ui/lib/file-utils.ts
|
|
46
|
+
function getFilePreviewType(mimeType) {
|
|
47
|
+
if (!mimeType) return "none";
|
|
48
|
+
if (mimeType.startsWith("image/")) return "image";
|
|
49
|
+
if (mimeType === "application/pdf") return "pdf";
|
|
50
|
+
if (mimeType.startsWith("text/")) return "text";
|
|
51
|
+
if (mimeType.startsWith("video/")) return "video";
|
|
52
|
+
if (mimeType.startsWith("audio/")) return "audio";
|
|
53
|
+
return "none";
|
|
54
|
+
}
|
|
55
|
+
function formatFileSize(bytes) {
|
|
56
|
+
if (bytes === 0) return "0 B";
|
|
57
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
58
|
+
const k = 1024;
|
|
59
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
60
|
+
const value = bytes / Math.pow(k, i);
|
|
61
|
+
return `${value % 1 === 0 ? value : value.toFixed(1)} ${units[i]}`;
|
|
62
|
+
}
|
|
63
|
+
function getFileExtension(filename, mimeType) {
|
|
64
|
+
const lastDot = filename.lastIndexOf(".");
|
|
65
|
+
if (lastDot !== -1 && lastDot !== 0) {
|
|
66
|
+
return filename.slice(lastDot + 1).toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
if (mimeType) {
|
|
69
|
+
return extensionFromMime(mimeType);
|
|
70
|
+
}
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
var mimeToExt = {
|
|
74
|
+
"application/pdf": "pdf",
|
|
75
|
+
"application/zip": "zip",
|
|
76
|
+
"application/x-rar-compressed": "rar",
|
|
77
|
+
"application/json": "json",
|
|
78
|
+
"application/xml": "xml",
|
|
79
|
+
"application/msword": "doc",
|
|
80
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
81
|
+
"application/vnd.ms-excel": "xls",
|
|
82
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
|
|
83
|
+
"application/vnd.ms-powerpoint": "ppt",
|
|
84
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
|
|
85
|
+
"text/csv": "csv",
|
|
86
|
+
"text/plain": "txt",
|
|
87
|
+
"text/html": "html",
|
|
88
|
+
"text/css": "css",
|
|
89
|
+
"text/javascript": "js"
|
|
90
|
+
};
|
|
91
|
+
function extensionFromMime(mimeType) {
|
|
92
|
+
if (mimeToExt[mimeType]) return mimeToExt[mimeType];
|
|
93
|
+
const subtype = mimeType.split("/")[1];
|
|
94
|
+
if (subtype && !subtype.includes(".") && !subtype.includes("+")) {
|
|
95
|
+
return subtype.toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
function isPreviewable(mimeType) {
|
|
100
|
+
const type = getFilePreviewType(mimeType);
|
|
101
|
+
return type === "image" || type === "pdf";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/ui/primitives/button.tsx
|
|
105
|
+
import * as React from "react";
|
|
106
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
107
|
+
import { cva } from "class-variance-authority";
|
|
108
|
+
import { jsx } from "react/jsx-runtime";
|
|
109
|
+
var buttonVariants = cva(
|
|
110
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
111
|
+
{
|
|
112
|
+
variants: {
|
|
113
|
+
variant: {
|
|
114
|
+
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
115
|
+
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
116
|
+
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
117
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
118
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
119
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
120
|
+
},
|
|
121
|
+
size: {
|
|
122
|
+
default: "h-9 px-4 py-2",
|
|
123
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
124
|
+
lg: "h-10 rounded-md px-8",
|
|
125
|
+
icon: "h-9 w-9"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
defaultVariants: {
|
|
129
|
+
variant: "default",
|
|
130
|
+
size: "default"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
var Button = React.forwardRef(
|
|
135
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
136
|
+
const Comp = asChild ? Slot : "button";
|
|
137
|
+
return /* @__PURE__ */ jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
Button.displayName = "Button";
|
|
141
|
+
|
|
142
|
+
// src/ui/primitives/badge.tsx
|
|
143
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
144
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
145
|
+
var badgeVariants = cva2(
|
|
146
|
+
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
147
|
+
{
|
|
148
|
+
variants: {
|
|
149
|
+
variant: {
|
|
150
|
+
default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
151
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
152
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
|
153
|
+
outline: "text-foreground"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
defaultVariants: {
|
|
157
|
+
variant: "default"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
function Badge({ className, variant, ...props }) {
|
|
162
|
+
return /* @__PURE__ */ jsx2("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/ui/primitives/avatar.tsx
|
|
166
|
+
import * as React2 from "react";
|
|
167
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
168
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
169
|
+
var Avatar = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
|
|
170
|
+
AvatarPrimitive.Root,
|
|
171
|
+
{
|
|
172
|
+
ref,
|
|
173
|
+
className: cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className),
|
|
174
|
+
...props
|
|
175
|
+
}
|
|
176
|
+
));
|
|
177
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
178
|
+
var AvatarImage = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(AvatarPrimitive.Image, { ref, className: cn("aspect-square h-full w-full", className), ...props }));
|
|
179
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
180
|
+
var AvatarFallback = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
|
|
181
|
+
AvatarPrimitive.Fallback,
|
|
182
|
+
{
|
|
183
|
+
ref,
|
|
184
|
+
className: cn("flex h-full w-full items-center justify-center rounded-full bg-muted", className),
|
|
185
|
+
...props
|
|
186
|
+
}
|
|
187
|
+
));
|
|
188
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
189
|
+
|
|
190
|
+
// src/ui/primitives/input-group.tsx
|
|
191
|
+
import * as React3 from "react";
|
|
192
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
193
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
194
|
+
var Input = React3.forwardRef(
|
|
195
|
+
({ className, type, ...props }, ref) => {
|
|
196
|
+
return /* @__PURE__ */ jsx4(
|
|
197
|
+
"input",
|
|
198
|
+
{
|
|
199
|
+
type,
|
|
200
|
+
className: cn(
|
|
201
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
202
|
+
className
|
|
203
|
+
),
|
|
204
|
+
ref,
|
|
205
|
+
...props
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
Input.displayName = "Input";
|
|
211
|
+
var Textarea = React3.forwardRef(
|
|
212
|
+
({ className, ...props }, ref) => {
|
|
213
|
+
return /* @__PURE__ */ jsx4(
|
|
214
|
+
"textarea",
|
|
215
|
+
{
|
|
216
|
+
className: cn(
|
|
217
|
+
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
218
|
+
className
|
|
219
|
+
),
|
|
220
|
+
ref,
|
|
221
|
+
...props
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
Textarea.displayName = "Textarea";
|
|
227
|
+
function InputGroup({ className, ...props }) {
|
|
228
|
+
return /* @__PURE__ */ jsx4(
|
|
229
|
+
"div",
|
|
230
|
+
{
|
|
231
|
+
"data-slot": "input-group",
|
|
232
|
+
role: "group",
|
|
233
|
+
className: cn(
|
|
234
|
+
"group/input-group border-input dark:bg-input/30 shadow-xs relative flex w-full items-center rounded-md border outline-none transition-[color,box-shadow]",
|
|
235
|
+
"h-9 has-[>textarea]:h-auto",
|
|
236
|
+
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
237
|
+
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
238
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
239
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-2.5",
|
|
240
|
+
"has-[[data-slot=input-group-control]:focus-visible]:ring-ring has-[[data-slot=input-group-control]:focus-visible]:ring-1",
|
|
241
|
+
"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",
|
|
242
|
+
className
|
|
243
|
+
),
|
|
244
|
+
...props
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
var inputGroupAddonVariants = cva3(
|
|
249
|
+
"text-muted-foreground flex h-auto cursor-text select-none items-center justify-center gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
|
|
250
|
+
{
|
|
251
|
+
variants: {
|
|
252
|
+
align: {
|
|
253
|
+
"inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
254
|
+
"inline-end": "order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]",
|
|
255
|
+
"block-start": "[.border-b]:pb-3 order-first w-full justify-start px-3 pt-2 pb-0 group-has-[>input]/input-group:pt-2.5",
|
|
256
|
+
"block-end": "[.border-t]:pt-3 order-last w-full justify-start px-3 pb-1.5 group-has-[>input]/input-group:pb-1.5"
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
defaultVariants: {
|
|
260
|
+
align: "inline-start"
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
function InputGroupAddon({
|
|
265
|
+
className,
|
|
266
|
+
align = "inline-start",
|
|
267
|
+
...props
|
|
268
|
+
}) {
|
|
269
|
+
return /* @__PURE__ */ jsx4(
|
|
270
|
+
"div",
|
|
271
|
+
{
|
|
272
|
+
role: "group",
|
|
273
|
+
"data-slot": "input-group-addon",
|
|
274
|
+
"data-align": align,
|
|
275
|
+
className: cn(inputGroupAddonVariants({ align }), className),
|
|
276
|
+
onClick: (e) => {
|
|
277
|
+
if (e.target.closest("button")) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
281
|
+
},
|
|
282
|
+
...props
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
var inputGroupButtonVariants = cva3("flex items-center gap-2 text-sm shadow-none", {
|
|
287
|
+
variants: {
|
|
288
|
+
size: {
|
|
289
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
|
|
290
|
+
sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
|
|
291
|
+
"icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
|
|
292
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0"
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
defaultVariants: {
|
|
296
|
+
size: "xs"
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
function InputGroupButton({
|
|
300
|
+
className,
|
|
301
|
+
type = "button",
|
|
302
|
+
variant = "ghost",
|
|
303
|
+
size = "xs",
|
|
304
|
+
...props
|
|
305
|
+
}) {
|
|
306
|
+
return /* @__PURE__ */ jsx4(
|
|
307
|
+
Button,
|
|
308
|
+
{
|
|
309
|
+
type,
|
|
310
|
+
"data-size": size,
|
|
311
|
+
variant,
|
|
312
|
+
className: cn(inputGroupButtonVariants({ size }), className),
|
|
313
|
+
...props
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
function InputGroupText({ className, ...props }) {
|
|
318
|
+
return /* @__PURE__ */ jsx4(
|
|
319
|
+
"span",
|
|
320
|
+
{
|
|
321
|
+
className: cn(
|
|
322
|
+
"text-muted-foreground flex items-center gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
|
|
323
|
+
className
|
|
324
|
+
),
|
|
325
|
+
...props
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
function InputGroupInput({ className, ...props }) {
|
|
330
|
+
return /* @__PURE__ */ jsx4(
|
|
331
|
+
Input,
|
|
332
|
+
{
|
|
333
|
+
"data-slot": "input-group-control",
|
|
334
|
+
className: cn("flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent", className),
|
|
335
|
+
...props
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
function InputGroupTextarea({ className, ...props }) {
|
|
340
|
+
return /* @__PURE__ */ jsx4(
|
|
341
|
+
Textarea,
|
|
342
|
+
{
|
|
343
|
+
"data-slot": "input-group-control",
|
|
344
|
+
className: cn(
|
|
345
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent min-h-0 pt-3.5 pb-1.5 pl-3.5 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
346
|
+
className
|
|
347
|
+
),
|
|
348
|
+
...props
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/ui/primitives/collapsible.tsx
|
|
354
|
+
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
|
355
|
+
var Collapsible = CollapsiblePrimitive.Root;
|
|
356
|
+
var CollapsibleTrigger2 = CollapsiblePrimitive.CollapsibleTrigger;
|
|
357
|
+
var CollapsibleContent2 = CollapsiblePrimitive.CollapsibleContent;
|
|
358
|
+
|
|
359
|
+
// src/ui/primitives/tooltip.tsx
|
|
360
|
+
import * as React4 from "react";
|
|
361
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
362
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
363
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
364
|
+
var Tooltip = TooltipPrimitive.Root;
|
|
365
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
366
|
+
var TooltipContent = React4.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx5(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx5(
|
|
367
|
+
TooltipPrimitive.Content,
|
|
368
|
+
{
|
|
369
|
+
ref,
|
|
370
|
+
sideOffset,
|
|
371
|
+
className: cn(
|
|
372
|
+
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground 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 origin-[--radix-tooltip-content-transform-origin]",
|
|
373
|
+
className
|
|
374
|
+
),
|
|
375
|
+
...props
|
|
376
|
+
}
|
|
377
|
+
) }));
|
|
378
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
379
|
+
|
|
380
|
+
// src/ui/primitives/accordion.tsx
|
|
381
|
+
import * as React5 from "react";
|
|
382
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
383
|
+
import { ChevronDown } from "lucide-react";
|
|
384
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
385
|
+
var Accordion = AccordionPrimitive.Root;
|
|
386
|
+
var AccordionItem = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(AccordionPrimitive.Item, { ref, className: cn("border-b", className), ...props }));
|
|
387
|
+
AccordionItem.displayName = "AccordionItem";
|
|
388
|
+
var AccordionTrigger = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx6(AccordionPrimitive.Header, { className: "flex", children: /* @__PURE__ */ jsxs(
|
|
389
|
+
AccordionPrimitive.Trigger,
|
|
390
|
+
{
|
|
391
|
+
ref,
|
|
392
|
+
className: cn(
|
|
393
|
+
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
|
|
394
|
+
className
|
|
395
|
+
),
|
|
396
|
+
...props,
|
|
397
|
+
children: [
|
|
398
|
+
children,
|
|
399
|
+
/* @__PURE__ */ jsx6(ChevronDown, { className: "h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" })
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
) }));
|
|
403
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
404
|
+
var AccordionContent = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
405
|
+
AccordionPrimitive.Content,
|
|
406
|
+
{
|
|
407
|
+
ref,
|
|
408
|
+
className: "overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
|
|
409
|
+
...props,
|
|
410
|
+
children: /* @__PURE__ */ jsx6("div", { className: cn("pb-4 pt-0", className), children })
|
|
411
|
+
}
|
|
412
|
+
));
|
|
413
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
414
|
+
|
|
415
|
+
// src/ui/primitives/dropdown-menu.tsx
|
|
416
|
+
import * as React6 from "react";
|
|
417
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
418
|
+
import { Check, ChevronRight, Circle } from "lucide-react";
|
|
419
|
+
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
420
|
+
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
421
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
422
|
+
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
423
|
+
var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
424
|
+
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
425
|
+
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
426
|
+
var DropdownMenuSubTrigger = React6.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs2(
|
|
427
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
428
|
+
{
|
|
429
|
+
ref,
|
|
430
|
+
className: cn(
|
|
431
|
+
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
432
|
+
inset && "pl-8",
|
|
433
|
+
className
|
|
434
|
+
),
|
|
435
|
+
...props,
|
|
436
|
+
children: [
|
|
437
|
+
children,
|
|
438
|
+
/* @__PURE__ */ jsx7(ChevronRight, { className: "ml-auto" })
|
|
439
|
+
]
|
|
440
|
+
}
|
|
441
|
+
));
|
|
442
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
443
|
+
var DropdownMenuSubContent = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
444
|
+
DropdownMenuPrimitive.SubContent,
|
|
445
|
+
{
|
|
446
|
+
ref,
|
|
447
|
+
className: cn(
|
|
448
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
449
|
+
className
|
|
450
|
+
),
|
|
451
|
+
...props
|
|
452
|
+
}
|
|
453
|
+
));
|
|
454
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
455
|
+
var DropdownMenuContent = React6.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx7(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx7(
|
|
456
|
+
DropdownMenuPrimitive.Content,
|
|
457
|
+
{
|
|
458
|
+
ref,
|
|
459
|
+
sideOffset,
|
|
460
|
+
className: cn(
|
|
461
|
+
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
462
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
463
|
+
className
|
|
464
|
+
),
|
|
465
|
+
...props
|
|
466
|
+
}
|
|
467
|
+
) }));
|
|
468
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
469
|
+
var DropdownMenuItem = React6.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
470
|
+
DropdownMenuPrimitive.Item,
|
|
471
|
+
{
|
|
472
|
+
ref,
|
|
473
|
+
className: cn(
|
|
474
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
475
|
+
inset && "pl-8",
|
|
476
|
+
className
|
|
477
|
+
),
|
|
478
|
+
...props
|
|
479
|
+
}
|
|
480
|
+
));
|
|
481
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
482
|
+
var DropdownMenuCheckboxItem = React6.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs2(
|
|
483
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
484
|
+
{
|
|
485
|
+
ref,
|
|
486
|
+
className: cn(
|
|
487
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
488
|
+
className
|
|
489
|
+
),
|
|
490
|
+
checked,
|
|
491
|
+
...props,
|
|
492
|
+
children: [
|
|
493
|
+
/* @__PURE__ */ jsx7("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx7(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx7(Check, { className: "h-4 w-4" }) }) }),
|
|
494
|
+
children
|
|
495
|
+
]
|
|
496
|
+
}
|
|
497
|
+
));
|
|
498
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
499
|
+
var DropdownMenuRadioItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs2(
|
|
500
|
+
DropdownMenuPrimitive.RadioItem,
|
|
501
|
+
{
|
|
502
|
+
ref,
|
|
503
|
+
className: cn(
|
|
504
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
505
|
+
className
|
|
506
|
+
),
|
|
507
|
+
...props,
|
|
508
|
+
children: [
|
|
509
|
+
/* @__PURE__ */ jsx7("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx7(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx7(Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
510
|
+
children
|
|
511
|
+
]
|
|
512
|
+
}
|
|
513
|
+
));
|
|
514
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
515
|
+
var DropdownMenuLabel = React6.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
516
|
+
DropdownMenuPrimitive.Label,
|
|
517
|
+
{
|
|
518
|
+
ref,
|
|
519
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
|
|
520
|
+
...props
|
|
521
|
+
}
|
|
522
|
+
));
|
|
523
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
524
|
+
var DropdownMenuSeparator = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(DropdownMenuPrimitive.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-muted", className), ...props }));
|
|
525
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
526
|
+
var DropdownMenuShortcut = ({ className, ...props }) => {
|
|
527
|
+
return /* @__PURE__ */ jsx7("span", { className: cn("ml-auto text-xs tracking-widest opacity-60", className), ...props });
|
|
528
|
+
};
|
|
529
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
530
|
+
|
|
531
|
+
// src/ui/primitives/hover-card.tsx
|
|
532
|
+
import * as React7 from "react";
|
|
533
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
534
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
535
|
+
var HoverCard = HoverCardPrimitive.Root;
|
|
536
|
+
var HoverCardTrigger = HoverCardPrimitive.Trigger;
|
|
537
|
+
var HoverCardContent = React7.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
538
|
+
HoverCardPrimitive.Content,
|
|
539
|
+
{
|
|
540
|
+
ref,
|
|
541
|
+
align,
|
|
542
|
+
sideOffset,
|
|
543
|
+
className: cn(
|
|
544
|
+
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 origin-[--radix-hover-card-content-transform-origin]",
|
|
545
|
+
className
|
|
546
|
+
),
|
|
547
|
+
...props
|
|
548
|
+
}
|
|
549
|
+
));
|
|
550
|
+
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
|
|
551
|
+
|
|
552
|
+
// src/ui/primitives/select.tsx
|
|
553
|
+
import * as React8 from "react";
|
|
554
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
555
|
+
import { Check as Check2, ChevronDown as ChevronDown2, ChevronUp } from "lucide-react";
|
|
556
|
+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
557
|
+
var Select = SelectPrimitive.Root;
|
|
558
|
+
var SelectGroup = SelectPrimitive.Group;
|
|
559
|
+
var SelectValue = SelectPrimitive.Value;
|
|
560
|
+
var SelectTrigger = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs3(
|
|
561
|
+
SelectPrimitive.Trigger,
|
|
562
|
+
{
|
|
563
|
+
ref,
|
|
564
|
+
className: cn(
|
|
565
|
+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
566
|
+
className
|
|
567
|
+
),
|
|
568
|
+
...props,
|
|
569
|
+
children: [
|
|
570
|
+
children,
|
|
571
|
+
/* @__PURE__ */ jsx9(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx9(ChevronDown2, { className: "h-4 w-4 opacity-50" }) })
|
|
572
|
+
]
|
|
573
|
+
}
|
|
574
|
+
));
|
|
575
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
576
|
+
var SelectScrollUpButton = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
577
|
+
SelectPrimitive.ScrollUpButton,
|
|
578
|
+
{
|
|
579
|
+
ref,
|
|
580
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
581
|
+
...props,
|
|
582
|
+
children: /* @__PURE__ */ jsx9(ChevronUp, { className: "h-4 w-4" })
|
|
583
|
+
}
|
|
584
|
+
));
|
|
585
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
586
|
+
var SelectScrollDownButton = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
587
|
+
SelectPrimitive.ScrollDownButton,
|
|
588
|
+
{
|
|
589
|
+
ref,
|
|
590
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
591
|
+
...props,
|
|
592
|
+
children: /* @__PURE__ */ jsx9(ChevronDown2, { className: "h-4 w-4" })
|
|
593
|
+
}
|
|
594
|
+
));
|
|
595
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
596
|
+
var SelectContent = React8.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx9(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs3(
|
|
597
|
+
SelectPrimitive.Content,
|
|
598
|
+
{
|
|
599
|
+
ref,
|
|
600
|
+
className: cn(
|
|
601
|
+
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 origin-[--radix-select-content-transform-origin]",
|
|
602
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
603
|
+
className
|
|
604
|
+
),
|
|
605
|
+
position,
|
|
606
|
+
...props,
|
|
607
|
+
children: [
|
|
608
|
+
/* @__PURE__ */ jsx9(SelectScrollUpButton, {}),
|
|
609
|
+
/* @__PURE__ */ jsx9(
|
|
610
|
+
SelectPrimitive.Viewport,
|
|
611
|
+
{
|
|
612
|
+
className: cn(
|
|
613
|
+
"p-1",
|
|
614
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
615
|
+
),
|
|
616
|
+
children
|
|
617
|
+
}
|
|
618
|
+
),
|
|
619
|
+
/* @__PURE__ */ jsx9(SelectScrollDownButton, {})
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
) }));
|
|
623
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
624
|
+
var SelectLabel = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(SelectPrimitive.Label, { ref, className: cn("px-2 py-1.5 text-sm font-semibold", className), ...props }));
|
|
625
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
626
|
+
var SelectItem = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs3(
|
|
627
|
+
SelectPrimitive.Item,
|
|
628
|
+
{
|
|
629
|
+
ref,
|
|
630
|
+
className: cn(
|
|
631
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
632
|
+
className
|
|
633
|
+
),
|
|
634
|
+
...props,
|
|
635
|
+
children: [
|
|
636
|
+
/* @__PURE__ */ jsx9("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(Check2, { className: "h-4 w-4" }) }) }),
|
|
637
|
+
/* @__PURE__ */ jsx9(SelectPrimitive.ItemText, { children })
|
|
638
|
+
]
|
|
639
|
+
}
|
|
640
|
+
));
|
|
641
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
642
|
+
var SelectSeparator = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(SelectPrimitive.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-muted", className), ...props }));
|
|
643
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
644
|
+
|
|
645
|
+
// src/ui/primitives/command.tsx
|
|
646
|
+
import * as React9 from "react";
|
|
647
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
648
|
+
import { Search } from "lucide-react";
|
|
649
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
650
|
+
var Command = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx10(
|
|
651
|
+
CommandPrimitive,
|
|
652
|
+
{
|
|
653
|
+
ref,
|
|
654
|
+
className: cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className),
|
|
655
|
+
...props
|
|
656
|
+
}
|
|
657
|
+
));
|
|
658
|
+
Command.displayName = CommandPrimitive.displayName;
|
|
659
|
+
var CommandInput = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs4("div", { className: "flex items-center border-b px-3", "cmdk-input-wrapper": "", children: [
|
|
660
|
+
/* @__PURE__ */ jsx10(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
661
|
+
/* @__PURE__ */ jsx10(
|
|
662
|
+
CommandPrimitive.Input,
|
|
663
|
+
{
|
|
664
|
+
ref,
|
|
665
|
+
className: cn(
|
|
666
|
+
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
|
667
|
+
className
|
|
668
|
+
),
|
|
669
|
+
...props
|
|
670
|
+
}
|
|
671
|
+
)
|
|
672
|
+
] }));
|
|
673
|
+
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
|
674
|
+
var CommandList = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx10(
|
|
675
|
+
CommandPrimitive.List,
|
|
676
|
+
{
|
|
677
|
+
ref,
|
|
678
|
+
className: cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className),
|
|
679
|
+
...props
|
|
680
|
+
}
|
|
681
|
+
));
|
|
682
|
+
CommandList.displayName = CommandPrimitive.List.displayName;
|
|
683
|
+
var CommandEmpty = React9.forwardRef((props, ref) => /* @__PURE__ */ jsx10(CommandPrimitive.Empty, { ref, className: "py-6 text-center text-sm", ...props }));
|
|
684
|
+
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
|
685
|
+
var CommandGroup = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx10(
|
|
686
|
+
CommandPrimitive.Group,
|
|
687
|
+
{
|
|
688
|
+
ref,
|
|
689
|
+
className: cn(
|
|
690
|
+
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
|
691
|
+
className
|
|
692
|
+
),
|
|
693
|
+
...props
|
|
694
|
+
}
|
|
695
|
+
));
|
|
696
|
+
CommandGroup.displayName = CommandPrimitive.Group.displayName;
|
|
697
|
+
var CommandSeparator = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx10(CommandPrimitive.Separator, { ref, className: cn("-mx-1 h-px bg-border", className), ...props }));
|
|
698
|
+
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
|
|
699
|
+
var CommandItem = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx10(
|
|
700
|
+
CommandPrimitive.Item,
|
|
701
|
+
{
|
|
702
|
+
ref,
|
|
703
|
+
className: cn(
|
|
704
|
+
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
705
|
+
className
|
|
706
|
+
),
|
|
707
|
+
...props
|
|
708
|
+
}
|
|
709
|
+
));
|
|
710
|
+
CommandItem.displayName = CommandPrimitive.Item.displayName;
|
|
711
|
+
var CommandShortcut = ({ className, ...props }) => {
|
|
712
|
+
return /* @__PURE__ */ jsx10("span", { className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className), ...props });
|
|
713
|
+
};
|
|
714
|
+
CommandShortcut.displayName = "CommandShortcut";
|
|
715
|
+
|
|
716
|
+
// src/ui/primitives/dialog.tsx
|
|
717
|
+
import * as React10 from "react";
|
|
718
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
719
|
+
import { cva as cva4 } from "class-variance-authority";
|
|
720
|
+
import { X } from "lucide-react";
|
|
721
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
722
|
+
var Dialog = DialogPrimitive.Root;
|
|
723
|
+
var DialogTrigger = DialogPrimitive.Trigger;
|
|
724
|
+
var DialogPortal = DialogPrimitive.Portal;
|
|
725
|
+
var DialogClose = DialogPrimitive.Close;
|
|
726
|
+
var DialogOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
727
|
+
DialogPrimitive.Overlay,
|
|
728
|
+
{
|
|
729
|
+
ref,
|
|
730
|
+
className: cn(
|
|
731
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
732
|
+
className
|
|
733
|
+
),
|
|
734
|
+
...props
|
|
735
|
+
}
|
|
736
|
+
));
|
|
737
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
738
|
+
var dialogContentVariants = cva4(
|
|
739
|
+
"fixed left-[50%] top-[50%] z-50 grid translate-x-[-50%] translate-y-[-50%] gap-4 border shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
740
|
+
{
|
|
741
|
+
variants: {
|
|
742
|
+
variant: {
|
|
743
|
+
default: "w-full max-w-lg bg-background p-6",
|
|
744
|
+
lightbox: "max-w-[90vw] max-h-[90vh] bg-background/95 backdrop-blur-sm p-2"
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
defaultVariants: {
|
|
748
|
+
variant: "default"
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
);
|
|
752
|
+
var DialogContent = React10.forwardRef(({ className, variant, children, ...props }, ref) => /* @__PURE__ */ jsxs5(DialogPortal, { children: [
|
|
753
|
+
/* @__PURE__ */ jsx11(DialogOverlay, {}),
|
|
754
|
+
/* @__PURE__ */ jsxs5(
|
|
755
|
+
DialogPrimitive.Content,
|
|
756
|
+
{
|
|
757
|
+
ref,
|
|
758
|
+
className: cn(dialogContentVariants({ variant }), className),
|
|
759
|
+
...props,
|
|
760
|
+
children: [
|
|
761
|
+
children,
|
|
762
|
+
/* @__PURE__ */ jsxs5(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
|
|
763
|
+
/* @__PURE__ */ jsx11(X, { className: "h-4 w-4" }),
|
|
764
|
+
/* @__PURE__ */ jsx11("span", { className: "sr-only", children: "Close" })
|
|
765
|
+
] })
|
|
766
|
+
]
|
|
767
|
+
}
|
|
768
|
+
)
|
|
769
|
+
] }));
|
|
770
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
771
|
+
var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx11("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
|
|
772
|
+
DialogHeader.displayName = "DialogHeader";
|
|
773
|
+
var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx11(
|
|
774
|
+
"div",
|
|
775
|
+
{
|
|
776
|
+
className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
|
|
777
|
+
...props
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
DialogFooter.displayName = "DialogFooter";
|
|
781
|
+
var DialogTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
782
|
+
DialogPrimitive.Title,
|
|
783
|
+
{
|
|
784
|
+
ref,
|
|
785
|
+
className: cn("text-lg font-semibold leading-none tracking-tight", className),
|
|
786
|
+
...props
|
|
787
|
+
}
|
|
788
|
+
));
|
|
789
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
790
|
+
var DialogDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
791
|
+
DialogPrimitive.Description,
|
|
792
|
+
{
|
|
793
|
+
ref,
|
|
794
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
795
|
+
...props
|
|
796
|
+
}
|
|
797
|
+
));
|
|
798
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
799
|
+
|
|
800
|
+
// src/ui/components/message.tsx
|
|
801
|
+
import { cva as cva5 } from "class-variance-authority";
|
|
802
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
803
|
+
var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx12(
|
|
804
|
+
"div",
|
|
805
|
+
{
|
|
806
|
+
className: cn(
|
|
807
|
+
"group flex w-full items-end justify-end gap-2 py-4",
|
|
808
|
+
from === "user" ? "is-user" : "is-assistant flex-row-reverse justify-end",
|
|
809
|
+
className
|
|
810
|
+
),
|
|
811
|
+
...props
|
|
812
|
+
}
|
|
813
|
+
);
|
|
814
|
+
var messageContentVariants = cva5(
|
|
815
|
+
"flex flex-col gap-2 overflow-hidden rounded-lg text-sm",
|
|
816
|
+
{
|
|
817
|
+
variants: {
|
|
818
|
+
variant: {
|
|
819
|
+
contained: [
|
|
820
|
+
"max-w-[80%] px-4 py-3",
|
|
821
|
+
"group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground",
|
|
822
|
+
"group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground"
|
|
823
|
+
],
|
|
824
|
+
flat: [
|
|
825
|
+
"group-[.is-user]:max-w-[80%] group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
|
|
826
|
+
"group-[.is-assistant]:text-foreground"
|
|
827
|
+
]
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
defaultVariants: {
|
|
831
|
+
variant: "contained"
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
);
|
|
835
|
+
var MessageContent = ({ children, className, variant, ...props }) => /* @__PURE__ */ jsx12("div", { className: cn(messageContentVariants({ variant, className })), ...props, children });
|
|
836
|
+
var MessageAvatar = ({ src, name, className, ...props }) => /* @__PURE__ */ jsxs6(Avatar, { className: cn("size-8 ring-1 ring-border", className), ...props, children: [
|
|
837
|
+
/* @__PURE__ */ jsx12(AvatarImage, { alt: "", className: "mt-0 mb-0", src }),
|
|
838
|
+
/* @__PURE__ */ jsx12(AvatarFallback, { children: name?.slice(0, 2) || "ME" })
|
|
839
|
+
] });
|
|
840
|
+
|
|
841
|
+
// src/ui/components/conversation.tsx
|
|
842
|
+
import { ArrowDownIcon } from "lucide-react";
|
|
843
|
+
import { useCallback } from "react";
|
|
844
|
+
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
845
|
+
import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
|
|
846
|
+
import { Fragment, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
847
|
+
var Conversation = ({ className, ...props }) => /* @__PURE__ */ jsx13(
|
|
848
|
+
StickToBottom,
|
|
849
|
+
{
|
|
850
|
+
className: cn("relative flex-1 overflow-y-auto", className),
|
|
851
|
+
initial: "smooth",
|
|
852
|
+
role: "log",
|
|
853
|
+
...props
|
|
854
|
+
}
|
|
855
|
+
);
|
|
856
|
+
var ConversationContent = ({ className, ...props }) => /* @__PURE__ */ jsx13(StickToBottom.Content, { className: cn("p-4", className), ...props });
|
|
857
|
+
var ConversationEmptyState = ({
|
|
858
|
+
className,
|
|
859
|
+
title = "No messages yet",
|
|
860
|
+
description = "Start a conversation to see messages here",
|
|
861
|
+
icon,
|
|
862
|
+
children,
|
|
863
|
+
...props
|
|
864
|
+
}) => /* @__PURE__ */ jsx13(
|
|
865
|
+
"div",
|
|
866
|
+
{
|
|
867
|
+
className: cn("flex size-full flex-col items-center justify-center gap-3 p-8 text-center", className),
|
|
868
|
+
...props,
|
|
869
|
+
children: children ?? /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
870
|
+
icon && /* @__PURE__ */ jsx13("div", { className: "text-muted-foreground", children: icon }),
|
|
871
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
|
|
872
|
+
/* @__PURE__ */ jsx13("h3", { className: "font-medium text-sm", children: title }),
|
|
873
|
+
description && /* @__PURE__ */ jsx13("p", { className: "text-muted-foreground text-sm", children: description })
|
|
874
|
+
] })
|
|
875
|
+
] })
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
var ConversationScrollButton = ({ className, ...props }) => {
|
|
879
|
+
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
880
|
+
const handleScrollToBottom = useCallback(() => {
|
|
881
|
+
scrollToBottom();
|
|
882
|
+
}, [scrollToBottom]);
|
|
883
|
+
return !isAtBottom && /* @__PURE__ */ jsx13(
|
|
884
|
+
Button,
|
|
885
|
+
{
|
|
886
|
+
className: cn("absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full", className),
|
|
887
|
+
onClick: handleScrollToBottom,
|
|
888
|
+
size: "icon",
|
|
889
|
+
type: "button",
|
|
890
|
+
variant: "outline",
|
|
891
|
+
...props,
|
|
892
|
+
children: /* @__PURE__ */ jsx13(ArrowDownIcon, { className: "size-4" })
|
|
893
|
+
}
|
|
894
|
+
);
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
// src/ui/components/response.tsx
|
|
898
|
+
import { memo } from "react";
|
|
899
|
+
import { Streamdown } from "streamdown";
|
|
900
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
901
|
+
var Response = memo(
|
|
902
|
+
({ className, ...props }) => /* @__PURE__ */ jsx14(
|
|
903
|
+
Streamdown,
|
|
904
|
+
{
|
|
905
|
+
className: cn("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className),
|
|
906
|
+
...props
|
|
907
|
+
}
|
|
908
|
+
),
|
|
909
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children
|
|
910
|
+
);
|
|
911
|
+
Response.displayName = "Response";
|
|
912
|
+
|
|
913
|
+
// src/ui/components/tool.tsx
|
|
914
|
+
import {
|
|
915
|
+
CheckCircleIcon,
|
|
916
|
+
ChevronDownIcon,
|
|
917
|
+
CircleIcon,
|
|
918
|
+
ClockIcon,
|
|
919
|
+
WrenchIcon,
|
|
920
|
+
XCircleIcon
|
|
921
|
+
} from "lucide-react";
|
|
922
|
+
import { isValidElement } from "react";
|
|
923
|
+
|
|
924
|
+
// src/ui/components/code-block.tsx
|
|
925
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
926
|
+
import {
|
|
927
|
+
createContext,
|
|
928
|
+
useContext,
|
|
929
|
+
useEffect,
|
|
930
|
+
useRef,
|
|
931
|
+
useState
|
|
932
|
+
} from "react";
|
|
933
|
+
import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
934
|
+
var CodeBlockContext = createContext({
|
|
935
|
+
code: ""
|
|
936
|
+
});
|
|
937
|
+
async function highlightCode(code, language, showLineNumbers = false) {
|
|
938
|
+
try {
|
|
939
|
+
const shiki = await import("shiki");
|
|
940
|
+
const lineNumberTransformer = showLineNumbers ? [
|
|
941
|
+
{
|
|
942
|
+
name: "line-numbers",
|
|
943
|
+
line(node, line) {
|
|
944
|
+
node.children.unshift({
|
|
945
|
+
type: "element",
|
|
946
|
+
tagName: "span",
|
|
947
|
+
properties: {
|
|
948
|
+
className: ["inline-block", "min-w-10", "mr-4", "text-right", "select-none", "text-muted-foreground"]
|
|
949
|
+
},
|
|
950
|
+
children: [{ type: "text", value: String(line) }]
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
] : [];
|
|
955
|
+
const [light, dark] = await Promise.all([
|
|
956
|
+
shiki.codeToHtml(code, { lang: language, theme: "one-light", transformers: lineNumberTransformer }),
|
|
957
|
+
shiki.codeToHtml(code, { lang: language, theme: "one-dark-pro", transformers: lineNumberTransformer })
|
|
958
|
+
]);
|
|
959
|
+
return [light, dark];
|
|
960
|
+
} catch {
|
|
961
|
+
return ["", ""];
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
var CodeBlock = ({
|
|
965
|
+
code,
|
|
966
|
+
language,
|
|
967
|
+
showLineNumbers = false,
|
|
968
|
+
className,
|
|
969
|
+
children,
|
|
970
|
+
...props
|
|
971
|
+
}) => {
|
|
972
|
+
const [html, setHtml] = useState("");
|
|
973
|
+
const [darkHtml, setDarkHtml] = useState("");
|
|
974
|
+
const effectIdRef = useRef(0);
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
const id = ++effectIdRef.current;
|
|
977
|
+
highlightCode(code, language, showLineNumbers).then(([light, dark]) => {
|
|
978
|
+
if (id === effectIdRef.current) {
|
|
979
|
+
setHtml(light);
|
|
980
|
+
setDarkHtml(dark);
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
}, [code, language, showLineNumbers]);
|
|
984
|
+
const useFallback = !html && !darkHtml;
|
|
985
|
+
return /* @__PURE__ */ jsx15(CodeBlockContext.Provider, { value: { code }, children: /* @__PURE__ */ jsx15(
|
|
986
|
+
"div",
|
|
987
|
+
{
|
|
988
|
+
className: cn(
|
|
989
|
+
"group relative w-full overflow-hidden rounded-md border bg-background text-foreground",
|
|
990
|
+
className
|
|
991
|
+
),
|
|
992
|
+
...props,
|
|
993
|
+
children: /* @__PURE__ */ jsxs8("div", { className: "relative", children: [
|
|
994
|
+
useFallback ? /* @__PURE__ */ jsx15("pre", { className: "m-0 overflow-auto bg-background p-4 text-foreground text-sm", children: /* @__PURE__ */ jsx15("code", { className: "font-mono text-sm", children: code }) }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
995
|
+
/* @__PURE__ */ jsx15(
|
|
996
|
+
"div",
|
|
997
|
+
{
|
|
998
|
+
className: "overflow-hidden dark:hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm",
|
|
999
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
1000
|
+
}
|
|
1001
|
+
),
|
|
1002
|
+
/* @__PURE__ */ jsx15(
|
|
1003
|
+
"div",
|
|
1004
|
+
{
|
|
1005
|
+
className: "hidden overflow-hidden dark:block [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm",
|
|
1006
|
+
dangerouslySetInnerHTML: { __html: darkHtml }
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
] }),
|
|
1010
|
+
children && /* @__PURE__ */ jsx15("div", { className: "absolute top-2 right-2 flex items-center gap-2", children })
|
|
1011
|
+
] })
|
|
1012
|
+
}
|
|
1013
|
+
) });
|
|
1014
|
+
};
|
|
1015
|
+
var CodeBlockCopyButton = ({
|
|
1016
|
+
onCopy,
|
|
1017
|
+
onError,
|
|
1018
|
+
timeout = 2e3,
|
|
1019
|
+
children,
|
|
1020
|
+
className,
|
|
1021
|
+
...props
|
|
1022
|
+
}) => {
|
|
1023
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
1024
|
+
const { code } = useContext(CodeBlockContext);
|
|
1025
|
+
const copyToClipboard = async () => {
|
|
1026
|
+
if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
|
|
1027
|
+
onError?.(new Error("Clipboard API not available"));
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
try {
|
|
1031
|
+
await navigator.clipboard.writeText(code);
|
|
1032
|
+
setIsCopied(true);
|
|
1033
|
+
onCopy?.();
|
|
1034
|
+
setTimeout(() => setIsCopied(false), timeout);
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
onError?.(error);
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
const Icon2 = isCopied ? CheckIcon : CopyIcon;
|
|
1040
|
+
return /* @__PURE__ */ jsx15(Button, { className: cn("shrink-0", className), onClick: copyToClipboard, size: "icon", variant: "ghost", ...props, children: children ?? /* @__PURE__ */ jsx15(Icon2, { size: 14 }) });
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
// src/ui/components/tool.tsx
|
|
1044
|
+
import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1045
|
+
var Tool = ({ className, ...props }) => /* @__PURE__ */ jsx16(Collapsible, { className: cn("not-prose mb-4 w-full rounded-md border", className), ...props });
|
|
1046
|
+
var getStatusBadge = (status) => {
|
|
1047
|
+
const labels = {
|
|
1048
|
+
"input-streaming": "Pending",
|
|
1049
|
+
"input-available": "Running",
|
|
1050
|
+
"approval-requested": "Awaiting Approval",
|
|
1051
|
+
"approval-responded": "Responded",
|
|
1052
|
+
"output-available": "Completed",
|
|
1053
|
+
"output-error": "Error",
|
|
1054
|
+
"output-denied": "Denied"
|
|
1055
|
+
};
|
|
1056
|
+
const icons = {
|
|
1057
|
+
"input-streaming": /* @__PURE__ */ jsx16(CircleIcon, { className: "size-4" }),
|
|
1058
|
+
"input-available": /* @__PURE__ */ jsx16(ClockIcon, { className: "size-4 animate-pulse" }),
|
|
1059
|
+
"approval-requested": /* @__PURE__ */ jsx16(ClockIcon, { className: "size-4 text-yellow-600" }),
|
|
1060
|
+
"approval-responded": /* @__PURE__ */ jsx16(CheckCircleIcon, { className: "size-4 text-blue-600" }),
|
|
1061
|
+
"output-available": /* @__PURE__ */ jsx16(CheckCircleIcon, { className: "size-4 text-green-600" }),
|
|
1062
|
+
"output-error": /* @__PURE__ */ jsx16(XCircleIcon, { className: "size-4 text-red-600" }),
|
|
1063
|
+
"output-denied": /* @__PURE__ */ jsx16(XCircleIcon, { className: "size-4 text-orange-600" })
|
|
1064
|
+
};
|
|
1065
|
+
return /* @__PURE__ */ jsxs9(Badge, { className: "gap-1.5 rounded-full text-xs", variant: "secondary", children: [
|
|
1066
|
+
icons[status],
|
|
1067
|
+
labels[status]
|
|
1068
|
+
] });
|
|
1069
|
+
};
|
|
1070
|
+
var ToolHeader = ({ className, title, type, state, ...props }) => /* @__PURE__ */ jsxs9(CollapsibleTrigger2, { className: cn("group flex w-full items-center justify-between gap-4 p-3", className), ...props, children: [
|
|
1071
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
|
|
1072
|
+
/* @__PURE__ */ jsx16(WrenchIcon, { className: "size-4 text-muted-foreground" }),
|
|
1073
|
+
/* @__PURE__ */ jsx16("span", { className: "font-medium text-sm", children: title ?? type?.split("-").slice(1).join("-") ?? "Tool" }),
|
|
1074
|
+
getStatusBadge(state)
|
|
1075
|
+
] }),
|
|
1076
|
+
/* @__PURE__ */ jsx16(ChevronDownIcon, { className: "size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })
|
|
1077
|
+
] });
|
|
1078
|
+
var ToolContent = ({ className, ...props }) => /* @__PURE__ */ jsx16(
|
|
1079
|
+
CollapsibleContent2,
|
|
1080
|
+
{
|
|
1081
|
+
className: cn(
|
|
1082
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1083
|
+
className
|
|
1084
|
+
),
|
|
1085
|
+
...props
|
|
1086
|
+
}
|
|
1087
|
+
);
|
|
1088
|
+
var ToolInput = ({ className, input, ...props }) => /* @__PURE__ */ jsxs9("div", { className: cn("space-y-2 overflow-hidden p-4", className), ...props, children: [
|
|
1089
|
+
/* @__PURE__ */ jsx16("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: "Parameters" }),
|
|
1090
|
+
/* @__PURE__ */ jsx16("div", { className: "rounded-md bg-muted/50", children: /* @__PURE__ */ jsx16(CodeBlock, { code: JSON.stringify(input, null, 2), language: "json" }) })
|
|
1091
|
+
] });
|
|
1092
|
+
var ToolOutput = ({ className, output, errorText, ...props }) => {
|
|
1093
|
+
if (!(output || errorText)) {
|
|
1094
|
+
return null;
|
|
1095
|
+
}
|
|
1096
|
+
let Output = /* @__PURE__ */ jsx16("div", { children: output });
|
|
1097
|
+
if (typeof output === "object" && !isValidElement(output)) {
|
|
1098
|
+
Output = /* @__PURE__ */ jsx16(CodeBlock, { code: JSON.stringify(output, null, 2), language: "json" });
|
|
1099
|
+
} else if (typeof output === "string") {
|
|
1100
|
+
Output = /* @__PURE__ */ jsx16(CodeBlock, { code: output, language: "json" });
|
|
1101
|
+
}
|
|
1102
|
+
return /* @__PURE__ */ jsxs9("div", { className: cn("space-y-2 p-4", className), ...props, children: [
|
|
1103
|
+
/* @__PURE__ */ jsx16("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: errorText ? "Error" : "Result" }),
|
|
1104
|
+
/* @__PURE__ */ jsxs9(
|
|
1105
|
+
"div",
|
|
1106
|
+
{
|
|
1107
|
+
className: cn(
|
|
1108
|
+
"overflow-x-auto rounded-md text-xs [&_table]:w-full",
|
|
1109
|
+
errorText ? "bg-destructive/10 text-destructive" : "bg-muted/50 text-foreground"
|
|
1110
|
+
),
|
|
1111
|
+
children: [
|
|
1112
|
+
errorText && /* @__PURE__ */ jsx16("div", { children: errorText }),
|
|
1113
|
+
Output
|
|
1114
|
+
]
|
|
1115
|
+
}
|
|
1116
|
+
)
|
|
1117
|
+
] });
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
// src/ui/components/artifact.tsx
|
|
1121
|
+
import { XIcon } from "lucide-react";
|
|
1122
|
+
import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1123
|
+
var Artifact = ({ className, ...props }) => /* @__PURE__ */ jsx17(
|
|
1124
|
+
"div",
|
|
1125
|
+
{
|
|
1126
|
+
className: cn("flex flex-col overflow-hidden rounded-lg border bg-background shadow-sm", className),
|
|
1127
|
+
...props
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
1130
|
+
var ArtifactHeader = ({ className, ...props }) => /* @__PURE__ */ jsx17("div", { className: cn("flex items-center justify-between border-b bg-muted/50 px-4 py-3", className), ...props });
|
|
1131
|
+
var ArtifactClose = ({
|
|
1132
|
+
className,
|
|
1133
|
+
children,
|
|
1134
|
+
size = "sm",
|
|
1135
|
+
variant = "ghost",
|
|
1136
|
+
...props
|
|
1137
|
+
}) => /* @__PURE__ */ jsxs10(
|
|
1138
|
+
Button,
|
|
1139
|
+
{
|
|
1140
|
+
className: cn("size-8 p-0 text-muted-foreground hover:text-foreground", className),
|
|
1141
|
+
size,
|
|
1142
|
+
type: "button",
|
|
1143
|
+
variant,
|
|
1144
|
+
...props,
|
|
1145
|
+
children: [
|
|
1146
|
+
children ?? /* @__PURE__ */ jsx17(XIcon, { className: "size-4" }),
|
|
1147
|
+
/* @__PURE__ */ jsx17("span", { className: "sr-only", children: "Close" })
|
|
1148
|
+
]
|
|
1149
|
+
}
|
|
1150
|
+
);
|
|
1151
|
+
var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ jsx17("p", { className: cn("font-medium text-foreground text-sm", className), ...props });
|
|
1152
|
+
var ArtifactDescription = ({ className, ...props }) => /* @__PURE__ */ jsx17("p", { className: cn("text-muted-foreground text-sm", className), ...props });
|
|
1153
|
+
var ArtifactActions = ({ className, ...props }) => /* @__PURE__ */ jsx17("div", { className: cn("flex items-center gap-1", className), ...props });
|
|
1154
|
+
var ArtifactAction = ({
|
|
1155
|
+
tooltip,
|
|
1156
|
+
label,
|
|
1157
|
+
icon: Icon2,
|
|
1158
|
+
children,
|
|
1159
|
+
className,
|
|
1160
|
+
size = "sm",
|
|
1161
|
+
variant = "ghost",
|
|
1162
|
+
...props
|
|
1163
|
+
}) => {
|
|
1164
|
+
const button = /* @__PURE__ */ jsxs10(
|
|
1165
|
+
Button,
|
|
1166
|
+
{
|
|
1167
|
+
className: cn("size-8 p-0 text-muted-foreground hover:text-foreground", className),
|
|
1168
|
+
size,
|
|
1169
|
+
type: "button",
|
|
1170
|
+
variant,
|
|
1171
|
+
...props,
|
|
1172
|
+
children: [
|
|
1173
|
+
Icon2 ? /* @__PURE__ */ jsx17(Icon2, { className: "size-4" }) : children,
|
|
1174
|
+
/* @__PURE__ */ jsx17("span", { className: "sr-only", children: label || tooltip })
|
|
1175
|
+
]
|
|
1176
|
+
}
|
|
1177
|
+
);
|
|
1178
|
+
if (tooltip) {
|
|
1179
|
+
return /* @__PURE__ */ jsx17(TooltipProvider, { children: /* @__PURE__ */ jsxs10(Tooltip, { children: [
|
|
1180
|
+
/* @__PURE__ */ jsx17(TooltipTrigger, { asChild: true, children: button }),
|
|
1181
|
+
/* @__PURE__ */ jsx17(TooltipContent, { children: /* @__PURE__ */ jsx17("p", { children: tooltip }) })
|
|
1182
|
+
] }) });
|
|
1183
|
+
}
|
|
1184
|
+
return button;
|
|
1185
|
+
};
|
|
1186
|
+
var ArtifactContent = ({ className, ...props }) => /* @__PURE__ */ jsx17("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
|
|
1187
|
+
|
|
1188
|
+
// src/ui/components/streaming-indicator.tsx
|
|
1189
|
+
import { Bot } from "lucide-react";
|
|
1190
|
+
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1191
|
+
function StreamingIndicator({ className, icon, avatar, ...props }) {
|
|
1192
|
+
return /* @__PURE__ */ jsxs11("div", { className: cn("flex items-start gap-3 px-1", className), ...props, children: [
|
|
1193
|
+
avatar ?? /* @__PURE__ */ jsx18("div", { className: "h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5", children: icon ?? /* @__PURE__ */ jsx18(Bot, { className: "h-4 w-4 text-primary" }) }),
|
|
1194
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 pt-2.5", children: [
|
|
1195
|
+
/* @__PURE__ */ jsx18("span", { className: "h-2 w-2 rounded-full bg-primary/60 animate-pulse" }),
|
|
1196
|
+
/* @__PURE__ */ jsx18("span", { className: "h-2 w-2 rounded-full bg-primary/60 animate-pulse [animation-delay:150ms]" }),
|
|
1197
|
+
/* @__PURE__ */ jsx18("span", { className: "h-2 w-2 rounded-full bg-primary/60 animate-pulse [animation-delay:300ms]" })
|
|
1198
|
+
] })
|
|
1199
|
+
] });
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/ui/components/audio-recorder.tsx
|
|
1203
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
1204
|
+
import { Loader2, Mic, Square } from "lucide-react";
|
|
1205
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1206
|
+
function encodeWav(samples, sampleRate) {
|
|
1207
|
+
const numChannels = 1;
|
|
1208
|
+
const bitsPerSample = 16;
|
|
1209
|
+
const byteRate = sampleRate * numChannels * (bitsPerSample / 8);
|
|
1210
|
+
const blockAlign = numChannels * (bitsPerSample / 8);
|
|
1211
|
+
const dataLength = samples.length * (bitsPerSample / 8);
|
|
1212
|
+
const buffer = new ArrayBuffer(44 + dataLength);
|
|
1213
|
+
const view = new DataView(buffer);
|
|
1214
|
+
writeString(view, 0, "RIFF");
|
|
1215
|
+
view.setUint32(4, 36 + dataLength, true);
|
|
1216
|
+
writeString(view, 8, "WAVE");
|
|
1217
|
+
writeString(view, 12, "fmt ");
|
|
1218
|
+
view.setUint32(16, 16, true);
|
|
1219
|
+
view.setUint16(20, 1, true);
|
|
1220
|
+
view.setUint16(22, numChannels, true);
|
|
1221
|
+
view.setUint32(24, sampleRate, true);
|
|
1222
|
+
view.setUint32(28, byteRate, true);
|
|
1223
|
+
view.setUint16(32, blockAlign, true);
|
|
1224
|
+
view.setUint16(34, bitsPerSample, true);
|
|
1225
|
+
writeString(view, 36, "data");
|
|
1226
|
+
view.setUint32(40, dataLength, true);
|
|
1227
|
+
let offset = 44;
|
|
1228
|
+
for (let i = 0; i < samples.length; i++) {
|
|
1229
|
+
const s = Math.max(-1, Math.min(1, samples[i]));
|
|
1230
|
+
view.setInt16(offset, s < 0 ? s * 32768 : s * 32767, true);
|
|
1231
|
+
offset += 2;
|
|
1232
|
+
}
|
|
1233
|
+
return new Blob([buffer], { type: "audio/wav" });
|
|
1234
|
+
}
|
|
1235
|
+
function writeString(view, offset, str) {
|
|
1236
|
+
for (let i = 0; i < str.length; i++) {
|
|
1237
|
+
view.setUint8(offset + i, str.charCodeAt(i));
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function createWorkletUrl() {
|
|
1241
|
+
const code = `
|
|
1242
|
+
class RecorderProcessor extends AudioWorkletProcessor {
|
|
1243
|
+
constructor() {
|
|
1244
|
+
super();
|
|
1245
|
+
this._stopped = false;
|
|
1246
|
+
this.port.onmessage = (e) => {
|
|
1247
|
+
if (e.data === 'stop') this._stopped = true;
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
process(inputs) {
|
|
1251
|
+
if (this._stopped) return false;
|
|
1252
|
+
const input = inputs[0];
|
|
1253
|
+
if (input && input[0]) {
|
|
1254
|
+
this.port.postMessage(new Float32Array(input[0]));
|
|
1255
|
+
}
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
registerProcessor('recorder-processor', RecorderProcessor);
|
|
1260
|
+
`;
|
|
1261
|
+
const blob = new Blob([code], { type: "application/javascript" });
|
|
1262
|
+
return URL.createObjectURL(blob);
|
|
1263
|
+
}
|
|
1264
|
+
function AudioRecorder({
|
|
1265
|
+
onRecordingComplete,
|
|
1266
|
+
disabled,
|
|
1267
|
+
className,
|
|
1268
|
+
mode = "send",
|
|
1269
|
+
transcriptionEndpoint,
|
|
1270
|
+
transcriptionHeaders,
|
|
1271
|
+
onTranscriptionComplete,
|
|
1272
|
+
transcriptionFieldName = "file",
|
|
1273
|
+
parseTranscriptionResponse,
|
|
1274
|
+
onRequestPermission,
|
|
1275
|
+
labels
|
|
1276
|
+
}) {
|
|
1277
|
+
const [isRecording, setIsRecording] = useState2(false);
|
|
1278
|
+
const [isTranscribing, setIsTranscribing] = useState2(false);
|
|
1279
|
+
const [duration, setDuration] = useState2(0);
|
|
1280
|
+
const [isSupported, setIsSupported] = useState2(true);
|
|
1281
|
+
const streamRef = useRef2(null);
|
|
1282
|
+
const audioContextRef = useRef2(null);
|
|
1283
|
+
const workletNodeRef = useRef2(null);
|
|
1284
|
+
const chunksRef = useRef2([]);
|
|
1285
|
+
const timerRef = useRef2(null);
|
|
1286
|
+
const workletUrlRef = useRef2(null);
|
|
1287
|
+
const onTranscriptionCompleteRef = useRef2(onTranscriptionComplete);
|
|
1288
|
+
onTranscriptionCompleteRef.current = onTranscriptionComplete;
|
|
1289
|
+
useEffect2(() => {
|
|
1290
|
+
if (typeof window === "undefined" || !navigator.mediaDevices?.getUserMedia) {
|
|
1291
|
+
setIsSupported(false);
|
|
1292
|
+
}
|
|
1293
|
+
return () => {
|
|
1294
|
+
if (timerRef.current) clearInterval(timerRef.current);
|
|
1295
|
+
if (workletNodeRef.current) {
|
|
1296
|
+
workletNodeRef.current.port.postMessage("stop");
|
|
1297
|
+
workletNodeRef.current.disconnect();
|
|
1298
|
+
workletNodeRef.current = null;
|
|
1299
|
+
}
|
|
1300
|
+
if (audioContextRef.current) {
|
|
1301
|
+
audioContextRef.current.close();
|
|
1302
|
+
audioContextRef.current = null;
|
|
1303
|
+
}
|
|
1304
|
+
if (streamRef.current) {
|
|
1305
|
+
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
1306
|
+
streamRef.current = null;
|
|
1307
|
+
}
|
|
1308
|
+
if (workletUrlRef.current) URL.revokeObjectURL(workletUrlRef.current);
|
|
1309
|
+
};
|
|
1310
|
+
}, []);
|
|
1311
|
+
const startRecording = useCallback2(async () => {
|
|
1312
|
+
try {
|
|
1313
|
+
if (onRequestPermission) {
|
|
1314
|
+
const granted = await onRequestPermission();
|
|
1315
|
+
if (!granted) return;
|
|
1316
|
+
}
|
|
1317
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
1318
|
+
streamRef.current = stream;
|
|
1319
|
+
const audioContext = new AudioContext();
|
|
1320
|
+
audioContextRef.current = audioContext;
|
|
1321
|
+
const workletUrl = createWorkletUrl();
|
|
1322
|
+
workletUrlRef.current = workletUrl;
|
|
1323
|
+
await audioContext.audioWorklet.addModule(workletUrl);
|
|
1324
|
+
const source = audioContext.createMediaStreamSource(stream);
|
|
1325
|
+
const workletNode = new AudioWorkletNode(audioContext, "recorder-processor");
|
|
1326
|
+
workletNodeRef.current = workletNode;
|
|
1327
|
+
chunksRef.current = [];
|
|
1328
|
+
workletNode.port.onmessage = (e) => {
|
|
1329
|
+
if (e.data instanceof Float32Array) {
|
|
1330
|
+
chunksRef.current.push(e.data);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
source.connect(workletNode);
|
|
1334
|
+
workletNode.connect(audioContext.destination);
|
|
1335
|
+
setIsRecording(true);
|
|
1336
|
+
setDuration(0);
|
|
1337
|
+
timerRef.current = setInterval(() => {
|
|
1338
|
+
setDuration((prev) => prev + 1);
|
|
1339
|
+
}, 1e3);
|
|
1340
|
+
} catch {
|
|
1341
|
+
console.error("Failed to start recording");
|
|
1342
|
+
}
|
|
1343
|
+
}, [onRequestPermission]);
|
|
1344
|
+
const stopRecording = useCallback2(async () => {
|
|
1345
|
+
if (workletNodeRef.current) {
|
|
1346
|
+
workletNodeRef.current.port.postMessage("stop");
|
|
1347
|
+
workletNodeRef.current.disconnect();
|
|
1348
|
+
workletNodeRef.current = null;
|
|
1349
|
+
}
|
|
1350
|
+
const audioContext = audioContextRef.current;
|
|
1351
|
+
let wavBlob = null;
|
|
1352
|
+
if (audioContext) {
|
|
1353
|
+
const sampleRate = audioContext.sampleRate;
|
|
1354
|
+
const totalLength = chunksRef.current.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
1355
|
+
const merged = new Float32Array(totalLength);
|
|
1356
|
+
let offset = 0;
|
|
1357
|
+
for (const chunk of chunksRef.current) {
|
|
1358
|
+
merged.set(chunk, offset);
|
|
1359
|
+
offset += chunk.length;
|
|
1360
|
+
}
|
|
1361
|
+
chunksRef.current = [];
|
|
1362
|
+
wavBlob = encodeWav(merged, sampleRate);
|
|
1363
|
+
audioContext.close();
|
|
1364
|
+
audioContextRef.current = null;
|
|
1365
|
+
}
|
|
1366
|
+
if (streamRef.current) {
|
|
1367
|
+
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
1368
|
+
streamRef.current = null;
|
|
1369
|
+
}
|
|
1370
|
+
if (workletUrlRef.current) {
|
|
1371
|
+
URL.revokeObjectURL(workletUrlRef.current);
|
|
1372
|
+
workletUrlRef.current = null;
|
|
1373
|
+
}
|
|
1374
|
+
setIsRecording(false);
|
|
1375
|
+
if (timerRef.current) {
|
|
1376
|
+
clearInterval(timerRef.current);
|
|
1377
|
+
timerRef.current = null;
|
|
1378
|
+
}
|
|
1379
|
+
if (!wavBlob) return;
|
|
1380
|
+
if (mode === "transcribe" && transcriptionEndpoint) {
|
|
1381
|
+
setIsTranscribing(true);
|
|
1382
|
+
const formData = new FormData();
|
|
1383
|
+
formData.append(transcriptionFieldName, wavBlob, "recording.wav");
|
|
1384
|
+
try {
|
|
1385
|
+
const res = await fetch(transcriptionEndpoint, {
|
|
1386
|
+
method: "POST",
|
|
1387
|
+
headers: transcriptionHeaders,
|
|
1388
|
+
credentials: "include",
|
|
1389
|
+
body: formData
|
|
1390
|
+
});
|
|
1391
|
+
const data = await res.json();
|
|
1392
|
+
const text = parseTranscriptionResponse ? parseTranscriptionResponse(data) : data.text || data.transcript || data.transcription || (typeof data === "string" ? data : "");
|
|
1393
|
+
if (text) onTranscriptionCompleteRef.current?.(text);
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
console.error("Transcription failed:", err);
|
|
1396
|
+
} finally {
|
|
1397
|
+
setIsTranscribing(false);
|
|
1398
|
+
}
|
|
1399
|
+
} else {
|
|
1400
|
+
onRecordingComplete(wavBlob);
|
|
1401
|
+
}
|
|
1402
|
+
}, [onRecordingComplete, mode, transcriptionEndpoint, transcriptionHeaders, transcriptionFieldName, parseTranscriptionResponse]);
|
|
1403
|
+
const resolvedLabels = {
|
|
1404
|
+
recordAudio: labels?.recordAudio ?? "Record audio",
|
|
1405
|
+
stopRecording: labels?.stopRecording ?? "Stop recording",
|
|
1406
|
+
transcribing: labels?.transcribing ?? "Transcribing..."
|
|
1407
|
+
};
|
|
1408
|
+
const formatDuration = (seconds) => {
|
|
1409
|
+
const m = Math.floor(seconds / 60);
|
|
1410
|
+
const s = seconds % 60;
|
|
1411
|
+
return `${m}:${s.toString().padStart(2, "0")}`;
|
|
1412
|
+
};
|
|
1413
|
+
if (!isSupported) return null;
|
|
1414
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn("flex items-center gap-1", className), children: [
|
|
1415
|
+
isRecording && /* @__PURE__ */ jsx19("span", { className: "text-xs text-destructive font-mono animate-pulse", children: formatDuration(duration) }),
|
|
1416
|
+
isTranscribing ? /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
1417
|
+
/* @__PURE__ */ jsx19(Loader2, { className: "size-3.5 animate-spin" }),
|
|
1418
|
+
resolvedLabels.transcribing
|
|
1419
|
+
] }) : /* @__PURE__ */ jsx19(
|
|
1420
|
+
Button,
|
|
1421
|
+
{
|
|
1422
|
+
type: "button",
|
|
1423
|
+
variant: "ghost",
|
|
1424
|
+
size: "icon",
|
|
1425
|
+
className: cn("h-8 w-8", isRecording && "text-destructive hover:text-destructive"),
|
|
1426
|
+
disabled,
|
|
1427
|
+
onClick: isRecording ? stopRecording : startRecording,
|
|
1428
|
+
title: isRecording ? resolvedLabels.stopRecording : resolvedLabels.recordAudio,
|
|
1429
|
+
children: isRecording ? /* @__PURE__ */ jsx19(Square, { className: "size-4 fill-current" }) : /* @__PURE__ */ jsx19(Mic, { className: "size-4" })
|
|
1430
|
+
}
|
|
1431
|
+
)
|
|
1432
|
+
] });
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// src/ui/components/smart-timestamp.tsx
|
|
1436
|
+
import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1437
|
+
function SmartTimestamp({ date, formatShort, className }) {
|
|
1438
|
+
const shortText = formatShort ? formatShort(date) : formatSmartTimestamp(date);
|
|
1439
|
+
const fullText = formatFullTimestamp(date);
|
|
1440
|
+
return /* @__PURE__ */ jsx20(TooltipProvider, { children: /* @__PURE__ */ jsxs13(Tooltip, { children: [
|
|
1441
|
+
/* @__PURE__ */ jsx20(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx20("span", { className, children: shortText }) }),
|
|
1442
|
+
/* @__PURE__ */ jsx20(TooltipContent, { children: /* @__PURE__ */ jsx20("p", { children: fullText }) })
|
|
1443
|
+
] }) });
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/ui/components/file-preview-card.tsx
|
|
1447
|
+
import { FileIcon, Search as Search2 } from "lucide-react";
|
|
1448
|
+
import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1449
|
+
function ExtBadge({ ext }) {
|
|
1450
|
+
if (!ext) return null;
|
|
1451
|
+
return /* @__PURE__ */ jsx21("span", { className: "absolute bottom-1.5 left-1.5 rounded px-1 py-0.5 text-[9px] font-semibold uppercase leading-none bg-background/80 text-muted-foreground border border-border/50 backdrop-blur-sm", children: ext });
|
|
1452
|
+
}
|
|
1453
|
+
function FilePreviewCard({ file, onClick, className }) {
|
|
1454
|
+
const previewType = getFilePreviewType(file.type);
|
|
1455
|
+
const isClickable = !!onClick;
|
|
1456
|
+
const ext = getFileExtension(file.name, file.type);
|
|
1457
|
+
const cardBase = cn(
|
|
1458
|
+
"group relative flex flex-col overflow-hidden rounded-xl border border-border bg-muted/20 w-28 h-28",
|
|
1459
|
+
isClickable && "cursor-pointer hover:border-foreground/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 transition-colors",
|
|
1460
|
+
className
|
|
1461
|
+
);
|
|
1462
|
+
if (previewType === "image" && file.url) {
|
|
1463
|
+
return /* @__PURE__ */ jsxs14("button", { type: "button", onClick, disabled: !isClickable, className: cardBase, children: [
|
|
1464
|
+
/* @__PURE__ */ jsx21("img", { src: file.url, alt: file.name, className: "w-full h-full object-cover" }),
|
|
1465
|
+
/* @__PURE__ */ jsx21(ExtBadge, { ext }),
|
|
1466
|
+
isClickable && /* @__PURE__ */ jsx21("div", { className: "absolute inset-0 flex items-center justify-center bg-black/0 group-hover:bg-black/30 transition-colors", children: /* @__PURE__ */ jsx21(Search2, { className: "h-5 w-5 text-white opacity-0 group-hover:opacity-100 transition-opacity" }) })
|
|
1467
|
+
] });
|
|
1468
|
+
}
|
|
1469
|
+
if (previewType === "pdf" && file.url) {
|
|
1470
|
+
return /* @__PURE__ */ jsxs14("button", { type: "button", onClick, disabled: !isClickable, className: cardBase, children: [
|
|
1471
|
+
/* @__PURE__ */ jsx21("div", { className: "w-full h-full overflow-hidden pointer-events-none", children: /* @__PURE__ */ jsx21(
|
|
1472
|
+
"object",
|
|
1473
|
+
{
|
|
1474
|
+
data: `${file.url}#page=1&view=FitH`,
|
|
1475
|
+
type: "application/pdf",
|
|
1476
|
+
className: "w-[200%] h-[200%] origin-top-left scale-50",
|
|
1477
|
+
"aria-label": file.name,
|
|
1478
|
+
children: /* @__PURE__ */ jsx21("div", { className: "flex items-center justify-center w-full h-full", children: /* @__PURE__ */ jsx21(FileIcon, { className: "h-8 w-8 text-muted-foreground/40" }) })
|
|
1479
|
+
}
|
|
1480
|
+
) }),
|
|
1481
|
+
/* @__PURE__ */ jsx21(ExtBadge, { ext }),
|
|
1482
|
+
isClickable && /* @__PURE__ */ jsx21("div", { className: "absolute inset-0 flex items-center justify-center bg-black/0 group-hover:bg-black/10 transition-colors", children: /* @__PURE__ */ jsx21(Search2, { className: "h-5 w-5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" }) })
|
|
1483
|
+
] });
|
|
1484
|
+
}
|
|
1485
|
+
return /* @__PURE__ */ jsxs14("button", { type: "button", onClick, disabled: !isClickable, className: cardBase, children: [
|
|
1486
|
+
/* @__PURE__ */ jsx21("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsx21(FileIcon, { className: "h-8 w-8 text-muted-foreground/40" }) }),
|
|
1487
|
+
/* @__PURE__ */ jsxs14("div", { className: "w-full text-center min-w-0 px-2 pb-2 space-y-0.5", children: [
|
|
1488
|
+
/* @__PURE__ */ jsx21("p", { className: "text-[10px] text-foreground truncate leading-tight", title: file.name, children: file.name }),
|
|
1489
|
+
file.size != null && file.size > 0 && /* @__PURE__ */ jsx21("p", { className: "text-[9px] text-muted-foreground leading-tight", children: formatFileSize(file.size) })
|
|
1490
|
+
] }),
|
|
1491
|
+
/* @__PURE__ */ jsx21(ExtBadge, { ext })
|
|
1492
|
+
] });
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// src/ui/components/file-preview-modal.tsx
|
|
1496
|
+
import { Download, FileIcon as FileIcon2 } from "lucide-react";
|
|
1497
|
+
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1498
|
+
function FilePreviewModal({ open, onOpenChange, file }) {
|
|
1499
|
+
if (!file) return null;
|
|
1500
|
+
const previewType = getFilePreviewType(file.type);
|
|
1501
|
+
const canPreview = isPreviewable(file.type) && !!file.url;
|
|
1502
|
+
return /* @__PURE__ */ jsx22(Dialog, { open, onOpenChange, children: previewType === "image" && file.url ? /* @__PURE__ */ jsxs15(DialogContent, { variant: "lightbox", "aria-describedby": void 0, children: [
|
|
1503
|
+
/* @__PURE__ */ jsx22(DialogTitle, { className: "sr-only", children: file.name }),
|
|
1504
|
+
/* @__PURE__ */ jsx22(
|
|
1505
|
+
"img",
|
|
1506
|
+
{
|
|
1507
|
+
src: file.url,
|
|
1508
|
+
alt: file.name,
|
|
1509
|
+
className: "max-h-[85vh] max-w-full object-contain rounded-md"
|
|
1510
|
+
}
|
|
1511
|
+
)
|
|
1512
|
+
] }) : previewType === "pdf" && file.url ? /* @__PURE__ */ jsxs15(DialogContent, { variant: "lightbox", className: "w-[80vw] h-[85vh]", "aria-describedby": void 0, children: [
|
|
1513
|
+
/* @__PURE__ */ jsx22(DialogTitle, { className: "sr-only", children: file.name }),
|
|
1514
|
+
/* @__PURE__ */ jsx22(
|
|
1515
|
+
"object",
|
|
1516
|
+
{
|
|
1517
|
+
data: file.url,
|
|
1518
|
+
type: "application/pdf",
|
|
1519
|
+
className: "w-full h-full rounded-md",
|
|
1520
|
+
children: /* @__PURE__ */ jsxs15("div", { className: "flex flex-col items-center justify-center h-full gap-3 text-muted-foreground", children: [
|
|
1521
|
+
/* @__PURE__ */ jsx22("p", { className: "text-sm", children: "Unable to display PDF" }),
|
|
1522
|
+
/* @__PURE__ */ jsxs15(
|
|
1523
|
+
"a",
|
|
1524
|
+
{
|
|
1525
|
+
href: file.url,
|
|
1526
|
+
target: "_blank",
|
|
1527
|
+
rel: "noopener noreferrer",
|
|
1528
|
+
className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline",
|
|
1529
|
+
children: [
|
|
1530
|
+
/* @__PURE__ */ jsx22(Download, { className: "h-4 w-4" }),
|
|
1531
|
+
"Download ",
|
|
1532
|
+
file.name
|
|
1533
|
+
]
|
|
1534
|
+
}
|
|
1535
|
+
)
|
|
1536
|
+
] })
|
|
1537
|
+
}
|
|
1538
|
+
)
|
|
1539
|
+
] }) : /* @__PURE__ */ jsxs15(DialogContent, { children: [
|
|
1540
|
+
/* @__PURE__ */ jsxs15(DialogHeader, { children: [
|
|
1541
|
+
/* @__PURE__ */ jsxs15(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
1542
|
+
/* @__PURE__ */ jsx22(FileIcon2, { className: "h-5 w-5 text-muted-foreground" }),
|
|
1543
|
+
file.name
|
|
1544
|
+
] }),
|
|
1545
|
+
/* @__PURE__ */ jsxs15(DialogDescription, { children: [
|
|
1546
|
+
file.size != null && file.size > 0 && /* @__PURE__ */ jsx22("span", { children: formatFileSize(file.size) }),
|
|
1547
|
+
!canPreview && /* @__PURE__ */ jsx22("span", { children: " \xB7 Preview not available for this file type" })
|
|
1548
|
+
] })
|
|
1549
|
+
] }),
|
|
1550
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex flex-col items-center justify-center py-8 text-muted-foreground gap-3", children: [
|
|
1551
|
+
/* @__PURE__ */ jsx22(FileIcon2, { className: "h-12 w-12" }),
|
|
1552
|
+
/* @__PURE__ */ jsx22("p", { className: "text-sm", children: "Preview not available" }),
|
|
1553
|
+
file.url && /^https?:\/\//i.test(file.url) && /* @__PURE__ */ jsxs15(
|
|
1554
|
+
"a",
|
|
1555
|
+
{
|
|
1556
|
+
href: file.url,
|
|
1557
|
+
target: "_blank",
|
|
1558
|
+
rel: "noopener noreferrer",
|
|
1559
|
+
className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline",
|
|
1560
|
+
children: [
|
|
1561
|
+
/* @__PURE__ */ jsx22(Download, { className: "h-4 w-4" }),
|
|
1562
|
+
"Download file"
|
|
1563
|
+
]
|
|
1564
|
+
}
|
|
1565
|
+
)
|
|
1566
|
+
] })
|
|
1567
|
+
] }) });
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// src/ui/components/image-lightbox.tsx
|
|
1571
|
+
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3 } from "react";
|
|
1572
|
+
import { ChevronLeft, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
1573
|
+
import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1574
|
+
function ImageLightbox({ open, onOpenChange, images, initialIndex = 0 }) {
|
|
1575
|
+
const [currentIndex, setCurrentIndex] = useState3(initialIndex);
|
|
1576
|
+
const hasMultiple = images.length > 1;
|
|
1577
|
+
useEffect3(() => {
|
|
1578
|
+
if (open) setCurrentIndex(initialIndex);
|
|
1579
|
+
}, [open, initialIndex]);
|
|
1580
|
+
const goNext = useCallback3(() => {
|
|
1581
|
+
setCurrentIndex((i) => (i + 1) % images.length);
|
|
1582
|
+
}, [images.length]);
|
|
1583
|
+
const goPrev = useCallback3(() => {
|
|
1584
|
+
setCurrentIndex((i) => (i - 1 + images.length) % images.length);
|
|
1585
|
+
}, [images.length]);
|
|
1586
|
+
useEffect3(() => {
|
|
1587
|
+
if (!open || !hasMultiple) return;
|
|
1588
|
+
const handler = (e) => {
|
|
1589
|
+
if (e.key === "ArrowRight") goNext();
|
|
1590
|
+
if (e.key === "ArrowLeft") goPrev();
|
|
1591
|
+
};
|
|
1592
|
+
window.addEventListener("keydown", handler);
|
|
1593
|
+
return () => window.removeEventListener("keydown", handler);
|
|
1594
|
+
}, [open, hasMultiple, goNext, goPrev]);
|
|
1595
|
+
if (images.length === 0) return null;
|
|
1596
|
+
const current = images[currentIndex];
|
|
1597
|
+
if (!current) return null;
|
|
1598
|
+
return /* @__PURE__ */ jsx23(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs16(DialogContent, { variant: "lightbox", "aria-describedby": void 0, children: [
|
|
1599
|
+
/* @__PURE__ */ jsx23(DialogTitle, { className: "sr-only", children: current.alt || `Image ${currentIndex + 1} of ${images.length}` }),
|
|
1600
|
+
/* @__PURE__ */ jsxs16("div", { className: "relative flex items-center justify-center", children: [
|
|
1601
|
+
/* @__PURE__ */ jsx23(
|
|
1602
|
+
"img",
|
|
1603
|
+
{
|
|
1604
|
+
src: current.url,
|
|
1605
|
+
alt: current.alt || "Image preview",
|
|
1606
|
+
className: "max-h-[85vh] max-w-full object-contain rounded-md"
|
|
1607
|
+
}
|
|
1608
|
+
),
|
|
1609
|
+
hasMultiple && /* @__PURE__ */ jsxs16(Fragment3, { children: [
|
|
1610
|
+
/* @__PURE__ */ jsxs16(
|
|
1611
|
+
"button",
|
|
1612
|
+
{
|
|
1613
|
+
type: "button",
|
|
1614
|
+
onClick: goPrev,
|
|
1615
|
+
className: cn(
|
|
1616
|
+
"absolute left-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white",
|
|
1617
|
+
"hover:bg-black/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors"
|
|
1618
|
+
),
|
|
1619
|
+
children: [
|
|
1620
|
+
/* @__PURE__ */ jsx23(ChevronLeft, { className: "h-5 w-5" }),
|
|
1621
|
+
/* @__PURE__ */ jsx23("span", { className: "sr-only", children: "Previous image" })
|
|
1622
|
+
]
|
|
1623
|
+
}
|
|
1624
|
+
),
|
|
1625
|
+
/* @__PURE__ */ jsxs16(
|
|
1626
|
+
"button",
|
|
1627
|
+
{
|
|
1628
|
+
type: "button",
|
|
1629
|
+
onClick: goNext,
|
|
1630
|
+
className: cn(
|
|
1631
|
+
"absolute right-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white",
|
|
1632
|
+
"hover:bg-black/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors"
|
|
1633
|
+
),
|
|
1634
|
+
children: [
|
|
1635
|
+
/* @__PURE__ */ jsx23(ChevronRight2, { className: "h-5 w-5" }),
|
|
1636
|
+
/* @__PURE__ */ jsx23("span", { className: "sr-only", children: "Next image" })
|
|
1637
|
+
]
|
|
1638
|
+
}
|
|
1639
|
+
),
|
|
1640
|
+
/* @__PURE__ */ jsxs16("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-black/50 px-3 py-1 text-xs text-white", children: [
|
|
1641
|
+
currentIndex + 1,
|
|
1642
|
+
" / ",
|
|
1643
|
+
images.length
|
|
1644
|
+
] })
|
|
1645
|
+
] })
|
|
1646
|
+
] })
|
|
1647
|
+
] }) });
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// src/ui/components/prompt-input/context.ts
|
|
1651
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
1652
|
+
var PromptInputController = createContext2(null);
|
|
1653
|
+
var ProviderAttachmentsContext = createContext2(null);
|
|
1654
|
+
var LocalAttachmentsContext = createContext2(null);
|
|
1655
|
+
var usePromptInputController = () => {
|
|
1656
|
+
const ctx = useContext2(PromptInputController);
|
|
1657
|
+
if (!ctx) {
|
|
1658
|
+
throw new Error("Wrap your component inside <PromptInputProvider> to use usePromptInputController().");
|
|
1659
|
+
}
|
|
1660
|
+
return ctx;
|
|
1661
|
+
};
|
|
1662
|
+
var useOptionalPromptInputController = () => useContext2(PromptInputController);
|
|
1663
|
+
var useProviderAttachments = () => {
|
|
1664
|
+
const ctx = useContext2(ProviderAttachmentsContext);
|
|
1665
|
+
if (!ctx) {
|
|
1666
|
+
throw new Error("Wrap your component inside <PromptInputProvider> to use useProviderAttachments().");
|
|
1667
|
+
}
|
|
1668
|
+
return ctx;
|
|
1669
|
+
};
|
|
1670
|
+
var useOptionalProviderAttachments = () => useContext2(ProviderAttachmentsContext);
|
|
1671
|
+
var usePromptInputAttachments = () => {
|
|
1672
|
+
const provider = useOptionalProviderAttachments();
|
|
1673
|
+
const local = useContext2(LocalAttachmentsContext);
|
|
1674
|
+
const context = provider ?? local;
|
|
1675
|
+
if (!context) {
|
|
1676
|
+
throw new Error("usePromptInputAttachments must be used within a PromptInput or PromptInputProvider");
|
|
1677
|
+
}
|
|
1678
|
+
return context;
|
|
1679
|
+
};
|
|
1680
|
+
var DropZoneContext = createContext2({ isDraggingOver: false });
|
|
1681
|
+
var usePromptInputDropZone = () => useContext2(DropZoneContext);
|
|
1682
|
+
|
|
1683
|
+
// src/ui/components/prompt-input/provider.tsx
|
|
1684
|
+
import { useCallback as useCallback4, useMemo, useRef as useRef3, useState as useState4 } from "react";
|
|
1685
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1686
|
+
function generateId() {
|
|
1687
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1688
|
+
return crypto.randomUUID();
|
|
1689
|
+
}
|
|
1690
|
+
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
1691
|
+
}
|
|
1692
|
+
function PromptInputProvider({ initialInput: initialTextInput = "", children }) {
|
|
1693
|
+
const [textInput, setTextInput] = useState4(initialTextInput);
|
|
1694
|
+
const clearInput = useCallback4(() => setTextInput(""), []);
|
|
1695
|
+
const [fileItems, setFileItems] = useState4([]);
|
|
1696
|
+
const fileInputRef = useRef3(null);
|
|
1697
|
+
const openRef = useRef3(() => {
|
|
1698
|
+
});
|
|
1699
|
+
const add = useCallback4((files) => {
|
|
1700
|
+
const incoming = Array.from(files);
|
|
1701
|
+
if (incoming.length === 0) return;
|
|
1702
|
+
setFileItems(
|
|
1703
|
+
(prev) => prev.concat(
|
|
1704
|
+
incoming.map((file) => ({
|
|
1705
|
+
id: generateId(),
|
|
1706
|
+
type: "file",
|
|
1707
|
+
url: URL.createObjectURL(file),
|
|
1708
|
+
mediaType: file.type,
|
|
1709
|
+
filename: file.name
|
|
1710
|
+
}))
|
|
1711
|
+
)
|
|
1712
|
+
);
|
|
1713
|
+
}, []);
|
|
1714
|
+
const remove = useCallback4((id) => {
|
|
1715
|
+
setFileItems((prev) => {
|
|
1716
|
+
const found = prev.find((f) => f.id === id);
|
|
1717
|
+
if (found?.url) URL.revokeObjectURL(found.url);
|
|
1718
|
+
return prev.filter((f) => f.id !== id);
|
|
1719
|
+
});
|
|
1720
|
+
}, []);
|
|
1721
|
+
const clear = useCallback4(() => {
|
|
1722
|
+
setFileItems((prev) => {
|
|
1723
|
+
for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);
|
|
1724
|
+
return [];
|
|
1725
|
+
});
|
|
1726
|
+
}, []);
|
|
1727
|
+
const openFileDialog = useCallback4(() => {
|
|
1728
|
+
openRef.current?.();
|
|
1729
|
+
}, []);
|
|
1730
|
+
const attachments = useMemo(
|
|
1731
|
+
() => ({
|
|
1732
|
+
files: fileItems,
|
|
1733
|
+
add,
|
|
1734
|
+
remove,
|
|
1735
|
+
clear,
|
|
1736
|
+
openFileDialog,
|
|
1737
|
+
fileInputRef
|
|
1738
|
+
}),
|
|
1739
|
+
[fileItems, add, remove, clear, openFileDialog]
|
|
1740
|
+
);
|
|
1741
|
+
const __registerFileInput = useCallback4(
|
|
1742
|
+
(ref, open) => {
|
|
1743
|
+
fileInputRef.current = ref.current;
|
|
1744
|
+
openRef.current = open;
|
|
1745
|
+
},
|
|
1746
|
+
[]
|
|
1747
|
+
);
|
|
1748
|
+
const controller = useMemo(
|
|
1749
|
+
() => ({
|
|
1750
|
+
textInput: { value: textInput, setInput: setTextInput, clear: clearInput },
|
|
1751
|
+
attachments,
|
|
1752
|
+
__registerFileInput
|
|
1753
|
+
}),
|
|
1754
|
+
[textInput, clearInput, attachments, __registerFileInput]
|
|
1755
|
+
);
|
|
1756
|
+
return /* @__PURE__ */ jsx24(PromptInputController.Provider, { value: controller, children: /* @__PURE__ */ jsx24(ProviderAttachmentsContext.Provider, { value: attachments, children }) });
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// src/ui/components/prompt-input/prompt-input.tsx
|
|
1760
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState5 } from "react";
|
|
1761
|
+
import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1762
|
+
function generateId2() {
|
|
1763
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1764
|
+
return crypto.randomUUID();
|
|
1765
|
+
}
|
|
1766
|
+
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
1767
|
+
}
|
|
1768
|
+
var PromptInput = ({
|
|
1769
|
+
className,
|
|
1770
|
+
accept,
|
|
1771
|
+
multiple,
|
|
1772
|
+
globalDrop,
|
|
1773
|
+
syncHiddenInput,
|
|
1774
|
+
maxFiles,
|
|
1775
|
+
maxFileSize,
|
|
1776
|
+
dragListenerTarget,
|
|
1777
|
+
onError,
|
|
1778
|
+
onSubmit,
|
|
1779
|
+
children,
|
|
1780
|
+
...props
|
|
1781
|
+
}) => {
|
|
1782
|
+
const controller = useOptionalPromptInputController();
|
|
1783
|
+
const usingProvider = !!controller;
|
|
1784
|
+
const inputRef = useRef4(null);
|
|
1785
|
+
const formRef = useRef4(null);
|
|
1786
|
+
const [items, setItems] = useState5([]);
|
|
1787
|
+
const files = usingProvider ? controller.attachments.files : items;
|
|
1788
|
+
const [isDraggingOver, setIsDraggingOver] = useState5(false);
|
|
1789
|
+
const dragCounter = useRef4(0);
|
|
1790
|
+
const openFileDialogLocal = useCallback5(() => {
|
|
1791
|
+
inputRef.current?.click();
|
|
1792
|
+
}, []);
|
|
1793
|
+
const matchesAccept = useCallback5(
|
|
1794
|
+
(f) => {
|
|
1795
|
+
if (!accept || accept.trim() === "") return true;
|
|
1796
|
+
const patterns = accept.split(",").map((p) => p.trim()).filter(Boolean);
|
|
1797
|
+
for (const pattern of patterns) {
|
|
1798
|
+
if (pattern.endsWith("/*")) {
|
|
1799
|
+
const prefix = pattern.slice(0, -1);
|
|
1800
|
+
if (f.type.startsWith(prefix)) return true;
|
|
1801
|
+
} else if (pattern.startsWith(".")) {
|
|
1802
|
+
const ext = f.name.toLowerCase().split(".").pop();
|
|
1803
|
+
if (ext && `.${ext}` === pattern.toLowerCase()) return true;
|
|
1804
|
+
} else if (f.type === pattern) {
|
|
1805
|
+
return true;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return false;
|
|
1809
|
+
},
|
|
1810
|
+
[accept]
|
|
1811
|
+
);
|
|
1812
|
+
const addLocal = useCallback5(
|
|
1813
|
+
(fileList) => {
|
|
1814
|
+
const incoming = Array.from(fileList);
|
|
1815
|
+
const accepted = incoming.filter((f) => matchesAccept(f));
|
|
1816
|
+
if (incoming.length && accepted.length === 0) {
|
|
1817
|
+
onError?.({ code: "accept", message: "No files match the accepted types." });
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
|
|
1821
|
+
const sized = accepted.filter(withinSize);
|
|
1822
|
+
if (accepted.length > 0 && sized.length === 0) {
|
|
1823
|
+
onError?.({ code: "max_file_size", message: "All files exceed the maximum size." });
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
setItems((prev) => {
|
|
1827
|
+
const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : void 0;
|
|
1828
|
+
const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
|
|
1829
|
+
if (typeof capacity === "number" && sized.length > capacity) {
|
|
1830
|
+
onError?.({ code: "max_files", message: "Too many files. Some were not added." });
|
|
1831
|
+
}
|
|
1832
|
+
const next = [];
|
|
1833
|
+
for (const file of capped) {
|
|
1834
|
+
next.push({
|
|
1835
|
+
id: generateId2(),
|
|
1836
|
+
type: "file",
|
|
1837
|
+
url: URL.createObjectURL(file),
|
|
1838
|
+
mediaType: file.type,
|
|
1839
|
+
filename: file.name
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
return prev.concat(next);
|
|
1843
|
+
});
|
|
1844
|
+
},
|
|
1845
|
+
[matchesAccept, maxFiles, maxFileSize, onError]
|
|
1846
|
+
);
|
|
1847
|
+
const add = usingProvider ? (fileList) => controller.attachments.add(fileList) : addLocal;
|
|
1848
|
+
const remove = usingProvider ? (id) => controller.attachments.remove(id) : (id) => setItems((prev) => {
|
|
1849
|
+
const found = prev.find((file) => file.id === id);
|
|
1850
|
+
if (found?.url) URL.revokeObjectURL(found.url);
|
|
1851
|
+
return prev.filter((file) => file.id !== id);
|
|
1852
|
+
});
|
|
1853
|
+
const clear = usingProvider ? () => controller.attachments.clear() : () => setItems((prev) => {
|
|
1854
|
+
for (const file of prev) if (file.url) URL.revokeObjectURL(file.url);
|
|
1855
|
+
return [];
|
|
1856
|
+
});
|
|
1857
|
+
const openFileDialog = usingProvider ? () => controller.attachments.openFileDialog() : openFileDialogLocal;
|
|
1858
|
+
useEffect4(() => {
|
|
1859
|
+
if (!usingProvider) return;
|
|
1860
|
+
controller.__registerFileInput(inputRef, () => inputRef.current?.click());
|
|
1861
|
+
}, [usingProvider, controller]);
|
|
1862
|
+
useEffect4(() => {
|
|
1863
|
+
if (syncHiddenInput && inputRef.current && files.length === 0) {
|
|
1864
|
+
inputRef.current.value = "";
|
|
1865
|
+
}
|
|
1866
|
+
}, [files, syncHiddenInput]);
|
|
1867
|
+
useEffect4(() => {
|
|
1868
|
+
if (globalDrop) return;
|
|
1869
|
+
const form = formRef.current;
|
|
1870
|
+
if (!form) return;
|
|
1871
|
+
const onDragOver = (e) => {
|
|
1872
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
1873
|
+
};
|
|
1874
|
+
const onDragEnter = (e) => {
|
|
1875
|
+
if (e.dataTransfer?.types?.includes("Files")) {
|
|
1876
|
+
e.preventDefault();
|
|
1877
|
+
dragCounter.current++;
|
|
1878
|
+
if (dragCounter.current === 1) setIsDraggingOver(true);
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
const onDragLeave = (_e) => {
|
|
1882
|
+
dragCounter.current--;
|
|
1883
|
+
if (dragCounter.current === 0) setIsDraggingOver(false);
|
|
1884
|
+
};
|
|
1885
|
+
const onDrop = (e) => {
|
|
1886
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
1887
|
+
dragCounter.current = 0;
|
|
1888
|
+
setIsDraggingOver(false);
|
|
1889
|
+
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
|
|
1890
|
+
};
|
|
1891
|
+
form.addEventListener("dragover", onDragOver);
|
|
1892
|
+
form.addEventListener("dragenter", onDragEnter);
|
|
1893
|
+
form.addEventListener("dragleave", onDragLeave);
|
|
1894
|
+
form.addEventListener("drop", onDrop);
|
|
1895
|
+
return () => {
|
|
1896
|
+
form.removeEventListener("dragover", onDragOver);
|
|
1897
|
+
form.removeEventListener("dragenter", onDragEnter);
|
|
1898
|
+
form.removeEventListener("dragleave", onDragLeave);
|
|
1899
|
+
form.removeEventListener("drop", onDrop);
|
|
1900
|
+
};
|
|
1901
|
+
}, [add, globalDrop]);
|
|
1902
|
+
useEffect4(() => {
|
|
1903
|
+
if (!globalDrop) return;
|
|
1904
|
+
const target = dragListenerTarget?.current ?? document;
|
|
1905
|
+
const onDragOver = (e) => {
|
|
1906
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
1907
|
+
};
|
|
1908
|
+
const onDragEnter = (e) => {
|
|
1909
|
+
if (e.dataTransfer?.types?.includes("Files")) {
|
|
1910
|
+
e.preventDefault();
|
|
1911
|
+
dragCounter.current++;
|
|
1912
|
+
if (dragCounter.current === 1) setIsDraggingOver(true);
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
const onDragLeave = (_e) => {
|
|
1916
|
+
dragCounter.current--;
|
|
1917
|
+
if (dragCounter.current === 0) setIsDraggingOver(false);
|
|
1918
|
+
};
|
|
1919
|
+
const onDrop = (e) => {
|
|
1920
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
1921
|
+
dragCounter.current = 0;
|
|
1922
|
+
setIsDraggingOver(false);
|
|
1923
|
+
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
|
|
1924
|
+
};
|
|
1925
|
+
target.addEventListener("dragover", onDragOver);
|
|
1926
|
+
target.addEventListener("dragenter", onDragEnter);
|
|
1927
|
+
target.addEventListener("dragleave", onDragLeave);
|
|
1928
|
+
target.addEventListener("drop", onDrop);
|
|
1929
|
+
return () => {
|
|
1930
|
+
target.removeEventListener("dragover", onDragOver);
|
|
1931
|
+
target.removeEventListener("dragenter", onDragEnter);
|
|
1932
|
+
target.removeEventListener("dragleave", onDragLeave);
|
|
1933
|
+
target.removeEventListener("drop", onDrop);
|
|
1934
|
+
};
|
|
1935
|
+
}, [add, globalDrop, dragListenerTarget]);
|
|
1936
|
+
const filesRef = useRef4(files);
|
|
1937
|
+
filesRef.current = files;
|
|
1938
|
+
useEffect4(
|
|
1939
|
+
() => () => {
|
|
1940
|
+
if (!usingProvider) {
|
|
1941
|
+
for (const f of filesRef.current) if (f.url) URL.revokeObjectURL(f.url);
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
[usingProvider]
|
|
1945
|
+
);
|
|
1946
|
+
const handleChange = (event) => {
|
|
1947
|
+
if (event.currentTarget.files) add(event.currentTarget.files);
|
|
1948
|
+
};
|
|
1949
|
+
const convertBlobUrlToDataUrl = async (url) => {
|
|
1950
|
+
const response = await fetch(url);
|
|
1951
|
+
const blob = await response.blob();
|
|
1952
|
+
return new Promise((resolve, reject) => {
|
|
1953
|
+
const reader = new FileReader();
|
|
1954
|
+
reader.onloadend = () => resolve(reader.result);
|
|
1955
|
+
reader.onerror = reject;
|
|
1956
|
+
reader.readAsDataURL(blob);
|
|
1957
|
+
});
|
|
1958
|
+
};
|
|
1959
|
+
const ctx = useMemo2(
|
|
1960
|
+
() => ({
|
|
1961
|
+
files: files.map((item) => ({ ...item, id: item.id })),
|
|
1962
|
+
add,
|
|
1963
|
+
remove,
|
|
1964
|
+
clear,
|
|
1965
|
+
openFileDialog,
|
|
1966
|
+
fileInputRef: inputRef
|
|
1967
|
+
}),
|
|
1968
|
+
[files, add, remove, clear, openFileDialog]
|
|
1969
|
+
);
|
|
1970
|
+
const handleSubmit = (event) => {
|
|
1971
|
+
event.preventDefault();
|
|
1972
|
+
const form = event.currentTarget;
|
|
1973
|
+
const text = usingProvider ? controller.textInput.value : (() => {
|
|
1974
|
+
const formData = new FormData(form);
|
|
1975
|
+
return formData.get("message") || "";
|
|
1976
|
+
})();
|
|
1977
|
+
if (!usingProvider) form.reset();
|
|
1978
|
+
Promise.all(
|
|
1979
|
+
files.map(async ({ id, ...item }) => {
|
|
1980
|
+
if (item.url && item.url.startsWith("blob:")) {
|
|
1981
|
+
return { ...item, url: await convertBlobUrlToDataUrl(item.url) };
|
|
1982
|
+
}
|
|
1983
|
+
return item;
|
|
1984
|
+
})
|
|
1985
|
+
).then((convertedFiles) => {
|
|
1986
|
+
try {
|
|
1987
|
+
const result = onSubmit({ text, files: convertedFiles }, event);
|
|
1988
|
+
if (result instanceof Promise) {
|
|
1989
|
+
result.then(() => {
|
|
1990
|
+
clear();
|
|
1991
|
+
if (usingProvider) controller.textInput.clear();
|
|
1992
|
+
}).catch(() => {
|
|
1993
|
+
});
|
|
1994
|
+
} else {
|
|
1995
|
+
clear();
|
|
1996
|
+
if (usingProvider) controller.textInput.clear();
|
|
1997
|
+
}
|
|
1998
|
+
} catch {
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
};
|
|
2002
|
+
const dropZoneValue = useMemo2(() => ({ isDraggingOver }), [isDraggingOver]);
|
|
2003
|
+
const inner = /* @__PURE__ */ jsx25(DropZoneContext.Provider, { value: dropZoneValue, children: /* @__PURE__ */ jsxs17("form", { className: cn("relative w-full", className), onSubmit: handleSubmit, ref: formRef, ...props, children: [
|
|
2004
|
+
/* @__PURE__ */ jsx25(
|
|
2005
|
+
"input",
|
|
2006
|
+
{
|
|
2007
|
+
accept,
|
|
2008
|
+
"aria-label": "Upload files",
|
|
2009
|
+
className: "hidden",
|
|
2010
|
+
multiple,
|
|
2011
|
+
onChange: handleChange,
|
|
2012
|
+
ref: inputRef,
|
|
2013
|
+
title: "Upload files",
|
|
2014
|
+
type: "file"
|
|
2015
|
+
}
|
|
2016
|
+
),
|
|
2017
|
+
/* @__PURE__ */ jsx25(InputGroup, { children })
|
|
2018
|
+
] }) });
|
|
2019
|
+
return usingProvider ? inner : /* @__PURE__ */ jsx25(LocalAttachmentsContext.Provider, { value: ctx, children: inner });
|
|
2020
|
+
};
|
|
2021
|
+
|
|
2022
|
+
// src/ui/components/prompt-input/textarea.tsx
|
|
2023
|
+
import { useState as useState6 } from "react";
|
|
2024
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
2025
|
+
var PromptInputTextarea = ({
|
|
2026
|
+
onChange,
|
|
2027
|
+
className,
|
|
2028
|
+
placeholder = "What would you like to know?",
|
|
2029
|
+
...props
|
|
2030
|
+
}) => {
|
|
2031
|
+
const controller = useOptionalPromptInputController();
|
|
2032
|
+
const attachments = usePromptInputAttachments();
|
|
2033
|
+
const [isComposing, setIsComposing] = useState6(false);
|
|
2034
|
+
const handleKeyDown = (e) => {
|
|
2035
|
+
if (e.key === "Enter") {
|
|
2036
|
+
if (isComposing || e.nativeEvent.isComposing) return;
|
|
2037
|
+
if (e.shiftKey) return;
|
|
2038
|
+
e.preventDefault();
|
|
2039
|
+
e.currentTarget.form?.requestSubmit();
|
|
2040
|
+
}
|
|
2041
|
+
if (e.key === "Backspace" && e.currentTarget.value === "" && attachments.files.length > 0) {
|
|
2042
|
+
e.preventDefault();
|
|
2043
|
+
const lastAttachment = attachments.files.at(-1);
|
|
2044
|
+
if (lastAttachment) attachments.remove(lastAttachment.id);
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
const handlePaste = (event) => {
|
|
2048
|
+
const items = event.clipboardData?.items;
|
|
2049
|
+
if (!items) return;
|
|
2050
|
+
const files = [];
|
|
2051
|
+
for (const item of items) {
|
|
2052
|
+
if (item.kind === "file") {
|
|
2053
|
+
const file = item.getAsFile();
|
|
2054
|
+
if (file) files.push(file);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
if (files.length > 0) {
|
|
2058
|
+
event.preventDefault();
|
|
2059
|
+
attachments.add(files);
|
|
2060
|
+
}
|
|
2061
|
+
};
|
|
2062
|
+
const controlledProps = controller ? {
|
|
2063
|
+
value: controller.textInput.value,
|
|
2064
|
+
onChange: (e) => {
|
|
2065
|
+
controller.textInput.setInput(e.currentTarget.value);
|
|
2066
|
+
onChange?.(e);
|
|
2067
|
+
}
|
|
2068
|
+
} : { onChange };
|
|
2069
|
+
return /* @__PURE__ */ jsx26(
|
|
2070
|
+
InputGroupTextarea,
|
|
2071
|
+
{
|
|
2072
|
+
className: cn("[field-sizing:content] max-h-48 text-base", className),
|
|
2073
|
+
rows: 1,
|
|
2074
|
+
name: "message",
|
|
2075
|
+
onCompositionEnd: () => setIsComposing(false),
|
|
2076
|
+
onCompositionStart: () => setIsComposing(true),
|
|
2077
|
+
onKeyDown: handleKeyDown,
|
|
2078
|
+
onPaste: handlePaste,
|
|
2079
|
+
placeholder,
|
|
2080
|
+
...props,
|
|
2081
|
+
...controlledProps
|
|
2082
|
+
}
|
|
2083
|
+
);
|
|
2084
|
+
};
|
|
2085
|
+
|
|
2086
|
+
// src/ui/components/prompt-input/attachments.tsx
|
|
2087
|
+
import { ImageIcon, MicIcon, PaperclipIcon, XIcon as XIcon2 } from "lucide-react";
|
|
2088
|
+
import { Fragment as Fragment4 } from "react";
|
|
2089
|
+
import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2090
|
+
function PromptInputAttachment({ data, className, ...props }) {
|
|
2091
|
+
const attachments = usePromptInputAttachments();
|
|
2092
|
+
const filename = data.filename || "";
|
|
2093
|
+
const isImage = data.mediaType?.startsWith("image/") && data.url;
|
|
2094
|
+
const isAudio = data.mediaType?.startsWith("audio/");
|
|
2095
|
+
const attachmentLabel = filename || (isImage ? "Image" : isAudio ? "Audio" : "Attachment");
|
|
2096
|
+
return /* @__PURE__ */ jsxs18(HoverCard, { openDelay: 0, closeDelay: 0, children: [
|
|
2097
|
+
/* @__PURE__ */ jsx27(HoverCardTrigger, { asChild: true, children: /* @__PURE__ */ jsxs18(
|
|
2098
|
+
"div",
|
|
2099
|
+
{
|
|
2100
|
+
className: cn(
|
|
2101
|
+
"group relative flex h-8 cursor-default select-none items-center gap-1.5 rounded-md border border-border px-1.5 font-medium text-sm transition-all hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
2102
|
+
className
|
|
2103
|
+
),
|
|
2104
|
+
...props,
|
|
2105
|
+
children: [
|
|
2106
|
+
/* @__PURE__ */ jsxs18("div", { className: "relative size-5 shrink-0", children: [
|
|
2107
|
+
/* @__PURE__ */ jsx27("div", { className: "absolute inset-0 flex size-5 items-center justify-center overflow-hidden rounded bg-background transition-opacity group-hover:opacity-0", children: isImage ? /* @__PURE__ */ jsx27("img", { alt: filename || "attachment", className: "size-5 object-cover", height: 20, src: data.url, width: 20 }) : isAudio ? /* @__PURE__ */ jsx27("div", { className: "flex size-5 items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx27(MicIcon, { className: "size-3" }) }) : /* @__PURE__ */ jsx27("div", { className: "flex size-5 items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx27(PaperclipIcon, { className: "size-3" }) }) }),
|
|
2108
|
+
/* @__PURE__ */ jsxs18(
|
|
2109
|
+
Button,
|
|
2110
|
+
{
|
|
2111
|
+
"aria-label": "Remove attachment",
|
|
2112
|
+
className: "absolute inset-0 size-5 cursor-pointer rounded p-0 opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100 [&>svg]:size-2.5",
|
|
2113
|
+
onClick: (e) => {
|
|
2114
|
+
e.stopPropagation();
|
|
2115
|
+
attachments.remove(data.id);
|
|
2116
|
+
},
|
|
2117
|
+
type: "button",
|
|
2118
|
+
variant: "ghost",
|
|
2119
|
+
children: [
|
|
2120
|
+
/* @__PURE__ */ jsx27(XIcon2, {}),
|
|
2121
|
+
/* @__PURE__ */ jsx27("span", { className: "sr-only", children: "Remove" })
|
|
2122
|
+
]
|
|
2123
|
+
}
|
|
2124
|
+
)
|
|
2125
|
+
] }),
|
|
2126
|
+
/* @__PURE__ */ jsx27("span", { className: "flex-1 truncate", children: attachmentLabel })
|
|
2127
|
+
]
|
|
2128
|
+
},
|
|
2129
|
+
data.id
|
|
2130
|
+
) }),
|
|
2131
|
+
/* @__PURE__ */ jsx27(HoverCardContent, { align: "start", className: "w-auto p-2", children: /* @__PURE__ */ jsxs18("div", { className: "w-auto space-y-3", children: [
|
|
2132
|
+
isImage && /* @__PURE__ */ jsx27("div", { className: "flex max-h-96 w-96 items-center justify-center overflow-hidden rounded-md border", children: /* @__PURE__ */ jsx27("img", { alt: filename || "attachment preview", className: "max-h-full max-w-full object-contain", height: 384, src: data.url, width: 448 }) }),
|
|
2133
|
+
isAudio && data.url && /* @__PURE__ */ jsx27("div", { className: "w-64", children: /* @__PURE__ */ jsx27("audio", { src: data.url, controls: true, className: "w-full" }) }),
|
|
2134
|
+
/* @__PURE__ */ jsx27("div", { className: "flex items-center gap-2.5", children: /* @__PURE__ */ jsxs18("div", { className: "min-w-0 flex-1 space-y-1 px-0.5", children: [
|
|
2135
|
+
/* @__PURE__ */ jsx27("h4", { className: "truncate font-semibold text-sm leading-none", children: filename || (isImage ? "Image" : "Attachment") }),
|
|
2136
|
+
data.mediaType && /* @__PURE__ */ jsx27("p", { className: "truncate font-mono text-muted-foreground text-xs", children: data.mediaType })
|
|
2137
|
+
] }) })
|
|
2138
|
+
] }) })
|
|
2139
|
+
] });
|
|
2140
|
+
}
|
|
2141
|
+
function PromptInputAttachments({ children }) {
|
|
2142
|
+
const attachments = usePromptInputAttachments();
|
|
2143
|
+
if (!attachments.files.length) return null;
|
|
2144
|
+
return attachments.files.map((file) => /* @__PURE__ */ jsx27(Fragment4, { children: children(file) }, file.id));
|
|
2145
|
+
}
|
|
2146
|
+
var PromptInputActionAddAttachments = ({
|
|
2147
|
+
label = "Add photos or files",
|
|
2148
|
+
...props
|
|
2149
|
+
}) => {
|
|
2150
|
+
const attachments = usePromptInputAttachments();
|
|
2151
|
+
return /* @__PURE__ */ jsxs18(
|
|
2152
|
+
DropdownMenuItem,
|
|
2153
|
+
{
|
|
2154
|
+
...props,
|
|
2155
|
+
onSelect: () => {
|
|
2156
|
+
attachments.openFileDialog();
|
|
2157
|
+
},
|
|
2158
|
+
children: [
|
|
2159
|
+
/* @__PURE__ */ jsx27(ImageIcon, { className: "mr-2 size-4" }),
|
|
2160
|
+
" ",
|
|
2161
|
+
label
|
|
2162
|
+
]
|
|
2163
|
+
}
|
|
2164
|
+
);
|
|
2165
|
+
};
|
|
2166
|
+
|
|
2167
|
+
// src/ui/components/prompt-input/footer.tsx
|
|
2168
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2169
|
+
var PromptInputBody = ({ className, ...props }) => /* @__PURE__ */ jsx28("div", { className: cn("contents", className), ...props });
|
|
2170
|
+
var PromptInputHeader = ({ className, ...props }) => /* @__PURE__ */ jsx28(InputGroupAddon, { align: "block-start", className: cn("flex-wrap gap-1 empty:hidden empty:p-0", className), ...props });
|
|
2171
|
+
var PromptInputFooter = ({ className, ...props }) => /* @__PURE__ */ jsx28(InputGroupAddon, { align: "block-end", className: cn("justify-between gap-1", className), ...props });
|
|
2172
|
+
var PromptInputTools = ({ className, ...props }) => /* @__PURE__ */ jsx28("div", { className: cn("flex items-center gap-1", className), ...props });
|
|
2173
|
+
|
|
2174
|
+
// src/ui/components/prompt-input/buttons.tsx
|
|
2175
|
+
import { Loader2Icon, PlusIcon, SendIcon, SquareIcon, XIcon as XIcon3 } from "lucide-react";
|
|
2176
|
+
import { Children } from "react";
|
|
2177
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
2178
|
+
var PromptInputButton = ({
|
|
2179
|
+
variant = "ghost",
|
|
2180
|
+
className,
|
|
2181
|
+
size,
|
|
2182
|
+
...props
|
|
2183
|
+
}) => {
|
|
2184
|
+
const newSize = size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
|
|
2185
|
+
return /* @__PURE__ */ jsx29(InputGroupButton, { className: cn(className), size: newSize, type: "button", variant, ...props });
|
|
2186
|
+
};
|
|
2187
|
+
var PromptInputSubmit = ({
|
|
2188
|
+
className,
|
|
2189
|
+
variant = "default",
|
|
2190
|
+
size = "icon-sm",
|
|
2191
|
+
status,
|
|
2192
|
+
children,
|
|
2193
|
+
...props
|
|
2194
|
+
}) => {
|
|
2195
|
+
let Icon2 = /* @__PURE__ */ jsx29(SendIcon, { className: "size-4" });
|
|
2196
|
+
if (status === "submitted") {
|
|
2197
|
+
Icon2 = /* @__PURE__ */ jsx29(Loader2Icon, { className: "size-4 animate-spin" });
|
|
2198
|
+
} else if (status === "streaming") {
|
|
2199
|
+
Icon2 = /* @__PURE__ */ jsx29(SquareIcon, { className: "size-4" });
|
|
2200
|
+
} else if (status === "error") {
|
|
2201
|
+
Icon2 = /* @__PURE__ */ jsx29(XIcon3, { className: "size-4" });
|
|
2202
|
+
}
|
|
2203
|
+
return /* @__PURE__ */ jsx29(InputGroupButton, { "aria-label": "Submit", className: cn(className), size, type: "submit", variant, ...props, children: children ?? Icon2 });
|
|
2204
|
+
};
|
|
2205
|
+
var PromptInputActionMenu = (props) => /* @__PURE__ */ jsx29(DropdownMenu, { ...props });
|
|
2206
|
+
var PromptInputActionMenuTrigger = ({
|
|
2207
|
+
className,
|
|
2208
|
+
children,
|
|
2209
|
+
...props
|
|
2210
|
+
}) => /* @__PURE__ */ jsx29(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(PromptInputButton, { className, ...props, children: children ?? /* @__PURE__ */ jsx29(PlusIcon, { className: "size-4" }) }) });
|
|
2211
|
+
var PromptInputActionMenuContent = ({
|
|
2212
|
+
className,
|
|
2213
|
+
...props
|
|
2214
|
+
}) => /* @__PURE__ */ jsx29(DropdownMenuContent, { align: "start", className: cn(className), ...props });
|
|
2215
|
+
var PromptInputActionMenuItem = ({
|
|
2216
|
+
className,
|
|
2217
|
+
...props
|
|
2218
|
+
}) => /* @__PURE__ */ jsx29(DropdownMenuItem, { className: cn(className), ...props });
|
|
2219
|
+
|
|
2220
|
+
// src/ui/components/prompt-input/drop-zone.tsx
|
|
2221
|
+
import { Upload } from "lucide-react";
|
|
2222
|
+
import { createPortal } from "react-dom";
|
|
2223
|
+
import { jsx as jsx30, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2224
|
+
var PromptInputDropZone = ({
|
|
2225
|
+
label = "Drop files here",
|
|
2226
|
+
className,
|
|
2227
|
+
container,
|
|
2228
|
+
...props
|
|
2229
|
+
}) => {
|
|
2230
|
+
const { isDraggingOver } = usePromptInputDropZone();
|
|
2231
|
+
const overlay = /* @__PURE__ */ jsxs19(
|
|
2232
|
+
"div",
|
|
2233
|
+
{
|
|
2234
|
+
className: cn(
|
|
2235
|
+
"absolute inset-0 z-10 pointer-events-none",
|
|
2236
|
+
"flex flex-col items-center justify-center gap-2",
|
|
2237
|
+
"border-2 border-dashed border-primary rounded-xl",
|
|
2238
|
+
"bg-primary/5 backdrop-blur-[2px] text-primary",
|
|
2239
|
+
"transition-opacity duration-200",
|
|
2240
|
+
isDraggingOver ? "opacity-100" : "opacity-0",
|
|
2241
|
+
className
|
|
2242
|
+
),
|
|
2243
|
+
"aria-hidden": !isDraggingOver,
|
|
2244
|
+
...props,
|
|
2245
|
+
children: [
|
|
2246
|
+
/* @__PURE__ */ jsx30(Upload, { className: "size-14" }),
|
|
2247
|
+
/* @__PURE__ */ jsx30("span", { className: "text-sm font-medium mt-2", children: label })
|
|
2248
|
+
]
|
|
2249
|
+
}
|
|
2250
|
+
);
|
|
2251
|
+
if (container?.current) {
|
|
2252
|
+
return createPortal(overlay, container.current);
|
|
2253
|
+
}
|
|
2254
|
+
return overlay;
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
// src/ui/components/prompt-input/speech.tsx
|
|
2258
|
+
import { MicIcon as MicIcon2 } from "lucide-react";
|
|
2259
|
+
import { useCallback as useCallback6, useEffect as useEffect5, useRef as useRef5, useState as useState7 } from "react";
|
|
2260
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
2261
|
+
var PromptInputSpeechButton = ({
|
|
2262
|
+
className,
|
|
2263
|
+
onTranscriptionChange,
|
|
2264
|
+
lang = "en-US",
|
|
2265
|
+
...props
|
|
2266
|
+
}) => {
|
|
2267
|
+
const [isListening, setIsListening] = useState7(false);
|
|
2268
|
+
const [recognition, setRecognition] = useState7(null);
|
|
2269
|
+
const recognitionRef = useRef5(null);
|
|
2270
|
+
const onTranscriptionChangeRef = useRef5(onTranscriptionChange);
|
|
2271
|
+
onTranscriptionChangeRef.current = onTranscriptionChange;
|
|
2272
|
+
const controller = useOptionalPromptInputController();
|
|
2273
|
+
useEffect5(() => {
|
|
2274
|
+
if (typeof window !== "undefined" && ("SpeechRecognition" in window || "webkitSpeechRecognition" in window)) {
|
|
2275
|
+
const SpeechRecognitionCtor = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
2276
|
+
const speechRecognition = new SpeechRecognitionCtor();
|
|
2277
|
+
speechRecognition.continuous = true;
|
|
2278
|
+
speechRecognition.interimResults = true;
|
|
2279
|
+
speechRecognition.lang = lang;
|
|
2280
|
+
speechRecognition.onstart = () => setIsListening(true);
|
|
2281
|
+
speechRecognition.onend = () => setIsListening(false);
|
|
2282
|
+
speechRecognition.onresult = (event) => {
|
|
2283
|
+
let finalTranscript = "";
|
|
2284
|
+
const results = Array.from(event.results);
|
|
2285
|
+
for (const result of results) {
|
|
2286
|
+
if (result.isFinal) finalTranscript += result[0]?.transcript ?? "";
|
|
2287
|
+
}
|
|
2288
|
+
if (finalTranscript) {
|
|
2289
|
+
if (controller) {
|
|
2290
|
+
const current = controller.textInput.value;
|
|
2291
|
+
const newValue = current + (current ? " " : "") + finalTranscript;
|
|
2292
|
+
controller.textInput.setInput(newValue);
|
|
2293
|
+
}
|
|
2294
|
+
onTranscriptionChangeRef.current?.(finalTranscript);
|
|
2295
|
+
}
|
|
2296
|
+
};
|
|
2297
|
+
speechRecognition.onerror = (event) => {
|
|
2298
|
+
console.error("Speech recognition error:", event.error);
|
|
2299
|
+
setIsListening(false);
|
|
2300
|
+
};
|
|
2301
|
+
recognitionRef.current = speechRecognition;
|
|
2302
|
+
setRecognition(speechRecognition);
|
|
2303
|
+
}
|
|
2304
|
+
return () => {
|
|
2305
|
+
if (recognitionRef.current) recognitionRef.current.stop();
|
|
2306
|
+
};
|
|
2307
|
+
}, [lang]);
|
|
2308
|
+
const toggleListening = useCallback6(() => {
|
|
2309
|
+
if (!recognition) return;
|
|
2310
|
+
if (isListening) recognition.stop();
|
|
2311
|
+
else recognition.start();
|
|
2312
|
+
}, [recognition, isListening]);
|
|
2313
|
+
return /* @__PURE__ */ jsx31(
|
|
2314
|
+
PromptInputButton,
|
|
2315
|
+
{
|
|
2316
|
+
className: cn(
|
|
2317
|
+
"relative transition-all duration-200",
|
|
2318
|
+
isListening && "animate-pulse bg-accent text-accent-foreground",
|
|
2319
|
+
className
|
|
2320
|
+
),
|
|
2321
|
+
disabled: !recognition,
|
|
2322
|
+
onClick: toggleListening,
|
|
2323
|
+
...props,
|
|
2324
|
+
children: /* @__PURE__ */ jsx31(MicIcon2, { className: "size-4" })
|
|
2325
|
+
}
|
|
2326
|
+
);
|
|
2327
|
+
};
|
|
2328
|
+
|
|
2329
|
+
// src/ui/components/prompt-input/model-select.tsx
|
|
2330
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
2331
|
+
var PromptInputModelSelect = (props) => /* @__PURE__ */ jsx32(Select, { ...props });
|
|
2332
|
+
var PromptInputModelSelectTrigger = ({
|
|
2333
|
+
className,
|
|
2334
|
+
...props
|
|
2335
|
+
}) => /* @__PURE__ */ jsx32(
|
|
2336
|
+
SelectTrigger,
|
|
2337
|
+
{
|
|
2338
|
+
className: cn(
|
|
2339
|
+
"border-none bg-transparent font-medium text-muted-foreground shadow-none transition-colors",
|
|
2340
|
+
'hover:bg-accent hover:text-foreground [&[aria-expanded="true"]]:bg-accent [&[aria-expanded="true"]]:text-foreground',
|
|
2341
|
+
className
|
|
2342
|
+
),
|
|
2343
|
+
...props
|
|
2344
|
+
}
|
|
2345
|
+
);
|
|
2346
|
+
var PromptInputModelSelectContent = ({ className, ...props }) => /* @__PURE__ */ jsx32(SelectContent, { className: cn(className), ...props });
|
|
2347
|
+
var PromptInputModelSelectItem = ({ className, ...props }) => /* @__PURE__ */ jsx32(SelectItem, { className: cn(className), ...props });
|
|
2348
|
+
var PromptInputModelSelectValue = ({ className, ...props }) => /* @__PURE__ */ jsx32(SelectValue, { className: cn(className), ...props });
|
|
2349
|
+
|
|
2350
|
+
// src/ui/components/prompt-input/tabs.tsx
|
|
2351
|
+
import { jsx as jsx33 } from "react/jsx-runtime";
|
|
2352
|
+
var PromptInputTabsList = ({ className, ...props }) => /* @__PURE__ */ jsx33("div", { className: cn(className), ...props });
|
|
2353
|
+
var PromptInputTab = ({ className, ...props }) => /* @__PURE__ */ jsx33("div", { className: cn(className), ...props });
|
|
2354
|
+
var PromptInputTabLabel = ({ className, ...props }) => /* @__PURE__ */ jsx33("h3", { className: cn("mb-2 px-3 font-medium text-muted-foreground text-xs", className), ...props });
|
|
2355
|
+
var PromptInputTabBody = ({ className, ...props }) => /* @__PURE__ */ jsx33("div", { className: cn("space-y-1", className), ...props });
|
|
2356
|
+
var PromptInputTabItem = ({ className, ...props }) => /* @__PURE__ */ jsx33("div", { className: cn("flex items-center gap-2 px-3 py-2 text-xs hover:bg-accent", className), ...props });
|
|
2357
|
+
|
|
2358
|
+
// src/ui/components/prompt-input/command.tsx
|
|
2359
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
2360
|
+
var PromptInputCommand = ({ className, ...props }) => /* @__PURE__ */ jsx34(Command, { className: cn(className), ...props });
|
|
2361
|
+
var PromptInputCommandInput = ({ className, ...props }) => /* @__PURE__ */ jsx34(CommandInput, { className: cn(className), ...props });
|
|
2362
|
+
var PromptInputCommandList = ({ className, ...props }) => /* @__PURE__ */ jsx34(CommandList, { className: cn(className), ...props });
|
|
2363
|
+
var PromptInputCommandEmpty = ({ className, ...props }) => /* @__PURE__ */ jsx34(CommandEmpty, { className: cn(className), ...props });
|
|
2364
|
+
var PromptInputCommandGroup = ({ className, ...props }) => /* @__PURE__ */ jsx34(CommandGroup, { className: cn(className), ...props });
|
|
2365
|
+
var PromptInputCommandItem = ({ className, ...props }) => /* @__PURE__ */ jsx34(CommandItem, { className: cn(className), ...props });
|
|
2366
|
+
var PromptInputCommandSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx34(CommandSeparator, { className: cn(className), ...props });
|
|
2367
|
+
|
|
2368
|
+
// src/ui/composed/agno-chat/agno-chat.tsx
|
|
2369
|
+
import { useCallback as useCallback7, useMemo as useMemo3, useRef as useRef6 } from "react";
|
|
2370
|
+
import { useAgnoChat, useAgnoToolExecution } from "@rodrigocoliveira/agno-react";
|
|
2371
|
+
|
|
2372
|
+
// src/ui/composed/agno-chat/context.ts
|
|
2373
|
+
import { createContext as createContext3, useContext as useContext3 } from "react";
|
|
2374
|
+
var AgnoChatContext = createContext3(null);
|
|
2375
|
+
function useAgnoChatContext() {
|
|
2376
|
+
const ctx = useContext3(AgnoChatContext);
|
|
2377
|
+
if (!ctx) {
|
|
2378
|
+
throw new Error(
|
|
2379
|
+
"useAgnoChatContext must be used within an <AgnoChat> provider. Wrap your component tree with <AgnoChat>."
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
return ctx;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// src/ui/composed/agno-chat/agno-chat.tsx
|
|
2386
|
+
import { jsx as jsx35 } from "react/jsx-runtime";
|
|
2387
|
+
function AgnoChatRoot({
|
|
2388
|
+
children,
|
|
2389
|
+
toolHandlers = {},
|
|
2390
|
+
autoExecuteTools = true,
|
|
2391
|
+
className,
|
|
2392
|
+
...divProps
|
|
2393
|
+
}) {
|
|
2394
|
+
const chat = useAgnoChat();
|
|
2395
|
+
const toolExec = useAgnoToolExecution(toolHandlers, autoExecuteTools);
|
|
2396
|
+
const containerRef = useRef6(null);
|
|
2397
|
+
const sendRef = useRef6(chat.sendMessage);
|
|
2398
|
+
sendRef.current = chat.sendMessage;
|
|
2399
|
+
const handleSend = useCallback7(async (message) => {
|
|
2400
|
+
try {
|
|
2401
|
+
await sendRef.current(message);
|
|
2402
|
+
} catch {
|
|
2403
|
+
}
|
|
2404
|
+
}, []);
|
|
2405
|
+
const {
|
|
2406
|
+
messages,
|
|
2407
|
+
sendMessage,
|
|
2408
|
+
clearMessages,
|
|
2409
|
+
cancelRun,
|
|
2410
|
+
isStreaming,
|
|
2411
|
+
isRefreshing,
|
|
2412
|
+
isCancelling,
|
|
2413
|
+
currentRunId,
|
|
2414
|
+
error,
|
|
2415
|
+
state
|
|
2416
|
+
} = chat;
|
|
2417
|
+
const {
|
|
2418
|
+
isPaused,
|
|
2419
|
+
isExecuting,
|
|
2420
|
+
pendingTools,
|
|
2421
|
+
executeAndContinue,
|
|
2422
|
+
executeTools,
|
|
2423
|
+
continueWithResults,
|
|
2424
|
+
executionError
|
|
2425
|
+
} = toolExec;
|
|
2426
|
+
const contextValue = useMemo3(
|
|
2427
|
+
() => ({
|
|
2428
|
+
// chat
|
|
2429
|
+
messages,
|
|
2430
|
+
sendMessage,
|
|
2431
|
+
clearMessages,
|
|
2432
|
+
cancelRun,
|
|
2433
|
+
isStreaming,
|
|
2434
|
+
isRefreshing,
|
|
2435
|
+
isCancelling: isCancelling ?? false,
|
|
2436
|
+
currentRunId,
|
|
2437
|
+
error,
|
|
2438
|
+
state,
|
|
2439
|
+
// tool execution
|
|
2440
|
+
isPaused,
|
|
2441
|
+
isExecuting,
|
|
2442
|
+
pendingTools,
|
|
2443
|
+
executeAndContinue,
|
|
2444
|
+
executeTools,
|
|
2445
|
+
continueWithResults,
|
|
2446
|
+
executionError,
|
|
2447
|
+
// derived
|
|
2448
|
+
handleSend,
|
|
2449
|
+
inputDisabled: isStreaming || isPaused,
|
|
2450
|
+
// drop zone
|
|
2451
|
+
dropZoneContainerRef: containerRef
|
|
2452
|
+
}),
|
|
2453
|
+
[
|
|
2454
|
+
messages,
|
|
2455
|
+
sendMessage,
|
|
2456
|
+
clearMessages,
|
|
2457
|
+
cancelRun,
|
|
2458
|
+
isStreaming,
|
|
2459
|
+
isRefreshing,
|
|
2460
|
+
isCancelling,
|
|
2461
|
+
currentRunId,
|
|
2462
|
+
error,
|
|
2463
|
+
state,
|
|
2464
|
+
isPaused,
|
|
2465
|
+
isExecuting,
|
|
2466
|
+
pendingTools,
|
|
2467
|
+
executeAndContinue,
|
|
2468
|
+
executeTools,
|
|
2469
|
+
continueWithResults,
|
|
2470
|
+
executionError,
|
|
2471
|
+
handleSend
|
|
2472
|
+
]
|
|
2473
|
+
);
|
|
2474
|
+
return /* @__PURE__ */ jsx35(AgnoChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx35("div", { ref: containerRef, className: cn("relative h-full flex flex-col", className), ...divProps, children }) });
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
// src/ui/composed/agno-chat/messages.tsx
|
|
2478
|
+
import { useEffect as useEffect6, useRef as useRef7 } from "react";
|
|
2479
|
+
|
|
2480
|
+
// src/ui/composed/AgnoMessageItem.tsx
|
|
2481
|
+
import { useState as useState8 } from "react";
|
|
2482
|
+
import { GenerativeUIRenderer } from "@rodrigocoliveira/agno-react";
|
|
2483
|
+
import {
|
|
2484
|
+
AlertCircle,
|
|
2485
|
+
FileIcon as FileIcon3,
|
|
2486
|
+
FileText,
|
|
2487
|
+
Image as ImageIcon2,
|
|
2488
|
+
Lightbulb,
|
|
2489
|
+
Music,
|
|
2490
|
+
Paperclip,
|
|
2491
|
+
Video
|
|
2492
|
+
} from "lucide-react";
|
|
2493
|
+
import { Fragment as Fragment5, jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2494
|
+
var defaultFormatTimestamp = formatSmartTimestamp;
|
|
2495
|
+
var getToolState = (tool) => {
|
|
2496
|
+
return tool.tool_call_error ? "output-error" : "output-available";
|
|
2497
|
+
};
|
|
2498
|
+
function AgnoMessageItem({
|
|
2499
|
+
message,
|
|
2500
|
+
className,
|
|
2501
|
+
classNames,
|
|
2502
|
+
renderContent,
|
|
2503
|
+
renderToolCall,
|
|
2504
|
+
renderMedia,
|
|
2505
|
+
renderActions,
|
|
2506
|
+
userAvatar,
|
|
2507
|
+
assistantAvatar,
|
|
2508
|
+
showReasoning = true,
|
|
2509
|
+
showReferences = true,
|
|
2510
|
+
showTimestamp = true,
|
|
2511
|
+
showGenerativeUI = true,
|
|
2512
|
+
showToolCalls = true,
|
|
2513
|
+
showFilePreview = true,
|
|
2514
|
+
showImageLightbox = true,
|
|
2515
|
+
formatTimestamp
|
|
2516
|
+
}) {
|
|
2517
|
+
const isUser = message.role === "user";
|
|
2518
|
+
const hasError = message.streamingError;
|
|
2519
|
+
const toolsWithUI = message.tool_calls?.filter((tool) => tool.ui_component) || [];
|
|
2520
|
+
const [preview, setPreview] = useState8(null);
|
|
2521
|
+
const isCustomTimestamp = !!formatTimestamp;
|
|
2522
|
+
const resolvedFormatTimestamp = formatTimestamp ?? defaultFormatTimestamp;
|
|
2523
|
+
const openImageLightbox = (images, index) => {
|
|
2524
|
+
if (!showImageLightbox) return;
|
|
2525
|
+
setPreview({ type: "image", images, initialIndex: index });
|
|
2526
|
+
};
|
|
2527
|
+
const openFilePreview = (file) => {
|
|
2528
|
+
if (!showFilePreview) return;
|
|
2529
|
+
setPreview({ type: "file", file });
|
|
2530
|
+
};
|
|
2531
|
+
const closePreview = () => setPreview(null);
|
|
2532
|
+
return /* @__PURE__ */ jsxs20("div", { className: cn("py-5 first:pt-2", isUser ? "flex justify-end" : "", classNames?.root, className), children: [
|
|
2533
|
+
isUser ? (
|
|
2534
|
+
/* User message */
|
|
2535
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-start gap-2.5 max-w-[80%] flex-row-reverse", children: [
|
|
2536
|
+
userAvatar,
|
|
2537
|
+
/* @__PURE__ */ jsxs20("div", { className: "space-y-1.5 flex flex-col items-end min-w-0", children: [
|
|
2538
|
+
(message.images && message.images.length > 0 || message.audio && message.audio.length > 0 || message.files && message.files.length > 0) && /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap gap-2 justify-end", children: [
|
|
2539
|
+
message.images?.map((img, idx) => /* @__PURE__ */ jsx36(
|
|
2540
|
+
FilePreviewCard,
|
|
2541
|
+
{
|
|
2542
|
+
file: { name: img.revised_prompt || `Image ${idx + 1}`, type: "image/png", url: img.url },
|
|
2543
|
+
onClick: showImageLightbox ? () => openImageLightbox(
|
|
2544
|
+
message.images.map((i) => ({ url: i.url, alt: i.revised_prompt })),
|
|
2545
|
+
idx
|
|
2546
|
+
) : void 0
|
|
2547
|
+
},
|
|
2548
|
+
`img-${idx}`
|
|
2549
|
+
)),
|
|
2550
|
+
message.audio?.map((audio, idx) => /* @__PURE__ */ jsxs20(
|
|
2551
|
+
"div",
|
|
2552
|
+
{
|
|
2553
|
+
className: "flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-2.5 py-1.5 text-xs text-foreground self-end",
|
|
2554
|
+
children: [
|
|
2555
|
+
/* @__PURE__ */ jsx36(Music, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
2556
|
+
/* @__PURE__ */ jsx36("span", { className: "truncate max-w-[150px]", children: audio.id || `Audio ${idx + 1}` })
|
|
2557
|
+
]
|
|
2558
|
+
},
|
|
2559
|
+
`audio-${idx}`
|
|
2560
|
+
)),
|
|
2561
|
+
message.files?.map(
|
|
2562
|
+
(file, idx) => showFilePreview ? /* @__PURE__ */ jsx36(
|
|
2563
|
+
FilePreviewCard,
|
|
2564
|
+
{
|
|
2565
|
+
file: { name: file.name, type: file.type, url: file.url, size: file.size },
|
|
2566
|
+
onClick: () => openFilePreview({ name: file.name, type: file.type, url: file.url, size: file.size })
|
|
2567
|
+
},
|
|
2568
|
+
`file-${idx}`
|
|
2569
|
+
) : /* @__PURE__ */ jsxs20(
|
|
2570
|
+
"div",
|
|
2571
|
+
{
|
|
2572
|
+
className: "flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-2.5 py-1.5 text-xs text-foreground self-end",
|
|
2573
|
+
children: [
|
|
2574
|
+
/* @__PURE__ */ jsx36(FileIcon3, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
2575
|
+
/* @__PURE__ */ jsx36("span", { className: "truncate max-w-[150px]", children: file.name })
|
|
2576
|
+
]
|
|
2577
|
+
},
|
|
2578
|
+
`file-${idx}`
|
|
2579
|
+
)
|
|
2580
|
+
)
|
|
2581
|
+
] }),
|
|
2582
|
+
message.content && /* @__PURE__ */ jsx36(
|
|
2583
|
+
"div",
|
|
2584
|
+
{
|
|
2585
|
+
className: cn(
|
|
2586
|
+
"rounded-2xl rounded-br-md px-4 py-2.5",
|
|
2587
|
+
classNames?.userBubble ?? "bg-primary text-primary-foreground",
|
|
2588
|
+
hasError && "opacity-70"
|
|
2589
|
+
),
|
|
2590
|
+
children: /* @__PURE__ */ jsx36("p", { className: "text-sm whitespace-pre-wrap", children: message.content })
|
|
2591
|
+
}
|
|
2592
|
+
),
|
|
2593
|
+
showTimestamp && /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-end gap-1.5 px-1", children: [
|
|
2594
|
+
/* @__PURE__ */ jsx36(
|
|
2595
|
+
SmartTimestamp,
|
|
2596
|
+
{
|
|
2597
|
+
date: new Date(message.created_at * 1e3),
|
|
2598
|
+
formatShort: isCustomTimestamp ? resolvedFormatTimestamp : void 0,
|
|
2599
|
+
className: "text-[11px] text-muted-foreground"
|
|
2600
|
+
}
|
|
2601
|
+
),
|
|
2602
|
+
hasError && /* @__PURE__ */ jsx36(AlertCircle, { className: "h-3 w-3 text-destructive" })
|
|
2603
|
+
] })
|
|
2604
|
+
] })
|
|
2605
|
+
] })
|
|
2606
|
+
) : (
|
|
2607
|
+
/* Assistant message */
|
|
2608
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-start gap-3", children: [
|
|
2609
|
+
assistantAvatar,
|
|
2610
|
+
/* @__PURE__ */ jsxs20("div", { className: cn("flex-1 min-w-0 space-y-3", classNames?.assistantContainer), children: [
|
|
2611
|
+
renderContent ? renderContent(message) : /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
2612
|
+
showReasoning && message.extra_data?.reasoning_steps && message.extra_data.reasoning_steps.length > 0 && /* @__PURE__ */ jsxs20("div", { className: cn("space-y-2 pt-1", classNames?.reasoning), children: [
|
|
2613
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
|
|
2614
|
+
/* @__PURE__ */ jsx36(Lightbulb, { className: "h-3.5 w-3.5" }),
|
|
2615
|
+
"Reasoning (",
|
|
2616
|
+
message.extra_data.reasoning_steps.length,
|
|
2617
|
+
" steps)"
|
|
2618
|
+
] }),
|
|
2619
|
+
/* @__PURE__ */ jsx36(Accordion, { type: "multiple", className: "w-full", children: message.extra_data.reasoning_steps.map((step, idx) => /* @__PURE__ */ jsxs20(AccordionItem, { value: `reasoning-${idx}`, className: "border-muted", children: [
|
|
2620
|
+
/* @__PURE__ */ jsx36(AccordionTrigger, { className: "text-xs py-1.5 hover:no-underline", children: step.title || `Step ${idx + 1}` }),
|
|
2621
|
+
/* @__PURE__ */ jsxs20(AccordionContent, { className: "space-y-1.5 text-xs text-muted-foreground", children: [
|
|
2622
|
+
step.action && /* @__PURE__ */ jsxs20("div", { children: [
|
|
2623
|
+
/* @__PURE__ */ jsx36("span", { className: "font-medium text-foreground", children: "Action:" }),
|
|
2624
|
+
" ",
|
|
2625
|
+
step.action
|
|
2626
|
+
] }),
|
|
2627
|
+
step.reasoning && /* @__PURE__ */ jsxs20("div", { children: [
|
|
2628
|
+
/* @__PURE__ */ jsx36("span", { className: "font-medium text-foreground", children: "Reasoning:" }),
|
|
2629
|
+
" ",
|
|
2630
|
+
step.reasoning
|
|
2631
|
+
] }),
|
|
2632
|
+
step.result && /* @__PURE__ */ jsxs20("div", { children: [
|
|
2633
|
+
/* @__PURE__ */ jsx36("span", { className: "font-medium text-foreground", children: "Result:" }),
|
|
2634
|
+
" ",
|
|
2635
|
+
step.result
|
|
2636
|
+
] }),
|
|
2637
|
+
step.confidence !== void 0 && /* @__PURE__ */ jsxs20("div", { children: [
|
|
2638
|
+
/* @__PURE__ */ jsx36("span", { className: "font-medium text-foreground", children: "Confidence:" }),
|
|
2639
|
+
" ",
|
|
2640
|
+
(step.confidence * 100).toFixed(1),
|
|
2641
|
+
"%"
|
|
2642
|
+
] })
|
|
2643
|
+
] })
|
|
2644
|
+
] }, idx)) })
|
|
2645
|
+
] }),
|
|
2646
|
+
message.content && /* @__PURE__ */ jsx36("div", { className: "prose prose-sm dark:prose-invert max-w-none prose-p:leading-relaxed prose-pre:bg-muted prose-pre:border prose-pre:border-border", children: /* @__PURE__ */ jsx36(Response, { children: message.content }) }),
|
|
2647
|
+
showGenerativeUI && toolsWithUI.length > 0 && /* @__PURE__ */ jsx36("div", { className: "space-y-3", children: toolsWithUI.map((tool) => {
|
|
2648
|
+
const uiComponent = tool.ui_component;
|
|
2649
|
+
return /* @__PURE__ */ jsx36("div", { children: uiComponent.layout === "artifact" ? /* @__PURE__ */ jsx36(Artifact, { children: /* @__PURE__ */ jsx36(GenerativeUIRenderer, { spec: uiComponent, className: "w-full p-2" }) }) : /* @__PURE__ */ jsx36(GenerativeUIRenderer, { spec: uiComponent, className: "w-full" }) }, tool.tool_call_id);
|
|
2650
|
+
}) }),
|
|
2651
|
+
renderMedia ? renderMedia(message) : (() => {
|
|
2652
|
+
const mediaClassName = classNames?.media;
|
|
2653
|
+
return /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
2654
|
+
message.images && message.images.length > 0 && /* @__PURE__ */ jsxs20("div", { className: cn("space-y-2 pt-1", mediaClassName), children: [
|
|
2655
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
|
|
2656
|
+
/* @__PURE__ */ jsx36(ImageIcon2, { className: "h-3.5 w-3.5" }),
|
|
2657
|
+
"Images (",
|
|
2658
|
+
message.images.length,
|
|
2659
|
+
")"
|
|
2660
|
+
] }),
|
|
2661
|
+
/* @__PURE__ */ jsx36("div", { className: "grid grid-cols-2 gap-2", children: message.images.map((img, idx) => /* @__PURE__ */ jsxs20("div", { className: "space-y-1", children: [
|
|
2662
|
+
showImageLightbox ? /* @__PURE__ */ jsx36(
|
|
2663
|
+
"button",
|
|
2664
|
+
{
|
|
2665
|
+
type: "button",
|
|
2666
|
+
onClick: () => openImageLightbox(
|
|
2667
|
+
message.images.map((i) => ({ url: i.url, alt: i.revised_prompt })),
|
|
2668
|
+
idx
|
|
2669
|
+
),
|
|
2670
|
+
className: "group relative w-full overflow-hidden rounded-lg border border-border cursor-pointer hover:border-primary/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors",
|
|
2671
|
+
children: /* @__PURE__ */ jsx36(
|
|
2672
|
+
"img",
|
|
2673
|
+
{
|
|
2674
|
+
src: img.url,
|
|
2675
|
+
alt: img.revised_prompt || "Generated image",
|
|
2676
|
+
className: "w-full rounded-lg"
|
|
2677
|
+
}
|
|
2678
|
+
)
|
|
2679
|
+
}
|
|
2680
|
+
) : /* @__PURE__ */ jsx36(
|
|
2681
|
+
"img",
|
|
2682
|
+
{
|
|
2683
|
+
src: img.url,
|
|
2684
|
+
alt: img.revised_prompt || "Generated image",
|
|
2685
|
+
className: "w-full rounded-lg border border-border"
|
|
2686
|
+
}
|
|
2687
|
+
),
|
|
2688
|
+
img.revised_prompt && /* @__PURE__ */ jsx36("p", { className: "text-[11px] text-muted-foreground italic px-0.5", children: img.revised_prompt })
|
|
2689
|
+
] }, idx)) })
|
|
2690
|
+
] }),
|
|
2691
|
+
message.videos && message.videos.length > 0 && /* @__PURE__ */ jsxs20("div", { className: cn("space-y-2 pt-1", mediaClassName), children: [
|
|
2692
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
|
|
2693
|
+
/* @__PURE__ */ jsx36(Video, { className: "h-3.5 w-3.5" }),
|
|
2694
|
+
"Videos (",
|
|
2695
|
+
message.videos.length,
|
|
2696
|
+
")"
|
|
2697
|
+
] }),
|
|
2698
|
+
/* @__PURE__ */ jsx36("div", { className: "space-y-2", children: message.videos.map((video, idx) => /* @__PURE__ */ jsx36("div", { children: video.url ? /* @__PURE__ */ jsx36(
|
|
2699
|
+
"video",
|
|
2700
|
+
{
|
|
2701
|
+
src: video.url,
|
|
2702
|
+
controls: true,
|
|
2703
|
+
className: "w-full rounded-lg border border-border"
|
|
2704
|
+
}
|
|
2705
|
+
) : /* @__PURE__ */ jsxs20("div", { className: "bg-muted/50 border border-border p-2.5 rounded-lg text-xs text-muted-foreground", children: [
|
|
2706
|
+
"Video ID: ",
|
|
2707
|
+
video.id,
|
|
2708
|
+
" (ETA: ",
|
|
2709
|
+
video.eta,
|
|
2710
|
+
"s)"
|
|
2711
|
+
] }) }, idx)) })
|
|
2712
|
+
] }),
|
|
2713
|
+
message.audio && message.audio.length > 0 && /* @__PURE__ */ jsxs20("div", { className: cn("space-y-2 pt-1", mediaClassName), children: [
|
|
2714
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
|
|
2715
|
+
/* @__PURE__ */ jsx36(Music, { className: "h-3.5 w-3.5" }),
|
|
2716
|
+
"Audio (",
|
|
2717
|
+
message.audio.length,
|
|
2718
|
+
")"
|
|
2719
|
+
] }),
|
|
2720
|
+
/* @__PURE__ */ jsx36("div", { className: "space-y-2", children: message.audio.map((audio, idx) => /* @__PURE__ */ jsx36("div", { children: audio.url ? /* @__PURE__ */ jsx36("audio", { src: audio.url, controls: true, className: "w-full" }) : audio.base64_audio ? /* @__PURE__ */ jsx36(
|
|
2721
|
+
"audio",
|
|
2722
|
+
{
|
|
2723
|
+
src: `data:${audio.mime_type || "audio/wav"};base64,${audio.base64_audio}`,
|
|
2724
|
+
controls: true,
|
|
2725
|
+
className: "w-full"
|
|
2726
|
+
}
|
|
2727
|
+
) : /* @__PURE__ */ jsx36("div", { className: "bg-muted/50 border border-border p-2.5 rounded-lg text-xs text-muted-foreground", children: "Audio data unavailable" }) }, idx)) })
|
|
2728
|
+
] }),
|
|
2729
|
+
message.files && message.files.length > 0 && /* @__PURE__ */ jsxs20("div", { className: cn("space-y-2 pt-1", mediaClassName), children: [
|
|
2730
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
|
|
2731
|
+
/* @__PURE__ */ jsx36(Paperclip, { className: "h-3.5 w-3.5" }),
|
|
2732
|
+
"Files (",
|
|
2733
|
+
message.files.length,
|
|
2734
|
+
")"
|
|
2735
|
+
] }),
|
|
2736
|
+
/* @__PURE__ */ jsx36("div", { className: "flex flex-wrap gap-2", children: message.files.map(
|
|
2737
|
+
(file, idx) => showFilePreview ? /* @__PURE__ */ jsx36(
|
|
2738
|
+
FilePreviewCard,
|
|
2739
|
+
{
|
|
2740
|
+
file: { name: file.name, type: file.type, url: file.url, size: file.size },
|
|
2741
|
+
onClick: () => openFilePreview({ name: file.name, type: file.type, url: file.url, size: file.size })
|
|
2742
|
+
},
|
|
2743
|
+
idx
|
|
2744
|
+
) : /* @__PURE__ */ jsxs20(
|
|
2745
|
+
"div",
|
|
2746
|
+
{
|
|
2747
|
+
className: "flex items-center gap-2 rounded-lg border border-border px-3 py-2 text-xs bg-muted/30 hover:bg-muted/50 transition-colors",
|
|
2748
|
+
children: [
|
|
2749
|
+
/* @__PURE__ */ jsx36(FileIcon3, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
2750
|
+
/* @__PURE__ */ jsx36("span", { className: "truncate max-w-[180px]", children: file.name }),
|
|
2751
|
+
file.size && /* @__PURE__ */ jsxs20("span", { className: "text-muted-foreground/70", children: [
|
|
2752
|
+
"(",
|
|
2753
|
+
(file.size / 1024).toFixed(1),
|
|
2754
|
+
"KB)"
|
|
2755
|
+
] }),
|
|
2756
|
+
file.url && /^https?:\/\//i.test(file.url) && /* @__PURE__ */ jsx36(
|
|
2757
|
+
"a",
|
|
2758
|
+
{
|
|
2759
|
+
href: file.url,
|
|
2760
|
+
target: "_blank",
|
|
2761
|
+
rel: "noopener noreferrer",
|
|
2762
|
+
className: "text-primary hover:underline font-medium",
|
|
2763
|
+
children: "View"
|
|
2764
|
+
}
|
|
2765
|
+
)
|
|
2766
|
+
]
|
|
2767
|
+
},
|
|
2768
|
+
idx
|
|
2769
|
+
)
|
|
2770
|
+
) })
|
|
2771
|
+
] }),
|
|
2772
|
+
message.response_audio && /* @__PURE__ */ jsxs20("div", { className: cn("space-y-2 pt-1", mediaClassName), children: [
|
|
2773
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
|
|
2774
|
+
/* @__PURE__ */ jsx36(Music, { className: "h-3.5 w-3.5" }),
|
|
2775
|
+
"Response Audio"
|
|
2776
|
+
] }),
|
|
2777
|
+
message.response_audio.transcript && /* @__PURE__ */ jsxs20("div", { className: "text-xs italic bg-muted/50 border border-border p-2.5 rounded-lg text-muted-foreground", children: [
|
|
2778
|
+
'"',
|
|
2779
|
+
message.response_audio.transcript,
|
|
2780
|
+
'"'
|
|
2781
|
+
] }),
|
|
2782
|
+
message.response_audio.content && /* @__PURE__ */ jsx36(
|
|
2783
|
+
"audio",
|
|
2784
|
+
{
|
|
2785
|
+
src: `data:audio/wav;base64,${message.response_audio.content}`,
|
|
2786
|
+
controls: true,
|
|
2787
|
+
className: "w-full"
|
|
2788
|
+
}
|
|
2789
|
+
)
|
|
2790
|
+
] })
|
|
2791
|
+
] });
|
|
2792
|
+
})(),
|
|
2793
|
+
showToolCalls && message.tool_calls && message.tool_calls.length > 0 && /* @__PURE__ */ jsx36("div", { className: cn("space-y-2 pt-1", classNames?.toolCalls), children: message.tool_calls.map(
|
|
2794
|
+
(tool, idx) => renderToolCall ? renderToolCall(tool, idx) : /* @__PURE__ */ jsxs20(Tool, { defaultOpen: idx === 0, children: [
|
|
2795
|
+
/* @__PURE__ */ jsx36(ToolHeader, { title: tool.tool_name, type: "tool-use", state: getToolState(tool) }),
|
|
2796
|
+
/* @__PURE__ */ jsxs20(ToolContent, { children: [
|
|
2797
|
+
/* @__PURE__ */ jsx36(ToolInput, { input: tool.tool_args }),
|
|
2798
|
+
tool.content && /* @__PURE__ */ jsx36(
|
|
2799
|
+
ToolOutput,
|
|
2800
|
+
{
|
|
2801
|
+
output: tool.content,
|
|
2802
|
+
errorText: tool.tool_call_error ? "Tool execution failed" : void 0
|
|
2803
|
+
}
|
|
2804
|
+
)
|
|
2805
|
+
] })
|
|
2806
|
+
] }, tool.tool_call_id || idx)
|
|
2807
|
+
) }),
|
|
2808
|
+
showReferences && message.extra_data?.references && message.extra_data.references.length > 0 && /* @__PURE__ */ jsxs20("div", { className: cn("space-y-2 pt-1", classNames?.references), children: [
|
|
2809
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
|
|
2810
|
+
/* @__PURE__ */ jsx36(FileText, { className: "h-3.5 w-3.5" }),
|
|
2811
|
+
"References (",
|
|
2812
|
+
message.extra_data.references.length,
|
|
2813
|
+
")"
|
|
2814
|
+
] }),
|
|
2815
|
+
/* @__PURE__ */ jsx36("div", { className: "space-y-2", children: message.extra_data.references.map((refData, idx) => /* @__PURE__ */ jsxs20("div", { className: "text-xs space-y-1.5", children: [
|
|
2816
|
+
refData.query && /* @__PURE__ */ jsxs20("div", { className: "font-medium text-foreground", children: [
|
|
2817
|
+
"Query: ",
|
|
2818
|
+
refData.query
|
|
2819
|
+
] }),
|
|
2820
|
+
refData.references.map((ref, refIdx) => /* @__PURE__ */ jsxs20("div", { className: "bg-muted/50 border border-border p-2.5 rounded-lg", children: [
|
|
2821
|
+
/* @__PURE__ */ jsxs20("div", { className: "italic text-muted-foreground mb-1", children: [
|
|
2822
|
+
'"',
|
|
2823
|
+
ref.content,
|
|
2824
|
+
'"'
|
|
2825
|
+
] }),
|
|
2826
|
+
/* @__PURE__ */ jsxs20("div", { className: "text-muted-foreground/70", children: [
|
|
2827
|
+
"Source: ",
|
|
2828
|
+
ref.name,
|
|
2829
|
+
" (chunk ",
|
|
2830
|
+
ref.meta_data.chunk,
|
|
2831
|
+
"/",
|
|
2832
|
+
ref.meta_data.chunk_size,
|
|
2833
|
+
")"
|
|
2834
|
+
] })
|
|
2835
|
+
] }, refIdx))
|
|
2836
|
+
] }, idx)) })
|
|
2837
|
+
] })
|
|
2838
|
+
] }),
|
|
2839
|
+
(renderActions || showTimestamp || hasError) && /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 pt-1", children: [
|
|
2840
|
+
renderActions && /* @__PURE__ */ jsx36("div", { className: cn("flex items-center gap-1", classNames?.actions), children: renderActions(message) }),
|
|
2841
|
+
hasError && /* @__PURE__ */ jsxs20("span", { className: "flex items-center gap-1 text-[11px] text-destructive", children: [
|
|
2842
|
+
/* @__PURE__ */ jsx36(AlertCircle, { className: "h-3 w-3" }),
|
|
2843
|
+
"Error"
|
|
2844
|
+
] }),
|
|
2845
|
+
showTimestamp && /* @__PURE__ */ jsx36(
|
|
2846
|
+
SmartTimestamp,
|
|
2847
|
+
{
|
|
2848
|
+
date: new Date(message.created_at * 1e3),
|
|
2849
|
+
formatShort: isCustomTimestamp ? resolvedFormatTimestamp : void 0,
|
|
2850
|
+
className: "text-[11px] text-muted-foreground"
|
|
2851
|
+
}
|
|
2852
|
+
)
|
|
2853
|
+
] })
|
|
2854
|
+
] })
|
|
2855
|
+
] })
|
|
2856
|
+
),
|
|
2857
|
+
preview?.type === "image" && /* @__PURE__ */ jsx36(
|
|
2858
|
+
ImageLightbox,
|
|
2859
|
+
{
|
|
2860
|
+
open: true,
|
|
2861
|
+
onOpenChange: (open) => {
|
|
2862
|
+
if (!open) closePreview();
|
|
2863
|
+
},
|
|
2864
|
+
images: preview.images,
|
|
2865
|
+
initialIndex: preview.initialIndex
|
|
2866
|
+
}
|
|
2867
|
+
),
|
|
2868
|
+
preview?.type === "file" && /* @__PURE__ */ jsx36(
|
|
2869
|
+
FilePreviewModal,
|
|
2870
|
+
{
|
|
2871
|
+
open: true,
|
|
2872
|
+
onOpenChange: (open) => {
|
|
2873
|
+
if (!open) closePreview();
|
|
2874
|
+
},
|
|
2875
|
+
file: preview.file
|
|
2876
|
+
}
|
|
2877
|
+
)
|
|
2878
|
+
] });
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
// src/ui/composed/agno-chat/suggested-prompts.tsx
|
|
2882
|
+
import { jsx as jsx37, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2883
|
+
function AgnoChatSuggestedPrompts({ className, prompts }) {
|
|
2884
|
+
const { handleSend } = useAgnoChatContext();
|
|
2885
|
+
if (prompts.length === 0) return null;
|
|
2886
|
+
return /* @__PURE__ */ jsx37("div", { className: cn("grid grid-cols-2 gap-2 w-full max-w-md", className), children: prompts.map((prompt, idx) => /* @__PURE__ */ jsxs21(
|
|
2887
|
+
"button",
|
|
2888
|
+
{
|
|
2889
|
+
onClick: () => handleSend(prompt.text),
|
|
2890
|
+
className: "flex items-center gap-2 px-3 py-2.5 rounded-xl border border-border bg-card hover:bg-accent/50 hover:border-primary/20 transition-all duration-200 text-left text-sm group",
|
|
2891
|
+
children: [
|
|
2892
|
+
prompt.icon && /* @__PURE__ */ jsx37("span", { className: "text-muted-foreground group-hover:text-primary transition-colors", children: prompt.icon }),
|
|
2893
|
+
/* @__PURE__ */ jsx37("span", { className: "text-muted-foreground group-hover:text-foreground transition-colors text-xs leading-snug", children: prompt.text })
|
|
2894
|
+
]
|
|
2895
|
+
},
|
|
2896
|
+
idx
|
|
2897
|
+
)) });
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
// src/ui/composed/agno-chat/messages.tsx
|
|
2901
|
+
import { Bot as Bot2 } from "lucide-react";
|
|
2902
|
+
import { jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2903
|
+
function ScrollOnNewUserMessage({ messageCount }) {
|
|
2904
|
+
const { scrollToBottom } = useStickToBottomContext2();
|
|
2905
|
+
const prevCount = useRef7(messageCount);
|
|
2906
|
+
useEffect6(() => {
|
|
2907
|
+
if (messageCount > prevCount.current) {
|
|
2908
|
+
scrollToBottom("smooth");
|
|
2909
|
+
}
|
|
2910
|
+
prevCount.current = messageCount;
|
|
2911
|
+
}, [messageCount, scrollToBottom]);
|
|
2912
|
+
return null;
|
|
2913
|
+
}
|
|
2914
|
+
var DEFAULT_PROMPTS = [
|
|
2915
|
+
{ text: "What can you help me with?" },
|
|
2916
|
+
{ text: "Explain how you work" }
|
|
2917
|
+
];
|
|
2918
|
+
function AgnoChatMessages({
|
|
2919
|
+
className,
|
|
2920
|
+
renderMessage,
|
|
2921
|
+
userAvatar,
|
|
2922
|
+
assistantAvatar,
|
|
2923
|
+
messageItemProps,
|
|
2924
|
+
emptyState,
|
|
2925
|
+
suggestedPrompts = DEFAULT_PROMPTS,
|
|
2926
|
+
children,
|
|
2927
|
+
showThinkingIndicator = true,
|
|
2928
|
+
renderThinkingIndicator
|
|
2929
|
+
}) {
|
|
2930
|
+
const { messages, isStreaming } = useAgnoChatContext();
|
|
2931
|
+
const lastMessage = messages[messages.length - 1];
|
|
2932
|
+
const isThinking = showThinkingIndicator && isStreaming && (!lastMessage || lastMessage.role !== "user") && !lastMessage?.content;
|
|
2933
|
+
const resolvedEmptyState = children ?? emptyState ?? /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center gap-6", children: [
|
|
2934
|
+
/* @__PURE__ */ jsxs22("div", { className: "relative", children: [
|
|
2935
|
+
/* @__PURE__ */ jsx38("div", { className: "h-16 w-16 rounded-2xl bg-primary/10 flex items-center justify-center", children: /* @__PURE__ */ jsx38(Bot2, { className: "h-8 w-8 text-primary" }) }),
|
|
2936
|
+
/* @__PURE__ */ jsx38("div", { className: "absolute -bottom-1 -right-1 h-5 w-5 rounded-full bg-green-500 border-2 border-background flex items-center justify-center", children: /* @__PURE__ */ jsx38("span", { className: "h-2 w-2 rounded-full bg-white animate-pulse" }) })
|
|
2937
|
+
] }),
|
|
2938
|
+
/* @__PURE__ */ jsxs22("div", { className: "space-y-2 text-center", children: [
|
|
2939
|
+
/* @__PURE__ */ jsx38("h3", { className: "text-xl font-semibold tracking-tight", children: "Welcome to Agno Chat" }),
|
|
2940
|
+
/* @__PURE__ */ jsx38("p", { className: "text-muted-foreground text-sm max-w-sm", children: "Start a conversation with your AI agent. Ask questions, explore ideas, or run tools." })
|
|
2941
|
+
] }),
|
|
2942
|
+
suggestedPrompts.length > 0 && /* @__PURE__ */ jsx38(AgnoChatSuggestedPrompts, { prompts: suggestedPrompts })
|
|
2943
|
+
] });
|
|
2944
|
+
return /* @__PURE__ */ jsxs22(Conversation, { className: cn("relative flex-1 w-full", className), children: [
|
|
2945
|
+
/* @__PURE__ */ jsx38(ScrollOnNewUserMessage, { messageCount: messages.length }),
|
|
2946
|
+
/* @__PURE__ */ jsxs22(ConversationContent, { className: "max-w-3xl mx-auto", children: [
|
|
2947
|
+
messages.length === 0 ? /* @__PURE__ */ jsx38(ConversationEmptyState, { children: resolvedEmptyState }) : messages.map((message, index) => {
|
|
2948
|
+
if (isThinking && index === messages.length - 1 && message === lastMessage) return null;
|
|
2949
|
+
return renderMessage ? renderMessage(message, index) : /* @__PURE__ */ jsx38(
|
|
2950
|
+
AgnoMessageItem,
|
|
2951
|
+
{
|
|
2952
|
+
message,
|
|
2953
|
+
userAvatar,
|
|
2954
|
+
assistantAvatar,
|
|
2955
|
+
...messageItemProps
|
|
2956
|
+
},
|
|
2957
|
+
`msg-${index}-${message.created_at}`
|
|
2958
|
+
);
|
|
2959
|
+
}),
|
|
2960
|
+
isThinking && /* @__PURE__ */ jsx38("div", { className: "py-2", children: renderThinkingIndicator ?? /* @__PURE__ */ jsx38(StreamingIndicator, { avatar: assistantAvatar }) })
|
|
2961
|
+
] }),
|
|
2962
|
+
/* @__PURE__ */ jsx38(ConversationScrollButton, {})
|
|
2963
|
+
] });
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
// src/ui/composed/agno-chat/empty-state.tsx
|
|
2967
|
+
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
2968
|
+
function AgnoChatEmptyState({ children, className, ...props }) {
|
|
2969
|
+
return /* @__PURE__ */ jsx39("div", { className: cn("flex flex-col items-center gap-6", className), ...props, children });
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
// src/ui/composed/agno-chat/tool-status.tsx
|
|
2973
|
+
import { Loader2 as Loader22, Wrench } from "lucide-react";
|
|
2974
|
+
import { Fragment as Fragment6, jsx as jsx40, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2975
|
+
function AgnoChatToolStatus({ className }) {
|
|
2976
|
+
const { isPaused, isExecuting, pendingTools } = useAgnoChatContext();
|
|
2977
|
+
if (!isPaused && !isExecuting) return null;
|
|
2978
|
+
return /* @__PURE__ */ jsx40("div", { className: cn("px-4 py-2.5 border-t border-border bg-primary/5", className), children: /* @__PURE__ */ jsx40("div", { className: "flex items-center gap-2.5 text-sm max-w-3xl mx-auto", children: isExecuting ? /* @__PURE__ */ jsxs23(Fragment6, { children: [
|
|
2979
|
+
/* @__PURE__ */ jsx40("div", { className: "h-5 w-5 rounded-full bg-primary/10 flex items-center justify-center", children: /* @__PURE__ */ jsx40(Loader22, { className: "h-3 w-3 animate-spin text-primary" }) }),
|
|
2980
|
+
/* @__PURE__ */ jsxs23("span", { className: "text-muted-foreground", children: [
|
|
2981
|
+
"Executing",
|
|
2982
|
+
" ",
|
|
2983
|
+
/* @__PURE__ */ jsx40("span", { className: "font-medium text-foreground", children: pendingTools.length }),
|
|
2984
|
+
" tool",
|
|
2985
|
+
pendingTools.length !== 1 ? "s" : "",
|
|
2986
|
+
"..."
|
|
2987
|
+
] })
|
|
2988
|
+
] }) : /* @__PURE__ */ jsxs23(Fragment6, { children: [
|
|
2989
|
+
/* @__PURE__ */ jsx40("div", { className: "h-5 w-5 rounded-full bg-amber-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx40(Wrench, { className: "h-3 w-3 text-amber-600 dark:text-amber-400" }) }),
|
|
2990
|
+
/* @__PURE__ */ jsxs23("span", { className: "text-muted-foreground", children: [
|
|
2991
|
+
"Preparing",
|
|
2992
|
+
" ",
|
|
2993
|
+
/* @__PURE__ */ jsx40("span", { className: "font-medium text-foreground", children: pendingTools.length }),
|
|
2994
|
+
" tool",
|
|
2995
|
+
pendingTools.length !== 1 ? "s" : "",
|
|
2996
|
+
"..."
|
|
2997
|
+
] })
|
|
2998
|
+
] }) }) });
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
// src/ui/composed/agno-chat/error-bar.tsx
|
|
3002
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
3003
|
+
function AgnoChatErrorBar({ className }) {
|
|
3004
|
+
const { error, executionError } = useAgnoChatContext();
|
|
3005
|
+
const message = error || executionError;
|
|
3006
|
+
if (!message) return null;
|
|
3007
|
+
return /* @__PURE__ */ jsx41("div", { className: cn("px-4 py-2.5 bg-destructive/5 border-t border-destructive/20", className), children: /* @__PURE__ */ jsx41("p", { className: "text-sm text-destructive max-w-3xl mx-auto", children: message }) });
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
// src/ui/composed/AgnoChatInput.tsx
|
|
3011
|
+
import { CircleStop } from "lucide-react";
|
|
3012
|
+
import { jsx as jsx42, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3013
|
+
var DEFAULT_ACCEPTED_FILE_TYPES = "image/*,audio/*,.pdf,.doc,.docx,.txt,.csv,.xlsx,.xls,.ppt,.pptx,.md,.json,.xml";
|
|
3014
|
+
function dataUrlToBlob(dataUrl) {
|
|
3015
|
+
const [header, base64] = dataUrl.split(",");
|
|
3016
|
+
const mime = header.match(/:(.*?);/)?.[1] || "application/octet-stream";
|
|
3017
|
+
const bytes = atob(base64);
|
|
3018
|
+
const buf = new Uint8Array(bytes.length);
|
|
3019
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
3020
|
+
buf[i] = bytes.charCodeAt(i);
|
|
3021
|
+
}
|
|
3022
|
+
return new Blob([buf], { type: mime });
|
|
3023
|
+
}
|
|
3024
|
+
function CancelButton({ onCancel }) {
|
|
3025
|
+
return /* @__PURE__ */ jsx42(
|
|
3026
|
+
Button,
|
|
3027
|
+
{
|
|
3028
|
+
type: "button",
|
|
3029
|
+
variant: "destructive",
|
|
3030
|
+
size: "icon",
|
|
3031
|
+
className: "h-8 w-8 rounded-lg",
|
|
3032
|
+
onClick: onCancel,
|
|
3033
|
+
"aria-label": "Stop",
|
|
3034
|
+
children: /* @__PURE__ */ jsx42(CircleStop, { className: "size-4" })
|
|
3035
|
+
}
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
function SubmitButton({ disabled, status }) {
|
|
3039
|
+
const { textInput } = usePromptInputController();
|
|
3040
|
+
const hasText = textInput.value.trim().length > 0;
|
|
3041
|
+
return /* @__PURE__ */ jsx42(PromptInputSubmit, { disabled: disabled || !hasText, status });
|
|
3042
|
+
}
|
|
3043
|
+
function AttachmentHeader() {
|
|
3044
|
+
const { files } = usePromptInputAttachments();
|
|
3045
|
+
if (files.length === 0) return null;
|
|
3046
|
+
return /* @__PURE__ */ jsx42(PromptInputHeader, { children: /* @__PURE__ */ jsx42(PromptInputAttachments, { children: (attachment) => /* @__PURE__ */ jsx42(PromptInputAttachment, { data: attachment }) }) });
|
|
3047
|
+
}
|
|
3048
|
+
function TranscribeAudioRecorder({
|
|
3049
|
+
endpoint,
|
|
3050
|
+
headers,
|
|
3051
|
+
disabled,
|
|
3052
|
+
parseResponse,
|
|
3053
|
+
onRequestPermission,
|
|
3054
|
+
labels
|
|
3055
|
+
}) {
|
|
3056
|
+
const { textInput } = usePromptInputController();
|
|
3057
|
+
return /* @__PURE__ */ jsx42(
|
|
3058
|
+
AudioRecorder,
|
|
3059
|
+
{
|
|
3060
|
+
mode: "transcribe",
|
|
3061
|
+
transcriptionEndpoint: endpoint,
|
|
3062
|
+
transcriptionHeaders: headers,
|
|
3063
|
+
parseTranscriptionResponse: parseResponse,
|
|
3064
|
+
onRequestPermission,
|
|
3065
|
+
onRecordingComplete: () => {
|
|
3066
|
+
},
|
|
3067
|
+
onTranscriptionComplete: (text) => {
|
|
3068
|
+
const current = textInput.value;
|
|
3069
|
+
textInput.setInput(current + (current ? " " : "") + text);
|
|
3070
|
+
},
|
|
3071
|
+
disabled,
|
|
3072
|
+
labels
|
|
3073
|
+
}
|
|
3074
|
+
);
|
|
3075
|
+
}
|
|
3076
|
+
function AgnoChatInput({
|
|
3077
|
+
onSend,
|
|
3078
|
+
disabled,
|
|
3079
|
+
placeholder,
|
|
3080
|
+
className,
|
|
3081
|
+
fileUpload,
|
|
3082
|
+
showAudioRecorder = false,
|
|
3083
|
+
showAttachments = true,
|
|
3084
|
+
status,
|
|
3085
|
+
isStreaming,
|
|
3086
|
+
onCancel,
|
|
3087
|
+
allowCancelRun = false,
|
|
3088
|
+
extraTools,
|
|
3089
|
+
audioMode = "send",
|
|
3090
|
+
transcriptionEndpoint,
|
|
3091
|
+
transcriptionHeaders,
|
|
3092
|
+
parseTranscriptionResponse,
|
|
3093
|
+
onRequestPermission,
|
|
3094
|
+
audioRecorderLabels,
|
|
3095
|
+
dropZoneContainerRef,
|
|
3096
|
+
dropZoneProps
|
|
3097
|
+
}) {
|
|
3098
|
+
const handleSubmit = (message) => {
|
|
3099
|
+
const text = message.text?.trim() || "";
|
|
3100
|
+
const files = message.files || [];
|
|
3101
|
+
if (!text) return;
|
|
3102
|
+
if (files.length === 0) {
|
|
3103
|
+
onSend(text);
|
|
3104
|
+
return;
|
|
3105
|
+
}
|
|
3106
|
+
const formData = new FormData();
|
|
3107
|
+
formData.append("message", text);
|
|
3108
|
+
for (const file of files) {
|
|
3109
|
+
if (!file.url) continue;
|
|
3110
|
+
const blob = file.url.startsWith("data:") ? dataUrlToBlob(file.url) : null;
|
|
3111
|
+
if (!blob) continue;
|
|
3112
|
+
const fileName = file.filename || "file";
|
|
3113
|
+
formData.append("files", blob, fileName);
|
|
3114
|
+
}
|
|
3115
|
+
onSend(formData);
|
|
3116
|
+
};
|
|
3117
|
+
const handleAudioRecording = (blob) => {
|
|
3118
|
+
const formData = new FormData();
|
|
3119
|
+
formData.append("message", "Audio message");
|
|
3120
|
+
formData.append("files", blob, `recording-${Date.now()}.wav`);
|
|
3121
|
+
onSend(formData);
|
|
3122
|
+
};
|
|
3123
|
+
const computedStatus = status ?? (disabled ? "submitted" : void 0);
|
|
3124
|
+
const showCancelButton = allowCancelRun && isStreaming && onCancel;
|
|
3125
|
+
return /* @__PURE__ */ jsx42(PromptInputProvider, { children: /* @__PURE__ */ jsxs24(
|
|
3126
|
+
PromptInput,
|
|
3127
|
+
{
|
|
3128
|
+
onSubmit: handleSubmit,
|
|
3129
|
+
accept: fileUpload?.accept ?? DEFAULT_ACCEPTED_FILE_TYPES,
|
|
3130
|
+
multiple: fileUpload?.multiple ?? true,
|
|
3131
|
+
maxFiles: fileUpload?.maxFiles,
|
|
3132
|
+
maxFileSize: fileUpload?.maxFileSize,
|
|
3133
|
+
globalDrop: !!dropZoneContainerRef,
|
|
3134
|
+
dragListenerTarget: dropZoneContainerRef,
|
|
3135
|
+
className: cn("w-full", className),
|
|
3136
|
+
children: [
|
|
3137
|
+
/* @__PURE__ */ jsx42(AttachmentHeader, {}),
|
|
3138
|
+
/* @__PURE__ */ jsx42(PromptInputBody, { children: /* @__PURE__ */ jsx42(
|
|
3139
|
+
PromptInputTextarea,
|
|
3140
|
+
{
|
|
3141
|
+
placeholder: placeholder || "Type your message... (Enter to send, Shift+Enter for new line)",
|
|
3142
|
+
disabled
|
|
3143
|
+
}
|
|
3144
|
+
) }),
|
|
3145
|
+
/* @__PURE__ */ jsxs24(PromptInputFooter, { children: [
|
|
3146
|
+
/* @__PURE__ */ jsxs24(PromptInputTools, { children: [
|
|
3147
|
+
showAttachments && /* @__PURE__ */ jsxs24(PromptInputActionMenu, { children: [
|
|
3148
|
+
/* @__PURE__ */ jsx42(PromptInputActionMenuTrigger, {}),
|
|
3149
|
+
/* @__PURE__ */ jsx42(PromptInputActionMenuContent, { children: /* @__PURE__ */ jsx42(PromptInputActionAddAttachments, { label: "Add files" }) })
|
|
3150
|
+
] }),
|
|
3151
|
+
showAudioRecorder && (audioMode === "transcribe" && transcriptionEndpoint ? /* @__PURE__ */ jsx42(
|
|
3152
|
+
TranscribeAudioRecorder,
|
|
3153
|
+
{
|
|
3154
|
+
endpoint: transcriptionEndpoint,
|
|
3155
|
+
headers: transcriptionHeaders,
|
|
3156
|
+
disabled,
|
|
3157
|
+
parseResponse: parseTranscriptionResponse,
|
|
3158
|
+
onRequestPermission,
|
|
3159
|
+
labels: audioRecorderLabels
|
|
3160
|
+
}
|
|
3161
|
+
) : /* @__PURE__ */ jsx42(AudioRecorder, { onRecordingComplete: handleAudioRecording, disabled, onRequestPermission, labels: audioRecorderLabels })),
|
|
3162
|
+
extraTools
|
|
3163
|
+
] }),
|
|
3164
|
+
showCancelButton ? /* @__PURE__ */ jsx42(CancelButton, { onCancel }) : /* @__PURE__ */ jsx42(SubmitButton, { disabled, status: computedStatus })
|
|
3165
|
+
] }),
|
|
3166
|
+
showAttachments && /* @__PURE__ */ jsx42(PromptInputDropZone, { ...dropZoneProps, container: dropZoneContainerRef })
|
|
3167
|
+
]
|
|
3168
|
+
}
|
|
3169
|
+
) });
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
// src/ui/composed/agno-chat/input.tsx
|
|
3173
|
+
import { jsx as jsx43 } from "react/jsx-runtime";
|
|
3174
|
+
function AgnoChatInputArea({
|
|
3175
|
+
className,
|
|
3176
|
+
children,
|
|
3177
|
+
placeholder,
|
|
3178
|
+
fileUpload,
|
|
3179
|
+
showAudioRecorder = false,
|
|
3180
|
+
showAttachments,
|
|
3181
|
+
extraTools,
|
|
3182
|
+
chatInputProps,
|
|
3183
|
+
audioMode,
|
|
3184
|
+
transcriptionEndpoint,
|
|
3185
|
+
transcriptionHeaders,
|
|
3186
|
+
parseTranscriptionResponse,
|
|
3187
|
+
onRequestPermission,
|
|
3188
|
+
audioRecorderLabels,
|
|
3189
|
+
allowCancelRun = false,
|
|
3190
|
+
dropZoneProps
|
|
3191
|
+
}) {
|
|
3192
|
+
const { handleSend, inputDisabled, isStreaming, isPaused, cancelRun, dropZoneContainerRef } = useAgnoChatContext();
|
|
3193
|
+
return /* @__PURE__ */ jsx43("div", { className: cn("border-t border-border bg-background/80 backdrop-blur-sm", className), children: /* @__PURE__ */ jsx43("div", { className: "mx-auto px-4 py-2", children: children ? children({ onSend: handleSend, disabled: inputDisabled, isStreaming, isPaused }) : /* @__PURE__ */ jsx43(
|
|
3194
|
+
AgnoChatInput,
|
|
3195
|
+
{
|
|
3196
|
+
...chatInputProps,
|
|
3197
|
+
onSend: handleSend,
|
|
3198
|
+
disabled: inputDisabled,
|
|
3199
|
+
isStreaming,
|
|
3200
|
+
onCancel: cancelRun,
|
|
3201
|
+
allowCancelRun,
|
|
3202
|
+
placeholder: placeholder ?? chatInputProps?.placeholder ?? "Message your agent...",
|
|
3203
|
+
fileUpload,
|
|
3204
|
+
showAudioRecorder,
|
|
3205
|
+
showAttachments,
|
|
3206
|
+
extraTools,
|
|
3207
|
+
audioMode,
|
|
3208
|
+
transcriptionEndpoint,
|
|
3209
|
+
transcriptionHeaders,
|
|
3210
|
+
parseTranscriptionResponse,
|
|
3211
|
+
onRequestPermission,
|
|
3212
|
+
audioRecorderLabels,
|
|
3213
|
+
dropZoneContainerRef,
|
|
3214
|
+
dropZoneProps
|
|
3215
|
+
}
|
|
3216
|
+
) }) });
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
// src/ui/composed/agno-chat/index.ts
|
|
3220
|
+
var AgnoChat = Object.assign(AgnoChatRoot, {
|
|
3221
|
+
Messages: AgnoChatMessages,
|
|
3222
|
+
EmptyState: AgnoChatEmptyState,
|
|
3223
|
+
SuggestedPrompts: AgnoChatSuggestedPrompts,
|
|
3224
|
+
ToolStatus: AgnoChatToolStatus,
|
|
3225
|
+
ErrorBar: AgnoChatErrorBar,
|
|
3226
|
+
Input: AgnoChatInputArea
|
|
3227
|
+
});
|
|
3228
|
+
|
|
3229
|
+
// src/ui/composed/AgnoChatInterface.tsx
|
|
3230
|
+
import { Bot as Bot3, Sparkles } from "lucide-react";
|
|
3231
|
+
import { jsx as jsx44, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3232
|
+
var DEFAULT_PROMPTS2 = [
|
|
3233
|
+
{ icon: /* @__PURE__ */ jsx44(Sparkles, { className: "h-3.5 w-3.5" }), text: "What can you help me with?" },
|
|
3234
|
+
{ icon: /* @__PURE__ */ jsx44(Bot3, { className: "h-3.5 w-3.5" }), text: "Explain how you work" }
|
|
3235
|
+
];
|
|
3236
|
+
function AgnoChatInterface({
|
|
3237
|
+
className,
|
|
3238
|
+
classNames,
|
|
3239
|
+
renderMessage,
|
|
3240
|
+
renderInput,
|
|
3241
|
+
emptyState,
|
|
3242
|
+
headerSlot,
|
|
3243
|
+
inputToolbarSlot,
|
|
3244
|
+
suggestedPrompts = DEFAULT_PROMPTS2,
|
|
3245
|
+
toolHandlers = {},
|
|
3246
|
+
autoExecuteTools = true,
|
|
3247
|
+
placeholder,
|
|
3248
|
+
userAvatar,
|
|
3249
|
+
assistantAvatar,
|
|
3250
|
+
fileUpload,
|
|
3251
|
+
showAudioRecorder = true,
|
|
3252
|
+
showAttachments = true,
|
|
3253
|
+
messageItemProps,
|
|
3254
|
+
chatInputProps,
|
|
3255
|
+
dropZoneLabel
|
|
3256
|
+
}) {
|
|
3257
|
+
return /* @__PURE__ */ jsxs25(
|
|
3258
|
+
AgnoChat,
|
|
3259
|
+
{
|
|
3260
|
+
toolHandlers,
|
|
3261
|
+
autoExecuteTools,
|
|
3262
|
+
className: cn(classNames?.root, className),
|
|
3263
|
+
children: [
|
|
3264
|
+
headerSlot,
|
|
3265
|
+
/* @__PURE__ */ jsx44(
|
|
3266
|
+
AgnoChat.Messages,
|
|
3267
|
+
{
|
|
3268
|
+
className: classNames?.messagesArea,
|
|
3269
|
+
renderMessage,
|
|
3270
|
+
userAvatar,
|
|
3271
|
+
assistantAvatar,
|
|
3272
|
+
messageItemProps,
|
|
3273
|
+
emptyState,
|
|
3274
|
+
suggestedPrompts
|
|
3275
|
+
}
|
|
3276
|
+
),
|
|
3277
|
+
/* @__PURE__ */ jsx44(AgnoChat.ToolStatus, { className: classNames?.toolStatusBar }),
|
|
3278
|
+
/* @__PURE__ */ jsx44(AgnoChat.ErrorBar, { className: classNames?.errorBar }),
|
|
3279
|
+
/* @__PURE__ */ jsx44(
|
|
3280
|
+
AgnoChat.Input,
|
|
3281
|
+
{
|
|
3282
|
+
className: classNames?.inputArea,
|
|
3283
|
+
placeholder,
|
|
3284
|
+
fileUpload,
|
|
3285
|
+
showAudioRecorder,
|
|
3286
|
+
showAttachments,
|
|
3287
|
+
extraTools: inputToolbarSlot,
|
|
3288
|
+
chatInputProps,
|
|
3289
|
+
dropZoneProps: { className: classNames?.dropZone, label: dropZoneLabel },
|
|
3290
|
+
children: renderInput ? ({ onSend, disabled }) => renderInput({ onSend, disabled }) : void 0
|
|
3291
|
+
}
|
|
3292
|
+
)
|
|
3293
|
+
]
|
|
3294
|
+
}
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3297
|
+
export {
|
|
3298
|
+
Accordion,
|
|
3299
|
+
AccordionContent,
|
|
3300
|
+
AccordionItem,
|
|
3301
|
+
AccordionTrigger,
|
|
3302
|
+
AgnoChat,
|
|
3303
|
+
AgnoChatEmptyState,
|
|
3304
|
+
AgnoChatErrorBar,
|
|
3305
|
+
AgnoChatInput,
|
|
3306
|
+
AgnoChatInputArea,
|
|
3307
|
+
AgnoChatInterface,
|
|
3308
|
+
AgnoChatMessages,
|
|
3309
|
+
AgnoChatRoot,
|
|
3310
|
+
AgnoChatSuggestedPrompts,
|
|
3311
|
+
AgnoChatToolStatus,
|
|
3312
|
+
AgnoMessageItem,
|
|
3313
|
+
Artifact,
|
|
3314
|
+
ArtifactAction,
|
|
3315
|
+
ArtifactActions,
|
|
3316
|
+
ArtifactClose,
|
|
3317
|
+
ArtifactContent,
|
|
3318
|
+
ArtifactDescription,
|
|
3319
|
+
ArtifactHeader,
|
|
3320
|
+
ArtifactTitle,
|
|
3321
|
+
AudioRecorder,
|
|
3322
|
+
Avatar,
|
|
3323
|
+
AvatarFallback,
|
|
3324
|
+
AvatarImage,
|
|
3325
|
+
Badge,
|
|
3326
|
+
Button,
|
|
3327
|
+
CodeBlock,
|
|
3328
|
+
CodeBlockCopyButton,
|
|
3329
|
+
Collapsible,
|
|
3330
|
+
CollapsibleContent2 as CollapsibleContent,
|
|
3331
|
+
CollapsibleTrigger2 as CollapsibleTrigger,
|
|
3332
|
+
Command,
|
|
3333
|
+
CommandEmpty,
|
|
3334
|
+
CommandGroup,
|
|
3335
|
+
CommandInput,
|
|
3336
|
+
CommandItem,
|
|
3337
|
+
CommandList,
|
|
3338
|
+
CommandSeparator,
|
|
3339
|
+
CommandShortcut,
|
|
3340
|
+
Conversation,
|
|
3341
|
+
ConversationContent,
|
|
3342
|
+
ConversationEmptyState,
|
|
3343
|
+
ConversationScrollButton,
|
|
3344
|
+
Dialog,
|
|
3345
|
+
DialogClose,
|
|
3346
|
+
DialogContent,
|
|
3347
|
+
DialogDescription,
|
|
3348
|
+
DialogFooter,
|
|
3349
|
+
DialogHeader,
|
|
3350
|
+
DialogOverlay,
|
|
3351
|
+
DialogPortal,
|
|
3352
|
+
DialogTitle,
|
|
3353
|
+
DialogTrigger,
|
|
3354
|
+
DropdownMenu,
|
|
3355
|
+
DropdownMenuCheckboxItem,
|
|
3356
|
+
DropdownMenuContent,
|
|
3357
|
+
DropdownMenuGroup,
|
|
3358
|
+
DropdownMenuItem,
|
|
3359
|
+
DropdownMenuLabel,
|
|
3360
|
+
DropdownMenuPortal,
|
|
3361
|
+
DropdownMenuRadioGroup,
|
|
3362
|
+
DropdownMenuRadioItem,
|
|
3363
|
+
DropdownMenuSeparator,
|
|
3364
|
+
DropdownMenuShortcut,
|
|
3365
|
+
DropdownMenuSub,
|
|
3366
|
+
DropdownMenuSubContent,
|
|
3367
|
+
DropdownMenuSubTrigger,
|
|
3368
|
+
DropdownMenuTrigger,
|
|
3369
|
+
FilePreviewCard,
|
|
3370
|
+
FilePreviewModal,
|
|
3371
|
+
HoverCard,
|
|
3372
|
+
HoverCardContent,
|
|
3373
|
+
HoverCardTrigger,
|
|
3374
|
+
ImageLightbox,
|
|
3375
|
+
InputGroup,
|
|
3376
|
+
InputGroupAddon,
|
|
3377
|
+
InputGroupButton,
|
|
3378
|
+
InputGroupInput,
|
|
3379
|
+
InputGroupText,
|
|
3380
|
+
InputGroupTextarea,
|
|
3381
|
+
Message,
|
|
3382
|
+
MessageAvatar,
|
|
3383
|
+
MessageContent,
|
|
3384
|
+
PromptInput,
|
|
3385
|
+
PromptInputActionAddAttachments,
|
|
3386
|
+
PromptInputActionMenu,
|
|
3387
|
+
PromptInputActionMenuContent,
|
|
3388
|
+
PromptInputActionMenuItem,
|
|
3389
|
+
PromptInputActionMenuTrigger,
|
|
3390
|
+
PromptInputAttachment,
|
|
3391
|
+
PromptInputAttachments,
|
|
3392
|
+
PromptInputBody,
|
|
3393
|
+
PromptInputButton,
|
|
3394
|
+
PromptInputCommand,
|
|
3395
|
+
PromptInputCommandEmpty,
|
|
3396
|
+
PromptInputCommandGroup,
|
|
3397
|
+
PromptInputCommandInput,
|
|
3398
|
+
PromptInputCommandItem,
|
|
3399
|
+
PromptInputCommandList,
|
|
3400
|
+
PromptInputCommandSeparator,
|
|
3401
|
+
PromptInputDropZone,
|
|
3402
|
+
PromptInputFooter,
|
|
3403
|
+
PromptInputHeader,
|
|
3404
|
+
PromptInputModelSelect,
|
|
3405
|
+
PromptInputModelSelectContent,
|
|
3406
|
+
PromptInputModelSelectItem,
|
|
3407
|
+
PromptInputModelSelectTrigger,
|
|
3408
|
+
PromptInputModelSelectValue,
|
|
3409
|
+
PromptInputProvider,
|
|
3410
|
+
PromptInputSpeechButton,
|
|
3411
|
+
PromptInputSubmit,
|
|
3412
|
+
PromptInputTab,
|
|
3413
|
+
PromptInputTabBody,
|
|
3414
|
+
PromptInputTabItem,
|
|
3415
|
+
PromptInputTabLabel,
|
|
3416
|
+
PromptInputTabsList,
|
|
3417
|
+
PromptInputTextarea,
|
|
3418
|
+
PromptInputTools,
|
|
3419
|
+
Response,
|
|
3420
|
+
Select,
|
|
3421
|
+
SelectContent,
|
|
3422
|
+
SelectGroup,
|
|
3423
|
+
SelectItem,
|
|
3424
|
+
SelectLabel,
|
|
3425
|
+
SelectScrollDownButton,
|
|
3426
|
+
SelectScrollUpButton,
|
|
3427
|
+
SelectSeparator,
|
|
3428
|
+
SelectTrigger,
|
|
3429
|
+
SelectValue,
|
|
3430
|
+
SmartTimestamp,
|
|
3431
|
+
StreamingIndicator,
|
|
3432
|
+
Tool,
|
|
3433
|
+
ToolContent,
|
|
3434
|
+
ToolHeader,
|
|
3435
|
+
ToolInput,
|
|
3436
|
+
ToolOutput,
|
|
3437
|
+
Tooltip,
|
|
3438
|
+
TooltipContent,
|
|
3439
|
+
TooltipProvider,
|
|
3440
|
+
TooltipTrigger,
|
|
3441
|
+
badgeVariants,
|
|
3442
|
+
buttonVariants,
|
|
3443
|
+
cn,
|
|
3444
|
+
formatFileSize,
|
|
3445
|
+
formatFullTimestamp,
|
|
3446
|
+
formatSmartTimestamp,
|
|
3447
|
+
getFileExtension,
|
|
3448
|
+
getFilePreviewType,
|
|
3449
|
+
isPreviewable,
|
|
3450
|
+
useAgnoChatContext,
|
|
3451
|
+
usePromptInputAttachments,
|
|
3452
|
+
usePromptInputController,
|
|
3453
|
+
usePromptInputDropZone,
|
|
3454
|
+
useProviderAttachments
|
|
3455
|
+
};
|