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