simple-support-chat 0.1.0 → 0.1.1
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/client/index.cjs +66 -15
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +67 -16
- package/dist/client/index.js.map +1 -1
- package/package.json +1 -1
package/dist/client/index.cjs
CHANGED
|
@@ -117,6 +117,49 @@ function useChatEngine({
|
|
|
117
117
|
);
|
|
118
118
|
return { messages, input, setInput, sending, sendMessage, handleKeyDown };
|
|
119
119
|
}
|
|
120
|
+
function useColorScheme() {
|
|
121
|
+
const [scheme, setScheme] = react.useState(() => {
|
|
122
|
+
if (typeof window === "undefined") return "light";
|
|
123
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
124
|
+
});
|
|
125
|
+
react.useEffect(() => {
|
|
126
|
+
if (typeof window === "undefined") return;
|
|
127
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
128
|
+
const handler = (e) => {
|
|
129
|
+
setScheme(e.matches ? "dark" : "light");
|
|
130
|
+
};
|
|
131
|
+
mq.addEventListener("change", handler);
|
|
132
|
+
return () => mq.removeEventListener("change", handler);
|
|
133
|
+
}, []);
|
|
134
|
+
return scheme;
|
|
135
|
+
}
|
|
136
|
+
var lightTokens = {
|
|
137
|
+
panelBg: "#ffffff",
|
|
138
|
+
panelBorder: "#e5e7eb",
|
|
139
|
+
inputBg: "#ffffff",
|
|
140
|
+
inputBorder: "#d1d5db",
|
|
141
|
+
inputText: "#1f2937",
|
|
142
|
+
inputPlaceholder: "#9ca3af",
|
|
143
|
+
receivedBg: "#f3f4f6",
|
|
144
|
+
receivedText: "#1f2937",
|
|
145
|
+
emptyText: "#9ca3af",
|
|
146
|
+
inputAreaBorder: "#e5e7eb"
|
|
147
|
+
};
|
|
148
|
+
var darkTokens = {
|
|
149
|
+
panelBg: "#1f2937",
|
|
150
|
+
panelBorder: "#374151",
|
|
151
|
+
inputBg: "#111827",
|
|
152
|
+
inputBorder: "#4b5563",
|
|
153
|
+
inputText: "#f9fafb",
|
|
154
|
+
inputPlaceholder: "#9ca3af",
|
|
155
|
+
receivedBg: "#374151",
|
|
156
|
+
receivedText: "#f3f4f6",
|
|
157
|
+
emptyText: "#6b7280",
|
|
158
|
+
inputAreaBorder: "#374151"
|
|
159
|
+
};
|
|
160
|
+
function getThemeTokens(scheme) {
|
|
161
|
+
return scheme === "dark" ? darkTokens : lightTokens;
|
|
162
|
+
}
|
|
120
163
|
function injectKeyframes() {
|
|
121
164
|
if (typeof document === "undefined") return;
|
|
122
165
|
if (document.querySelector("[data-support-chat-keyframes]")) return;
|
|
@@ -144,6 +187,8 @@ function ChatBubble({
|
|
|
144
187
|
user
|
|
145
188
|
}) {
|
|
146
189
|
const [isOpen, setIsOpen] = react.useState(false);
|
|
190
|
+
const colorScheme = useColorScheme();
|
|
191
|
+
const theme = react.useMemo(() => getThemeTokens(colorScheme), [colorScheme]);
|
|
147
192
|
const { messages, input, setInput, sending, sendMessage, handleKeyDown } = useChatEngine({ apiUrl, user });
|
|
148
193
|
const [panelState, setPanelState] = react.useState(
|
|
149
194
|
"closed"
|
|
@@ -301,8 +346,8 @@ function ChatBubble({
|
|
|
301
346
|
boxShadow: "0 8px 30px rgba(0,0,0,0.12)",
|
|
302
347
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
303
348
|
fontSize: "14px",
|
|
304
|
-
backgroundColor:
|
|
305
|
-
border:
|
|
349
|
+
backgroundColor: theme.panelBg,
|
|
350
|
+
border: `1px solid ${theme.panelBorder}`,
|
|
306
351
|
animation: panelState === "open" ? "sc-slide-in 0.25s ease-out forwards" : "sc-slide-out 0.2s ease-in forwards"
|
|
307
352
|
},
|
|
308
353
|
children: [
|
|
@@ -359,7 +404,7 @@ function ChatBubble({
|
|
|
359
404
|
"div",
|
|
360
405
|
{
|
|
361
406
|
style: {
|
|
362
|
-
color:
|
|
407
|
+
color: theme.emptyText,
|
|
363
408
|
textAlign: "center",
|
|
364
409
|
marginTop: "40px"
|
|
365
410
|
},
|
|
@@ -374,8 +419,8 @@ function ChatBubble({
|
|
|
374
419
|
maxWidth: "80%",
|
|
375
420
|
padding: "10px 14px",
|
|
376
421
|
borderRadius: msg.sender === "user" ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
|
|
377
|
-
backgroundColor: msg.sender === "user" ? color :
|
|
378
|
-
color: msg.sender === "user" ? "#fff" :
|
|
422
|
+
backgroundColor: msg.sender === "user" ? color : theme.receivedBg,
|
|
423
|
+
color: msg.sender === "user" ? "#fff" : theme.receivedText,
|
|
379
424
|
wordBreak: "break-word"
|
|
380
425
|
},
|
|
381
426
|
children: msg.text
|
|
@@ -390,7 +435,7 @@ function ChatBubble({
|
|
|
390
435
|
"div",
|
|
391
436
|
{
|
|
392
437
|
style: {
|
|
393
|
-
borderTop:
|
|
438
|
+
borderTop: `1px solid ${theme.inputAreaBorder}`,
|
|
394
439
|
padding: "12px",
|
|
395
440
|
display: "flex",
|
|
396
441
|
gap: "8px",
|
|
@@ -409,12 +454,14 @@ function ChatBubble({
|
|
|
409
454
|
"aria-label": "Type your message",
|
|
410
455
|
style: {
|
|
411
456
|
flex: 1,
|
|
412
|
-
border:
|
|
457
|
+
border: `1px solid ${theme.inputBorder}`,
|
|
413
458
|
borderRadius: "8px",
|
|
414
459
|
padding: "10px 12px",
|
|
415
460
|
fontSize: "14px",
|
|
416
461
|
outline: "none",
|
|
417
|
-
fontFamily: "inherit"
|
|
462
|
+
fontFamily: "inherit",
|
|
463
|
+
backgroundColor: theme.inputBg,
|
|
464
|
+
color: theme.inputText
|
|
418
465
|
}
|
|
419
466
|
}
|
|
420
467
|
),
|
|
@@ -483,6 +530,8 @@ function SupportChatModal({
|
|
|
483
530
|
user
|
|
484
531
|
}) {
|
|
485
532
|
const { messages, input, setInput, sending, sendMessage, handleKeyDown } = useChatEngine({ apiUrl, user });
|
|
533
|
+
const colorScheme = useColorScheme();
|
|
534
|
+
const theme = react.useMemo(() => getThemeTokens(colorScheme), [colorScheme]);
|
|
486
535
|
const modalRef = react.useRef(null);
|
|
487
536
|
const inputRef = react.useRef(null);
|
|
488
537
|
const messagesEndRef = react.useRef(null);
|
|
@@ -600,7 +649,7 @@ function SupportChatModal({
|
|
|
600
649
|
maxWidth: "calc(100vw - 32px)",
|
|
601
650
|
height: "600px",
|
|
602
651
|
maxHeight: "calc(100vh - 64px)",
|
|
603
|
-
backgroundColor:
|
|
652
|
+
backgroundColor: theme.panelBg,
|
|
604
653
|
borderRadius: "16px",
|
|
605
654
|
overflow: "hidden",
|
|
606
655
|
display: "flex",
|
|
@@ -662,7 +711,7 @@ function SupportChatModal({
|
|
|
662
711
|
"div",
|
|
663
712
|
{
|
|
664
713
|
style: {
|
|
665
|
-
color:
|
|
714
|
+
color: theme.emptyText,
|
|
666
715
|
textAlign: "center",
|
|
667
716
|
marginTop: "60px"
|
|
668
717
|
},
|
|
@@ -677,8 +726,8 @@ function SupportChatModal({
|
|
|
677
726
|
maxWidth: "80%",
|
|
678
727
|
padding: "10px 14px",
|
|
679
728
|
borderRadius: msg.sender === "user" ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
|
|
680
|
-
backgroundColor: msg.sender === "user" ? color :
|
|
681
|
-
color: msg.sender === "user" ? "#fff" :
|
|
729
|
+
backgroundColor: msg.sender === "user" ? color : theme.receivedBg,
|
|
730
|
+
color: msg.sender === "user" ? "#fff" : theme.receivedText,
|
|
682
731
|
wordBreak: "break-word"
|
|
683
732
|
},
|
|
684
733
|
children: msg.text
|
|
@@ -693,7 +742,7 @@ function SupportChatModal({
|
|
|
693
742
|
"div",
|
|
694
743
|
{
|
|
695
744
|
style: {
|
|
696
|
-
borderTop:
|
|
745
|
+
borderTop: `1px solid ${theme.inputAreaBorder}`,
|
|
697
746
|
padding: "16px 20px",
|
|
698
747
|
display: "flex",
|
|
699
748
|
gap: "8px",
|
|
@@ -712,12 +761,14 @@ function SupportChatModal({
|
|
|
712
761
|
"aria-label": "Type your message",
|
|
713
762
|
style: {
|
|
714
763
|
flex: 1,
|
|
715
|
-
border:
|
|
764
|
+
border: `1px solid ${theme.inputBorder}`,
|
|
716
765
|
borderRadius: "8px",
|
|
717
766
|
padding: "12px 14px",
|
|
718
767
|
fontSize: "14px",
|
|
719
768
|
outline: "none",
|
|
720
|
-
fontFamily: "inherit"
|
|
769
|
+
fontFamily: "inherit",
|
|
770
|
+
backgroundColor: theme.inputBg,
|
|
771
|
+
color: theme.inputText
|
|
721
772
|
}
|
|
722
773
|
}
|
|
723
774
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/context.ts","../../src/client/useChatEngine.ts","../../src/client/ChatBubble.tsx","../../src/client/SupportChatModal.tsx","../../src/client/useSupportChat.ts"],"names":["useState","useCallback","useRef","useEffect","jsxs","Fragment","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { AnonymousContext } from \"./types\";\r\n\r\nconst SESSION_KEY = \"support-chat-session-id\";\r\n\r\n/** Generate a random session ID */\r\nfunction generateSessionId(): string {\r\n const chars =\r\n \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n let id = \"\";\r\n for (let i = 0; i < 16; i++) {\r\n id += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return id;\r\n}\r\n\r\n/** Get or create a persistent session ID from localStorage */\r\nexport function getSessionId(): string {\r\n if (typeof window === \"undefined\") return generateSessionId();\r\n\r\n try {\r\n const existing = localStorage.getItem(SESSION_KEY);\r\n if (existing) return existing;\r\n\r\n const newId = generateSessionId();\r\n localStorage.setItem(SESSION_KEY, newId);\r\n return newId;\r\n } catch {\r\n // localStorage may be blocked (private browsing, etc.)\r\n return generateSessionId();\r\n }\r\n}\r\n\r\n/** Collect anonymous browser context */\r\nexport function collectAnonymousContext(): AnonymousContext {\r\n const sessionId = getSessionId();\r\n\r\n if (typeof window === \"undefined\") {\r\n return {\r\n pageUrl: \"\",\r\n referrer: \"\",\r\n userAgent: \"\",\r\n screenSize: \"\",\r\n timezone: \"\",\r\n sessionId,\r\n };\r\n }\r\n\r\n return {\r\n pageUrl: window.location.href,\r\n referrer: document.referrer,\r\n userAgent: navigator.userAgent,\r\n screenSize: `${window.screen.width}x${window.screen.height}`,\r\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n sessionId,\r\n };\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { ChatMessage, ChatUser } from \"./types\";\r\nimport { collectAnonymousContext, getSessionId } from \"./context\";\r\n\r\n/** Options for initializing the chat engine */\r\nexport interface ChatEngineOptions {\r\n /** URL of the support chat API endpoint */\r\n apiUrl: string;\r\n /** Authenticated user info (optional) */\r\n user?: ChatUser;\r\n}\r\n\r\n/** Return value from useChatEngine */\r\nexport interface ChatEngineState {\r\n /** All chat messages */\r\n messages: ChatMessage[];\r\n /** Current input value */\r\n input: string;\r\n /** Update the input value */\r\n setInput: (value: string) => void;\r\n /** Whether a message is currently being sent */\r\n sending: boolean;\r\n /** Send the current input as a message */\r\n sendMessage: () => Promise<void>;\r\n /** Handle keydown on the input (Enter to send) */\r\n handleKeyDown: (e: React.KeyboardEvent) => void;\r\n}\r\n\r\n/**\r\n * Shared chat engine hook used by both ChatBubble and SupportChatModal.\r\n * Manages message state, input, and API communication.\r\n */\r\nexport function useChatEngine({\r\n apiUrl,\r\n user,\r\n}: ChatEngineOptions): ChatEngineState {\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\r\n const [sending, setSending] = useState(false);\r\n\r\n const sendMessage = useCallback(async () => {\r\n const text = input.trim();\r\n if (!text || sending) return;\r\n\r\n const msg: ChatMessage = {\r\n id: `msg-${Date.now()}`,\r\n text,\r\n sender: \"user\",\r\n timestamp: Date.now(),\r\n };\r\n\r\n setMessages((prev) => [...prev, msg]);\r\n setInput(\"\");\r\n setSending(true);\r\n\r\n try {\r\n const context = collectAnonymousContext();\r\n const response = await fetch(apiUrl, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n message: text,\r\n user: user ?? undefined,\r\n sessionId: user?.id ?? getSessionId(),\r\n context,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Failed to send message. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n }\r\n } catch {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Connection error. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n } finally {\r\n setSending(false);\r\n }\r\n }, [input, sending, apiUrl, user]);\r\n\r\n const handleKeyDown = useCallback(\r\n (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\" && !e.shiftKey) {\r\n e.preventDefault();\r\n void sendMessage();\r\n }\r\n },\r\n [sendMessage],\r\n );\r\n\r\n return { messages, input, setInput, sending, sendMessage, handleKeyDown };\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from \"react\";\r\nimport type { ChatBubbleProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\n\r\n/**\r\n * Inject a <style> tag for keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-slide-in {\r\n from { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * ChatBubble -- floating support chat widget.\r\n *\r\n * Renders a circular button that opens an inline chat panel.\r\n * Messages are sent via POST to the configured `apiUrl`.\r\n *\r\n * @example\r\n * ```tsx\r\n * <ChatBubble apiUrl=\"/api/support\" />\r\n * ```\r\n */\r\nexport function ChatBubble({\r\n apiUrl,\r\n position = \"bottom-right\",\r\n color = \"#2563eb\",\r\n title = \"Support\",\r\n placeholder = \"Type a message...\",\r\n show = true,\r\n user,\r\n}: ChatBubbleProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n // Use shared chat engine for message logic\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n // Animation state: \"open\" | \"closing\" | \"closed\"\r\n const [panelState, setPanelState] = useState<\"open\" | \"closing\" | \"closed\">(\r\n \"closed\",\r\n );\r\n\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n // Inject keyframe animations on first render\r\n useEffect(() => {\r\n injectKeyframes();\r\n }, []);\r\n\r\n // Sync isOpen -> panelState\r\n useEffect(() => {\r\n if (isOpen) {\r\n setPanelState(\"open\");\r\n } else if (panelState === \"open\") {\r\n // Start closing animation\r\n setPanelState(\"closing\");\r\n }\r\n }, [isOpen]);\r\n\r\n // When closing animation ends, set to fully closed\r\n const handleAnimationEnd = useCallback(() => {\r\n if (panelState === \"closing\") {\r\n setPanelState(\"closed\");\r\n }\r\n }, [panelState]);\r\n\r\n // Focus input when panel opens\r\n useEffect(() => {\r\n if (panelState === \"open\" && inputRef.current) {\r\n inputRef.current.focus();\r\n }\r\n }, [panelState]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Focus trap: keep Tab cycling inside the dialog when open\r\n useEffect(() => {\r\n if (panelState !== \"open\" || !panelRef.current) return;\r\n\r\n const panel = panelRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n setIsOpen(false);\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = panel.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [panelState]);\r\n\r\n const toggle = useCallback(() => setIsOpen((o) => !o), []);\r\n\r\n // Position styles for the bubble button\r\n const positionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n ...(position.includes(\"bottom\") ? { bottom: \"20px\" } : { top: \"20px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Panel position & responsive sizing\r\n // On mobile (<480px viewport), expand to near-full screen\r\n const panelPositionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n width: \"380px\",\r\n maxWidth: \"calc(100vw - 40px)\",\r\n height: \"500px\",\r\n maxHeight: \"calc(100vh - 120px)\",\r\n ...(position.includes(\"bottom\") ? { bottom: \"80px\" } : { top: \"80px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Check if panel is visible (open or animating out)\r\n const showPanel = panelState === \"open\" || panelState === \"closing\";\r\n\r\n return (\r\n <>\r\n {/* Responsive overrides injected as inline <style> */}\r\n {showPanel && (\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-panel] {\r\n width: calc(100vw - 16px) !important;\r\n height: calc(100vh - 100px) !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n left: 8px !important;\r\n right: 8px !important;\r\n bottom: 72px !important;\r\n border-radius: 12px !important;\r\n }\r\n }\r\n `}</style>\r\n )}\r\n\r\n {/* Floating bubble button */}\r\n {show && (\r\n <button\r\n onClick={toggle}\r\n aria-label={isOpen ? \"Close support chat\" : \"Open support chat\"}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"dialog\"\r\n style={{\r\n ...positionStyles,\r\n width: \"56px\",\r\n height: \"56px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: color,\r\n border: \"none\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\r\n transition: \"transform 0.2s ease, box-shadow 0.2s ease\",\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = \"scale(1.1)\";\r\n e.currentTarget.style.boxShadow = \"0 6px 20px rgba(0,0,0,0.2)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = \"scale(1)\";\r\n e.currentTarget.style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n >\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n {isOpen ? (\r\n <path d=\"M18 6L6 18M6 6l12 12\" />\r\n ) : (\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\r\n )}\r\n </svg>\r\n </button>\r\n )}\r\n\r\n {/* Chat panel */}\r\n {showPanel && (\r\n <div\r\n ref={panelRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-panel=\"\"\r\n onAnimationEnd={handleAnimationEnd}\r\n style={{\r\n ...panelPositionStyles,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n borderRadius: \"12px\",\r\n overflow: \"hidden\",\r\n boxShadow: \"0 8px 30px rgba(0,0,0,0.12)\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n backgroundColor: \"#fff\",\r\n border: \"1px solid #e5e7eb\",\r\n animation:\r\n panelState === \"open\"\r\n ? \"sc-slide-in 0.25s ease-out forwards\"\r\n : \"sc-slide-out 0.2s ease-in forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"16px\" }}>{title}</span>\r\n <button\r\n onClick={() => setIsOpen(false)}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"18px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: \"#9ca3af\",\r\n textAlign: \"center\",\r\n marginTop: \"40px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : \"#f3f4f6\",\r\n color: msg.sender === \"user\" ? \"#fff\" : \"#1f2937\",\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: \"1px solid #e5e7eb\",\r\n padding: \"12px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: \"1px solid #d1d5db\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 12px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 16px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useRef, useEffect, useCallback } from \"react\";\r\nimport type { SupportChatModalProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\n\r\n/**\r\n * Inject a <style> tag for modal keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectModalKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-modal-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-modal-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-modal-backdrop-in {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n }\r\n @keyframes sc-modal-backdrop-out {\r\n from { opacity: 1; }\r\n to { opacity: 0; }\r\n }\r\n @keyframes sc-modal-slide-in {\r\n from { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-modal-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * SupportChatModal -- modal-based support chat.\r\n *\r\n * Renders a centered modal dialog for support chat, designed to be\r\n * triggered from custom UI elements (e.g., \"Contact Us\" links).\r\n *\r\n * Desktop: centered modal, max-width ~500px, with backdrop overlay.\r\n * Mobile: full-screen modal.\r\n *\r\n * Use with the `useSupportChat()` hook:\r\n *\r\n * @example\r\n * ```tsx\r\n * import { SupportChatModal, useSupportChat } from 'support-chat';\r\n *\r\n * function App() {\r\n * const { open, close, isOpen } = useSupportChat();\r\n * return (\r\n * <>\r\n * <button onClick={open}>Contact Us</button>\r\n * <SupportChatModal\r\n * apiUrl=\"/api/support\"\r\n * isOpen={isOpen}\r\n * onClose={close}\r\n * />\r\n * </>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function SupportChatModal({\r\n apiUrl,\r\n isOpen,\r\n onClose,\r\n color = \"#2563eb\",\r\n title = \"Contact Us\",\r\n placeholder = \"Type a message...\",\r\n user,\r\n}: SupportChatModalProps) {\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n const modalRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const closingRef = useRef(false);\r\n const backdropRef = useRef<HTMLDivElement>(null);\r\n\r\n // Inject modal keyframes on first render\r\n useEffect(() => {\r\n injectModalKeyframes();\r\n }, []);\r\n\r\n // Focus input when modal opens\r\n useEffect(() => {\r\n if (isOpen && inputRef.current) {\r\n // Small delay to let the modal render before focusing\r\n const timer = setTimeout(() => inputRef.current?.focus(), 50);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [isOpen]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Prevent body scroll when modal is open\r\n useEffect(() => {\r\n if (isOpen) {\r\n const prev = document.body.style.overflow;\r\n document.body.style.overflow = \"hidden\";\r\n return () => {\r\n document.body.style.overflow = prev;\r\n };\r\n }\r\n }, [isOpen]);\r\n\r\n // Focus trap and Escape key handling\r\n useEffect(() => {\r\n if (!isOpen || !modalRef.current) return;\r\n\r\n const modal = modalRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onClose();\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [isOpen, onClose]);\r\n\r\n // Handle close with animation\r\n const handleClose = useCallback(() => {\r\n closingRef.current = true;\r\n onClose();\r\n }, [onClose]);\r\n\r\n // Handle backdrop click\r\n const handleBackdropClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget) {\r\n handleClose();\r\n }\r\n },\r\n [handleClose],\r\n );\r\n\r\n if (!isOpen) return null;\r\n\r\n return (\r\n <>\r\n {/* Responsive style for full-screen on mobile */}\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-modal] {\r\n width: 100vw !important;\r\n height: 100vh !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n border-radius: 0 !important;\r\n margin: 0 !important;\r\n }\r\n [data-support-chat-modal-backdrop] {\r\n align-items: stretch !important;\r\n justify-content: stretch !important;\r\n }\r\n }\r\n `}</style>\r\n\r\n {/* Backdrop overlay */}\r\n <div\r\n ref={backdropRef}\r\n data-support-chat-modal-backdrop=\"\"\r\n onClick={handleBackdropClick}\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n zIndex: 100000,\r\n backgroundColor: \"rgba(0, 0, 0, 0.5)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n animation: \"sc-modal-backdrop-in 0.2s ease-out forwards\",\r\n }}\r\n >\r\n {/* Modal dialog */}\r\n <div\r\n ref={modalRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-modal=\"\"\r\n style={{\r\n width: \"500px\",\r\n maxWidth: \"calc(100vw - 32px)\",\r\n height: \"600px\",\r\n maxHeight: \"calc(100vh - 64px)\",\r\n backgroundColor: \"#fff\",\r\n borderRadius: \"16px\",\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n boxShadow: \"0 20px 60px rgba(0, 0, 0, 0.2)\",\r\n animation: \"sc-modal-slide-in 0.3s ease-out forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"20px 24px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"18px\" }}>{title}</span>\r\n <button\r\n onClick={handleClose}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"20px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"20px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: \"#9ca3af\",\r\n textAlign: \"center\",\r\n marginTop: \"60px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : \"#f3f4f6\",\r\n color: msg.sender === \"user\" ? \"#fff\" : \"#1f2937\",\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: \"1px solid #e5e7eb\",\r\n padding: \"16px 20px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: \"1px solid #d1d5db\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 14px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 20px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { SupportChatState } from \"./types\";\r\n\r\n/**\r\n * Hook that returns controls for opening/closing the support chat.\r\n * Use with `<SupportChatModal />` for modal-triggered chat.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { open, close, toggle, isOpen } = useSupportChat();\r\n * return <button onClick={open}>Contact Us</button>;\r\n * ```\r\n */\r\nexport function useSupportChat(): SupportChatState {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const open = useCallback(() => setIsOpen(true), []);\r\n const close = useCallback(() => setIsOpen(false), []);\r\n const toggle = useCallback(() => setIsOpen((prev) => !prev), []);\r\n\r\n return { open, close, toggle, isOpen };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/context.ts","../../src/client/useChatEngine.ts","../../src/client/useColorScheme.ts","../../src/client/ChatBubble.tsx","../../src/client/SupportChatModal.tsx","../../src/client/useSupportChat.ts"],"names":["useState","useCallback","useEffect","useMemo","useRef","jsxs","Fragment","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { AnonymousContext } from \"./types\";\r\n\r\nconst SESSION_KEY = \"support-chat-session-id\";\r\n\r\n/** Generate a random session ID */\r\nfunction generateSessionId(): string {\r\n const chars =\r\n \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n let id = \"\";\r\n for (let i = 0; i < 16; i++) {\r\n id += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return id;\r\n}\r\n\r\n/** Get or create a persistent session ID from localStorage */\r\nexport function getSessionId(): string {\r\n if (typeof window === \"undefined\") return generateSessionId();\r\n\r\n try {\r\n const existing = localStorage.getItem(SESSION_KEY);\r\n if (existing) return existing;\r\n\r\n const newId = generateSessionId();\r\n localStorage.setItem(SESSION_KEY, newId);\r\n return newId;\r\n } catch {\r\n // localStorage may be blocked (private browsing, etc.)\r\n return generateSessionId();\r\n }\r\n}\r\n\r\n/** Collect anonymous browser context */\r\nexport function collectAnonymousContext(): AnonymousContext {\r\n const sessionId = getSessionId();\r\n\r\n if (typeof window === \"undefined\") {\r\n return {\r\n pageUrl: \"\",\r\n referrer: \"\",\r\n userAgent: \"\",\r\n screenSize: \"\",\r\n timezone: \"\",\r\n sessionId,\r\n };\r\n }\r\n\r\n return {\r\n pageUrl: window.location.href,\r\n referrer: document.referrer,\r\n userAgent: navigator.userAgent,\r\n screenSize: `${window.screen.width}x${window.screen.height}`,\r\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n sessionId,\r\n };\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { ChatMessage, ChatUser } from \"./types\";\r\nimport { collectAnonymousContext, getSessionId } from \"./context\";\r\n\r\n/** Options for initializing the chat engine */\r\nexport interface ChatEngineOptions {\r\n /** URL of the support chat API endpoint */\r\n apiUrl: string;\r\n /** Authenticated user info (optional) */\r\n user?: ChatUser;\r\n}\r\n\r\n/** Return value from useChatEngine */\r\nexport interface ChatEngineState {\r\n /** All chat messages */\r\n messages: ChatMessage[];\r\n /** Current input value */\r\n input: string;\r\n /** Update the input value */\r\n setInput: (value: string) => void;\r\n /** Whether a message is currently being sent */\r\n sending: boolean;\r\n /** Send the current input as a message */\r\n sendMessage: () => Promise<void>;\r\n /** Handle keydown on the input (Enter to send) */\r\n handleKeyDown: (e: React.KeyboardEvent) => void;\r\n}\r\n\r\n/**\r\n * Shared chat engine hook used by both ChatBubble and SupportChatModal.\r\n * Manages message state, input, and API communication.\r\n */\r\nexport function useChatEngine({\r\n apiUrl,\r\n user,\r\n}: ChatEngineOptions): ChatEngineState {\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\r\n const [sending, setSending] = useState(false);\r\n\r\n const sendMessage = useCallback(async () => {\r\n const text = input.trim();\r\n if (!text || sending) return;\r\n\r\n const msg: ChatMessage = {\r\n id: `msg-${Date.now()}`,\r\n text,\r\n sender: \"user\",\r\n timestamp: Date.now(),\r\n };\r\n\r\n setMessages((prev) => [...prev, msg]);\r\n setInput(\"\");\r\n setSending(true);\r\n\r\n try {\r\n const context = collectAnonymousContext();\r\n const response = await fetch(apiUrl, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n message: text,\r\n user: user ?? undefined,\r\n sessionId: user?.id ?? getSessionId(),\r\n context,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Failed to send message. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n }\r\n } catch {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Connection error. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n } finally {\r\n setSending(false);\r\n }\r\n }, [input, sending, apiUrl, user]);\r\n\r\n const handleKeyDown = useCallback(\r\n (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\" && !e.shiftKey) {\r\n e.preventDefault();\r\n void sendMessage();\r\n }\r\n },\r\n [sendMessage],\r\n );\r\n\r\n return { messages, input, setInput, sending, sendMessage, handleKeyDown };\r\n}\r\n","import { useState, useEffect } from \"react\";\r\n\r\nexport type ColorScheme = \"light\" | \"dark\";\r\n\r\n/**\r\n * Detects the user's system color scheme preference via `prefers-color-scheme`.\r\n * Returns \"dark\" or \"light\". Updates reactively if the user changes their system setting.\r\n */\r\nexport function useColorScheme(): ColorScheme {\r\n const [scheme, setScheme] = useState<ColorScheme>(() => {\r\n if (typeof window === \"undefined\") return \"light\";\r\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\r\n ? \"dark\"\r\n : \"light\";\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === \"undefined\") return;\r\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\r\n const handler = (e: MediaQueryListEvent) => {\r\n setScheme(e.matches ? \"dark\" : \"light\");\r\n };\r\n mq.addEventListener(\"change\", handler);\r\n return () => mq.removeEventListener(\"change\", handler);\r\n }, []);\r\n\r\n return scheme;\r\n}\r\n\r\n/** Resolved color tokens for light and dark modes. */\r\nexport interface ThemeTokens {\r\n panelBg: string;\r\n panelBorder: string;\r\n inputBg: string;\r\n inputBorder: string;\r\n inputText: string;\r\n inputPlaceholder: string;\r\n receivedBg: string;\r\n receivedText: string;\r\n emptyText: string;\r\n inputAreaBorder: string;\r\n}\r\n\r\nconst lightTokens: ThemeTokens = {\r\n panelBg: \"#ffffff\",\r\n panelBorder: \"#e5e7eb\",\r\n inputBg: \"#ffffff\",\r\n inputBorder: \"#d1d5db\",\r\n inputText: \"#1f2937\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#f3f4f6\",\r\n receivedText: \"#1f2937\",\r\n emptyText: \"#9ca3af\",\r\n inputAreaBorder: \"#e5e7eb\",\r\n};\r\n\r\nconst darkTokens: ThemeTokens = {\r\n panelBg: \"#1f2937\",\r\n panelBorder: \"#374151\",\r\n inputBg: \"#111827\",\r\n inputBorder: \"#4b5563\",\r\n inputText: \"#f9fafb\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#374151\",\r\n receivedText: \"#f3f4f6\",\r\n emptyText: \"#6b7280\",\r\n inputAreaBorder: \"#374151\",\r\n};\r\n\r\nexport function getThemeTokens(scheme: ColorScheme): ThemeTokens {\r\n return scheme === \"dark\" ? darkTokens : lightTokens;\r\n}\r\n","import { useState, useCallback, useRef, useEffect, useMemo } from \"react\";\r\nimport type { ChatBubbleProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-slide-in {\r\n from { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * ChatBubble -- floating support chat widget.\r\n *\r\n * Renders a circular button that opens an inline chat panel.\r\n * Messages are sent via POST to the configured `apiUrl`.\r\n *\r\n * @example\r\n * ```tsx\r\n * <ChatBubble apiUrl=\"/api/support\" />\r\n * ```\r\n */\r\nexport function ChatBubble({\r\n apiUrl,\r\n position = \"bottom-right\",\r\n color = \"#2563eb\",\r\n title = \"Support\",\r\n placeholder = \"Type a message...\",\r\n show = true,\r\n user,\r\n}: ChatBubbleProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n // Detect system color scheme\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n // Use shared chat engine for message logic\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n // Animation state: \"open\" | \"closing\" | \"closed\"\r\n const [panelState, setPanelState] = useState<\"open\" | \"closing\" | \"closed\">(\r\n \"closed\",\r\n );\r\n\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n // Inject keyframe animations on first render\r\n useEffect(() => {\r\n injectKeyframes();\r\n }, []);\r\n\r\n // Sync isOpen -> panelState\r\n useEffect(() => {\r\n if (isOpen) {\r\n setPanelState(\"open\");\r\n } else if (panelState === \"open\") {\r\n // Start closing animation\r\n setPanelState(\"closing\");\r\n }\r\n }, [isOpen]);\r\n\r\n // When closing animation ends, set to fully closed\r\n const handleAnimationEnd = useCallback(() => {\r\n if (panelState === \"closing\") {\r\n setPanelState(\"closed\");\r\n }\r\n }, [panelState]);\r\n\r\n // Focus input when panel opens\r\n useEffect(() => {\r\n if (panelState === \"open\" && inputRef.current) {\r\n inputRef.current.focus();\r\n }\r\n }, [panelState]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Focus trap: keep Tab cycling inside the dialog when open\r\n useEffect(() => {\r\n if (panelState !== \"open\" || !panelRef.current) return;\r\n\r\n const panel = panelRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n setIsOpen(false);\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = panel.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [panelState]);\r\n\r\n const toggle = useCallback(() => setIsOpen((o) => !o), []);\r\n\r\n // Position styles for the bubble button\r\n const positionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n ...(position.includes(\"bottom\") ? { bottom: \"20px\" } : { top: \"20px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Panel position & responsive sizing\r\n // On mobile (<480px viewport), expand to near-full screen\r\n const panelPositionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n width: \"380px\",\r\n maxWidth: \"calc(100vw - 40px)\",\r\n height: \"500px\",\r\n maxHeight: \"calc(100vh - 120px)\",\r\n ...(position.includes(\"bottom\") ? { bottom: \"80px\" } : { top: \"80px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Check if panel is visible (open or animating out)\r\n const showPanel = panelState === \"open\" || panelState === \"closing\";\r\n\r\n return (\r\n <>\r\n {/* Responsive overrides injected as inline <style> */}\r\n {showPanel && (\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-panel] {\r\n width: calc(100vw - 16px) !important;\r\n height: calc(100vh - 100px) !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n left: 8px !important;\r\n right: 8px !important;\r\n bottom: 72px !important;\r\n border-radius: 12px !important;\r\n }\r\n }\r\n `}</style>\r\n )}\r\n\r\n {/* Floating bubble button */}\r\n {show && (\r\n <button\r\n onClick={toggle}\r\n aria-label={isOpen ? \"Close support chat\" : \"Open support chat\"}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"dialog\"\r\n style={{\r\n ...positionStyles,\r\n width: \"56px\",\r\n height: \"56px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: color,\r\n border: \"none\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\r\n transition: \"transform 0.2s ease, box-shadow 0.2s ease\",\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = \"scale(1.1)\";\r\n e.currentTarget.style.boxShadow = \"0 6px 20px rgba(0,0,0,0.2)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = \"scale(1)\";\r\n e.currentTarget.style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n >\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n {isOpen ? (\r\n <path d=\"M18 6L6 18M6 6l12 12\" />\r\n ) : (\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\r\n )}\r\n </svg>\r\n </button>\r\n )}\r\n\r\n {/* Chat panel */}\r\n {showPanel && (\r\n <div\r\n ref={panelRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-panel=\"\"\r\n onAnimationEnd={handleAnimationEnd}\r\n style={{\r\n ...panelPositionStyles,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n borderRadius: \"12px\",\r\n overflow: \"hidden\",\r\n boxShadow: \"0 8px 30px rgba(0,0,0,0.12)\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n backgroundColor: theme.panelBg,\r\n border: `1px solid ${theme.panelBorder}`,\r\n animation:\r\n panelState === \"open\"\r\n ? \"sc-slide-in 0.25s ease-out forwards\"\r\n : \"sc-slide-out 0.2s ease-in forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"16px\" }}>{title}</span>\r\n <button\r\n onClick={() => setIsOpen(false)}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"18px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"40px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"12px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"10px 12px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 16px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useRef, useEffect, useCallback, useMemo } from \"react\";\r\nimport type { SupportChatModalProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for modal keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectModalKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-modal-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-modal-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-modal-backdrop-in {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n }\r\n @keyframes sc-modal-backdrop-out {\r\n from { opacity: 1; }\r\n to { opacity: 0; }\r\n }\r\n @keyframes sc-modal-slide-in {\r\n from { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-modal-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * SupportChatModal -- modal-based support chat.\r\n *\r\n * Renders a centered modal dialog for support chat, designed to be\r\n * triggered from custom UI elements (e.g., \"Contact Us\" links).\r\n *\r\n * Desktop: centered modal, max-width ~500px, with backdrop overlay.\r\n * Mobile: full-screen modal.\r\n *\r\n * Use with the `useSupportChat()` hook:\r\n *\r\n * @example\r\n * ```tsx\r\n * import { SupportChatModal, useSupportChat } from 'support-chat';\r\n *\r\n * function App() {\r\n * const { open, close, isOpen } = useSupportChat();\r\n * return (\r\n * <>\r\n * <button onClick={open}>Contact Us</button>\r\n * <SupportChatModal\r\n * apiUrl=\"/api/support\"\r\n * isOpen={isOpen}\r\n * onClose={close}\r\n * />\r\n * </>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function SupportChatModal({\r\n apiUrl,\r\n isOpen,\r\n onClose,\r\n color = \"#2563eb\",\r\n title = \"Contact Us\",\r\n placeholder = \"Type a message...\",\r\n user,\r\n}: SupportChatModalProps) {\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n const modalRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const closingRef = useRef(false);\r\n const backdropRef = useRef<HTMLDivElement>(null);\r\n\r\n // Inject modal keyframes on first render\r\n useEffect(() => {\r\n injectModalKeyframes();\r\n }, []);\r\n\r\n // Focus input when modal opens\r\n useEffect(() => {\r\n if (isOpen && inputRef.current) {\r\n // Small delay to let the modal render before focusing\r\n const timer = setTimeout(() => inputRef.current?.focus(), 50);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [isOpen]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Prevent body scroll when modal is open\r\n useEffect(() => {\r\n if (isOpen) {\r\n const prev = document.body.style.overflow;\r\n document.body.style.overflow = \"hidden\";\r\n return () => {\r\n document.body.style.overflow = prev;\r\n };\r\n }\r\n }, [isOpen]);\r\n\r\n // Focus trap and Escape key handling\r\n useEffect(() => {\r\n if (!isOpen || !modalRef.current) return;\r\n\r\n const modal = modalRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onClose();\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [isOpen, onClose]);\r\n\r\n // Handle close with animation\r\n const handleClose = useCallback(() => {\r\n closingRef.current = true;\r\n onClose();\r\n }, [onClose]);\r\n\r\n // Handle backdrop click\r\n const handleBackdropClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget) {\r\n handleClose();\r\n }\r\n },\r\n [handleClose],\r\n );\r\n\r\n if (!isOpen) return null;\r\n\r\n return (\r\n <>\r\n {/* Responsive style for full-screen on mobile */}\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-modal] {\r\n width: 100vw !important;\r\n height: 100vh !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n border-radius: 0 !important;\r\n margin: 0 !important;\r\n }\r\n [data-support-chat-modal-backdrop] {\r\n align-items: stretch !important;\r\n justify-content: stretch !important;\r\n }\r\n }\r\n `}</style>\r\n\r\n {/* Backdrop overlay */}\r\n <div\r\n ref={backdropRef}\r\n data-support-chat-modal-backdrop=\"\"\r\n onClick={handleBackdropClick}\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n zIndex: 100000,\r\n backgroundColor: \"rgba(0, 0, 0, 0.5)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n animation: \"sc-modal-backdrop-in 0.2s ease-out forwards\",\r\n }}\r\n >\r\n {/* Modal dialog */}\r\n <div\r\n ref={modalRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-modal=\"\"\r\n style={{\r\n width: \"500px\",\r\n maxWidth: \"calc(100vw - 32px)\",\r\n height: \"600px\",\r\n maxHeight: \"calc(100vh - 64px)\",\r\n backgroundColor: theme.panelBg,\r\n borderRadius: \"16px\",\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n boxShadow: \"0 20px 60px rgba(0, 0, 0, 0.2)\",\r\n animation: \"sc-modal-slide-in 0.3s ease-out forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"20px 24px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"18px\" }}>{title}</span>\r\n <button\r\n onClick={handleClose}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"20px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"20px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"60px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"16px 20px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"12px 14px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 20px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { SupportChatState } from \"./types\";\r\n\r\n/**\r\n * Hook that returns controls for opening/closing the support chat.\r\n * Use with `<SupportChatModal />` for modal-triggered chat.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { open, close, toggle, isOpen } = useSupportChat();\r\n * return <button onClick={open}>Contact Us</button>;\r\n * ```\r\n */\r\nexport function useSupportChat(): SupportChatState {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const open = useCallback(() => setIsOpen(true), []);\r\n const close = useCallback(() => setIsOpen(false), []);\r\n const toggle = useCallback(() => setIsOpen((prev) => !prev), []);\r\n\r\n return { open, close, toggle, isOpen };\r\n}\r\n"]}
|
package/dist/client/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
1
|
+
import { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/client/ChatBubble.tsx
|
|
@@ -115,6 +115,49 @@ function useChatEngine({
|
|
|
115
115
|
);
|
|
116
116
|
return { messages, input, setInput, sending, sendMessage, handleKeyDown };
|
|
117
117
|
}
|
|
118
|
+
function useColorScheme() {
|
|
119
|
+
const [scheme, setScheme] = useState(() => {
|
|
120
|
+
if (typeof window === "undefined") return "light";
|
|
121
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
122
|
+
});
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (typeof window === "undefined") return;
|
|
125
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
126
|
+
const handler = (e) => {
|
|
127
|
+
setScheme(e.matches ? "dark" : "light");
|
|
128
|
+
};
|
|
129
|
+
mq.addEventListener("change", handler);
|
|
130
|
+
return () => mq.removeEventListener("change", handler);
|
|
131
|
+
}, []);
|
|
132
|
+
return scheme;
|
|
133
|
+
}
|
|
134
|
+
var lightTokens = {
|
|
135
|
+
panelBg: "#ffffff",
|
|
136
|
+
panelBorder: "#e5e7eb",
|
|
137
|
+
inputBg: "#ffffff",
|
|
138
|
+
inputBorder: "#d1d5db",
|
|
139
|
+
inputText: "#1f2937",
|
|
140
|
+
inputPlaceholder: "#9ca3af",
|
|
141
|
+
receivedBg: "#f3f4f6",
|
|
142
|
+
receivedText: "#1f2937",
|
|
143
|
+
emptyText: "#9ca3af",
|
|
144
|
+
inputAreaBorder: "#e5e7eb"
|
|
145
|
+
};
|
|
146
|
+
var darkTokens = {
|
|
147
|
+
panelBg: "#1f2937",
|
|
148
|
+
panelBorder: "#374151",
|
|
149
|
+
inputBg: "#111827",
|
|
150
|
+
inputBorder: "#4b5563",
|
|
151
|
+
inputText: "#f9fafb",
|
|
152
|
+
inputPlaceholder: "#9ca3af",
|
|
153
|
+
receivedBg: "#374151",
|
|
154
|
+
receivedText: "#f3f4f6",
|
|
155
|
+
emptyText: "#6b7280",
|
|
156
|
+
inputAreaBorder: "#374151"
|
|
157
|
+
};
|
|
158
|
+
function getThemeTokens(scheme) {
|
|
159
|
+
return scheme === "dark" ? darkTokens : lightTokens;
|
|
160
|
+
}
|
|
118
161
|
function injectKeyframes() {
|
|
119
162
|
if (typeof document === "undefined") return;
|
|
120
163
|
if (document.querySelector("[data-support-chat-keyframes]")) return;
|
|
@@ -142,6 +185,8 @@ function ChatBubble({
|
|
|
142
185
|
user
|
|
143
186
|
}) {
|
|
144
187
|
const [isOpen, setIsOpen] = useState(false);
|
|
188
|
+
const colorScheme = useColorScheme();
|
|
189
|
+
const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);
|
|
145
190
|
const { messages, input, setInput, sending, sendMessage, handleKeyDown } = useChatEngine({ apiUrl, user });
|
|
146
191
|
const [panelState, setPanelState] = useState(
|
|
147
192
|
"closed"
|
|
@@ -299,8 +344,8 @@ function ChatBubble({
|
|
|
299
344
|
boxShadow: "0 8px 30px rgba(0,0,0,0.12)",
|
|
300
345
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
301
346
|
fontSize: "14px",
|
|
302
|
-
backgroundColor:
|
|
303
|
-
border:
|
|
347
|
+
backgroundColor: theme.panelBg,
|
|
348
|
+
border: `1px solid ${theme.panelBorder}`,
|
|
304
349
|
animation: panelState === "open" ? "sc-slide-in 0.25s ease-out forwards" : "sc-slide-out 0.2s ease-in forwards"
|
|
305
350
|
},
|
|
306
351
|
children: [
|
|
@@ -357,7 +402,7 @@ function ChatBubble({
|
|
|
357
402
|
"div",
|
|
358
403
|
{
|
|
359
404
|
style: {
|
|
360
|
-
color:
|
|
405
|
+
color: theme.emptyText,
|
|
361
406
|
textAlign: "center",
|
|
362
407
|
marginTop: "40px"
|
|
363
408
|
},
|
|
@@ -372,8 +417,8 @@ function ChatBubble({
|
|
|
372
417
|
maxWidth: "80%",
|
|
373
418
|
padding: "10px 14px",
|
|
374
419
|
borderRadius: msg.sender === "user" ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
|
|
375
|
-
backgroundColor: msg.sender === "user" ? color :
|
|
376
|
-
color: msg.sender === "user" ? "#fff" :
|
|
420
|
+
backgroundColor: msg.sender === "user" ? color : theme.receivedBg,
|
|
421
|
+
color: msg.sender === "user" ? "#fff" : theme.receivedText,
|
|
377
422
|
wordBreak: "break-word"
|
|
378
423
|
},
|
|
379
424
|
children: msg.text
|
|
@@ -388,7 +433,7 @@ function ChatBubble({
|
|
|
388
433
|
"div",
|
|
389
434
|
{
|
|
390
435
|
style: {
|
|
391
|
-
borderTop:
|
|
436
|
+
borderTop: `1px solid ${theme.inputAreaBorder}`,
|
|
392
437
|
padding: "12px",
|
|
393
438
|
display: "flex",
|
|
394
439
|
gap: "8px",
|
|
@@ -407,12 +452,14 @@ function ChatBubble({
|
|
|
407
452
|
"aria-label": "Type your message",
|
|
408
453
|
style: {
|
|
409
454
|
flex: 1,
|
|
410
|
-
border:
|
|
455
|
+
border: `1px solid ${theme.inputBorder}`,
|
|
411
456
|
borderRadius: "8px",
|
|
412
457
|
padding: "10px 12px",
|
|
413
458
|
fontSize: "14px",
|
|
414
459
|
outline: "none",
|
|
415
|
-
fontFamily: "inherit"
|
|
460
|
+
fontFamily: "inherit",
|
|
461
|
+
backgroundColor: theme.inputBg,
|
|
462
|
+
color: theme.inputText
|
|
416
463
|
}
|
|
417
464
|
}
|
|
418
465
|
),
|
|
@@ -481,6 +528,8 @@ function SupportChatModal({
|
|
|
481
528
|
user
|
|
482
529
|
}) {
|
|
483
530
|
const { messages, input, setInput, sending, sendMessage, handleKeyDown } = useChatEngine({ apiUrl, user });
|
|
531
|
+
const colorScheme = useColorScheme();
|
|
532
|
+
const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);
|
|
484
533
|
const modalRef = useRef(null);
|
|
485
534
|
const inputRef = useRef(null);
|
|
486
535
|
const messagesEndRef = useRef(null);
|
|
@@ -598,7 +647,7 @@ function SupportChatModal({
|
|
|
598
647
|
maxWidth: "calc(100vw - 32px)",
|
|
599
648
|
height: "600px",
|
|
600
649
|
maxHeight: "calc(100vh - 64px)",
|
|
601
|
-
backgroundColor:
|
|
650
|
+
backgroundColor: theme.panelBg,
|
|
602
651
|
borderRadius: "16px",
|
|
603
652
|
overflow: "hidden",
|
|
604
653
|
display: "flex",
|
|
@@ -660,7 +709,7 @@ function SupportChatModal({
|
|
|
660
709
|
"div",
|
|
661
710
|
{
|
|
662
711
|
style: {
|
|
663
|
-
color:
|
|
712
|
+
color: theme.emptyText,
|
|
664
713
|
textAlign: "center",
|
|
665
714
|
marginTop: "60px"
|
|
666
715
|
},
|
|
@@ -675,8 +724,8 @@ function SupportChatModal({
|
|
|
675
724
|
maxWidth: "80%",
|
|
676
725
|
padding: "10px 14px",
|
|
677
726
|
borderRadius: msg.sender === "user" ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
|
|
678
|
-
backgroundColor: msg.sender === "user" ? color :
|
|
679
|
-
color: msg.sender === "user" ? "#fff" :
|
|
727
|
+
backgroundColor: msg.sender === "user" ? color : theme.receivedBg,
|
|
728
|
+
color: msg.sender === "user" ? "#fff" : theme.receivedText,
|
|
680
729
|
wordBreak: "break-word"
|
|
681
730
|
},
|
|
682
731
|
children: msg.text
|
|
@@ -691,7 +740,7 @@ function SupportChatModal({
|
|
|
691
740
|
"div",
|
|
692
741
|
{
|
|
693
742
|
style: {
|
|
694
|
-
borderTop:
|
|
743
|
+
borderTop: `1px solid ${theme.inputAreaBorder}`,
|
|
695
744
|
padding: "16px 20px",
|
|
696
745
|
display: "flex",
|
|
697
746
|
gap: "8px",
|
|
@@ -710,12 +759,14 @@ function SupportChatModal({
|
|
|
710
759
|
"aria-label": "Type your message",
|
|
711
760
|
style: {
|
|
712
761
|
flex: 1,
|
|
713
|
-
border:
|
|
762
|
+
border: `1px solid ${theme.inputBorder}`,
|
|
714
763
|
borderRadius: "8px",
|
|
715
764
|
padding: "12px 14px",
|
|
716
765
|
fontSize: "14px",
|
|
717
766
|
outline: "none",
|
|
718
|
-
fontFamily: "inherit"
|
|
767
|
+
fontFamily: "inherit",
|
|
768
|
+
backgroundColor: theme.inputBg,
|
|
769
|
+
color: theme.inputText
|
|
719
770
|
}
|
|
720
771
|
}
|
|
721
772
|
),
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/context.ts","../../src/client/useChatEngine.ts","../../src/client/ChatBubble.tsx","../../src/client/SupportChatModal.tsx","../../src/client/useSupportChat.ts"],"names":["useState","useCallback","useRef","useEffect","jsxs","Fragment","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type { AnonymousContext } from \"./types\";\r\n\r\nconst SESSION_KEY = \"support-chat-session-id\";\r\n\r\n/** Generate a random session ID */\r\nfunction generateSessionId(): string {\r\n const chars =\r\n \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n let id = \"\";\r\n for (let i = 0; i < 16; i++) {\r\n id += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return id;\r\n}\r\n\r\n/** Get or create a persistent session ID from localStorage */\r\nexport function getSessionId(): string {\r\n if (typeof window === \"undefined\") return generateSessionId();\r\n\r\n try {\r\n const existing = localStorage.getItem(SESSION_KEY);\r\n if (existing) return existing;\r\n\r\n const newId = generateSessionId();\r\n localStorage.setItem(SESSION_KEY, newId);\r\n return newId;\r\n } catch {\r\n // localStorage may be blocked (private browsing, etc.)\r\n return generateSessionId();\r\n }\r\n}\r\n\r\n/** Collect anonymous browser context */\r\nexport function collectAnonymousContext(): AnonymousContext {\r\n const sessionId = getSessionId();\r\n\r\n if (typeof window === \"undefined\") {\r\n return {\r\n pageUrl: \"\",\r\n referrer: \"\",\r\n userAgent: \"\",\r\n screenSize: \"\",\r\n timezone: \"\",\r\n sessionId,\r\n };\r\n }\r\n\r\n return {\r\n pageUrl: window.location.href,\r\n referrer: document.referrer,\r\n userAgent: navigator.userAgent,\r\n screenSize: `${window.screen.width}x${window.screen.height}`,\r\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n sessionId,\r\n };\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { ChatMessage, ChatUser } from \"./types\";\r\nimport { collectAnonymousContext, getSessionId } from \"./context\";\r\n\r\n/** Options for initializing the chat engine */\r\nexport interface ChatEngineOptions {\r\n /** URL of the support chat API endpoint */\r\n apiUrl: string;\r\n /** Authenticated user info (optional) */\r\n user?: ChatUser;\r\n}\r\n\r\n/** Return value from useChatEngine */\r\nexport interface ChatEngineState {\r\n /** All chat messages */\r\n messages: ChatMessage[];\r\n /** Current input value */\r\n input: string;\r\n /** Update the input value */\r\n setInput: (value: string) => void;\r\n /** Whether a message is currently being sent */\r\n sending: boolean;\r\n /** Send the current input as a message */\r\n sendMessage: () => Promise<void>;\r\n /** Handle keydown on the input (Enter to send) */\r\n handleKeyDown: (e: React.KeyboardEvent) => void;\r\n}\r\n\r\n/**\r\n * Shared chat engine hook used by both ChatBubble and SupportChatModal.\r\n * Manages message state, input, and API communication.\r\n */\r\nexport function useChatEngine({\r\n apiUrl,\r\n user,\r\n}: ChatEngineOptions): ChatEngineState {\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\r\n const [sending, setSending] = useState(false);\r\n\r\n const sendMessage = useCallback(async () => {\r\n const text = input.trim();\r\n if (!text || sending) return;\r\n\r\n const msg: ChatMessage = {\r\n id: `msg-${Date.now()}`,\r\n text,\r\n sender: \"user\",\r\n timestamp: Date.now(),\r\n };\r\n\r\n setMessages((prev) => [...prev, msg]);\r\n setInput(\"\");\r\n setSending(true);\r\n\r\n try {\r\n const context = collectAnonymousContext();\r\n const response = await fetch(apiUrl, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n message: text,\r\n user: user ?? undefined,\r\n sessionId: user?.id ?? getSessionId(),\r\n context,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Failed to send message. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n }\r\n } catch {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Connection error. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n } finally {\r\n setSending(false);\r\n }\r\n }, [input, sending, apiUrl, user]);\r\n\r\n const handleKeyDown = useCallback(\r\n (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\" && !e.shiftKey) {\r\n e.preventDefault();\r\n void sendMessage();\r\n }\r\n },\r\n [sendMessage],\r\n );\r\n\r\n return { messages, input, setInput, sending, sendMessage, handleKeyDown };\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from \"react\";\r\nimport type { ChatBubbleProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\n\r\n/**\r\n * Inject a <style> tag for keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-slide-in {\r\n from { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * ChatBubble -- floating support chat widget.\r\n *\r\n * Renders a circular button that opens an inline chat panel.\r\n * Messages are sent via POST to the configured `apiUrl`.\r\n *\r\n * @example\r\n * ```tsx\r\n * <ChatBubble apiUrl=\"/api/support\" />\r\n * ```\r\n */\r\nexport function ChatBubble({\r\n apiUrl,\r\n position = \"bottom-right\",\r\n color = \"#2563eb\",\r\n title = \"Support\",\r\n placeholder = \"Type a message...\",\r\n show = true,\r\n user,\r\n}: ChatBubbleProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n // Use shared chat engine for message logic\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n // Animation state: \"open\" | \"closing\" | \"closed\"\r\n const [panelState, setPanelState] = useState<\"open\" | \"closing\" | \"closed\">(\r\n \"closed\",\r\n );\r\n\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n // Inject keyframe animations on first render\r\n useEffect(() => {\r\n injectKeyframes();\r\n }, []);\r\n\r\n // Sync isOpen -> panelState\r\n useEffect(() => {\r\n if (isOpen) {\r\n setPanelState(\"open\");\r\n } else if (panelState === \"open\") {\r\n // Start closing animation\r\n setPanelState(\"closing\");\r\n }\r\n }, [isOpen]);\r\n\r\n // When closing animation ends, set to fully closed\r\n const handleAnimationEnd = useCallback(() => {\r\n if (panelState === \"closing\") {\r\n setPanelState(\"closed\");\r\n }\r\n }, [panelState]);\r\n\r\n // Focus input when panel opens\r\n useEffect(() => {\r\n if (panelState === \"open\" && inputRef.current) {\r\n inputRef.current.focus();\r\n }\r\n }, [panelState]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Focus trap: keep Tab cycling inside the dialog when open\r\n useEffect(() => {\r\n if (panelState !== \"open\" || !panelRef.current) return;\r\n\r\n const panel = panelRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n setIsOpen(false);\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = panel.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [panelState]);\r\n\r\n const toggle = useCallback(() => setIsOpen((o) => !o), []);\r\n\r\n // Position styles for the bubble button\r\n const positionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n ...(position.includes(\"bottom\") ? { bottom: \"20px\" } : { top: \"20px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Panel position & responsive sizing\r\n // On mobile (<480px viewport), expand to near-full screen\r\n const panelPositionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n width: \"380px\",\r\n maxWidth: \"calc(100vw - 40px)\",\r\n height: \"500px\",\r\n maxHeight: \"calc(100vh - 120px)\",\r\n ...(position.includes(\"bottom\") ? { bottom: \"80px\" } : { top: \"80px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Check if panel is visible (open or animating out)\r\n const showPanel = panelState === \"open\" || panelState === \"closing\";\r\n\r\n return (\r\n <>\r\n {/* Responsive overrides injected as inline <style> */}\r\n {showPanel && (\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-panel] {\r\n width: calc(100vw - 16px) !important;\r\n height: calc(100vh - 100px) !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n left: 8px !important;\r\n right: 8px !important;\r\n bottom: 72px !important;\r\n border-radius: 12px !important;\r\n }\r\n }\r\n `}</style>\r\n )}\r\n\r\n {/* Floating bubble button */}\r\n {show && (\r\n <button\r\n onClick={toggle}\r\n aria-label={isOpen ? \"Close support chat\" : \"Open support chat\"}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"dialog\"\r\n style={{\r\n ...positionStyles,\r\n width: \"56px\",\r\n height: \"56px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: color,\r\n border: \"none\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\r\n transition: \"transform 0.2s ease, box-shadow 0.2s ease\",\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = \"scale(1.1)\";\r\n e.currentTarget.style.boxShadow = \"0 6px 20px rgba(0,0,0,0.2)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = \"scale(1)\";\r\n e.currentTarget.style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n >\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n {isOpen ? (\r\n <path d=\"M18 6L6 18M6 6l12 12\" />\r\n ) : (\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\r\n )}\r\n </svg>\r\n </button>\r\n )}\r\n\r\n {/* Chat panel */}\r\n {showPanel && (\r\n <div\r\n ref={panelRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-panel=\"\"\r\n onAnimationEnd={handleAnimationEnd}\r\n style={{\r\n ...panelPositionStyles,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n borderRadius: \"12px\",\r\n overflow: \"hidden\",\r\n boxShadow: \"0 8px 30px rgba(0,0,0,0.12)\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n backgroundColor: \"#fff\",\r\n border: \"1px solid #e5e7eb\",\r\n animation:\r\n panelState === \"open\"\r\n ? \"sc-slide-in 0.25s ease-out forwards\"\r\n : \"sc-slide-out 0.2s ease-in forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"16px\" }}>{title}</span>\r\n <button\r\n onClick={() => setIsOpen(false)}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"18px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: \"#9ca3af\",\r\n textAlign: \"center\",\r\n marginTop: \"40px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : \"#f3f4f6\",\r\n color: msg.sender === \"user\" ? \"#fff\" : \"#1f2937\",\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: \"1px solid #e5e7eb\",\r\n padding: \"12px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: \"1px solid #d1d5db\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 12px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 16px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useRef, useEffect, useCallback } from \"react\";\r\nimport type { SupportChatModalProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\n\r\n/**\r\n * Inject a <style> tag for modal keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectModalKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-modal-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-modal-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-modal-backdrop-in {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n }\r\n @keyframes sc-modal-backdrop-out {\r\n from { opacity: 1; }\r\n to { opacity: 0; }\r\n }\r\n @keyframes sc-modal-slide-in {\r\n from { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-modal-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * SupportChatModal -- modal-based support chat.\r\n *\r\n * Renders a centered modal dialog for support chat, designed to be\r\n * triggered from custom UI elements (e.g., \"Contact Us\" links).\r\n *\r\n * Desktop: centered modal, max-width ~500px, with backdrop overlay.\r\n * Mobile: full-screen modal.\r\n *\r\n * Use with the `useSupportChat()` hook:\r\n *\r\n * @example\r\n * ```tsx\r\n * import { SupportChatModal, useSupportChat } from 'support-chat';\r\n *\r\n * function App() {\r\n * const { open, close, isOpen } = useSupportChat();\r\n * return (\r\n * <>\r\n * <button onClick={open}>Contact Us</button>\r\n * <SupportChatModal\r\n * apiUrl=\"/api/support\"\r\n * isOpen={isOpen}\r\n * onClose={close}\r\n * />\r\n * </>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function SupportChatModal({\r\n apiUrl,\r\n isOpen,\r\n onClose,\r\n color = \"#2563eb\",\r\n title = \"Contact Us\",\r\n placeholder = \"Type a message...\",\r\n user,\r\n}: SupportChatModalProps) {\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n const modalRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const closingRef = useRef(false);\r\n const backdropRef = useRef<HTMLDivElement>(null);\r\n\r\n // Inject modal keyframes on first render\r\n useEffect(() => {\r\n injectModalKeyframes();\r\n }, []);\r\n\r\n // Focus input when modal opens\r\n useEffect(() => {\r\n if (isOpen && inputRef.current) {\r\n // Small delay to let the modal render before focusing\r\n const timer = setTimeout(() => inputRef.current?.focus(), 50);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [isOpen]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Prevent body scroll when modal is open\r\n useEffect(() => {\r\n if (isOpen) {\r\n const prev = document.body.style.overflow;\r\n document.body.style.overflow = \"hidden\";\r\n return () => {\r\n document.body.style.overflow = prev;\r\n };\r\n }\r\n }, [isOpen]);\r\n\r\n // Focus trap and Escape key handling\r\n useEffect(() => {\r\n if (!isOpen || !modalRef.current) return;\r\n\r\n const modal = modalRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onClose();\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [isOpen, onClose]);\r\n\r\n // Handle close with animation\r\n const handleClose = useCallback(() => {\r\n closingRef.current = true;\r\n onClose();\r\n }, [onClose]);\r\n\r\n // Handle backdrop click\r\n const handleBackdropClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget) {\r\n handleClose();\r\n }\r\n },\r\n [handleClose],\r\n );\r\n\r\n if (!isOpen) return null;\r\n\r\n return (\r\n <>\r\n {/* Responsive style for full-screen on mobile */}\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-modal] {\r\n width: 100vw !important;\r\n height: 100vh !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n border-radius: 0 !important;\r\n margin: 0 !important;\r\n }\r\n [data-support-chat-modal-backdrop] {\r\n align-items: stretch !important;\r\n justify-content: stretch !important;\r\n }\r\n }\r\n `}</style>\r\n\r\n {/* Backdrop overlay */}\r\n <div\r\n ref={backdropRef}\r\n data-support-chat-modal-backdrop=\"\"\r\n onClick={handleBackdropClick}\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n zIndex: 100000,\r\n backgroundColor: \"rgba(0, 0, 0, 0.5)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n animation: \"sc-modal-backdrop-in 0.2s ease-out forwards\",\r\n }}\r\n >\r\n {/* Modal dialog */}\r\n <div\r\n ref={modalRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-modal=\"\"\r\n style={{\r\n width: \"500px\",\r\n maxWidth: \"calc(100vw - 32px)\",\r\n height: \"600px\",\r\n maxHeight: \"calc(100vh - 64px)\",\r\n backgroundColor: \"#fff\",\r\n borderRadius: \"16px\",\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n boxShadow: \"0 20px 60px rgba(0, 0, 0, 0.2)\",\r\n animation: \"sc-modal-slide-in 0.3s ease-out forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"20px 24px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"18px\" }}>{title}</span>\r\n <button\r\n onClick={handleClose}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"20px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"20px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: \"#9ca3af\",\r\n textAlign: \"center\",\r\n marginTop: \"60px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : \"#f3f4f6\",\r\n color: msg.sender === \"user\" ? \"#fff\" : \"#1f2937\",\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: \"1px solid #e5e7eb\",\r\n padding: \"16px 20px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: \"1px solid #d1d5db\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 14px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 20px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { SupportChatState } from \"./types\";\r\n\r\n/**\r\n * Hook that returns controls for opening/closing the support chat.\r\n * Use with `<SupportChatModal />` for modal-triggered chat.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { open, close, toggle, isOpen } = useSupportChat();\r\n * return <button onClick={open}>Contact Us</button>;\r\n * ```\r\n */\r\nexport function useSupportChat(): SupportChatState {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const open = useCallback(() => setIsOpen(true), []);\r\n const close = useCallback(() => setIsOpen(false), []);\r\n const toggle = useCallback(() => setIsOpen((prev) => !prev), []);\r\n\r\n return { open, close, toggle, isOpen };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/context.ts","../../src/client/useChatEngine.ts","../../src/client/useColorScheme.ts","../../src/client/ChatBubble.tsx","../../src/client/SupportChatModal.tsx","../../src/client/useSupportChat.ts"],"names":["useState","useEffect","useCallback","useMemo","useRef","jsxs","Fragment","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type { AnonymousContext } from \"./types\";\r\n\r\nconst SESSION_KEY = \"support-chat-session-id\";\r\n\r\n/** Generate a random session ID */\r\nfunction generateSessionId(): string {\r\n const chars =\r\n \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n let id = \"\";\r\n for (let i = 0; i < 16; i++) {\r\n id += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return id;\r\n}\r\n\r\n/** Get or create a persistent session ID from localStorage */\r\nexport function getSessionId(): string {\r\n if (typeof window === \"undefined\") return generateSessionId();\r\n\r\n try {\r\n const existing = localStorage.getItem(SESSION_KEY);\r\n if (existing) return existing;\r\n\r\n const newId = generateSessionId();\r\n localStorage.setItem(SESSION_KEY, newId);\r\n return newId;\r\n } catch {\r\n // localStorage may be blocked (private browsing, etc.)\r\n return generateSessionId();\r\n }\r\n}\r\n\r\n/** Collect anonymous browser context */\r\nexport function collectAnonymousContext(): AnonymousContext {\r\n const sessionId = getSessionId();\r\n\r\n if (typeof window === \"undefined\") {\r\n return {\r\n pageUrl: \"\",\r\n referrer: \"\",\r\n userAgent: \"\",\r\n screenSize: \"\",\r\n timezone: \"\",\r\n sessionId,\r\n };\r\n }\r\n\r\n return {\r\n pageUrl: window.location.href,\r\n referrer: document.referrer,\r\n userAgent: navigator.userAgent,\r\n screenSize: `${window.screen.width}x${window.screen.height}`,\r\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n sessionId,\r\n };\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { ChatMessage, ChatUser } from \"./types\";\r\nimport { collectAnonymousContext, getSessionId } from \"./context\";\r\n\r\n/** Options for initializing the chat engine */\r\nexport interface ChatEngineOptions {\r\n /** URL of the support chat API endpoint */\r\n apiUrl: string;\r\n /** Authenticated user info (optional) */\r\n user?: ChatUser;\r\n}\r\n\r\n/** Return value from useChatEngine */\r\nexport interface ChatEngineState {\r\n /** All chat messages */\r\n messages: ChatMessage[];\r\n /** Current input value */\r\n input: string;\r\n /** Update the input value */\r\n setInput: (value: string) => void;\r\n /** Whether a message is currently being sent */\r\n sending: boolean;\r\n /** Send the current input as a message */\r\n sendMessage: () => Promise<void>;\r\n /** Handle keydown on the input (Enter to send) */\r\n handleKeyDown: (e: React.KeyboardEvent) => void;\r\n}\r\n\r\n/**\r\n * Shared chat engine hook used by both ChatBubble and SupportChatModal.\r\n * Manages message state, input, and API communication.\r\n */\r\nexport function useChatEngine({\r\n apiUrl,\r\n user,\r\n}: ChatEngineOptions): ChatEngineState {\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\r\n const [sending, setSending] = useState(false);\r\n\r\n const sendMessage = useCallback(async () => {\r\n const text = input.trim();\r\n if (!text || sending) return;\r\n\r\n const msg: ChatMessage = {\r\n id: `msg-${Date.now()}`,\r\n text,\r\n sender: \"user\",\r\n timestamp: Date.now(),\r\n };\r\n\r\n setMessages((prev) => [...prev, msg]);\r\n setInput(\"\");\r\n setSending(true);\r\n\r\n try {\r\n const context = collectAnonymousContext();\r\n const response = await fetch(apiUrl, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n message: text,\r\n user: user ?? undefined,\r\n sessionId: user?.id ?? getSessionId(),\r\n context,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Failed to send message. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n }\r\n } catch {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Connection error. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n } finally {\r\n setSending(false);\r\n }\r\n }, [input, sending, apiUrl, user]);\r\n\r\n const handleKeyDown = useCallback(\r\n (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\" && !e.shiftKey) {\r\n e.preventDefault();\r\n void sendMessage();\r\n }\r\n },\r\n [sendMessage],\r\n );\r\n\r\n return { messages, input, setInput, sending, sendMessage, handleKeyDown };\r\n}\r\n","import { useState, useEffect } from \"react\";\r\n\r\nexport type ColorScheme = \"light\" | \"dark\";\r\n\r\n/**\r\n * Detects the user's system color scheme preference via `prefers-color-scheme`.\r\n * Returns \"dark\" or \"light\". Updates reactively if the user changes their system setting.\r\n */\r\nexport function useColorScheme(): ColorScheme {\r\n const [scheme, setScheme] = useState<ColorScheme>(() => {\r\n if (typeof window === \"undefined\") return \"light\";\r\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\r\n ? \"dark\"\r\n : \"light\";\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === \"undefined\") return;\r\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\r\n const handler = (e: MediaQueryListEvent) => {\r\n setScheme(e.matches ? \"dark\" : \"light\");\r\n };\r\n mq.addEventListener(\"change\", handler);\r\n return () => mq.removeEventListener(\"change\", handler);\r\n }, []);\r\n\r\n return scheme;\r\n}\r\n\r\n/** Resolved color tokens for light and dark modes. */\r\nexport interface ThemeTokens {\r\n panelBg: string;\r\n panelBorder: string;\r\n inputBg: string;\r\n inputBorder: string;\r\n inputText: string;\r\n inputPlaceholder: string;\r\n receivedBg: string;\r\n receivedText: string;\r\n emptyText: string;\r\n inputAreaBorder: string;\r\n}\r\n\r\nconst lightTokens: ThemeTokens = {\r\n panelBg: \"#ffffff\",\r\n panelBorder: \"#e5e7eb\",\r\n inputBg: \"#ffffff\",\r\n inputBorder: \"#d1d5db\",\r\n inputText: \"#1f2937\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#f3f4f6\",\r\n receivedText: \"#1f2937\",\r\n emptyText: \"#9ca3af\",\r\n inputAreaBorder: \"#e5e7eb\",\r\n};\r\n\r\nconst darkTokens: ThemeTokens = {\r\n panelBg: \"#1f2937\",\r\n panelBorder: \"#374151\",\r\n inputBg: \"#111827\",\r\n inputBorder: \"#4b5563\",\r\n inputText: \"#f9fafb\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#374151\",\r\n receivedText: \"#f3f4f6\",\r\n emptyText: \"#6b7280\",\r\n inputAreaBorder: \"#374151\",\r\n};\r\n\r\nexport function getThemeTokens(scheme: ColorScheme): ThemeTokens {\r\n return scheme === \"dark\" ? darkTokens : lightTokens;\r\n}\r\n","import { useState, useCallback, useRef, useEffect, useMemo } from \"react\";\r\nimport type { ChatBubbleProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-slide-in {\r\n from { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * ChatBubble -- floating support chat widget.\r\n *\r\n * Renders a circular button that opens an inline chat panel.\r\n * Messages are sent via POST to the configured `apiUrl`.\r\n *\r\n * @example\r\n * ```tsx\r\n * <ChatBubble apiUrl=\"/api/support\" />\r\n * ```\r\n */\r\nexport function ChatBubble({\r\n apiUrl,\r\n position = \"bottom-right\",\r\n color = \"#2563eb\",\r\n title = \"Support\",\r\n placeholder = \"Type a message...\",\r\n show = true,\r\n user,\r\n}: ChatBubbleProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n // Detect system color scheme\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n // Use shared chat engine for message logic\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n // Animation state: \"open\" | \"closing\" | \"closed\"\r\n const [panelState, setPanelState] = useState<\"open\" | \"closing\" | \"closed\">(\r\n \"closed\",\r\n );\r\n\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n // Inject keyframe animations on first render\r\n useEffect(() => {\r\n injectKeyframes();\r\n }, []);\r\n\r\n // Sync isOpen -> panelState\r\n useEffect(() => {\r\n if (isOpen) {\r\n setPanelState(\"open\");\r\n } else if (panelState === \"open\") {\r\n // Start closing animation\r\n setPanelState(\"closing\");\r\n }\r\n }, [isOpen]);\r\n\r\n // When closing animation ends, set to fully closed\r\n const handleAnimationEnd = useCallback(() => {\r\n if (panelState === \"closing\") {\r\n setPanelState(\"closed\");\r\n }\r\n }, [panelState]);\r\n\r\n // Focus input when panel opens\r\n useEffect(() => {\r\n if (panelState === \"open\" && inputRef.current) {\r\n inputRef.current.focus();\r\n }\r\n }, [panelState]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Focus trap: keep Tab cycling inside the dialog when open\r\n useEffect(() => {\r\n if (panelState !== \"open\" || !panelRef.current) return;\r\n\r\n const panel = panelRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n setIsOpen(false);\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = panel.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [panelState]);\r\n\r\n const toggle = useCallback(() => setIsOpen((o) => !o), []);\r\n\r\n // Position styles for the bubble button\r\n const positionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n ...(position.includes(\"bottom\") ? { bottom: \"20px\" } : { top: \"20px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Panel position & responsive sizing\r\n // On mobile (<480px viewport), expand to near-full screen\r\n const panelPositionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n width: \"380px\",\r\n maxWidth: \"calc(100vw - 40px)\",\r\n height: \"500px\",\r\n maxHeight: \"calc(100vh - 120px)\",\r\n ...(position.includes(\"bottom\") ? { bottom: \"80px\" } : { top: \"80px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Check if panel is visible (open or animating out)\r\n const showPanel = panelState === \"open\" || panelState === \"closing\";\r\n\r\n return (\r\n <>\r\n {/* Responsive overrides injected as inline <style> */}\r\n {showPanel && (\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-panel] {\r\n width: calc(100vw - 16px) !important;\r\n height: calc(100vh - 100px) !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n left: 8px !important;\r\n right: 8px !important;\r\n bottom: 72px !important;\r\n border-radius: 12px !important;\r\n }\r\n }\r\n `}</style>\r\n )}\r\n\r\n {/* Floating bubble button */}\r\n {show && (\r\n <button\r\n onClick={toggle}\r\n aria-label={isOpen ? \"Close support chat\" : \"Open support chat\"}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"dialog\"\r\n style={{\r\n ...positionStyles,\r\n width: \"56px\",\r\n height: \"56px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: color,\r\n border: \"none\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\r\n transition: \"transform 0.2s ease, box-shadow 0.2s ease\",\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = \"scale(1.1)\";\r\n e.currentTarget.style.boxShadow = \"0 6px 20px rgba(0,0,0,0.2)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = \"scale(1)\";\r\n e.currentTarget.style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n >\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n {isOpen ? (\r\n <path d=\"M18 6L6 18M6 6l12 12\" />\r\n ) : (\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\r\n )}\r\n </svg>\r\n </button>\r\n )}\r\n\r\n {/* Chat panel */}\r\n {showPanel && (\r\n <div\r\n ref={panelRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-panel=\"\"\r\n onAnimationEnd={handleAnimationEnd}\r\n style={{\r\n ...panelPositionStyles,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n borderRadius: \"12px\",\r\n overflow: \"hidden\",\r\n boxShadow: \"0 8px 30px rgba(0,0,0,0.12)\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n backgroundColor: theme.panelBg,\r\n border: `1px solid ${theme.panelBorder}`,\r\n animation:\r\n panelState === \"open\"\r\n ? \"sc-slide-in 0.25s ease-out forwards\"\r\n : \"sc-slide-out 0.2s ease-in forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"16px\" }}>{title}</span>\r\n <button\r\n onClick={() => setIsOpen(false)}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"18px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"40px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"12px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"10px 12px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 16px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useRef, useEffect, useCallback, useMemo } from \"react\";\r\nimport type { SupportChatModalProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for modal keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectModalKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-modal-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-modal-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-modal-backdrop-in {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n }\r\n @keyframes sc-modal-backdrop-out {\r\n from { opacity: 1; }\r\n to { opacity: 0; }\r\n }\r\n @keyframes sc-modal-slide-in {\r\n from { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-modal-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * SupportChatModal -- modal-based support chat.\r\n *\r\n * Renders a centered modal dialog for support chat, designed to be\r\n * triggered from custom UI elements (e.g., \"Contact Us\" links).\r\n *\r\n * Desktop: centered modal, max-width ~500px, with backdrop overlay.\r\n * Mobile: full-screen modal.\r\n *\r\n * Use with the `useSupportChat()` hook:\r\n *\r\n * @example\r\n * ```tsx\r\n * import { SupportChatModal, useSupportChat } from 'support-chat';\r\n *\r\n * function App() {\r\n * const { open, close, isOpen } = useSupportChat();\r\n * return (\r\n * <>\r\n * <button onClick={open}>Contact Us</button>\r\n * <SupportChatModal\r\n * apiUrl=\"/api/support\"\r\n * isOpen={isOpen}\r\n * onClose={close}\r\n * />\r\n * </>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function SupportChatModal({\r\n apiUrl,\r\n isOpen,\r\n onClose,\r\n color = \"#2563eb\",\r\n title = \"Contact Us\",\r\n placeholder = \"Type a message...\",\r\n user,\r\n}: SupportChatModalProps) {\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n const modalRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const closingRef = useRef(false);\r\n const backdropRef = useRef<HTMLDivElement>(null);\r\n\r\n // Inject modal keyframes on first render\r\n useEffect(() => {\r\n injectModalKeyframes();\r\n }, []);\r\n\r\n // Focus input when modal opens\r\n useEffect(() => {\r\n if (isOpen && inputRef.current) {\r\n // Small delay to let the modal render before focusing\r\n const timer = setTimeout(() => inputRef.current?.focus(), 50);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [isOpen]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Prevent body scroll when modal is open\r\n useEffect(() => {\r\n if (isOpen) {\r\n const prev = document.body.style.overflow;\r\n document.body.style.overflow = \"hidden\";\r\n return () => {\r\n document.body.style.overflow = prev;\r\n };\r\n }\r\n }, [isOpen]);\r\n\r\n // Focus trap and Escape key handling\r\n useEffect(() => {\r\n if (!isOpen || !modalRef.current) return;\r\n\r\n const modal = modalRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onClose();\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [isOpen, onClose]);\r\n\r\n // Handle close with animation\r\n const handleClose = useCallback(() => {\r\n closingRef.current = true;\r\n onClose();\r\n }, [onClose]);\r\n\r\n // Handle backdrop click\r\n const handleBackdropClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget) {\r\n handleClose();\r\n }\r\n },\r\n [handleClose],\r\n );\r\n\r\n if (!isOpen) return null;\r\n\r\n return (\r\n <>\r\n {/* Responsive style for full-screen on mobile */}\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-modal] {\r\n width: 100vw !important;\r\n height: 100vh !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n border-radius: 0 !important;\r\n margin: 0 !important;\r\n }\r\n [data-support-chat-modal-backdrop] {\r\n align-items: stretch !important;\r\n justify-content: stretch !important;\r\n }\r\n }\r\n `}</style>\r\n\r\n {/* Backdrop overlay */}\r\n <div\r\n ref={backdropRef}\r\n data-support-chat-modal-backdrop=\"\"\r\n onClick={handleBackdropClick}\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n zIndex: 100000,\r\n backgroundColor: \"rgba(0, 0, 0, 0.5)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n animation: \"sc-modal-backdrop-in 0.2s ease-out forwards\",\r\n }}\r\n >\r\n {/* Modal dialog */}\r\n <div\r\n ref={modalRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-modal=\"\"\r\n style={{\r\n width: \"500px\",\r\n maxWidth: \"calc(100vw - 32px)\",\r\n height: \"600px\",\r\n maxHeight: \"calc(100vh - 64px)\",\r\n backgroundColor: theme.panelBg,\r\n borderRadius: \"16px\",\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n boxShadow: \"0 20px 60px rgba(0, 0, 0, 0.2)\",\r\n animation: \"sc-modal-slide-in 0.3s ease-out forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"20px 24px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"18px\" }}>{title}</span>\r\n <button\r\n onClick={handleClose}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"20px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"20px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"60px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"16px 20px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"12px 14px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 20px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { SupportChatState } from \"./types\";\r\n\r\n/**\r\n * Hook that returns controls for opening/closing the support chat.\r\n * Use with `<SupportChatModal />` for modal-triggered chat.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { open, close, toggle, isOpen } = useSupportChat();\r\n * return <button onClick={open}>Contact Us</button>;\r\n * ```\r\n */\r\nexport function useSupportChat(): SupportChatState {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const open = useCallback(() => setIsOpen(true), []);\r\n const close = useCallback(() => setIsOpen(false), []);\r\n const toggle = useCallback(() => setIsOpen((prev) => !prev), []);\r\n\r\n return { open, close, toggle, isOpen };\r\n}\r\n"]}
|
package/package.json
CHANGED