@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.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