@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/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
+ };