simple-support-chat 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.
@@ -0,0 +1,771 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/client/ChatBubble.tsx
7
+
8
+ // src/client/context.ts
9
+ var SESSION_KEY = "support-chat-session-id";
10
+ function generateSessionId() {
11
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
12
+ let id = "";
13
+ for (let i = 0; i < 16; i++) {
14
+ id += chars.charAt(Math.floor(Math.random() * chars.length));
15
+ }
16
+ return id;
17
+ }
18
+ function getSessionId() {
19
+ if (typeof window === "undefined") return generateSessionId();
20
+ try {
21
+ const existing = localStorage.getItem(SESSION_KEY);
22
+ if (existing) return existing;
23
+ const newId = generateSessionId();
24
+ localStorage.setItem(SESSION_KEY, newId);
25
+ return newId;
26
+ } catch {
27
+ return generateSessionId();
28
+ }
29
+ }
30
+ function collectAnonymousContext() {
31
+ const sessionId = getSessionId();
32
+ if (typeof window === "undefined") {
33
+ return {
34
+ pageUrl: "",
35
+ referrer: "",
36
+ userAgent: "",
37
+ screenSize: "",
38
+ timezone: "",
39
+ sessionId
40
+ };
41
+ }
42
+ return {
43
+ pageUrl: window.location.href,
44
+ referrer: document.referrer,
45
+ userAgent: navigator.userAgent,
46
+ screenSize: `${window.screen.width}x${window.screen.height}`,
47
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
48
+ sessionId
49
+ };
50
+ }
51
+
52
+ // src/client/useChatEngine.ts
53
+ function useChatEngine({
54
+ apiUrl,
55
+ user
56
+ }) {
57
+ const [messages, setMessages] = react.useState([]);
58
+ const [input, setInput] = react.useState("");
59
+ const [sending, setSending] = react.useState(false);
60
+ const sendMessage = react.useCallback(async () => {
61
+ const text = input.trim();
62
+ if (!text || sending) return;
63
+ const msg = {
64
+ id: `msg-${Date.now()}`,
65
+ text,
66
+ sender: "user",
67
+ timestamp: Date.now()
68
+ };
69
+ setMessages((prev) => [...prev, msg]);
70
+ setInput("");
71
+ setSending(true);
72
+ try {
73
+ const context = collectAnonymousContext();
74
+ const response = await fetch(apiUrl, {
75
+ method: "POST",
76
+ headers: { "Content-Type": "application/json" },
77
+ body: JSON.stringify({
78
+ message: text,
79
+ user: user ?? void 0,
80
+ sessionId: user?.id ?? getSessionId(),
81
+ context
82
+ })
83
+ });
84
+ if (!response.ok) {
85
+ setMessages((prev) => [
86
+ ...prev,
87
+ {
88
+ id: `err-${Date.now()}`,
89
+ text: "Failed to send message. Please try again.",
90
+ sender: "system",
91
+ timestamp: Date.now()
92
+ }
93
+ ]);
94
+ }
95
+ } catch {
96
+ setMessages((prev) => [
97
+ ...prev,
98
+ {
99
+ id: `err-${Date.now()}`,
100
+ text: "Connection error. Please try again.",
101
+ sender: "system",
102
+ timestamp: Date.now()
103
+ }
104
+ ]);
105
+ } finally {
106
+ setSending(false);
107
+ }
108
+ }, [input, sending, apiUrl, user]);
109
+ const handleKeyDown = react.useCallback(
110
+ (e) => {
111
+ if (e.key === "Enter" && !e.shiftKey) {
112
+ e.preventDefault();
113
+ void sendMessage();
114
+ }
115
+ },
116
+ [sendMessage]
117
+ );
118
+ return { messages, input, setInput, sending, sendMessage, handleKeyDown };
119
+ }
120
+ function injectKeyframes() {
121
+ if (typeof document === "undefined") return;
122
+ if (document.querySelector("[data-support-chat-keyframes]")) return;
123
+ const style = document.createElement("style");
124
+ style.setAttribute("data-support-chat-keyframes", "");
125
+ style.textContent = `
126
+ @keyframes sc-slide-in {
127
+ from { opacity: 0; transform: translateY(12px) scale(0.96); }
128
+ to { opacity: 1; transform: translateY(0) scale(1); }
129
+ }
130
+ @keyframes sc-slide-out {
131
+ from { opacity: 1; transform: translateY(0) scale(1); }
132
+ to { opacity: 0; transform: translateY(12px) scale(0.96); }
133
+ }
134
+ `;
135
+ document.head.appendChild(style);
136
+ }
137
+ function ChatBubble({
138
+ apiUrl,
139
+ position = "bottom-right",
140
+ color = "#2563eb",
141
+ title = "Support",
142
+ placeholder = "Type a message...",
143
+ show = true,
144
+ user
145
+ }) {
146
+ const [isOpen, setIsOpen] = react.useState(false);
147
+ const { messages, input, setInput, sending, sendMessage, handleKeyDown } = useChatEngine({ apiUrl, user });
148
+ const [panelState, setPanelState] = react.useState(
149
+ "closed"
150
+ );
151
+ const panelRef = react.useRef(null);
152
+ const messagesEndRef = react.useRef(null);
153
+ const inputRef = react.useRef(null);
154
+ react.useEffect(() => {
155
+ injectKeyframes();
156
+ }, []);
157
+ react.useEffect(() => {
158
+ if (isOpen) {
159
+ setPanelState("open");
160
+ } else if (panelState === "open") {
161
+ setPanelState("closing");
162
+ }
163
+ }, [isOpen]);
164
+ const handleAnimationEnd = react.useCallback(() => {
165
+ if (panelState === "closing") {
166
+ setPanelState("closed");
167
+ }
168
+ }, [panelState]);
169
+ react.useEffect(() => {
170
+ if (panelState === "open" && inputRef.current) {
171
+ inputRef.current.focus();
172
+ }
173
+ }, [panelState]);
174
+ react.useEffect(() => {
175
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
176
+ }, [messages]);
177
+ react.useEffect(() => {
178
+ if (panelState !== "open" || !panelRef.current) return;
179
+ const panel = panelRef.current;
180
+ const handleTrapKeyDown = (e) => {
181
+ if (e.key === "Escape") {
182
+ setIsOpen(false);
183
+ return;
184
+ }
185
+ if (e.key !== "Tab") return;
186
+ const focusable = panel.querySelectorAll(
187
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
188
+ );
189
+ if (focusable.length === 0) return;
190
+ const first = focusable[0];
191
+ const last = focusable[focusable.length - 1];
192
+ if (e.shiftKey) {
193
+ if (document.activeElement === first) {
194
+ e.preventDefault();
195
+ last.focus();
196
+ }
197
+ } else {
198
+ if (document.activeElement === last) {
199
+ e.preventDefault();
200
+ first.focus();
201
+ }
202
+ }
203
+ };
204
+ document.addEventListener("keydown", handleTrapKeyDown);
205
+ return () => document.removeEventListener("keydown", handleTrapKeyDown);
206
+ }, [panelState]);
207
+ const toggle = react.useCallback(() => setIsOpen((o) => !o), []);
208
+ const positionStyles = {
209
+ position: "fixed",
210
+ zIndex: 99999,
211
+ ...position.includes("bottom") ? { bottom: "20px" } : { top: "20px" },
212
+ ...position.includes("right") ? { right: "20px" } : { left: "20px" }
213
+ };
214
+ const panelPositionStyles = {
215
+ position: "fixed",
216
+ zIndex: 99999,
217
+ width: "380px",
218
+ maxWidth: "calc(100vw - 40px)",
219
+ height: "500px",
220
+ maxHeight: "calc(100vh - 120px)",
221
+ ...position.includes("bottom") ? { bottom: "80px" } : { top: "80px" },
222
+ ...position.includes("right") ? { right: "20px" } : { left: "20px" }
223
+ };
224
+ const showPanel = panelState === "open" || panelState === "closing";
225
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
226
+ showPanel && /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
227
+ @media (max-width: 479px) {
228
+ [data-support-chat-panel] {
229
+ width: calc(100vw - 16px) !important;
230
+ height: calc(100vh - 100px) !important;
231
+ max-width: none !important;
232
+ max-height: none !important;
233
+ left: 8px !important;
234
+ right: 8px !important;
235
+ bottom: 72px !important;
236
+ border-radius: 12px !important;
237
+ }
238
+ }
239
+ ` }),
240
+ show && /* @__PURE__ */ jsxRuntime.jsx(
241
+ "button",
242
+ {
243
+ onClick: toggle,
244
+ "aria-label": isOpen ? "Close support chat" : "Open support chat",
245
+ "aria-expanded": isOpen,
246
+ "aria-haspopup": "dialog",
247
+ style: {
248
+ ...positionStyles,
249
+ width: "56px",
250
+ height: "56px",
251
+ borderRadius: "50%",
252
+ backgroundColor: color,
253
+ border: "none",
254
+ cursor: "pointer",
255
+ display: "flex",
256
+ alignItems: "center",
257
+ justifyContent: "center",
258
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
259
+ transition: "transform 0.2s ease, box-shadow 0.2s ease"
260
+ },
261
+ onMouseEnter: (e) => {
262
+ e.currentTarget.style.transform = "scale(1.1)";
263
+ e.currentTarget.style.boxShadow = "0 6px 20px rgba(0,0,0,0.2)";
264
+ },
265
+ onMouseLeave: (e) => {
266
+ e.currentTarget.style.transform = "scale(1)";
267
+ e.currentTarget.style.boxShadow = "0 4px 12px rgba(0,0,0,0.15)";
268
+ },
269
+ children: /* @__PURE__ */ jsxRuntime.jsx(
270
+ "svg",
271
+ {
272
+ width: "24",
273
+ height: "24",
274
+ viewBox: "0 0 24 24",
275
+ fill: "none",
276
+ stroke: "white",
277
+ strokeWidth: "2",
278
+ strokeLinecap: "round",
279
+ strokeLinejoin: "round",
280
+ "aria-hidden": "true",
281
+ children: isOpen ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
282
+ }
283
+ )
284
+ }
285
+ ),
286
+ showPanel && /* @__PURE__ */ jsxRuntime.jsxs(
287
+ "div",
288
+ {
289
+ ref: panelRef,
290
+ role: "dialog",
291
+ "aria-label": "Support chat",
292
+ "aria-modal": "true",
293
+ "data-support-chat-panel": "",
294
+ onAnimationEnd: handleAnimationEnd,
295
+ style: {
296
+ ...panelPositionStyles,
297
+ display: "flex",
298
+ flexDirection: "column",
299
+ borderRadius: "12px",
300
+ overflow: "hidden",
301
+ boxShadow: "0 8px 30px rgba(0,0,0,0.12)",
302
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
303
+ fontSize: "14px",
304
+ backgroundColor: "#fff",
305
+ border: "1px solid #e5e7eb",
306
+ animation: panelState === "open" ? "sc-slide-in 0.25s ease-out forwards" : "sc-slide-out 0.2s ease-in forwards"
307
+ },
308
+ children: [
309
+ /* @__PURE__ */ jsxRuntime.jsxs(
310
+ "div",
311
+ {
312
+ style: {
313
+ backgroundColor: color,
314
+ color: "#fff",
315
+ padding: "16px",
316
+ display: "flex",
317
+ alignItems: "center",
318
+ justifyContent: "space-between",
319
+ flexShrink: 0
320
+ },
321
+ children: [
322
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, fontSize: "16px" }, children: title }),
323
+ /* @__PURE__ */ jsxRuntime.jsx(
324
+ "button",
325
+ {
326
+ onClick: () => setIsOpen(false),
327
+ "aria-label": "Close chat",
328
+ style: {
329
+ background: "none",
330
+ border: "none",
331
+ color: "#fff",
332
+ cursor: "pointer",
333
+ padding: "4px",
334
+ lineHeight: 1,
335
+ fontSize: "18px"
336
+ },
337
+ children: "\u2715"
338
+ }
339
+ )
340
+ ]
341
+ }
342
+ ),
343
+ /* @__PURE__ */ jsxRuntime.jsxs(
344
+ "div",
345
+ {
346
+ role: "log",
347
+ "aria-live": "polite",
348
+ "aria-label": "Chat messages",
349
+ style: {
350
+ flex: 1,
351
+ overflowY: "auto",
352
+ padding: "16px",
353
+ display: "flex",
354
+ flexDirection: "column",
355
+ gap: "8px"
356
+ },
357
+ children: [
358
+ messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(
359
+ "div",
360
+ {
361
+ style: {
362
+ color: "#9ca3af",
363
+ textAlign: "center",
364
+ marginTop: "40px"
365
+ },
366
+ children: "Send us a message and we will get back to you!"
367
+ }
368
+ ),
369
+ messages.map((msg) => /* @__PURE__ */ jsxRuntime.jsx(
370
+ "div",
371
+ {
372
+ style: {
373
+ alignSelf: msg.sender === "user" ? "flex-end" : "flex-start",
374
+ maxWidth: "80%",
375
+ padding: "10px 14px",
376
+ borderRadius: msg.sender === "user" ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
377
+ backgroundColor: msg.sender === "user" ? color : "#f3f4f6",
378
+ color: msg.sender === "user" ? "#fff" : "#1f2937",
379
+ wordBreak: "break-word"
380
+ },
381
+ children: msg.text
382
+ },
383
+ msg.id
384
+ )),
385
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
386
+ ]
387
+ }
388
+ ),
389
+ /* @__PURE__ */ jsxRuntime.jsxs(
390
+ "div",
391
+ {
392
+ style: {
393
+ borderTop: "1px solid #e5e7eb",
394
+ padding: "12px",
395
+ display: "flex",
396
+ gap: "8px",
397
+ flexShrink: 0
398
+ },
399
+ children: [
400
+ /* @__PURE__ */ jsxRuntime.jsx(
401
+ "input",
402
+ {
403
+ ref: inputRef,
404
+ type: "text",
405
+ value: input,
406
+ onChange: (e) => setInput(e.target.value),
407
+ onKeyDown: handleKeyDown,
408
+ placeholder,
409
+ "aria-label": "Type your message",
410
+ style: {
411
+ flex: 1,
412
+ border: "1px solid #d1d5db",
413
+ borderRadius: "8px",
414
+ padding: "10px 12px",
415
+ fontSize: "14px",
416
+ outline: "none",
417
+ fontFamily: "inherit"
418
+ }
419
+ }
420
+ ),
421
+ /* @__PURE__ */ jsxRuntime.jsx(
422
+ "button",
423
+ {
424
+ onClick: () => void sendMessage(),
425
+ disabled: sending || !input.trim(),
426
+ "aria-label": "Send message",
427
+ style: {
428
+ backgroundColor: color,
429
+ color: "#fff",
430
+ border: "none",
431
+ borderRadius: "8px",
432
+ padding: "10px 16px",
433
+ cursor: sending || !input.trim() ? "not-allowed" : "pointer",
434
+ opacity: sending || !input.trim() ? 0.5 : 1,
435
+ fontWeight: 600,
436
+ fontSize: "14px",
437
+ fontFamily: "inherit",
438
+ transition: "opacity 0.2s ease"
439
+ },
440
+ children: "Send"
441
+ }
442
+ )
443
+ ]
444
+ }
445
+ )
446
+ ]
447
+ }
448
+ )
449
+ ] });
450
+ }
451
+ function injectModalKeyframes() {
452
+ if (typeof document === "undefined") return;
453
+ if (document.querySelector("[data-support-chat-modal-keyframes]")) return;
454
+ const style = document.createElement("style");
455
+ style.setAttribute("data-support-chat-modal-keyframes", "");
456
+ style.textContent = `
457
+ @keyframes sc-modal-backdrop-in {
458
+ from { opacity: 0; }
459
+ to { opacity: 1; }
460
+ }
461
+ @keyframes sc-modal-backdrop-out {
462
+ from { opacity: 1; }
463
+ to { opacity: 0; }
464
+ }
465
+ @keyframes sc-modal-slide-in {
466
+ from { opacity: 0; transform: translateY(20px) scale(0.96); }
467
+ to { opacity: 1; transform: translateY(0) scale(1); }
468
+ }
469
+ @keyframes sc-modal-slide-out {
470
+ from { opacity: 1; transform: translateY(0) scale(1); }
471
+ to { opacity: 0; transform: translateY(20px) scale(0.96); }
472
+ }
473
+ `;
474
+ document.head.appendChild(style);
475
+ }
476
+ function SupportChatModal({
477
+ apiUrl,
478
+ isOpen,
479
+ onClose,
480
+ color = "#2563eb",
481
+ title = "Contact Us",
482
+ placeholder = "Type a message...",
483
+ user
484
+ }) {
485
+ const { messages, input, setInput, sending, sendMessage, handleKeyDown } = useChatEngine({ apiUrl, user });
486
+ const modalRef = react.useRef(null);
487
+ const inputRef = react.useRef(null);
488
+ const messagesEndRef = react.useRef(null);
489
+ const closingRef = react.useRef(false);
490
+ const backdropRef = react.useRef(null);
491
+ react.useEffect(() => {
492
+ injectModalKeyframes();
493
+ }, []);
494
+ react.useEffect(() => {
495
+ if (isOpen && inputRef.current) {
496
+ const timer = setTimeout(() => inputRef.current?.focus(), 50);
497
+ return () => clearTimeout(timer);
498
+ }
499
+ }, [isOpen]);
500
+ react.useEffect(() => {
501
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
502
+ }, [messages]);
503
+ react.useEffect(() => {
504
+ if (isOpen) {
505
+ const prev = document.body.style.overflow;
506
+ document.body.style.overflow = "hidden";
507
+ return () => {
508
+ document.body.style.overflow = prev;
509
+ };
510
+ }
511
+ }, [isOpen]);
512
+ react.useEffect(() => {
513
+ if (!isOpen || !modalRef.current) return;
514
+ const modal = modalRef.current;
515
+ const handleTrapKeyDown = (e) => {
516
+ if (e.key === "Escape") {
517
+ onClose();
518
+ return;
519
+ }
520
+ if (e.key !== "Tab") return;
521
+ const focusable = modal.querySelectorAll(
522
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
523
+ );
524
+ if (focusable.length === 0) return;
525
+ const first = focusable[0];
526
+ const last = focusable[focusable.length - 1];
527
+ if (e.shiftKey) {
528
+ if (document.activeElement === first) {
529
+ e.preventDefault();
530
+ last.focus();
531
+ }
532
+ } else {
533
+ if (document.activeElement === last) {
534
+ e.preventDefault();
535
+ first.focus();
536
+ }
537
+ }
538
+ };
539
+ document.addEventListener("keydown", handleTrapKeyDown);
540
+ return () => document.removeEventListener("keydown", handleTrapKeyDown);
541
+ }, [isOpen, onClose]);
542
+ const handleClose = react.useCallback(() => {
543
+ closingRef.current = true;
544
+ onClose();
545
+ }, [onClose]);
546
+ const handleBackdropClick = react.useCallback(
547
+ (e) => {
548
+ if (e.target === e.currentTarget) {
549
+ handleClose();
550
+ }
551
+ },
552
+ [handleClose]
553
+ );
554
+ if (!isOpen) return null;
555
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
556
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
557
+ @media (max-width: 479px) {
558
+ [data-support-chat-modal] {
559
+ width: 100vw !important;
560
+ height: 100vh !important;
561
+ max-width: none !important;
562
+ max-height: none !important;
563
+ border-radius: 0 !important;
564
+ margin: 0 !important;
565
+ }
566
+ [data-support-chat-modal-backdrop] {
567
+ align-items: stretch !important;
568
+ justify-content: stretch !important;
569
+ }
570
+ }
571
+ ` }),
572
+ /* @__PURE__ */ jsxRuntime.jsx(
573
+ "div",
574
+ {
575
+ ref: backdropRef,
576
+ "data-support-chat-modal-backdrop": "",
577
+ onClick: handleBackdropClick,
578
+ style: {
579
+ position: "fixed",
580
+ inset: 0,
581
+ zIndex: 1e5,
582
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
583
+ display: "flex",
584
+ alignItems: "center",
585
+ justifyContent: "center",
586
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
587
+ fontSize: "14px",
588
+ animation: "sc-modal-backdrop-in 0.2s ease-out forwards"
589
+ },
590
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
591
+ "div",
592
+ {
593
+ ref: modalRef,
594
+ role: "dialog",
595
+ "aria-label": "Support chat",
596
+ "aria-modal": "true",
597
+ "data-support-chat-modal": "",
598
+ style: {
599
+ width: "500px",
600
+ maxWidth: "calc(100vw - 32px)",
601
+ height: "600px",
602
+ maxHeight: "calc(100vh - 64px)",
603
+ backgroundColor: "#fff",
604
+ borderRadius: "16px",
605
+ overflow: "hidden",
606
+ display: "flex",
607
+ flexDirection: "column",
608
+ boxShadow: "0 20px 60px rgba(0, 0, 0, 0.2)",
609
+ animation: "sc-modal-slide-in 0.3s ease-out forwards"
610
+ },
611
+ children: [
612
+ /* @__PURE__ */ jsxRuntime.jsxs(
613
+ "div",
614
+ {
615
+ style: {
616
+ backgroundColor: color,
617
+ color: "#fff",
618
+ padding: "20px 24px",
619
+ display: "flex",
620
+ alignItems: "center",
621
+ justifyContent: "space-between",
622
+ flexShrink: 0
623
+ },
624
+ children: [
625
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, fontSize: "18px" }, children: title }),
626
+ /* @__PURE__ */ jsxRuntime.jsx(
627
+ "button",
628
+ {
629
+ onClick: handleClose,
630
+ "aria-label": "Close chat",
631
+ style: {
632
+ background: "none",
633
+ border: "none",
634
+ color: "#fff",
635
+ cursor: "pointer",
636
+ padding: "4px",
637
+ lineHeight: 1,
638
+ fontSize: "20px"
639
+ },
640
+ children: "\u2715"
641
+ }
642
+ )
643
+ ]
644
+ }
645
+ ),
646
+ /* @__PURE__ */ jsxRuntime.jsxs(
647
+ "div",
648
+ {
649
+ role: "log",
650
+ "aria-live": "polite",
651
+ "aria-label": "Chat messages",
652
+ style: {
653
+ flex: 1,
654
+ overflowY: "auto",
655
+ padding: "20px",
656
+ display: "flex",
657
+ flexDirection: "column",
658
+ gap: "8px"
659
+ },
660
+ children: [
661
+ messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(
662
+ "div",
663
+ {
664
+ style: {
665
+ color: "#9ca3af",
666
+ textAlign: "center",
667
+ marginTop: "60px"
668
+ },
669
+ children: "Send us a message and we will get back to you!"
670
+ }
671
+ ),
672
+ messages.map((msg) => /* @__PURE__ */ jsxRuntime.jsx(
673
+ "div",
674
+ {
675
+ style: {
676
+ alignSelf: msg.sender === "user" ? "flex-end" : "flex-start",
677
+ maxWidth: "80%",
678
+ padding: "10px 14px",
679
+ borderRadius: msg.sender === "user" ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
680
+ backgroundColor: msg.sender === "user" ? color : "#f3f4f6",
681
+ color: msg.sender === "user" ? "#fff" : "#1f2937",
682
+ wordBreak: "break-word"
683
+ },
684
+ children: msg.text
685
+ },
686
+ msg.id
687
+ )),
688
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
689
+ ]
690
+ }
691
+ ),
692
+ /* @__PURE__ */ jsxRuntime.jsxs(
693
+ "div",
694
+ {
695
+ style: {
696
+ borderTop: "1px solid #e5e7eb",
697
+ padding: "16px 20px",
698
+ display: "flex",
699
+ gap: "8px",
700
+ flexShrink: 0
701
+ },
702
+ children: [
703
+ /* @__PURE__ */ jsxRuntime.jsx(
704
+ "input",
705
+ {
706
+ ref: inputRef,
707
+ type: "text",
708
+ value: input,
709
+ onChange: (e) => setInput(e.target.value),
710
+ onKeyDown: handleKeyDown,
711
+ placeholder,
712
+ "aria-label": "Type your message",
713
+ style: {
714
+ flex: 1,
715
+ border: "1px solid #d1d5db",
716
+ borderRadius: "8px",
717
+ padding: "12px 14px",
718
+ fontSize: "14px",
719
+ outline: "none",
720
+ fontFamily: "inherit"
721
+ }
722
+ }
723
+ ),
724
+ /* @__PURE__ */ jsxRuntime.jsx(
725
+ "button",
726
+ {
727
+ onClick: () => void sendMessage(),
728
+ disabled: sending || !input.trim(),
729
+ "aria-label": "Send message",
730
+ style: {
731
+ backgroundColor: color,
732
+ color: "#fff",
733
+ border: "none",
734
+ borderRadius: "8px",
735
+ padding: "12px 20px",
736
+ cursor: sending || !input.trim() ? "not-allowed" : "pointer",
737
+ opacity: sending || !input.trim() ? 0.5 : 1,
738
+ fontWeight: 600,
739
+ fontSize: "14px",
740
+ fontFamily: "inherit",
741
+ transition: "opacity 0.2s ease"
742
+ },
743
+ children: "Send"
744
+ }
745
+ )
746
+ ]
747
+ }
748
+ )
749
+ ]
750
+ }
751
+ )
752
+ }
753
+ )
754
+ ] });
755
+ }
756
+ function useSupportChat() {
757
+ const [isOpen, setIsOpen] = react.useState(false);
758
+ const open = react.useCallback(() => setIsOpen(true), []);
759
+ const close = react.useCallback(() => setIsOpen(false), []);
760
+ const toggle = react.useCallback(() => setIsOpen((prev) => !prev), []);
761
+ return { open, close, toggle, isOpen };
762
+ }
763
+
764
+ exports.ChatBubble = ChatBubble;
765
+ exports.SupportChatModal = SupportChatModal;
766
+ exports.collectAnonymousContext = collectAnonymousContext;
767
+ exports.getSessionId = getSessionId;
768
+ exports.useChatEngine = useChatEngine;
769
+ exports.useSupportChat = useSupportChat;
770
+ //# sourceMappingURL=index.cjs.map
771
+ //# sourceMappingURL=index.cjs.map