@dmanikanta17/chat-ui-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1073 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1042 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +44 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1073 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ChatUI: () => ChatUI
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/ChatUI.tsx
|
|
38
|
+
var import_react4 = require("react");
|
|
39
|
+
var import_lucide_react3 = require("lucide-react");
|
|
40
|
+
|
|
41
|
+
// src/components/ui/button.tsx
|
|
42
|
+
var import_react_slot = require("@radix-ui/react-slot");
|
|
43
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
44
|
+
|
|
45
|
+
// src/lib/utils.ts
|
|
46
|
+
var import_clsx = require("clsx");
|
|
47
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
48
|
+
function cn(...inputs) {
|
|
49
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/components/ui/button.tsx
|
|
53
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
54
|
+
var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
55
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all cursor-pointer 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",
|
|
56
|
+
{
|
|
57
|
+
variants: {
|
|
58
|
+
variant: {
|
|
59
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
60
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
61
|
+
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",
|
|
62
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
63
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
64
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
65
|
+
},
|
|
66
|
+
size: {
|
|
67
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
68
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
69
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
70
|
+
icon: "size-9",
|
|
71
|
+
"icon-sm": "size-8",
|
|
72
|
+
"icon-lg": "size-10"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
defaultVariants: {
|
|
76
|
+
variant: "default",
|
|
77
|
+
size: "default"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
function Button({
|
|
82
|
+
className,
|
|
83
|
+
variant,
|
|
84
|
+
size,
|
|
85
|
+
asChild = false,
|
|
86
|
+
...props
|
|
87
|
+
}) {
|
|
88
|
+
const Comp = asChild ? import_react_slot.Slot : "button";
|
|
89
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
90
|
+
Comp,
|
|
91
|
+
{
|
|
92
|
+
"data-slot": "button",
|
|
93
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
94
|
+
...props
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/components/ui/avatar.tsx
|
|
100
|
+
var AvatarPrimitive = __toESM(require("@radix-ui/react-avatar"));
|
|
101
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
102
|
+
function Avatar({
|
|
103
|
+
className,
|
|
104
|
+
...props
|
|
105
|
+
}) {
|
|
106
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
107
|
+
AvatarPrimitive.Root,
|
|
108
|
+
{
|
|
109
|
+
"data-slot": "avatar",
|
|
110
|
+
className: cn(
|
|
111
|
+
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
112
|
+
className
|
|
113
|
+
),
|
|
114
|
+
...props
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
function AvatarImage({
|
|
119
|
+
className,
|
|
120
|
+
...props
|
|
121
|
+
}) {
|
|
122
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
123
|
+
AvatarPrimitive.Image,
|
|
124
|
+
{
|
|
125
|
+
"data-slot": "avatar-image",
|
|
126
|
+
className: cn("aspect-square size-full", className),
|
|
127
|
+
...props
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
function AvatarFallback({
|
|
132
|
+
className,
|
|
133
|
+
...props
|
|
134
|
+
}) {
|
|
135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
136
|
+
AvatarPrimitive.Fallback,
|
|
137
|
+
{
|
|
138
|
+
"data-slot": "avatar-fallback",
|
|
139
|
+
className: cn(
|
|
140
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
141
|
+
className
|
|
142
|
+
),
|
|
143
|
+
...props
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/components/ui/message-loading.tsx
|
|
149
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
150
|
+
function MessageLoading() {
|
|
151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
152
|
+
"svg",
|
|
153
|
+
{
|
|
154
|
+
width: "24",
|
|
155
|
+
height: "24",
|
|
156
|
+
viewBox: "0 0 24 24",
|
|
157
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
158
|
+
className: "text-foreground",
|
|
159
|
+
children: [
|
|
160
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "4", cy: "12", r: "2", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
161
|
+
"animate",
|
|
162
|
+
{
|
|
163
|
+
id: "spinner_qFRN",
|
|
164
|
+
begin: "0;spinner_OcgL.end+0.25s",
|
|
165
|
+
attributeName: "cy",
|
|
166
|
+
calcMode: "spline",
|
|
167
|
+
dur: "0.6s",
|
|
168
|
+
values: "12;6;12",
|
|
169
|
+
keySplines: ".33,.66,.66,1;.33,0,.66,.33"
|
|
170
|
+
}
|
|
171
|
+
) }),
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "2", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
173
|
+
"animate",
|
|
174
|
+
{
|
|
175
|
+
begin: "spinner_qFRN.begin+0.1s",
|
|
176
|
+
attributeName: "cy",
|
|
177
|
+
calcMode: "spline",
|
|
178
|
+
dur: "0.6s",
|
|
179
|
+
values: "12;6;12",
|
|
180
|
+
keySplines: ".33,.66,.66,1;.33,0,.66,.33"
|
|
181
|
+
}
|
|
182
|
+
) }),
|
|
183
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "20", cy: "12", r: "2", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
184
|
+
"animate",
|
|
185
|
+
{
|
|
186
|
+
id: "spinner_OcgL",
|
|
187
|
+
begin: "spinner_qFRN.begin+0.2s",
|
|
188
|
+
attributeName: "cy",
|
|
189
|
+
calcMode: "spline",
|
|
190
|
+
dur: "0.6s",
|
|
191
|
+
values: "12;6;12",
|
|
192
|
+
keySplines: ".33,.66,.66,1;.33,0,.66,.33"
|
|
193
|
+
}
|
|
194
|
+
) })
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/components/ui/chat-bubble.tsx
|
|
201
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
202
|
+
function ChatBubble({
|
|
203
|
+
variant = "received",
|
|
204
|
+
layout = "default",
|
|
205
|
+
className,
|
|
206
|
+
children
|
|
207
|
+
}) {
|
|
208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
209
|
+
"div",
|
|
210
|
+
{
|
|
211
|
+
className: cn(
|
|
212
|
+
"flex items-start gap-2 mb-4",
|
|
213
|
+
variant === "sent" && "flex-row-reverse",
|
|
214
|
+
className
|
|
215
|
+
),
|
|
216
|
+
children
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
function ChatBubbleMessage({
|
|
221
|
+
variant = "received",
|
|
222
|
+
isLoading,
|
|
223
|
+
className,
|
|
224
|
+
children
|
|
225
|
+
}) {
|
|
226
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
227
|
+
"div",
|
|
228
|
+
{
|
|
229
|
+
className: cn(
|
|
230
|
+
"rounded-[1.25rem] px-4 py-2 text-sm max-w-[85%]",
|
|
231
|
+
variant === "sent" ? "bg-primary text-primary-foreground" : "bg-muted",
|
|
232
|
+
className
|
|
233
|
+
),
|
|
234
|
+
children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageLoading, {}) }) : children
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
function ChatBubbleAvatar({
|
|
239
|
+
src,
|
|
240
|
+
fallback = "AI",
|
|
241
|
+
className
|
|
242
|
+
}) {
|
|
243
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Avatar, { className: cn("h-8 w-8", className), children: [
|
|
244
|
+
src && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AvatarImage, { src }),
|
|
245
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AvatarFallback, { children: fallback })
|
|
246
|
+
] });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/components/ui/chat-input.tsx
|
|
250
|
+
var React = __toESM(require("react"));
|
|
251
|
+
|
|
252
|
+
// src/components/ui/textarea.tsx
|
|
253
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
254
|
+
function Textarea({ className, ...props }) {
|
|
255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
256
|
+
"textarea",
|
|
257
|
+
{
|
|
258
|
+
"data-slot": "textarea",
|
|
259
|
+
className: cn(
|
|
260
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
261
|
+
className
|
|
262
|
+
),
|
|
263
|
+
...props
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/components/ui/chat-input.tsx
|
|
269
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
270
|
+
var ChatInput = React.forwardRef(
|
|
271
|
+
({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
272
|
+
Textarea,
|
|
273
|
+
{
|
|
274
|
+
autoComplete: "off",
|
|
275
|
+
ref,
|
|
276
|
+
name: "message",
|
|
277
|
+
className: cn(
|
|
278
|
+
"max-h-12 px-4 py-3 bg-background text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full rounded-md flex items-center h-16 resize-none",
|
|
279
|
+
className
|
|
280
|
+
),
|
|
281
|
+
...props
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
ChatInput.displayName = "ChatInput";
|
|
286
|
+
|
|
287
|
+
// src/components/ui/expandable-chat.tsx
|
|
288
|
+
var import_react = require("react");
|
|
289
|
+
var import_lucide_react = require("lucide-react");
|
|
290
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
291
|
+
var chatConfig = {
|
|
292
|
+
dimensions: {
|
|
293
|
+
sm: "sm:max-w-sm sm:max-h-[500px]",
|
|
294
|
+
md: "sm:max-w-md sm:max-h-[600px]",
|
|
295
|
+
lg: "sm:max-w-lg sm:max-h-[700px]",
|
|
296
|
+
xl: "sm:max-w-xl sm:max-h-[800px]",
|
|
297
|
+
full: "sm:w-full sm:h-full"
|
|
298
|
+
},
|
|
299
|
+
positions: {
|
|
300
|
+
"bottom-right": "bottom-5 right-5",
|
|
301
|
+
"bottom-left": "bottom-5 left-5"
|
|
302
|
+
},
|
|
303
|
+
chatPositions: {
|
|
304
|
+
"bottom-right": "sm:bottom-[calc(100%+10px)] sm:right-0",
|
|
305
|
+
"bottom-left": "sm:bottom-[calc(100%+10px)] sm:left-0"
|
|
306
|
+
},
|
|
307
|
+
states: {
|
|
308
|
+
open: "pointer-events-auto opacity-100 visible scale-100 translate-y-0",
|
|
309
|
+
closed: "pointer-events-none opacity-0 invisible scale-100 sm:translate-y-5"
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var ExpandableChat = ({
|
|
313
|
+
className,
|
|
314
|
+
position = "bottom-right",
|
|
315
|
+
size = "md",
|
|
316
|
+
icon,
|
|
317
|
+
children,
|
|
318
|
+
isOpen: controlledIsOpen,
|
|
319
|
+
onOpenChange,
|
|
320
|
+
...props
|
|
321
|
+
}) => {
|
|
322
|
+
const [internalIsOpen, setInternalIsOpen] = (0, import_react.useState)(false);
|
|
323
|
+
const isControlled = controlledIsOpen !== void 0;
|
|
324
|
+
const isOpen = isControlled ? controlledIsOpen : internalIsOpen;
|
|
325
|
+
const chatRef = (0, import_react.useRef)(null);
|
|
326
|
+
const toggleChat = () => {
|
|
327
|
+
const newState = !isOpen;
|
|
328
|
+
if (isControlled && onOpenChange) {
|
|
329
|
+
onOpenChange(newState);
|
|
330
|
+
} else {
|
|
331
|
+
setInternalIsOpen(newState);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
335
|
+
"div",
|
|
336
|
+
{
|
|
337
|
+
className: cn(`fixed ${chatConfig.positions[position]} z-50`, className),
|
|
338
|
+
...props,
|
|
339
|
+
children: [
|
|
340
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
341
|
+
"div",
|
|
342
|
+
{
|
|
343
|
+
ref: chatRef,
|
|
344
|
+
className: cn(
|
|
345
|
+
"flex flex-col bg-background dark:bg-zinc-900 sm:rounded-lg shadow-md overflow-hidden transition-all duration-250 ease-out sm:absolute sm:w-[90vw] sm:h-[80vh] fixed inset-0 w-full h-full sm:inset-auto",
|
|
346
|
+
chatConfig.chatPositions[position],
|
|
347
|
+
chatConfig.dimensions[size],
|
|
348
|
+
isOpen ? chatConfig.states.open : chatConfig.states.closed,
|
|
349
|
+
className
|
|
350
|
+
),
|
|
351
|
+
children: [
|
|
352
|
+
children,
|
|
353
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
354
|
+
Button,
|
|
355
|
+
{
|
|
356
|
+
variant: "ghost",
|
|
357
|
+
size: "icon",
|
|
358
|
+
className: "absolute top-2 right-2 sm:hidden",
|
|
359
|
+
onClick: toggleChat,
|
|
360
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react.X, { className: "h-4 w-4" })
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
),
|
|
366
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
367
|
+
ExpandableChatToggle,
|
|
368
|
+
{
|
|
369
|
+
icon,
|
|
370
|
+
isOpen,
|
|
371
|
+
toggleChat
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
};
|
|
378
|
+
ExpandableChat.displayName = "ExpandableChat";
|
|
379
|
+
var ExpandableChatHeader = ({
|
|
380
|
+
className,
|
|
381
|
+
...props
|
|
382
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
383
|
+
"div",
|
|
384
|
+
{
|
|
385
|
+
className: cn("flex items-center justify-between p-4 border-b", className),
|
|
386
|
+
...props
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
ExpandableChatHeader.displayName = "ExpandableChatHeader";
|
|
390
|
+
var ExpandableChatBody = ({
|
|
391
|
+
className,
|
|
392
|
+
...props
|
|
393
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cn("flex-grow overflow-y-auto", className), ...props });
|
|
394
|
+
ExpandableChatBody.displayName = "ExpandableChatBody";
|
|
395
|
+
var ExpandableChatFooter = ({
|
|
396
|
+
className,
|
|
397
|
+
...props
|
|
398
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cn("border-t p-4", className), ...props });
|
|
399
|
+
ExpandableChatFooter.displayName = "ExpandableChatFooter";
|
|
400
|
+
var ExpandableChatToggle = ({
|
|
401
|
+
className,
|
|
402
|
+
icon,
|
|
403
|
+
isOpen,
|
|
404
|
+
toggleChat,
|
|
405
|
+
...props
|
|
406
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
407
|
+
Button,
|
|
408
|
+
{
|
|
409
|
+
variant: "default",
|
|
410
|
+
onClick: toggleChat,
|
|
411
|
+
className: cn(
|
|
412
|
+
"w-16 h-16 rounded-full shadow-md flex items-center justify-center hover:shadow-lg hover:shadow-black/30 transition-all duration-300 p-0 ring-0",
|
|
413
|
+
!isOpen ? "!bg-transparent !border-0" : "",
|
|
414
|
+
className
|
|
415
|
+
),
|
|
416
|
+
...props,
|
|
417
|
+
children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react.X, { className: "h-6 w-6" }) : icon || /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react.MessageCircle, { className: "h-6 w-6" })
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
ExpandableChatToggle.displayName = "ExpandableChatToggle";
|
|
421
|
+
|
|
422
|
+
// src/components/ui/chat-message-list.tsx
|
|
423
|
+
var React3 = __toESM(require("react"));
|
|
424
|
+
var import_lucide_react2 = require("lucide-react");
|
|
425
|
+
|
|
426
|
+
// src/components/hooks/use-auto-scroll.ts
|
|
427
|
+
var import_react2 = require("react");
|
|
428
|
+
function useAutoScroll(options = {}) {
|
|
429
|
+
const { offset = 20, smooth = false, content } = options;
|
|
430
|
+
const scrollRef = (0, import_react2.useRef)(null);
|
|
431
|
+
const lastContentHeight = (0, import_react2.useRef)(0);
|
|
432
|
+
const userHasScrolled = (0, import_react2.useRef)(false);
|
|
433
|
+
const [scrollState, setScrollState] = (0, import_react2.useState)({
|
|
434
|
+
isAtBottom: true,
|
|
435
|
+
autoScrollEnabled: true
|
|
436
|
+
});
|
|
437
|
+
const checkIsAtBottom = (0, import_react2.useCallback)(
|
|
438
|
+
(element) => {
|
|
439
|
+
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
440
|
+
const distanceToBottom = Math.abs(
|
|
441
|
+
scrollHeight - scrollTop - clientHeight
|
|
442
|
+
);
|
|
443
|
+
return distanceToBottom <= offset;
|
|
444
|
+
},
|
|
445
|
+
[offset]
|
|
446
|
+
);
|
|
447
|
+
const scrollToBottom = (0, import_react2.useCallback)(
|
|
448
|
+
(instant) => {
|
|
449
|
+
if (!scrollRef.current) return;
|
|
450
|
+
const targetScrollTop = scrollRef.current.scrollHeight - scrollRef.current.clientHeight;
|
|
451
|
+
if (instant) {
|
|
452
|
+
scrollRef.current.scrollTop = targetScrollTop;
|
|
453
|
+
} else {
|
|
454
|
+
scrollRef.current.scrollTo({
|
|
455
|
+
top: targetScrollTop,
|
|
456
|
+
behavior: smooth ? "smooth" : "auto"
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
setScrollState({
|
|
460
|
+
isAtBottom: true,
|
|
461
|
+
autoScrollEnabled: true
|
|
462
|
+
});
|
|
463
|
+
userHasScrolled.current = false;
|
|
464
|
+
},
|
|
465
|
+
[smooth]
|
|
466
|
+
);
|
|
467
|
+
const handleScroll = (0, import_react2.useCallback)(() => {
|
|
468
|
+
if (!scrollRef.current) return;
|
|
469
|
+
const atBottom = checkIsAtBottom(scrollRef.current);
|
|
470
|
+
setScrollState((prev) => ({
|
|
471
|
+
isAtBottom: atBottom,
|
|
472
|
+
// Re-enable auto-scroll if at the bottom
|
|
473
|
+
autoScrollEnabled: atBottom ? true : prev.autoScrollEnabled
|
|
474
|
+
}));
|
|
475
|
+
}, [checkIsAtBottom]);
|
|
476
|
+
(0, import_react2.useEffect)(() => {
|
|
477
|
+
const element = scrollRef.current;
|
|
478
|
+
if (!element) return;
|
|
479
|
+
element.addEventListener("scroll", handleScroll, { passive: true });
|
|
480
|
+
return () => element.removeEventListener("scroll", handleScroll);
|
|
481
|
+
}, [handleScroll]);
|
|
482
|
+
(0, import_react2.useEffect)(() => {
|
|
483
|
+
const scrollElement = scrollRef.current;
|
|
484
|
+
if (!scrollElement) return;
|
|
485
|
+
const currentHeight = scrollElement.scrollHeight;
|
|
486
|
+
const hasNewContent = currentHeight !== lastContentHeight.current;
|
|
487
|
+
if (hasNewContent) {
|
|
488
|
+
if (scrollState.autoScrollEnabled) {
|
|
489
|
+
requestAnimationFrame(() => {
|
|
490
|
+
scrollToBottom(lastContentHeight.current === 0);
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
lastContentHeight.current = currentHeight;
|
|
494
|
+
}
|
|
495
|
+
}, [content, scrollState.autoScrollEnabled, scrollToBottom]);
|
|
496
|
+
(0, import_react2.useEffect)(() => {
|
|
497
|
+
const element = scrollRef.current;
|
|
498
|
+
if (!element) return;
|
|
499
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
500
|
+
if (scrollState.autoScrollEnabled) {
|
|
501
|
+
scrollToBottom(true);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
resizeObserver.observe(element);
|
|
505
|
+
return () => resizeObserver.disconnect();
|
|
506
|
+
}, [scrollState.autoScrollEnabled, scrollToBottom]);
|
|
507
|
+
const disableAutoScroll = (0, import_react2.useCallback)(() => {
|
|
508
|
+
const atBottom = scrollRef.current ? checkIsAtBottom(scrollRef.current) : false;
|
|
509
|
+
if (!atBottom) {
|
|
510
|
+
userHasScrolled.current = true;
|
|
511
|
+
setScrollState((prev) => ({
|
|
512
|
+
...prev,
|
|
513
|
+
autoScrollEnabled: false
|
|
514
|
+
}));
|
|
515
|
+
}
|
|
516
|
+
}, [checkIsAtBottom]);
|
|
517
|
+
return {
|
|
518
|
+
scrollRef,
|
|
519
|
+
isAtBottom: scrollState.isAtBottom,
|
|
520
|
+
autoScrollEnabled: scrollState.autoScrollEnabled,
|
|
521
|
+
scrollToBottom: () => scrollToBottom(false),
|
|
522
|
+
disableAutoScroll
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/components/ui/chat-message-list.tsx
|
|
527
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
528
|
+
var ChatMessageList = React3.forwardRef(
|
|
529
|
+
({ className, children, smooth = false, ...props }, _ref) => {
|
|
530
|
+
const {
|
|
531
|
+
scrollRef,
|
|
532
|
+
isAtBottom,
|
|
533
|
+
autoScrollEnabled,
|
|
534
|
+
scrollToBottom,
|
|
535
|
+
disableAutoScroll
|
|
536
|
+
} = useAutoScroll({
|
|
537
|
+
smooth,
|
|
538
|
+
content: children
|
|
539
|
+
});
|
|
540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative w-full h-full", children: [
|
|
541
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
542
|
+
"div",
|
|
543
|
+
{
|
|
544
|
+
className: `flex flex-col w-full h-full p-4 overflow-y-auto ${className}`,
|
|
545
|
+
ref: scrollRef,
|
|
546
|
+
onWheel: disableAutoScroll,
|
|
547
|
+
onTouchMove: disableAutoScroll,
|
|
548
|
+
...props,
|
|
549
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex flex-col gap-6", children })
|
|
550
|
+
}
|
|
551
|
+
),
|
|
552
|
+
!isAtBottom && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
553
|
+
Button,
|
|
554
|
+
{
|
|
555
|
+
onClick: () => {
|
|
556
|
+
scrollToBottom();
|
|
557
|
+
},
|
|
558
|
+
size: "icon",
|
|
559
|
+
variant: "outline",
|
|
560
|
+
className: "absolute bottom-2 left-1/2 transform -translate-x-1/2 inline-flex rounded-full shadow-md",
|
|
561
|
+
"aria-label": "Scroll to bottom",
|
|
562
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.ArrowDown, { className: "h-4 w-4" })
|
|
563
|
+
}
|
|
564
|
+
)
|
|
565
|
+
] });
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
ChatMessageList.displayName = "ChatMessageList";
|
|
569
|
+
|
|
570
|
+
// src/components/ui/shining-text.tsx
|
|
571
|
+
var import_framer_motion = require("framer-motion");
|
|
572
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
573
|
+
function ShiningText({ text }) {
|
|
574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
575
|
+
import_framer_motion.motion.div,
|
|
576
|
+
{
|
|
577
|
+
className: "bg-[linear-gradient(110deg,#9ca3af,35%,#fff,50%,#9ca3af,75%,#9ca3af)] bg-[length:200%_100%] bg-clip-text text-sm text-transparent m-0 inline-block",
|
|
578
|
+
initial: { backgroundPosition: "200% 0" },
|
|
579
|
+
animate: { backgroundPosition: "-200% 0" },
|
|
580
|
+
transition: {
|
|
581
|
+
repeat: Infinity,
|
|
582
|
+
duration: 2,
|
|
583
|
+
ease: "linear"
|
|
584
|
+
},
|
|
585
|
+
children: text
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/components/ui/moving-border.tsx
|
|
591
|
+
var import_framer_motion2 = require("framer-motion");
|
|
592
|
+
var import_react3 = require("react");
|
|
593
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
594
|
+
function Button2({
|
|
595
|
+
borderRadius = "1.75rem",
|
|
596
|
+
children,
|
|
597
|
+
as: Component = "button",
|
|
598
|
+
containerClassName,
|
|
599
|
+
borderClassName,
|
|
600
|
+
duration,
|
|
601
|
+
className,
|
|
602
|
+
...otherProps
|
|
603
|
+
}) {
|
|
604
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
605
|
+
Component,
|
|
606
|
+
{
|
|
607
|
+
className: cn(
|
|
608
|
+
"bg-transparent relative text-xl h-16 w-40 p-[1px] overflow-hidden ",
|
|
609
|
+
containerClassName
|
|
610
|
+
),
|
|
611
|
+
style: {
|
|
612
|
+
borderRadius
|
|
613
|
+
},
|
|
614
|
+
...otherProps,
|
|
615
|
+
children: [
|
|
616
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
617
|
+
"div",
|
|
618
|
+
{
|
|
619
|
+
className: "absolute inset-0",
|
|
620
|
+
style: { borderRadius: `calc(${borderRadius} * 0.96)` },
|
|
621
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MovingBorder, { duration, rx: "30%", ry: "30%", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
622
|
+
"div",
|
|
623
|
+
{
|
|
624
|
+
className: cn(
|
|
625
|
+
"h-20 w-20 opacity-[0.8] bg-[radial-gradient(#0ea5e9_40%,transparent_60%)]",
|
|
626
|
+
borderClassName
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
) })
|
|
630
|
+
}
|
|
631
|
+
),
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
633
|
+
"div",
|
|
634
|
+
{
|
|
635
|
+
className: cn(
|
|
636
|
+
"relative bg-background border border-border backdrop-blur-xl text-foreground flex items-center justify-center w-full h-full text-sm antialiased",
|
|
637
|
+
className
|
|
638
|
+
),
|
|
639
|
+
style: {
|
|
640
|
+
borderRadius: `calc(${borderRadius} * 0.96)`
|
|
641
|
+
},
|
|
642
|
+
children
|
|
643
|
+
}
|
|
644
|
+
)
|
|
645
|
+
]
|
|
646
|
+
}
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
var MovingBorder = ({
|
|
650
|
+
children,
|
|
651
|
+
duration = 2e3,
|
|
652
|
+
rx,
|
|
653
|
+
ry,
|
|
654
|
+
...otherProps
|
|
655
|
+
}) => {
|
|
656
|
+
const pathRef = (0, import_react3.useRef)(null);
|
|
657
|
+
const progress = (0, import_framer_motion2.useMotionValue)(0);
|
|
658
|
+
(0, import_framer_motion2.useAnimationFrame)((time) => {
|
|
659
|
+
const length = pathRef.current?.getTotalLength();
|
|
660
|
+
if (length) {
|
|
661
|
+
const pxPerMillisecond = length / duration;
|
|
662
|
+
progress.set(time * pxPerMillisecond % length);
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
const x = (0, import_framer_motion2.useTransform)(
|
|
666
|
+
progress,
|
|
667
|
+
(val) => pathRef.current?.getPointAtLength(val).x
|
|
668
|
+
);
|
|
669
|
+
const y = (0, import_framer_motion2.useTransform)(
|
|
670
|
+
progress,
|
|
671
|
+
(val) => pathRef.current?.getPointAtLength(val).y
|
|
672
|
+
);
|
|
673
|
+
const transform = import_framer_motion2.useMotionTemplate`translateX(${x}px) translateY(${y}px) translateX(-50%) translateY(-50%)`;
|
|
674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
676
|
+
"svg",
|
|
677
|
+
{
|
|
678
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
679
|
+
preserveAspectRatio: "none",
|
|
680
|
+
className: "absolute h-full w-full",
|
|
681
|
+
width: "100%",
|
|
682
|
+
height: "100%",
|
|
683
|
+
...otherProps,
|
|
684
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
685
|
+
"rect",
|
|
686
|
+
{
|
|
687
|
+
fill: "none",
|
|
688
|
+
width: "100%",
|
|
689
|
+
height: "100%",
|
|
690
|
+
rx,
|
|
691
|
+
ry,
|
|
692
|
+
ref: pathRef
|
|
693
|
+
}
|
|
694
|
+
)
|
|
695
|
+
}
|
|
696
|
+
),
|
|
697
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
698
|
+
import_framer_motion2.motion.div,
|
|
699
|
+
{
|
|
700
|
+
style: {
|
|
701
|
+
position: "absolute",
|
|
702
|
+
top: 0,
|
|
703
|
+
left: 0,
|
|
704
|
+
display: "inline-block",
|
|
705
|
+
transform
|
|
706
|
+
},
|
|
707
|
+
children
|
|
708
|
+
}
|
|
709
|
+
)
|
|
710
|
+
] });
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// src/ChatUI.tsx
|
|
714
|
+
var import_react_markdown = __toESM(require("react-markdown"));
|
|
715
|
+
var import_remark_gfm = __toESM(require("remark-gfm"));
|
|
716
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
717
|
+
function ChatUI({
|
|
718
|
+
endpoint,
|
|
719
|
+
logoSrc = "/bot_logo.jpg",
|
|
720
|
+
soundSrc = "/sound.mp3",
|
|
721
|
+
title = "AI Assistant",
|
|
722
|
+
welcomeMessage = "Welcome to Services! \u{1F44B}",
|
|
723
|
+
description = "I'm here to help you navigate our services. Feel free to ask me anything!"
|
|
724
|
+
}) {
|
|
725
|
+
const [messages, setMessages] = (0, import_react4.useState)([]);
|
|
726
|
+
const [input, setInput] = (0, import_react4.useState)("");
|
|
727
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
|
|
728
|
+
const [isInitialized, setIsInitialized] = (0, import_react4.useState)(false);
|
|
729
|
+
(0, import_react4.useEffect)(() => {
|
|
730
|
+
const savedMessages = localStorage.getItem("chat-history");
|
|
731
|
+
if (savedMessages) {
|
|
732
|
+
try {
|
|
733
|
+
setMessages(JSON.parse(savedMessages));
|
|
734
|
+
} catch (e) {
|
|
735
|
+
console.error("Failed to parse chat history", e);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
setIsInitialized(true);
|
|
739
|
+
}, []);
|
|
740
|
+
const [isChatOpen, setIsChatOpen] = (0, import_react4.useState)(false);
|
|
741
|
+
const [showNotification, setShowNotification] = (0, import_react4.useState)(false);
|
|
742
|
+
const [isDismissed, setIsDismissed] = (0, import_react4.useState)(false);
|
|
743
|
+
(0, import_react4.useEffect)(() => {
|
|
744
|
+
const timer = setTimeout(() => {
|
|
745
|
+
if (!isDismissed && !isChatOpen) {
|
|
746
|
+
setShowNotification(true);
|
|
747
|
+
const audio = new Audio(soundSrc);
|
|
748
|
+
audio.play().catch((error) => {
|
|
749
|
+
if (error.name !== "NotAllowedError") {
|
|
750
|
+
console.error("Error playing notification sound:", error);
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}, 2e3);
|
|
755
|
+
return () => clearTimeout(timer);
|
|
756
|
+
}, [isChatOpen, isDismissed, soundSrc]);
|
|
757
|
+
(0, import_react4.useEffect)(() => {
|
|
758
|
+
if (isChatOpen) {
|
|
759
|
+
setShowNotification(false);
|
|
760
|
+
setIsDismissed(true);
|
|
761
|
+
}
|
|
762
|
+
}, [isChatOpen]);
|
|
763
|
+
const handleDismissNotification = () => {
|
|
764
|
+
setShowNotification(false);
|
|
765
|
+
setIsDismissed(true);
|
|
766
|
+
};
|
|
767
|
+
(0, import_react4.useEffect)(() => {
|
|
768
|
+
if (isInitialized) {
|
|
769
|
+
localStorage.setItem("chat-history", JSON.stringify(messages));
|
|
770
|
+
}
|
|
771
|
+
}, [messages, isInitialized]);
|
|
772
|
+
const handleReset = () => {
|
|
773
|
+
setMessages([]);
|
|
774
|
+
localStorage.removeItem("chat-history");
|
|
775
|
+
};
|
|
776
|
+
const handleSubmit = async (e) => {
|
|
777
|
+
e.preventDefault();
|
|
778
|
+
if (!input.trim() || isLoading) return;
|
|
779
|
+
const userQuestion = input.trim();
|
|
780
|
+
const userMessage = {
|
|
781
|
+
id: Date.now().toString(),
|
|
782
|
+
role: "user",
|
|
783
|
+
content: userQuestion
|
|
784
|
+
};
|
|
785
|
+
const history = messages.map((msg) => ({
|
|
786
|
+
role: msg.role,
|
|
787
|
+
content: msg.content
|
|
788
|
+
}));
|
|
789
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
790
|
+
setInput("");
|
|
791
|
+
setIsLoading(true);
|
|
792
|
+
try {
|
|
793
|
+
const response = await fetch(endpoint, {
|
|
794
|
+
method: "POST",
|
|
795
|
+
headers: {
|
|
796
|
+
"Content-Type": "application/json"
|
|
797
|
+
},
|
|
798
|
+
body: JSON.stringify({
|
|
799
|
+
query: userQuestion,
|
|
800
|
+
history,
|
|
801
|
+
stream: true
|
|
802
|
+
})
|
|
803
|
+
});
|
|
804
|
+
if (!response.body) {
|
|
805
|
+
throw new Error("ReadableStream not supported.");
|
|
806
|
+
}
|
|
807
|
+
const reader = response.body.getReader();
|
|
808
|
+
const decoder = new TextDecoder();
|
|
809
|
+
let aiResponse = "";
|
|
810
|
+
let isFirstChunk = true;
|
|
811
|
+
while (true) {
|
|
812
|
+
const { value, done } = await reader.read();
|
|
813
|
+
if (done) break;
|
|
814
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
815
|
+
aiResponse += chunk;
|
|
816
|
+
if (isFirstChunk) {
|
|
817
|
+
isFirstChunk = false;
|
|
818
|
+
setIsLoading(false);
|
|
819
|
+
const botMessageId = (Date.now() + 1).toString();
|
|
820
|
+
const botMessage = {
|
|
821
|
+
id: botMessageId,
|
|
822
|
+
role: "assistant",
|
|
823
|
+
content: aiResponse
|
|
824
|
+
};
|
|
825
|
+
setMessages((prev) => [...prev, botMessage]);
|
|
826
|
+
} else {
|
|
827
|
+
setMessages((prev) => {
|
|
828
|
+
const updated = [...prev];
|
|
829
|
+
const lastMsgIndex = updated.length - 1;
|
|
830
|
+
if (lastMsgIndex >= 0 && updated[lastMsgIndex].role === "assistant") {
|
|
831
|
+
updated[lastMsgIndex] = {
|
|
832
|
+
...updated[lastMsgIndex],
|
|
833
|
+
content: aiResponse
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
return updated;
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
const audio = new Audio(soundSrc);
|
|
841
|
+
audio.play().catch((e2) => console.error("Error playing notification sound:", e2));
|
|
842
|
+
} catch (error) {
|
|
843
|
+
console.error("Chat error:", error);
|
|
844
|
+
setMessages((prev) => [
|
|
845
|
+
...prev,
|
|
846
|
+
{
|
|
847
|
+
id: (Date.now() + 2).toString(),
|
|
848
|
+
role: "assistant",
|
|
849
|
+
content: "I apologize, but I encountered an error. Please try again later."
|
|
850
|
+
}
|
|
851
|
+
]);
|
|
852
|
+
} finally {
|
|
853
|
+
setIsLoading(false);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
857
|
+
showNotification && !isChatOpen && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "fixed bottom-24 right-4 sm:right-5 z-50 animate-in fade-in slide-in-from-bottom-5 duration-300", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative", children: [
|
|
858
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
859
|
+
Button2,
|
|
860
|
+
{
|
|
861
|
+
as: "div",
|
|
862
|
+
borderRadius: "0.75rem",
|
|
863
|
+
duration: 3e3,
|
|
864
|
+
containerClassName: "w-72 sm:w-80 h-auto overflow-hidden rounded-xl bg-transparent",
|
|
865
|
+
borderClassName: "bg-[radial-gradient(#0ea5e9_40%,transparent_60%)]",
|
|
866
|
+
className: "bg-background dark:bg-zinc-900 border dark:border-zinc-800 p-5 items-start justify-start flex-col w-full h-full text-foreground shadow-lg",
|
|
867
|
+
children: [
|
|
868
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
869
|
+
Button,
|
|
870
|
+
{
|
|
871
|
+
variant: "ghost",
|
|
872
|
+
size: "icon",
|
|
873
|
+
className: "absolute top-3 right-3 h-6 w-6 text-muted-foreground hover:text-foreground z-10",
|
|
874
|
+
onClick: handleDismissNotification,
|
|
875
|
+
children: [
|
|
876
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.X, { className: "h-4 w-4" }),
|
|
877
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sr-only", children: "Close" })
|
|
878
|
+
]
|
|
879
|
+
}
|
|
880
|
+
),
|
|
881
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-start gap-3 mb-4 w-full", children: [
|
|
882
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "relative h-10 w-10 shrink-0 overflow-hidden rounded-full ring-2 ring-background dark:ring-zinc-800", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: logoSrc, alt: "AI", className: "object-cover h-full w-full" }) }),
|
|
883
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col pt-0.5", children: [
|
|
884
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "font-bold text-sm leading-tight text-foreground", children: title }),
|
|
885
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1.5 mt-0.5", children: [
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "relative flex h-2 w-2", children: [
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
888
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-green-500" })
|
|
889
|
+
] }),
|
|
890
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs text-muted-foreground font-medium", children: "Online" })
|
|
891
|
+
] })
|
|
892
|
+
] })
|
|
893
|
+
] }),
|
|
894
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "w-full text-left", children: [
|
|
895
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm font-medium text-foreground leading-snug", children: welcomeMessage }),
|
|
896
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-muted-foreground leading-relaxed mt-2", children: description }),
|
|
897
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
898
|
+
Button,
|
|
899
|
+
{
|
|
900
|
+
className: "w-full mt-4 h-10 rounded-lg bg-[#535bf2] hover:bg-[#464ec9] text-white shadow-sm transition-all duration-200 font-medium",
|
|
901
|
+
onClick: () => setIsChatOpen(true),
|
|
902
|
+
children: "Start chatting"
|
|
903
|
+
}
|
|
904
|
+
)
|
|
905
|
+
] })
|
|
906
|
+
]
|
|
907
|
+
}
|
|
908
|
+
),
|
|
909
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "absolute -bottom-2 right-6 w-4 h-4 bg-background dark:bg-zinc-900 border-b border-r dark:border-zinc-800 transform rotate-45 z-0" })
|
|
910
|
+
] }) }),
|
|
911
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
912
|
+
ExpandableChat,
|
|
913
|
+
{
|
|
914
|
+
size: "lg",
|
|
915
|
+
position: "bottom-right",
|
|
916
|
+
isOpen: isChatOpen,
|
|
917
|
+
onOpenChange: setIsChatOpen,
|
|
918
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "relative h-full w-full overflow-hidden rounded-full", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: logoSrc, alt: "AI", className: "object-cover h-full w-full" }) }),
|
|
919
|
+
className: "z-50",
|
|
920
|
+
children: [
|
|
921
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ExpandableChatHeader, { className: "bg-muted/40 flex-col text-center justify-center border-b p-4 relative", children: [
|
|
922
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("h1", { className: "text-xl font-semibold flex items-center justify-center gap-2", children: [
|
|
923
|
+
title,
|
|
924
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Sparkles, { className: "h-4 w-4 text-yellow-500 fill-yellow-500" })
|
|
925
|
+
] }),
|
|
926
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-sm text-muted-foreground flex items-center justify-center gap-1.5 pt-1", children: [
|
|
927
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "flex h-2 w-2 rounded-full bg-green-500 animate-pulse" }),
|
|
928
|
+
"Online and ready to help"
|
|
929
|
+
] }),
|
|
930
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
931
|
+
Button,
|
|
932
|
+
{
|
|
933
|
+
variant: "ghost",
|
|
934
|
+
size: "icon",
|
|
935
|
+
className: "absolute top-3 left-3 h-8 w-8 text-muted-foreground hover:text-foreground hover:bg-muted/50 sm:top-5 sm:right-5 sm:left-auto",
|
|
936
|
+
onClick: handleReset,
|
|
937
|
+
title: "Reset Chat",
|
|
938
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.RefreshCcw, { className: "h-4 w-4" })
|
|
939
|
+
}
|
|
940
|
+
)
|
|
941
|
+
] }),
|
|
942
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ExpandableChatBody, { className: "bg-background/50", children: messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center justify-center h-full p-6 text-center animate-in fade-in duration-500", children: [
|
|
943
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "bg-background rounded-full p-4 mb-6 shadow-md ring-1 ring-border/50", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "relative w-16 h-16 overflow-hidden rounded-full", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: logoSrc, alt: "AI Logo", className: "object-cover w-full h-full" }) }) }),
|
|
944
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "text-2xl font-bold mb-2 tracking-tight", children: title }),
|
|
945
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-muted-foreground text-sm", children: [
|
|
946
|
+
"Welcome to ",
|
|
947
|
+
title,
|
|
948
|
+
" \u{1F49B}"
|
|
949
|
+
] })
|
|
950
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ChatMessageList, { children: [
|
|
951
|
+
messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
952
|
+
ChatBubble,
|
|
953
|
+
{
|
|
954
|
+
variant: message.role === "user" ? "sent" : "received",
|
|
955
|
+
children: [
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
957
|
+
ChatBubbleAvatar,
|
|
958
|
+
{
|
|
959
|
+
className: "h-8 w-8 shrink-0",
|
|
960
|
+
src: message.role === "user" ? void 0 : logoSrc,
|
|
961
|
+
fallback: message.role === "user" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.User, { className: "h-4 w-4" }) : "AI"
|
|
962
|
+
}
|
|
963
|
+
),
|
|
964
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
965
|
+
ChatBubbleMessage,
|
|
966
|
+
{
|
|
967
|
+
variant: message.role === "user" ? "sent" : "received",
|
|
968
|
+
children: message.role === "user" ? message.content : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn(
|
|
969
|
+
"prose dark:prose-invert text-sm break-words leading-normal max-w-none",
|
|
970
|
+
"prose-p:m-0 prose-ul:m-0 prose-ol:m-0 prose-li:m-0"
|
|
971
|
+
), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
972
|
+
import_react_markdown.default,
|
|
973
|
+
{
|
|
974
|
+
remarkPlugins: [import_remark_gfm.default],
|
|
975
|
+
components: {
|
|
976
|
+
ul: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ul", { className: "list-disc pl-4 my-1", ...props }),
|
|
977
|
+
ol: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ol", { className: "list-decimal pl-4 my-1", ...props }),
|
|
978
|
+
li: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { className: "my-0.5 pl-1", ...props }),
|
|
979
|
+
p: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mb-2 last:mb-0", ...props }),
|
|
980
|
+
strong: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-bold text-foreground", ...props }),
|
|
981
|
+
a: ({ node, href, children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
982
|
+
"a",
|
|
983
|
+
{
|
|
984
|
+
href,
|
|
985
|
+
target: "_blank",
|
|
986
|
+
rel: "noopener noreferrer",
|
|
987
|
+
className: "font-semibold text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300 hover:underline transition-colors break-all",
|
|
988
|
+
...props,
|
|
989
|
+
children
|
|
990
|
+
}
|
|
991
|
+
)
|
|
992
|
+
},
|
|
993
|
+
children: message.content
|
|
994
|
+
}
|
|
995
|
+
) })
|
|
996
|
+
}
|
|
997
|
+
)
|
|
998
|
+
]
|
|
999
|
+
},
|
|
1000
|
+
message.id
|
|
1001
|
+
)),
|
|
1002
|
+
isLoading && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ChatBubble, { variant: "received", children: [
|
|
1003
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1004
|
+
ChatBubbleAvatar,
|
|
1005
|
+
{
|
|
1006
|
+
className: "h-8 w-8 shrink-0",
|
|
1007
|
+
src: logoSrc,
|
|
1008
|
+
fallback: "AI"
|
|
1009
|
+
}
|
|
1010
|
+
),
|
|
1011
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ChatBubbleMessage, { className: "bg-transparent p-0 flex items-center gap-2", children: [
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Sparkles, { className: "h-4 w-4 text-foreground/50" }),
|
|
1013
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ShiningText, { text: "AI Assistant thinking..." })
|
|
1014
|
+
] })
|
|
1015
|
+
] })
|
|
1016
|
+
] }) }),
|
|
1017
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ExpandableChatFooter, { className: "bg-muted/40 p-3", children: [
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1019
|
+
"form",
|
|
1020
|
+
{
|
|
1021
|
+
onSubmit: handleSubmit,
|
|
1022
|
+
className: "relative rounded-3xl border bg-background focus-within:ring-1 focus-within:ring-ring p-1 shadow-sm",
|
|
1023
|
+
children: [
|
|
1024
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1025
|
+
ChatInput,
|
|
1026
|
+
{
|
|
1027
|
+
value: input,
|
|
1028
|
+
onChange: (e) => setInput(e.target.value),
|
|
1029
|
+
placeholder: "Message...",
|
|
1030
|
+
rows: 1,
|
|
1031
|
+
className: "min-h-0 h-auto max-h-32 resize-none rounded-2xl bg-background border-0 px-3 py-2.5 shadow-none focus-visible:ring-0 text-base sm:text-sm",
|
|
1032
|
+
onKeyDown: (e) => {
|
|
1033
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
1034
|
+
e.preventDefault();
|
|
1035
|
+
handleSubmit(e);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
),
|
|
1040
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between px-2 pb-1", children: [
|
|
1041
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex items-center gap-2" }),
|
|
1042
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1043
|
+
Button,
|
|
1044
|
+
{
|
|
1045
|
+
type: "submit",
|
|
1046
|
+
size: "icon",
|
|
1047
|
+
className: "h-8 w-8 rounded-full transition-all duration-200",
|
|
1048
|
+
disabled: !input.trim() || isLoading,
|
|
1049
|
+
children: [
|
|
1050
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Send, { className: "h-4 w-4" }),
|
|
1051
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sr-only", children: "Send" })
|
|
1052
|
+
]
|
|
1053
|
+
}
|
|
1054
|
+
)
|
|
1055
|
+
] })
|
|
1056
|
+
]
|
|
1057
|
+
}
|
|
1058
|
+
),
|
|
1059
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-2 text-center flex flex-col items-center justify-center gap-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-[10px] text-muted-foreground/60", children: [
|
|
1060
|
+
"Powered by ",
|
|
1061
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("a", { href: "#", className: "hover:underline hover:text-primary transition-colors", children: "ChatUI" })
|
|
1062
|
+
] }) })
|
|
1063
|
+
] })
|
|
1064
|
+
]
|
|
1065
|
+
}
|
|
1066
|
+
)
|
|
1067
|
+
] });
|
|
1068
|
+
}
|
|
1069
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1070
|
+
0 && (module.exports = {
|
|
1071
|
+
ChatUI
|
|
1072
|
+
});
|
|
1073
|
+
//# sourceMappingURL=index.js.map
|