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