@mordn/chat-widget 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2663 @@
1
+ "use client";
2
+ "use strict";
3
+ "use client";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // src/index.ts
33
+ var src_exports = {};
34
+ __export(src_exports, {
35
+ Button: () => Button,
36
+ ChatWidget: () => ChatWidget,
37
+ Dialog: () => Dialog,
38
+ DialogContent: () => DialogContent,
39
+ DialogDescription: () => DialogDescription,
40
+ DialogHeader: () => DialogHeader,
41
+ DialogTitle: () => DialogTitle,
42
+ Input: () => Input,
43
+ default: () => ChatWidget_default,
44
+ fontOptions: () => fontOptions,
45
+ useChatTheme: () => useChatTheme
46
+ });
47
+ module.exports = __toCommonJS(src_exports);
48
+
49
+ // src/ChatWidget.tsx
50
+ var import_react11 = require("react");
51
+
52
+ // src/ui/button.tsx
53
+ var import_react_slot = require("@radix-ui/react-slot");
54
+ var import_class_variance_authority = require("class-variance-authority");
55
+
56
+ // src/utils/cn.ts
57
+ var import_clsx = require("clsx");
58
+ var import_tailwind_merge = require("tailwind-merge");
59
+ function cn(...inputs) {
60
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
61
+ }
62
+
63
+ // src/ui/button.tsx
64
+ var import_jsx_runtime = require("react/jsx-runtime");
65
+ var buttonVariants = (0, import_class_variance_authority.cva)(
66
+ "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",
67
+ {
68
+ variants: {
69
+ variant: {
70
+ default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
71
+ 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",
72
+ 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",
73
+ secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
74
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
75
+ link: "text-primary underline-offset-4 hover:underline"
76
+ },
77
+ size: {
78
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
79
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
80
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
81
+ icon: "size-9"
82
+ }
83
+ },
84
+ defaultVariants: {
85
+ variant: "default",
86
+ size: "default"
87
+ }
88
+ }
89
+ );
90
+ function Button({
91
+ className,
92
+ variant,
93
+ size,
94
+ asChild = false,
95
+ ...props
96
+ }) {
97
+ const Comp = asChild ? import_react_slot.Slot : "button";
98
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
99
+ Comp,
100
+ {
101
+ "data-slot": "button",
102
+ className: cn(buttonVariants({ variant, size, className })),
103
+ ...props
104
+ }
105
+ );
106
+ }
107
+
108
+ // src/components/conversation.tsx
109
+ var import_lucide_react = require("lucide-react");
110
+ var import_react = require("react");
111
+ var import_use_stick_to_bottom = require("use-stick-to-bottom");
112
+ var import_jsx_runtime2 = require("react/jsx-runtime");
113
+ var Conversation = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
114
+ import_use_stick_to_bottom.StickToBottom,
115
+ {
116
+ className: cn("relative flex-1 overflow-y-auto", className),
117
+ initial: "smooth",
118
+ resize: "smooth",
119
+ role: "log",
120
+ ...props
121
+ }
122
+ );
123
+ var ConversationContent = ({
124
+ className,
125
+ ...props
126
+ }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_use_stick_to_bottom.StickToBottom.Content, { className: cn("p-4", className), ...props });
127
+ var ConversationScrollButton = ({
128
+ className,
129
+ ...props
130
+ }) => {
131
+ const { isAtBottom, scrollToBottom } = (0, import_use_stick_to_bottom.useStickToBottomContext)();
132
+ const handleScrollToBottom = (0, import_react.useCallback)(() => {
133
+ scrollToBottom();
134
+ }, [scrollToBottom]);
135
+ return !isAtBottom && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
136
+ Button,
137
+ {
138
+ className: cn(
139
+ "absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full",
140
+ className
141
+ ),
142
+ onClick: handleScrollToBottom,
143
+ size: "icon",
144
+ type: "button",
145
+ variant: "outline",
146
+ ...props,
147
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.ArrowDownIcon, { className: "size-4" })
148
+ }
149
+ );
150
+ };
151
+
152
+ // src/ui/avatar.tsx
153
+ var AvatarPrimitive = __toESM(require("@radix-ui/react-avatar"));
154
+ var import_jsx_runtime3 = require("react/jsx-runtime");
155
+
156
+ // src/components/message.tsx
157
+ var import_class_variance_authority2 = require("class-variance-authority");
158
+ var import_jsx_runtime4 = require("react/jsx-runtime");
159
+ var Message = ({ className, from, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
160
+ "div",
161
+ {
162
+ className: cn(
163
+ "group flex w-full items-end justify-end gap-2",
164
+ from === "user" ? "is-user" : "is-assistant flex-row-reverse justify-end",
165
+ className
166
+ ),
167
+ ...props
168
+ }
169
+ );
170
+ var messageContentVariants = (0, import_class_variance_authority2.cva)(
171
+ "flex flex-col gap-2 overflow-hidden leading-relaxed chat-message-content",
172
+ {
173
+ variants: {
174
+ variant: {
175
+ contained: [
176
+ // User messages: compact bubbles on the right (max 85% width)
177
+ "group-[.is-user]:max-w-[85%] group-[.is-user]:rounded-2xl group-[.is-user]:rounded-br-lg group-[.is-user]:shadow-sm group-[.is-user]:px-4 group-[.is-user]:py-3",
178
+ // Assistant messages: no bubble, just text on background (max 100% width)
179
+ "group-[.is-assistant]:max-w-full"
180
+ ],
181
+ flat: [
182
+ // User messages: compact on the right
183
+ "group-[.is-user]:max-w-[85%] group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:rounded-2xl group-[.is-user]:rounded-br-lg",
184
+ // Assistant messages: full width
185
+ "group-[.is-assistant]:max-w-full"
186
+ ]
187
+ }
188
+ },
189
+ defaultVariants: {
190
+ variant: "contained"
191
+ }
192
+ }
193
+ );
194
+ var MessageContent = ({
195
+ children,
196
+ className,
197
+ variant,
198
+ ...props
199
+ }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
200
+ "div",
201
+ {
202
+ className: cn(messageContentVariants({ variant, className })),
203
+ ...props,
204
+ children
205
+ }
206
+ );
207
+
208
+ // src/ui/dropdown-menu.tsx
209
+ var DropdownMenuPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"));
210
+ var import_lucide_react2 = require("lucide-react");
211
+ var import_jsx_runtime5 = require("react/jsx-runtime");
212
+
213
+ // src/ui/select.tsx
214
+ var SelectPrimitive = __toESM(require("@radix-ui/react-select"));
215
+ var import_lucide_react3 = require("lucide-react");
216
+ var import_jsx_runtime6 = require("react/jsx-runtime");
217
+
218
+ // src/ui/textarea.tsx
219
+ var import_jsx_runtime7 = require("react/jsx-runtime");
220
+ function Textarea({ className, ...props }) {
221
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
222
+ "textarea",
223
+ {
224
+ "data-slot": "textarea",
225
+ className: cn(
226
+ "border-input placeholder:text-muted-foreground focus-visible:border-ring aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base transition-colors outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
227
+ className
228
+ ),
229
+ ...props
230
+ }
231
+ );
232
+ }
233
+
234
+ // src/components/prompt-input.tsx
235
+ var import_lucide_react4 = require("lucide-react");
236
+ var import_nanoid = require("nanoid");
237
+ var import_react2 = require("react");
238
+ var import_jsx_runtime8 = require("react/jsx-runtime");
239
+ var AttachmentsContext = (0, import_react2.createContext)(null);
240
+ var usePromptInputAttachments = () => {
241
+ const context = (0, import_react2.useContext)(AttachmentsContext);
242
+ if (!context) {
243
+ throw new Error(
244
+ "usePromptInputAttachments must be used within a PromptInput"
245
+ );
246
+ }
247
+ return context;
248
+ };
249
+ function PromptInputAttachment({
250
+ data,
251
+ className,
252
+ ...props
253
+ }) {
254
+ const attachments = usePromptInputAttachments();
255
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
256
+ "div",
257
+ {
258
+ className: cn("group relative h-14 w-14 rounded-lg border", className),
259
+ ...props,
260
+ children: [
261
+ data.mediaType?.startsWith("image/") && data.url ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
262
+ "img",
263
+ {
264
+ alt: data.filename || "attachment",
265
+ className: "size-full rounded-lg object-cover",
266
+ height: 56,
267
+ src: data.url,
268
+ width: 56
269
+ }
270
+ ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex size-full items-center justify-center text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.PaperclipIcon, { className: "size-4" }) }),
271
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
272
+ Button,
273
+ {
274
+ "aria-label": "Remove attachment",
275
+ className: "-right-1 -top-1 absolute h-4 w-4 rounded-full opacity-0 group-hover:opacity-100 p-0 [&_svg]:h-2 [&_svg]:w-2",
276
+ onClick: () => attachments.remove(data.id),
277
+ size: "icon",
278
+ type: "button",
279
+ variant: "outline",
280
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.XIcon, { className: "h-2 w-2 shrink-0" })
281
+ }
282
+ )
283
+ ]
284
+ },
285
+ data.id
286
+ );
287
+ }
288
+ function PromptInputAttachments({
289
+ className,
290
+ children,
291
+ ...props
292
+ }) {
293
+ const attachments = usePromptInputAttachments();
294
+ const [height, setHeight] = (0, import_react2.useState)(0);
295
+ const contentRef = (0, import_react2.useRef)(null);
296
+ (0, import_react2.useLayoutEffect)(() => {
297
+ const el = contentRef.current;
298
+ if (!el) {
299
+ return;
300
+ }
301
+ const ro = new ResizeObserver(() => {
302
+ setHeight(el.getBoundingClientRect().height);
303
+ });
304
+ ro.observe(el);
305
+ setHeight(el.getBoundingClientRect().height);
306
+ return () => ro.disconnect();
307
+ }, []);
308
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
309
+ "div",
310
+ {
311
+ "aria-live": "polite",
312
+ className: cn(
313
+ "overflow-hidden transition-[height] duration-200 ease-out",
314
+ className
315
+ ),
316
+ style: { height: attachments.files.length ? height : 0 },
317
+ ...props,
318
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex flex-wrap gap-2 p-3 pt-3", ref: contentRef, children: attachments.files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react2.Fragment, { children: children(file) }, file.id)) })
319
+ }
320
+ );
321
+ }
322
+ var PromptInput = ({
323
+ className,
324
+ accept,
325
+ multiple,
326
+ globalDrop,
327
+ syncHiddenInput,
328
+ maxFiles,
329
+ maxFileSize,
330
+ onError,
331
+ onSubmit,
332
+ ...props
333
+ }) => {
334
+ const [items, setItems] = (0, import_react2.useState)([]);
335
+ const inputRef = (0, import_react2.useRef)(null);
336
+ const anchorRef = (0, import_react2.useRef)(null);
337
+ const formRef = (0, import_react2.useRef)(null);
338
+ (0, import_react2.useEffect)(() => {
339
+ const root = anchorRef.current?.closest("form");
340
+ if (root instanceof HTMLFormElement) {
341
+ formRef.current = root;
342
+ }
343
+ }, []);
344
+ const openFileDialog = (0, import_react2.useCallback)(() => {
345
+ inputRef.current?.click();
346
+ }, []);
347
+ const matchesAccept = (0, import_react2.useCallback)(
348
+ (f) => {
349
+ if (!accept || accept.trim() === "") {
350
+ return true;
351
+ }
352
+ if (accept.includes("image/*")) {
353
+ return f.type.startsWith("image/");
354
+ }
355
+ return true;
356
+ },
357
+ [accept]
358
+ );
359
+ const add = (0, import_react2.useCallback)(
360
+ (files) => {
361
+ const incoming = Array.from(files);
362
+ const accepted = incoming.filter((f) => matchesAccept(f));
363
+ if (accepted.length === 0) {
364
+ onError?.({
365
+ code: "accept",
366
+ message: "No files match the accepted types."
367
+ });
368
+ return;
369
+ }
370
+ const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
371
+ const sized = accepted.filter(withinSize);
372
+ if (sized.length === 0 && accepted.length > 0) {
373
+ onError?.({
374
+ code: "max_file_size",
375
+ message: "All files exceed the maximum size."
376
+ });
377
+ return;
378
+ }
379
+ setItems((prev) => {
380
+ const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : void 0;
381
+ const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
382
+ if (typeof capacity === "number" && sized.length > capacity) {
383
+ onError?.({
384
+ code: "max_files",
385
+ message: "Too many files. Some were not added."
386
+ });
387
+ }
388
+ const next = [];
389
+ for (const file of capped) {
390
+ next.push({
391
+ id: (0, import_nanoid.nanoid)(),
392
+ type: "file",
393
+ url: URL.createObjectURL(file),
394
+ mediaType: file.type,
395
+ filename: file.name
396
+ });
397
+ }
398
+ return prev.concat(next);
399
+ });
400
+ },
401
+ [matchesAccept, maxFiles, maxFileSize, onError]
402
+ );
403
+ const remove = (0, import_react2.useCallback)((id) => {
404
+ setItems((prev) => {
405
+ const found = prev.find((file) => file.id === id);
406
+ if (found?.url) {
407
+ URL.revokeObjectURL(found.url);
408
+ }
409
+ return prev.filter((file) => file.id !== id);
410
+ });
411
+ }, []);
412
+ const clear = (0, import_react2.useCallback)(() => {
413
+ setItems((prev) => {
414
+ for (const file of prev) {
415
+ if (file.url) {
416
+ URL.revokeObjectURL(file.url);
417
+ }
418
+ }
419
+ return [];
420
+ });
421
+ }, []);
422
+ (0, import_react2.useEffect)(() => {
423
+ if (syncHiddenInput && inputRef.current) {
424
+ if (items.length === 0) {
425
+ inputRef.current.value = "";
426
+ }
427
+ }
428
+ }, [items, syncHiddenInput]);
429
+ (0, import_react2.useEffect)(() => {
430
+ const form = formRef.current;
431
+ if (!form) {
432
+ return;
433
+ }
434
+ const onDragOver = (e) => {
435
+ if (e.dataTransfer?.types?.includes("Files")) {
436
+ e.preventDefault();
437
+ }
438
+ };
439
+ const onDrop = (e) => {
440
+ if (e.dataTransfer?.types?.includes("Files")) {
441
+ e.preventDefault();
442
+ }
443
+ if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
444
+ add(e.dataTransfer.files);
445
+ }
446
+ };
447
+ form.addEventListener("dragover", onDragOver);
448
+ form.addEventListener("drop", onDrop);
449
+ return () => {
450
+ form.removeEventListener("dragover", onDragOver);
451
+ form.removeEventListener("drop", onDrop);
452
+ };
453
+ }, [add]);
454
+ (0, import_react2.useEffect)(() => {
455
+ if (!globalDrop) {
456
+ return;
457
+ }
458
+ const onDragOver = (e) => {
459
+ if (e.dataTransfer?.types?.includes("Files")) {
460
+ e.preventDefault();
461
+ }
462
+ };
463
+ const onDrop = (e) => {
464
+ if (e.dataTransfer?.types?.includes("Files")) {
465
+ e.preventDefault();
466
+ }
467
+ if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
468
+ add(e.dataTransfer.files);
469
+ }
470
+ };
471
+ document.addEventListener("dragover", onDragOver);
472
+ document.addEventListener("drop", onDrop);
473
+ return () => {
474
+ document.removeEventListener("dragover", onDragOver);
475
+ document.removeEventListener("drop", onDrop);
476
+ };
477
+ }, [add, globalDrop]);
478
+ const handleChange = (event) => {
479
+ if (event.currentTarget.files) {
480
+ add(event.currentTarget.files);
481
+ }
482
+ };
483
+ const handleSubmit = (event) => {
484
+ event.preventDefault();
485
+ const files = items.map(({ ...item }) => ({
486
+ ...item
487
+ }));
488
+ onSubmit({ text: event.currentTarget.message.value, files }, event);
489
+ clear();
490
+ };
491
+ const ctx = (0, import_react2.useMemo)(
492
+ () => ({
493
+ files: items.map((item) => ({ ...item, id: item.id })),
494
+ add,
495
+ remove,
496
+ clear,
497
+ openFileDialog,
498
+ fileInputRef: inputRef
499
+ }),
500
+ [items, add, remove, clear, openFileDialog]
501
+ );
502
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(AttachmentsContext.Provider, { value: ctx, children: [
503
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": "true", className: "hidden", ref: anchorRef }),
504
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
505
+ "input",
506
+ {
507
+ accept,
508
+ className: "hidden",
509
+ multiple,
510
+ onChange: handleChange,
511
+ ref: inputRef,
512
+ type: "file"
513
+ }
514
+ ),
515
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
516
+ "form",
517
+ {
518
+ className: cn(
519
+ "w-full divide-y overflow-hidden rounded-xl border bg-background focus-within:border-ring transition-colors",
520
+ "[&:focus-within]:shadow-none [&:focus]:shadow-none shadow-none",
521
+ className
522
+ ),
523
+ onSubmit: handleSubmit,
524
+ style: { boxShadow: "none" },
525
+ ...props
526
+ }
527
+ )
528
+ ] });
529
+ };
530
+ var PromptInputBody = ({
531
+ className,
532
+ ...props
533
+ }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: cn(className, "flex flex-col"), ...props });
534
+ var PromptInputTextarea = ({
535
+ onChange,
536
+ className,
537
+ placeholder = "What would you like to know?",
538
+ ...props
539
+ }) => {
540
+ const attachments = usePromptInputAttachments();
541
+ const handleKeyDown = (e) => {
542
+ if (e.key === "Enter") {
543
+ if (e.nativeEvent.isComposing) {
544
+ return;
545
+ }
546
+ if (e.shiftKey) {
547
+ return;
548
+ }
549
+ e.preventDefault();
550
+ const form = e.currentTarget.form;
551
+ if (form) {
552
+ form.requestSubmit();
553
+ }
554
+ }
555
+ };
556
+ const handlePaste = (event) => {
557
+ const items = event.clipboardData?.items;
558
+ if (!items) {
559
+ return;
560
+ }
561
+ const files = [];
562
+ for (const item of items) {
563
+ if (item.kind === "file") {
564
+ const file = item.getAsFile();
565
+ if (file) {
566
+ files.push(file);
567
+ }
568
+ }
569
+ }
570
+ if (files.length > 0) {
571
+ event.preventDefault();
572
+ attachments.add(files);
573
+ }
574
+ };
575
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
576
+ Textarea,
577
+ {
578
+ className: cn(
579
+ "w-full resize-none rounded-none border-none p-3 shadow-none outline-none ring-0",
580
+ "field-sizing-content",
581
+ "max-h-48 min-h-16",
582
+ "focus-visible:ring-0 focus-visible:border-red-500 focus-visible:shadow-none focus:ring-0 focus:shadow-none",
583
+ className
584
+ ),
585
+ name: "message",
586
+ onChange: (e) => {
587
+ onChange?.(e);
588
+ },
589
+ onKeyDown: handleKeyDown,
590
+ onPaste: handlePaste,
591
+ placeholder,
592
+ ...props
593
+ }
594
+ );
595
+ };
596
+ var PromptInputToolbar = ({
597
+ className,
598
+ ...props
599
+ }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
600
+ "div",
601
+ {
602
+ className: cn("flex items-center justify-between p-1", className),
603
+ ...props
604
+ }
605
+ );
606
+ var PromptInputTools = ({
607
+ className,
608
+ ...props
609
+ }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
610
+ "div",
611
+ {
612
+ className: cn(
613
+ "flex items-center gap-1",
614
+ "[&_button:first-child]:rounded-bl-xl",
615
+ className
616
+ ),
617
+ ...props
618
+ }
619
+ );
620
+ var PromptInputButton = ({
621
+ variant = "ghost",
622
+ className,
623
+ size,
624
+ ...props
625
+ }) => {
626
+ const newSize = size ?? import_react2.Children.count(props.children) > 1 ? "default" : "icon";
627
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
628
+ Button,
629
+ {
630
+ className: cn(
631
+ "shrink-0 gap-1.5 rounded-lg",
632
+ variant === "ghost" && "text-muted-foreground",
633
+ newSize === "default" && "px-3",
634
+ className
635
+ ),
636
+ size: newSize,
637
+ type: "button",
638
+ variant,
639
+ ...props
640
+ }
641
+ );
642
+ };
643
+ var PromptInputSubmit = ({
644
+ className,
645
+ variant = "default",
646
+ size = "icon",
647
+ status,
648
+ children,
649
+ ...props
650
+ }) => {
651
+ let Icon2 = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.SendIcon, { className: "size-4" });
652
+ if (status === "submitted") {
653
+ Icon2 = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Loader2Icon, { className: "size-4 animate-spin" });
654
+ } else if (status === "streaming") {
655
+ Icon2 = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.SquareIcon, { className: "size-4" });
656
+ } else if (status === "error") {
657
+ Icon2 = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.XIcon, { className: "size-4" });
658
+ }
659
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
660
+ Button,
661
+ {
662
+ className: cn("gap-1.5 rounded-lg", className),
663
+ size,
664
+ type: "submit",
665
+ variant,
666
+ ...props,
667
+ children: children ?? Icon2
668
+ }
669
+ );
670
+ };
671
+
672
+ // src/components/message-attachments.tsx
673
+ var import_lucide_react5 = require("lucide-react");
674
+ var import_jsx_runtime9 = require("react/jsx-runtime");
675
+ function MessageAttachments({ attachments, className }) {
676
+ if (!attachments || attachments.length === 0) {
677
+ return null;
678
+ }
679
+ const handleAttachmentClick = (attachment) => {
680
+ if (attachment.url.startsWith("data:")) {
681
+ const newWindow = window.open("", "_blank");
682
+ if (newWindow) {
683
+ newWindow.document.write(`
684
+ <html>
685
+ <head><title>${attachment.filename}</title></head>
686
+ <body style="margin:0; padding:20px; background:#f5f5f5; display:flex; justify-content:center; align-items:center; min-height:100vh;">
687
+ <img src="${attachment.url}" alt="${attachment.filename}" style="max-width:100%; max-height:100%; object-fit:contain; border-radius:8px; box-shadow:0 4px 12px rgba(0,0,0,0.15);" />
688
+ </body>
689
+ </html>
690
+ `);
691
+ newWindow.document.close();
692
+ }
693
+ } else if (attachment.url.startsWith("blob:")) {
694
+ window.open(attachment.url, "_blank");
695
+ } else if (attachment.url.startsWith("http")) {
696
+ window.open(attachment.url, "_blank");
697
+ } else {
698
+ window.open(attachment.url, "_blank");
699
+ }
700
+ };
701
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: cn("flex flex-wrap gap-2", className), children: attachments.map((attachment, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
702
+ "div",
703
+ {
704
+ className: "group relative h-14 w-14 rounded-lg",
705
+ children: attachment.mediaType.startsWith("image/") ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
706
+ "img",
707
+ {
708
+ src: attachment.url,
709
+ alt: attachment.filename,
710
+ className: "size-full rounded-lg object-cover cursor-pointer hover:opacity-80 transition-opacity",
711
+ onClick: () => handleAttachmentClick(attachment)
712
+ }
713
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
714
+ "div",
715
+ {
716
+ className: "flex size-full items-center justify-center text-muted-foreground cursor-pointer hover:bg-secondary/50 rounded-lg transition-colors",
717
+ onClick: () => handleAttachmentClick(attachment),
718
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.PaperclipIcon, { className: "size-4" })
719
+ }
720
+ )
721
+ },
722
+ index
723
+ )) });
724
+ }
725
+
726
+ // src/components/interface.tsx
727
+ var import_react8 = require("react");
728
+ var import_lucide_react10 = require("lucide-react");
729
+ var import_react9 = require("react");
730
+ var import_react10 = require("@ai-sdk/react");
731
+ var import_ai = require("ai");
732
+
733
+ // src/components/response.tsx
734
+ var import_react4 = require("react");
735
+ var import_streamdown = require("streamdown");
736
+
737
+ // src/hooks/use-code-scroll.ts
738
+ var import_react3 = require("react");
739
+ function useCodeBlockAutoScroll(isStreaming) {
740
+ const observerRef = (0, import_react3.useRef)(null);
741
+ const containerRef = (0, import_react3.useRef)(null);
742
+ (0, import_react3.useEffect)(() => {
743
+ if (!isStreaming || !containerRef.current) {
744
+ if (observerRef.current) {
745
+ observerRef.current.disconnect();
746
+ observerRef.current = null;
747
+ }
748
+ return;
749
+ }
750
+ observerRef.current = new MutationObserver(() => {
751
+ if (!containerRef.current) return;
752
+ const codeBlocks = containerRef.current.querySelectorAll("pre");
753
+ codeBlocks.forEach((pre) => {
754
+ pre.scrollTop = pre.scrollHeight;
755
+ });
756
+ });
757
+ observerRef.current.observe(containerRef.current, {
758
+ childList: true,
759
+ subtree: true,
760
+ characterData: true
761
+ });
762
+ return () => {
763
+ if (observerRef.current) {
764
+ observerRef.current.disconnect();
765
+ }
766
+ };
767
+ }, [isStreaming]);
768
+ return containerRef;
769
+ }
770
+
771
+ // src/components/response.tsx
772
+ var import_jsx_runtime10 = require("react/jsx-runtime");
773
+ var Response = (0, import_react4.memo)(
774
+ ({ className, isStreaming = false, ...props }) => {
775
+ const containerRef = useCodeBlockAutoScroll(isStreaming);
776
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: containerRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
777
+ import_streamdown.Streamdown,
778
+ {
779
+ className: cn(
780
+ "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
781
+ className
782
+ ),
783
+ ...props
784
+ }
785
+ ) });
786
+ },
787
+ (prevProps, nextProps) => prevProps.children === nextProps.children
788
+ );
789
+ Response.displayName = "Response";
790
+
791
+ // src/ui/collapsible.tsx
792
+ var CollapsiblePrimitive = __toESM(require("@radix-ui/react-collapsible"));
793
+ var import_jsx_runtime11 = require("react/jsx-runtime");
794
+ function Collapsible({
795
+ ...props
796
+ }) {
797
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CollapsiblePrimitive.Root, { "data-slot": "collapsible", ...props });
798
+ }
799
+ function CollapsibleTrigger2({
800
+ ...props
801
+ }) {
802
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
803
+ CollapsiblePrimitive.CollapsibleTrigger,
804
+ {
805
+ "data-slot": "collapsible-trigger",
806
+ ...props
807
+ }
808
+ );
809
+ }
810
+ function CollapsibleContent2({
811
+ ...props
812
+ }) {
813
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
814
+ CollapsiblePrimitive.CollapsibleContent,
815
+ {
816
+ "data-slot": "collapsible-content",
817
+ ...props
818
+ }
819
+ );
820
+ }
821
+
822
+ // src/components/sources.tsx
823
+ var import_lucide_react6 = require("lucide-react");
824
+ var import_jsx_runtime12 = require("react/jsx-runtime");
825
+ var Sources = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
826
+ Collapsible,
827
+ {
828
+ className: cn("not-prose mb-4 text-primary text-xs", className),
829
+ ...props
830
+ }
831
+ );
832
+ var SourcesTrigger = ({
833
+ className,
834
+ count,
835
+ children,
836
+ ...props
837
+ }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
838
+ CollapsibleTrigger2,
839
+ {
840
+ className: cn("flex items-center gap-2", className),
841
+ ...props,
842
+ children: children ?? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
843
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "font-medium", children: [
844
+ "Used ",
845
+ count,
846
+ " sources"
847
+ ] }),
848
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.ChevronDownIcon, { className: "h-4 w-4" })
849
+ ] })
850
+ }
851
+ );
852
+ var SourcesContent = ({
853
+ className,
854
+ ...props
855
+ }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
856
+ CollapsibleContent2,
857
+ {
858
+ className: cn(
859
+ "mt-3 flex w-fit flex-col gap-2",
860
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
861
+ className
862
+ ),
863
+ ...props
864
+ }
865
+ );
866
+ var Source = ({ href, title, children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
867
+ "a",
868
+ {
869
+ className: "flex items-center gap-2",
870
+ href,
871
+ rel: "noreferrer",
872
+ target: "_blank",
873
+ ...props,
874
+ children: children ?? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.BookIcon, { className: "h-4 w-4" }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block font-medium", children: title })
877
+ ] })
878
+ }
879
+ );
880
+
881
+ // src/components/reasoning.tsx
882
+ var import_react_use_controllable_state = require("@radix-ui/react-use-controllable-state");
883
+ var import_lucide_react7 = require("lucide-react");
884
+ var import_react5 = require("react");
885
+ var import_jsx_runtime13 = require("react/jsx-runtime");
886
+ var ReasoningContext = (0, import_react5.createContext)(null);
887
+ var useReasoning = () => {
888
+ const context = (0, import_react5.useContext)(ReasoningContext);
889
+ if (!context) {
890
+ throw new Error("Reasoning components must be used within Reasoning");
891
+ }
892
+ return context;
893
+ };
894
+ var AUTO_CLOSE_DELAY = 1e3;
895
+ var MS_IN_S = 1e3;
896
+ var Reasoning = (0, import_react5.memo)(
897
+ ({
898
+ className,
899
+ isStreaming = false,
900
+ open,
901
+ defaultOpen = true,
902
+ onOpenChange,
903
+ duration: durationProp,
904
+ children,
905
+ ...props
906
+ }) => {
907
+ const [isOpen, setIsOpen] = (0, import_react_use_controllable_state.useControllableState)({
908
+ prop: open,
909
+ defaultProp: defaultOpen,
910
+ onChange: onOpenChange
911
+ });
912
+ const [duration, setDuration] = (0, import_react_use_controllable_state.useControllableState)({
913
+ prop: durationProp,
914
+ defaultProp: void 0
915
+ });
916
+ const [hasAutoClosed, setHasAutoClosed] = (0, import_react5.useState)(false);
917
+ const [startTime, setStartTime] = (0, import_react5.useState)(null);
918
+ (0, import_react5.useEffect)(() => {
919
+ if (isStreaming) {
920
+ if (startTime === null) {
921
+ setStartTime(Date.now());
922
+ }
923
+ } else if (startTime !== null) {
924
+ setDuration(Math.ceil((Date.now() - startTime) / MS_IN_S));
925
+ setStartTime(null);
926
+ }
927
+ }, [isStreaming, startTime, setDuration]);
928
+ (0, import_react5.useEffect)(() => {
929
+ if (defaultOpen && !isStreaming && isOpen && !hasAutoClosed && durationProp !== void 0) {
930
+ const timer = setTimeout(() => {
931
+ setIsOpen(false);
932
+ setHasAutoClosed(true);
933
+ }, AUTO_CLOSE_DELAY);
934
+ return () => clearTimeout(timer);
935
+ }
936
+ }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosed, durationProp]);
937
+ const handleOpenChange = (newOpen) => {
938
+ setIsOpen(newOpen);
939
+ };
940
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
941
+ ReasoningContext.Provider,
942
+ {
943
+ value: { isStreaming, isOpen, setIsOpen, duration },
944
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
945
+ Collapsible,
946
+ {
947
+ className: cn("not-prose", className),
948
+ onOpenChange: handleOpenChange,
949
+ open: isOpen,
950
+ ...props,
951
+ children
952
+ }
953
+ )
954
+ }
955
+ );
956
+ }
957
+ );
958
+ var getThinkingMessage = (isStreaming, duration) => {
959
+ if (isStreaming) {
960
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: "Thinking..." });
961
+ }
962
+ if (duration === void 0) {
963
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: "Thought process" });
964
+ }
965
+ if (duration === 0) {
966
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: "Thought process" });
967
+ }
968
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
969
+ "Thought for ",
970
+ duration,
971
+ " seconds"
972
+ ] });
973
+ };
974
+ var ReasoningTrigger = (0, import_react5.memo)(
975
+ ({ className, children, ...props }) => {
976
+ const { isStreaming, isOpen, duration } = useReasoning();
977
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
978
+ CollapsibleTrigger2,
979
+ {
980
+ className: cn(
981
+ "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",
982
+ className
983
+ ),
984
+ ...props,
985
+ children: children ?? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
986
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.BrainIcon, { className: "size-4 flex-shrink-0" }),
987
+ getThinkingMessage(isStreaming, duration),
988
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
989
+ import_lucide_react7.ChevronDownIcon,
990
+ {
991
+ className: cn(
992
+ "size-4 flex-shrink-0 transition-transform ml-1",
993
+ isOpen ? "rotate-180" : "rotate-0"
994
+ )
995
+ }
996
+ )
997
+ ] })
998
+ }
999
+ );
1000
+ }
1001
+ );
1002
+ var ReasoningContent = (0, import_react5.memo)(
1003
+ ({ className, children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1004
+ CollapsibleContent2,
1005
+ {
1006
+ className: cn(
1007
+ "mt-4 text-sm",
1008
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
1009
+ className
1010
+ ),
1011
+ ...props,
1012
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Response, { className: "grid gap-2", children })
1013
+ }
1014
+ )
1015
+ );
1016
+ Reasoning.displayName = "Reasoning";
1017
+ ReasoningTrigger.displayName = "ReasoningTrigger";
1018
+ ReasoningContent.displayName = "ReasoningContent";
1019
+
1020
+ // src/components/loader.tsx
1021
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1022
+ var LoaderIcon = ({ size = 16 }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1023
+ "svg",
1024
+ {
1025
+ height: size,
1026
+ strokeLinejoin: "round",
1027
+ style: { color: "currentcolor" },
1028
+ viewBox: "0 0 16 16",
1029
+ width: size,
1030
+ children: [
1031
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("title", { children: "Loader" }),
1032
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("g", { clipPath: "url(#clip0_2393_1490)", children: [
1033
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: "M8 0V4", stroke: "currentColor", strokeWidth: "1.5" }),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1035
+ "path",
1036
+ {
1037
+ d: "M8 16V12",
1038
+ opacity: "0.5",
1039
+ stroke: "currentColor",
1040
+ strokeWidth: "1.5"
1041
+ }
1042
+ ),
1043
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1044
+ "path",
1045
+ {
1046
+ d: "M3.29773 1.52783L5.64887 4.7639",
1047
+ opacity: "0.9",
1048
+ stroke: "currentColor",
1049
+ strokeWidth: "1.5"
1050
+ }
1051
+ ),
1052
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1053
+ "path",
1054
+ {
1055
+ d: "M12.7023 1.52783L10.3511 4.7639",
1056
+ opacity: "0.1",
1057
+ stroke: "currentColor",
1058
+ strokeWidth: "1.5"
1059
+ }
1060
+ ),
1061
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1062
+ "path",
1063
+ {
1064
+ d: "M12.7023 14.472L10.3511 11.236",
1065
+ opacity: "0.4",
1066
+ stroke: "currentColor",
1067
+ strokeWidth: "1.5"
1068
+ }
1069
+ ),
1070
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1071
+ "path",
1072
+ {
1073
+ d: "M3.29773 14.472L5.64887 11.236",
1074
+ opacity: "0.6",
1075
+ stroke: "currentColor",
1076
+ strokeWidth: "1.5"
1077
+ }
1078
+ ),
1079
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1080
+ "path",
1081
+ {
1082
+ d: "M15.6085 5.52783L11.8043 6.7639",
1083
+ opacity: "0.2",
1084
+ stroke: "currentColor",
1085
+ strokeWidth: "1.5"
1086
+ }
1087
+ ),
1088
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1089
+ "path",
1090
+ {
1091
+ d: "M0.391602 10.472L4.19583 9.23598",
1092
+ opacity: "0.7",
1093
+ stroke: "currentColor",
1094
+ strokeWidth: "1.5"
1095
+ }
1096
+ ),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1098
+ "path",
1099
+ {
1100
+ d: "M15.6085 10.4722L11.8043 9.2361",
1101
+ opacity: "0.3",
1102
+ stroke: "currentColor",
1103
+ strokeWidth: "1.5"
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1107
+ "path",
1108
+ {
1109
+ d: "M0.391602 5.52783L4.19583 6.7639",
1110
+ opacity: "0.8",
1111
+ stroke: "currentColor",
1112
+ strokeWidth: "1.5"
1113
+ }
1114
+ )
1115
+ ] }),
1116
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("clipPath", { id: "clip0_2393_1490", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("rect", { fill: "white", height: "16", width: "16" }) }) })
1117
+ ]
1118
+ }
1119
+ );
1120
+ var Loader = ({ className, size = 16, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1121
+ "div",
1122
+ {
1123
+ className: cn(
1124
+ "inline-flex animate-spin items-center justify-center",
1125
+ className
1126
+ ),
1127
+ ...props,
1128
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LoaderIcon, { size })
1129
+ }
1130
+ );
1131
+
1132
+ // src/ui/badge.tsx
1133
+ var import_react_slot2 = require("@radix-ui/react-slot");
1134
+ var import_class_variance_authority3 = require("class-variance-authority");
1135
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1136
+ var badgeVariants = (0, import_class_variance_authority3.cva)(
1137
+ "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",
1138
+ {
1139
+ variants: {
1140
+ variant: {
1141
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
1142
+ secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
1143
+ 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",
1144
+ outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"
1145
+ }
1146
+ },
1147
+ defaultVariants: {
1148
+ variant: "default"
1149
+ }
1150
+ }
1151
+ );
1152
+ function Badge({
1153
+ className,
1154
+ variant,
1155
+ asChild = false,
1156
+ ...props
1157
+ }) {
1158
+ const Comp = asChild ? import_react_slot2.Slot : "span";
1159
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1160
+ Comp,
1161
+ {
1162
+ "data-slot": "badge",
1163
+ className: cn(badgeVariants({ variant }), className),
1164
+ ...props
1165
+ }
1166
+ );
1167
+ }
1168
+
1169
+ // src/components/tool.tsx
1170
+ var import_lucide_react9 = require("lucide-react");
1171
+ var import_react7 = require("react");
1172
+
1173
+ // src/components/code-block.tsx
1174
+ var import_lucide_react8 = require("lucide-react");
1175
+ var import_react6 = require("react");
1176
+ var import_react_syntax_highlighter = require("react-syntax-highlighter");
1177
+ var import_prism = require("react-syntax-highlighter/dist/esm/styles/prism");
1178
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1179
+ var CodeBlockContext = (0, import_react6.createContext)({
1180
+ code: ""
1181
+ });
1182
+ var CodeBlock = ({
1183
+ code,
1184
+ language,
1185
+ showLineNumbers = false,
1186
+ className,
1187
+ children,
1188
+ ...props
1189
+ }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(CodeBlockContext.Provider, { value: { code }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1190
+ "div",
1191
+ {
1192
+ className: cn(
1193
+ "relative w-full overflow-hidden rounded-md border bg-background text-foreground",
1194
+ className
1195
+ ),
1196
+ ...props,
1197
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "relative max-h-96 overflow-y-auto", children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1199
+ import_react_syntax_highlighter.Prism,
1200
+ {
1201
+ className: "overflow-hidden dark:hidden",
1202
+ codeTagProps: {
1203
+ className: "font-mono text-sm"
1204
+ },
1205
+ customStyle: {
1206
+ margin: 0,
1207
+ padding: "1rem",
1208
+ fontSize: "0.875rem",
1209
+ background: "hsl(var(--background))",
1210
+ color: "hsl(var(--foreground))"
1211
+ },
1212
+ language,
1213
+ lineNumberStyle: {
1214
+ color: "hsl(var(--muted-foreground))",
1215
+ paddingRight: "1rem",
1216
+ minWidth: "2.5rem"
1217
+ },
1218
+ showLineNumbers,
1219
+ style: import_prism.oneLight,
1220
+ children: code
1221
+ }
1222
+ ),
1223
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1224
+ import_react_syntax_highlighter.Prism,
1225
+ {
1226
+ className: "hidden overflow-hidden dark:block",
1227
+ codeTagProps: {
1228
+ className: "font-mono text-sm"
1229
+ },
1230
+ customStyle: {
1231
+ margin: 0,
1232
+ padding: "1rem",
1233
+ fontSize: "0.875rem",
1234
+ background: "hsl(var(--background))",
1235
+ color: "hsl(var(--foreground))"
1236
+ },
1237
+ language,
1238
+ lineNumberStyle: {
1239
+ color: "hsl(var(--muted-foreground))",
1240
+ paddingRight: "1rem",
1241
+ minWidth: "2.5rem"
1242
+ },
1243
+ showLineNumbers,
1244
+ style: import_prism.oneDark,
1245
+ children: code
1246
+ }
1247
+ ),
1248
+ children && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "absolute top-2 right-2 flex items-center gap-2", children })
1249
+ ] })
1250
+ }
1251
+ ) });
1252
+
1253
+ // src/components/tool.tsx
1254
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1255
+ var Tool = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1256
+ Collapsible,
1257
+ {
1258
+ className: cn("not-prose w-full rounded-md border", className),
1259
+ ...props
1260
+ }
1261
+ );
1262
+ var getStatusBadge = (status) => {
1263
+ const labels = {
1264
+ "input-streaming": "Pending",
1265
+ "input-available": "Running",
1266
+ "output-available": "Completed",
1267
+ "output-error": "Error"
1268
+ };
1269
+ const icons = {
1270
+ "input-streaming": /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react9.CircleIcon, { className: "size-4" }),
1271
+ "input-available": /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react9.ClockIcon, { className: "size-4 animate-pulse" }),
1272
+ "output-available": /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react9.CheckCircleIcon, { className: "size-4 text-green-600" }),
1273
+ "output-error": /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react9.XCircleIcon, { className: "size-4 text-red-600" })
1274
+ };
1275
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Badge, { className: "gap-1.5 rounded-full text-xs", variant: "secondary", children: [
1276
+ icons[status],
1277
+ labels[status]
1278
+ ] });
1279
+ };
1280
+ var ToolHeader = ({
1281
+ className,
1282
+ title,
1283
+ type,
1284
+ state,
1285
+ ...props
1286
+ }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1287
+ CollapsibleTrigger2,
1288
+ {
1289
+ className: cn(
1290
+ "flex w-full items-center justify-between gap-4 p-2",
1291
+ className
1292
+ ),
1293
+ ...props,
1294
+ children: [
1295
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2", children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react9.WrenchIcon, { className: "size-4 text-muted-foreground" }),
1297
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium text-sm", children: title ?? type.split("-").slice(1).join("-") }),
1298
+ getStatusBadge(state)
1299
+ ] }),
1300
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react9.ChevronDownIcon, { className: "size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })
1301
+ ]
1302
+ }
1303
+ );
1304
+ var ToolContent = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1305
+ CollapsibleContent2,
1306
+ {
1307
+ className: cn(
1308
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
1309
+ className
1310
+ ),
1311
+ ...props
1312
+ }
1313
+ );
1314
+ var ToolInput = ({ className, input, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: cn("space-y-2 overflow-hidden p-2", className), ...props, children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: "Parameters" }),
1316
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rounded-md bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CodeBlock, { code: JSON.stringify(input, null, 2), language: "json" }) })
1317
+ ] });
1318
+ var ToolOutput = ({
1319
+ className,
1320
+ output,
1321
+ errorText,
1322
+ ...props
1323
+ }) => {
1324
+ if (!(output || errorText)) {
1325
+ return null;
1326
+ }
1327
+ let Output = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { children: output });
1328
+ if (typeof output === "object" && !(0, import_react7.isValidElement)(output)) {
1329
+ Output = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CodeBlock, { code: JSON.stringify(output, null, 2), language: "json" });
1330
+ } else if (typeof output === "string") {
1331
+ Output = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CodeBlock, { code: output, language: "json" });
1332
+ }
1333
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: cn("space-y-2 p-2", className), ...props, children: [
1334
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: errorText ? "Error" : "Result" }),
1335
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1336
+ "div",
1337
+ {
1338
+ className: cn(
1339
+ "overflow-x-auto rounded-md text-xs [&_table]:w-full",
1340
+ errorText ? "bg-destructive/10 text-destructive" : "bg-muted/50 text-foreground"
1341
+ ),
1342
+ children: [
1343
+ errorText && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { children: errorText }),
1344
+ Output
1345
+ ]
1346
+ }
1347
+ )
1348
+ ] });
1349
+ };
1350
+
1351
+ // src/components/interface.tsx
1352
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1353
+ function ChatInterface({ id, initialMessages, config, onClose } = {}) {
1354
+ const themeMode = config?.theme?.mode || "light";
1355
+ const [input, setInput] = (0, import_react8.useState)("");
1356
+ const [showHistory, setShowHistory] = (0, import_react8.useState)(false);
1357
+ const [conversations, setConversations] = (0, import_react8.useState)([]);
1358
+ const [loadingHistory, setLoadingHistory] = (0, import_react8.useState)(false);
1359
+ const [historyLoaded, setHistoryLoaded] = (0, import_react8.useState)(false);
1360
+ const [searchQuery, setSearchQuery] = (0, import_react8.useState)("");
1361
+ const [uploadError, setUploadError] = (0, import_react8.useState)(null);
1362
+ (0, import_react8.useEffect)(() => {
1363
+ if (uploadError) {
1364
+ const timeoutId = setTimeout(() => setUploadError(null), 5e3);
1365
+ return () => clearTimeout(timeoutId);
1366
+ }
1367
+ }, [uploadError]);
1368
+ const [tabs, setTabs] = (0, import_react8.useState)([]);
1369
+ const [activeTabId, setActiveTabId] = (0, import_react8.useState)("");
1370
+ const [initialTabCreated, setInitialTabCreated] = (0, import_react8.useState)(false);
1371
+ const [isInitializing, setIsInitializing] = (0, import_react8.useState)(true);
1372
+ const [componentWidth, setComponentWidth] = (0, import_react8.useState)(768);
1373
+ const [isResizing, setIsResizing] = (0, import_react8.useState)(false);
1374
+ const lastSyncedTabId = (0, import_react8.useRef)("");
1375
+ const hasInitialized = (0, import_react8.useRef)(false);
1376
+ const { messages, sendMessage, status, setMessages } = (0, import_react10.useChat)({
1377
+ id: activeTabId || "temp-id",
1378
+ transport: new import_ai.DefaultChatTransport({
1379
+ api: "/api/chat",
1380
+ headers: {
1381
+ "X-User-Id": config?.userId || ""
1382
+ }
1383
+ }),
1384
+ // Throttle UI updates to 200ms to prevent hanging during streaming
1385
+ experimental_throttle: 200
1386
+ });
1387
+ const handleSubmit = async (message) => {
1388
+ const hasText = Boolean(message.text);
1389
+ const hasAttachments = Boolean(message.files?.length);
1390
+ if (!(hasText || hasAttachments)) {
1391
+ return;
1392
+ }
1393
+ let uploadedFiles = [];
1394
+ if (message.files && message.files.length > 0) {
1395
+ try {
1396
+ const uploadPromises = message.files.map(async (file) => {
1397
+ try {
1398
+ const response = await fetch(file.url);
1399
+ const blob = await response.blob();
1400
+ const fileObj = new File([blob], file.filename || "unknown", { type: file.mediaType });
1401
+ const formData = new FormData();
1402
+ formData.append("file", fileObj);
1403
+ formData.append("conversationId", activeTabId || "default");
1404
+ formData.append("userId", config?.userId || "demo-user");
1405
+ const uploadResponse = await fetch("/api/chat/upload", {
1406
+ method: "POST",
1407
+ body: formData
1408
+ });
1409
+ if (!uploadResponse.ok) {
1410
+ const errorText = await uploadResponse.text();
1411
+ console.error(`Upload failed for ${file.filename}:`, errorText);
1412
+ return null;
1413
+ }
1414
+ const uploadResult = await uploadResponse.json();
1415
+ return {
1416
+ id: file.id || "unknown",
1417
+ type: "file",
1418
+ url: uploadResult.url,
1419
+ filename: uploadResult.filename,
1420
+ mediaType: uploadResult.mediaType,
1421
+ size: uploadResult.size
1422
+ };
1423
+ } catch (error) {
1424
+ console.error(`Error uploading ${file.filename}:`, error);
1425
+ return null;
1426
+ }
1427
+ });
1428
+ const results = await Promise.all(uploadPromises);
1429
+ uploadedFiles = results.filter((result) => result !== null);
1430
+ if (uploadedFiles.length === 0) {
1431
+ const errorMsg = "All file uploads failed. Please try again.";
1432
+ setUploadError(errorMsg);
1433
+ console.error(errorMsg);
1434
+ return;
1435
+ }
1436
+ if (uploadedFiles.length < message.files.length) {
1437
+ const warnMsg = `Warning: Only ${uploadedFiles.length} of ${message.files.length} files uploaded successfully.`;
1438
+ setUploadError(warnMsg);
1439
+ console.warn(warnMsg);
1440
+ }
1441
+ } catch (error) {
1442
+ const errorMsg = "Error uploading files. Please try again.";
1443
+ setUploadError(errorMsg);
1444
+ console.error("Error in file upload process:", error);
1445
+ return;
1446
+ }
1447
+ }
1448
+ sendMessage({
1449
+ text: message.text || "Sent with attachments",
1450
+ files: uploadedFiles
1451
+ });
1452
+ const activeTab = tabs.find((tab) => tab.id === activeTabId);
1453
+ if (activeTab && activeTab.title === "New Chat" && message.text) {
1454
+ const newTitle = message.text.slice(0, 100);
1455
+ setTabs(
1456
+ (prevTabs) => prevTabs.map(
1457
+ (tab) => tab.id === activeTabId ? { ...tab, title: newTitle } : tab
1458
+ )
1459
+ );
1460
+ }
1461
+ setInput("");
1462
+ };
1463
+ const AttachButton = () => {
1464
+ const attachments = usePromptInputAttachments();
1465
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1466
+ PromptInputButton,
1467
+ {
1468
+ variant: "ghost",
1469
+ onClick: () => attachments.openFileDialog(),
1470
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.PlusIcon, { className: "size-4" })
1471
+ }
1472
+ );
1473
+ };
1474
+ const loadConversation = async (conversationId) => {
1475
+ if (!config?.userId) {
1476
+ console.log("Cannot load conversation - no userId");
1477
+ return;
1478
+ }
1479
+ try {
1480
+ const response = await fetch(`/api/chat/history/${conversationId}?userId=${config.userId}`);
1481
+ if (response.ok) {
1482
+ const data = await response.json();
1483
+ const loadedMessages = data.messages || [];
1484
+ setTimeout(() => {
1485
+ setMessages(loadedMessages);
1486
+ }, 0);
1487
+ } else if (response.status === 404) {
1488
+ console.log("Conversation not found in database yet (new chat)");
1489
+ setMessages([]);
1490
+ } else {
1491
+ console.error("Error loading messages:", response.status, response.statusText);
1492
+ }
1493
+ } catch (error) {
1494
+ console.error("Error loading conversation:", error);
1495
+ }
1496
+ };
1497
+ const generateUniqueTabId = () => {
1498
+ let newTabId;
1499
+ let attempts = 0;
1500
+ do {
1501
+ newTabId = `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1502
+ attempts++;
1503
+ } while (tabs.find((tab) => tab.id === newTabId) && attempts < 10);
1504
+ if (attempts >= 10) {
1505
+ throw new Error("Unable to generate unique tab ID");
1506
+ }
1507
+ return newTabId;
1508
+ };
1509
+ const createNewTab = (0, import_react8.useCallback)(() => {
1510
+ if (!initialTabCreated) {
1511
+ console.warn("Cannot create new tab while initializing");
1512
+ return;
1513
+ }
1514
+ const newTabId = generateUniqueTabId();
1515
+ setTabs((prevTabs) => {
1516
+ const existingTab = prevTabs.find((tab) => tab.id === newTabId);
1517
+ if (existingTab) {
1518
+ console.warn("Tab with ID already exists:", newTabId);
1519
+ return prevTabs;
1520
+ }
1521
+ const newTab = {
1522
+ id: newTabId,
1523
+ title: "New Chat",
1524
+ isActive: true
1525
+ };
1526
+ const updatedTabs = prevTabs.map((tab) => ({
1527
+ ...tab,
1528
+ isActive: false
1529
+ }));
1530
+ return [...updatedTabs, newTab];
1531
+ });
1532
+ setActiveTabId(newTabId);
1533
+ setMessages([]);
1534
+ setInput("");
1535
+ }, [initialTabCreated]);
1536
+ const startNewConversation = (0, import_react8.useCallback)(() => {
1537
+ createNewTab();
1538
+ }, [createNewTab]);
1539
+ (0, import_react8.useEffect)(() => {
1540
+ return () => {
1541
+ };
1542
+ }, []);
1543
+ const switchToTab = async (tabId) => {
1544
+ const targetTab = tabs.find((tab) => tab.id === tabId);
1545
+ if (!targetTab) return;
1546
+ setTabs(
1547
+ (prevTabs) => prevTabs.map((tab) => ({
1548
+ ...tab,
1549
+ isActive: tab.id === tabId
1550
+ }))
1551
+ );
1552
+ setActiveTabId(tabId);
1553
+ await loadConversation(tabId);
1554
+ };
1555
+ const closeTab = (tabId) => {
1556
+ if (tabs.length <= 1) return;
1557
+ const filteredTabs = tabs.filter((tab) => tab.id !== tabId);
1558
+ if (tabId === activeTabId && filteredTabs.length > 0) {
1559
+ const newActiveTab = filteredTabs[0];
1560
+ setTabs(filteredTabs.map((tab) => ({
1561
+ ...tab,
1562
+ isActive: tab.id === newActiveTab.id
1563
+ })));
1564
+ switchToTab(newActiveTab.id);
1565
+ } else {
1566
+ setTabs(filteredTabs);
1567
+ }
1568
+ if (filteredTabs.length > 0) {
1569
+ localStorage.setItem("chat-tabs", JSON.stringify(filteredTabs));
1570
+ if (tabId === activeTabId) {
1571
+ const newActiveTab = filteredTabs[0];
1572
+ localStorage.setItem("active-tab-id", newActiveTab.id);
1573
+ }
1574
+ }
1575
+ };
1576
+ const fetchConversations = async () => {
1577
+ if (historyLoaded) return;
1578
+ if (!config?.userId) {
1579
+ return;
1580
+ }
1581
+ setLoadingHistory(true);
1582
+ try {
1583
+ const response = await fetch(`/api/chat/history?userId=${config.userId}`);
1584
+ if (response.ok) {
1585
+ const data = await response.json();
1586
+ setConversations(data.conversations || []);
1587
+ setHistoryLoaded(true);
1588
+ } else {
1589
+ console.error("[ChatInterface] Failed to fetch chat history, status:", response.status);
1590
+ const errorText = await response.text();
1591
+ console.error("[ChatInterface] Error response:", errorText);
1592
+ }
1593
+ } catch (error) {
1594
+ console.error("[ChatInterface] Error fetching chat history:", error);
1595
+ } finally {
1596
+ setLoadingHistory(false);
1597
+ }
1598
+ };
1599
+ (0, import_react8.useEffect)(() => {
1600
+ if (showHistory && !historyLoaded && config?.userId) {
1601
+ fetchConversations();
1602
+ }
1603
+ }, [showHistory, historyLoaded, config?.userId]);
1604
+ (0, import_react8.useEffect)(() => {
1605
+ if (!historyLoaded && config?.userId) {
1606
+ fetchConversations();
1607
+ }
1608
+ }, [historyLoaded, config?.userId]);
1609
+ (0, import_react8.useEffect)(() => {
1610
+ if (tabs.length > 0) {
1611
+ const timeoutId = setTimeout(() => {
1612
+ localStorage.setItem("chat-tabs", JSON.stringify(tabs));
1613
+ localStorage.setItem("active-tab-id", activeTabId);
1614
+ }, 500);
1615
+ return () => clearTimeout(timeoutId);
1616
+ }
1617
+ }, [tabs, activeTabId]);
1618
+ (0, import_react8.useEffect)(() => {
1619
+ if (hasInitialized.current) return;
1620
+ const loadInitialTabs = () => {
1621
+ try {
1622
+ const savedTabs = localStorage.getItem("chat-tabs");
1623
+ const savedActiveTabId = localStorage.getItem("active-tab-id");
1624
+ if (savedTabs && savedTabs !== "[]") {
1625
+ const parsedTabs = JSON.parse(savedTabs);
1626
+ setTabs(parsedTabs);
1627
+ const activeId = savedActiveTabId || parsedTabs[0]?.id;
1628
+ setActiveTabId(activeId);
1629
+ setInitialTabCreated(true);
1630
+ } else if (!initialTabCreated && tabs.length === 0) {
1631
+ const initialTabId = `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1632
+ const currentTab = {
1633
+ id: initialTabId,
1634
+ title: "New Chat",
1635
+ isActive: true
1636
+ };
1637
+ setTabs([currentTab]);
1638
+ setActiveTabId(initialTabId);
1639
+ setInitialTabCreated(true);
1640
+ }
1641
+ } finally {
1642
+ setIsInitializing(false);
1643
+ }
1644
+ };
1645
+ loadInitialTabs();
1646
+ hasInitialized.current = true;
1647
+ }, []);
1648
+ const hasLoadedInitialMessages = (0, import_react8.useRef)(false);
1649
+ (0, import_react8.useEffect)(() => {
1650
+ if (hasLoadedInitialMessages.current) return;
1651
+ if (!config?.userId) return;
1652
+ if (!activeTabId) return;
1653
+ if (isInitializing) return;
1654
+ loadConversation(activeTabId);
1655
+ hasLoadedInitialMessages.current = true;
1656
+ }, [config?.userId, activeTabId, isInitializing]);
1657
+ (0, import_react8.useEffect)(() => {
1658
+ if (isInitializing) return;
1659
+ if (activeTabId && tabs.length > 0 && activeTabId !== lastSyncedTabId.current) {
1660
+ lastSyncedTabId.current = activeTabId;
1661
+ setInput("");
1662
+ }
1663
+ }, [activeTabId, isInitializing, tabs.length]);
1664
+ const groupedConversations = (0, import_react8.useMemo)(() => {
1665
+ const filtered = conversations.filter(
1666
+ (conv) => searchQuery === "" || conv.title.toLowerCase().includes(searchQuery.toLowerCase())
1667
+ );
1668
+ const groups = {};
1669
+ const now = /* @__PURE__ */ new Date();
1670
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
1671
+ const yesterday = new Date(today);
1672
+ yesterday.setDate(yesterday.getDate() - 1);
1673
+ filtered.forEach((conv) => {
1674
+ const convDate = new Date(conv.updated_at);
1675
+ const diffTime = now.getTime() - convDate.getTime();
1676
+ const diffDays = Math.floor(diffTime / (1e3 * 60 * 60 * 24));
1677
+ const diffWeeks = Math.floor(diffDays / 7);
1678
+ const diffMonths = Math.floor(diffDays / 30);
1679
+ let groupKey;
1680
+ if (convDate >= today) {
1681
+ groupKey = "Today";
1682
+ } else if (convDate >= yesterday) {
1683
+ groupKey = "Yesterday";
1684
+ } else if (diffDays <= 7) {
1685
+ groupKey = `${diffDays}d ago`;
1686
+ } else if (diffWeeks <= 4) {
1687
+ groupKey = `${diffWeeks}w ago`;
1688
+ } else if (diffMonths <= 12) {
1689
+ groupKey = `${diffMonths}mo ago`;
1690
+ } else {
1691
+ const diffYears = Math.floor(diffMonths / 12);
1692
+ groupKey = `${diffYears}y ago`;
1693
+ }
1694
+ if (!groups[groupKey]) {
1695
+ groups[groupKey] = [];
1696
+ }
1697
+ groups[groupKey].push(conv);
1698
+ });
1699
+ const sortedGroups = Object.entries(groups).sort((a, b) => {
1700
+ const order = ["Today", "Yesterday"];
1701
+ const aIndex = order.indexOf(a[0]);
1702
+ const bIndex = order.indexOf(b[0]);
1703
+ if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex;
1704
+ if (aIndex !== -1) return -1;
1705
+ if (bIndex !== -1) return 1;
1706
+ const aMatch = a[0].match(/(\d+)([dw]|mo|y)/);
1707
+ const bMatch = b[0].match(/(\d+)([dw]|mo|y)/);
1708
+ if (aMatch && bMatch) {
1709
+ const aNum = parseInt(aMatch[1]);
1710
+ const bNum = parseInt(bMatch[1]);
1711
+ const aUnit = aMatch[2];
1712
+ const bUnit = bMatch[2];
1713
+ const unitToDays = { "d": 1, "w": 7, "mo": 30, "y": 365 };
1714
+ const aDays = aNum * unitToDays[aUnit];
1715
+ const bDays = bNum * unitToDays[bUnit];
1716
+ return aDays - bDays;
1717
+ }
1718
+ return 0;
1719
+ });
1720
+ return sortedGroups;
1721
+ }, [conversations, searchQuery]);
1722
+ const renderMessages = () => messages.map((message, index) => {
1723
+ const sourceParts = message.parts?.filter((part) => part.type === "source-url") || [];
1724
+ const fileParts = message.parts?.filter((part) => part.type === "file") || [];
1725
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: index > 0 ? "mt-6" : "", children: [
1726
+ message.role === "assistant" && sourceParts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Sources, { children: [
1727
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SourcesTrigger, { count: sourceParts.length }),
1728
+ sourceParts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SourcesContent, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1729
+ Source,
1730
+ {
1731
+ href: part.url,
1732
+ title: part.url
1733
+ },
1734
+ `${message.id}-${i}`
1735
+ ) }, `${message.id}-${i}`))
1736
+ ] }),
1737
+ fileParts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: cn(
1738
+ "flex mb-1",
1739
+ message.role === "user" ? "justify-end" : "justify-start"
1740
+ ), children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1741
+ MessageAttachments,
1742
+ {
1743
+ attachments: fileParts.map((part) => ({
1744
+ filename: part.filename || "unknown",
1745
+ mediaType: part.mediaType,
1746
+ url: part.url,
1747
+ size: part.size || 0
1748
+ }))
1749
+ }
1750
+ ) }),
1751
+ message.parts ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-2", children: message.parts.map((part, i) => {
1752
+ switch (part.type) {
1753
+ case "text":
1754
+ const isTextStreaming = status === "streaming" && i === message.parts.length - 1 && message.id === messages.at(-1)?.id;
1755
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Message, { from: message.role, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MessageContent, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Response, { isStreaming: isTextStreaming, children: part.text }) }) }) }, `${message.id}-${i}`);
1756
+ case "reasoning":
1757
+ const isCurrentlyStreaming = status === "streaming" && i === message.parts.length - 1 && message.id === messages.at(-1)?.id;
1758
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1759
+ Reasoning,
1760
+ {
1761
+ className: "w-full",
1762
+ isStreaming: isCurrentlyStreaming,
1763
+ defaultOpen: false,
1764
+ open: isCurrentlyStreaming ? true : void 0,
1765
+ children: [
1766
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReasoningTrigger, {}),
1767
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReasoningContent, { children: part.text })
1768
+ ]
1769
+ },
1770
+ `${message.id}-${i}`
1771
+ );
1772
+ default:
1773
+ if (part.type.startsWith("tool-") || part.type === "dynamic-tool") {
1774
+ const toolPart = part;
1775
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Tool, { children: [
1776
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ToolHeader, { type: part.type, state: toolPart.state }),
1777
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(ToolContent, { children: [
1778
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ToolInput, { input: toolPart.input }),
1779
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ToolOutput, { output: toolPart.output, errorText: toolPart.errorText })
1780
+ ] })
1781
+ ] }, `${message.id}-${i}`);
1782
+ }
1783
+ return null;
1784
+ }
1785
+ }) }) : (
1786
+ /* Handle standard AI SDK messages with content or text property */
1787
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Message, { from: message.role, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MessageContent, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Response, { children: message.content || message.text }) }) }) }, `${message.id}-content`)
1788
+ )
1789
+ ] }, message.id);
1790
+ });
1791
+ const handleSelectConversation = async (selectedConversationId, conversationTitle) => {
1792
+ if (!config?.userId) return;
1793
+ try {
1794
+ const existingTab = tabs.find((tab) => tab.id === selectedConversationId);
1795
+ if (existingTab) {
1796
+ switchToTab(selectedConversationId);
1797
+ setShowHistory(false);
1798
+ return;
1799
+ }
1800
+ const newTab = {
1801
+ id: selectedConversationId,
1802
+ title: conversationTitle,
1803
+ isActive: true
1804
+ };
1805
+ setTabs((prevTabs) => {
1806
+ const updatedTabs = prevTabs.map((tab) => ({
1807
+ ...tab,
1808
+ isActive: false
1809
+ }));
1810
+ return [...updatedTabs, newTab];
1811
+ });
1812
+ setActiveTabId(selectedConversationId);
1813
+ await loadConversation(selectedConversationId);
1814
+ setShowHistory(false);
1815
+ } catch (error) {
1816
+ console.error("Error loading conversation:", error);
1817
+ }
1818
+ };
1819
+ const dropdownRef = (0, import_react8.useRef)(null);
1820
+ (0, import_react8.useEffect)(() => {
1821
+ const handleClickOutside = (event) => {
1822
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
1823
+ setShowHistory(false);
1824
+ }
1825
+ };
1826
+ if (showHistory) {
1827
+ document.addEventListener("mousedown", handleClickOutside);
1828
+ }
1829
+ return () => {
1830
+ document.removeEventListener("mousedown", handleClickOutside);
1831
+ };
1832
+ }, [showHistory]);
1833
+ (0, import_react8.useEffect)(() => {
1834
+ if (!isResizing) return;
1835
+ const handleMouseMove = (e) => {
1836
+ const newWidth = window.innerWidth - e.clientX;
1837
+ const minWidth = 300;
1838
+ const maxWidth = window.innerWidth * 0.8;
1839
+ setComponentWidth(Math.max(minWidth, Math.min(maxWidth, newWidth)));
1840
+ };
1841
+ const handleMouseUp = () => {
1842
+ setIsResizing(false);
1843
+ };
1844
+ document.addEventListener("mousemove", handleMouseMove);
1845
+ document.addEventListener("mouseup", handleMouseUp);
1846
+ return () => {
1847
+ document.removeEventListener("mousemove", handleMouseMove);
1848
+ document.removeEventListener("mouseup", handleMouseUp);
1849
+ };
1850
+ }, [isResizing]);
1851
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: cn("w-full h-full flex flex-col bg-white dark:bg-gray-900 overflow-hidden ring-1 ring-black/[0.02] dark:ring-white/[0.03]", themeMode === "dark" && "dark"), children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1852
+ "div",
1853
+ {
1854
+ className: cn(
1855
+ "flex flex-col h-full w-full overflow-hidden relative chat-widget-container",
1856
+ themeMode === "dark" && "dark"
1857
+ ),
1858
+ children: [
1859
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2 px-3 py-2 border-b backdrop-blur-sm relative z-20", style: {
1860
+ borderColor: themeMode === "dark" ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.08)",
1861
+ backgroundColor: themeMode === "dark" ? "rgba(37,37,37,0.8)" : "rgba(255,255,255,0.8)"
1862
+ }, children: [
1863
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center gap-1 flex-1 min-w-0 overflow-x-auto scrollbar-hide py-0.5 scroll-smooth", children: tabs.map((tab, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1864
+ "div",
1865
+ {
1866
+ className: "relative flex items-center gap-1.5 px-3 py-1.5 rounded-lg cursor-pointer transition-all duration-150 group flex-shrink-0 min-w-0",
1867
+ style: {
1868
+ backgroundColor: tab.isActive ? themeMode === "dark" ? "#444444" : "#f5f5f5" : "transparent",
1869
+ color: tab.isActive ? themeMode === "dark" ? "#e5e5e5" : "#171717" : themeMode === "dark" ? "#999999" : "#737373"
1870
+ },
1871
+ onMouseEnter: (e) => {
1872
+ if (!tab.isActive) {
1873
+ e.currentTarget.style.backgroundColor = themeMode === "dark" ? "rgba(68,68,68,0.5)" : "rgba(245,245,245,0.5)";
1874
+ }
1875
+ },
1876
+ onMouseLeave: (e) => {
1877
+ if (!tab.isActive) {
1878
+ e.currentTarget.style.backgroundColor = "transparent";
1879
+ }
1880
+ },
1881
+ onClick: (e) => {
1882
+ e.stopPropagation();
1883
+ switchToTab(tab.id);
1884
+ },
1885
+ children: [
1886
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate max-w-28 text-[13px] font-medium transition-colors", children: tab.title }),
1887
+ tabs.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1888
+ "button",
1889
+ {
1890
+ onClick: (e) => {
1891
+ e.stopPropagation();
1892
+ closeTab(tab.id);
1893
+ },
1894
+ className: "rounded-lg p-1 transition-all duration-150 flex-shrink-0 -mr-1",
1895
+ style: {
1896
+ opacity: tab.isActive ? 0.6 : 0
1897
+ },
1898
+ onMouseEnter: (e) => {
1899
+ e.currentTarget.style.opacity = "1";
1900
+ e.currentTarget.style.backgroundColor = themeMode === "dark" ? "#555555" : "#e5e5e5";
1901
+ },
1902
+ onMouseLeave: (e) => {
1903
+ e.currentTarget.style.opacity = tab.isActive ? "0.6" : "0";
1904
+ e.currentTarget.style.backgroundColor = "transparent";
1905
+ },
1906
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.XIcon, { className: "h-3 w-3", strokeWidth: 2.5 })
1907
+ }
1908
+ )
1909
+ ]
1910
+ },
1911
+ tab.id
1912
+ )) }),
1913
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
1914
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1915
+ "button",
1916
+ {
1917
+ onClick: createNewTab,
1918
+ className: "flex items-center justify-center w-7 h-7 rounded-lg transition-all duration-150",
1919
+ style: {
1920
+ color: themeMode === "dark" ? "#999999" : "#737373"
1921
+ },
1922
+ onMouseEnter: (e) => {
1923
+ e.currentTarget.style.color = themeMode === "dark" ? "#e5e5e5" : "#171717";
1924
+ e.currentTarget.style.backgroundColor = themeMode === "dark" ? "#444444" : "#f5f5f5";
1925
+ },
1926
+ onMouseLeave: (e) => {
1927
+ e.currentTarget.style.color = themeMode === "dark" ? "#999999" : "#737373";
1928
+ e.currentTarget.style.backgroundColor = "transparent";
1929
+ },
1930
+ title: "New Chat",
1931
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.PlusIcon, { className: "h-4 w-4", strokeWidth: 2 })
1932
+ }
1933
+ ),
1934
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "relative", ref: dropdownRef, children: [
1935
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1936
+ "button",
1937
+ {
1938
+ onClick: () => setShowHistory(!showHistory),
1939
+ className: "flex items-center justify-center w-7 h-7 rounded-lg transition-all duration-150",
1940
+ style: {
1941
+ color: showHistory ? themeMode === "dark" ? "#e5e5e5" : "#171717" : themeMode === "dark" ? "#999999" : "#737373",
1942
+ backgroundColor: showHistory ? themeMode === "dark" ? "#444444" : "#f5f5f5" : "transparent"
1943
+ },
1944
+ onMouseEnter: (e) => {
1945
+ if (!showHistory) {
1946
+ e.currentTarget.style.color = themeMode === "dark" ? "#e5e5e5" : "#171717";
1947
+ e.currentTarget.style.backgroundColor = themeMode === "dark" ? "#444444" : "#f5f5f5";
1948
+ }
1949
+ },
1950
+ onMouseLeave: (e) => {
1951
+ if (!showHistory) {
1952
+ e.currentTarget.style.color = themeMode === "dark" ? "#999999" : "#737373";
1953
+ e.currentTarget.style.backgroundColor = "transparent";
1954
+ }
1955
+ },
1956
+ title: "Chat History",
1957
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.HistoryIcon, { className: "h-4 w-4", strokeWidth: 2 })
1958
+ }
1959
+ ),
1960
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "absolute right-0 top-full mt-1.5 w-72 rounded-xl shadow-[0_4px_24px_rgba(0,0,0,0.08)] dark:shadow-[0_4px_24px_rgba(0,0,0,0.3)] z-50 animate-in fade-in slide-in-from-top-1 duration-150 overflow-hidden", style: {
1961
+ backgroundColor: themeMode === "dark" ? "#252525" : "#ffffff",
1962
+ border: `1px solid ${themeMode === "dark" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)"}`
1963
+ }, children: [
1964
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "p-2.5 border-b", style: {
1965
+ borderColor: themeMode === "dark" ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.08)",
1966
+ backgroundColor: themeMode === "dark" ? "rgba(0,0,0,0.2)" : "rgba(0,0,0,0.02)"
1967
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "relative", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.SearchIcon, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5", style: {
1969
+ color: themeMode === "dark" ? "#666666" : "#999999"
1970
+ }, strokeWidth: 2 }),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1972
+ "input",
1973
+ {
1974
+ type: "text",
1975
+ placeholder: "Search",
1976
+ value: searchQuery,
1977
+ onChange: (e) => setSearchQuery(e.target.value),
1978
+ className: "w-full h-7 pl-8 pr-2.5 text-[13px] rounded-lg focus:outline-none transition-all",
1979
+ style: {
1980
+ backgroundColor: themeMode === "dark" ? "#1a1a1a" : "#ffffff",
1981
+ border: `1px solid ${themeMode === "dark" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)"}`,
1982
+ color: themeMode === "dark" ? "#e5e5e5" : "#171717"
1983
+ }
1984
+ }
1985
+ )
1986
+ ] }) }),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "max-h-[300px] overflow-y-auto ai-assistant-scrollbar", children: loadingHistory ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-[13px]", style: { color: themeMode === "dark" ? "#999999" : "#737373" }, children: "Loading..." }) }) : conversations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 px-4 text-center", children: [
1988
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "w-12 h-12 rounded-full flex items-center justify-center mb-3", style: {
1989
+ backgroundColor: themeMode === "dark" ? "#333333" : "#f5f5f5"
1990
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.MessageSquareIcon, { className: "h-5 w-5", style: { color: themeMode === "dark" ? "#666666" : "#a3a3a3" }, strokeWidth: 2 }) }),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[13px] font-medium mb-0.5", style: { color: themeMode === "dark" ? "#e5e5e5" : "#171717" }, children: "No Conversations" }),
1992
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[12px]", style: { color: themeMode === "dark" ? "#999999" : "#737373" }, children: "Start a new chat to begin" })
1993
+ ] }) : groupedConversations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 px-4 text-center", children: [
1994
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "w-12 h-12 rounded-full flex items-center justify-center mb-3", style: {
1995
+ backgroundColor: themeMode === "dark" ? "#333333" : "#f5f5f5"
1996
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.SearchIcon, { className: "h-5 w-5", style: { color: themeMode === "dark" ? "#666666" : "#a3a3a3" }, strokeWidth: 2 }) }),
1997
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[13px] font-medium mb-0.5", style: { color: themeMode === "dark" ? "#e5e5e5" : "#171717" }, children: "No Results" }),
1998
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[12px]", style: { color: themeMode === "dark" ? "#999999" : "#737373" }, children: "Try a different search" })
1999
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "py-0.5", children: groupedConversations.map(([groupName, groupConversations]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mb-0.5", children: [
2000
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "px-2.5 py-1 sticky top-0 backdrop-blur-sm z-10", style: {
2001
+ backgroundColor: themeMode === "dark" ? "rgba(37,37,37,0.95)" : "rgba(255,255,255,0.95)"
2002
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { className: "text-[10px] font-semibold uppercase tracking-wide", style: { color: themeMode === "dark" ? "#999999" : "#737373" }, children: groupName }) }),
2003
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "px-1 space-y-0.5", children: groupConversations.map((conversation) => {
2004
+ const isActiveConversation = activeTabId === conversation.id;
2005
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2006
+ "button",
2007
+ {
2008
+ className: "w-full px-2 py-1 rounded-md transition-all duration-150 text-left group relative",
2009
+ style: {
2010
+ backgroundColor: isActiveConversation ? themeMode === "dark" ? "#333333" : "#f5f5f5" : "transparent"
2011
+ },
2012
+ onMouseEnter: (e) => {
2013
+ if (!isActiveConversation) {
2014
+ e.currentTarget.style.backgroundColor = themeMode === "dark" ? "rgba(51,51,51,0.5)" : "rgba(245,245,245,0.5)";
2015
+ }
2016
+ },
2017
+ onMouseLeave: (e) => {
2018
+ if (!isActiveConversation) {
2019
+ e.currentTarget.style.backgroundColor = "transparent";
2020
+ }
2021
+ },
2022
+ onClick: () => handleSelectConversation(conversation.id, conversation.title),
2023
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2024
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[12px] line-clamp-1 transition-colors leading-tight", style: {
2025
+ fontWeight: isActiveConversation ? 500 : 400,
2026
+ color: isActiveConversation ? themeMode === "dark" ? "#e5e5e5" : "#171717" : themeMode === "dark" ? "#cccccc" : "#404040"
2027
+ }, children: conversation.title }) }),
2028
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2029
+ import_lucide_react10.ChevronRightIcon,
2030
+ {
2031
+ className: "h-3 w-3 transition-all duration-150 flex-shrink-0",
2032
+ style: {
2033
+ color: themeMode === "dark" ? "#666666" : "#a3a3a3",
2034
+ opacity: isActiveConversation ? 1 : 0
2035
+ },
2036
+ strokeWidth: 2
2037
+ }
2038
+ )
2039
+ ] })
2040
+ },
2041
+ conversation.id
2042
+ );
2043
+ }) })
2044
+ ] }, groupName)) }) })
2045
+ ] })
2046
+ ] }),
2047
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2048
+ "button",
2049
+ {
2050
+ onClick: onClose,
2051
+ className: "flex items-center justify-center w-7 h-7 rounded-lg transition-all duration-150",
2052
+ style: {
2053
+ color: themeMode === "dark" ? "#999999" : "#737373"
2054
+ },
2055
+ onMouseEnter: (e) => {
2056
+ e.currentTarget.style.color = themeMode === "dark" ? "#e5e5e5" : "#171717";
2057
+ e.currentTarget.style.backgroundColor = themeMode === "dark" ? "#444444" : "#f5f5f5";
2058
+ },
2059
+ onMouseLeave: (e) => {
2060
+ e.currentTarget.style.color = themeMode === "dark" ? "#999999" : "#737373";
2061
+ e.currentTarget.style.backgroundColor = "transparent";
2062
+ },
2063
+ title: "Close Chat",
2064
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react10.XIcon, { className: "h-4 w-4", strokeWidth: 2 })
2065
+ }
2066
+ )
2067
+ ] })
2068
+ ] }),
2069
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Conversation, { className: "flex-1 max-w-full ai-assistant-scrollbar", children: [
2070
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(ConversationContent, { className: "max-w-[96%] mx-auto py-6", children: [
2071
+ renderMessages(),
2072
+ status === "submitted" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Message, { from: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MessageContent, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Loader, { size: 16 }) }) }) })
2073
+ ] }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ConversationScrollButton, {})
2075
+ ] }),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "px-5 pb-5", children: [
2077
+ uploadError && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mb-3 px-4 py-3 bg-red-50 dark:bg-red-900/20 border border-red-200/60 dark:border-red-800/60 rounded-2xl text-sm text-red-700 dark:text-red-400 shadow-sm", children: uploadError }),
2078
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(PromptInput, { onSubmit: handleSubmit, globalDrop: true, multiple: true, accept: "image/*", children: [
2079
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(PromptInputBody, { children: [
2080
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PromptInputAttachments, { children: (attachment) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PromptInputAttachment, { data: attachment }) }),
2081
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2082
+ PromptInputTextarea,
2083
+ {
2084
+ onChange: (e) => setInput(e.target.value),
2085
+ value: input
2086
+ }
2087
+ )
2088
+ ] }),
2089
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(PromptInputToolbar, { children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PromptInputTools, { children: config?.features?.fileUpload !== false && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AttachButton, {}) }),
2091
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PromptInputSubmit, { disabled: !input, status })
2092
+ ] })
2093
+ ] })
2094
+ ] })
2095
+ ]
2096
+ }
2097
+ ) });
2098
+ }
2099
+
2100
+ // src/ChatWidget.tsx
2101
+ var import_lucide_react11 = require("lucide-react");
2102
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2103
+ function ChatWidget({
2104
+ userId,
2105
+ conversationId,
2106
+ initialMessages,
2107
+ className,
2108
+ model,
2109
+ systemPrompt,
2110
+ temperature,
2111
+ theme,
2112
+ features,
2113
+ display
2114
+ }) {
2115
+ const initialWidth = display?.width || "30vw";
2116
+ const showToggleButton = display?.showToggleButton !== false;
2117
+ const [isOpen, setIsOpen] = (0, import_react11.useState)(display?.defaultOpen || false);
2118
+ const [width, setWidth] = (0, import_react11.useState)(() => {
2119
+ const value = parseInt(initialWidth);
2120
+ if (initialWidth.includes("vw")) {
2121
+ return value / 100 * window.innerWidth;
2122
+ }
2123
+ return value || window.innerWidth * 0.3;
2124
+ });
2125
+ const isResizing = (0, import_react11.useRef)(false);
2126
+ const handleMouseDown = (0, import_react11.useCallback)((e) => {
2127
+ e.preventDefault();
2128
+ isResizing.current = true;
2129
+ document.body.style.cursor = "ew-resize";
2130
+ document.body.style.userSelect = "none";
2131
+ }, []);
2132
+ (0, import_react11.useEffect)(() => {
2133
+ const getConstraints = () => ({
2134
+ minWidth: window.innerWidth * 0.2,
2135
+ maxWidth: window.innerWidth * 0.6
2136
+ });
2137
+ const handleMouseMove = (e) => {
2138
+ if (!isResizing.current) return;
2139
+ const { minWidth, maxWidth } = getConstraints();
2140
+ const newWidth = window.innerWidth - e.clientX;
2141
+ setWidth(Math.min(maxWidth, Math.max(minWidth, newWidth)));
2142
+ };
2143
+ const handleMouseUp = () => {
2144
+ if (isResizing.current) {
2145
+ isResizing.current = false;
2146
+ document.body.style.cursor = "";
2147
+ document.body.style.userSelect = "";
2148
+ }
2149
+ };
2150
+ const handleWindowResize = () => {
2151
+ const { minWidth, maxWidth } = getConstraints();
2152
+ setWidth((prev) => Math.min(maxWidth, Math.max(minWidth, prev)));
2153
+ };
2154
+ document.addEventListener("mousemove", handleMouseMove);
2155
+ document.addEventListener("mouseup", handleMouseUp);
2156
+ window.addEventListener("resize", handleWindowResize);
2157
+ return () => {
2158
+ document.removeEventListener("mousemove", handleMouseMove);
2159
+ document.removeEventListener("mouseup", handleMouseUp);
2160
+ window.removeEventListener("resize", handleWindowResize);
2161
+ };
2162
+ }, []);
2163
+ const config = (0, import_react11.useMemo)(() => ({
2164
+ userId,
2165
+ model,
2166
+ systemPrompt,
2167
+ temperature,
2168
+ theme,
2169
+ features
2170
+ }), [userId, model, systemPrompt, temperature, theme, features]);
2171
+ const togglePosition = display?.toggleButtonPosition || { bottom: "24px", right: "24px" };
2172
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2173
+ showToggleButton && !isOpen && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2174
+ "button",
2175
+ {
2176
+ onClick: () => setIsOpen(true),
2177
+ className: "fixed z-50 rounded-full bg-primary text-primary-foreground shadow-lg hover:opacity-90 transition-all p-4",
2178
+ style: togglePosition,
2179
+ "aria-label": "Open chat",
2180
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react11.MessageCircle, { className: "h-6 w-6" })
2181
+ }
2182
+ ),
2183
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2184
+ "div",
2185
+ {
2186
+ className: `fixed top-0 right-0 h-screen z-50 border-l border-gray-200 dark:border-gray-800 ${className || ""}`,
2187
+ style: {
2188
+ animation: "chat-slide-in-right 0.3s ease-out forwards",
2189
+ width: `${width}px`
2190
+ },
2191
+ children: [
2192
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2193
+ "div",
2194
+ {
2195
+ onMouseDown: handleMouseDown,
2196
+ className: "absolute left-0 top-0 h-full w-1 cursor-ew-resize hover:bg-primary/20 active:bg-primary/30 transition-colors z-10"
2197
+ }
2198
+ ),
2199
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "w-full h-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2200
+ ChatInterface,
2201
+ {
2202
+ id: conversationId,
2203
+ initialMessages,
2204
+ config,
2205
+ onClose: () => setIsOpen(false)
2206
+ }
2207
+ ) })
2208
+ ]
2209
+ }
2210
+ )
2211
+ ] });
2212
+ }
2213
+ var ChatWidget_default = ChatWidget;
2214
+
2215
+ // src/hooks/use-chat-theme.ts
2216
+ var import_react12 = require("react");
2217
+
2218
+ // src/utils/models.ts
2219
+ var MODELS = [
2220
+ {
2221
+ name: "GPT-5 Nano",
2222
+ value: "openai/gpt-5-nano"
2223
+ },
2224
+ // Anthropic models
2225
+ {
2226
+ name: "Claude Sonnet 4.5",
2227
+ value: "anthropic/claude-sonnet-4-5"
2228
+ },
2229
+ {
2230
+ name: "Claude Sonnet 4",
2231
+ value: "anthropic/claude-sonnet-4-0"
2232
+ },
2233
+ {
2234
+ name: "Claude Haiku 3.5",
2235
+ value: "anthropic/claude-3-5-haiku-latest"
2236
+ },
2237
+ // OpenAI models
2238
+ {
2239
+ name: "GPT-5",
2240
+ value: "openai/gpt-5"
2241
+ },
2242
+ {
2243
+ name: "GPT-OSS-120B",
2244
+ value: "openai/gpt-oss-120b"
2245
+ },
2246
+ {
2247
+ name: "GPT 4o",
2248
+ value: "openai/gpt-4o"
2249
+ },
2250
+ // Google models
2251
+ {
2252
+ name: "Gemini 2.5 Flash Lite",
2253
+ value: "google/gemini-2.5-flash-lite"
2254
+ },
2255
+ {
2256
+ name: "Gemini 2.5 Flash",
2257
+ value: "google/gemini-2.5-flash"
2258
+ },
2259
+ {
2260
+ name: "Gemini 2.5 Pro",
2261
+ value: "google/gemini-2.5-pro"
2262
+ }
2263
+ ];
2264
+ var DEFAULT_MODEL = MODELS[0].value;
2265
+
2266
+ // src/hooks/use-chat-theme.ts
2267
+ function hexToHSL(hex) {
2268
+ hex = hex.replace("#", "");
2269
+ const r = parseInt(hex.substring(0, 2), 16) / 255;
2270
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
2271
+ const b = parseInt(hex.substring(4, 6), 16) / 255;
2272
+ const max = Math.max(r, g, b);
2273
+ const min = Math.min(r, g, b);
2274
+ let h = 0;
2275
+ let s = 0;
2276
+ const l = (max + min) / 2;
2277
+ if (max !== min) {
2278
+ const d = max - min;
2279
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
2280
+ switch (max) {
2281
+ case r:
2282
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
2283
+ break;
2284
+ case g:
2285
+ h = ((b - r) / d + 2) / 6;
2286
+ break;
2287
+ case b:
2288
+ h = ((r - g) / d + 4) / 6;
2289
+ break;
2290
+ }
2291
+ }
2292
+ return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
2293
+ }
2294
+ var defaultTheme = {
2295
+ // Light mode
2296
+ lightPrimary: "#3b82f6",
2297
+ // Blue
2298
+ lightSecondary: "#f5f5f5",
2299
+ // Light gray
2300
+ lightText: "#0a0a0a",
2301
+ // Near black
2302
+ // Dark mode
2303
+ darkPrimary: "#3b82f6",
2304
+ // Blue (same as light)
2305
+ darkSecondary: "#262626",
2306
+ // Dark gray
2307
+ darkText: "#ffffff",
2308
+ // White
2309
+ // Typography
2310
+ fontFamily: "system-ui",
2311
+ fontSize: 14
2312
+ };
2313
+ var fontOptions = [
2314
+ { value: "system-ui", label: "System Default" },
2315
+ { value: "Inter, sans-serif", label: "Inter" },
2316
+ { value: "Roboto, sans-serif", label: "Roboto" },
2317
+ { value: "Open Sans, sans-serif", label: "Open Sans" },
2318
+ { value: "Lato, sans-serif", label: "Lato" },
2319
+ { value: "Poppins, sans-serif", label: "Poppins" },
2320
+ { value: "Montserrat, sans-serif", label: "Montserrat" },
2321
+ { value: "Georgia, serif", label: "Georgia" },
2322
+ { value: "ui-monospace, monospace", label: "Monospace" }
2323
+ ];
2324
+ var defaultConversationStarters = [
2325
+ { text: "How can I help you today?", enabled: true },
2326
+ { text: "What features does this product offer?", enabled: true },
2327
+ { text: "Tell me about your capabilities", enabled: true }
2328
+ ];
2329
+ var defaultModel = DEFAULT_MODEL;
2330
+ var defaultSystemPrompt = "You are a helpful AI assistant.";
2331
+ var defaultTemperature = 0.7;
2332
+ var defaultThemeMode = "light";
2333
+ function useChatTheme() {
2334
+ const [theme, setTheme] = (0, import_react12.useState)(defaultTheme);
2335
+ const [conversationStarters, setConversationStarters] = (0, import_react12.useState)(defaultConversationStarters);
2336
+ const [model, setModel] = (0, import_react12.useState)(defaultModel);
2337
+ const [systemPrompt, setSystemPrompt] = (0, import_react12.useState)(defaultSystemPrompt);
2338
+ const [temperature, setTemperature] = (0, import_react12.useState)(defaultTemperature);
2339
+ const [themeMode, setThemeMode] = (0, import_react12.useState)(defaultThemeMode);
2340
+ (0, import_react12.useEffect)(() => {
2341
+ const savedTheme = localStorage.getItem("chat-theme");
2342
+ if (savedTheme) {
2343
+ try {
2344
+ setTheme(JSON.parse(savedTheme));
2345
+ } catch (error) {
2346
+ console.error("Error loading theme:", error);
2347
+ }
2348
+ }
2349
+ const savedStarters = localStorage.getItem("chat-conversation-starters");
2350
+ if (savedStarters) {
2351
+ try {
2352
+ setConversationStarters(JSON.parse(savedStarters));
2353
+ } catch (error) {
2354
+ console.error("Error loading conversation starters:", error);
2355
+ }
2356
+ }
2357
+ const savedModel = localStorage.getItem("chat-model");
2358
+ if (savedModel) {
2359
+ try {
2360
+ setModel(savedModel);
2361
+ } catch (error) {
2362
+ console.error("Error loading model:", error);
2363
+ }
2364
+ }
2365
+ const savedSystemPrompt = localStorage.getItem("chat-system-prompt");
2366
+ if (savedSystemPrompt) {
2367
+ try {
2368
+ setSystemPrompt(savedSystemPrompt);
2369
+ } catch (error) {
2370
+ console.error("Error loading system prompt:", error);
2371
+ }
2372
+ }
2373
+ const savedTemperature = localStorage.getItem("chat-temperature");
2374
+ if (savedTemperature) {
2375
+ try {
2376
+ setTemperature(parseFloat(savedTemperature));
2377
+ } catch (error) {
2378
+ console.error("Error loading temperature:", error);
2379
+ }
2380
+ }
2381
+ const savedThemeMode = localStorage.getItem("chat-theme-mode");
2382
+ if (savedThemeMode) {
2383
+ try {
2384
+ setThemeMode(savedThemeMode);
2385
+ } catch (error) {
2386
+ console.error("Error loading theme mode:", error);
2387
+ }
2388
+ }
2389
+ const handleThemeChange = (e) => {
2390
+ const customEvent = e;
2391
+ setTheme(customEvent.detail);
2392
+ };
2393
+ const handleStartersChange = (e) => {
2394
+ const customEvent = e;
2395
+ setConversationStarters(customEvent.detail);
2396
+ };
2397
+ const handleSystemPromptChange = (e) => {
2398
+ const customEvent = e;
2399
+ setSystemPrompt(customEvent.detail);
2400
+ };
2401
+ const handleModelChange = (e) => {
2402
+ const customEvent = e;
2403
+ setModel(customEvent.detail);
2404
+ };
2405
+ const handleTemperatureChange = (e) => {
2406
+ const customEvent = e;
2407
+ setTemperature(customEvent.detail);
2408
+ };
2409
+ const handleThemeModeChange = (e) => {
2410
+ const customEvent = e;
2411
+ setThemeMode(customEvent.detail);
2412
+ };
2413
+ window.addEventListener("chat-theme-change", handleThemeChange);
2414
+ window.addEventListener("chat-starters-change", handleStartersChange);
2415
+ window.addEventListener("chat-model-change", handleModelChange);
2416
+ window.addEventListener("chat-system-prompt-change", handleSystemPromptChange);
2417
+ window.addEventListener("chat-temperature-change", handleTemperatureChange);
2418
+ window.addEventListener("chat-theme-mode-change", handleThemeModeChange);
2419
+ return () => {
2420
+ window.removeEventListener("chat-theme-change", handleThemeChange);
2421
+ window.removeEventListener("chat-starters-change", handleStartersChange);
2422
+ window.removeEventListener("chat-model-change", handleModelChange);
2423
+ window.removeEventListener("chat-system-prompt-change", handleSystemPromptChange);
2424
+ window.removeEventListener("chat-temperature-change", handleTemperatureChange);
2425
+ window.removeEventListener("chat-theme-mode-change", handleThemeModeChange);
2426
+ };
2427
+ }, []);
2428
+ (0, import_react12.useEffect)(() => {
2429
+ localStorage.setItem("chat-theme", JSON.stringify(theme));
2430
+ const root = document.documentElement;
2431
+ if (themeMode === "light") {
2432
+ root.style.setProperty("--chat-primary", hexToHSL(theme.lightPrimary));
2433
+ root.style.setProperty("--chat-secondary", hexToHSL(theme.lightSecondary));
2434
+ root.style.setProperty("--chat-text", hexToHSL(theme.lightText));
2435
+ } else {
2436
+ root.style.setProperty("--chat-primary", hexToHSL(theme.darkPrimary));
2437
+ root.style.setProperty("--chat-secondary", hexToHSL(theme.darkSecondary));
2438
+ root.style.setProperty("--chat-text", hexToHSL(theme.darkText));
2439
+ }
2440
+ root.style.setProperty("--chat-font-family", theme.fontFamily);
2441
+ root.style.setProperty("--chat-font-size", `${theme.fontSize}px`);
2442
+ window.dispatchEvent(new CustomEvent("chat-theme-change", { detail: theme }));
2443
+ }, [theme, themeMode]);
2444
+ (0, import_react12.useEffect)(() => {
2445
+ localStorage.setItem("chat-conversation-starters", JSON.stringify(conversationStarters));
2446
+ window.dispatchEvent(new CustomEvent("chat-starters-change", { detail: conversationStarters }));
2447
+ }, [conversationStarters]);
2448
+ (0, import_react12.useEffect)(() => {
2449
+ localStorage.setItem("chat-model", model);
2450
+ window.dispatchEvent(new CustomEvent("chat-model-change", { detail: model }));
2451
+ }, [model]);
2452
+ (0, import_react12.useEffect)(() => {
2453
+ localStorage.setItem("chat-system-prompt", systemPrompt);
2454
+ window.dispatchEvent(new CustomEvent("chat-system-prompt-change", { detail: systemPrompt }));
2455
+ }, [systemPrompt]);
2456
+ (0, import_react12.useEffect)(() => {
2457
+ localStorage.setItem("chat-temperature", temperature.toString());
2458
+ window.dispatchEvent(new CustomEvent("chat-temperature-change", { detail: temperature }));
2459
+ }, [temperature]);
2460
+ (0, import_react12.useEffect)(() => {
2461
+ localStorage.setItem("chat-theme-mode", themeMode);
2462
+ window.dispatchEvent(new CustomEvent("chat-theme-mode-change", { detail: themeMode }));
2463
+ }, [themeMode]);
2464
+ const updateColor = (key, value) => {
2465
+ setTheme((prev) => ({ ...prev, [key]: value }));
2466
+ };
2467
+ const updateLightColors = (colors) => {
2468
+ setTheme((prev) => ({
2469
+ ...prev,
2470
+ ...colors.primary && { lightPrimary: colors.primary },
2471
+ ...colors.secondary && { lightSecondary: colors.secondary },
2472
+ ...colors.text && { lightText: colors.text }
2473
+ }));
2474
+ };
2475
+ const updateDarkColors = (colors) => {
2476
+ setTheme((prev) => ({
2477
+ ...prev,
2478
+ ...colors.primary && { darkPrimary: colors.primary },
2479
+ ...colors.secondary && { darkSecondary: colors.secondary },
2480
+ ...colors.text && { darkText: colors.text }
2481
+ }));
2482
+ };
2483
+ const resetTheme = () => {
2484
+ setTheme(defaultTheme);
2485
+ };
2486
+ const updateFontSize = (size) => {
2487
+ setTheme((prev) => ({ ...prev, fontSize: size }));
2488
+ };
2489
+ const updateFontFamily = (family) => {
2490
+ setTheme((prev) => ({ ...prev, fontFamily: family }));
2491
+ };
2492
+ const updateConversationStarters = (starters) => {
2493
+ setConversationStarters(starters);
2494
+ };
2495
+ const updateSystemPrompt = (prompt) => {
2496
+ setSystemPrompt(prompt);
2497
+ };
2498
+ const updateModel = (selectedModel) => {
2499
+ setModel(selectedModel);
2500
+ };
2501
+ const updateTemperature = (temp) => {
2502
+ setTemperature(temp);
2503
+ };
2504
+ const updateThemeMode = (mode) => {
2505
+ setThemeMode(mode);
2506
+ };
2507
+ return {
2508
+ theme,
2509
+ updateColor,
2510
+ updateLightColors,
2511
+ updateDarkColors,
2512
+ resetTheme,
2513
+ updateFontSize,
2514
+ updateFontFamily,
2515
+ conversationStarters,
2516
+ updateConversationStarters,
2517
+ model,
2518
+ updateModel,
2519
+ systemPrompt,
2520
+ updateSystemPrompt,
2521
+ temperature,
2522
+ updateTemperature,
2523
+ themeMode,
2524
+ updateThemeMode
2525
+ };
2526
+ }
2527
+
2528
+ // src/ui/input.tsx
2529
+ var React = __toESM(require("react"));
2530
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2531
+ var Input = React.forwardRef(
2532
+ ({ className, type, ...props }, ref) => {
2533
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2534
+ "input",
2535
+ {
2536
+ type,
2537
+ className: cn(
2538
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
2539
+ className
2540
+ ),
2541
+ ref,
2542
+ ...props
2543
+ }
2544
+ );
2545
+ }
2546
+ );
2547
+ Input.displayName = "Input";
2548
+
2549
+ // src/ui/dialog.tsx
2550
+ var DialogPrimitive = __toESM(require("@radix-ui/react-dialog"));
2551
+ var import_lucide_react12 = require("lucide-react");
2552
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2553
+ function Dialog({
2554
+ ...props
2555
+ }) {
2556
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
2557
+ }
2558
+ function DialogPortal({
2559
+ ...props
2560
+ }) {
2561
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
2562
+ }
2563
+ function DialogOverlay({
2564
+ className,
2565
+ ...props
2566
+ }) {
2567
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2568
+ DialogPrimitive.Overlay,
2569
+ {
2570
+ "data-slot": "dialog-overlay",
2571
+ className: cn(
2572
+ "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",
2573
+ className
2574
+ ),
2575
+ ...props
2576
+ }
2577
+ );
2578
+ }
2579
+ function DialogContent({
2580
+ className,
2581
+ children,
2582
+ showCloseButton = true,
2583
+ ...props
2584
+ }) {
2585
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogPortal, { "data-slot": "dialog-portal", children: [
2586
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogOverlay, {}),
2587
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2588
+ DialogPrimitive.Content,
2589
+ {
2590
+ "data-slot": "dialog-content",
2591
+ className: cn(
2592
+ "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",
2593
+ className
2594
+ ),
2595
+ ...props,
2596
+ children: [
2597
+ children,
2598
+ showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2599
+ DialogPrimitive.Close,
2600
+ {
2601
+ "data-slot": "dialog-close",
2602
+ 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",
2603
+ children: [
2604
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react12.XIcon, {}),
2605
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "sr-only", children: "Close" })
2606
+ ]
2607
+ }
2608
+ )
2609
+ ]
2610
+ }
2611
+ )
2612
+ ] });
2613
+ }
2614
+ function DialogHeader({ className, ...props }) {
2615
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2616
+ "div",
2617
+ {
2618
+ "data-slot": "dialog-header",
2619
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
2620
+ ...props
2621
+ }
2622
+ );
2623
+ }
2624
+ function DialogTitle({
2625
+ className,
2626
+ ...props
2627
+ }) {
2628
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2629
+ DialogPrimitive.Title,
2630
+ {
2631
+ "data-slot": "dialog-title",
2632
+ className: cn("text-lg leading-none font-semibold", className),
2633
+ ...props
2634
+ }
2635
+ );
2636
+ }
2637
+ function DialogDescription({
2638
+ className,
2639
+ ...props
2640
+ }) {
2641
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2642
+ DialogPrimitive.Description,
2643
+ {
2644
+ "data-slot": "dialog-description",
2645
+ className: cn("text-muted-foreground text-sm", className),
2646
+ ...props
2647
+ }
2648
+ );
2649
+ }
2650
+ // Annotate the CommonJS export names for ESM import in node:
2651
+ 0 && (module.exports = {
2652
+ Button,
2653
+ ChatWidget,
2654
+ Dialog,
2655
+ DialogContent,
2656
+ DialogDescription,
2657
+ DialogHeader,
2658
+ DialogTitle,
2659
+ Input,
2660
+ fontOptions,
2661
+ useChatTheme
2662
+ });
2663
+ //# sourceMappingURL=index.js.map