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.
@@ -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: "#fff",
305
- border: "1px solid #e5e7eb",
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: "#9ca3af",
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 : "#f3f4f6",
378
- color: msg.sender === "user" ? "#fff" : "#1f2937",
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: "1px solid #e5e7eb",
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: "1px solid #d1d5db",
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: "#fff",
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: "#9ca3af",
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 : "#f3f4f6",
681
- color: msg.sender === "user" ? "#fff" : "#1f2937",
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: "1px solid #e5e7eb",
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: "1px solid #d1d5db",
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 &#x2715;\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 &#x2715;\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 &#x2715;\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 &#x2715;\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"]}
@@ -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: "#fff",
303
- border: "1px solid #e5e7eb",
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: "#9ca3af",
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 : "#f3f4f6",
376
- color: msg.sender === "user" ? "#fff" : "#1f2937",
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: "1px solid #e5e7eb",
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: "1px solid #d1d5db",
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: "#fff",
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: "#9ca3af",
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 : "#f3f4f6",
679
- color: msg.sender === "user" ? "#fff" : "#1f2937",
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: "1px solid #e5e7eb",
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: "1px solid #d1d5db",
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
  ),
@@ -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 &#x2715;\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 &#x2715;\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 &#x2715;\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 &#x2715;\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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-support-chat",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Embeddable chat widget SDK that routes customer messages to Slack threads. Open-source Crisp alternative.",
5
5
  "license": "MIT",
6
6
  "author": "Indigo AI",