@copilotz/chat-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4177 @@
1
+ // src/components/chat/ChatUI.tsx
2
+ import { useState as useState8, useEffect as useEffect7, useRef as useRef4, useCallback as useCallback4 } from "react";
3
+
4
+ // src/config/chatConfig.ts
5
+ var defaultChatConfig = {
6
+ branding: {
7
+ logo: null,
8
+ avatar: null,
9
+ title: "Chat Assistant",
10
+ subtitle: "How can I help you today?"
11
+ },
12
+ labels: {
13
+ inputPlaceholder: "Type your message...",
14
+ sendButton: "Send",
15
+ sendMessageTooltip: "Send message",
16
+ newThread: "New Conversation",
17
+ deleteThread: "Delete Conversation",
18
+ copyMessage: "Copy",
19
+ editMessage: "Edit",
20
+ regenerateMessage: "Regenerate",
21
+ stopGeneration: "Stop",
22
+ stopGenerationTooltip: "Stop generation",
23
+ attachFiles: "Attach Files",
24
+ attachFileTooltip: "Attach file",
25
+ recordAudio: "Record Audio",
26
+ recordAudioTooltip: "Record audio",
27
+ // Header labels
28
+ exportData: "Export data",
29
+ importData: "Import data",
30
+ clearAll: "Clear all",
31
+ sidebarToggle: "Menu",
32
+ customComponentToggle: "Toggle",
33
+ settings: "Settings",
34
+ toggleDarkMode: "Toggle Dark Mode",
35
+ lightMode: "Light Mode",
36
+ darkMode: "Dark Mode",
37
+ // Sidebar labels
38
+ newChat: "New Conversation",
39
+ search: "Search conversations...",
40
+ customComponentLabel: "Custom",
41
+ showArchived: "Show Archived",
42
+ hideArchived: "Hide Archived",
43
+ noThreadsFound: "No conversations found",
44
+ noThreadsYet: "No conversations yet",
45
+ deleteConfirmTitle: "Delete Conversation",
46
+ deleteConfirmDescription: "Are you sure you want to delete this conversation? This action cannot be undone. All messages will be permanently lost.",
47
+ renameThread: "Rename",
48
+ archiveThread: "Archive",
49
+ unarchiveThread: "Unarchive",
50
+ today: "Today",
51
+ yesterday: "Yesterday",
52
+ createNewThread: "Create New Conversation",
53
+ threadNamePlaceholder: "Conversation name (optional)",
54
+ cancel: "Cancel",
55
+ create: "Create Conversation",
56
+ footerLabel: "Assistant can make mistakes. Check the AI results.",
57
+ toolUsed: "Tool Used",
58
+ daysAgo: "days ago",
59
+ inputHelpText: "Press Enter to send, Shift+Enter to add a new line.",
60
+ thinking: "Thinking...",
61
+ defaultThreadName: "Main Thread"
62
+ },
63
+ features: {
64
+ enableThreads: true,
65
+ enableFileUpload: true,
66
+ enableAudioRecording: true,
67
+ enableMessageEditing: true,
68
+ enableMessageCopy: true,
69
+ enableRegeneration: true,
70
+ enableToolCallsDisplay: true,
71
+ maxAttachments: 4,
72
+ maxFileSize: 10 * 1024 * 1024
73
+ // 10MB
74
+ },
75
+ ui: {
76
+ theme: "auto",
77
+ showTimestamps: false,
78
+ showAvatars: true,
79
+ compactMode: false,
80
+ showWordCount: false
81
+ },
82
+ customComponent: {},
83
+ headerActions: null
84
+ };
85
+ function mergeConfig(_baseConfig, userConfig) {
86
+ if (!userConfig) return defaultChatConfig;
87
+ return {
88
+ branding: {
89
+ ...defaultChatConfig.branding,
90
+ ...userConfig.branding
91
+ },
92
+ labels: {
93
+ ...defaultChatConfig.labels,
94
+ ...userConfig.labels
95
+ },
96
+ features: {
97
+ ...defaultChatConfig.features,
98
+ ...userConfig.features
99
+ },
100
+ ui: {
101
+ ...defaultChatConfig.ui,
102
+ ...userConfig.ui
103
+ },
104
+ customComponent: userConfig.customComponent || defaultChatConfig.customComponent,
105
+ headerActions: userConfig.headerActions || defaultChatConfig.headerActions
106
+ };
107
+ }
108
+ var chatConfigPresets = {
109
+ minimal: {
110
+ features: {
111
+ enableThreads: false,
112
+ enableFileUpload: false,
113
+ enableAudioRecording: false,
114
+ enableMessageEditing: false,
115
+ enableMessageCopy: true,
116
+ enableRegeneration: true,
117
+ enableToolCallsDisplay: false
118
+ },
119
+ ui: {
120
+ compactMode: true,
121
+ showTimestamps: false,
122
+ showAvatars: false
123
+ }
124
+ },
125
+ full: {
126
+ features: {
127
+ enableThreads: true,
128
+ enableFileUpload: true,
129
+ enableAudioRecording: true,
130
+ enableMessageEditing: true,
131
+ enableMessageCopy: true,
132
+ enableRegeneration: true,
133
+ enableToolCallsDisplay: true
134
+ },
135
+ ui: {
136
+ showTimestamps: true,
137
+ showAvatars: true,
138
+ compactMode: false,
139
+ showWordCount: true
140
+ }
141
+ },
142
+ developer: {
143
+ features: {
144
+ enableThreads: true,
145
+ enableFileUpload: true,
146
+ enableAudioRecording: false,
147
+ enableMessageEditing: true,
148
+ enableMessageCopy: true,
149
+ enableRegeneration: true,
150
+ enableToolCallsDisplay: true
151
+ },
152
+ ui: {
153
+ showTimestamps: true,
154
+ showAvatars: true,
155
+ compactMode: false,
156
+ showWordCount: true
157
+ }
158
+ },
159
+ customer_support: {
160
+ branding: {
161
+ title: "Customer Support",
162
+ subtitle: "How can I help you today?"
163
+ },
164
+ features: {
165
+ enableThreads: true,
166
+ enableFileUpload: true,
167
+ enableAudioRecording: false,
168
+ enableMessageEditing: false,
169
+ enableMessageCopy: true,
170
+ enableRegeneration: false,
171
+ enableToolCallsDisplay: false
172
+ },
173
+ ui: {
174
+ showTimestamps: true,
175
+ showAvatars: true,
176
+ compactMode: false
177
+ }
178
+ }
179
+ };
180
+ function validateConfig(config) {
181
+ const errors = [];
182
+ if (config.features?.maxAttachments && config.features.maxAttachments < 1) {
183
+ errors.push("maxAttachments must be at least 1");
184
+ }
185
+ if (config.features?.maxFileSize && config.features.maxFileSize < 1024) {
186
+ errors.push("maxFileSize must be at least 1024 bytes (1KB)");
187
+ }
188
+ if (config.branding?.title && typeof config.branding.title !== "string") {
189
+ errors.push("branding.title must be a string");
190
+ }
191
+ return errors;
192
+ }
193
+ var themeUtils = {
194
+ getSystemTheme: () => {
195
+ if (typeof globalThis.matchMedia === "undefined") return "light";
196
+ return globalThis.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
197
+ },
198
+ resolveTheme: (theme) => {
199
+ return theme === "auto" ? themeUtils.getSystemTheme() : theme;
200
+ },
201
+ applyTheme: (theme) => {
202
+ if (typeof document === "undefined") return;
203
+ const resolvedTheme = themeUtils.resolveTheme(theme);
204
+ document.documentElement.classList.toggle("dark", resolvedTheme === "dark");
205
+ }
206
+ };
207
+ var featureFlags = {
208
+ isEnabled: (config, feature) => {
209
+ return config.features[feature] === true;
210
+ },
211
+ getEnabledFeatures: (config) => {
212
+ return Object.entries(config.features).filter(([_, enabled]) => enabled === true).map(([feature]) => feature);
213
+ },
214
+ hasAnyFeature: (config, features) => {
215
+ return features.some((feature) => featureFlags.isEnabled(config, feature));
216
+ }
217
+ };
218
+ var configUtils = {
219
+ createConfigHook: (config) => {
220
+ return {
221
+ config,
222
+ isFeatureEnabled: (feature) => featureFlags.isEnabled(config, feature),
223
+ getLabel: (key) => config.labels[key],
224
+ getBranding: () => config.branding,
225
+ getUI: () => config.ui
226
+ };
227
+ }
228
+ };
229
+
230
+ // src/components/chat/Message.tsx
231
+ import { useState, useRef } from "react";
232
+ import ReactMarkdown from "react-markdown";
233
+ import remarkGfm from "remark-gfm";
234
+ import rehypeHighlight from "rehype-highlight";
235
+
236
+ // src/components/ui/button.tsx
237
+ import { Slot } from "@radix-ui/react-slot";
238
+ import { cva } from "class-variance-authority";
239
+
240
+ // src/lib/utils.ts
241
+ import { clsx } from "clsx";
242
+ import { twMerge } from "tailwind-merge";
243
+ function cn(...inputs) {
244
+ return twMerge(clsx(inputs));
245
+ }
246
+ var formatDate = (timestamp, labels) => {
247
+ const date = new Date(timestamp);
248
+ const now = /* @__PURE__ */ new Date();
249
+ const diffMs = now.getTime() - date.getTime();
250
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
251
+ if (diffDays === 0) {
252
+ return labels?.today || "Today";
253
+ } else if (diffDays === 1) {
254
+ return labels?.yesterday || "Yesterday";
255
+ } else if (diffDays < 7) {
256
+ return `${diffDays} ${labels?.daysAgo || "days ago"}`;
257
+ } else {
258
+ return date.toLocaleDateString("en-US", {
259
+ day: "2-digit",
260
+ month: "short"
261
+ });
262
+ }
263
+ };
264
+
265
+ // src/components/ui/button.tsx
266
+ import { jsx } from "react/jsx-runtime";
267
+ var buttonVariants = cva(
268
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
269
+ {
270
+ variants: {
271
+ variant: {
272
+ default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
273
+ destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
274
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
275
+ secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
276
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
277
+ link: "text-primary underline-offset-4 hover:underline"
278
+ },
279
+ size: {
280
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
281
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
282
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
283
+ icon: "size-9"
284
+ }
285
+ },
286
+ defaultVariants: {
287
+ variant: "default",
288
+ size: "default"
289
+ }
290
+ }
291
+ );
292
+ function Button({
293
+ className,
294
+ variant,
295
+ size,
296
+ asChild = false,
297
+ ...props
298
+ }) {
299
+ const Comp = asChild ? Slot : "button";
300
+ return /* @__PURE__ */ jsx(
301
+ Comp,
302
+ {
303
+ "data-slot": "button",
304
+ className: cn(buttonVariants({ variant, size, className })),
305
+ ...props
306
+ }
307
+ );
308
+ }
309
+
310
+ // src/components/ui/avatar.tsx
311
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
312
+ import { jsx as jsx2 } from "react/jsx-runtime";
313
+ function Avatar({
314
+ className,
315
+ ...props
316
+ }) {
317
+ return /* @__PURE__ */ jsx2(
318
+ AvatarPrimitive.Root,
319
+ {
320
+ "data-slot": "avatar",
321
+ className: cn(
322
+ "relative flex size-8 shrink-0 overflow-hidden rounded-full",
323
+ className
324
+ ),
325
+ ...props
326
+ }
327
+ );
328
+ }
329
+ function AvatarImage({
330
+ className,
331
+ ...props
332
+ }) {
333
+ return /* @__PURE__ */ jsx2(
334
+ AvatarPrimitive.Image,
335
+ {
336
+ "data-slot": "avatar-image",
337
+ className: cn("aspect-square size-full", className),
338
+ ...props
339
+ }
340
+ );
341
+ }
342
+ function AvatarFallback({
343
+ className,
344
+ ...props
345
+ }) {
346
+ return /* @__PURE__ */ jsx2(
347
+ AvatarPrimitive.Fallback,
348
+ {
349
+ "data-slot": "avatar-fallback",
350
+ className: cn(
351
+ "bg-muted flex size-full items-center justify-center rounded-full",
352
+ className
353
+ ),
354
+ ...props
355
+ }
356
+ );
357
+ }
358
+
359
+ // src/components/ui/badge.tsx
360
+ import { Slot as Slot2 } from "@radix-ui/react-slot";
361
+ import { cva as cva2 } from "class-variance-authority";
362
+ import { jsx as jsx3 } from "react/jsx-runtime";
363
+ var badgeVariants = cva2(
364
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
365
+ {
366
+ variants: {
367
+ variant: {
368
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
369
+ secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
370
+ destructive: "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
371
+ outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"
372
+ }
373
+ },
374
+ defaultVariants: {
375
+ variant: "default"
376
+ }
377
+ }
378
+ );
379
+ function Badge({
380
+ className,
381
+ variant,
382
+ asChild = false,
383
+ ...props
384
+ }) {
385
+ const Comp = asChild ? Slot2 : "span";
386
+ return /* @__PURE__ */ jsx3(
387
+ Comp,
388
+ {
389
+ "data-slot": "badge",
390
+ className: cn(badgeVariants({ variant }), className),
391
+ ...props
392
+ }
393
+ );
394
+ }
395
+
396
+ // src/components/ui/card.tsx
397
+ import { jsx as jsx4 } from "react/jsx-runtime";
398
+ function Card({ className, ...props }) {
399
+ return /* @__PURE__ */ jsx4(
400
+ "div",
401
+ {
402
+ "data-slot": "card",
403
+ className: cn(
404
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
405
+ className
406
+ ),
407
+ ...props
408
+ }
409
+ );
410
+ }
411
+ function CardHeader({ className, ...props }) {
412
+ return /* @__PURE__ */ jsx4(
413
+ "div",
414
+ {
415
+ "data-slot": "card-header",
416
+ className: cn(
417
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
418
+ className
419
+ ),
420
+ ...props
421
+ }
422
+ );
423
+ }
424
+ function CardTitle({ className, ...props }) {
425
+ return /* @__PURE__ */ jsx4(
426
+ "div",
427
+ {
428
+ "data-slot": "card-title",
429
+ className: cn("leading-none font-semibold", className),
430
+ ...props
431
+ }
432
+ );
433
+ }
434
+ function CardContent({ className, ...props }) {
435
+ return /* @__PURE__ */ jsx4(
436
+ "div",
437
+ {
438
+ "data-slot": "card-content",
439
+ className: cn("px-6", className),
440
+ ...props
441
+ }
442
+ );
443
+ }
444
+
445
+ // src/components/ui/textarea.tsx
446
+ import { jsx as jsx5 } from "react/jsx-runtime";
447
+ function Textarea({ className, ...props }) {
448
+ return /* @__PURE__ */ jsx5(
449
+ "textarea",
450
+ {
451
+ "data-slot": "textarea",
452
+ className: cn(
453
+ "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
454
+ className
455
+ ),
456
+ ...props
457
+ }
458
+ );
459
+ }
460
+
461
+ // src/components/ui/tooltip.tsx
462
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
463
+ import { jsx as jsx6, jsxs } from "react/jsx-runtime";
464
+ function TooltipProvider({
465
+ delayDuration = 0,
466
+ ...props
467
+ }) {
468
+ return /* @__PURE__ */ jsx6(
469
+ TooltipPrimitive.Provider,
470
+ {
471
+ "data-slot": "tooltip-provider",
472
+ delayDuration,
473
+ ...props
474
+ }
475
+ );
476
+ }
477
+ function Tooltip({
478
+ ...props
479
+ }) {
480
+ return /* @__PURE__ */ jsx6(TooltipProvider, { children: /* @__PURE__ */ jsx6(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) });
481
+ }
482
+ function TooltipTrigger({
483
+ ...props
484
+ }) {
485
+ return /* @__PURE__ */ jsx6(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
486
+ }
487
+ function TooltipContent({
488
+ className,
489
+ sideOffset = 0,
490
+ children,
491
+ ...props
492
+ }) {
493
+ return /* @__PURE__ */ jsx6(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
494
+ TooltipPrimitive.Content,
495
+ {
496
+ "data-slot": "tooltip-content",
497
+ sideOffset,
498
+ className: cn(
499
+ "bg-primary 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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
500
+ className
501
+ ),
502
+ ...props,
503
+ children: [
504
+ children,
505
+ /* @__PURE__ */ jsx6(TooltipPrimitive.Arrow, { className: "bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
506
+ ]
507
+ }
508
+ ) });
509
+ }
510
+
511
+ // src/components/chat/Message.tsx
512
+ import {
513
+ Copy,
514
+ Edit,
515
+ RotateCcw,
516
+ Check,
517
+ X,
518
+ Wrench,
519
+ Clock,
520
+ ChevronRight,
521
+ ChevronDown
522
+ } from "lucide-react";
523
+ import { Fragment, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
524
+ var ThinkingIndicator = ({ label = "Thinking..." }) => {
525
+ return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 py-2", children: [
526
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-1", children: [
527
+ /* @__PURE__ */ jsx7(
528
+ "span",
529
+ {
530
+ className: "inline-block w-2 h-2 bg-primary rounded-full animate-bounce",
531
+ style: { animationDelay: "0ms" }
532
+ }
533
+ ),
534
+ /* @__PURE__ */ jsx7(
535
+ "span",
536
+ {
537
+ className: "inline-block w-2 h-2 bg-primary rounded-full animate-bounce",
538
+ style: { animationDelay: "150ms" }
539
+ }
540
+ ),
541
+ /* @__PURE__ */ jsx7(
542
+ "span",
543
+ {
544
+ className: "inline-block w-2 h-2 bg-primary rounded-full animate-bounce",
545
+ style: { animationDelay: "300ms" }
546
+ }
547
+ )
548
+ ] }),
549
+ /* @__PURE__ */ jsx7("span", { className: "text-sm text-muted-foreground animate-pulse", children: label })
550
+ ] });
551
+ };
552
+ var StreamingText = ({
553
+ content,
554
+ isStreaming = false,
555
+ thinkingLabel = "Thinking..."
556
+ }) => {
557
+ const hasContent = content.trim().length > 0;
558
+ return /* @__PURE__ */ jsxs2("div", { className: "prose prose-sm max-w-none dark:prose-invert", children: [
559
+ hasContent ? /* @__PURE__ */ jsx7(
560
+ ReactMarkdown,
561
+ {
562
+ remarkPlugins: [remarkGfm],
563
+ rehypePlugins: isStreaming ? [] : [rehypeHighlight],
564
+ components: {
565
+ code: ({ node, className, children, ...props }) => {
566
+ const inline = props.inline;
567
+ const match = /language-(\w+)/.exec(className || "");
568
+ return !inline && match ? /* @__PURE__ */ jsx7("pre", { className: "relative", children: /* @__PURE__ */ jsx7("code", { className, ...props, children }) }) : /* @__PURE__ */ jsx7("code", { className: "bg-muted px-1 py-0.5 rounded text-sm", ...props, children });
569
+ }
570
+ },
571
+ children: content
572
+ }
573
+ ) : isStreaming ? (
574
+ // Show thinking indicator while waiting for first token
575
+ /* @__PURE__ */ jsx7(ThinkingIndicator, { label: thinkingLabel })
576
+ ) : null,
577
+ isStreaming && hasContent && /* @__PURE__ */ jsx7("span", { className: "inline-block w-2 h-4 bg-primary animate-pulse ml-1" })
578
+ ] });
579
+ };
580
+ var MediaRenderer = ({ attachment }) => {
581
+ const [isPlaying, setIsPlaying] = useState(false);
582
+ const audioRef = useRef(null);
583
+ const videoRef = useRef(null);
584
+ const togglePlayback = () => {
585
+ if (attachment.kind === "audio" && audioRef.current) {
586
+ if (isPlaying) {
587
+ audioRef.current.pause();
588
+ } else {
589
+ audioRef.current.play();
590
+ }
591
+ setIsPlaying(!isPlaying);
592
+ } else if (attachment.kind === "video" && videoRef.current) {
593
+ if (isPlaying) {
594
+ videoRef.current.pause();
595
+ } else {
596
+ videoRef.current.play();
597
+ }
598
+ setIsPlaying(!isPlaying);
599
+ }
600
+ };
601
+ const formatDuration = (ms) => {
602
+ if (!ms) return "";
603
+ const seconds = Math.floor(ms / 1e3);
604
+ const minutes = Math.floor(seconds / 60);
605
+ return `${minutes}:${(seconds % 60).toString().padStart(2, "0")}`;
606
+ };
607
+ switch (attachment.kind) {
608
+ case "image":
609
+ return /* @__PURE__ */ jsxs2("div", { className: "relative rounded-lg overflow-hidden border bg-muted/20 max-w-md", children: [
610
+ /* @__PURE__ */ jsx7(
611
+ "img",
612
+ {
613
+ src: attachment.dataUrl,
614
+ alt: attachment.fileName || "Attachment",
615
+ className: "w-full h-auto object-cover",
616
+ loading: "lazy"
617
+ }
618
+ ),
619
+ attachment.fileName && /* @__PURE__ */ jsx7("div", { className: "absolute bottom-0 left-0 right-0 bg-black/50 text-white text-xs p-2", children: attachment.fileName })
620
+ ] });
621
+ case "audio":
622
+ return /* @__PURE__ */ jsx7("div", { className: "flex w-full max-w-md py-0 min-w-64 items-center gap-3", children: /* @__PURE__ */ jsx7(
623
+ "audio",
624
+ {
625
+ ref: audioRef,
626
+ src: attachment.dataUrl,
627
+ onPlay: () => setIsPlaying(true),
628
+ onPause: () => setIsPlaying(false),
629
+ onEnded: () => setIsPlaying(false),
630
+ className: "w-full mt-2",
631
+ controls: true
632
+ }
633
+ ) });
634
+ case "video":
635
+ return /* @__PURE__ */ jsxs2("div", { className: "relative rounded-lg overflow-hidden border bg-muted/20 max-w-lg", children: [
636
+ /* @__PURE__ */ jsx7(
637
+ "video",
638
+ {
639
+ ref: videoRef,
640
+ src: attachment.dataUrl,
641
+ poster: attachment.poster,
642
+ controls: true,
643
+ className: "w-full h-auto",
644
+ onPlay: () => setIsPlaying(true),
645
+ onPause: () => setIsPlaying(false),
646
+ onEnded: () => setIsPlaying(false)
647
+ }
648
+ ),
649
+ attachment.fileName && /* @__PURE__ */ jsx7("div", { className: "absolute bottom-0 left-0 right-0 bg-black/50 text-white text-xs p-2", children: attachment.fileName })
650
+ ] });
651
+ default:
652
+ return null;
653
+ }
654
+ };
655
+ var ToolCallsDisplay = ({ toolCalls, label }) => {
656
+ const [expandedCall, setExpandedCall] = useState(null);
657
+ const getStatusIcon = (status) => {
658
+ switch (status) {
659
+ case "pending":
660
+ return /* @__PURE__ */ jsx7(Clock, { className: "h-3 w-3 text-muted-foreground" });
661
+ case "running":
662
+ return /* @__PURE__ */ jsx7("div", { className: "h-3 w-3 border-2 border-primary border-t-transparent rounded-full animate-spin" });
663
+ case "completed":
664
+ return /* @__PURE__ */ jsx7(Check, { className: "h-3 w-3 text-green-500" });
665
+ case "failed":
666
+ return /* @__PURE__ */ jsx7(X, { className: "h-3 w-3 text-destructive" });
667
+ }
668
+ };
669
+ const getStatusBadgeClasses = (status) => {
670
+ switch (status) {
671
+ case "pending":
672
+ return "bg-muted text-muted-foreground";
673
+ case "running":
674
+ return "bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300";
675
+ case "completed":
676
+ return "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300";
677
+ case "failed":
678
+ return "bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300";
679
+ }
680
+ };
681
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
682
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-xs uppercase tracking-wide text-muted-foreground font-semibold", children: [
683
+ /* @__PURE__ */ jsx7(Wrench, { className: "h-3 w-3" }),
684
+ label || "Ferramenta utilizada"
685
+ ] }),
686
+ toolCalls.map((call) => {
687
+ const isExpanded = expandedCall === call.id;
688
+ const ToggleIcon = isExpanded ? ChevronDown : ChevronRight;
689
+ return /* @__PURE__ */ jsxs2(Card, { className: "border border-dashed border-primary/40 bg-card/60", children: [
690
+ /* @__PURE__ */ jsxs2(
691
+ "button",
692
+ {
693
+ type: "button",
694
+ className: "flex w-full items-center justify-between gap-3 px-3 py-2 text-left",
695
+ onClick: () => setExpandedCall(isExpanded ? null : call.id),
696
+ children: [
697
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
698
+ getStatusIcon(call.status),
699
+ /* @__PURE__ */ jsx7("span", { className: "font-medium text-sm", children: call.name }),
700
+ /* @__PURE__ */ jsx7(Badge, { variant: "secondary", className: getStatusBadgeClasses(call.status), children: call.status })
701
+ ] }),
702
+ /* @__PURE__ */ jsx7(ToggleIcon, { className: "h-4 w-4 text-muted-foreground" })
703
+ ]
704
+ }
705
+ ),
706
+ isExpanded && /* @__PURE__ */ jsxs2(CardContent, { className: "pt-0 pb-3 px-3 text-xs space-y-2", children: [
707
+ /* @__PURE__ */ jsxs2("div", { children: [
708
+ /* @__PURE__ */ jsx7("div", { className: "font-medium text-muted-foreground mb-1", children: "Args" }),
709
+ /* @__PURE__ */ jsx7("pre", { className: "rounded bg-muted p-2 overflow-x-auto text-xs", children: JSON.stringify(call.arguments, null, 2) })
710
+ ] }),
711
+ typeof call.result !== "undefined" && /* @__PURE__ */ jsxs2("div", { children: [
712
+ /* @__PURE__ */ jsx7("div", { className: "font-medium text-muted-foreground mb-1", children: "Result" }),
713
+ /* @__PURE__ */ jsx7("pre", { className: "rounded bg-muted p-2 overflow-x-auto text-xs", children: JSON.stringify(call.result, null, 2) })
714
+ ] }),
715
+ call.startTime && call.endTime && /* @__PURE__ */ jsxs2("div", { className: "text-muted-foreground", children: [
716
+ "Executed in ",
717
+ call.endTime - call.startTime,
718
+ "ms"
719
+ ] })
720
+ ] })
721
+ ] }, call.id);
722
+ })
723
+ ] });
724
+ };
725
+ var Message = ({
726
+ message,
727
+ isUser,
728
+ userAvatar,
729
+ userName = "Voc\xEA",
730
+ assistantAvatar,
731
+ assistantName = "Assistente",
732
+ showTimestamp = false,
733
+ showAvatar = true,
734
+ enableCopy = true,
735
+ enableEdit = true,
736
+ enableRegenerate = true,
737
+ enableToolCallsDisplay = false,
738
+ compactMode = false,
739
+ onAction,
740
+ className = "",
741
+ toolUsedLabel,
742
+ thinkingLabel = "Thinking..."
743
+ }) => {
744
+ const [isEditing, setIsEditing] = useState(false);
745
+ const [editContent, setEditContent] = useState(message.content);
746
+ const [showActions, setShowActions] = useState(false);
747
+ const [copied, setCopied] = useState(false);
748
+ const messageIsUser = isUser ?? message.role === "user";
749
+ const canEdit = enableEdit && messageIsUser;
750
+ const canRegenerate = enableRegenerate && !messageIsUser;
751
+ const handleCopy = async () => {
752
+ try {
753
+ await navigator.clipboard.writeText(message.content);
754
+ setCopied(true);
755
+ setTimeout(() => setCopied(false), 2e3);
756
+ onAction?.({ action: "copy", messageId: message.id, content: message.content });
757
+ } catch (error) {
758
+ console.error("Failed to copy message:", error);
759
+ }
760
+ };
761
+ const handleEdit = () => {
762
+ if (isEditing) {
763
+ if (editContent.trim() !== message.content) {
764
+ onAction?.({ action: "edit", messageId: message.id, content: editContent.trim() });
765
+ }
766
+ setIsEditing(false);
767
+ } else {
768
+ setEditContent(message.content);
769
+ setIsEditing(true);
770
+ }
771
+ };
772
+ const handleCancelEdit = () => {
773
+ setEditContent(message.content);
774
+ setIsEditing(false);
775
+ };
776
+ const handleRegenerate = () => {
777
+ onAction?.({ action: "regenerate", messageId: message.id });
778
+ };
779
+ const formatTime = (timestamp) => {
780
+ return new Date(timestamp).toLocaleTimeString("pt-BR", {
781
+ hour: "2-digit",
782
+ minute: "2-digit"
783
+ });
784
+ };
785
+ return /* @__PURE__ */ jsx7(TooltipProvider, { children: /* @__PURE__ */ jsxs2(
786
+ "div",
787
+ {
788
+ className: `flex w-full flex-col ${className} max-w-[800px] mx-auto`,
789
+ onMouseEnter: () => setShowActions(true),
790
+ onMouseLeave: () => setShowActions(false),
791
+ children: [
792
+ /* @__PURE__ */ jsxs2("div", { className: `flex gap-3 ${messageIsUser ? "flex-row-reverse" : "flex-row"} w-full mb-1`, children: [
793
+ showAvatar && /* @__PURE__ */ jsx7("div", { className: `flex-shrink-0 ${compactMode ? "mt-1" : "mt-0"}`, children: /* @__PURE__ */ jsx7(Avatar, { className: compactMode ? "h-6 w-6" : "h-8 w-8", children: messageIsUser ? /* @__PURE__ */ jsxs2(Fragment, { children: [
794
+ /* @__PURE__ */ jsx7(AvatarImage, { src: userAvatar, alt: userName }),
795
+ /* @__PURE__ */ jsx7(AvatarFallback, { className: "bg-primary text-primary-foreground", children: userName.charAt(0).toUpperCase() })
796
+ ] }) : /* @__PURE__ */ jsx7(Fragment, { children: assistantAvatar || /* @__PURE__ */ jsx7(AvatarFallback, { className: "bg-secondary text-secondary-foreground", children: "AI" }) }) }) }),
797
+ /* @__PURE__ */ jsxs2("div", { className: `flex items-center gap-2 mb-1 ${messageIsUser ? "flex-row-reverse" : "flex-row"}`, children: [
798
+ /* @__PURE__ */ jsx7("span", { className: `font-medium ${compactMode ? "text-sm" : "text-base"}`, children: messageIsUser ? userName : assistantName }),
799
+ showTimestamp && /* @__PURE__ */ jsx7("span", { className: "text-xs text-muted-foreground", children: formatTime(message.timestamp) }),
800
+ message.isEdited && /* @__PURE__ */ jsx7(Badge, { variant: "outline", className: "text-xs", children: "editado" })
801
+ ] })
802
+ ] }),
803
+ /* @__PURE__ */ jsx7("div", { className: `flex-1 min-w-0 ${messageIsUser ? "text-right" : "text-left"}`, children: /* @__PURE__ */ jsxs2("div", { className: `relative inline-flex flex-col ${messageIsUser ? "rounded-lg p-3 bg-primary text-primary-foreground ml-auto max-w-[85%]" : "max-w-[85%]"}`, children: [
804
+ isEditing ? /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
805
+ /* @__PURE__ */ jsx7(
806
+ Textarea,
807
+ {
808
+ value: editContent,
809
+ onChange: (e) => setEditContent(e.target.value),
810
+ className: "min-h-[100px] resize-none",
811
+ autoFocus: true
812
+ }
813
+ ),
814
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-2 justify-end", children: [
815
+ /* @__PURE__ */ jsxs2(Button, { variant: "outline", size: "sm", onClick: handleCancelEdit, children: [
816
+ /* @__PURE__ */ jsx7(X, { className: "h-4 w-4 mr-1" }),
817
+ "Cancelar"
818
+ ] }),
819
+ /* @__PURE__ */ jsxs2(Button, { size: "sm", onClick: handleEdit, children: [
820
+ /* @__PURE__ */ jsx7(Check, { className: "h-4 w-4 mr-1" }),
821
+ "Salvar"
822
+ ] })
823
+ ] })
824
+ ] }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
825
+ enableToolCallsDisplay && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mb-3", children: /* @__PURE__ */ jsx7(ToolCallsDisplay, { toolCalls: message.toolCalls, label: toolUsedLabel }) }),
826
+ /* @__PURE__ */ jsx7(
827
+ StreamingText,
828
+ {
829
+ content: message.content,
830
+ isStreaming: message.isStreaming,
831
+ thinkingLabel
832
+ }
833
+ ),
834
+ message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mt-3 space-y-2", children: message.attachments.map((attachment, index) => /* @__PURE__ */ jsx7(MediaRenderer, { attachment }, index)) })
835
+ ] }),
836
+ !isEditing && (showActions || copied) && /* @__PURE__ */ jsxs2("div", { className: `absolute -top-2 flex gap-1 ${messageIsUser ? "-left-2" : "-right-2"}`, children: [
837
+ enableCopy && /* @__PURE__ */ jsxs2(Tooltip, { children: [
838
+ /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
839
+ Button,
840
+ {
841
+ variant: "secondary",
842
+ size: "icon",
843
+ className: "h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity",
844
+ onClick: handleCopy,
845
+ children: copied ? /* @__PURE__ */ jsx7(Check, { className: "h-3 w-3 text-green-500" }) : /* @__PURE__ */ jsx7(Copy, { className: "h-3 w-3" })
846
+ }
847
+ ) }),
848
+ /* @__PURE__ */ jsx7(TooltipContent, { children: copied ? "Copiado!" : "Copiar" })
849
+ ] }),
850
+ canEdit && /* @__PURE__ */ jsxs2(Tooltip, { children: [
851
+ /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
852
+ Button,
853
+ {
854
+ variant: "secondary",
855
+ size: "icon",
856
+ className: "h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity",
857
+ onClick: handleEdit,
858
+ children: /* @__PURE__ */ jsx7(Edit, { className: "h-3 w-3" })
859
+ }
860
+ ) }),
861
+ /* @__PURE__ */ jsx7(TooltipContent, { children: "Editar" })
862
+ ] }),
863
+ canRegenerate && /* @__PURE__ */ jsxs2(Tooltip, { children: [
864
+ /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
865
+ Button,
866
+ {
867
+ variant: "secondary",
868
+ size: "icon",
869
+ className: "h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity",
870
+ onClick: handleRegenerate,
871
+ children: /* @__PURE__ */ jsx7(RotateCcw, { className: "h-3 w-3" })
872
+ }
873
+ ) }),
874
+ /* @__PURE__ */ jsx7(TooltipContent, { children: "Regenerar" })
875
+ ] })
876
+ ] })
877
+ ] }) })
878
+ ]
879
+ }
880
+ ) });
881
+ };
882
+
883
+ // src/components/chat/Sidebar.tsx
884
+ import { useState as useState4, useRef as useRef2, useEffect as useEffect4 } from "react";
885
+
886
+ // src/components/ui/input.tsx
887
+ import { jsx as jsx8 } from "react/jsx-runtime";
888
+ function Input({ className, type, ...props }) {
889
+ return /* @__PURE__ */ jsx8(
890
+ "input",
891
+ {
892
+ type,
893
+ "data-slot": "input",
894
+ className: cn(
895
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
896
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
897
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
898
+ className
899
+ ),
900
+ ...props
901
+ }
902
+ );
903
+ }
904
+
905
+ // src/components/ui/sidebar.tsx
906
+ import * as React3 from "react";
907
+ import { Slot as Slot3 } from "@radix-ui/react-slot";
908
+ import { cva as cva3 } from "class-variance-authority";
909
+ import { PanelLeftIcon } from "lucide-react";
910
+
911
+ // src/hooks/use-mobile.ts
912
+ import * as React2 from "react";
913
+ var MOBILE_BREAKPOINT = 768;
914
+ function useIsMobile() {
915
+ const [isMobile, setIsMobile] = React2.useState(void 0);
916
+ React2.useEffect(() => {
917
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
918
+ const onChange = () => {
919
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
920
+ };
921
+ mql.addEventListener("change", onChange);
922
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
923
+ return () => mql.removeEventListener("change", onChange);
924
+ }, []);
925
+ return !!isMobile;
926
+ }
927
+
928
+ // src/components/ui/separator.tsx
929
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
930
+ import { jsx as jsx9 } from "react/jsx-runtime";
931
+ function Separator({
932
+ className,
933
+ orientation = "horizontal",
934
+ decorative = true,
935
+ ...props
936
+ }) {
937
+ return /* @__PURE__ */ jsx9(
938
+ SeparatorPrimitive.Root,
939
+ {
940
+ "data-slot": "separator",
941
+ decorative,
942
+ orientation,
943
+ className: cn(
944
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
945
+ className
946
+ ),
947
+ ...props
948
+ }
949
+ );
950
+ }
951
+
952
+ // src/components/ui/sheet.tsx
953
+ import * as SheetPrimitive from "@radix-ui/react-dialog";
954
+ import { XIcon } from "lucide-react";
955
+ import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
956
+ function Sheet({ ...props }) {
957
+ return /* @__PURE__ */ jsx10(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
958
+ }
959
+ function SheetPortal({
960
+ ...props
961
+ }) {
962
+ return /* @__PURE__ */ jsx10(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
963
+ }
964
+ function SheetOverlay({
965
+ className,
966
+ ...props
967
+ }) {
968
+ return /* @__PURE__ */ jsx10(
969
+ SheetPrimitive.Overlay,
970
+ {
971
+ "data-slot": "sheet-overlay",
972
+ className: cn(
973
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
974
+ className
975
+ ),
976
+ ...props
977
+ }
978
+ );
979
+ }
980
+ function SheetContent({
981
+ className,
982
+ children,
983
+ side = "right",
984
+ ...props
985
+ }) {
986
+ return /* @__PURE__ */ jsxs3(SheetPortal, { children: [
987
+ /* @__PURE__ */ jsx10(SheetOverlay, {}),
988
+ /* @__PURE__ */ jsxs3(
989
+ SheetPrimitive.Content,
990
+ {
991
+ "data-slot": "sheet-content",
992
+ className: cn(
993
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
994
+ side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
995
+ side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
996
+ side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
997
+ side === "bottom" && "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
998
+ className
999
+ ),
1000
+ ...props,
1001
+ children: [
1002
+ children,
1003
+ /* @__PURE__ */ jsxs3(SheetPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none", children: [
1004
+ /* @__PURE__ */ jsx10(XIcon, { className: "size-4" }),
1005
+ /* @__PURE__ */ jsx10("span", { className: "sr-only", children: "Close" })
1006
+ ] })
1007
+ ]
1008
+ }
1009
+ )
1010
+ ] });
1011
+ }
1012
+ function SheetHeader({ className, ...props }) {
1013
+ return /* @__PURE__ */ jsx10(
1014
+ "div",
1015
+ {
1016
+ "data-slot": "sheet-header",
1017
+ className: cn("flex flex-col gap-1.5 p-4", className),
1018
+ ...props
1019
+ }
1020
+ );
1021
+ }
1022
+ function SheetTitle({
1023
+ className,
1024
+ ...props
1025
+ }) {
1026
+ return /* @__PURE__ */ jsx10(
1027
+ SheetPrimitive.Title,
1028
+ {
1029
+ "data-slot": "sheet-title",
1030
+ className: cn("text-foreground font-semibold", className),
1031
+ ...props
1032
+ }
1033
+ );
1034
+ }
1035
+ function SheetDescription({
1036
+ className,
1037
+ ...props
1038
+ }) {
1039
+ return /* @__PURE__ */ jsx10(
1040
+ SheetPrimitive.Description,
1041
+ {
1042
+ "data-slot": "sheet-description",
1043
+ className: cn("text-muted-foreground text-sm", className),
1044
+ ...props
1045
+ }
1046
+ );
1047
+ }
1048
+
1049
+ // src/components/ui/sidebar.tsx
1050
+ import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
1051
+ var SIDEBAR_COOKIE_NAME = "sidebar_state";
1052
+ var SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
1053
+ var SIDEBAR_WIDTH = "16rem";
1054
+ var SIDEBAR_WIDTH_MOBILE = "18rem";
1055
+ var SIDEBAR_WIDTH_ICON = "3rem";
1056
+ var SIDEBAR_KEYBOARD_SHORTCUT = "b";
1057
+ var SidebarContext = React3.createContext(null);
1058
+ function useSidebar() {
1059
+ const context = React3.useContext(SidebarContext);
1060
+ if (!context) {
1061
+ throw new Error("useSidebar must be used within a SidebarProvider.");
1062
+ }
1063
+ return context;
1064
+ }
1065
+ function SidebarProvider({
1066
+ defaultOpen = true,
1067
+ open: openProp,
1068
+ onOpenChange: setOpenProp,
1069
+ className,
1070
+ style,
1071
+ children,
1072
+ ...props
1073
+ }) {
1074
+ const isMobile = useIsMobile();
1075
+ const [openMobile, setOpenMobile] = React3.useState(false);
1076
+ const [_open, _setOpen] = React3.useState(defaultOpen);
1077
+ const open = openProp ?? _open;
1078
+ const setOpen = React3.useCallback(
1079
+ (value) => {
1080
+ const openState = typeof value === "function" ? value(open) : value;
1081
+ if (setOpenProp) {
1082
+ setOpenProp(openState);
1083
+ } else {
1084
+ _setOpen(openState);
1085
+ }
1086
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
1087
+ },
1088
+ [setOpenProp, open]
1089
+ );
1090
+ const toggleSidebar = React3.useCallback(() => {
1091
+ return isMobile ? setOpenMobile((open2) => !open2) : setOpen((open2) => !open2);
1092
+ }, [isMobile, setOpen, setOpenMobile]);
1093
+ React3.useEffect(() => {
1094
+ const handleKeyDown = (event) => {
1095
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
1096
+ event.preventDefault();
1097
+ toggleSidebar();
1098
+ }
1099
+ };
1100
+ window.addEventListener("keydown", handleKeyDown);
1101
+ return () => window.removeEventListener("keydown", handleKeyDown);
1102
+ }, [toggleSidebar]);
1103
+ const state = open ? "expanded" : "collapsed";
1104
+ const contextValue = React3.useMemo(
1105
+ () => ({
1106
+ state,
1107
+ open,
1108
+ setOpen,
1109
+ isMobile,
1110
+ openMobile,
1111
+ setOpenMobile,
1112
+ toggleSidebar
1113
+ }),
1114
+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
1115
+ );
1116
+ return /* @__PURE__ */ jsx11(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx11(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx11(
1117
+ "div",
1118
+ {
1119
+ "data-slot": "sidebar-wrapper",
1120
+ style: {
1121
+ "--sidebar-width": SIDEBAR_WIDTH,
1122
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
1123
+ ...style
1124
+ },
1125
+ className: cn(
1126
+ "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
1127
+ className
1128
+ ),
1129
+ ...props,
1130
+ children
1131
+ }
1132
+ ) }) });
1133
+ }
1134
+ function Sidebar({
1135
+ side = "left",
1136
+ variant = "sidebar",
1137
+ collapsible = "offcanvas",
1138
+ className,
1139
+ children,
1140
+ ...props
1141
+ }) {
1142
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
1143
+ if (collapsible === "none") {
1144
+ return /* @__PURE__ */ jsx11(
1145
+ "div",
1146
+ {
1147
+ "data-slot": "sidebar",
1148
+ className: cn(
1149
+ "bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",
1150
+ className
1151
+ ),
1152
+ ...props,
1153
+ children
1154
+ }
1155
+ );
1156
+ }
1157
+ if (isMobile) {
1158
+ return /* @__PURE__ */ jsx11(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: /* @__PURE__ */ jsxs4(
1159
+ SheetContent,
1160
+ {
1161
+ "data-sidebar": "sidebar",
1162
+ "data-slot": "sidebar",
1163
+ "data-mobile": "true",
1164
+ className: "bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden",
1165
+ style: {
1166
+ "--sidebar-width": SIDEBAR_WIDTH_MOBILE
1167
+ },
1168
+ side,
1169
+ children: [
1170
+ /* @__PURE__ */ jsxs4(SheetHeader, { className: "sr-only", children: [
1171
+ /* @__PURE__ */ jsx11(SheetTitle, { children: "Sidebar" }),
1172
+ /* @__PURE__ */ jsx11(SheetDescription, { children: "Displays the mobile sidebar." })
1173
+ ] }),
1174
+ /* @__PURE__ */ jsx11("div", { className: "flex h-full w-full flex-col", children })
1175
+ ]
1176
+ }
1177
+ ) });
1178
+ }
1179
+ return /* @__PURE__ */ jsxs4(
1180
+ "div",
1181
+ {
1182
+ className: "group peer text-sidebar-foreground hidden md:block",
1183
+ "data-state": state,
1184
+ "data-collapsible": state === "collapsed" ? collapsible : "",
1185
+ "data-variant": variant,
1186
+ "data-side": side,
1187
+ "data-slot": "sidebar",
1188
+ children: [
1189
+ /* @__PURE__ */ jsx11(
1190
+ "div",
1191
+ {
1192
+ "data-slot": "sidebar-gap",
1193
+ className: cn(
1194
+ "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
1195
+ "group-data-[collapsible=offcanvas]:w-0",
1196
+ "group-data-[side=right]:rotate-180",
1197
+ variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
1198
+ )
1199
+ }
1200
+ ),
1201
+ /* @__PURE__ */ jsx11(
1202
+ "div",
1203
+ {
1204
+ "data-slot": "sidebar-container",
1205
+ className: cn(
1206
+ "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
1207
+ side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
1208
+ // Adjust the padding for floating and inset variants.
1209
+ variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
1210
+ className
1211
+ ),
1212
+ ...props,
1213
+ children: /* @__PURE__ */ jsx11(
1214
+ "div",
1215
+ {
1216
+ "data-sidebar": "sidebar",
1217
+ "data-slot": "sidebar-inner",
1218
+ className: "bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm",
1219
+ children
1220
+ }
1221
+ )
1222
+ }
1223
+ )
1224
+ ]
1225
+ }
1226
+ );
1227
+ }
1228
+ function SidebarTrigger({
1229
+ className,
1230
+ onClick,
1231
+ ...props
1232
+ }) {
1233
+ const { toggleSidebar } = useSidebar();
1234
+ return /* @__PURE__ */ jsxs4(
1235
+ Button,
1236
+ {
1237
+ "data-sidebar": "trigger",
1238
+ "data-slot": "sidebar-trigger",
1239
+ variant: "ghost",
1240
+ size: "icon",
1241
+ className: cn("size-7", className),
1242
+ onClick: (event) => {
1243
+ onClick?.(event);
1244
+ toggleSidebar();
1245
+ },
1246
+ ...props,
1247
+ children: [
1248
+ /* @__PURE__ */ jsx11(PanelLeftIcon, {}),
1249
+ /* @__PURE__ */ jsx11("span", { className: "sr-only", children: "Toggle Sidebar" })
1250
+ ]
1251
+ }
1252
+ );
1253
+ }
1254
+ function SidebarRail({ className, ...props }) {
1255
+ const { toggleSidebar } = useSidebar();
1256
+ return /* @__PURE__ */ jsx11(
1257
+ "button",
1258
+ {
1259
+ "data-sidebar": "rail",
1260
+ "data-slot": "sidebar-rail",
1261
+ "aria-label": "Toggle Sidebar",
1262
+ tabIndex: -1,
1263
+ onClick: toggleSidebar,
1264
+ title: "Toggle Sidebar",
1265
+ className: cn(
1266
+ "hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
1267
+ "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
1268
+ "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
1269
+ "hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
1270
+ "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
1271
+ "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
1272
+ className
1273
+ ),
1274
+ ...props
1275
+ }
1276
+ );
1277
+ }
1278
+ function SidebarInset({ className, ...props }) {
1279
+ return /* @__PURE__ */ jsx11(
1280
+ "main",
1281
+ {
1282
+ "data-slot": "sidebar-inset",
1283
+ className: cn(
1284
+ "bg-background relative flex w-full flex-1 flex-col",
1285
+ "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
1286
+ className
1287
+ ),
1288
+ ...props
1289
+ }
1290
+ );
1291
+ }
1292
+ function SidebarHeader({ className, ...props }) {
1293
+ return /* @__PURE__ */ jsx11(
1294
+ "div",
1295
+ {
1296
+ "data-slot": "sidebar-header",
1297
+ "data-sidebar": "header",
1298
+ className: cn("flex flex-col gap-2 p-2", className),
1299
+ ...props
1300
+ }
1301
+ );
1302
+ }
1303
+ function SidebarFooter({ className, ...props }) {
1304
+ return /* @__PURE__ */ jsx11(
1305
+ "div",
1306
+ {
1307
+ "data-slot": "sidebar-footer",
1308
+ "data-sidebar": "footer",
1309
+ className: cn("flex flex-col gap-2 p-2", className),
1310
+ ...props
1311
+ }
1312
+ );
1313
+ }
1314
+ function SidebarContent({ className, ...props }) {
1315
+ return /* @__PURE__ */ jsx11(
1316
+ "div",
1317
+ {
1318
+ "data-slot": "sidebar-content",
1319
+ "data-sidebar": "content",
1320
+ className: cn(
1321
+ "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
1322
+ className
1323
+ ),
1324
+ ...props
1325
+ }
1326
+ );
1327
+ }
1328
+ function SidebarGroup({ className, ...props }) {
1329
+ return /* @__PURE__ */ jsx11(
1330
+ "div",
1331
+ {
1332
+ "data-slot": "sidebar-group",
1333
+ "data-sidebar": "group",
1334
+ className: cn("relative flex w-full min-w-0 flex-col p-2", className),
1335
+ ...props
1336
+ }
1337
+ );
1338
+ }
1339
+ function SidebarGroupLabel({
1340
+ className,
1341
+ asChild = false,
1342
+ ...props
1343
+ }) {
1344
+ const Comp = asChild ? Slot3 : "div";
1345
+ return /* @__PURE__ */ jsx11(
1346
+ Comp,
1347
+ {
1348
+ "data-slot": "sidebar-group-label",
1349
+ "data-sidebar": "group-label",
1350
+ className: cn(
1351
+ "text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
1352
+ "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
1353
+ className
1354
+ ),
1355
+ ...props
1356
+ }
1357
+ );
1358
+ }
1359
+ function SidebarGroupContent({
1360
+ className,
1361
+ ...props
1362
+ }) {
1363
+ return /* @__PURE__ */ jsx11(
1364
+ "div",
1365
+ {
1366
+ "data-slot": "sidebar-group-content",
1367
+ "data-sidebar": "group-content",
1368
+ className: cn("w-full text-sm", className),
1369
+ ...props
1370
+ }
1371
+ );
1372
+ }
1373
+ function SidebarMenu({ className, ...props }) {
1374
+ return /* @__PURE__ */ jsx11(
1375
+ "ul",
1376
+ {
1377
+ "data-slot": "sidebar-menu",
1378
+ "data-sidebar": "menu",
1379
+ className: cn("flex w-full min-w-0 flex-col gap-1", className),
1380
+ ...props
1381
+ }
1382
+ );
1383
+ }
1384
+ function SidebarMenuItem({ className, ...props }) {
1385
+ return /* @__PURE__ */ jsx11(
1386
+ "li",
1387
+ {
1388
+ "data-slot": "sidebar-menu-item",
1389
+ "data-sidebar": "menu-item",
1390
+ className: cn("group/menu-item relative", className),
1391
+ ...props
1392
+ }
1393
+ );
1394
+ }
1395
+ var sidebarMenuButtonVariants = cva3(
1396
+ "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
1397
+ {
1398
+ variants: {
1399
+ variant: {
1400
+ default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
1401
+ outline: "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
1402
+ },
1403
+ size: {
1404
+ default: "h-8 text-sm",
1405
+ sm: "h-7 text-xs",
1406
+ lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
1407
+ }
1408
+ },
1409
+ defaultVariants: {
1410
+ variant: "default",
1411
+ size: "default"
1412
+ }
1413
+ }
1414
+ );
1415
+ function SidebarMenuButton({
1416
+ asChild = false,
1417
+ isActive = false,
1418
+ variant = "default",
1419
+ size = "default",
1420
+ tooltip,
1421
+ className,
1422
+ ...props
1423
+ }) {
1424
+ const Comp = asChild ? Slot3 : "button";
1425
+ const { isMobile, state } = useSidebar();
1426
+ const button = /* @__PURE__ */ jsx11(
1427
+ Comp,
1428
+ {
1429
+ "data-slot": "sidebar-menu-button",
1430
+ "data-sidebar": "menu-button",
1431
+ "data-size": size,
1432
+ "data-active": isActive,
1433
+ className: cn(sidebarMenuButtonVariants({ variant, size }), className),
1434
+ ...props
1435
+ }
1436
+ );
1437
+ if (!tooltip) {
1438
+ return button;
1439
+ }
1440
+ if (typeof tooltip === "string") {
1441
+ tooltip = {
1442
+ children: tooltip
1443
+ };
1444
+ }
1445
+ return /* @__PURE__ */ jsxs4(Tooltip, { children: [
1446
+ /* @__PURE__ */ jsx11(TooltipTrigger, { asChild: true, children: button }),
1447
+ /* @__PURE__ */ jsx11(
1448
+ TooltipContent,
1449
+ {
1450
+ side: "right",
1451
+ align: "center",
1452
+ hidden: state !== "collapsed" || isMobile,
1453
+ ...tooltip
1454
+ }
1455
+ )
1456
+ ] });
1457
+ }
1458
+ function SidebarMenuAction({
1459
+ className,
1460
+ asChild = false,
1461
+ showOnHover = false,
1462
+ ...props
1463
+ }) {
1464
+ const Comp = asChild ? Slot3 : "button";
1465
+ return /* @__PURE__ */ jsx11(
1466
+ Comp,
1467
+ {
1468
+ "data-slot": "sidebar-menu-action",
1469
+ "data-sidebar": "menu-action",
1470
+ className: cn(
1471
+ "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
1472
+ // Increases the hit area of the button on mobile.
1473
+ "after:absolute after:-inset-2 md:after:hidden",
1474
+ "peer-data-[size=sm]/menu-button:top-1",
1475
+ "peer-data-[size=default]/menu-button:top-1.5",
1476
+ "peer-data-[size=lg]/menu-button:top-2.5",
1477
+ "group-data-[collapsible=icon]:hidden",
1478
+ showOnHover && "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
1479
+ className
1480
+ ),
1481
+ ...props
1482
+ }
1483
+ );
1484
+ }
1485
+
1486
+ // src/components/ui/dialog.tsx
1487
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
1488
+ import { XIcon as XIcon2 } from "lucide-react";
1489
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
1490
+ function Dialog({
1491
+ ...props
1492
+ }) {
1493
+ return /* @__PURE__ */ jsx12(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
1494
+ }
1495
+ function DialogTrigger({
1496
+ ...props
1497
+ }) {
1498
+ return /* @__PURE__ */ jsx12(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
1499
+ }
1500
+ function DialogPortal({
1501
+ ...props
1502
+ }) {
1503
+ return /* @__PURE__ */ jsx12(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
1504
+ }
1505
+ function DialogOverlay({
1506
+ className,
1507
+ ...props
1508
+ }) {
1509
+ return /* @__PURE__ */ jsx12(
1510
+ DialogPrimitive.Overlay,
1511
+ {
1512
+ "data-slot": "dialog-overlay",
1513
+ className: cn(
1514
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1515
+ className
1516
+ ),
1517
+ ...props
1518
+ }
1519
+ );
1520
+ }
1521
+ function DialogContent({
1522
+ className,
1523
+ children,
1524
+ showCloseButton = true,
1525
+ ...props
1526
+ }) {
1527
+ return /* @__PURE__ */ jsxs5(DialogPortal, { "data-slot": "dialog-portal", children: [
1528
+ /* @__PURE__ */ jsx12(DialogOverlay, {}),
1529
+ /* @__PURE__ */ jsxs5(
1530
+ DialogPrimitive.Content,
1531
+ {
1532
+ "data-slot": "dialog-content",
1533
+ className: cn(
1534
+ "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
1535
+ className
1536
+ ),
1537
+ ...props,
1538
+ children: [
1539
+ children,
1540
+ showCloseButton && /* @__PURE__ */ jsxs5(
1541
+ DialogPrimitive.Close,
1542
+ {
1543
+ "data-slot": "dialog-close",
1544
+ className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1545
+ children: [
1546
+ /* @__PURE__ */ jsx12(XIcon2, {}),
1547
+ /* @__PURE__ */ jsx12("span", { className: "sr-only", children: "Close" })
1548
+ ]
1549
+ }
1550
+ )
1551
+ ]
1552
+ }
1553
+ )
1554
+ ] });
1555
+ }
1556
+ function DialogHeader({ className, ...props }) {
1557
+ return /* @__PURE__ */ jsx12(
1558
+ "div",
1559
+ {
1560
+ "data-slot": "dialog-header",
1561
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1562
+ ...props
1563
+ }
1564
+ );
1565
+ }
1566
+ function DialogFooter({ className, ...props }) {
1567
+ return /* @__PURE__ */ jsx12(
1568
+ "div",
1569
+ {
1570
+ "data-slot": "dialog-footer",
1571
+ className: cn(
1572
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1573
+ className
1574
+ ),
1575
+ ...props
1576
+ }
1577
+ );
1578
+ }
1579
+ function DialogTitle({
1580
+ className,
1581
+ ...props
1582
+ }) {
1583
+ return /* @__PURE__ */ jsx12(
1584
+ DialogPrimitive.Title,
1585
+ {
1586
+ "data-slot": "dialog-title",
1587
+ className: cn("text-lg leading-none font-semibold", className),
1588
+ ...props
1589
+ }
1590
+ );
1591
+ }
1592
+ function DialogDescription({
1593
+ className,
1594
+ ...props
1595
+ }) {
1596
+ return /* @__PURE__ */ jsx12(
1597
+ DialogPrimitive.Description,
1598
+ {
1599
+ "data-slot": "dialog-description",
1600
+ className: cn("text-muted-foreground text-sm", className),
1601
+ ...props
1602
+ }
1603
+ );
1604
+ }
1605
+
1606
+ // src/components/ui/alert-dialog.tsx
1607
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
1608
+ import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
1609
+ function AlertDialog({
1610
+ ...props
1611
+ }) {
1612
+ return /* @__PURE__ */ jsx13(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", ...props });
1613
+ }
1614
+ function AlertDialogPortal({
1615
+ ...props
1616
+ }) {
1617
+ return /* @__PURE__ */ jsx13(AlertDialogPrimitive.Portal, { "data-slot": "alert-dialog-portal", ...props });
1618
+ }
1619
+ function AlertDialogOverlay({
1620
+ className,
1621
+ ...props
1622
+ }) {
1623
+ return /* @__PURE__ */ jsx13(
1624
+ AlertDialogPrimitive.Overlay,
1625
+ {
1626
+ "data-slot": "alert-dialog-overlay",
1627
+ className: cn(
1628
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1629
+ className
1630
+ ),
1631
+ ...props
1632
+ }
1633
+ );
1634
+ }
1635
+ function AlertDialogContent({
1636
+ className,
1637
+ ...props
1638
+ }) {
1639
+ return /* @__PURE__ */ jsxs6(AlertDialogPortal, { children: [
1640
+ /* @__PURE__ */ jsx13(AlertDialogOverlay, {}),
1641
+ /* @__PURE__ */ jsx13(
1642
+ AlertDialogPrimitive.Content,
1643
+ {
1644
+ "data-slot": "alert-dialog-content",
1645
+ className: cn(
1646
+ "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
1647
+ className
1648
+ ),
1649
+ ...props
1650
+ }
1651
+ )
1652
+ ] });
1653
+ }
1654
+ function AlertDialogHeader({
1655
+ className,
1656
+ ...props
1657
+ }) {
1658
+ return /* @__PURE__ */ jsx13(
1659
+ "div",
1660
+ {
1661
+ "data-slot": "alert-dialog-header",
1662
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1663
+ ...props
1664
+ }
1665
+ );
1666
+ }
1667
+ function AlertDialogFooter({
1668
+ className,
1669
+ ...props
1670
+ }) {
1671
+ return /* @__PURE__ */ jsx13(
1672
+ "div",
1673
+ {
1674
+ "data-slot": "alert-dialog-footer",
1675
+ className: cn(
1676
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1677
+ className
1678
+ ),
1679
+ ...props
1680
+ }
1681
+ );
1682
+ }
1683
+ function AlertDialogTitle({
1684
+ className,
1685
+ ...props
1686
+ }) {
1687
+ return /* @__PURE__ */ jsx13(
1688
+ AlertDialogPrimitive.Title,
1689
+ {
1690
+ "data-slot": "alert-dialog-title",
1691
+ className: cn("text-lg font-semibold", className),
1692
+ ...props
1693
+ }
1694
+ );
1695
+ }
1696
+ function AlertDialogDescription({
1697
+ className,
1698
+ ...props
1699
+ }) {
1700
+ return /* @__PURE__ */ jsx13(
1701
+ AlertDialogPrimitive.Description,
1702
+ {
1703
+ "data-slot": "alert-dialog-description",
1704
+ className: cn("text-muted-foreground text-sm", className),
1705
+ ...props
1706
+ }
1707
+ );
1708
+ }
1709
+ function AlertDialogAction({
1710
+ className,
1711
+ ...props
1712
+ }) {
1713
+ return /* @__PURE__ */ jsx13(
1714
+ AlertDialogPrimitive.Action,
1715
+ {
1716
+ className: cn(buttonVariants(), className),
1717
+ ...props
1718
+ }
1719
+ );
1720
+ }
1721
+ function AlertDialogCancel({
1722
+ className,
1723
+ ...props
1724
+ }) {
1725
+ return /* @__PURE__ */ jsx13(
1726
+ AlertDialogPrimitive.Cancel,
1727
+ {
1728
+ className: cn(buttonVariants({ variant: "outline" }), className),
1729
+ ...props
1730
+ }
1731
+ );
1732
+ }
1733
+
1734
+ // src/components/ui/dropdown-menu.tsx
1735
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
1736
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
1737
+ import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
1738
+ function DropdownMenu({
1739
+ ...props
1740
+ }) {
1741
+ return /* @__PURE__ */ jsx14(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1742
+ }
1743
+ function DropdownMenuTrigger({
1744
+ ...props
1745
+ }) {
1746
+ return /* @__PURE__ */ jsx14(
1747
+ DropdownMenuPrimitive.Trigger,
1748
+ {
1749
+ "data-slot": "dropdown-menu-trigger",
1750
+ ...props
1751
+ }
1752
+ );
1753
+ }
1754
+ function DropdownMenuContent({
1755
+ className,
1756
+ sideOffset = 4,
1757
+ ...props
1758
+ }) {
1759
+ return /* @__PURE__ */ jsx14(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx14(
1760
+ DropdownMenuPrimitive.Content,
1761
+ {
1762
+ "data-slot": "dropdown-menu-content",
1763
+ sideOffset,
1764
+ className: cn(
1765
+ "bg-popover text-popover-foreground 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 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
1766
+ className
1767
+ ),
1768
+ ...props
1769
+ }
1770
+ ) });
1771
+ }
1772
+ function DropdownMenuItem({
1773
+ className,
1774
+ inset,
1775
+ variant = "default",
1776
+ ...props
1777
+ }) {
1778
+ return /* @__PURE__ */ jsx14(
1779
+ DropdownMenuPrimitive.Item,
1780
+ {
1781
+ "data-slot": "dropdown-menu-item",
1782
+ "data-inset": inset,
1783
+ "data-variant": variant,
1784
+ className: cn(
1785
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1786
+ className
1787
+ ),
1788
+ ...props
1789
+ }
1790
+ );
1791
+ }
1792
+ function DropdownMenuLabel({
1793
+ className,
1794
+ inset,
1795
+ ...props
1796
+ }) {
1797
+ return /* @__PURE__ */ jsx14(
1798
+ DropdownMenuPrimitive.Label,
1799
+ {
1800
+ "data-slot": "dropdown-menu-label",
1801
+ "data-inset": inset,
1802
+ className: cn(
1803
+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
1804
+ className
1805
+ ),
1806
+ ...props
1807
+ }
1808
+ );
1809
+ }
1810
+ function DropdownMenuSeparator({
1811
+ className,
1812
+ ...props
1813
+ }) {
1814
+ return /* @__PURE__ */ jsx14(
1815
+ DropdownMenuPrimitive.Separator,
1816
+ {
1817
+ "data-slot": "dropdown-menu-separator",
1818
+ className: cn("bg-border -mx-1 my-1 h-px", className),
1819
+ ...props
1820
+ }
1821
+ );
1822
+ }
1823
+
1824
+ // src/components/chat/Sidebar.tsx
1825
+ import {
1826
+ Plus,
1827
+ MoreHorizontal,
1828
+ Edit2,
1829
+ Trash2,
1830
+ Archive,
1831
+ Search,
1832
+ Filter
1833
+ } from "lucide-react";
1834
+
1835
+ // src/components/chat/UserMenu.tsx
1836
+ import {
1837
+ User,
1838
+ Settings,
1839
+ LogOut,
1840
+ ChevronsUpDown,
1841
+ Moon,
1842
+ Sun,
1843
+ Palette
1844
+ } from "lucide-react";
1845
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
1846
+ var getInitials = (name, email) => {
1847
+ if (name) {
1848
+ return name.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase();
1849
+ }
1850
+ if (email) {
1851
+ return email[0].toUpperCase();
1852
+ }
1853
+ return "U";
1854
+ };
1855
+ var getDisplayName = (user, guestLabel) => {
1856
+ if (!user) return guestLabel || "Guest";
1857
+ return user.name || user.email?.split("@")[0] || guestLabel || "Guest";
1858
+ };
1859
+ var UserMenu = ({
1860
+ user,
1861
+ config,
1862
+ callbacks,
1863
+ currentTheme = "system",
1864
+ showThemeOptions = true,
1865
+ additionalItems
1866
+ }) => {
1867
+ const { isMobile } = useSidebar();
1868
+ const labels = {
1869
+ profile: config?.labels?.profile || "Profile",
1870
+ settings: config?.labels?.settings || "Settings",
1871
+ theme: config?.labels?.theme || "Theme",
1872
+ lightMode: config?.labels?.lightMode || "Light",
1873
+ darkMode: config?.labels?.darkMode || "Dark",
1874
+ systemTheme: config?.labels?.systemTheme || "System",
1875
+ logout: config?.labels?.logout || "Log out",
1876
+ guest: config?.labels?.guest || "Guest"
1877
+ };
1878
+ const displayName = getDisplayName(user, labels.guest);
1879
+ const initials = getInitials(user?.name, user?.email);
1880
+ return /* @__PURE__ */ jsx15(SidebarMenu, { children: /* @__PURE__ */ jsx15(SidebarMenuItem, { children: /* @__PURE__ */ jsxs8(DropdownMenu, { children: [
1881
+ /* @__PURE__ */ jsx15(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs8(
1882
+ SidebarMenuButton,
1883
+ {
1884
+ size: "lg",
1885
+ className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
1886
+ tooltip: displayName,
1887
+ children: [
1888
+ /* @__PURE__ */ jsxs8(Avatar, { className: "h-8 w-8 rounded-lg", children: [
1889
+ user?.avatar && /* @__PURE__ */ jsx15(AvatarImage, { src: user.avatar, alt: displayName }),
1890
+ /* @__PURE__ */ jsx15(AvatarFallback, { className: "rounded-lg bg-primary/10 text-primary text-xs font-medium", children: initials })
1891
+ ] }),
1892
+ /* @__PURE__ */ jsxs8("div", { className: "grid flex-1 text-left text-sm leading-tight group-data-[collapsible=icon]:hidden", children: [
1893
+ /* @__PURE__ */ jsx15("span", { className: "truncate font-medium", children: displayName }),
1894
+ user?.email && /* @__PURE__ */ jsx15("span", { className: "truncate text-xs text-muted-foreground", children: user.email })
1895
+ ] }),
1896
+ /* @__PURE__ */ jsx15(ChevronsUpDown, { className: "ml-auto size-4 group-data-[collapsible=icon]:hidden" })
1897
+ ]
1898
+ }
1899
+ ) }),
1900
+ /* @__PURE__ */ jsxs8(
1901
+ DropdownMenuContent,
1902
+ {
1903
+ className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg",
1904
+ side: isMobile ? "bottom" : "right",
1905
+ align: "end",
1906
+ sideOffset: 4,
1907
+ children: [
1908
+ /* @__PURE__ */ jsx15(DropdownMenuLabel, { className: "p-0 font-normal", children: /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 px-1 py-1.5 text-left text-sm", children: [
1909
+ /* @__PURE__ */ jsxs8(Avatar, { className: "h-8 w-8 rounded-lg", children: [
1910
+ user?.avatar && /* @__PURE__ */ jsx15(AvatarImage, { src: user.avatar, alt: displayName }),
1911
+ /* @__PURE__ */ jsx15(AvatarFallback, { className: "rounded-lg bg-primary/10 text-primary text-xs font-medium", children: initials })
1912
+ ] }),
1913
+ /* @__PURE__ */ jsxs8("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
1914
+ /* @__PURE__ */ jsx15("span", { className: "truncate font-medium", children: displayName }),
1915
+ user?.email && /* @__PURE__ */ jsx15("span", { className: "truncate text-xs text-muted-foreground", children: user.email })
1916
+ ] })
1917
+ ] }) }),
1918
+ /* @__PURE__ */ jsx15(DropdownMenuSeparator, {}),
1919
+ callbacks?.onViewProfile && /* @__PURE__ */ jsxs8(DropdownMenuItem, { onClick: callbacks.onViewProfile, children: [
1920
+ /* @__PURE__ */ jsx15(User, { className: "mr-2 h-4 w-4" }),
1921
+ /* @__PURE__ */ jsx15("span", { children: labels.profile })
1922
+ ] }),
1923
+ callbacks?.onOpenSettings && /* @__PURE__ */ jsxs8(DropdownMenuItem, { onClick: callbacks.onOpenSettings, children: [
1924
+ /* @__PURE__ */ jsx15(Settings, { className: "mr-2 h-4 w-4" }),
1925
+ /* @__PURE__ */ jsx15("span", { children: labels.settings })
1926
+ ] }),
1927
+ additionalItems,
1928
+ showThemeOptions && callbacks?.onThemeChange && /* @__PURE__ */ jsxs8(Fragment2, { children: [
1929
+ /* @__PURE__ */ jsx15(DropdownMenuSeparator, {}),
1930
+ /* @__PURE__ */ jsxs8(
1931
+ DropdownMenuItem,
1932
+ {
1933
+ onClick: () => callbacks.onThemeChange?.("light"),
1934
+ className: currentTheme === "light" ? "bg-accent" : "",
1935
+ children: [
1936
+ /* @__PURE__ */ jsx15(Sun, { className: "mr-2 h-4 w-4" }),
1937
+ /* @__PURE__ */ jsx15("span", { children: labels.lightMode })
1938
+ ]
1939
+ }
1940
+ ),
1941
+ /* @__PURE__ */ jsxs8(
1942
+ DropdownMenuItem,
1943
+ {
1944
+ onClick: () => callbacks.onThemeChange?.("dark"),
1945
+ className: currentTheme === "dark" ? "bg-accent" : "",
1946
+ children: [
1947
+ /* @__PURE__ */ jsx15(Moon, { className: "mr-2 h-4 w-4" }),
1948
+ /* @__PURE__ */ jsx15("span", { children: labels.darkMode })
1949
+ ]
1950
+ }
1951
+ ),
1952
+ /* @__PURE__ */ jsxs8(
1953
+ DropdownMenuItem,
1954
+ {
1955
+ onClick: () => callbacks.onThemeChange?.("system"),
1956
+ className: currentTheme === "system" ? "bg-accent" : "",
1957
+ children: [
1958
+ /* @__PURE__ */ jsx15(Palette, { className: "mr-2 h-4 w-4" }),
1959
+ /* @__PURE__ */ jsx15("span", { children: labels.systemTheme })
1960
+ ]
1961
+ }
1962
+ )
1963
+ ] }),
1964
+ callbacks?.onLogout && /* @__PURE__ */ jsxs8(Fragment2, { children: [
1965
+ /* @__PURE__ */ jsx15(DropdownMenuSeparator, {}),
1966
+ /* @__PURE__ */ jsxs8(
1967
+ DropdownMenuItem,
1968
+ {
1969
+ onClick: callbacks.onLogout,
1970
+ className: "text-destructive focus:text-destructive focus:bg-destructive/10",
1971
+ children: [
1972
+ /* @__PURE__ */ jsx15(LogOut, { className: "mr-2 h-4 w-4" }),
1973
+ /* @__PURE__ */ jsx15("span", { children: labels.logout })
1974
+ ]
1975
+ }
1976
+ )
1977
+ ] })
1978
+ ]
1979
+ }
1980
+ )
1981
+ ] }) }) });
1982
+ };
1983
+
1984
+ // src/components/chat/Sidebar.tsx
1985
+ import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
1986
+ var CreateThreadDialog = ({ config, onCreateThread, trigger }) => {
1987
+ const [title, setTitle] = useState4("");
1988
+ const [isOpen, setIsOpen] = useState4(false);
1989
+ const handleCreate = () => {
1990
+ onCreateThread(title.trim() || void 0);
1991
+ setTitle("");
1992
+ setIsOpen(false);
1993
+ };
1994
+ return /* @__PURE__ */ jsxs9(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: [
1995
+ /* @__PURE__ */ jsx16(DialogTrigger, { asChild: true, children: trigger || /* @__PURE__ */ jsxs9(Button, { className: "w-full justify-start", variant: "outline", children: [
1996
+ /* @__PURE__ */ jsx16(Plus, { className: "mr-2 h-4 w-4" }),
1997
+ config.labels?.newChat || "New Chat"
1998
+ ] }) }),
1999
+ /* @__PURE__ */ jsxs9(DialogContent, { children: [
2000
+ /* @__PURE__ */ jsxs9(DialogHeader, { children: [
2001
+ /* @__PURE__ */ jsx16(DialogTitle, { children: config.labels?.createNewThread || "New Conversation" }),
2002
+ /* @__PURE__ */ jsx16(DialogDescription, { children: "Give your new conversation a name or leave blank to auto-generate one." })
2003
+ ] }),
2004
+ /* @__PURE__ */ jsx16(
2005
+ Input,
2006
+ {
2007
+ value: title,
2008
+ onChange: (e) => setTitle(e.target.value),
2009
+ placeholder: config.labels?.threadNamePlaceholder || "Conversation name",
2010
+ onKeyDown: (e) => e.key === "Enter" && handleCreate(),
2011
+ autoFocus: true
2012
+ }
2013
+ ),
2014
+ /* @__PURE__ */ jsxs9(DialogFooter, { children: [
2015
+ /* @__PURE__ */ jsx16(Button, { variant: "outline", onClick: () => setIsOpen(false), children: config.labels?.cancel || "Cancel" }),
2016
+ /* @__PURE__ */ jsx16(Button, { onClick: handleCreate, children: config.labels?.create || "Create" })
2017
+ ] })
2018
+ ] })
2019
+ ] });
2020
+ };
2021
+ var ThreadInitialsIcon = ({ title }) => {
2022
+ const initials = title?.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase() || "?";
2023
+ return /* @__PURE__ */ jsx16("div", { className: "flex shrink-0 items-center justify-center rounded bg-muted text-[10px] font-medium", children: initials });
2024
+ };
2025
+ var Sidebar2 = ({
2026
+ threads,
2027
+ currentThreadId,
2028
+ config,
2029
+ onCreateThread,
2030
+ onSelectThread,
2031
+ onRenameThread,
2032
+ onDeleteThread,
2033
+ onArchiveThread,
2034
+ // User menu props
2035
+ user,
2036
+ userMenuCallbacks,
2037
+ currentTheme,
2038
+ showThemeOptions = true,
2039
+ userMenuAdditionalItems,
2040
+ ...props
2041
+ }) => {
2042
+ const [searchQuery, setSearchQuery] = useState4("");
2043
+ const [showArchived, setShowArchived] = useState4(false);
2044
+ const [deleteThreadId, setDeleteThreadId] = useState4(null);
2045
+ const [editingThreadId, setEditingThreadId] = useState4(null);
2046
+ const [editTitle, setEditTitle] = useState4("");
2047
+ const inputRef = useRef2(null);
2048
+ const { setOpen } = useSidebar();
2049
+ useEffect4(() => {
2050
+ if (editingThreadId && inputRef.current) {
2051
+ inputRef.current.focus();
2052
+ inputRef.current.select();
2053
+ }
2054
+ }, [editingThreadId]);
2055
+ const filteredThreads = threads.filter((thread) => {
2056
+ const title = (thread.title ?? "").toString();
2057
+ const matchesSearch = title.toLowerCase().includes(searchQuery.toLowerCase());
2058
+ const matchesArchiveFilter = showArchived || !thread.isArchived;
2059
+ return matchesSearch && matchesArchiveFilter;
2060
+ });
2061
+ const groupedThreads = filteredThreads.reduce((groups, thread) => {
2062
+ const date = new Date(thread.updatedAt);
2063
+ const today = /* @__PURE__ */ new Date();
2064
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
2065
+ let groupKey;
2066
+ if (date.toDateString() === today.toDateString()) {
2067
+ groupKey = config.labels?.today || "Today";
2068
+ } else if (date.toDateString() === yesterday.toDateString()) {
2069
+ groupKey = config.labels?.yesterday || "Yesterday";
2070
+ } else {
2071
+ groupKey = date.toLocaleDateString("en-US", {
2072
+ weekday: "long",
2073
+ day: "2-digit",
2074
+ month: "long"
2075
+ });
2076
+ }
2077
+ if (!groups[groupKey]) {
2078
+ groups[groupKey] = [];
2079
+ }
2080
+ groups[groupKey].push(thread);
2081
+ return groups;
2082
+ }, {});
2083
+ const handleDeleteThread = (threadId) => {
2084
+ onDeleteThread?.(threadId);
2085
+ setDeleteThreadId(null);
2086
+ };
2087
+ const startEditing = (thread) => {
2088
+ setEditingThreadId(thread.id);
2089
+ setEditTitle(thread.title || "");
2090
+ };
2091
+ const saveEdit = () => {
2092
+ if (editingThreadId && editTitle.trim()) {
2093
+ onRenameThread?.(editingThreadId, editTitle.trim());
2094
+ }
2095
+ setEditingThreadId(null);
2096
+ };
2097
+ const cancelEdit = () => {
2098
+ setEditingThreadId(null);
2099
+ };
2100
+ return /* @__PURE__ */ jsxs9(Sidebar, { collapsible: "icon", ...props, children: [
2101
+ /* @__PURE__ */ jsxs9(SidebarHeader, { children: [
2102
+ onCreateThread && /* @__PURE__ */ jsx16(
2103
+ CreateThreadDialog,
2104
+ {
2105
+ config,
2106
+ onCreateThread,
2107
+ trigger: /* @__PURE__ */ jsx16(SidebarMenu, { children: /* @__PURE__ */ jsx16(SidebarMenuItem, { children: /* @__PURE__ */ jsxs9(
2108
+ SidebarMenuButton,
2109
+ {
2110
+ size: "lg",
2111
+ className: "w-full justify-start gap-2 border border-sidebar-border shadow-sm hover:bg-sidebar-accent hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:justify-center",
2112
+ tooltip: config.labels?.newChat || "New Chat",
2113
+ children: [
2114
+ /* @__PURE__ */ jsx16(Plus, { className: "size-4" }),
2115
+ /* @__PURE__ */ jsx16("span", { className: "group-data-[collapsible=icon]:hidden", children: config.labels?.newChat || "New Chat" })
2116
+ ]
2117
+ }
2118
+ ) }) })
2119
+ }
2120
+ ),
2121
+ /* @__PURE__ */ jsxs9("div", { className: "px-2 py-1 mt-6", children: [
2122
+ /* @__PURE__ */ jsxs9("div", { className: "relative group-data-[collapsible=icon]:hidden", children: [
2123
+ /* @__PURE__ */ jsx16(Search, { className: "pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 select-none opacity-50" }),
2124
+ /* @__PURE__ */ jsx16(
2125
+ Input,
2126
+ {
2127
+ className: "pl-8 h-8 bg-sidebar-accent/50 border-sidebar-border focus-visible:ring-1 focus-visible:ring-sidebar-ring",
2128
+ placeholder: config.labels?.search || "Search...",
2129
+ value: searchQuery,
2130
+ onChange: (e) => setSearchQuery(e.target.value)
2131
+ }
2132
+ )
2133
+ ] }),
2134
+ /* @__PURE__ */ jsx16("div", { className: "hidden group-data-[collapsible=icon]:flex justify-center", children: /* @__PURE__ */ jsx16(
2135
+ Button,
2136
+ {
2137
+ variant: "ghost",
2138
+ size: "icon",
2139
+ className: "h-7 w-7",
2140
+ onClick: () => setOpen(true),
2141
+ title: config.labels?.search || "Search",
2142
+ children: /* @__PURE__ */ jsx16(Search, { className: "h-4 w-4" })
2143
+ }
2144
+ ) })
2145
+ ] })
2146
+ ] }),
2147
+ /* @__PURE__ */ jsxs9(SidebarContent, { children: [
2148
+ threads.some((t) => t.isArchived) && /* @__PURE__ */ jsx16("div", { className: "px-4 py-2 mt-2 group-data-[collapsible=icon]:hidden", children: /* @__PURE__ */ jsxs9(
2149
+ Button,
2150
+ {
2151
+ variant: "ghost",
2152
+ size: "sm",
2153
+ onClick: () => setShowArchived(!showArchived),
2154
+ className: "h-6 text-xs w-full justify-start text-muted-foreground",
2155
+ children: [
2156
+ /* @__PURE__ */ jsx16(Filter, { className: "mr-2 h-3 w-3" }),
2157
+ showArchived ? config.labels?.hideArchived || "Hide Archived" : config.labels?.showArchived || "Show Archived"
2158
+ ]
2159
+ }
2160
+ ) }),
2161
+ Object.keys(groupedThreads).length === 0 ? /* @__PURE__ */ jsxs9("div", { className: "px-4 py-8 text-center text-muted-foreground group-data-[collapsible=icon]:hidden", children: [
2162
+ /* @__PURE__ */ jsx16("div", { className: "mx-auto h-8 w-8 mb-2 flex items-center justify-center rounded-full bg-muted/50", children: /* @__PURE__ */ jsx16(Plus, { className: "h-4 w-4 opacity-50" }) }),
2163
+ /* @__PURE__ */ jsx16("p", { className: "text-xs", children: searchQuery ? config.labels?.noThreadsFound || "No conversations found" : config.labels?.noThreadsYet || "No conversations yet" })
2164
+ ] }) : Object.entries(groupedThreads).map(([group, groupThreads]) => /* @__PURE__ */ jsxs9(SidebarGroup, { className: "mt-2", children: [
2165
+ /* @__PURE__ */ jsx16(SidebarGroupLabel, { className: "group-data-[collapsible=icon]:hidden", children: group }),
2166
+ /* @__PURE__ */ jsx16(SidebarGroupContent, { children: /* @__PURE__ */ jsx16(SidebarMenu, { children: groupThreads.map((thread) => /* @__PURE__ */ jsxs9(SidebarMenuItem, { children: [
2167
+ editingThreadId === thread.id ? /* @__PURE__ */ jsx16("div", { className: "flex items-center gap-1 px-2 py-1", children: /* @__PURE__ */ jsx16(
2168
+ Input,
2169
+ {
2170
+ ref: inputRef,
2171
+ value: editTitle,
2172
+ onChange: (e) => setEditTitle(e.target.value),
2173
+ onKeyDown: (e) => {
2174
+ if (e.key === "Enter") saveEdit();
2175
+ if (e.key === "Escape") cancelEdit();
2176
+ },
2177
+ onBlur: saveEdit,
2178
+ className: "h-7 text-sm"
2179
+ }
2180
+ ) }) : /* @__PURE__ */ jsxs9(
2181
+ SidebarMenuButton,
2182
+ {
2183
+ isActive: currentThreadId === thread.id,
2184
+ onClick: () => onSelectThread?.(thread.id),
2185
+ tooltip: thread.title,
2186
+ children: [
2187
+ /* @__PURE__ */ jsx16(ThreadInitialsIcon, { title: thread.title || "?" }),
2188
+ /* @__PURE__ */ jsx16("div", { className: "flex flex-col items-start gap-0.5 flex-1 min-w-0 group-data-[collapsible=icon]:hidden", children: /* @__PURE__ */ jsx16("span", { className: "truncate w-full", children: thread.title || "New Chat" }) }),
2189
+ thread.isArchived && /* @__PURE__ */ jsx16(Archive, { className: "ml-auto h-3 w-3 opacity-50 group-data-[collapsible=icon]:hidden" })
2190
+ ]
2191
+ }
2192
+ ),
2193
+ !editingThreadId && /* @__PURE__ */ jsxs9(DropdownMenu, { children: [
2194
+ /* @__PURE__ */ jsx16(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs9(SidebarMenuAction, { showOnHover: true, children: [
2195
+ /* @__PURE__ */ jsx16(MoreHorizontal, {}),
2196
+ /* @__PURE__ */ jsx16("span", { className: "sr-only", children: "More" })
2197
+ ] }) }),
2198
+ /* @__PURE__ */ jsxs9(DropdownMenuContent, { className: "w-48", side: "right", align: "start", children: [
2199
+ /* @__PURE__ */ jsxs9(DropdownMenuItem, { onClick: () => startEditing(thread), children: [
2200
+ /* @__PURE__ */ jsx16(Edit2, { className: "mr-2 h-4 w-4" }),
2201
+ /* @__PURE__ */ jsx16("span", { children: config.labels?.renameThread || "Rename" })
2202
+ ] }),
2203
+ /* @__PURE__ */ jsxs9(DropdownMenuItem, { onClick: () => onArchiveThread?.(thread.id), children: [
2204
+ /* @__PURE__ */ jsx16(Archive, { className: "mr-2 h-4 w-4" }),
2205
+ /* @__PURE__ */ jsx16("span", { children: thread.isArchived ? config.labels?.unarchiveThread || "Unarchive" : config.labels?.archiveThread || "Archive" })
2206
+ ] }),
2207
+ /* @__PURE__ */ jsx16(DropdownMenuSeparator, {}),
2208
+ /* @__PURE__ */ jsxs9(
2209
+ DropdownMenuItem,
2210
+ {
2211
+ onClick: () => setDeleteThreadId(thread.id),
2212
+ className: "text-destructive focus:text-destructive",
2213
+ children: [
2214
+ /* @__PURE__ */ jsx16(Trash2, { className: "mr-2 h-4 w-4" }),
2215
+ /* @__PURE__ */ jsx16("span", { children: config.labels?.deleteThread || "Delete" })
2216
+ ]
2217
+ }
2218
+ )
2219
+ ] })
2220
+ ] })
2221
+ ] }, thread.id)) }) })
2222
+ ] }, group))
2223
+ ] }),
2224
+ /* @__PURE__ */ jsx16(SidebarFooter, { children: /* @__PURE__ */ jsx16(
2225
+ UserMenu,
2226
+ {
2227
+ user,
2228
+ config: config.userMenu,
2229
+ callbacks: userMenuCallbacks,
2230
+ currentTheme,
2231
+ showThemeOptions,
2232
+ additionalItems: userMenuAdditionalItems
2233
+ }
2234
+ ) }),
2235
+ /* @__PURE__ */ jsx16(SidebarRail, {}),
2236
+ /* @__PURE__ */ jsx16(AlertDialog, { open: !!deleteThreadId, onOpenChange: () => setDeleteThreadId(null), children: /* @__PURE__ */ jsxs9(AlertDialogContent, { children: [
2237
+ /* @__PURE__ */ jsxs9(AlertDialogHeader, { children: [
2238
+ /* @__PURE__ */ jsx16(AlertDialogTitle, { children: config.labels?.deleteConfirmTitle || "Delete Conversation" }),
2239
+ /* @__PURE__ */ jsx16(AlertDialogDescription, { children: config.labels?.deleteConfirmDescription || "Are you sure you want to delete this conversation? This action cannot be undone." })
2240
+ ] }),
2241
+ /* @__PURE__ */ jsxs9(AlertDialogFooter, { children: [
2242
+ /* @__PURE__ */ jsx16(AlertDialogCancel, { children: config.labels?.cancel || "Cancel" }),
2243
+ /* @__PURE__ */ jsx16(
2244
+ AlertDialogAction,
2245
+ {
2246
+ onClick: () => deleteThreadId && handleDeleteThread(deleteThreadId),
2247
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
2248
+ children: config.labels?.deleteThread || "Delete"
2249
+ }
2250
+ )
2251
+ ] })
2252
+ ] }) })
2253
+ ] });
2254
+ };
2255
+
2256
+ // src/components/chat/ChatHeader.tsx
2257
+ import React5 from "react";
2258
+ import {
2259
+ Bot,
2260
+ MoreVertical,
2261
+ Download,
2262
+ Upload,
2263
+ Trash2 as Trash22,
2264
+ Plus as Plus2,
2265
+ Menu,
2266
+ Moon as Moon2,
2267
+ Sun as Sun2
2268
+ } from "lucide-react";
2269
+ import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
2270
+ var ChatHeader = ({
2271
+ config,
2272
+ currentThreadTitle,
2273
+ onSidebarToggle: _onSidebarToggle,
2274
+ onCustomComponentToggle,
2275
+ onNewThread,
2276
+ onExportData,
2277
+ onImportData,
2278
+ onClearAll,
2279
+ showCustomComponentButton,
2280
+ isMobile,
2281
+ className = ""
2282
+ }) => {
2283
+ const [isDarkMode, setIsDarkMode] = React5.useState(() => {
2284
+ if (typeof window === "undefined") return false;
2285
+ return document.documentElement.classList.contains("dark");
2286
+ });
2287
+ React5.useEffect(() => {
2288
+ const observer = new MutationObserver(() => {
2289
+ setIsDarkMode(document.documentElement.classList.contains("dark"));
2290
+ });
2291
+ observer.observe(document.documentElement, {
2292
+ attributes: true,
2293
+ attributeFilter: ["class"]
2294
+ });
2295
+ const mediaQuery = globalThis.matchMedia("(prefers-color-scheme: dark)");
2296
+ const handleSystemThemeChange = (e) => {
2297
+ const savedTheme = localStorage.getItem("theme");
2298
+ if (!savedTheme) {
2299
+ setIsDarkMode(e.matches);
2300
+ }
2301
+ };
2302
+ mediaQuery.addEventListener("change", handleSystemThemeChange);
2303
+ return () => {
2304
+ observer.disconnect();
2305
+ mediaQuery.removeEventListener("change", handleSystemThemeChange);
2306
+ };
2307
+ }, []);
2308
+ const toggleDarkMode = () => {
2309
+ const isDark = document.documentElement.classList.contains("dark");
2310
+ if (isDark) {
2311
+ document.documentElement.classList.remove("dark");
2312
+ localStorage.setItem("theme", "light");
2313
+ } else {
2314
+ document.documentElement.classList.add("dark");
2315
+ localStorage.setItem("theme", "dark");
2316
+ }
2317
+ setIsDarkMode(!isDark);
2318
+ };
2319
+ const handleImportClick = () => {
2320
+ const input = document.createElement("input");
2321
+ input.type = "file";
2322
+ input.accept = ".json";
2323
+ input.onchange = (e) => {
2324
+ const file = e.target.files?.[0];
2325
+ if (file && onImportData) {
2326
+ onImportData(file);
2327
+ }
2328
+ };
2329
+ input.click();
2330
+ };
2331
+ return /* @__PURE__ */ jsx17(
2332
+ Card,
2333
+ {
2334
+ "data-chat-header": true,
2335
+ className: `py-0 border-b rounded-none relative z-10 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 ${className}`,
2336
+ style: isMobile ? { paddingTop: "env(safe-area-inset-top)" } : void 0,
2337
+ children: /* @__PURE__ */ jsx17(CardHeader, { className: "p-2", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between", children: [
2338
+ /* @__PURE__ */ jsx17("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxs10(Tooltip, { children: [
2339
+ /* @__PURE__ */ jsx17(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx17(SidebarTrigger, { className: "-ml-1" }) }),
2340
+ /* @__PURE__ */ jsx17(TooltipContent, { children: config.labels?.sidebarToggle || "Toggle Sidebar" })
2341
+ ] }) }),
2342
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-3 flex-1 justify-center", children: [
2343
+ config.branding?.logo || /* @__PURE__ */ jsx17(Avatar, { className: "h-8 w-8", children: /* @__PURE__ */ jsx17(AvatarFallback, { children: /* @__PURE__ */ jsx17(Bot, { className: "h-4 w-4" }) }) }),
2344
+ /* @__PURE__ */ jsx17("div", { className: "text-center hidden md:block", children: /* @__PURE__ */ jsx17(CardTitle, { className: "text-sm font-medium", children: config.branding?.title || "Chat Assistant" }) }),
2345
+ /* @__PURE__ */ jsx17("div", { className: "md:hidden text-sm font-medium truncate max-w-[150px]", children: currentThreadTitle || config.branding?.title || "Chat" })
2346
+ ] }),
2347
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1", children: [
2348
+ showCustomComponentButton && config.customComponent && /* @__PURE__ */ jsxs10(Tooltip, { children: [
2349
+ /* @__PURE__ */ jsx17(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx17(
2350
+ Button,
2351
+ {
2352
+ variant: "ghost",
2353
+ size: "icon",
2354
+ className: "h-8 w-8",
2355
+ onClick: onCustomComponentToggle,
2356
+ children: config.customComponent.icon || /* @__PURE__ */ jsx17(Menu, { className: "h-4 w-4" })
2357
+ }
2358
+ ) }),
2359
+ /* @__PURE__ */ jsx17(TooltipContent, { children: config.customComponent.label || config.labels?.customComponentToggle || "Toggle" })
2360
+ ] }),
2361
+ config.headerActions,
2362
+ /* @__PURE__ */ jsxs10(DropdownMenu, { children: [
2363
+ /* @__PURE__ */ jsx17(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx17(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", children: /* @__PURE__ */ jsx17(MoreVertical, { className: "h-4 w-4" }) }) }),
2364
+ /* @__PURE__ */ jsxs10(DropdownMenuContent, { align: "end", children: [
2365
+ onNewThread && /* @__PURE__ */ jsxs10(Fragment3, { children: [
2366
+ /* @__PURE__ */ jsxs10(DropdownMenuItem, { onClick: () => onNewThread?.(), className: "font-medium text-primary", children: [
2367
+ /* @__PURE__ */ jsx17(Plus2, { className: "h-4 w-4 mr-2" }),
2368
+ config.labels?.newThread || "New Thread"
2369
+ ] }),
2370
+ /* @__PURE__ */ jsx17(DropdownMenuSeparator, {})
2371
+ ] }),
2372
+ onExportData && /* @__PURE__ */ jsxs10(DropdownMenuItem, { onClick: onExportData, children: [
2373
+ /* @__PURE__ */ jsx17(Download, { className: "h-4 w-4 mr-2" }),
2374
+ config.labels?.exportData || "Export Data"
2375
+ ] }),
2376
+ onImportData && /* @__PURE__ */ jsxs10(DropdownMenuItem, { onClick: handleImportClick, children: [
2377
+ /* @__PURE__ */ jsx17(Upload, { className: "h-4 w-4 mr-2" }),
2378
+ config.labels?.importData || "Import Data"
2379
+ ] }),
2380
+ (onExportData || onImportData) && /* @__PURE__ */ jsx17(DropdownMenuSeparator, {}),
2381
+ /* @__PURE__ */ jsx17(DropdownMenuItem, { onClick: toggleDarkMode, children: isDarkMode ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
2382
+ /* @__PURE__ */ jsx17(Sun2, { className: "h-4 w-4 mr-2" }),
2383
+ config.labels?.lightMode || "Light Mode"
2384
+ ] }) : /* @__PURE__ */ jsxs10(Fragment3, { children: [
2385
+ /* @__PURE__ */ jsx17(Moon2, { className: "h-4 w-4 mr-2" }),
2386
+ config.labels?.darkMode || "Dark Mode"
2387
+ ] }) }),
2388
+ onClearAll && /* @__PURE__ */ jsxs10(Fragment3, { children: [
2389
+ /* @__PURE__ */ jsx17(DropdownMenuSeparator, {}),
2390
+ /* @__PURE__ */ jsxs10(
2391
+ DropdownMenuItem,
2392
+ {
2393
+ onClick: onClearAll,
2394
+ className: "text-destructive",
2395
+ children: [
2396
+ /* @__PURE__ */ jsx17(Trash22, { className: "h-4 w-4 mr-2" }),
2397
+ config.labels?.clearAll || "Clear All"
2398
+ ]
2399
+ }
2400
+ )
2401
+ ] })
2402
+ ] })
2403
+ ] })
2404
+ ] })
2405
+ ] }) })
2406
+ }
2407
+ );
2408
+ };
2409
+
2410
+ // src/components/chat/ChatInput.tsx
2411
+ import { useState as useState6, useRef as useRef3, useCallback as useCallback3, useEffect as useEffect6 } from "react";
2412
+
2413
+ // src/components/chat/UserContext.tsx
2414
+ import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo2, useState as useState5 } from "react";
2415
+ import { jsx as jsx18 } from "react/jsx-runtime";
2416
+ var Ctx = createContext2(void 0);
2417
+ var ChatUserContextProvider = ({ children, initial }) => {
2418
+ const [ctx, setCtx] = useState5(() => ({
2419
+ updatedAt: Date.now(),
2420
+ ...initial ?? {}
2421
+ }));
2422
+ useEffect5(() => {
2423
+ if (!initial) return;
2424
+ setCtx((prev) => ({
2425
+ ...prev,
2426
+ ...initial,
2427
+ updatedAt: Date.now()
2428
+ }));
2429
+ }, [initial]);
2430
+ const setPartial = useCallback2((next) => {
2431
+ setCtx((prev) => {
2432
+ const partial = typeof next === "function" ? next(prev) : next;
2433
+ return { ...prev, ...partial, updatedAt: Date.now() };
2434
+ });
2435
+ }, []);
2436
+ const value = useMemo2(() => ({
2437
+ context: ctx,
2438
+ setContext: setPartial,
2439
+ resetContext: () => setCtx({ updatedAt: Date.now() })
2440
+ }), [ctx, setPartial]);
2441
+ return /* @__PURE__ */ jsx18(Ctx.Provider, { value, children });
2442
+ };
2443
+ function useChatUserContext() {
2444
+ const v = useContext2(Ctx);
2445
+ if (!v) throw new Error("useChatUserContext must be used within ChatUserContextProvider");
2446
+ return v;
2447
+ }
2448
+
2449
+ // src/components/ui/progress.tsx
2450
+ import * as ProgressPrimitive from "@radix-ui/react-progress";
2451
+ import { jsx as jsx19 } from "react/jsx-runtime";
2452
+ function Progress({
2453
+ className,
2454
+ value,
2455
+ ...props
2456
+ }) {
2457
+ return /* @__PURE__ */ jsx19(
2458
+ ProgressPrimitive.Root,
2459
+ {
2460
+ "data-slot": "progress",
2461
+ className: cn(
2462
+ "bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
2463
+ className
2464
+ ),
2465
+ ...props,
2466
+ children: /* @__PURE__ */ jsx19(
2467
+ ProgressPrimitive.Indicator,
2468
+ {
2469
+ "data-slot": "progress-indicator",
2470
+ className: "bg-primary h-full w-full flex-1 transition-all",
2471
+ style: { transform: `translateX(-${100 - (value || 0)}%)` }
2472
+ }
2473
+ )
2474
+ }
2475
+ );
2476
+ }
2477
+
2478
+ // src/components/chat/ChatInput.tsx
2479
+ import {
2480
+ Send,
2481
+ Paperclip,
2482
+ Mic,
2483
+ Image as Image2,
2484
+ Video,
2485
+ FileText,
2486
+ X as X2,
2487
+ Square,
2488
+ Play as Play2,
2489
+ Pause as Pause2,
2490
+ Loader2
2491
+ } from "lucide-react";
2492
+ import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
2493
+ var FileUploadItem = ({ file, progress, onCancel }) => {
2494
+ const guessTypeFromName = (name) => {
2495
+ const ext = (name || "").split(".").pop()?.toLowerCase();
2496
+ switch (ext) {
2497
+ case "jpg":
2498
+ case "jpeg":
2499
+ case "png":
2500
+ case "gif":
2501
+ case "webp":
2502
+ case "bmp":
2503
+ case "svg":
2504
+ return "image/*";
2505
+ case "mp4":
2506
+ case "mov":
2507
+ case "m4v":
2508
+ case "webm":
2509
+ return "video/*";
2510
+ case "mp3":
2511
+ case "wav":
2512
+ case "m4a":
2513
+ case "ogg":
2514
+ return "audio/*";
2515
+ default:
2516
+ return "";
2517
+ }
2518
+ };
2519
+ const getFileIcon = (type, name) => {
2520
+ const t = typeof type === "string" && type.length > 0 ? type : guessTypeFromName(name);
2521
+ if (t.startsWith("image/")) return /* @__PURE__ */ jsx20(Image2, { className: "h-4 w-4" });
2522
+ if (t.startsWith("video/")) return /* @__PURE__ */ jsx20(Video, { className: "h-4 w-4" });
2523
+ if (t.startsWith("audio/")) return /* @__PURE__ */ jsx20(Mic, { className: "h-4 w-4" });
2524
+ return /* @__PURE__ */ jsx20(FileText, { className: "h-4 w-4" });
2525
+ };
2526
+ const formatFileSize = (bytes) => {
2527
+ if (bytes === 0) return "0 Bytes";
2528
+ const k = 1024;
2529
+ const sizes = ["Bytes", "KB", "MB", "GB"];
2530
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2531
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
2532
+ };
2533
+ return /* @__PURE__ */ jsx20(Card, { className: "relative", children: /* @__PURE__ */ jsx20(CardContent, { className: "p-3", children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-3", children: [
2534
+ getFileIcon(file.type, file.name),
2535
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 min-w-0", children: [
2536
+ /* @__PURE__ */ jsx20("p", { className: "text-sm font-medium truncate", children: file.name }),
2537
+ /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: formatFileSize(file.size ?? 0) }),
2538
+ /* @__PURE__ */ jsx20(Progress, { value: progress, className: "h-1 mt-1" })
2539
+ ] }),
2540
+ /* @__PURE__ */ jsx20(
2541
+ Button,
2542
+ {
2543
+ variant: "ghost",
2544
+ size: "icon",
2545
+ className: "h-6 w-6",
2546
+ onClick: onCancel,
2547
+ children: /* @__PURE__ */ jsx20(X2, { className: "h-3 w-3" })
2548
+ }
2549
+ )
2550
+ ] }) }) });
2551
+ };
2552
+ var AttachmentPreview = ({ attachment, onRemove }) => {
2553
+ const [isPlaying, setIsPlaying] = useState6(false);
2554
+ const audioRef = useRef3(null);
2555
+ const handlePlayPause = () => {
2556
+ if (audioRef.current) {
2557
+ if (isPlaying) {
2558
+ audioRef.current.pause();
2559
+ } else {
2560
+ audioRef.current.play();
2561
+ }
2562
+ setIsPlaying(!isPlaying);
2563
+ }
2564
+ };
2565
+ const formatDuration = (ms) => {
2566
+ if (!ms) return "";
2567
+ const seconds = Math.floor(ms / 1e3);
2568
+ const minutes = Math.floor(seconds / 60);
2569
+ return `${minutes}:${(seconds % 60).toString().padStart(2, "0")}`;
2570
+ };
2571
+ return /* @__PURE__ */ jsx20(Card, { className: "relative group", children: /* @__PURE__ */ jsxs11(CardContent, { className: "p-2", children: [
2572
+ attachment.kind === "image" && /* @__PURE__ */ jsxs11("div", { className: "relative", children: [
2573
+ /* @__PURE__ */ jsx20(
2574
+ "img",
2575
+ {
2576
+ src: attachment.dataUrl,
2577
+ alt: attachment.fileName || "Anexo",
2578
+ className: "w-full h-20 object-cover rounded"
2579
+ }
2580
+ ),
2581
+ /* @__PURE__ */ jsx20("div", { className: "absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity rounded flex items-center justify-center", children: /* @__PURE__ */ jsx20(
2582
+ Button,
2583
+ {
2584
+ variant: "destructive",
2585
+ size: "icon",
2586
+ className: "h-6 w-6",
2587
+ onClick: onRemove,
2588
+ children: /* @__PURE__ */ jsx20(X2, { className: "h-3 w-3" })
2589
+ }
2590
+ ) })
2591
+ ] }),
2592
+ attachment.kind === "video" && /* @__PURE__ */ jsxs11("div", { className: "relative", children: [
2593
+ /* @__PURE__ */ jsx20(
2594
+ "video",
2595
+ {
2596
+ src: attachment.dataUrl,
2597
+ poster: attachment.poster,
2598
+ className: "w-full h-20 object-cover rounded",
2599
+ muted: true
2600
+ }
2601
+ ),
2602
+ /* @__PURE__ */ jsx20("div", { className: "absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity rounded flex items-center justify-center", children: /* @__PURE__ */ jsx20(
2603
+ Button,
2604
+ {
2605
+ variant: "destructive",
2606
+ size: "icon",
2607
+ className: "h-6 w-6",
2608
+ onClick: onRemove,
2609
+ children: /* @__PURE__ */ jsx20(X2, { className: "h-3 w-3" })
2610
+ }
2611
+ ) }),
2612
+ /* @__PURE__ */ jsx20(Badge, { className: "absolute bottom-1 right-1 text-xs", children: formatDuration(attachment.durationMs) })
2613
+ ] }),
2614
+ attachment.kind === "audio" && /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 p-2", children: [
2615
+ /* @__PURE__ */ jsx20(
2616
+ Button,
2617
+ {
2618
+ variant: "outline",
2619
+ size: "icon",
2620
+ className: "h-8 w-8",
2621
+ onClick: handlePlayPause,
2622
+ children: isPlaying ? /* @__PURE__ */ jsx20(Pause2, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx20(Play2, { className: "h-3 w-3" })
2623
+ }
2624
+ ),
2625
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1", children: [
2626
+ /* @__PURE__ */ jsx20("p", { className: "text-xs font-medium", children: attachment.fileName || "\xC1udio" }),
2627
+ /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: formatDuration(attachment.durationMs) })
2628
+ ] }),
2629
+ /* @__PURE__ */ jsx20(
2630
+ "audio",
2631
+ {
2632
+ ref: audioRef,
2633
+ src: attachment.dataUrl,
2634
+ onPlay: () => setIsPlaying(true),
2635
+ onPause: () => setIsPlaying(false),
2636
+ onEnded: () => setIsPlaying(false)
2637
+ }
2638
+ ),
2639
+ /* @__PURE__ */ jsx20(
2640
+ Button,
2641
+ {
2642
+ variant: "ghost",
2643
+ size: "icon",
2644
+ className: "h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity",
2645
+ onClick: onRemove,
2646
+ children: /* @__PURE__ */ jsx20(X2, { className: "h-3 w-3" })
2647
+ }
2648
+ )
2649
+ ] }),
2650
+ attachment.fileName && attachment.kind !== "audio" && /* @__PURE__ */ jsx20("div", { className: "absolute bottom-0 left-0 right-0 bg-black/70 text-white text-xs p-1 rounded-b", children: /* @__PURE__ */ jsx20("p", { className: "truncate", children: attachment.fileName }) })
2651
+ ] }) });
2652
+ };
2653
+ var AudioRecorder = ({ isRecording, onStartRecording, onStopRecording, onCancel, recordingDuration, config }) => {
2654
+ const formatTime = (seconds) => {
2655
+ const mins = Math.floor(seconds / 60);
2656
+ const secs = seconds % 60;
2657
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
2658
+ };
2659
+ if (!isRecording) {
2660
+ return /* @__PURE__ */ jsxs11(Tooltip, { children: [
2661
+ /* @__PURE__ */ jsx20(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx20(
2662
+ Button,
2663
+ {
2664
+ variant: "outline",
2665
+ size: "icon",
2666
+ onClick: onStartRecording,
2667
+ className: "h-10 w-10",
2668
+ children: /* @__PURE__ */ jsx20(Mic, { className: "h-4 w-4" })
2669
+ }
2670
+ ) }),
2671
+ /* @__PURE__ */ jsx20(TooltipContent, { children: config?.labels?.recordAudioTooltip })
2672
+ ] });
2673
+ }
2674
+ return /* @__PURE__ */ jsx20(Card, { className: "border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950", children: /* @__PURE__ */ jsx20(CardContent, { className: "p-3", children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-3", children: [
2675
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
2676
+ /* @__PURE__ */ jsx20("div", { className: "h-3 w-3 bg-red-500 rounded-full animate-pulse" }),
2677
+ /* @__PURE__ */ jsx20("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: "Gravando" })
2678
+ ] }),
2679
+ /* @__PURE__ */ jsx20(Badge, { variant: "outline", className: "text-xs", children: formatTime(recordingDuration) }),
2680
+ /* @__PURE__ */ jsxs11("div", { className: "flex gap-1 ml-auto", children: [
2681
+ /* @__PURE__ */ jsxs11(
2682
+ Button,
2683
+ {
2684
+ variant: "outline",
2685
+ size: "sm",
2686
+ onClick: onCancel,
2687
+ children: [
2688
+ /* @__PURE__ */ jsx20(X2, { className: "h-3 w-3 mr-1" }),
2689
+ "Cancelar"
2690
+ ]
2691
+ }
2692
+ ),
2693
+ /* @__PURE__ */ jsxs11(
2694
+ Button,
2695
+ {
2696
+ variant: "default",
2697
+ size: "sm",
2698
+ onClick: onStopRecording,
2699
+ children: [
2700
+ /* @__PURE__ */ jsx20(Square, { className: "h-3 w-3 mr-1" }),
2701
+ "Parar"
2702
+ ]
2703
+ }
2704
+ )
2705
+ ] })
2706
+ ] }) }) });
2707
+ };
2708
+ var ChatInput = ({
2709
+ value,
2710
+ onChange,
2711
+ onSubmit,
2712
+ attachments,
2713
+ onAttachmentsChange,
2714
+ placeholder = "Digite sua mensagem...",
2715
+ disabled = false,
2716
+ isGenerating = false,
2717
+ onStopGeneration,
2718
+ enableFileUpload = true,
2719
+ enableAudioRecording = true,
2720
+ maxAttachments = 4,
2721
+ maxFileSize = 10 * 1024 * 1024,
2722
+ // 10MB
2723
+ acceptedFileTypes = ["image/*", "video/*", "audio/*"],
2724
+ className = "",
2725
+ config
2726
+ }) => {
2727
+ const [isRecording, setIsRecording] = useState6(false);
2728
+ const { setContext } = useChatUserContext();
2729
+ const [recordingDuration, setRecordingDuration] = useState6(0);
2730
+ const [uploadProgress, setUploadProgress] = useState6(/* @__PURE__ */ new Map());
2731
+ const textareaRef = useRef3(null);
2732
+ const fileInputRef = useRef3(null);
2733
+ const mediaRecorderRef = useRef3(null);
2734
+ const recordingStartTime = useRef3(0);
2735
+ const recordingInterval = useRef3(null);
2736
+ const mediaStreamRef = useRef3(null);
2737
+ useEffect6(() => {
2738
+ return () => {
2739
+ if (mediaStreamRef.current) {
2740
+ mediaStreamRef.current.getTracks().forEach((track) => track.stop());
2741
+ }
2742
+ if (recordingInterval.current) {
2743
+ clearInterval(recordingInterval.current);
2744
+ }
2745
+ };
2746
+ }, []);
2747
+ const handleSubmit = (e) => {
2748
+ e.preventDefault();
2749
+ if (!value.trim() && attachments.length === 0 || disabled || isGenerating) return;
2750
+ onSubmit(value.trim(), attachments);
2751
+ onChange("");
2752
+ onAttachmentsChange([]);
2753
+ };
2754
+ const handleKeyDown = (e) => {
2755
+ if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && window.innerWidth > 768) {
2756
+ e.preventDefault();
2757
+ handleSubmit(e);
2758
+ }
2759
+ };
2760
+ const processFile = async (file) => {
2761
+ if (file.size > maxFileSize) {
2762
+ alert(`Arquivo muito grande. M\xE1ximo permitido: ${Math.round(maxFileSize / 1024 / 1024)}MB`);
2763
+ return null;
2764
+ }
2765
+ const fileId = `${Date.now()}_${Math.random().toString(36).slice(2)}`;
2766
+ setUploadProgress((prev) => new Map(prev.set(fileId, {
2767
+ fileName: file.name,
2768
+ progress: 0,
2769
+ status: "uploading"
2770
+ })));
2771
+ try {
2772
+ for (let progress = 0; progress <= 100; progress += 20) {
2773
+ await new Promise((resolve) => setTimeout(resolve, 100));
2774
+ setUploadProgress((prev) => new Map(prev.set(fileId, {
2775
+ fileName: file.name,
2776
+ progress,
2777
+ status: "uploading"
2778
+ })));
2779
+ }
2780
+ const dataUrl = await new Promise((resolve, reject) => {
2781
+ const reader = new FileReader();
2782
+ reader.onload = () => resolve(reader.result);
2783
+ reader.onerror = reject;
2784
+ reader.readAsDataURL(file);
2785
+ });
2786
+ setUploadProgress((prev) => {
2787
+ const newMap = new Map(prev);
2788
+ newMap.delete(fileId);
2789
+ return newMap;
2790
+ });
2791
+ const attachment = {
2792
+ kind: file.type.startsWith("image/") ? "image" : file.type.startsWith("video/") ? "video" : file.type.startsWith("audio/") ? "audio" : "image",
2793
+ dataUrl,
2794
+ mimeType: file.type,
2795
+ fileName: file.name,
2796
+ size: file.size
2797
+ };
2798
+ if (attachment.kind === "video") {
2799
+ try {
2800
+ const video = document.createElement("video");
2801
+ video.src = dataUrl;
2802
+ await new Promise((resolve) => {
2803
+ video.onloadedmetadata = resolve;
2804
+ });
2805
+ attachment.durationMs = video.duration * 1e3;
2806
+ } catch (error) {
2807
+ console.warn("Could not get video duration:", error);
2808
+ }
2809
+ }
2810
+ if (attachment.kind === "image") {
2811
+ setContext({ lastReferenceImage: { dataUrl: attachment.dataUrl, mimeType: attachment.mimeType, addedAt: Date.now() } });
2812
+ }
2813
+ return attachment;
2814
+ } catch (error) {
2815
+ console.error("Error processing file:", error);
2816
+ setUploadProgress((prev) => {
2817
+ const newMap = new Map(prev);
2818
+ newMap.delete(fileId);
2819
+ return newMap;
2820
+ });
2821
+ alert("Erro ao processar arquivo");
2822
+ return null;
2823
+ }
2824
+ };
2825
+ const handleFileSelect = async (e) => {
2826
+ const files = e.target.files;
2827
+ if (!files) return;
2828
+ const remainingSlots = maxAttachments - attachments.length;
2829
+ const filesToProcess = Array.from(files).slice(0, remainingSlots);
2830
+ for (const file of filesToProcess) {
2831
+ const attachment = await processFile(file);
2832
+ if (attachment) {
2833
+ onAttachmentsChange([...attachments, attachment]);
2834
+ }
2835
+ }
2836
+ e.target.value = "";
2837
+ };
2838
+ const handleDrop = useCallback3(async (e) => {
2839
+ e.preventDefault();
2840
+ if (!enableFileUpload) return;
2841
+ const files = Array.from(e.dataTransfer.files);
2842
+ const remainingSlots = maxAttachments - attachments.length;
2843
+ const filesToProcess = files.slice(0, remainingSlots);
2844
+ for (const file of filesToProcess) {
2845
+ const attachment = await processFile(file);
2846
+ if (attachment) {
2847
+ onAttachmentsChange([...attachments, attachment]);
2848
+ }
2849
+ }
2850
+ }, [attachments, enableFileUpload, maxAttachments, onAttachmentsChange]);
2851
+ const handleDragOver = useCallback3((e) => {
2852
+ e.preventDefault();
2853
+ }, []);
2854
+ const startRecording = async () => {
2855
+ try {
2856
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
2857
+ mediaStreamRef.current = stream;
2858
+ const mediaRecorder = new MediaRecorder(stream);
2859
+ mediaRecorderRef.current = mediaRecorder;
2860
+ const chunks = [];
2861
+ mediaRecorder.ondataavailable = (e) => {
2862
+ chunks.push(e.data);
2863
+ };
2864
+ mediaRecorder.onstop = async () => {
2865
+ const blob = new Blob(chunks, { type: "audio/webm" });
2866
+ const dataUrl = await new Promise((resolve, reject) => {
2867
+ const reader = new FileReader();
2868
+ reader.onload = () => resolve(reader.result);
2869
+ reader.onerror = reject;
2870
+ reader.readAsDataURL(blob);
2871
+ });
2872
+ const attachment = {
2873
+ kind: "audio",
2874
+ dataUrl,
2875
+ mimeType: blob.type,
2876
+ durationMs: recordingDuration * 1e3,
2877
+ fileName: `audio_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19)}.webm`,
2878
+ size: blob.size
2879
+ };
2880
+ onAttachmentsChange([...attachments, attachment]);
2881
+ if (mediaStreamRef.current) {
2882
+ mediaStreamRef.current.getTracks().forEach((track) => track.stop());
2883
+ mediaStreamRef.current = null;
2884
+ }
2885
+ };
2886
+ recordingStartTime.current = Date.now();
2887
+ setRecordingDuration(0);
2888
+ setIsRecording(true);
2889
+ mediaRecorder.start();
2890
+ recordingInterval.current = setInterval(() => {
2891
+ const duration = Math.floor((Date.now() - recordingStartTime.current) / 1e3);
2892
+ setRecordingDuration(duration);
2893
+ }, 1e3);
2894
+ } catch (error) {
2895
+ console.error("Error starting recording:", error);
2896
+ alert("N\xE3o foi poss\xEDvel acessar o microfone");
2897
+ }
2898
+ };
2899
+ const stopRecording = () => {
2900
+ if (mediaRecorderRef.current && isRecording) {
2901
+ mediaRecorderRef.current.stop();
2902
+ setIsRecording(false);
2903
+ if (recordingInterval.current) {
2904
+ clearInterval(recordingInterval.current);
2905
+ }
2906
+ }
2907
+ };
2908
+ const cancelRecording = () => {
2909
+ if (mediaRecorderRef.current && isRecording) {
2910
+ mediaRecorderRef.current.stop();
2911
+ setIsRecording(false);
2912
+ if (recordingInterval.current) {
2913
+ clearInterval(recordingInterval.current);
2914
+ }
2915
+ if (mediaStreamRef.current) {
2916
+ mediaStreamRef.current.getTracks().forEach((track) => track.stop());
2917
+ mediaStreamRef.current = null;
2918
+ }
2919
+ }
2920
+ };
2921
+ const removeAttachment = (index) => {
2922
+ const newAttachments = attachments.filter((_, i) => i !== index);
2923
+ onAttachmentsChange(newAttachments);
2924
+ };
2925
+ const canAddMoreAttachments = attachments.length < maxAttachments;
2926
+ return /* @__PURE__ */ jsx20(TooltipProvider, { children: /* @__PURE__ */ jsx20("div", { className: `border-t py-0 bg-transparent ${className}`, children: /* @__PURE__ */ jsxs11("div", { className: "px-0 md:p-2 pb-1 space-y-4 bg-transparent", children: [
2927
+ uploadProgress.size > 0 && /* @__PURE__ */ jsx20("div", { className: "space-y-2", children: Array.from(uploadProgress.entries()).map(([id, progress]) => /* @__PURE__ */ jsx20(
2928
+ FileUploadItem,
2929
+ {
2930
+ file: { name: progress.fileName },
2931
+ progress: progress.progress,
2932
+ onCancel: () => {
2933
+ setUploadProgress((prev) => {
2934
+ const newMap = new Map(prev);
2935
+ newMap.delete(id);
2936
+ return newMap;
2937
+ });
2938
+ }
2939
+ },
2940
+ id
2941
+ )) }),
2942
+ isRecording && /* @__PURE__ */ jsx20(
2943
+ AudioRecorder,
2944
+ {
2945
+ isRecording,
2946
+ onStartRecording: startRecording,
2947
+ onStopRecording: stopRecording,
2948
+ onCancel: cancelRecording,
2949
+ recordingDuration,
2950
+ config
2951
+ }
2952
+ ),
2953
+ attachments.length > 0 && /* @__PURE__ */ jsx20("div", { className: "grid grid-cols-4 gap-2", children: attachments.map((attachment, index) => /* @__PURE__ */ jsx20(
2954
+ AttachmentPreview,
2955
+ {
2956
+ attachment,
2957
+ onRemove: () => removeAttachment(index)
2958
+ },
2959
+ index
2960
+ )) }),
2961
+ /* @__PURE__ */ jsx20("form", { onSubmit: handleSubmit, className: "mb-1 flex justify-center", children: /* @__PURE__ */ jsxs11(
2962
+ "div",
2963
+ {
2964
+ className: "flex items-end gap-2 p-3 border rounded-lg bg-background w-full md:min-w-3xl max-w-3xl",
2965
+ onDrop: handleDrop,
2966
+ onDragOver: handleDragOver,
2967
+ children: [
2968
+ enableFileUpload && canAddMoreAttachments && /* @__PURE__ */ jsxs11(Fragment4, { children: [
2969
+ /* @__PURE__ */ jsx20(
2970
+ "input",
2971
+ {
2972
+ ref: fileInputRef,
2973
+ type: "file",
2974
+ multiple: true,
2975
+ accept: acceptedFileTypes.join(","),
2976
+ onChange: handleFileSelect,
2977
+ className: "hidden"
2978
+ }
2979
+ ),
2980
+ /* @__PURE__ */ jsxs11(Tooltip, { children: [
2981
+ /* @__PURE__ */ jsx20(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx20(
2982
+ Button,
2983
+ {
2984
+ type: "button",
2985
+ variant: "outline",
2986
+ size: "icon",
2987
+ className: "h-10 w-10",
2988
+ onClick: (e) => {
2989
+ e.preventDefault();
2990
+ e.stopPropagation();
2991
+ fileInputRef.current?.click();
2992
+ },
2993
+ disabled,
2994
+ children: /* @__PURE__ */ jsx20(Paperclip, { className: "h-4 w-4" })
2995
+ }
2996
+ ) }),
2997
+ /* @__PURE__ */ jsx20(TooltipContent, { children: config?.labels?.attachFileTooltip })
2998
+ ] })
2999
+ ] }),
3000
+ /* @__PURE__ */ jsx20("div", { className: "flex-1", children: /* @__PURE__ */ jsx20(
3001
+ Textarea,
3002
+ {
3003
+ ref: textareaRef,
3004
+ value,
3005
+ onChange: (e) => onChange(e.target.value),
3006
+ onKeyDown: handleKeyDown,
3007
+ placeholder,
3008
+ disabled: disabled || isGenerating,
3009
+ className: "max-h-[120px] resize-none border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0",
3010
+ rows: 1
3011
+ }
3012
+ ) }),
3013
+ enableAudioRecording && !isRecording && canAddMoreAttachments && !value.trim() && /* @__PURE__ */ jsx20(
3014
+ AudioRecorder,
3015
+ {
3016
+ isRecording,
3017
+ onStartRecording: startRecording,
3018
+ onStopRecording: stopRecording,
3019
+ onCancel: cancelRecording,
3020
+ recordingDuration,
3021
+ config
3022
+ }
3023
+ ),
3024
+ isGenerating ? /* @__PURE__ */ jsxs11(Tooltip, { children: [
3025
+ /* @__PURE__ */ jsx20(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx20(
3026
+ Button,
3027
+ {
3028
+ type: "button",
3029
+ variant: "outline",
3030
+ size: "icon",
3031
+ className: "h-10 w-10",
3032
+ onClick: onStopGeneration,
3033
+ children: /* @__PURE__ */ jsx20(Square, { className: "h-4 w-4" })
3034
+ }
3035
+ ) }),
3036
+ /* @__PURE__ */ jsx20(TooltipContent, { children: config?.labels?.stopGenerationTooltip })
3037
+ ] }) : /* @__PURE__ */ jsxs11(Tooltip, { children: [
3038
+ /* @__PURE__ */ jsx20(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx20(
3039
+ Button,
3040
+ {
3041
+ type: "submit",
3042
+ size: "icon",
3043
+ className: "h-10 w-10",
3044
+ disabled: disabled || !value.trim() && attachments.length === 0,
3045
+ children: disabled ? /* @__PURE__ */ jsx20(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx20(Send, { className: "h-4 w-4" })
3046
+ }
3047
+ ) }),
3048
+ /* @__PURE__ */ jsx20(TooltipContent, { children: config?.labels?.sendMessageTooltip })
3049
+ ] })
3050
+ ]
3051
+ }
3052
+ ) }),
3053
+ /* @__PURE__ */ jsxs11("div", { className: "text-[10px] text-muted-foreground text-center", children: [
3054
+ window.innerWidth > 768 ? config?.labels?.inputHelpText : "",
3055
+ attachments.length > 0 && /* @__PURE__ */ jsxs11(Fragment4, { children: [
3056
+ " \u2022 ",
3057
+ attachments.length,
3058
+ "/",
3059
+ maxAttachments,
3060
+ " anexos"
3061
+ ] }),
3062
+ config?.labels?.footerLabel && /* @__PURE__ */ jsxs11(Fragment4, { children: [
3063
+ " \u2022 ",
3064
+ config.labels.footerLabel
3065
+ ] })
3066
+ ] })
3067
+ ] }) }) });
3068
+ };
3069
+
3070
+ // src/components/chat/UserProfile.tsx
3071
+ import { useState as useState7 } from "react";
3072
+
3073
+ // src/components/ui/scroll-area.tsx
3074
+ import * as React8 from "react";
3075
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
3076
+ import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
3077
+ var ScrollArea = React8.forwardRef(({ className, children, viewportClassName, onScroll, onScrollCapture, ...props }, ref) => {
3078
+ return /* @__PURE__ */ jsxs12(
3079
+ ScrollAreaPrimitive.Root,
3080
+ {
3081
+ "data-slot": "scroll-area",
3082
+ className: cn("relative", className),
3083
+ ...props,
3084
+ children: [
3085
+ /* @__PURE__ */ jsx21(
3086
+ ScrollAreaPrimitive.Viewport,
3087
+ {
3088
+ ref,
3089
+ "data-slot": "scroll-area-viewport",
3090
+ className: cn(
3091
+ "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1",
3092
+ viewportClassName
3093
+ ),
3094
+ onScroll,
3095
+ onScrollCapture,
3096
+ children
3097
+ }
3098
+ ),
3099
+ /* @__PURE__ */ jsx21(ScrollBar, {}),
3100
+ /* @__PURE__ */ jsx21(ScrollAreaPrimitive.Corner, {})
3101
+ ]
3102
+ }
3103
+ );
3104
+ });
3105
+ ScrollArea.displayName = "ScrollArea";
3106
+ function ScrollBar({
3107
+ className,
3108
+ orientation = "vertical",
3109
+ ...props
3110
+ }) {
3111
+ return /* @__PURE__ */ jsx21(
3112
+ ScrollAreaPrimitive.ScrollAreaScrollbar,
3113
+ {
3114
+ "data-slot": "scroll-area-scrollbar",
3115
+ orientation,
3116
+ className: cn(
3117
+ "flex touch-none p-px transition-colors select-none",
3118
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
3119
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
3120
+ className
3121
+ ),
3122
+ ...props,
3123
+ children: /* @__PURE__ */ jsx21(
3124
+ ScrollAreaPrimitive.ScrollAreaThumb,
3125
+ {
3126
+ "data-slot": "scroll-area-thumb",
3127
+ className: "bg-border relative flex-1 rounded-full"
3128
+ }
3129
+ )
3130
+ }
3131
+ );
3132
+ }
3133
+
3134
+ // src/components/chat/UserProfile.tsx
3135
+ import {
3136
+ User as User2,
3137
+ Mail,
3138
+ AtSign,
3139
+ Calendar,
3140
+ MapPin,
3141
+ Phone,
3142
+ Globe,
3143
+ Building,
3144
+ Briefcase,
3145
+ Users,
3146
+ UserPlus,
3147
+ Image as Image3,
3148
+ BadgeCheck,
3149
+ FileText as FileText2,
3150
+ Brain,
3151
+ Plus as Plus3,
3152
+ Trash2 as Trash23,
3153
+ Target,
3154
+ Lightbulb,
3155
+ Info,
3156
+ Heart,
3157
+ Bot as Bot2,
3158
+ Pencil,
3159
+ Check as Check2,
3160
+ X as X3
3161
+ } from "lucide-react";
3162
+ import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
3163
+ var getInitials2 = (name, email) => {
3164
+ if (name) {
3165
+ return name.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase();
3166
+ }
3167
+ if (email) {
3168
+ return email[0].toUpperCase();
3169
+ }
3170
+ return "U";
3171
+ };
3172
+ var getFieldIcon = (type, key) => {
3173
+ const iconClass = "h-4 w-4 text-muted-foreground";
3174
+ switch (type) {
3175
+ case "email":
3176
+ return /* @__PURE__ */ jsx22(Mail, { className: iconClass });
3177
+ case "phone":
3178
+ return /* @__PURE__ */ jsx22(Phone, { className: iconClass });
3179
+ case "url":
3180
+ return /* @__PURE__ */ jsx22(Globe, { className: iconClass });
3181
+ case "date":
3182
+ return /* @__PURE__ */ jsx22(Calendar, { className: iconClass });
3183
+ }
3184
+ const lowerKey = key?.toLowerCase() || "";
3185
+ if (lowerKey.includes("follower")) return /* @__PURE__ */ jsx22(Users, { className: iconClass });
3186
+ if (lowerKey.includes("following")) return /* @__PURE__ */ jsx22(UserPlus, { className: iconClass });
3187
+ if (lowerKey.includes("post") || lowerKey.includes("publication")) return /* @__PURE__ */ jsx22(Image3, { className: iconClass });
3188
+ if (lowerKey.includes("verified") || lowerKey.includes("badge")) return /* @__PURE__ */ jsx22(BadgeCheck, { className: iconClass });
3189
+ if (lowerKey.includes("bio")) return /* @__PURE__ */ jsx22(FileText2, { className: iconClass });
3190
+ if (lowerKey.includes("email")) return /* @__PURE__ */ jsx22(Mail, { className: iconClass });
3191
+ if (lowerKey.includes("phone") || lowerKey.includes("tel")) return /* @__PURE__ */ jsx22(Phone, { className: iconClass });
3192
+ if (lowerKey.includes("location") || lowerKey.includes("address") || lowerKey.includes("city")) return /* @__PURE__ */ jsx22(MapPin, { className: iconClass });
3193
+ if (lowerKey.includes("company") || lowerKey.includes("org")) return /* @__PURE__ */ jsx22(Building, { className: iconClass });
3194
+ if (lowerKey.includes("job") || lowerKey.includes("role") || lowerKey.includes("title") || lowerKey.includes("position")) return /* @__PURE__ */ jsx22(Briefcase, { className: iconClass });
3195
+ if (lowerKey.includes("website") || lowerKey.includes("url") || lowerKey.includes("link")) return /* @__PURE__ */ jsx22(Globe, { className: iconClass });
3196
+ if (lowerKey.includes("username") || lowerKey.includes("handle")) return /* @__PURE__ */ jsx22(AtSign, { className: iconClass });
3197
+ if (lowerKey.includes("date") || lowerKey.includes("birthday") || lowerKey.includes("joined")) return /* @__PURE__ */ jsx22(Calendar, { className: iconClass });
3198
+ return /* @__PURE__ */ jsx22(User2, { className: iconClass });
3199
+ };
3200
+ var formatValue = (value, type, key) => {
3201
+ if (value === null || value === void 0) return "-";
3202
+ if (typeof value === "boolean") {
3203
+ if (key?.toLowerCase().includes("verified")) {
3204
+ return value ? "Verified \u2713" : "Not verified";
3205
+ }
3206
+ return value ? "Yes" : "No";
3207
+ }
3208
+ if (type === "date" && (typeof value === "string" || typeof value === "number")) {
3209
+ try {
3210
+ return new Date(value).toLocaleDateString("en-US");
3211
+ } catch {
3212
+ return String(value);
3213
+ }
3214
+ }
3215
+ return String(value);
3216
+ };
3217
+ var normalizeCustomFields = (fields) => {
3218
+ if (!fields) return [];
3219
+ if (Array.isArray(fields)) {
3220
+ return fields;
3221
+ }
3222
+ return Object.entries(fields).filter(([_, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => ({
3223
+ key,
3224
+ label: key.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").replace(/^\w/, (c) => c.toUpperCase()).trim(),
3225
+ value
3226
+ }));
3227
+ };
3228
+ var getMemoryCategoryIcon = (category) => {
3229
+ const iconClass = "h-4 w-4 text-muted-foreground";
3230
+ switch (category) {
3231
+ case "preference":
3232
+ return /* @__PURE__ */ jsx22(Heart, { className: iconClass });
3233
+ case "fact":
3234
+ return /* @__PURE__ */ jsx22(Info, { className: iconClass });
3235
+ case "goal":
3236
+ return /* @__PURE__ */ jsx22(Target, { className: iconClass });
3237
+ case "context":
3238
+ return /* @__PURE__ */ jsx22(Lightbulb, { className: iconClass });
3239
+ default:
3240
+ return /* @__PURE__ */ jsx22(Brain, { className: iconClass });
3241
+ }
3242
+ };
3243
+ var getMemoryCategoryLabel = (category) => {
3244
+ switch (category) {
3245
+ case "preference":
3246
+ return "Prefer\xEAncia";
3247
+ case "fact":
3248
+ return "Fato";
3249
+ case "goal":
3250
+ return "Meta";
3251
+ case "context":
3252
+ return "Contexto";
3253
+ default:
3254
+ return "Outro";
3255
+ }
3256
+ };
3257
+ var UserProfile = ({
3258
+ isOpen,
3259
+ onClose,
3260
+ user,
3261
+ customFields,
3262
+ memories = [],
3263
+ config,
3264
+ onEditProfile,
3265
+ onLogout,
3266
+ onAddMemory,
3267
+ onUpdateMemory,
3268
+ onDeleteMemory,
3269
+ className
3270
+ }) => {
3271
+ const [newMemoryContent, setNewMemoryContent] = useState7("");
3272
+ const [isAddingMemory, setIsAddingMemory] = useState7(false);
3273
+ const [editingMemoryId, setEditingMemoryId] = useState7(null);
3274
+ const [editingMemoryContent, setEditingMemoryContent] = useState7("");
3275
+ const handleAddMemory = () => {
3276
+ if (newMemoryContent.trim() && onAddMemory) {
3277
+ onAddMemory(newMemoryContent.trim(), "other");
3278
+ setNewMemoryContent("");
3279
+ setIsAddingMemory(false);
3280
+ }
3281
+ };
3282
+ const handleStartEdit = (memory) => {
3283
+ setEditingMemoryId(memory.id);
3284
+ setEditingMemoryContent(memory.content);
3285
+ };
3286
+ const handleSaveEdit = () => {
3287
+ if (editingMemoryId && editingMemoryContent.trim() && onUpdateMemory) {
3288
+ onUpdateMemory(editingMemoryId, editingMemoryContent.trim());
3289
+ setEditingMemoryId(null);
3290
+ setEditingMemoryContent("");
3291
+ }
3292
+ };
3293
+ const handleCancelEdit = () => {
3294
+ setEditingMemoryId(null);
3295
+ setEditingMemoryContent("");
3296
+ };
3297
+ const labels = {
3298
+ title: config?.labels?.title || "Profile",
3299
+ basicInfo: config?.labels?.basicInfo || "Account",
3300
+ customFields: config?.labels?.customFields || "Details",
3301
+ memories: config?.labels?.memories || "Memories",
3302
+ addMemory: config?.labels?.addMemory || "Add memory",
3303
+ noMemories: config?.labels?.noMemories || "No memories yet",
3304
+ close: config?.labels?.close || "Close",
3305
+ noCustomFields: config?.labels?.noCustomFields || "No additional information"
3306
+ };
3307
+ const displayName = user?.name || user?.email?.split("@")[0] || "User";
3308
+ const initials = getInitials2(user?.name, user?.email);
3309
+ const normalizedFields = normalizeCustomFields(customFields);
3310
+ return /* @__PURE__ */ jsx22(Sheet, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs13(
3311
+ SheetContent,
3312
+ {
3313
+ side: "right",
3314
+ className: cn("w-full sm:max-w-md p-0 flex flex-col h-full overflow-hidden", className),
3315
+ children: [
3316
+ /* @__PURE__ */ jsx22(SheetHeader, { className: "px-6 py-4 border-b shrink-0", children: /* @__PURE__ */ jsx22("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx22(SheetTitle, { children: labels.title }) }) }),
3317
+ /* @__PURE__ */ jsx22(ScrollArea, { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxs13("div", { className: "p-6 space-y-6", children: [
3318
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-col items-center text-center space-y-4", children: [
3319
+ /* @__PURE__ */ jsxs13(Avatar, { className: "h-24 w-24 shrink-0", children: [
3320
+ user?.avatar && /* @__PURE__ */ jsx22(AvatarImage, { src: user.avatar, alt: displayName }),
3321
+ /* @__PURE__ */ jsx22(AvatarFallback, { className: "text-2xl bg-primary/10 text-primary", children: initials })
3322
+ ] }),
3323
+ /* @__PURE__ */ jsxs13("div", { className: "w-full px-2", children: [
3324
+ /* @__PURE__ */ jsx22("h2", { className: "text-xl font-semibold break-words", children: displayName }),
3325
+ user?.email && /* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground break-words", children: user.email })
3326
+ ] })
3327
+ ] }),
3328
+ /* @__PURE__ */ jsx22(Separator, {}),
3329
+ /* @__PURE__ */ jsxs13("div", { className: "space-y-3", children: [
3330
+ /* @__PURE__ */ jsx22("h3", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wider", children: labels.basicInfo }),
3331
+ /* @__PURE__ */ jsxs13("div", { className: "space-y-2", children: [
3332
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-3 p-3 rounded-lg bg-muted/50", children: [
3333
+ /* @__PURE__ */ jsx22(User2, { className: "h-4 w-4 text-muted-foreground mt-0.5 shrink-0" }),
3334
+ /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
3335
+ /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground", children: "Name" }),
3336
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-medium break-words", children: displayName })
3337
+ ] })
3338
+ ] }),
3339
+ user?.email && /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-3 p-3 rounded-lg bg-muted/50", children: [
3340
+ /* @__PURE__ */ jsx22(AtSign, { className: "h-4 w-4 text-muted-foreground mt-0.5 shrink-0" }),
3341
+ /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
3342
+ /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground", children: "Handle" }),
3343
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-medium break-words", children: user.email })
3344
+ ] })
3345
+ ] }),
3346
+ user?.id && user.id !== user?.name && user.id !== user?.email && /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-3 p-3 rounded-lg bg-muted/50", children: [
3347
+ /* @__PURE__ */ jsx22(User2, { className: "h-4 w-4 text-muted-foreground mt-0.5 shrink-0" }),
3348
+ /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
3349
+ /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground", children: "ID" }),
3350
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-medium break-words", children: user.id })
3351
+ ] })
3352
+ ] })
3353
+ ] })
3354
+ ] }),
3355
+ normalizedFields.length > 0 && /* @__PURE__ */ jsxs13(Fragment5, { children: [
3356
+ /* @__PURE__ */ jsx22(Separator, {}),
3357
+ /* @__PURE__ */ jsxs13("div", { className: "space-y-3", children: [
3358
+ /* @__PURE__ */ jsx22("h3", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wider", children: labels.customFields }),
3359
+ /* @__PURE__ */ jsx22("div", { className: "space-y-2", children: normalizedFields.map((field) => {
3360
+ const isBioField = field.key.toLowerCase().includes("bio");
3361
+ return /* @__PURE__ */ jsxs13(
3362
+ "div",
3363
+ {
3364
+ className: "flex items-start gap-3 p-3 rounded-lg bg-muted/50",
3365
+ children: [
3366
+ /* @__PURE__ */ jsx22("div", { className: "mt-0.5 shrink-0", children: field.icon || getFieldIcon(field.type, field.key) }),
3367
+ /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
3368
+ /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground", children: field.label }),
3369
+ /* @__PURE__ */ jsx22("p", { className: cn(
3370
+ "text-sm font-medium",
3371
+ isBioField ? "whitespace-pre-wrap break-words" : "break-words"
3372
+ ), children: formatValue(field.value, field.type, field.key) })
3373
+ ] })
3374
+ ]
3375
+ },
3376
+ field.key
3377
+ );
3378
+ }) })
3379
+ ] })
3380
+ ] }),
3381
+ /* @__PURE__ */ jsx22(Separator, {}),
3382
+ /* @__PURE__ */ jsxs13("div", { className: "space-y-3", children: [
3383
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between", children: [
3384
+ /* @__PURE__ */ jsxs13("h3", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: [
3385
+ /* @__PURE__ */ jsx22(Brain, { className: "h-4 w-4" }),
3386
+ labels.memories
3387
+ ] }),
3388
+ onAddMemory && /* @__PURE__ */ jsx22(
3389
+ Button,
3390
+ {
3391
+ variant: "ghost",
3392
+ size: "sm",
3393
+ className: "h-7 px-2",
3394
+ onClick: () => setIsAddingMemory(true),
3395
+ children: /* @__PURE__ */ jsx22(Plus3, { className: "h-4 w-4" })
3396
+ }
3397
+ )
3398
+ ] }),
3399
+ isAddingMemory && onAddMemory && /* @__PURE__ */ jsxs13("div", { className: "flex gap-2", children: [
3400
+ /* @__PURE__ */ jsx22(
3401
+ Input,
3402
+ {
3403
+ value: newMemoryContent,
3404
+ onChange: (e) => setNewMemoryContent(e.target.value),
3405
+ placeholder: "O que devo lembrar?",
3406
+ className: "flex-1 h-9",
3407
+ onKeyDown: (e) => {
3408
+ if (e.key === "Enter") handleAddMemory();
3409
+ if (e.key === "Escape") {
3410
+ setIsAddingMemory(false);
3411
+ setNewMemoryContent("");
3412
+ }
3413
+ },
3414
+ autoFocus: true
3415
+ }
3416
+ ),
3417
+ /* @__PURE__ */ jsx22(Button, { size: "sm", onClick: handleAddMemory, disabled: !newMemoryContent.trim(), children: "Salvar" })
3418
+ ] }),
3419
+ /* @__PURE__ */ jsx22("div", { className: "space-y-2", children: memories.length === 0 ? /* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground text-center py-4", children: labels.noMemories }) : memories.map((memory) => {
3420
+ const isEditing = editingMemoryId === memory.id;
3421
+ return /* @__PURE__ */ jsxs13(
3422
+ "div",
3423
+ {
3424
+ className: "flex items-start gap-3 p-3 rounded-lg bg-muted/50 group",
3425
+ children: [
3426
+ /* @__PURE__ */ jsx22("div", { className: "mt-0.5 shrink-0", children: memory.source === "agent" ? /* @__PURE__ */ jsx22(Bot2, { className: "h-4 w-4 text-primary" }) : getMemoryCategoryIcon(memory.category) }),
3427
+ /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
3428
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 mb-0.5", children: [
3429
+ /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: getMemoryCategoryLabel(memory.category) }),
3430
+ /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: "\u2022" }),
3431
+ /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: memory.source === "agent" ? "IA" : "Voc\xEA" })
3432
+ ] }),
3433
+ isEditing ? /* @__PURE__ */ jsxs13("div", { className: "space-y-2", children: [
3434
+ /* @__PURE__ */ jsx22(
3435
+ Textarea,
3436
+ {
3437
+ value: editingMemoryContent,
3438
+ onChange: (e) => setEditingMemoryContent(e.target.value),
3439
+ className: "min-h-[60px] text-sm resize-none",
3440
+ autoFocus: true,
3441
+ onKeyDown: (e) => {
3442
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
3443
+ handleSaveEdit();
3444
+ }
3445
+ if (e.key === "Escape") {
3446
+ handleCancelEdit();
3447
+ }
3448
+ }
3449
+ }
3450
+ ),
3451
+ /* @__PURE__ */ jsxs13("div", { className: "flex gap-1 justify-end", children: [
3452
+ /* @__PURE__ */ jsxs13(
3453
+ Button,
3454
+ {
3455
+ variant: "ghost",
3456
+ size: "sm",
3457
+ className: "h-7 px-2",
3458
+ onClick: handleCancelEdit,
3459
+ children: [
3460
+ /* @__PURE__ */ jsx22(X3, { className: "h-3.5 w-3.5 mr-1" }),
3461
+ "Cancelar"
3462
+ ]
3463
+ }
3464
+ ),
3465
+ /* @__PURE__ */ jsxs13(
3466
+ Button,
3467
+ {
3468
+ size: "sm",
3469
+ className: "h-7 px-2",
3470
+ onClick: handleSaveEdit,
3471
+ disabled: !editingMemoryContent.trim(),
3472
+ children: [
3473
+ /* @__PURE__ */ jsx22(Check2, { className: "h-3.5 w-3.5 mr-1" }),
3474
+ "Salvar"
3475
+ ]
3476
+ }
3477
+ )
3478
+ ] })
3479
+ ] }) : /* @__PURE__ */ jsx22("p", { className: "text-sm break-words", children: memory.content })
3480
+ ] }),
3481
+ !isEditing && (onUpdateMemory || onDeleteMemory) && /* @__PURE__ */ jsxs13("div", { className: "flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity shrink-0", children: [
3482
+ onUpdateMemory && /* @__PURE__ */ jsx22(
3483
+ Button,
3484
+ {
3485
+ variant: "ghost",
3486
+ size: "icon",
3487
+ className: "h-7 w-7",
3488
+ onClick: () => handleStartEdit(memory),
3489
+ children: /* @__PURE__ */ jsx22(Pencil, { className: "h-3.5 w-3.5 text-muted-foreground" })
3490
+ }
3491
+ ),
3492
+ onDeleteMemory && /* @__PURE__ */ jsx22(
3493
+ Button,
3494
+ {
3495
+ variant: "ghost",
3496
+ size: "icon",
3497
+ className: "h-7 w-7",
3498
+ onClick: () => onDeleteMemory(memory.id),
3499
+ children: /* @__PURE__ */ jsx22(Trash23, { className: "h-3.5 w-3.5 text-destructive" })
3500
+ }
3501
+ )
3502
+ ] })
3503
+ ]
3504
+ },
3505
+ memory.id
3506
+ );
3507
+ }) })
3508
+ ] })
3509
+ ] }) }),
3510
+ /* @__PURE__ */ jsxs13("div", { className: "p-4 border-t space-y-2 shrink-0", children: [
3511
+ onEditProfile && /* @__PURE__ */ jsx22(
3512
+ Button,
3513
+ {
3514
+ variant: "outline",
3515
+ className: "w-full",
3516
+ onClick: onEditProfile,
3517
+ children: "Edit Profile"
3518
+ }
3519
+ ),
3520
+ onLogout && /* @__PURE__ */ jsx22(
3521
+ Button,
3522
+ {
3523
+ variant: "destructive",
3524
+ className: "w-full",
3525
+ onClick: onLogout,
3526
+ children: "Log out"
3527
+ }
3528
+ )
3529
+ ] })
3530
+ ]
3531
+ }
3532
+ ) });
3533
+ };
3534
+
3535
+ // src/components/chat/ChatUI.tsx
3536
+ import { Sparkles } from "lucide-react";
3537
+ import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
3538
+ var ChatUI = ({
3539
+ messages = [],
3540
+ threads = [],
3541
+ currentThreadId = null,
3542
+ config: userConfig,
3543
+ sidebar: _sidebar,
3544
+ isGenerating = false,
3545
+ callbacks = {},
3546
+ user,
3547
+ assistant,
3548
+ suggestions = [],
3549
+ className = "",
3550
+ onAddMemory,
3551
+ onUpdateMemory,
3552
+ onDeleteMemory
3553
+ }) => {
3554
+ const config = mergeConfig(defaultChatConfig, userConfig);
3555
+ const [isMobile, setIsMobile] = useState8(false);
3556
+ const [isUserProfileOpen, setIsUserProfileOpen] = useState8(false);
3557
+ let userContext;
3558
+ try {
3559
+ const contextValue = useChatUserContext();
3560
+ userContext = contextValue?.context;
3561
+ } catch {
3562
+ userContext = void 0;
3563
+ }
3564
+ const getInitialSidebarState = () => {
3565
+ if (typeof globalThis.innerWidth === "number") {
3566
+ return globalThis.innerWidth >= 1024;
3567
+ }
3568
+ return false;
3569
+ };
3570
+ const [state, setState] = useState8({
3571
+ input: "",
3572
+ attachments: [],
3573
+ isRecording: false,
3574
+ selectedThreadId: currentThreadId,
3575
+ isAtBottom: true,
3576
+ showSidebar: getInitialSidebarState(),
3577
+ // Open by default on desktop
3578
+ showThreads: false,
3579
+ // No longer used for main sidebar
3580
+ editingMessageId: null,
3581
+ isSidebarCollapsed: false
3582
+ // No longer used for main sidebar
3583
+ });
3584
+ useEffect7(() => {
3585
+ if (currentThreadId !== state.selectedThreadId) {
3586
+ setState((prev) => ({ ...prev, selectedThreadId: currentThreadId }));
3587
+ }
3588
+ }, [currentThreadId]);
3589
+ const messagesEndRef = useRef4(null);
3590
+ const scrollAreaRef = useRef4(null);
3591
+ const [isCustomMounted, setIsCustomMounted] = useState8(false);
3592
+ const [isCustomVisible, setIsCustomVisible] = useState8(false);
3593
+ const createStateCallback = useCallback4(
3594
+ (setter) => ({
3595
+ setState: (newState) => setter?.(newState),
3596
+ getState: () => state
3597
+ }),
3598
+ [state]
3599
+ );
3600
+ useEffect7(() => {
3601
+ const checkMobile = () => {
3602
+ setIsMobile(globalThis.innerWidth < 1024);
3603
+ };
3604
+ checkMobile();
3605
+ globalThis.addEventListener("resize", checkMobile);
3606
+ return () => globalThis.removeEventListener("resize", checkMobile);
3607
+ }, []);
3608
+ useEffect7(() => {
3609
+ if (!isMobile || !config.customComponent?.component) return;
3610
+ if (state.showSidebar) {
3611
+ setIsCustomMounted(true);
3612
+ requestAnimationFrame(() => setIsCustomVisible(true));
3613
+ } else {
3614
+ setIsCustomVisible(false);
3615
+ const t = setTimeout(() => setIsCustomMounted(false), 200);
3616
+ return () => clearTimeout(t);
3617
+ }
3618
+ }, [state.showSidebar, isMobile, config.customComponent]);
3619
+ useEffect7(() => {
3620
+ if (!state.isAtBottom) return;
3621
+ const viewport = scrollAreaRef.current;
3622
+ if (!viewport) return;
3623
+ const target = viewport.scrollHeight;
3624
+ try {
3625
+ viewport.scrollTo({ top: target, behavior: "smooth" });
3626
+ } catch {
3627
+ viewport.scrollTop = target;
3628
+ }
3629
+ }, [messages, state.isAtBottom]);
3630
+ const handleScroll = useCallback4((e) => {
3631
+ const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
3632
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
3633
+ setState((prev) => ({ ...prev, isAtBottom }));
3634
+ }, []);
3635
+ const handleSendMessage = useCallback4((content, attachments = []) => {
3636
+ if (!content.trim() && attachments.length === 0) return;
3637
+ callbacks.onSendMessage?.(content, attachments, createStateCallback());
3638
+ setState((prev) => ({
3639
+ ...prev,
3640
+ input: "",
3641
+ attachments: []
3642
+ }));
3643
+ }, [callbacks, createStateCallback]);
3644
+ const handleMessageAction = useCallback4((event) => {
3645
+ const { action, messageId, content } = event;
3646
+ switch (action) {
3647
+ case "copy":
3648
+ callbacks.onCopyMessage?.(messageId, content || "", createStateCallback());
3649
+ break;
3650
+ case "edit":
3651
+ if (content) {
3652
+ callbacks.onEditMessage?.(messageId, content, createStateCallback());
3653
+ }
3654
+ break;
3655
+ case "regenerate":
3656
+ callbacks.onRegenerateMessage?.(messageId, createStateCallback());
3657
+ break;
3658
+ case "delete":
3659
+ callbacks.onDeleteMessage?.(messageId, createStateCallback());
3660
+ break;
3661
+ }
3662
+ }, [callbacks, createStateCallback]);
3663
+ const handleCreateThread = useCallback4((title) => {
3664
+ callbacks.onCreateThread?.(title, createStateCallback(setState));
3665
+ }, [callbacks, createStateCallback]);
3666
+ const handleSelectThread = useCallback4((threadId) => {
3667
+ callbacks.onSelectThread?.(threadId, createStateCallback());
3668
+ }, [callbacks, createStateCallback]);
3669
+ const handleRenameThread = useCallback4((threadId, newTitle) => {
3670
+ callbacks.onRenameThread?.(threadId, newTitle, createStateCallback());
3671
+ }, [callbacks, createStateCallback]);
3672
+ const handleDeleteThread = useCallback4((threadId) => {
3673
+ callbacks.onDeleteThread?.(threadId, createStateCallback());
3674
+ }, [callbacks, createStateCallback]);
3675
+ const handleArchiveThread = useCallback4((threadId) => {
3676
+ callbacks.onArchiveThread?.(threadId, createStateCallback());
3677
+ }, [callbacks, createStateCallback]);
3678
+ const closeSidebar = useCallback4(() => {
3679
+ setState((prev) => ({ ...prev, showSidebar: false }));
3680
+ }, []);
3681
+ const renderCustomComponent = useCallback4(() => {
3682
+ const component = config?.customComponent?.component;
3683
+ if (!component) return null;
3684
+ if (typeof component === "function") {
3685
+ return component({ onClose: closeSidebar, isMobile });
3686
+ }
3687
+ return component;
3688
+ }, [config?.customComponent?.component, closeSidebar, isMobile]);
3689
+ const renderSuggestions = () => {
3690
+ if (messages.length > 0 || !suggestions.length) return null;
3691
+ return /* @__PURE__ */ jsxs14("div", { className: "text-center py-8", children: [
3692
+ /* @__PURE__ */ jsx23("div", { className: "inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4", children: /* @__PURE__ */ jsx23(Sparkles, { className: "w-8 h-8 text-primary" }) }),
3693
+ /* @__PURE__ */ jsx23("h3", { className: "text-lg font-semibold mb-2", children: config.branding.title }),
3694
+ /* @__PURE__ */ jsx23("p", { className: "text-muted-foreground mb-6", children: config.branding.subtitle }),
3695
+ /* @__PURE__ */ jsx23("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 max-w-2xl mx-auto", children: suggestions.map((suggestion, index) => /* @__PURE__ */ jsx23(
3696
+ Card,
3697
+ {
3698
+ className: "cursor-pointer hover:bg-muted/50 transition-colors",
3699
+ onClick: () => handleSendMessage(suggestion),
3700
+ children: /* @__PURE__ */ jsx23(CardContent, { className: "p-4 text-left", children: /* @__PURE__ */ jsx23("p", { className: "text-sm", children: suggestion }) })
3701
+ },
3702
+ index
3703
+ )) })
3704
+ ] });
3705
+ };
3706
+ return /* @__PURE__ */ jsx23(TooltipProvider, { children: /* @__PURE__ */ jsx23(SidebarProvider, { defaultOpen: true, children: /* @__PURE__ */ jsxs14("div", { className: `flex h-[100svh] md:h-screen bg-background w-full overflow-hidden ${className}`, children: [
3707
+ /* @__PURE__ */ jsx23(
3708
+ Sidebar2,
3709
+ {
3710
+ threads,
3711
+ currentThreadId: state.selectedThreadId,
3712
+ config,
3713
+ onCreateThread: handleCreateThread,
3714
+ onSelectThread: handleSelectThread,
3715
+ onRenameThread: handleRenameThread,
3716
+ onDeleteThread: handleDeleteThread,
3717
+ onArchiveThread: handleArchiveThread,
3718
+ user: user ? {
3719
+ id: user.id,
3720
+ name: user.name,
3721
+ email: user.email,
3722
+ avatar: user.avatar
3723
+ } : null,
3724
+ userMenuCallbacks: {
3725
+ onViewProfile: () => {
3726
+ setIsUserProfileOpen(true);
3727
+ callbacks.onViewProfile?.();
3728
+ },
3729
+ onOpenSettings: callbacks.onOpenSettings,
3730
+ onThemeChange: callbacks.onThemeChange,
3731
+ onLogout: callbacks.onLogout
3732
+ },
3733
+ currentTheme: config.ui.theme === "auto" ? "system" : config.ui.theme,
3734
+ showThemeOptions: !!callbacks.onThemeChange
3735
+ }
3736
+ ),
3737
+ /* @__PURE__ */ jsx23(SidebarInset, { children: /* @__PURE__ */ jsxs14("div", { className: "flex flex-col h-full min-h-0", children: [
3738
+ /* @__PURE__ */ jsx23(
3739
+ ChatHeader,
3740
+ {
3741
+ config,
3742
+ currentThreadTitle: threads.find((t) => t.id === state.selectedThreadId)?.title,
3743
+ isMobile,
3744
+ onCustomComponentToggle: () => setState((prev) => ({ ...prev, showSidebar: !prev.showSidebar })),
3745
+ onNewThread: handleCreateThread,
3746
+ showCustomComponentButton: !!config?.customComponent?.component
3747
+ }
3748
+ ),
3749
+ /* @__PURE__ */ jsxs14("div", { className: "flex flex-1 flex-row min-h-0 overflow-hidden", children: [
3750
+ /* @__PURE__ */ jsxs14("div", { className: "flex-1 flex flex-col min-h-0", children: [
3751
+ /* @__PURE__ */ jsx23(
3752
+ ScrollArea,
3753
+ {
3754
+ ref: scrollAreaRef,
3755
+ className: "flex-1 min-h-0",
3756
+ viewportClassName: "p-4 overscroll-contain",
3757
+ onScrollCapture: handleScroll,
3758
+ children: /* @__PURE__ */ jsxs14("div", { className: "max-w-4xl mx-auto space-y-4 pb-4", children: [
3759
+ renderSuggestions(),
3760
+ messages.map((message) => /* @__PURE__ */ jsx23(
3761
+ Message,
3762
+ {
3763
+ message,
3764
+ userAvatar: user?.avatar,
3765
+ userName: user?.name,
3766
+ assistantAvatar: assistant?.avatar,
3767
+ assistantName: assistant?.name,
3768
+ showTimestamp: config.ui.showTimestamps,
3769
+ showAvatar: config.ui.showAvatars,
3770
+ enableCopy: config.features.enableMessageCopy,
3771
+ enableEdit: config.features.enableMessageEditing,
3772
+ enableRegenerate: config.features.enableRegeneration,
3773
+ enableToolCallsDisplay: config.features.enableToolCallsDisplay,
3774
+ compactMode: config.ui.compactMode,
3775
+ onAction: handleMessageAction,
3776
+ toolUsedLabel: config.labels.toolUsed,
3777
+ thinkingLabel: config.labels.thinking
3778
+ },
3779
+ message.id
3780
+ )),
3781
+ /* @__PURE__ */ jsx23("div", { ref: messagesEndRef })
3782
+ ] })
3783
+ }
3784
+ ),
3785
+ /* @__PURE__ */ jsx23("div", { className: "bg-background pb-[env(safe-area-inset-bottom)]", children: /* @__PURE__ */ jsx23(
3786
+ ChatInput,
3787
+ {
3788
+ value: state.input,
3789
+ onChange: (value) => setState((prev) => ({ ...prev, input: value })),
3790
+ onSubmit: handleSendMessage,
3791
+ attachments: state.attachments,
3792
+ onAttachmentsChange: (attachments) => setState((prev) => ({ ...prev, attachments })),
3793
+ placeholder: config.labels.inputPlaceholder,
3794
+ disabled: false,
3795
+ isGenerating,
3796
+ onStopGeneration: callbacks.onStopGeneration,
3797
+ enableFileUpload: config.features.enableFileUpload,
3798
+ enableAudioRecording: config.features.enableAudioRecording,
3799
+ maxAttachments: config.features.maxAttachments,
3800
+ maxFileSize: config.features.maxFileSize,
3801
+ config
3802
+ }
3803
+ ) })
3804
+ ] }),
3805
+ config?.customComponent?.component && !isMobile && /* @__PURE__ */ jsx23(
3806
+ "div",
3807
+ {
3808
+ className: `h-full transition-all duration-300 ease-in-out overflow-hidden ${state.showSidebar ? "w-80" : "w-0"}`,
3809
+ children: state.showSidebar && /* @__PURE__ */ jsx23("div", { className: "flex flex-col h-full border-l bg-background animate-in slide-in-from-right-4 duration-300 w-80", children: renderCustomComponent() })
3810
+ }
3811
+ )
3812
+ ] })
3813
+ ] }) }),
3814
+ isCustomMounted && config.customComponent?.component && isMobile && /* @__PURE__ */ jsxs14("div", { className: "fixed inset-0 z-50", children: [
3815
+ /* @__PURE__ */ jsx23(
3816
+ "div",
3817
+ {
3818
+ className: `absolute inset-0 bg-background/80 backdrop-blur-sm transition-opacity duration-200 ease-out ${isCustomVisible ? "opacity-100" : "opacity-0"}`,
3819
+ style: { willChange: "opacity" },
3820
+ onClick: closeSidebar
3821
+ }
3822
+ ),
3823
+ /* @__PURE__ */ jsx23(
3824
+ "div",
3825
+ {
3826
+ className: `absolute top-0 right-0 h-full w-full bg-background transform-gpu transition-transform duration-200 ease-out ${isCustomVisible ? "translate-x-0" : "translate-x-full"}`,
3827
+ style: { willChange: "transform" },
3828
+ children: /* @__PURE__ */ jsx23("div", { className: "h-full flex flex-col", children: renderCustomComponent() })
3829
+ }
3830
+ )
3831
+ ] }),
3832
+ /* @__PURE__ */ jsx23(
3833
+ UserProfile,
3834
+ {
3835
+ isOpen: isUserProfileOpen,
3836
+ onClose: () => setIsUserProfileOpen(false),
3837
+ user: user ? {
3838
+ id: user.id,
3839
+ name: user.name,
3840
+ email: user.email,
3841
+ avatar: user.avatar
3842
+ } : null,
3843
+ customFields: userContext?.customFields,
3844
+ memories: userContext?.memories?.items,
3845
+ onLogout: callbacks.onLogout,
3846
+ onAddMemory,
3847
+ onUpdateMemory,
3848
+ onDeleteMemory
3849
+ }
3850
+ )
3851
+ ] }) }) });
3852
+ };
3853
+
3854
+ // src/components/chat/ThreadManager.tsx
3855
+ import { useState as useState9, useRef as useRef5, useEffect as useEffect8 } from "react";
3856
+ import {
3857
+ Plus as Plus4,
3858
+ MessageSquare,
3859
+ MoreVertical as MoreVertical2,
3860
+ Edit2 as Edit22,
3861
+ Trash2 as Trash24,
3862
+ Archive as Archive2,
3863
+ Search as Search2,
3864
+ Filter as Filter2,
3865
+ Calendar as Calendar2,
3866
+ Hash,
3867
+ X as X4,
3868
+ Check as Check3
3869
+ } from "lucide-react";
3870
+ import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
3871
+ var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onArchive }) => {
3872
+ const [isEditing, setIsEditing] = useState9(false);
3873
+ const [editTitle, setEditTitle] = useState9(thread.title);
3874
+ const inputRef = useRef5(null);
3875
+ useEffect8(() => {
3876
+ if (isEditing && inputRef.current) {
3877
+ inputRef.current.focus();
3878
+ inputRef.current.select();
3879
+ }
3880
+ }, [isEditing]);
3881
+ const handleSaveEdit = () => {
3882
+ const trimmedTitle = editTitle.trim();
3883
+ if (trimmedTitle && trimmedTitle !== thread.title) {
3884
+ onRename(trimmedTitle);
3885
+ }
3886
+ setIsEditing(false);
3887
+ };
3888
+ const handleCancelEdit = () => {
3889
+ setEditTitle(thread.title);
3890
+ setIsEditing(false);
3891
+ };
3892
+ const handleKeyDown = (e) => {
3893
+ if (e.key === "Enter") {
3894
+ handleSaveEdit();
3895
+ } else if (e.key === "Escape") {
3896
+ handleCancelEdit();
3897
+ }
3898
+ };
3899
+ return /* @__PURE__ */ jsx24(Card, { className: `cursor-pointer transition-all duration-200 hover:shadow-md py-0 ${isActive ? "ring-2 ring-primary bg-primary/5" : "hover:bg-muted/50"}`, children: /* @__PURE__ */ jsx24(CardContent, { className: "p-3 max-w-sm", children: /* @__PURE__ */ jsxs15("div", { className: "flex items-start justify-between gap-2", children: [
3900
+ /* @__PURE__ */ jsx24("div", { className: "flex-1 min-w-0", onClick: onSelect, children: isEditing ? /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
3901
+ /* @__PURE__ */ jsx24(
3902
+ Input,
3903
+ {
3904
+ ref: inputRef,
3905
+ value: editTitle,
3906
+ onChange: (e) => setEditTitle(e.target.value),
3907
+ onKeyDown: handleKeyDown,
3908
+ onBlur: handleSaveEdit,
3909
+ className: "h-8 text-sm",
3910
+ placeholder: config?.labels?.threadNamePlaceholder || "Conversation name"
3911
+ }
3912
+ ),
3913
+ /* @__PURE__ */ jsx24(Button, { size: "sm", variant: "ghost", onClick: handleSaveEdit, children: /* @__PURE__ */ jsx24(Check3, { className: "h-3 w-3" }) }),
3914
+ /* @__PURE__ */ jsx24(Button, { size: "sm", variant: "ghost", onClick: handleCancelEdit, children: /* @__PURE__ */ jsx24(X4, { className: "h-3 w-3" }) })
3915
+ ] }) : /* @__PURE__ */ jsxs15(Fragment6, { children: [
3916
+ /* @__PURE__ */ jsx24("h4", { className: "font-medium text-sm truncate mb-1", children: thread.title }),
3917
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
3918
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
3919
+ /* @__PURE__ */ jsx24(Hash, { className: "h-3 w-3" }),
3920
+ thread.messageCount,
3921
+ " msgs"
3922
+ ] }),
3923
+ /* @__PURE__ */ jsx24(Separator, { orientation: "vertical", className: "h-3" }),
3924
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
3925
+ /* @__PURE__ */ jsx24(Calendar2, { className: "h-3 w-3" }),
3926
+ formatDate(thread.updatedAt, config?.labels)
3927
+ ] }),
3928
+ thread.isArchived && /* @__PURE__ */ jsxs15(Fragment6, { children: [
3929
+ /* @__PURE__ */ jsx24(Separator, { orientation: "vertical", className: "h-3" }),
3930
+ /* @__PURE__ */ jsxs15(Badge, { variant: "secondary", className: "text-xs", children: [
3931
+ /* @__PURE__ */ jsx24(Archive2, { className: "h-2 w-2 mr-1" }),
3932
+ config?.labels?.archiveThread || "Archived"
3933
+ ] })
3934
+ ] })
3935
+ ] })
3936
+ ] }) }),
3937
+ !isEditing && /* @__PURE__ */ jsxs15(DropdownMenu, { children: [
3938
+ /* @__PURE__ */ jsx24(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx24(Button, { variant: "ghost", size: "icon", className: "h-6 w-6 m-auto", children: /* @__PURE__ */ jsx24(MoreVertical2, { className: "h-3 w-3" }) }) }),
3939
+ /* @__PURE__ */ jsxs15(DropdownMenuContent, { align: "end", children: [
3940
+ /* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: () => setIsEditing(true), children: [
3941
+ /* @__PURE__ */ jsx24(Edit22, { className: "h-4 w-4 mr-2" }),
3942
+ config?.labels?.renameThread || "Rename"
3943
+ ] }),
3944
+ /* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: onArchive, children: [
3945
+ /* @__PURE__ */ jsx24(Archive2, { className: "h-4 w-4 mr-2" }),
3946
+ thread.isArchived ? config?.labels?.unarchiveThread || "Unarchive" : config?.labels?.archiveThread || "Archive"
3947
+ ] }),
3948
+ /* @__PURE__ */ jsx24(DropdownMenuSeparator, {}),
3949
+ /* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: onDelete, className: "text-destructive", children: [
3950
+ /* @__PURE__ */ jsx24(Trash24, { className: "h-4 w-4 mr-2" }),
3951
+ config?.labels?.deleteThread || "Delete"
3952
+ ] })
3953
+ ] })
3954
+ ] })
3955
+ ] }) }) });
3956
+ };
3957
+ var CreateThreadDialog2 = ({ onCreateThread, config }) => {
3958
+ const [title, setTitle] = useState9("");
3959
+ const [isOpen, setIsOpen] = useState9(false);
3960
+ const handleCreate = () => {
3961
+ onCreateThread(title.trim() || void 0);
3962
+ setTitle("");
3963
+ setIsOpen(false);
3964
+ };
3965
+ return /* @__PURE__ */ jsxs15(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: [
3966
+ /* @__PURE__ */ jsx24(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs15(Button, { variant: "outline", className: "w-full", children: [
3967
+ /* @__PURE__ */ jsx24(Plus4, { className: "h-4 w-4 mr-2" }),
3968
+ config?.labels?.createNewThread || "New Conversation"
3969
+ ] }) }),
3970
+ /* @__PURE__ */ jsxs15(DialogContent, { children: [
3971
+ /* @__PURE__ */ jsxs15(DialogHeader, { children: [
3972
+ /* @__PURE__ */ jsx24(DialogTitle, { children: config?.labels?.createNewThread || "Create New Conversation" }),
3973
+ /* @__PURE__ */ jsx24(DialogDescription, { children: "Give your new conversation a name or leave blank to auto-generate one." })
3974
+ ] }),
3975
+ /* @__PURE__ */ jsx24(
3976
+ Input,
3977
+ {
3978
+ value: title,
3979
+ onChange: (e) => setTitle(e.target.value),
3980
+ placeholder: config?.labels?.threadNamePlaceholder || "Conversation name (optional)",
3981
+ onKeyDown: (e) => e.key === "Enter" && handleCreate(),
3982
+ autoFocus: true
3983
+ }
3984
+ ),
3985
+ /* @__PURE__ */ jsxs15(DialogFooter, { children: [
3986
+ /* @__PURE__ */ jsx24(Button, { variant: "outline", onClick: () => setIsOpen(false), children: config?.labels?.cancel || "Cancel" }),
3987
+ /* @__PURE__ */ jsx24(Button, { onClick: handleCreate, children: config?.labels?.create || "Create" })
3988
+ ] })
3989
+ ] })
3990
+ ] });
3991
+ };
3992
+ var ThreadManager = ({
3993
+ threads,
3994
+ currentThreadId,
3995
+ config,
3996
+ onCreateThread,
3997
+ onSelectThread,
3998
+ onRenameThread,
3999
+ onDeleteThread,
4000
+ onArchiveThread,
4001
+ isOpen = false,
4002
+ onClose,
4003
+ className = ""
4004
+ }) => {
4005
+ const [searchQuery, setSearchQuery] = useState9("");
4006
+ const [showArchived, setShowArchived] = useState9(false);
4007
+ const [deleteThreadId, setDeleteThreadId] = useState9(null);
4008
+ const filteredThreads = threads.filter((thread) => {
4009
+ const title = (thread.title ?? "").toString();
4010
+ const matchesSearch = title.toLowerCase().includes(searchQuery.toLowerCase());
4011
+ const matchesArchiveFilter = showArchived || !thread.isArchived;
4012
+ return matchesSearch && matchesArchiveFilter;
4013
+ });
4014
+ const groupedThreads = filteredThreads.reduce((groups, thread) => {
4015
+ const date = new Date(thread.updatedAt);
4016
+ const today = /* @__PURE__ */ new Date();
4017
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
4018
+ let groupKey;
4019
+ if (date.toDateString() === today.toDateString()) {
4020
+ groupKey = config?.labels?.today || "Today";
4021
+ } else if (date.toDateString() === yesterday.toDateString()) {
4022
+ groupKey = config?.labels?.yesterday || "Yesterday";
4023
+ } else {
4024
+ groupKey = date.toLocaleDateString("en-US", {
4025
+ weekday: "long",
4026
+ day: "2-digit",
4027
+ month: "long"
4028
+ });
4029
+ }
4030
+ if (!groups[groupKey]) {
4031
+ groups[groupKey] = [];
4032
+ }
4033
+ groups[groupKey].push(thread);
4034
+ return groups;
4035
+ }, {});
4036
+ const handleDeleteThread = (threadId) => {
4037
+ onDeleteThread?.(threadId);
4038
+ setDeleteThreadId(null);
4039
+ };
4040
+ if (!isOpen) return null;
4041
+ return /* @__PURE__ */ jsx24(TooltipProvider, { children: /* @__PURE__ */ jsxs15("div", { className: `fixed inset-0 z-50 bg-background/80 backdrop-blur-sm ${className}`, children: [
4042
+ /* @__PURE__ */ jsx24("div", { className: "fixed left-0 top-0 h-full w-full max-w-md border-r bg-background shadow-lg", children: /* @__PURE__ */ jsxs15(Card, { className: "h-full border-0 rounded-none", children: [
4043
+ /* @__PURE__ */ jsxs15(CardHeader, { className: "border-b", children: [
4044
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between", children: [
4045
+ /* @__PURE__ */ jsxs15(CardTitle, { className: "flex items-center gap-2", children: [
4046
+ /* @__PURE__ */ jsx24(MessageSquare, { className: "h-5 w-5" }),
4047
+ config?.labels?.newChat || "Conversations"
4048
+ ] }),
4049
+ /* @__PURE__ */ jsx24(Button, { variant: "ghost", size: "icon", onClick: onClose, children: /* @__PURE__ */ jsx24(X4, { className: "h-4 w-4" }) })
4050
+ ] }),
4051
+ /* @__PURE__ */ jsxs15("div", { className: "space-y-3", children: [
4052
+ /* @__PURE__ */ jsxs15("div", { className: "relative", children: [
4053
+ /* @__PURE__ */ jsx24(Search2, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
4054
+ /* @__PURE__ */ jsx24(
4055
+ Input,
4056
+ {
4057
+ placeholder: config?.labels?.search || "Search conversations...",
4058
+ value: searchQuery,
4059
+ onChange: (e) => setSearchQuery(e.target.value),
4060
+ className: "pl-9"
4061
+ }
4062
+ )
4063
+ ] }),
4064
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between", children: [
4065
+ /* @__PURE__ */ jsxs15(
4066
+ Button,
4067
+ {
4068
+ variant: "outline",
4069
+ size: "sm",
4070
+ onClick: () => setShowArchived(!showArchived),
4071
+ className: "text-xs",
4072
+ children: [
4073
+ /* @__PURE__ */ jsx24(Filter2, { className: "h-3 w-3 mr-1" }),
4074
+ showArchived ? config?.labels?.hideArchived || "Hide Archived" : config?.labels?.showArchived || "Show Archived"
4075
+ ]
4076
+ }
4077
+ ),
4078
+ /* @__PURE__ */ jsxs15(Badge, { variant: "secondary", className: "text-xs", children: [
4079
+ filteredThreads.length,
4080
+ " / ",
4081
+ threads.length
4082
+ ] })
4083
+ ] })
4084
+ ] })
4085
+ ] }),
4086
+ /* @__PURE__ */ jsxs15(CardContent, { className: "p-0 flex-1", children: [
4087
+ /* @__PURE__ */ jsx24("div", { className: "p-4", children: onCreateThread && /* @__PURE__ */ jsx24(CreateThreadDialog2, { onCreateThread, config }) }),
4088
+ /* @__PURE__ */ jsx24(ScrollArea, { className: "h-[calc(100vh-280px)]", children: /* @__PURE__ */ jsx24("div", { className: "px-4 pb-4 space-y-4", children: Object.keys(groupedThreads).length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "text-center py-8 text-muted-foreground", children: [
4089
+ /* @__PURE__ */ jsx24(MessageSquare, { className: "h-12 w-12 mx-auto mb-3 opacity-50" }),
4090
+ /* @__PURE__ */ jsx24("p", { className: "text-sm", children: searchQuery ? config?.labels?.noThreadsFound || "No conversations found" : config?.labels?.noThreadsYet || "No conversations yet" })
4091
+ ] }) : Object.entries(groupedThreads).map(([group, groupThreads]) => /* @__PURE__ */ jsxs15("div", { children: [
4092
+ /* @__PURE__ */ jsx24("h3", { className: "text-sm font-medium text-muted-foreground mb-2 px-2", children: group }),
4093
+ /* @__PURE__ */ jsx24("div", { className: "space-y-2", children: groupThreads.map((thread) => /* @__PURE__ */ jsx24(
4094
+ ThreadItem,
4095
+ {
4096
+ thread,
4097
+ isActive: currentThreadId === thread.id,
4098
+ config,
4099
+ onSelect: () => onSelectThread?.(thread.id),
4100
+ onRename: (newTitle) => onRenameThread?.(thread.id, newTitle),
4101
+ onDelete: () => setDeleteThreadId(thread.id),
4102
+ onArchive: () => onArchiveThread?.(thread.id)
4103
+ },
4104
+ thread.id
4105
+ )) })
4106
+ ] }, group)) }) })
4107
+ ] })
4108
+ ] }) }),
4109
+ /* @__PURE__ */ jsx24(AlertDialog, { open: !!deleteThreadId, onOpenChange: () => setDeleteThreadId(null), children: /* @__PURE__ */ jsxs15(AlertDialogContent, { children: [
4110
+ /* @__PURE__ */ jsxs15(AlertDialogHeader, { children: [
4111
+ /* @__PURE__ */ jsx24(AlertDialogTitle, { children: config?.labels?.deleteConfirmTitle || "Delete Conversation" }),
4112
+ /* @__PURE__ */ jsx24(AlertDialogDescription, { children: config?.labels?.deleteConfirmDescription || "Are you sure you want to delete this conversation? This action cannot be undone." })
4113
+ ] }),
4114
+ /* @__PURE__ */ jsxs15(AlertDialogFooter, { children: [
4115
+ /* @__PURE__ */ jsx24(AlertDialogCancel, { children: config?.labels?.cancel || "Cancel" }),
4116
+ /* @__PURE__ */ jsx24(
4117
+ AlertDialogAction,
4118
+ {
4119
+ onClick: () => deleteThreadId && handleDeleteThread(deleteThreadId),
4120
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
4121
+ children: config?.labels?.deleteThread || "Delete"
4122
+ }
4123
+ )
4124
+ ] })
4125
+ ] }) })
4126
+ ] }) });
4127
+ };
4128
+
4129
+ // src/lib/chatUtils.ts
4130
+ var chatUtils = {
4131
+ generateId: () => globalThis.crypto?.randomUUID?.() ?? `id-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
4132
+ generateMessageId: () => chatUtils.generateId(),
4133
+ generateThreadId: () => chatUtils.generateId(),
4134
+ createMessage: (role, content, attachments) => ({
4135
+ id: chatUtils.generateMessageId(),
4136
+ role,
4137
+ content,
4138
+ timestamp: Date.now(),
4139
+ attachments,
4140
+ isComplete: true
4141
+ }),
4142
+ createThread: (title) => ({
4143
+ id: chatUtils.generateThreadId(),
4144
+ title,
4145
+ createdAt: Date.now(),
4146
+ updatedAt: Date.now(),
4147
+ messageCount: 0
4148
+ }),
4149
+ generateThreadTitle: (firstMessage) => {
4150
+ const cleaned = firstMessage.replace(/[^\w\s]/g, "").trim();
4151
+ const words = cleaned.split(/\s+/).slice(0, 6);
4152
+ return words.join(" ") || "Nova Conversa";
4153
+ }
4154
+ };
4155
+ export {
4156
+ ChatHeader,
4157
+ ChatInput,
4158
+ ChatUI,
4159
+ ChatUserContextProvider,
4160
+ Message,
4161
+ Sidebar2 as Sidebar,
4162
+ ThreadManager,
4163
+ UserMenu,
4164
+ UserProfile,
4165
+ chatConfigPresets,
4166
+ chatUtils,
4167
+ cn,
4168
+ configUtils,
4169
+ defaultChatConfig,
4170
+ featureFlags,
4171
+ formatDate,
4172
+ mergeConfig,
4173
+ themeUtils,
4174
+ useChatUserContext,
4175
+ validateConfig
4176
+ };
4177
+ //# sourceMappingURL=index.js.map