@enjoys/react-chatbot-plugin 1.9.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/context/ChatContext.ts","../src/styles/theme.ts","../src/components/icons/Icons.tsx","../src/components/Launcher.tsx","../src/components/ChatHeader.tsx","../src/components/WelcomeScreen.tsx","../src/components/forms/TextField.tsx","../src/components/forms/SelectField.tsx","../src/components/forms/RadioField.tsx","../src/components/forms/CheckboxField.tsx","../src/components/forms/FileUploadField.tsx","../src/components/forms/DynamicForm.tsx","../src/components/LoginScreen.tsx","../src/utils/markdown.tsx","../src/components/MessageBubble.tsx","../src/components/QuickReplies.tsx","../src/components/TypingIndicator.tsx","../src/components/MessageList.tsx","../src/components/EmojiPicker.tsx","../src/components/FileUpload.tsx","../src/components/ChatInput.tsx","../src/components/Branding.tsx","../src/utils/helpers.ts","../src/engine/FlowEngine.ts","../src/core/LiveAgentAdapter.ts","../src/core/drivers/WsDriver.ts","../src/core/drivers/SioDriver.ts","../src/types/liveAgent.ts","../src/hooks/useLiveAgent.ts","../src/hooks/useChat.ts","../src/components/ChatWindow.tsx","../src/core/PluginManager.ts","../src/components/ChatBot.tsx","../src/plugins/analyticsPlugin.ts","../src/plugins/loggerPlugin.ts","../src/plugins/utils/http.ts","../src/plugins/webhookPlugin.ts","../src/plugins/crmPlugin.ts","../src/plugins/emailPlugin.ts","../src/plugins/aiPlugin.ts","../src/plugins/intentPlugin.ts","../src/plugins/typingPlugin.ts","../src/plugins/utils/timer.ts","../src/plugins/autoReplyPlugin.ts","../src/plugins/validationPlugin.ts","../src/plugins/uploadPlugin.ts","../src/plugins/utils/storage.ts","../src/plugins/persistencePlugin.ts","../src/plugins/syncPlugin.ts","../src/plugins/authPlugin.ts","../src/plugins/rateLimitPlugin.ts","../src/plugins/pushPlugin.ts","../src/plugins/soundPlugin.ts","../src/plugins/agentPlugin.ts","../src/plugins/transferPlugin.ts","../src/plugins/themePlugin.ts","../src/plugins/componentPlugin.ts","../src/plugins/leadPlugin.ts","../src/plugins/campaignPlugin.ts","../src/plugins/schedulerPlugin.ts","../src/plugins/reminderPlugin.ts","../src/plugins/i18nPlugin.ts","../src/plugins/debugPlugin.ts","../src/plugins/devtoolsPlugin.ts","../src/plugins/mediaPlugin.ts","../src/plugins/markdownPlugin.ts","../src/plugins/liveAgentPlugin.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\r\nimport type { ChatMessage, ChatBotProps } from '../types';\r\nimport type { PluginManager } from '../core/PluginManager';\r\nimport type { AgentInfo } from '../types/liveAgent';\r\n\r\nexport interface ChatState {\r\n isOpen: boolean;\r\n messages: ChatMessage[];\r\n isTyping: boolean;\r\n showWelcome: boolean;\r\n currentStepId: string | null;\r\n collectedData: Record<string, unknown>;\r\n isLoggedIn: boolean;\r\n isLiveAgent: boolean;\r\n agentInfo: AgentInfo | null;\r\n}\r\n\r\nexport type ChatAction =\r\n | { type: 'TOGGLE_OPEN' }\r\n | { type: 'SET_OPEN'; payload: boolean }\r\n | { type: 'ADD_MESSAGE'; payload: ChatMessage }\r\n | { type: 'ADD_MESSAGES'; payload: ChatMessage[] }\r\n | { type: 'SET_TYPING'; payload: boolean }\r\n | { type: 'DISMISS_WELCOME' }\r\n | { type: 'SET_STEP'; payload: string | null }\r\n | { type: 'SET_DATA'; payload: Record<string, unknown> }\r\n | { type: 'SET_LOGGED_IN'; payload: boolean }\r\n | { type: 'CLEAR_QUICK_REPLIES' }\r\n | { type: 'RESET_CHAT' }\r\n | { type: 'UPDATE_MESSAGE'; payload: { id: string; updates: Partial<ChatMessage> } }\r\n | { type: 'SET_LIVE_AGENT'; payload: boolean }\r\n | { type: 'SET_AGENT_INFO'; payload: AgentInfo | null };\r\n\r\nexport function chatReducer(state: ChatState, action: ChatAction): ChatState {\r\n switch (action.type) {\r\n case 'TOGGLE_OPEN':\r\n return { ...state, isOpen: !state.isOpen };\r\n case 'SET_OPEN':\r\n return { ...state, isOpen: action.payload };\r\n case 'ADD_MESSAGE':\r\n return { ...state, messages: [...state.messages, action.payload] };\r\n case 'ADD_MESSAGES':\r\n return { ...state, messages: [...state.messages, ...action.payload] };\r\n case 'SET_TYPING':\r\n return { ...state, isTyping: action.payload };\r\n case 'DISMISS_WELCOME':\r\n return { ...state, showWelcome: false };\r\n case 'SET_STEP':\r\n return { ...state, currentStepId: action.payload };\r\n case 'SET_DATA':\r\n return { ...state, collectedData: { ...state.collectedData, ...action.payload } };\r\n case 'SET_LOGGED_IN':\r\n return { ...state, isLoggedIn: action.payload };\r\n case 'CLEAR_QUICK_REPLIES': {\r\n // Only clear quick replies from the last message that has them\r\n let lastIdx = -1;\r\n for (let i = state.messages.length - 1; i >= 0; i--) {\r\n if (state.messages[i].quickReplies) { lastIdx = i; break; }\r\n }\r\n if (lastIdx === -1) return state;\r\n const updated = [...state.messages];\r\n updated[lastIdx] = { ...updated[lastIdx], quickReplies: undefined };\r\n return { ...state, messages: updated };\r\n }\r\n case 'RESET_CHAT':\r\n return {\r\n ...state,\r\n messages: [],\r\n isTyping: false,\r\n currentStepId: null,\r\n collectedData: {},\r\n };\r\n case 'UPDATE_MESSAGE':\r\n return {\r\n ...state,\r\n messages: state.messages.map((m) =>\r\n m.id === action.payload.id ? { ...m, ...action.payload.updates } : m,\r\n ),\r\n };\r\n case 'SET_LIVE_AGENT':\r\n return { ...state, isLiveAgent: action.payload };\r\n case 'SET_AGENT_INFO':\r\n return { ...state, agentInfo: action.payload };\r\n default:\r\n return state;\r\n }\r\n}\r\n\r\nexport const initialState = (props: ChatBotProps): ChatState => ({\r\n isOpen: props.defaultOpen ?? false,\r\n messages: props.initialMessages ?? [],\r\n isTyping: false,\r\n showWelcome: !!props.customizeChat?.welcomeScreen?.content,\r\n currentStepId: null,\r\n collectedData: {},\r\n isLoggedIn: !props.loginForm,\r\n isLiveAgent: false,\r\n agentInfo: null,\r\n});\r\n\r\ninterface ChatContextValue {\r\n state: ChatState;\r\n dispatch: React.Dispatch<ChatAction>;\r\n props: ChatBotProps;\r\n pluginManager?: PluginManager | null;\r\n}\r\n\r\nexport const ChatContext = createContext<ChatContextValue | null>(null);\r\n\r\nexport function useChatContext(): ChatContextValue {\r\n const ctx = useContext(ChatContext);\r\n if (!ctx) throw new Error('useChatContext must be used within ChatProvider');\r\n return ctx;\r\n}\r\n","import type { ChatTheme, ChatStyle } from '../types';\r\nimport type { CSSProperties } from 'react';\r\n\r\n// ─── Resolved Style Map ─────────────────────────────────────────\r\n\r\n/** Resolved inline styles for each chatbot section. Uses `CSSProperties` by reference\r\n * so the emitted `.d.ts` stays compact instead of expanding every CSS property. */\r\nexport interface ChatStyles {\r\n root: CSSProperties;\r\n launcher: CSSProperties;\r\n window: CSSProperties;\r\n header: CSSProperties;\r\n messageList: CSSProperties;\r\n inputArea: CSSProperties;\r\n botBubble: CSSProperties;\r\n userBubble: CSSProperties;\r\n}\r\n\r\n// ─── Light Mode Defaults ─────────────────────────────────────────\r\n\r\nconst lightDefaults: Required<ChatTheme> = {\r\n primaryColor: '#6C5CE7',\r\n headerBg: 'linear-gradient(135deg, #6C5CE7 0%, #A29BFE 100%)',\r\n headerText: '#FFFFFF',\r\n bubbleBg: 'rgba(241, 243, 249, 0.85)',\r\n bubbleText: '#2D3436',\r\n userBubbleBg: 'linear-gradient(135deg, #6C5CE7 0%, #A29BFE 100%)',\r\n userBubbleText: '#FFFFFF',\r\n fontFamily: '\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: '14px',\r\n borderRadius: '20px',\r\n windowWidth: '400px',\r\n windowHeight: '600px',\r\n mode: 'light',\r\n};\r\n\r\n// ─── Dark Mode Overrides ─────────────────────────────────────────\r\n\r\nconst darkOverrides: Partial<ChatTheme> = {\r\n headerBg: 'linear-gradient(135deg, #2D1B69 0%, #4A3298 100%)',\r\n headerText: '#F0F0FF',\r\n bubbleBg: 'rgba(45, 45, 70, 0.85)',\r\n bubbleText: '#E8E8F0',\r\n userBubbleBg: 'linear-gradient(135deg, #6C5CE7 0%, #A29BFE 100%)',\r\n userBubbleText: '#FFFFFF',\r\n};\r\n\r\nexport function resolveTheme(theme?: ChatTheme): Required<ChatTheme> {\r\n const base = { ...lightDefaults, ...theme };\r\n if (base.mode === 'dark') {\r\n return { ...base, ...darkOverrides, ...theme };\r\n }\r\n return base;\r\n}\r\n\r\n// ─── CSS Custom Properties ───────────────────────────────────────\r\n\r\nexport function buildCSSVariables(theme: Required<ChatTheme>): Record<string, string> {\r\n return {\r\n '--cb-primary': theme.primaryColor,\r\n '--cb-header-bg': theme.headerBg,\r\n '--cb-header-text': theme.headerText,\r\n '--cb-bubble-bg': theme.bubbleBg,\r\n '--cb-bubble-text': theme.bubbleText,\r\n '--cb-user-bubble-bg': theme.userBubbleBg,\r\n '--cb-user-bubble-text': theme.userBubbleText,\r\n '--cb-font-family': theme.fontFamily,\r\n '--cb-font-size': theme.fontSize,\r\n '--cb-border-radius': theme.borderRadius,\r\n '--cb-window-width': theme.windowWidth,\r\n '--cb-window-height': theme.windowHeight,\r\n '--cb-bg': theme.mode === 'dark' ? 'rgba(22, 22, 40, 0.95)' : 'rgba(255, 255, 255, 0.92)',\r\n '--cb-border': theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)',\r\n '--cb-input-bg': theme.mode === 'dark' ? 'rgba(40, 40, 65, 0.8)' : 'rgba(245, 247, 252, 0.9)',\r\n '--cb-input-border': theme.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.06)',\r\n '--cb-input-text': theme.mode === 'dark' ? '#E0E0E0' : '#2D3436',\r\n '--cb-branding-bg': theme.mode === 'dark' ? 'rgba(20, 20, 35, 0.8)' : 'rgba(250, 250, 255, 0.8)',\r\n };\r\n}\r\n\r\n// ─── Inline Styles Builder ───────────────────────────────────────\r\n\r\nexport function buildStyles(\r\n theme: Required<ChatTheme>,\r\n overrides?: ChatStyle,\r\n): ChatStyles {\r\n const isDark = theme.mode === 'dark';\r\n\r\n const styles = {\r\n root: {\r\n fontFamily: theme.fontFamily,\r\n fontSize: theme.fontSize,\r\n lineHeight: '1.5',\r\n } satisfies CSSProperties,\r\n\r\n launcher: {\r\n position: 'fixed',\r\n width: '62px',\r\n height: '62px',\r\n borderRadius: '50%',\r\n background: theme.headerBg,\r\n color: '#fff',\r\n border: 'none',\r\n cursor: 'pointer',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n boxShadow: `0 6px 24px rgba(108, 92, 231, 0.4), 0 2px 8px rgba(0,0,0,0.1)`,\r\n transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\r\n zIndex: 9998,\r\n ...overrides?.launcher,\r\n } satisfies CSSProperties,\r\n\r\n window: {\r\n position: 'fixed',\r\n width: theme.windowWidth,\r\n height: theme.windowHeight,\r\n maxHeight: '85vh',\r\n borderRadius: theme.borderRadius,\r\n overflow: 'hidden',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n boxShadow: isDark\r\n ? '0 20px 60px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.05)'\r\n : '0 20px 60px rgba(108, 92, 231, 0.15), 0 8px 24px rgba(0,0,0,0.08), 0 0 0 1px rgba(0,0,0,0.04)',\r\n backgroundColor: isDark ? 'rgba(22, 22, 40, 0.95)' : 'rgba(255, 255, 255, 0.95)',\r\n backdropFilter: 'blur(20px)',\r\n WebkitBackdropFilter: 'blur(20px)',\r\n border: isDark ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(255,255,255,0.8)',\r\n zIndex: 9999,\r\n animation: 'cb-window-enter 0.35s cubic-bezier(0.4, 0, 0.2, 1)',\r\n ...overrides?.window,\r\n } satisfies CSSProperties,\r\n\r\n header: {\r\n background: theme.headerBg,\r\n color: theme.headerText,\r\n padding: '18px 20px',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n gap: '12px',\r\n flexShrink: 0,\r\n position: 'relative',\r\n overflow: 'hidden',\r\n ...overrides?.header,\r\n } satisfies CSSProperties,\r\n\r\n messageList: {\r\n flex: 1,\r\n overflowY: 'auto',\r\n padding: '20px 16px',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: '10px',\r\n background: isDark\r\n ? 'linear-gradient(180deg, rgba(22, 22, 40, 0.98) 0%, rgba(30, 30, 50, 0.98) 100%)'\r\n : 'linear-gradient(180deg, rgba(248, 249, 254, 0.95) 0%, rgba(255, 255, 255, 0.95) 100%)',\r\n ...overrides?.messageList,\r\n } satisfies CSSProperties,\r\n\r\n inputArea: {\r\n padding: '12px 16px 14px',\r\n borderTop: `1px solid ${isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.05)'}`,\r\n backgroundColor: isDark ? 'rgba(20, 20, 38, 0.9)' : 'rgba(255, 255, 255, 0.95)',\r\n backdropFilter: 'blur(12px)',\r\n WebkitBackdropFilter: 'blur(12px)',\r\n flexShrink: 0,\r\n ...overrides?.inputArea,\r\n } satisfies CSSProperties,\r\n\r\n botBubble: {\r\n background: isDark ? 'rgba(45, 45, 70, 0.7)' : 'rgba(241, 243, 249, 0.9)',\r\n color: isDark ? '#E8E8F0' : '#2D3436',\r\n padding: '12px 16px',\r\n borderRadius: '18px 18px 18px 4px',\r\n maxWidth: '82%',\r\n alignSelf: 'flex-start',\r\n wordBreak: 'break-word',\r\n whiteSpace: 'pre-wrap',\r\n backdropFilter: 'blur(8px)',\r\n WebkitBackdropFilter: 'blur(8px)',\r\n border: isDark ? '1px solid rgba(255,255,255,0.06)' : '1px solid rgba(0,0,0,0.04)',\r\n boxShadow: isDark\r\n ? '0 2px 8px rgba(0,0,0,0.2)'\r\n : '0 2px 8px rgba(0,0,0,0.04)',\r\n fontSize: '14px',\r\n lineHeight: '1.55',\r\n letterSpacing: '0.01em',\r\n } satisfies CSSProperties,\r\n\r\n userBubble: {\r\n background: theme.userBubbleBg,\r\n color: theme.userBubbleText,\r\n padding: '12px 16px',\r\n borderRadius: '18px 18px 4px 18px',\r\n maxWidth: '82%',\r\n alignSelf: 'flex-end',\r\n wordBreak: 'break-word',\r\n whiteSpace: 'pre-wrap',\r\n boxShadow: '0 4px 14px rgba(108, 92, 231, 0.25)',\r\n fontSize: '14px',\r\n lineHeight: '1.55',\r\n letterSpacing: '0.01em',\r\n } satisfies CSSProperties,\r\n };\r\n\r\n return styles;\r\n}\r\n\r\n\r\n","import React from 'react';\n\ninterface IconProps {\n size?: number;\n color?: string;\n className?: string;\n}\n\nexport const SendIcon: React.FC<IconProps> = ({ size = 18, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill={color}>\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" />\n </svg>\n);\n\nexport const ChatBubbleIcon: React.FC<IconProps> = ({ size = 28, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\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\" />\n </svg>\n);\n\nexport const CloseIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\nexport const MinimizeIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n);\n\nexport const EmojiIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 14s1.5 2 4 2 4-2 4-2\" />\n <line x1=\"9\" y1=\"9\" x2=\"9.01\" y2=\"9\" />\n <line x1=\"15\" y1=\"9\" x2=\"15.01\" y2=\"9\" />\n </svg>\n);\n\nexport const AttachmentIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48\" />\n </svg>\n);\n\nexport const FileIcon: React.FC<IconProps> = ({ size = 16, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\" />\n <polyline points=\"14 2 14 8 20 8\" />\n </svg>\n);\n\nexport const ImageIcon: React.FC<IconProps> = ({ size = 16, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n);\n\nexport const RemoveIcon: React.FC<IconProps> = ({ size = 14, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\" />\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\" />\n </svg>\n);\n\nexport const RestartIcon: React.FC<IconProps> = ({ size = 16, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"1 4 1 10 7 10\" />\n <path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\" />\n </svg>\n);\n","import React from 'react';\nimport type { CSSProperties } from 'react';\nimport type { ChatStyles } from '../styles/theme';\nimport { ChatBubbleIcon, CloseIcon } from './icons';\nimport { useChatContext } from '../context/ChatContext';\n\ninterface LauncherProps {\n onClick: () => void;\n isOpen: boolean;\n position: 'bottom-right' | 'bottom-left';\n styles: ChatStyles;\n icon?: React.ReactNode;\n closeIcon?: React.ReactNode;\n zIndex?: number;\n}\n\nexport const Launcher: React.FC<LauncherProps> = ({\n onClick,\n isOpen,\n position,\n styles,\n icon,\n closeIcon,\n zIndex,\n}) => {\n const { props: chatProps } = useChatContext();\n const icons = chatProps.icons;\n const posStyle: CSSProperties =\n position === 'bottom-left'\n ? { bottom: '24px', left: '24px' }\n : { bottom: '24px', right: '24px' };\n\n return (\n <button\n onClick={onClick}\n aria-label={isOpen ? 'Close chat' : 'Open chat'}\n style={{\n ...styles.launcher,\n ...posStyle,\n ...(zIndex != null ? { zIndex } : {}),\n transform: isOpen ? 'scale(0.92) rotate(90deg)' : 'scale(1)',\n animation: isOpen ? 'none' : 'cb-launcher-pulse 3s ease-in-out infinite',\n }}\n >\n {isOpen\n ? closeIcon ?? icons?.close ?? <CloseIcon size={22} />\n : icon ?? icons?.chatBubble ?? <ChatBubbleIcon size={26} />}\n </button>\n );\n};\n","import React from 'react';\nimport type { HeaderConfig } from '../types';\nimport type { ChatStyles as ThemeStyles } from '../styles/theme';\nimport { CloseIcon, MinimizeIcon, RestartIcon } from './icons';\nimport { useChatContext } from '../context/ChatContext';\n\ninterface ChatHeaderProps {\n config: HeaderConfig;\n styles: ThemeStyles;\n onClose: () => void;\n onRestart?: () => void;\n logo?: string;\n logoWidth?: string;\n}\n\nexport const ChatHeader: React.FC<ChatHeaderProps> = ({ config, styles, onClose, onRestart, logo, logoWidth }) => {\n const { props: chatProps } = useChatContext();\n const icons = chatProps.icons;\n return (\n <div style={styles.header}>\n {/* Decorative glow overlay */}\n <div\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: 'linear-gradient(135deg, rgba(255,255,255,0.12) 0%, transparent 50%)',\n pointerEvents: 'none',\n }}\n />\n <div style={{ display: 'flex', alignItems: 'center', gap: '12px', flex: 1, position: 'relative', zIndex: 1 }}>\n {config.avatar && (\n <div style={{ position: 'relative' }}>\n <img\n src={config.avatar}\n alt=\"\"\n style={{\n width: '40px',\n height: '40px',\n borderRadius: '50%',\n objectFit: 'cover',\n border: '2px solid rgba(255,255,255,0.3)',\n }}\n />\n <span\n style={{\n position: 'absolute',\n bottom: '1px',\n right: '1px',\n width: '10px',\n height: '10px',\n backgroundColor: '#2ECC71',\n borderRadius: '50%',\n border: '2px solid rgba(255,255,255,0.8)',\n }}\n />\n </div>\n )}\n {logo && !config.avatar && (\n <img\n src={logo}\n alt=\"\"\n style={{ width: logoWidth ?? '36px', height: 'auto', objectFit: 'contain', filter: 'brightness(1.1)' }}\n />\n )}\n {!config.avatar && !logo && (\n <div\n style={{\n width: '40px',\n height: '40px',\n borderRadius: '50%',\n background: 'rgba(255,255,255,0.2)',\n backdropFilter: 'blur(4px)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '18px',\n fontWeight: 700,\n color: 'rgba(255,255,255,0.9)',\n border: '1px solid rgba(255,255,255,0.2)',\n }}\n >\n {(config.title ?? 'C').charAt(0).toUpperCase()}\n </div>\n )}\n <div>\n <div style={{ fontWeight: 600, fontSize: '16px', letterSpacing: '-0.01em' }}>\n {config.title ?? 'Chat with us'}\n </div>\n {config.subtitle && (\n <div style={{\n fontSize: '12px',\n opacity: 0.8,\n display: 'flex',\n alignItems: 'center',\n gap: '5px',\n marginTop: '1px',\n }}>\n <span style={{\n width: '6px',\n height: '6px',\n borderRadius: '50%',\n backgroundColor: '#2ECC71',\n display: 'inline-block',\n }} />\n {config.subtitle}\n </div>\n )}\n </div>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '2px', position: 'relative', zIndex: 1 }}>\n {config.showRestart && onRestart && (\n <button\n onClick={onRestart}\n aria-label=\"Restart conversation\"\n title=\"Restart conversation\"\n style={{\n background: 'rgba(255,255,255,0.1)',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '8px',\n transition: 'background 0.2s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.2)')}\n onMouseLeave={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.1)')}\n >\n {icons?.restart ?? <RestartIcon size={16} />}\n </button>\n )}\n {config.showMinimize && (\n <button\n onClick={onClose}\n aria-label=\"Minimize chat\"\n style={{\n background: 'rgba(255,255,255,0.1)',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '8px',\n transition: 'background 0.2s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.2)')}\n onMouseLeave={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.1)')}\n >\n {icons?.minimize ?? <MinimizeIcon size={16} />}\n </button>\n )}\n {config.showClose !== false && (\n <button\n onClick={onClose}\n aria-label=\"Close chat\"\n style={{\n background: 'rgba(255,255,255,0.1)',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '8px',\n transition: 'background 0.2s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.2)')}\n onMouseLeave={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.1)')}\n >\n {icons?.close ?? <CloseIcon size={18} />}\n </button>\n )}\n </div>\n </div>\n );\n};\n","import React from 'react';\nimport type { ReactNode } from 'react';\n\ninterface WelcomeScreenProps {\n content: ReactNode;\n onDismiss: () => void;\n primaryColor: string;\n}\n\nexport const WelcomeScreen: React.FC<WelcomeScreenProps> = ({ content, onDismiss, primaryColor }) => {\n return (\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'auto',\n background: 'linear-gradient(180deg, rgba(248, 249, 254, 0.95) 0%, rgba(255, 255, 255, 0.98) 100%)',\n }}\n >\n <div style={{ flex: 1, padding: '28px 24px', overflow: 'auto' }}>\n {content}\n </div>\n <div\n style={{\n padding: '16px 20px',\n borderTop: '1px solid rgba(0,0,0,0.05)',\n backdropFilter: 'blur(12px)',\n WebkitBackdropFilter: 'blur(12px)',\n flexShrink: 0,\n }}\n >\n <button\n onClick={onDismiss}\n style={{\n width: '100%',\n padding: '14px',\n background: `linear-gradient(135deg, ${primaryColor} 0%, ${adjustColor(primaryColor, 30)} 100%)`,\n color: '#fff',\n border: 'none',\n borderRadius: '14px',\n fontSize: '15px',\n fontWeight: 600,\n cursor: 'pointer',\n fontFamily: 'inherit',\n letterSpacing: '0.02em',\n boxShadow: `0 6px 20px ${primaryColor}44`,\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.transform = 'translateY(-1px)';\n e.currentTarget.style.boxShadow = `0 8px 28px ${primaryColor}55`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.transform = 'translateY(0)';\n e.currentTarget.style.boxShadow = `0 6px 20px ${primaryColor}44`;\n }}\n >\n Start Chat\n </button>\n </div>\n </div>\n );\n};\n\nfunction adjustColor(hex: string, amount: number): string {\n const num = parseInt(hex.replace('#', ''), 16);\n const r = Math.min(255, ((num >> 16) & 0xff) + amount);\n const g = Math.min(255, ((num >> 8) & 0xff) + amount);\n const b = Math.min(255, (num & 0xff) + amount);\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;\n}\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface TextFieldProps {\n field: FormFieldConfig;\n value: string;\n onChange: (value: string) => void;\n error?: string;\n}\n\nexport const TextField: React.FC<TextFieldProps> = ({ field, value, onChange, error }) => {\n const isTextarea = field.type === 'textarea';\n const inputType = field.type === 'textarea' ? undefined : field.type;\n\n const baseStyle: React.CSSProperties = {\n width: '100%',\n padding: '10px 14px',\n border: `1.5px solid ${error ? 'rgba(229, 62, 62, 0.5)' : 'rgba(0,0,0,0.08)'}`,\n borderRadius: '12px',\n fontSize: '13px',\n fontFamily: 'inherit',\n outline: 'none',\n boxSizing: 'border-box',\n transition: 'all 0.2s ease',\n backgroundColor: 'rgba(245, 247, 252, 0.6)',\n color: '#2D3436',\n letterSpacing: '0.01em',\n };\n\n return (\n <div style={{ marginBottom: '14px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500, color: '#2D3436' }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '3px' }}>*</span>}\n </label>\n )}\n {isTextarea ? (\n <textarea\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={field.placeholder}\n required={field.required}\n rows={3}\n style={{ ...baseStyle, resize: 'vertical' }}\n minLength={field.validation?.minLength}\n maxLength={field.validation?.maxLength}\n />\n ) : (\n <input\n type={inputType}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={field.placeholder}\n required={field.required}\n style={baseStyle}\n min={field.validation?.min}\n max={field.validation?.max}\n minLength={field.validation?.minLength}\n maxLength={field.validation?.maxLength}\n pattern={field.validation?.pattern}\n />\n )}\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface SelectFieldProps {\n field: FormFieldConfig;\n value: string | string[];\n onChange: (value: string | string[]) => void;\n error?: string;\n}\n\nexport const SelectField: React.FC<SelectFieldProps> = ({ field, value, onChange, error }) => {\n const isMulti = field.type === 'multiselect' || field.multiple;\n\n const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\n if (isMulti) {\n const selected = Array.from(e.target.selectedOptions, (opt) => opt.value);\n onChange(selected);\n } else {\n onChange(e.target.value);\n }\n };\n\n const selectValue = isMulti\n ? Array.isArray(value) ? value : [value].filter(Boolean)\n : typeof value === 'string' ? value : '';\n\n return (\n <div style={{ marginBottom: '14px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500, color: '#2D3436' }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '3px' }}>*</span>}\n </label>\n )}\n <select\n value={selectValue}\n onChange={handleChange}\n multiple={isMulti}\n required={field.required}\n style={{\n width: '100%',\n padding: '10px 14px',\n border: `1.5px solid ${error ? 'rgba(229, 62, 62, 0.5)' : 'rgba(0,0,0,0.08)'}`,\n borderRadius: '12px',\n fontSize: '13px',\n fontFamily: 'inherit',\n outline: 'none',\n backgroundColor: 'rgba(245, 247, 252, 0.6)',\n color: '#2D3436',\n boxSizing: 'border-box',\n transition: 'all 0.2s ease',\n ...(isMulti ? { minHeight: '80px' } : {}),\n }}\n >\n {!isMulti && <option value=\"\">Select...</option>}\n {field.options?.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n {isMulti && (\n <div style={{ fontSize: '11px', color: '#888', marginTop: '2px' }}>\n Hold Ctrl/Cmd to select multiple\n </div>\n )}\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface RadioFieldProps {\n field: FormFieldConfig;\n value: string;\n onChange: (value: string) => void;\n error?: string;\n}\n\nexport const RadioField: React.FC<RadioFieldProps> = ({ field, value, onChange, error }) => {\n return (\n <div style={{ marginBottom: '12px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500 }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '2px' }}>*</span>}\n </label>\n )}\n <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>\n {field.options?.map((opt) => (\n <label\n key={opt.value}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n <input\n type=\"radio\"\n name={field.name}\n value={opt.value}\n checked={value === opt.value}\n onChange={() => onChange(opt.value)}\n style={{ margin: 0 }}\n />\n {opt.label}\n </label>\n ))}\n </div>\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface CheckboxFieldProps {\n field: FormFieldConfig;\n value: string[];\n onChange: (value: string[]) => void;\n error?: string;\n}\n\nexport const CheckboxField: React.FC<CheckboxFieldProps> = ({ field, value, onChange, error }) => {\n const handleToggle = (optValue: string) => {\n if (value.includes(optValue)) {\n onChange(value.filter((v) => v !== optValue));\n } else {\n onChange([...value, optValue]);\n }\n };\n\n return (\n <div style={{ marginBottom: '12px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500 }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '2px' }}>*</span>}\n </label>\n )}\n <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>\n {field.options?.map((opt) => (\n <label\n key={opt.value}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n <input\n type=\"checkbox\"\n checked={value.includes(opt.value)}\n onChange={() => handleToggle(opt.value)}\n style={{ margin: 0 }}\n />\n {opt.label}\n </label>\n ))}\n </div>\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React, { useRef } from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface FileUploadFieldProps {\n field: FormFieldConfig;\n value: FileList | null;\n onChange: (files: FileList | null) => void;\n error?: string;\n primaryColor: string;\n}\n\nexport const FileUploadField: React.FC<FileUploadFieldProps> = ({\n field,\n value,\n onChange,\n error,\n primaryColor,\n}) => {\n const inputRef = useRef<HTMLInputElement>(null);\n\n const fileNames = value ? Array.from(value).map((f) => f.name).join(', ') : '';\n\n return (\n <div style={{ marginBottom: '12px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px', fontWeight: 500 }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '2px' }}>*</span>}\n </label>\n )}\n <input\n ref={inputRef}\n type=\"file\"\n accept={field.accept}\n multiple={field.multiple}\n onChange={(e) => onChange(e.target.files)}\n style={{ display: 'none' }}\n />\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n style={{\n padding: '8px 16px',\n border: `1px dashed ${error ? '#E53E3E' : '#D1D5DB'}`,\n borderRadius: '8px',\n backgroundColor: '#FAFAFA',\n cursor: 'pointer',\n fontSize: '13px',\n color: '#555',\n width: '100%',\n textAlign: 'left',\n }}\n >\n {fileNames || field.placeholder || 'Choose file(s)...'}\n </button>\n {fileNames && (\n <div style={{ fontSize: '12px', color: primaryColor, marginTop: '4px' }}>\n {Array.from(value!).length} file(s) selected\n </div>\n )}\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React, { useState, useCallback } from 'react';\r\nimport type { FormConfig, FormFieldConfig } from '../../types';\r\nimport type { FormFieldRenderMap } from '../../types/form';\r\nimport { TextField } from './TextField';\r\nimport { SelectField } from './SelectField';\r\nimport { RadioField } from './RadioField';\r\nimport { CheckboxField } from './CheckboxField';\r\nimport { FileUploadField } from './FileUploadField';\r\n\r\ninterface DynamicFormProps {\r\n config: FormConfig;\r\n onSubmit: (data: Record<string, unknown>) => void;\r\n primaryColor: string;\r\n renderFormField?: FormFieldRenderMap;\r\n}\r\n\r\nexport const DynamicForm: React.FC<DynamicFormProps> = ({ config, onSubmit, primaryColor, renderFormField }) => {\r\n const [values, setValues] = useState<Record<string, unknown>>(() => {\r\n const init: Record<string, unknown> = {};\r\n for (const field of config.fields) {\r\n if (field.defaultValue !== undefined) {\r\n init[field.name] = field.defaultValue;\r\n } else if (field.type === 'checkbox' || field.type === 'multiselect') {\r\n init[field.name] = [];\r\n } else if (field.type === 'file') {\r\n init[field.name] = null;\r\n } else {\r\n init[field.name] = '';\r\n }\r\n }\r\n return init;\r\n });\r\n\r\n const [errors, setErrors] = useState<Record<string, string>>({});\r\n const [submitted, setSubmitted] = useState(false);\r\n\r\n const setValue = useCallback((name: string, value: unknown) => {\r\n setValues((prev) => ({ ...prev, [name]: value }));\r\n setErrors((prev) => {\r\n const next = { ...prev };\r\n delete next[name];\r\n return next;\r\n });\r\n }, []);\r\n\r\n const validate = (): boolean => {\r\n const newErrors: Record<string, string> = {};\r\n\r\n for (const field of config.fields) {\r\n const val = values[field.name];\r\n\r\n // Required check\r\n if (field.required) {\r\n if (\r\n val === '' ||\r\n val === null ||\r\n val === undefined ||\r\n (Array.isArray(val) && val.length === 0)\r\n ) {\r\n newErrors[field.name] = field.validation?.message ?? `${field.label || field.name} is required`;\r\n continue;\r\n }\r\n }\r\n\r\n // Pattern check\r\n if (field.validation?.pattern && typeof val === 'string' && val) {\r\n try {\r\n const regex = new RegExp(field.validation.pattern);\r\n if (!regex.test(val)) {\r\n newErrors[field.name] = field.validation.message ?? 'Invalid format';\r\n }\r\n } catch {\r\n // Invalid regex pattern in config — skip validation\r\n }\r\n }\r\n }\r\n\r\n setErrors(newErrors);\r\n return Object.keys(newErrors).length === 0;\r\n };\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (!validate()) return;\r\n setSubmitted(true);\r\n onSubmit(values);\r\n };\r\n\r\n if (submitted) {\r\n return (\r\n <div\r\n style={{\r\n padding: '16px',\r\n background: 'linear-gradient(135deg, rgba(46, 213, 115, 0.1), rgba(46, 213, 115, 0.05))',\r\n borderRadius: '14px',\r\n fontSize: '14px',\r\n color: '#2ecc71',\r\n textAlign: 'center',\r\n fontWeight: 500,\r\n border: '1px solid rgba(46, 213, 115, 0.15)',\r\n animation: 'cb-fade-in 0.3s ease-out',\r\n }}\r\n >\r\n ✓ Submitted successfully\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <form\r\n onSubmit={handleSubmit}\r\n style={{\r\n background: 'rgba(255, 255, 255, 0.7)',\r\n backdropFilter: 'blur(12px)',\r\n WebkitBackdropFilter: 'blur(12px)',\r\n borderRadius: '16px',\r\n padding: '20px',\r\n border: '1px solid rgba(0,0,0,0.06)',\r\n boxShadow: '0 4px 16px rgba(0,0,0,0.04)',\r\n animation: 'cb-slide-up 0.35s ease-out',\r\n }}\r\n >\r\n {config.title && (\r\n <div style={{ fontWeight: 600, fontSize: '15px', marginBottom: '4px', color: '#2D3436', letterSpacing: '-0.01em' }}>\r\n {config.title}\r\n </div>\r\n )}\r\n {config.description && (\r\n <div style={{ fontSize: '12px', color: 'rgba(0,0,0,0.45)', marginBottom: '16px', lineHeight: '1.5' }}>\r\n {config.description}\r\n </div>\r\n )}\r\n\r\n {config.fields.map((field) => (\r\n <FormField\r\n key={field.name}\r\n field={field}\r\n value={values[field.name]}\r\n onChange={(v) => setValue(field.name, v)}\r\n error={errors[field.name]}\r\n primaryColor={primaryColor}\r\n renderFormField={renderFormField}\r\n />\r\n ))}\r\n\r\n <button\r\n type=\"submit\"\r\n style={{\r\n width: '100%',\r\n padding: '12px',\r\n background: `linear-gradient(135deg, ${primaryColor} 0%, ${adjustColor(primaryColor, 30)} 100%)`,\r\n color: '#fff',\r\n border: 'none',\r\n borderRadius: '12px',\r\n fontSize: '14px',\r\n fontWeight: 600,\r\n cursor: 'pointer',\r\n marginTop: '8px',\r\n fontFamily: 'inherit',\r\n letterSpacing: '0.02em',\r\n boxShadow: `0 4px 14px ${primaryColor}33`,\r\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = 'translateY(-1px)';\r\n e.currentTarget.style.boxShadow = `0 6px 20px ${primaryColor}44`;\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = 'translateY(0)';\r\n e.currentTarget.style.boxShadow = `0 4px 14px ${primaryColor}33`;\r\n }}\r\n >\r\n {config.submitLabel ?? 'Submit'}\r\n </button>\r\n </form>\r\n );\r\n};\r\n\r\n// ─── Field Router ────────────────────────────────────────────────\r\n\r\ninterface FormFieldProps {\r\n field: FormFieldConfig;\r\n value: unknown;\r\n onChange: (value: unknown) => void;\r\n error?: string;\r\n primaryColor: string;\r\n renderFormField?: FormFieldRenderMap;\r\n}\r\n\r\nconst FormField: React.FC<FormFieldProps> = ({ field, value, onChange, error, primaryColor, renderFormField }) => {\r\n // Check for custom renderer override\r\n const customRenderer = renderFormField?.[field.type as keyof FormFieldRenderMap];\r\n\r\n switch (field.type) {\r\n case 'text':\r\n case 'email':\r\n case 'password':\r\n case 'number':\r\n case 'tel':\r\n case 'url':\r\n case 'textarea':\r\n case 'date':\r\n case 'time': {\r\n const typedProps = { type: field.type as 'text', field, value: String(value ?? ''), onChange: onChange as (v: string) => void, error };\r\n const defaultEl = <TextField field={field} value={String(value ?? '')} onChange={onChange as (v: string) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'select':\r\n case 'multiselect': {\r\n const typedProps = { type: field.type as 'select', field, value: value as string | string[], onChange: onChange as (v: string | string[]) => void, error };\r\n const defaultEl = <SelectField field={field} value={value as string | string[]} onChange={onChange as (v: string | string[]) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'radio': {\r\n const typedProps = { type: 'radio' as const, field, value: String(value ?? ''), onChange: onChange as (v: string) => void, error };\r\n const defaultEl = <RadioField field={field} value={String(value ?? '')} onChange={onChange as (v: string) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'checkbox': {\r\n const typedProps = { type: 'checkbox' as const, field, value: ((value as string[]) ?? []), onChange: onChange as (v: string[]) => void, error };\r\n const defaultEl = <CheckboxField field={field} value={(value as string[]) ?? []} onChange={onChange as (v: string[]) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'file': {\r\n const typedProps = { type: 'file' as const, field, value: value as FileList | null, onChange: onChange as (v: FileList | null) => void, error, primaryColor };\r\n const defaultEl = <FileUploadField field={field} value={value as FileList | null} onChange={onChange as (v: FileList | null) => void} error={error} primaryColor={primaryColor} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'hidden':\r\n return <input type=\"hidden\" name={field.name} value={String(value ?? '')} />;\r\n default:\r\n return null;\r\n }\r\n};\r\n\r\nfunction adjustColor(hex: string, amount: number): string {\r\n const num = parseInt(hex.replace('#', ''), 16);\r\n const r = Math.min(255, ((num >> 16) & 0xff) + amount);\r\n const g = Math.min(255, ((num >> 8) & 0xff) + amount);\r\n const b = Math.min(255, (num & 0xff) + amount);\r\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;\r\n}\r\n","import React from 'react';\nimport type { FormConfig } from '../types';\nimport type { FormFieldRenderMap } from '../types/form';\nimport { DynamicForm } from './forms/DynamicForm';\n\ninterface LoginScreenProps {\n config: FormConfig;\n onLogin: (data: Record<string, unknown>) => void;\n primaryColor: string;\n renderFormField?: FormFieldRenderMap;\n}\n\nexport const LoginScreen: React.FC<LoginScreenProps> = ({ config, onLogin, primaryColor, renderFormField }) => {\n return (\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n padding: '24px',\n overflow: 'auto',\n background: 'linear-gradient(180deg, rgba(248, 249, 254, 0.95) 0%, rgba(255, 255, 255, 0.98) 100%)',\n }}\n >\n <DynamicForm config={config} onSubmit={onLogin} primaryColor={primaryColor} renderFormField={renderFormField} />\n </div>\n );\n};\n","import React from 'react';\r\nimport type { MarkdownOptions } from '../types/config';\r\n\r\n/**\r\n * Lightweight markdown-to-JSX renderer. No external dependencies.\r\n * Supports: bold, italic, code (inline + block), links, lists,\r\n * strikethrough, headings, and line breaks.\r\n */\r\nexport function renderMarkdown(\r\n text: string,\r\n options: MarkdownOptions = {},\r\n): React.ReactNode {\r\n const cfg: Required<MarkdownOptions> = {\r\n bold: options.bold ?? true,\r\n italic: options.italic ?? true,\r\n code: options.code ?? true,\r\n links: options.links ?? true,\r\n lists: options.lists ?? true,\r\n strikethrough: options.strikethrough ?? true,\r\n headings: options.headings ?? false,\r\n };\r\n\r\n // Split by code blocks first to avoid processing markdown inside them\r\n const parts = text.split(/(```[\\s\\S]*?```)/g);\r\n const elements: React.ReactNode[] = [];\r\n\r\n parts.forEach((part, idx) => {\r\n if (cfg.code && part.startsWith('```') && part.endsWith('```')) {\r\n const code = part.slice(3, -3).replace(/^\\w*\\n/, ''); // strip optional language tag\r\n elements.push(\r\n <pre key={idx} style={{ background: 'rgba(0,0,0,0.06)', borderRadius: '6px', padding: '8px 12px', overflowX: 'auto', fontSize: '13px', margin: '4px 0' }}>\r\n <code>{code}</code>\r\n </pre>,\r\n );\r\n } else {\r\n elements.push(...renderBlock(part, cfg, idx));\r\n }\r\n });\r\n\r\n return <>{elements}</>;\r\n}\r\n\r\nfunction renderBlock(\r\n text: string,\r\n cfg: Required<MarkdownOptions>,\r\n blockKey: number,\r\n): React.ReactNode[] {\r\n const lines = text.split('\\n');\r\n const result: React.ReactNode[] = [];\r\n let listItems: React.ReactNode[] = [];\r\n let listKey = 0;\r\n\r\n const flushList = () => {\r\n if (listItems.length > 0) {\r\n result.push(\r\n <ul key={`${blockKey}-ul-${listKey++}`} style={{ margin: '4px 0', paddingLeft: '20px' }}>\r\n {listItems}\r\n </ul>,\r\n );\r\n listItems = [];\r\n }\r\n };\r\n\r\n lines.forEach((line, lineIdx) => {\r\n const key = `${blockKey}-${lineIdx}`;\r\n\r\n // Headings\r\n if (cfg.headings) {\r\n const headingMatch = line.match(/^(#{1,3})\\s+(.+)$/);\r\n if (headingMatch) {\r\n flushList();\r\n const level = headingMatch[1].length as 1 | 2 | 3;\r\n const content = renderInline(headingMatch[2], cfg);\r\n const sizes = { 1: '1.4em', 2: '1.2em', 3: '1.05em' };\r\n result.push(\r\n <div key={key} style={{ fontWeight: 700, fontSize: sizes[level], margin: '6px 0 2px' }}>\r\n {content}\r\n </div>,\r\n );\r\n return;\r\n }\r\n }\r\n\r\n // List items\r\n if (cfg.lists && /^[\\-\\*•]\\s+/.test(line)) {\r\n const content = line.replace(/^[\\-\\*•]\\s+/, '');\r\n listItems.push(<li key={key}>{renderInline(content, cfg)}</li>);\r\n return;\r\n }\r\n\r\n // Ordered list\r\n if (cfg.lists && /^\\d+\\.\\s+/.test(line)) {\r\n flushList(); // flush unordered first\r\n const content = line.replace(/^\\d+\\.\\s+/, '');\r\n // Wrap single ordered items inline for now\r\n result.push(\r\n <div key={key} style={{ paddingLeft: '20px' }}>\r\n {line.match(/^\\d+/)![0]}. {renderInline(content, cfg)}\r\n </div>,\r\n );\r\n return;\r\n }\r\n\r\n flushList();\r\n\r\n // Empty line = paragraph break\r\n if (line.trim() === '') {\r\n result.push(<br key={key} />);\r\n return;\r\n }\r\n\r\n // Normal line\r\n result.push(\r\n <span key={key} style={{ display: 'block' }}>\r\n {renderInline(line, cfg)}\r\n </span>,\r\n );\r\n });\r\n\r\n flushList();\r\n return result;\r\n}\r\n\r\n/** Parses inline markdown (bold, italic, code, links, strikethrough) into React nodes. */\r\nfunction renderInline(text: string, cfg: Required<MarkdownOptions>): React.ReactNode {\r\n // Build a regex that matches all inline patterns we support\r\n const patterns: string[] = [];\r\n\r\n if (cfg.code) patterns.push('`([^`]+)`');\r\n if (cfg.bold) patterns.push('\\\\*\\\\*([^*]+)\\\\*\\\\*', '__([^_]+)__');\r\n if (cfg.strikethrough) patterns.push('~~([^~]+)~~');\r\n if (cfg.italic) patterns.push('\\\\*([^*]+)\\\\*', '(?<!\\\\w)_([^_]+)_(?!\\\\w)');\r\n if (cfg.links) patterns.push('\\\\[([^\\\\]]+)\\\\]\\\\((https?:\\\\/\\\\/[^)]+)\\\\)');\r\n\r\n if (patterns.length === 0) return text;\r\n\r\n const combined = new RegExp(patterns.join('|'), 'g');\r\n const parts: React.ReactNode[] = [];\r\n let lastIndex = 0;\r\n let key = 0;\r\n let match: RegExpExecArray | null;\r\n\r\n while ((match = combined.exec(text)) !== null) {\r\n // Push preceding text\r\n if (match.index > lastIndex) {\r\n parts.push(text.slice(lastIndex, match.index));\r\n }\r\n\r\n const full = match[0];\r\n\r\n if (cfg.code && full.startsWith('`') && full.endsWith('`')) {\r\n parts.push(\r\n <code key={key++} style={{ background: 'rgba(0,0,0,0.06)', borderRadius: '3px', padding: '1px 4px', fontSize: '0.9em' }}>\r\n {full.slice(1, -1)}\r\n </code>,\r\n );\r\n } else if (cfg.bold && (full.startsWith('**') || full.startsWith('__'))) {\r\n const inner = full.startsWith('**') ? full.slice(2, -2) : full.slice(2, -2);\r\n parts.push(<strong key={key++}>{renderInline(inner, { ...cfg, bold: false })}</strong>);\r\n } else if (cfg.strikethrough && full.startsWith('~~')) {\r\n parts.push(<del key={key++}>{renderInline(full.slice(2, -2), { ...cfg, strikethrough: false })}</del>);\r\n } else if (cfg.italic && (full.startsWith('*') || full.startsWith('_'))) {\r\n const inner = full.slice(1, -1);\r\n parts.push(<em key={key++}>{renderInline(inner, { ...cfg, italic: false })}</em>);\r\n } else if (cfg.links && full.startsWith('[')) {\r\n const linkMatch = full.match(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^)]+)\\)/);\r\n if (linkMatch) {\r\n parts.push(\r\n <a key={key++} href={linkMatch[2]} target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: 'inherit', textDecoration: 'underline' }}>\r\n {linkMatch[1]}\r\n </a>,\r\n );\r\n }\r\n } else {\r\n parts.push(full);\r\n }\r\n\r\n lastIndex = match.index + full.length;\r\n }\r\n\r\n if (lastIndex < text.length) {\r\n parts.push(text.slice(lastIndex));\r\n }\r\n\r\n return parts.length === 1 ? parts[0] : <>{parts}</>;\r\n}\r\n","import React from 'react';\r\nimport type { ChatMessage, MessageAttachment } from '../types';\r\nimport type { ChatStyles } from '../styles/theme';\r\nimport { FileIcon } from './icons';\r\nimport { useChatContext } from '../context/ChatContext';\r\nimport { renderMarkdown } from '../utils/markdown';\r\n\r\ninterface MessageBubbleProps {\r\n message: ChatMessage;\r\n styles: ChatStyles;\r\n}\r\n\r\nexport const MessageBubble: React.FC<MessageBubbleProps> = ({ message, styles }) => {\r\n const { props: chatProps } = useChatContext();\r\n const isBot = message.sender === 'bot';\r\n const isAgent = message.sender === 'agent';\r\n const isSystem = message.sender === 'system';\r\n const bubbleStyle = isBot || isSystem || isAgent ? styles.botBubble : styles.userBubble;\r\n\r\n const hasContent = message.text || (message.attachments && message.attachments.length > 0);\r\n if (!hasContent) return null;\r\n\r\n const agentName = message.agentName ?? (message.metadata?.agentName as string | undefined);\r\n\r\n const systemStyle: React.CSSProperties = isSystem\r\n ? {\r\n background: 'transparent',\r\n border: 'none',\r\n boxShadow: 'none',\r\n color: '#999',\r\n fontSize: '12px',\r\n alignSelf: 'center',\r\n padding: '6px 12px',\r\n backdropFilter: 'none',\r\n WebkitBackdropFilter: 'none',\r\n }\r\n : {};\r\n\r\n return (\r\n <div\r\n style={{\r\n ...bubbleStyle,\r\n ...systemStyle,\r\n animation: 'cb-fade-in 0.3s ease-out',\r\n }}\r\n >\r\n {isAgent && agentName && (\r\n <div style={{ fontSize: '11px', fontWeight: 600, opacity: 0.7, marginBottom: '4px' }}>\r\n {agentName}\r\n </div>\r\n )}\r\n {message.text && (\r\n <span style={{ display: 'block' }}>\r\n {chatProps.markdown\r\n ? renderMarkdown(message.text, chatProps.markdown === true ? {} : chatProps.markdown)\r\n : message.text}\r\n </span>\r\n )}\r\n {message.attachments && message.attachments.length > 0 && (\r\n <div style={{ marginTop: message.text ? '10px' : 0, display: 'flex', flexDirection: 'column', gap: '6px' }}>\r\n {message.attachments.map((attachment, i) => (\r\n <AttachmentPreview key={i} attachment={attachment} isBot={isBot} />\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\n// ─── Attachment Preview ──────────────────────────────────────────\r\n\r\ninterface AttachmentPreviewProps {\r\n attachment: MessageAttachment;\r\n isBot: boolean;\r\n}\r\n\r\nconst AttachmentPreview: React.FC<AttachmentPreviewProps> = ({ attachment, isBot }) => {\r\n const { props: chatProps } = useChatContext();\r\n const icons = chatProps.icons;\r\n const isImage = attachment.type.startsWith('image/');\r\n\r\n if (isImage && attachment.url) {\r\n return (\r\n <div style={{ borderRadius: '12px', overflow: 'hidden', maxWidth: '220px' }}>\r\n <img\r\n src={attachment.url}\r\n alt={attachment.name}\r\n style={{\r\n width: '100%',\r\n height: 'auto',\r\n display: 'block',\r\n borderRadius: '12px',\r\n }}\r\n />\r\n <div style={{ fontSize: '11px', padding: '4px 0', opacity: 0.6 }}>{attachment.name}</div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <a\r\n href={attachment.url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '8px',\r\n padding: '8px 12px',\r\n backgroundColor: isBot ? 'rgba(0,0,0,0.04)' : 'rgba(255,255,255,0.15)',\r\n borderRadius: '10px',\r\n textDecoration: 'none',\r\n color: 'inherit',\r\n fontSize: '13px',\r\n border: isBot ? '1px solid rgba(0,0,0,0.06)' : '1px solid rgba(255,255,255,0.15)',\r\n transition: 'background 0.2s ease',\r\n }}\r\n >\r\n {icons?.file ?? <FileIcon size={16} />}\r\n <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>\r\n {attachment.name}\r\n </span>\r\n {attachment.size && (\r\n <span style={{ fontSize: '11px', opacity: 0.5, flexShrink: 0 }}>\r\n {formatFileSize(attachment.size)}\r\n </span>\r\n )}\r\n </a>\r\n );\r\n};\r\n\r\nfunction formatFileSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes}B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\r\n}\r\n","import React from 'react';\nimport type { FlowQuickReply } from '../types';\n\ninterface QuickRepliesProps {\n replies: FlowQuickReply[];\n onSelect: (value: string, label: string) => void;\n primaryColor: string;\n}\n\nexport const QuickReplies: React.FC<QuickRepliesProps> = ({ replies, onSelect, primaryColor }) => {\n return (\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '8px',\n alignSelf: 'flex-start',\n maxWidth: '90%',\n animation: 'cb-slide-up 0.35s ease-out',\n padding: '4px 0',\n }}\n >\n {replies.map((reply) => (\n <button\n key={reply.value}\n onClick={() => onSelect(reply.value, reply.label)}\n style={{\n padding: '8px 18px',\n borderRadius: '22px',\n border: `1.5px solid ${primaryColor}`,\n backgroundColor: 'rgba(108, 92, 231, 0.06)',\n color: primaryColor,\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 500,\n fontFamily: 'inherit',\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\n backdropFilter: 'blur(4px)',\n WebkitBackdropFilter: 'blur(4px)',\n letterSpacing: '0.01em',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = primaryColor;\n e.currentTarget.style.color = '#fff';\n e.currentTarget.style.transform = 'translateY(-1px)';\n e.currentTarget.style.boxShadow = `0 4px 14px ${primaryColor}44`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(108, 92, 231, 0.06)';\n e.currentTarget.style.color = primaryColor;\n e.currentTarget.style.transform = 'translateY(0)';\n e.currentTarget.style.boxShadow = 'none';\n }}\n >\n {reply.label}\n </button>\n ))}\n </div>\n );\n};\n","import React from 'react';\n\ninterface TypingIndicatorProps {\n color: string;\n}\n\nexport const TypingIndicator: React.FC<TypingIndicatorProps> = ({ color }) => {\n const dotStyle: React.CSSProperties = {\n width: '7px',\n height: '7px',\n borderRadius: '50%',\n backgroundColor: color,\n opacity: 0.35,\n animation: 'cb-typing-bounce 1.4s infinite ease-in-out',\n };\n\n return (\n <div\n style={{\n display: 'flex',\n gap: '5px',\n padding: '14px 18px',\n background: 'rgba(241, 243, 249, 0.8)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n borderRadius: '18px 18px 18px 4px',\n alignSelf: 'flex-start',\n alignItems: 'center',\n border: '1px solid rgba(0,0,0,0.04)',\n boxShadow: '0 2px 8px rgba(0,0,0,0.04)',\n animation: 'cb-fade-in 0.3s ease-out',\n }}\n >\n <span style={{ ...dotStyle, animationDelay: '0s' }} />\n <span style={{ ...dotStyle, animationDelay: '0.2s' }} />\n <span style={{ ...dotStyle, animationDelay: '0.4s' }} />\n </div>\n );\n};\n","import React, { useRef, useEffect } from 'react';\r\nimport type { ComponentType } from 'react';\r\nimport type { ChatMessage } from '../types';\r\nimport type { StepComponentProps, FlowActionResult, ChatCustomizeChat } from '../types/config';\r\nimport type { FormFieldRenderMap } from '../types/form';\r\nimport type { ChatStyles } from '../styles/theme';\r\nimport { MessageBubble } from './MessageBubble';\r\nimport { QuickReplies } from './QuickReplies';\r\nimport { TypingIndicator } from './TypingIndicator';\r\nimport { DynamicForm } from './forms/DynamicForm';\r\n\r\ninterface MessageListProps {\r\n messages: ChatMessage[];\r\n isTyping: boolean;\r\n styles: ChatStyles;\r\n primaryColor: string;\r\n onQuickReply: (value: string, label: string) => void;\r\n onFormSubmit: (formId: string, data: Record<string, unknown>) => void;\r\n /** Map of custom step components */\r\n components?: Record<string, ComponentType<StepComponentProps>>;\r\n /** Called when a custom component completes */\r\n onComponentComplete?: (result?: FlowActionResult) => void;\r\n /** Collected flow data — passed to custom components */\r\n collectedData?: Record<string, unknown>;\r\n /** Current step ID */\r\n currentStepId?: string | null;\r\n /** Custom form field renderers per field type */\r\n renderFormField?: FormFieldRenderMap;\r\n /** Slot overrides from customizeChat */\r\n customizeChat?: ChatCustomizeChat;\r\n}\r\n\r\nexport const MessageList: React.FC<MessageListProps> = ({\r\n messages,\r\n isTyping,\r\n styles,\r\n primaryColor,\r\n onQuickReply,\r\n onFormSubmit,\r\n components,\r\n onComponentComplete,\r\n collectedData,\r\n currentStepId,\r\n renderFormField,\r\n customizeChat,\r\n}) => {\r\n const bottomRef = useRef<HTMLDivElement>(null);\r\n\r\n useEffect(() => {\r\n bottomRef.current?.scrollIntoView({ behavior: 'smooth' });\r\n }, [messages, isTyping]);\r\n\r\n const Bubble = customizeChat?.messageBubble?.component ?? MessageBubble;\r\n const QR = customizeChat?.quickReplies?.component ?? QuickReplies;\r\n const Typing = customizeChat?.typingIndicator?.component ?? TypingIndicator;\r\n\r\n return (\r\n <div style={styles.messageList} className=\"cb-scrollbar\">\r\n {messages.map((msg) => (\r\n <React.Fragment key={msg.id}>\r\n <Bubble message={msg} styles={styles} />\r\n {msg.quickReplies && msg.quickReplies.length > 0 && (\r\n <QR\r\n replies={msg.quickReplies}\r\n onSelect={onQuickReply}\r\n primaryColor={primaryColor}\r\n />\r\n )}\r\n {msg.form && (\r\n <div style={{ alignSelf: 'flex-start', width: '92%', animation: 'cb-slide-up 0.35s ease-out' }}>\r\n <DynamicForm\r\n config={msg.form}\r\n onSubmit={(data) => onFormSubmit(msg.form!.id, data)}\r\n primaryColor={primaryColor}\r\n renderFormField={renderFormField}\r\n />\r\n </div>\r\n )}\r\n {msg.component && components?.[msg.component] && (\r\n <div style={{ alignSelf: 'flex-start', width: '92%', animation: 'cb-slide-up 0.35s ease-out' }}>\r\n {React.createElement(components[msg.component], {\r\n stepId: currentStepId ?? '',\r\n data: collectedData ?? {},\r\n onComplete: (result?: FlowActionResult) => onComponentComplete?.(result),\r\n })}\r\n </div>\r\n )}\r\n </React.Fragment>\r\n ))}\r\n {isTyping && <Typing color={primaryColor} />}\r\n <div ref={bottomRef} />\r\n </div>\r\n );\r\n};\r\n","import React, { useState, useRef, useEffect } from 'react';\n\nconst EMOJI_CATEGORIES = [\n {\n name: 'Smileys',\n emojis: ['😀', '😃', '😄', '😁', '😅', '😂', '🤣', '😊', '😇', '🙂', '😉', '😍', '🥰', '😘', '😋', '😜', '🤪', '🤗', '🤔', '🤫', '🤭', '😏', '😐', '😑', '😶', '😌', '😴', '🤤', '😷', '🤒'],\n },\n {\n name: 'Gestures',\n emojis: ['👍', '👎', '👌', '✌️', '🤞', '🤟', '🤘', '👋', '🤚', '✋', '🖖', '👏', '🙌', '🤝', '🙏', '💪', '🖐️', '☝️', '👆', '👇', '👈', '👉', '🤙', '🫡', '🫶', '🫰', '🫳', '🫴', '🫲', '🫱'],\n },\n {\n name: 'Hearts',\n emojis: ['❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔', '❣️', '💕', '💞', '💓', '💗', '💖', '💘', '💝', '💟', '♥️', '🫀', '💌', '💐', '🌹', '🌺', '🌸', '🌼', '🌻', '🌷', '💮'],\n },\n {\n name: 'Objects',\n emojis: ['🔥', '⭐', '✨', '💯', '🎉', '🎊', '🎯', '🚀', '💡', '📌', '📎', '🔗', '💻', '📱', '☎️', '📧', '📝', '📋', '📊', '📈', '🗂️', '📁', '🔒', '🔑', '⚙️', '🛠️', '🔧', '📦', '🏷️', '✅'],\n },\n];\n\ninterface EmojiPickerProps {\n onSelect: (emoji: string) => void;\n onClose: () => void;\n primaryColor: string;\n}\n\nexport const EmojiPicker: React.FC<EmojiPickerProps> = ({ onSelect, onClose, primaryColor }) => {\n const [activeCategory, setActiveCategory] = useState(0);\n const pickerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (pickerRef.current && !pickerRef.current.contains(e.target as Node)) {\n onClose();\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [onClose]);\n\n const currentEmojis = EMOJI_CATEGORIES[activeCategory]?.emojis ?? [];\n\n return (\n <div\n ref={pickerRef}\n style={{\n position: 'absolute',\n bottom: '100%',\n left: 0,\n width: '280px',\n backgroundColor: 'rgba(255, 255, 255, 0.92)',\n backdropFilter: 'blur(20px)',\n WebkitBackdropFilter: 'blur(20px)',\n borderRadius: '16px',\n boxShadow: '0 8px 32px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04)',\n border: '1px solid rgba(255,255,255,0.8)',\n overflow: 'hidden',\n zIndex: 10,\n marginBottom: '8px',\n animation: 'cb-slide-up 0.25s ease-out',\n }}\n >\n {/* Category tabs */}\n <div\n style={{\n display: 'flex',\n borderBottom: '1px solid rgba(0,0,0,0.06)',\n padding: '6px',\n gap: '3px',\n }}\n >\n {EMOJI_CATEGORIES.map((cat, idx) => (\n <button\n key={cat.name}\n onClick={() => setActiveCategory(idx)}\n title={cat.name}\n style={{\n flex: 1,\n padding: '6px 4px',\n border: 'none',\n borderRadius: '8px',\n cursor: 'pointer',\n fontSize: '11px',\n fontWeight: 600,\n fontFamily: 'inherit',\n letterSpacing: '0.02em',\n background: idx === activeCategory\n ? `linear-gradient(135deg, ${primaryColor}, ${primaryColor}CC)`\n : 'transparent',\n color: idx === activeCategory ? '#fff' : 'rgba(0,0,0,0.4)',\n transition: 'all 0.2s ease',\n boxShadow: idx === activeCategory ? `0 2px 8px ${primaryColor}33` : 'none',\n }}\n >\n {cat.name}\n </button>\n ))}\n </div>\n\n {/* Emoji Grid */}\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(8, 1fr)',\n gap: '2px',\n padding: '8px',\n maxHeight: '180px',\n overflowY: 'auto',\n }}\n >\n {currentEmojis.map((emoji) => (\n <button\n key={emoji}\n onClick={() => {\n onSelect(emoji);\n onClose();\n }}\n style={{\n width: '30px',\n height: '30px',\n border: 'none',\n backgroundColor: 'transparent',\n cursor: 'pointer',\n fontSize: '18px',\n borderRadius: '6px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = 'rgba(108, 92, 231, 0.08)')}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = 'transparent')}\n >\n {emoji}\n </button>\n ))}\n </div>\n </div>\n );\n};\n","import React, { useRef } from 'react';\r\nimport type { FileUploadConfig } from '../types/config';\r\nimport { AttachmentIcon, RemoveIcon, FileIcon, ImageIcon } from './icons';\r\nimport { useChatContext } from '../context/ChatContext';\r\n\r\ninterface FileUploadButtonProps {\r\n config: FileUploadConfig;\r\n onFiles: (files: File[]) => void;\r\n selectedFiles: File[];\r\n onRemoveFile: (index: number) => void;\r\n primaryColor: string;\r\n}\r\n\r\nexport const FileUploadButton: React.FC<FileUploadButtonProps> = ({\r\n config,\r\n onFiles,\r\n selectedFiles,\r\n onRemoveFile,\r\n primaryColor,\r\n}) => {\r\n const { props: chatProps } = useChatContext();\r\n const icons = chatProps.icons;\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n const files = e.target.files;\r\n if (!files || files.length === 0) return;\r\n\r\n const fileArr = Array.from(files);\r\n\r\n // Validate file size\r\n if (config.maxSize) {\r\n const oversized = fileArr.filter((f) => f.size > config.maxSize!);\r\n if (oversized.length > 0) {\r\n alert(`File(s) too large. Max size: ${formatSize(config.maxSize)}`);\r\n return;\r\n }\r\n }\r\n\r\n // Validate max count\r\n const maxFiles = config.maxFiles ?? 5;\r\n if (selectedFiles.length + fileArr.length > maxFiles) {\r\n alert(`Maximum ${maxFiles} files allowed`);\r\n return;\r\n }\r\n\r\n onFiles(fileArr);\r\n // Reset input to allow re-selecting same file\r\n if (inputRef.current) inputRef.current.value = '';\r\n };\r\n\r\n return (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n type=\"file\"\r\n accept={config.accept}\r\n multiple={config.multiple !== false}\r\n onChange={handleChange}\r\n style={{ display: 'none' }}\r\n />\r\n <button\r\n type=\"button\"\r\n onClick={() => inputRef.current?.click()}\r\n aria-label=\"Attach file\"\r\n title=\"Attach file\"\r\n style={{\r\n background: 'none',\r\n border: 'none',\r\n cursor: 'pointer',\r\n padding: '6px',\r\n display: 'flex',\r\n alignItems: 'center',\r\n color: '#999',\r\n borderRadius: '6px',\r\n transition: 'color 0.15s ease',\r\n }}\r\n onMouseEnter={(e) => (e.currentTarget.style.color = primaryColor)}\r\n onMouseLeave={(e) => (e.currentTarget.style.color = '#999')}\r\n >\r\n {icons?.attachment ?? <AttachmentIcon size={20} />}\r\n </button>\r\n </div>\r\n );\r\n};\r\n\r\n// ─── File Preview List ───────────────────────────────────────────\r\n\r\ninterface FilePreviewListProps {\r\n files: File[];\r\n onRemove: (index: number) => void;\r\n primaryColor: string;\r\n}\r\n\r\nexport const FilePreviewList: React.FC<FilePreviewListProps> = ({\r\n files,\r\n onRemove,\r\n primaryColor,\r\n}) => {\r\n if (files.length === 0) return null;\r\n\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n flexWrap: 'wrap',\r\n gap: '6px',\r\n padding: '8px 12px 0',\r\n }}\r\n >\r\n {files.map((file, idx) => (\r\n <FilePreviewChip\r\n key={`${file.name}-${idx}`}\r\n file={file}\r\n onRemove={() => onRemove(idx)}\r\n primaryColor={primaryColor}\r\n />\r\n ))}\r\n </div>\r\n );\r\n};\r\n\r\n// ─── Single File Chip ────────────────────────────────────────────\r\n\r\ninterface FilePreviewChipProps {\r\n file: File;\r\n onRemove: () => void;\r\n primaryColor: string;\r\n}\r\n\r\nconst FilePreviewChip: React.FC<FilePreviewChipProps> = ({ file, onRemove, primaryColor }) => {\r\n const { props: chatProps } = useChatContext();\r\n const icons = chatProps.icons;\r\n const isImage = file.type.startsWith('image/');\r\n\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '6px',\r\n padding: '4px 8px',\r\n backgroundColor: '#F3F4F6',\r\n borderRadius: '8px',\r\n fontSize: '12px',\r\n maxWidth: '200px',\r\n }}\r\n >\r\n <span style={{ color: primaryColor, flexShrink: 0 }}>\r\n {isImage ? (icons?.image ?? <ImageIcon size={14} />) : (icons?.file ?? <FileIcon size={14} />)}\r\n </span>\r\n <span\r\n style={{\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n color: '#555',\r\n }}\r\n >\r\n {file.name}\r\n </span>\r\n <span style={{ color: '#999', fontSize: '11px', flexShrink: 0 }}>\r\n {formatSize(file.size)}\r\n </span>\r\n <button\r\n onClick={onRemove}\r\n style={{\r\n background: 'none',\r\n border: 'none',\r\n cursor: 'pointer',\r\n padding: '0',\r\n display: 'flex',\r\n color: '#999',\r\n flexShrink: 0,\r\n }}\r\n >\r\n {icons?.remove ?? <RemoveIcon size={14} />}\r\n </button>\r\n </div>\r\n );\r\n};\r\n\r\n// ─── Utils ───────────────────────────────────────────────────────\r\n\r\nfunction formatSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes}B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\r\n}\r\n","import React, { useState, useRef, useCallback } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { FileUploadConfig } from '../types/config';\nimport { SendIcon, EmojiIcon } from './icons';\nimport { EmojiPicker } from './EmojiPicker';\nimport { FileUploadButton, FilePreviewList } from './FileUpload';\nimport { useChatContext } from '../context/ChatContext';\n\ninterface ChatInputProps {\n onSend: (text: string, files?: File[]) => void;\n placeholder?: string;\n primaryColor: string;\n isDark?: boolean;\n disabled?: boolean;\n styleOverride?: CSSProperties;\n enableEmoji?: boolean;\n fileUpload?: FileUploadConfig;\n onFileUpload?: (files: File[]) => void;\n}\n\nexport const ChatInput: React.FC<ChatInputProps> = ({\n onSend,\n placeholder = 'Type a message...',\n primaryColor,\n isDark = false,\n disabled,\n styleOverride,\n enableEmoji = false,\n fileUpload,\n onFileUpload,\n}) => {\n const { props: chatProps } = useChatContext();\n const icons = chatProps.icons;\n const [text, setText] = useState('');\n const [showEmoji, setShowEmoji] = useState(false);\n const [attachedFiles, setAttachedFiles] = useState<File[]>([]);\n const inputRef = useRef<HTMLTextAreaElement>(null);\n\n const handleSend = useCallback(() => {\n const trimmed = text.trim();\n if (!trimmed && attachedFiles.length === 0) return;\n onSend(trimmed, attachedFiles.length > 0 ? attachedFiles : undefined);\n setText('');\n setAttachedFiles([]);\n inputRef.current?.focus();\n }, [text, attachedFiles, onSend]);\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n };\n\n const handleEmojiSelect = (emoji: string) => {\n setText((prev) => prev + emoji);\n inputRef.current?.focus();\n };\n\n const handleFiles = (files: File[]) => {\n setAttachedFiles((prev) => [...prev, ...files]);\n onFileUpload?.(files);\n };\n\n const handleRemoveFile = (index: number) => {\n setAttachedFiles((prev) => prev.filter((_, i) => i !== index));\n };\n\n const hasContent = text.trim() || attachedFiles.length > 0;\n\n return (\n <div style={{ position: 'relative', ...styleOverride }}>\n {/* File preview above input */}\n {attachedFiles.length > 0 && (\n <FilePreviewList\n files={attachedFiles}\n onRemove={handleRemoveFile}\n primaryColor={primaryColor}\n />\n )}\n\n {/* Emoji picker */}\n {showEmoji && (\n <EmojiPicker\n onSelect={handleEmojiSelect}\n onClose={() => setShowEmoji(false)}\n primaryColor={primaryColor}\n />\n )}\n\n <div\n style={{\n display: 'flex',\n gap: '8px',\n alignItems: 'flex-end',\n background: isDark ? 'rgba(40, 40, 65, 0.5)' : 'rgba(245, 247, 252, 0.7)',\n borderRadius: '16px',\n border: `1px solid ${isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)'}`,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n padding: '6px 6px 6px 12px',\n }}\n >\n {/* Action buttons */}\n <div style={{ display: 'flex', alignItems: 'center', gap: '2px', flexShrink: 0, paddingBottom: '2px' }}>\n {enableEmoji && (\n <button\n type=\"button\"\n onClick={() => setShowEmoji(!showEmoji)}\n aria-label=\"Emoji\"\n title=\"Emoji\"\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n color: showEmoji ? primaryColor : (isDark ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.3)'),\n borderRadius: '8px',\n transition: 'all 0.2s ease',\n }}\n >\n {icons?.emoji ?? <EmojiIcon size={20} />}\n </button>\n )}\n\n {fileUpload?.enabled && (\n <FileUploadButton\n config={fileUpload}\n onFiles={handleFiles}\n selectedFiles={attachedFiles}\n onRemoveFile={handleRemoveFile}\n primaryColor={primaryColor}\n />\n )}\n </div>\n\n {/* Text Input */}\n <textarea\n ref={inputRef}\n value={text}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n style={{\n flex: 1,\n padding: '8px 2px',\n border: 'none',\n borderRadius: '12px',\n outline: 'none',\n resize: 'none',\n fontFamily: 'inherit',\n fontSize: '14px',\n lineHeight: '1.45',\n maxHeight: '100px',\n overflowY: 'auto',\n backgroundColor: 'transparent',\n color: isDark ? '#E0E0E0' : '#2D3436',\n letterSpacing: '0.01em',\n }}\n />\n\n {/* Send Button */}\n <button\n onClick={handleSend}\n disabled={disabled || !hasContent}\n aria-label=\"Send message\"\n style={{\n width: '36px',\n height: '36px',\n borderRadius: '12px',\n background: hasContent\n ? `linear-gradient(135deg, ${primaryColor} 0%, ${adjustColor(primaryColor, 30)} 100%)`\n : (isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)'),\n color: hasContent ? '#fff' : (isDark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.2)'),\n border: 'none',\n cursor: hasContent ? 'pointer' : 'default',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\n boxShadow: hasContent ? `0 4px 12px ${primaryColor}44` : 'none',\n }}\n >\n {icons?.send ?? <SendIcon size={16} />}\n </button>\n </div>\n </div>\n );\n};\n\nfunction adjustColor(hex: string, amount: number): string {\n const num = parseInt(hex.replace('#', ''), 16);\n const r = Math.min(255, ((num >> 16) & 0xff) + amount);\n const g = Math.min(255, ((num >> 8) & 0xff) + amount);\n const b = Math.min(255, (num & 0xff) + amount);\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;\n}\n","import React from 'react';\nimport type { BrandingConfig } from '../types/config';\n\ninterface BrandingProps {\n config: BrandingConfig;\n primaryColor: string;\n}\n\nexport const Branding: React.FC<BrandingProps> = ({ config, primaryColor }) => {\n if (config.showBranding === false) return null;\n\n const text = config.poweredBy ?? 'React ChatBot';\n\n return (\n <div\n style={{\n padding: '8px 16px',\n textAlign: 'center',\n fontSize: '11px',\n color: 'rgba(0,0,0,0.35)',\n background: 'rgba(250, 250, 255, 0.7)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n borderTop: '1px solid rgba(0,0,0,0.04)',\n flexShrink: 0,\n letterSpacing: '0.02em',\n }}\n >\n Powered by{' '}\n {config.poweredByUrl ? (\n <a\n href={config.poweredByUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{\n color: primaryColor,\n textDecoration: 'none',\n fontWeight: 600,\n transition: 'opacity 0.2s ease',\n }}\n >\n {text}\n </a>\n ) : (\n <span style={{ color: primaryColor, fontWeight: 600 }}>{text}</span>\n )}\n </div>\n );\n};\n","let counter = 0;\n\nexport const uid = (): string => `msg_${Date.now()}_${++counter}`;\n\nexport const classNames = (...args: (string | false | null | undefined)[]): string =>\n args.filter(Boolean).join(' ');\n\nexport const delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n","import type { FlowConfig, FlowStep, ChatMessage, FlowQuickReply } from '../types';\r\nimport { uid } from '../utils/helpers';\r\n\r\nexport class FlowEngine {\r\n private steps: Map<string, FlowStep>;\r\n private startStep: string;\r\n private collectedData: Record<string, unknown> = {};\r\n private stepHistory: string[] = [];\r\n\r\n constructor(flow: FlowConfig) {\r\n this.startStep = flow.startStep;\r\n this.steps = new Map(flow.steps.map((s) => [s.id, s]));\r\n }\r\n\r\n getStartStepId(): string {\r\n return this.startStep;\r\n }\r\n\r\n getStep(id: string): FlowStep | undefined {\r\n return this.steps.get(id);\r\n }\r\n\r\n getData(): Record<string, unknown> {\r\n return { ...this.collectedData };\r\n }\r\n\r\n setData(key: string, value: unknown): void {\r\n this.collectedData[key] = value;\r\n }\r\n\r\n mergeData(data: Record<string, unknown>): void {\r\n Object.assign(this.collectedData, data);\r\n }\r\n\r\n /** Push a step onto the history stack (called when entering a step) */\r\n pushHistory(stepId: string): void {\r\n this.stepHistory.push(stepId);\r\n }\r\n\r\n /** Pop and return the previous step (go back) */\r\n popHistory(): string | undefined {\r\n // Remove current step\r\n this.stepHistory.pop();\r\n // Return previous step\r\n return this.stepHistory.pop();\r\n }\r\n\r\n /** Check if there's a previous step to go back to */\r\n canGoBack(): boolean {\r\n return this.stepHistory.length > 1;\r\n }\r\n\r\n /** Reset the engine to initial state */\r\n reset(): void {\r\n this.collectedData = {};\r\n this.stepHistory = [];\r\n }\r\n\r\n resolveNext(step: FlowStep, userValue?: string): string | undefined {\r\n // Conditional branching\r\n if (step.condition) {\r\n const { field, operator, value, then: thenStep, else: elseStep } = step.condition;\r\n const fieldVal = this.collectedData[field];\r\n const match = this.evaluate(fieldVal, operator, value);\r\n return match ? thenStep : elseStep;\r\n }\r\n\r\n // Quick-reply selected → find the reply's next\r\n if (userValue && step.quickReplies) {\r\n const reply = step.quickReplies.find((r) => r.value === userValue);\r\n if (reply?.next) return reply.next;\r\n }\r\n\r\n return step.next;\r\n }\r\n\r\n /** Returns true if the step expects a quick reply (not free text) */\r\n stepExpectsQuickReply(step: FlowStep): boolean {\r\n return !!(step.quickReplies && step.quickReplies.length > 0);\r\n }\r\n\r\n /** Returns true if the step expects a form submission */\r\n stepExpectsForm(step: FlowStep): boolean {\r\n return !!step.form;\r\n }\r\n\r\n /** Try to fuzzy-match user text against quick reply labels */\r\n matchQuickReply(step: FlowStep, text: string): FlowQuickReply | undefined {\r\n if (!step.quickReplies) return undefined;\r\n const lower = text.toLowerCase().trim();\r\n if (!lower) return undefined;\r\n // Exact value match\r\n const exact = step.quickReplies.find((r) => r.value.toLowerCase() === lower);\r\n if (exact) return exact;\r\n // Exact label match \r\n const labelMatch = step.quickReplies.find((r) => r.label.toLowerCase().replace(/[^\\w\\s]/g, '').trim() === lower);\r\n if (labelMatch) return labelMatch;\r\n // Contains match\r\n const contains = step.quickReplies.find((r) => lower.includes(r.value.toLowerCase()) || r.label.toLowerCase().includes(lower));\r\n return contains;\r\n }\r\n\r\n buildMessages(step: FlowStep): ChatMessage[] {\r\n const messages: ChatMessage[] = [];\r\n\r\n const texts = step.messages ?? (step.message ? [step.message] : []);\r\n for (const text of texts) {\r\n messages.push({\r\n id: uid(),\r\n sender: 'bot',\r\n text,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n // Attach quick replies to the last message\r\n if (step.quickReplies && messages.length > 0) {\r\n messages[messages.length - 1]!.quickReplies = step.quickReplies;\r\n }\r\n\r\n // If step has a form, create a form message\r\n if (step.form) {\r\n messages.push({\r\n id: uid(),\r\n sender: 'bot',\r\n timestamp: Date.now(),\r\n form: step.form,\r\n });\r\n }\r\n\r\n // If step has a custom component, create a component message\r\n if (step.component) {\r\n messages.push({\r\n id: uid(),\r\n sender: 'bot',\r\n timestamp: Date.now(),\r\n component: step.component,\r\n });\r\n }\r\n\r\n return messages;\r\n }\r\n\r\n private evaluate(\r\n fieldVal: unknown,\r\n operator: string,\r\n value: string | number,\r\n ): boolean {\r\n switch (operator) {\r\n case 'eq':\r\n return String(fieldVal) === String(value);\r\n case 'neq':\r\n return String(fieldVal) !== String(value);\r\n case 'contains':\r\n return String(fieldVal).includes(String(value));\r\n case 'gt':\r\n return Number(fieldVal) > Number(value);\r\n case 'lt':\r\n return Number(fieldVal) < Number(value);\r\n default:\r\n return false;\r\n }\r\n }\r\n}\r\n\r\nexport function createQuickReplyMessage(\r\n replies: FlowQuickReply[],\r\n): ChatMessage {\r\n return {\r\n id: uid(),\r\n sender: 'bot',\r\n timestamp: Date.now(),\r\n quickReplies: replies,\r\n };\r\n}\r\n","// ─── Live Agent Adapter ──────────────────────────────────────────\r\n// Protocol-agnostic wrapper over WebSocket / Socket.IO\r\n\r\nimport type { ResolvedLiveAgentEvents, LiveAgentConfig, DEFAULT_LIVE_AGENT_EVENTS } from '../types/liveAgent';\r\n\r\nexport type DriverListener = (data: unknown) => void;\r\n\r\n/** Uniform interface implemented by WsDriver and SioDriver */\r\nexport interface LiveAgentDriver {\r\n send(event: string, data: unknown): void;\r\n on(event: string, handler: DriverListener): void;\r\n off(event: string, handler: DriverListener): void;\r\n isConnected(): boolean;\r\n destroy(): void;\r\n}\r\n\r\nexport class LiveAgentAdapter {\r\n private driver: LiveAgentDriver;\r\n readonly events: ResolvedLiveAgentEvents;\r\n readonly sessionId: string;\r\n\r\n constructor(\r\n driver: LiveAgentDriver,\r\n events: ResolvedLiveAgentEvents,\r\n sessionId: string,\r\n ) {\r\n this.driver = driver;\r\n this.events = events;\r\n this.sessionId = sessionId;\r\n }\r\n\r\n /** Send a user message to the server */\r\n sendUserMessage(text: string, attachments?: unknown[]): void {\r\n this.driver.send(this.events.userMessage, {\r\n text,\r\n sessionId: this.sessionId,\r\n timestamp: Date.now(),\r\n ...(attachments?.length ? { attachments } : {}),\r\n });\r\n }\r\n\r\n /** Send user typing indicator */\r\n sendUserTyping(): void {\r\n this.driver.send(this.events.userTyping, {\r\n sessionId: this.sessionId,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n /** Request transfer to a live agent */\r\n requestTransfer(department?: string): void {\r\n this.driver.send(this.events.transferRequest, {\r\n sessionId: this.sessionId,\r\n department,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n /** Initialize or restore a session */\r\n initSession(): void {\r\n this.driver.send(this.events.sessionInit, {\r\n sessionId: this.sessionId,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n /** Subscribe to a server event */\r\n on(event: string, handler: DriverListener): void {\r\n this.driver.on(event, handler);\r\n }\r\n\r\n /** Unsubscribe from a server event */\r\n off(event: string, handler: DriverListener): void {\r\n this.driver.off(event, handler);\r\n }\r\n\r\n isConnected(): boolean {\r\n return this.driver.isConnected();\r\n }\r\n\r\n destroy(): void {\r\n this.driver.destroy();\r\n }\r\n}\r\n","// ─── WebSocket Driver ────────────────────────────────────────────\r\n// Wraps native WebSocket in the LiveAgentDriver interface.\r\n// Messages are JSON-encoded with { event, data } envelope.\r\n\r\nimport type { LiveAgentDriver, DriverListener } from '../LiveAgentAdapter';\r\n\r\nexport class WsDriver implements LiveAgentDriver {\r\n private ws: WebSocket;\r\n private listeners = new Map<string, Set<DriverListener>>();\r\n private boundOnMessage: (e: MessageEvent) => void;\r\n\r\n constructor(ws: WebSocket) {\r\n this.ws = ws;\r\n this.boundOnMessage = this.handleMessage.bind(this);\r\n this.ws.addEventListener('message', this.boundOnMessage);\r\n }\r\n\r\n send(event: string, data: unknown): void {\r\n if (this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify({ event, data }));\r\n }\r\n }\r\n\r\n on(event: string, handler: DriverListener): void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(handler);\r\n }\r\n\r\n off(event: string, handler: DriverListener): void {\r\n this.listeners.get(event)?.delete(handler);\r\n }\r\n\r\n isConnected(): boolean {\r\n return this.ws.readyState === WebSocket.OPEN;\r\n }\r\n\r\n destroy(): void {\r\n this.ws.removeEventListener('message', this.boundOnMessage);\r\n this.listeners.clear();\r\n }\r\n\r\n private handleMessage(e: MessageEvent): void {\r\n try {\r\n const parsed = JSON.parse(String(e.data)) as { event?: string; data?: unknown };\r\n if (parsed.event) {\r\n const handlers = this.listeners.get(parsed.event);\r\n if (handlers) {\r\n handlers.forEach((h) => h(parsed.data));\r\n }\r\n }\r\n } catch {\r\n // Non-JSON message — ignore\r\n }\r\n }\r\n}\r\n","// ─── Socket.IO Driver ────────────────────────────────────────────\r\n// Wraps a Socket.IO client socket in the LiveAgentDriver interface.\r\n\r\nimport type { LiveAgentDriver, DriverListener } from '../LiveAgentAdapter';\r\n\r\n/** Minimal Socket.IO client interface (avoids hard dependency) */\r\ninterface SioSocket {\r\n emit(event: string, ...args: unknown[]): void;\r\n on(event: string, handler: (...args: unknown[]) => void): void;\r\n off(event: string, handler: (...args: unknown[]) => void): void;\r\n connected: boolean;\r\n}\r\n\r\nexport class SioDriver implements LiveAgentDriver {\r\n private socket: SioSocket;\r\n private registeredHandlers: Array<{ event: string; handler: DriverListener }> = [];\r\n\r\n constructor(socket: SioSocket) {\r\n this.socket = socket;\r\n }\r\n\r\n send(event: string, data: unknown): void {\r\n this.socket.emit(event, data);\r\n }\r\n\r\n on(event: string, handler: DriverListener): void {\r\n this.socket.on(event, handler as (...args: unknown[]) => void);\r\n this.registeredHandlers.push({ event, handler });\r\n }\r\n\r\n off(event: string, handler: DriverListener): void {\r\n this.socket.off(event, handler as (...args: unknown[]) => void);\r\n this.registeredHandlers = this.registeredHandlers.filter(\r\n (h) => !(h.event === event && h.handler === handler),\r\n );\r\n }\r\n\r\n isConnected(): boolean {\r\n return this.socket.connected;\r\n }\r\n\r\n destroy(): void {\r\n for (const { event, handler } of this.registeredHandlers) {\r\n this.socket.off(event, handler as (...args: unknown[]) => void);\r\n }\r\n this.registeredHandlers = [];\r\n }\r\n}\r\n","// ─── Live Agent Types ────────────────────────────────────────────\r\n// WebSocket / Socket.IO real-time agent chat support\r\n\r\nexport interface AgentInfo {\r\n name: string;\r\n avatar?: string;\r\n department?: string;\r\n}\r\n\r\nexport interface LiveAgentEvents {\r\n /** Server → client: new message from agent (default: 'agent:message') */\r\n agentMessage?: string;\r\n /** Client → server: user sends message (default: 'user:message') */\r\n userMessage?: string;\r\n /** Server → client: agent joined (default: 'agent:joined') */\r\n agentJoined?: string;\r\n /** Server → client: agent left / disconnected (default: 'agent:left') */\r\n agentLeft?: string;\r\n /** Server → client: agent is typing (default: 'agent:typing') */\r\n agentTyping?: string;\r\n /** Client → server: user is typing (default: 'user:typing') */\r\n userTyping?: string;\r\n /** Client → server: session init / restore (default: 'session:init') */\r\n sessionInit?: string;\r\n /** Server → client: session history restored (default: 'session:history') */\r\n sessionHistory?: string;\r\n /** Client → server: request agent transfer (default: 'transfer:request') */\r\n transferRequest?: string;\r\n /** Server → client: transfer accepted (default: 'transfer:accepted') */\r\n transferAccepted?: string;\r\n /** Server → client: queue position update (default: 'queue:update') */\r\n queueUpdate?: string;\r\n}\r\n\r\nexport interface LiveAgentConfig {\r\n /** Transport type — 'ws' for native WebSocket, 'socketio' for Socket.IO client */\r\n type: 'ws' | 'socketio';\r\n\r\n /** The pre-created instance — a WebSocket or Socket.IO socket */\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n instance: any;\r\n\r\n /** Session ID for persistence across refreshes (auto-generated if omitted) */\r\n sessionId?: string;\r\n\r\n /** Customize server event names */\r\n events?: LiveAgentEvents;\r\n\r\n /** Persist session messages to localStorage across refreshes (default: true) */\r\n persistSession?: boolean;\r\n\r\n /** Storage key prefix (default: 'chatbot_live_') */\r\n storageKey?: string;\r\n\r\n /** Callbacks */\r\n onAgentJoined?: (agent: AgentInfo) => void;\r\n onAgentLeft?: (agent: AgentInfo) => void;\r\n onQueueUpdate?: (position: number, estimatedWait?: number) => void;\r\n onSessionRestored?: (messageCount: number) => void;\r\n onError?: (error: Error) => void;\r\n onConnect?: () => void;\r\n onDisconnect?: () => void;\r\n}\r\n\r\n/** Resolved event names with defaults applied */\r\nexport interface ResolvedLiveAgentEvents {\r\n agentMessage: string;\r\n userMessage: string;\r\n agentJoined: string;\r\n agentLeft: string;\r\n agentTyping: string;\r\n userTyping: string;\r\n sessionInit: string;\r\n sessionHistory: string;\r\n transferRequest: string;\r\n transferAccepted: string;\r\n queueUpdate: string;\r\n}\r\n\r\nexport const DEFAULT_LIVE_AGENT_EVENTS: ResolvedLiveAgentEvents = {\r\n agentMessage: 'agent:message',\r\n userMessage: 'user:message',\r\n agentJoined: 'agent:joined',\r\n agentLeft: 'agent:left',\r\n agentTyping: 'agent:typing',\r\n userTyping: 'user:typing',\r\n sessionInit: 'session:init',\r\n sessionHistory: 'session:history',\r\n transferRequest: 'transfer:request',\r\n transferAccepted: 'transfer:accepted',\r\n queueUpdate: 'queue:update',\r\n};\r\n","// ─── useLiveAgent Hook ───────────────────────────────────────────\r\n// Initializes the LiveAgentAdapter, listens for server events,\r\n// dispatches messages, and handles session persistence.\r\n\r\nimport { useEffect, useRef, useCallback } from 'react';\r\nimport { LiveAgentAdapter } from '../core/LiveAgentAdapter';\r\nimport { WsDriver } from '../core/drivers/WsDriver';\r\nimport { SioDriver } from '../core/drivers/SioDriver';\r\nimport { DEFAULT_LIVE_AGENT_EVENTS } from '../types/liveAgent';\r\nimport type { LiveAgentConfig, ResolvedLiveAgentEvents, AgentInfo } from '../types/liveAgent';\r\nimport type { ChatMessage } from '../types/message';\r\nimport type { ChatAction } from '../context/ChatContext';\r\nimport { uid } from '../utils/helpers';\r\n\r\ninterface UseLiveAgentOptions {\r\n config: LiveAgentConfig | undefined;\r\n dispatch: React.Dispatch<ChatAction>;\r\n messages: ChatMessage[];\r\n}\r\n\r\nfunction resolveEvents(custom?: LiveAgentConfig['events']): ResolvedLiveAgentEvents {\r\n return { ...DEFAULT_LIVE_AGENT_EVENTS, ...custom };\r\n}\r\n\r\nfunction resolveSessionId(config: LiveAgentConfig): string {\r\n if (config.sessionId) return config.sessionId;\r\n const storageKey = (config.storageKey ?? 'chatbot_live_') + 'session_id';\r\n if (typeof localStorage !== 'undefined') {\r\n const existing = localStorage.getItem(storageKey);\r\n if (existing) return existing;\r\n const id = uid();\r\n localStorage.setItem(storageKey, id);\r\n return id;\r\n }\r\n return uid();\r\n}\r\n\r\nexport function useLiveAgent({ config, dispatch, messages }: UseLiveAgentOptions) {\r\n const adapterRef = useRef<LiveAgentAdapter | null>(null);\r\n const isLiveRef = useRef(false);\r\n const agentInfoRef = useRef<AgentInfo | null>(null);\r\n const typingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const messagesRef = useRef(messages);\r\n messagesRef.current = messages;\r\n\r\n // ── Build adapter on mount ──────────────────────────────────\r\n useEffect(() => {\r\n if (!config) return;\r\n\r\n const events = resolveEvents(config.events);\r\n const sessionId = resolveSessionId(config);\r\n\r\n const driver =\r\n config.type === 'socketio'\r\n ? new SioDriver(config.instance)\r\n : new WsDriver(config.instance);\r\n\r\n const adapter = new LiveAgentAdapter(driver, events, sessionId);\r\n adapterRef.current = adapter;\r\n\r\n // ── Listen: agent message ───────────────────────────────\r\n const onAgentMessage = (data: unknown) => {\r\n const d = data as { text?: string; agent?: AgentInfo; attachments?: ChatMessage['attachments'] };\r\n if (!d.text && !d.attachments?.length) return;\r\n\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'agent' as ChatMessage['sender'],\r\n text: d.text,\r\n timestamp: Date.now(),\r\n ...(d.attachments?.length ? { attachments: d.attachments } : {}),\r\n metadata: d.agent ? { agentName: d.agent.name, agentAvatar: d.agent.avatar } : undefined,\r\n };\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n dispatch({ type: 'ADD_MESSAGE', payload: msg });\r\n persistMessages([...messagesRef.current, msg], config);\r\n };\r\n\r\n // ── Listen: agent joined ────────────────────────────────\r\n const onAgentJoined = (data: unknown) => {\r\n const d = data as AgentInfo;\r\n agentInfoRef.current = d;\r\n isLiveRef.current = true;\r\n dispatch({ type: 'SET_LIVE_AGENT', payload: true } as ChatAction);\r\n dispatch({ type: 'SET_AGENT_INFO', payload: d } as ChatAction);\r\n\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: `${d.name} joined the chat`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n config.onAgentJoined?.(d);\r\n };\r\n\r\n // ── Listen: agent left ──────────────────────────────────\r\n const onAgentLeft = (data: unknown) => {\r\n const d = data as AgentInfo;\r\n agentInfoRef.current = null;\r\n isLiveRef.current = false;\r\n dispatch({ type: 'SET_LIVE_AGENT', payload: false } as ChatAction);\r\n dispatch({ type: 'SET_AGENT_INFO', payload: null } as ChatAction);\r\n\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: `${d.name} left the chat`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n config.onAgentLeft?.(d);\r\n };\r\n\r\n // ── Listen: agent typing ────────────────────────────────\r\n const onAgentTyping = () => {\r\n dispatch({ type: 'SET_TYPING', payload: true });\r\n // Auto-clear after 3s if no new typing event\r\n if (typingTimerRef.current) clearTimeout(typingTimerRef.current);\r\n typingTimerRef.current = setTimeout(() => {\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n }, 3000);\r\n };\r\n\r\n // ── Listen: transfer accepted ───────────────────────────\r\n const onTransferAccepted = (data: unknown) => {\r\n const d = data as { agent?: AgentInfo; message?: string };\r\n if (d.agent) {\r\n agentInfoRef.current = d.agent;\r\n isLiveRef.current = true;\r\n dispatch({ type: 'SET_LIVE_AGENT', payload: true } as ChatAction);\r\n dispatch({ type: 'SET_AGENT_INFO', payload: d.agent } as ChatAction);\r\n }\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: d.message ?? `Connected to ${d.agent?.name ?? 'an agent'}`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n };\r\n\r\n // ── Listen: queue update ────────────────────────────────\r\n const onQueueUpdate = (data: unknown) => {\r\n const d = data as { position: number; estimatedWait?: number };\r\n const waitText = d.estimatedWait ? ` Estimated wait: ~${d.estimatedWait} min.` : '';\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: `You are #${d.position} in queue.${waitText}`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n config.onQueueUpdate?.(d.position, d.estimatedWait);\r\n };\r\n\r\n // ── Listen: session history ─────────────────────────────\r\n const onSessionHistory = (data: unknown) => {\r\n const d = data as { messages?: ChatMessage[] };\r\n if (d.messages?.length) {\r\n dispatch({ type: 'ADD_MESSAGES', payload: d.messages });\r\n config.onSessionRestored?.(d.messages.length);\r\n }\r\n };\r\n\r\n // Register all listeners\r\n adapter.on(events.agentMessage, onAgentMessage);\r\n adapter.on(events.agentJoined, onAgentJoined);\r\n adapter.on(events.agentLeft, onAgentLeft);\r\n adapter.on(events.agentTyping, onAgentTyping);\r\n adapter.on(events.transferAccepted, onTransferAccepted);\r\n adapter.on(events.queueUpdate, onQueueUpdate);\r\n adapter.on(events.sessionHistory, onSessionHistory);\r\n\r\n // Restore from local storage if available\r\n if (config.persistSession !== false) {\r\n const stored = loadPersistedMessages(config);\r\n if (stored.length) {\r\n dispatch({ type: 'ADD_MESSAGES', payload: stored });\r\n config.onSessionRestored?.(stored.length);\r\n }\r\n }\r\n\r\n // Init session with server\r\n adapter.initSession();\r\n config.onConnect?.();\r\n\r\n return () => {\r\n adapter.off(events.agentMessage, onAgentMessage);\r\n adapter.off(events.agentJoined, onAgentJoined);\r\n adapter.off(events.agentLeft, onAgentLeft);\r\n adapter.off(events.agentTyping, onAgentTyping);\r\n adapter.off(events.transferAccepted, onTransferAccepted);\r\n adapter.off(events.queueUpdate, onQueueUpdate);\r\n adapter.off(events.sessionHistory, onSessionHistory);\r\n if (typingTimerRef.current) clearTimeout(typingTimerRef.current);\r\n adapter.destroy();\r\n adapterRef.current = null;\r\n config.onDisconnect?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [config, dispatch]);\r\n\r\n // ── Public: send message via adapter ────────────────────────\r\n const sendLiveMessage = useCallback(\r\n (text: string, attachments?: unknown[]) => {\r\n adapterRef.current?.sendUserMessage(text, attachments);\r\n },\r\n [],\r\n );\r\n\r\n // ── Public: send typing indicator (throttled) ──────────────\r\n const sendTyping = useCallback(() => {\r\n adapterRef.current?.sendUserTyping();\r\n }, []);\r\n\r\n // ── Public: request transfer ───────────────────────────────\r\n const requestTransfer = useCallback(\r\n (department?: string) => {\r\n adapterRef.current?.requestTransfer(department);\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: department\r\n ? `Requesting transfer to ${department}…`\r\n : 'Requesting a live agent…',\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n },\r\n [dispatch],\r\n );\r\n\r\n return {\r\n adapter: adapterRef,\r\n sendLiveMessage,\r\n sendTyping,\r\n requestTransfer,\r\n isLive: isLiveRef,\r\n agentInfo: agentInfoRef,\r\n };\r\n}\r\n\r\n// ─── Local Storage Persistence Helpers ───────────────────────────\r\n\r\nfunction storageKey(config: LiveAgentConfig): string {\r\n const prefix = config.storageKey ?? 'chatbot_live_';\r\n const sid = config.sessionId ?? 'default';\r\n return `${prefix}${sid}`;\r\n}\r\n\r\nfunction persistMessages(messages: ChatMessage[], config: LiveAgentConfig): void {\r\n if (config.persistSession === false) return;\r\n if (typeof localStorage === 'undefined') return;\r\n try {\r\n localStorage.setItem(storageKey(config), JSON.stringify(messages));\r\n } catch { /* quota exceeded — silent */ }\r\n}\r\n\r\nfunction loadPersistedMessages(config: LiveAgentConfig): ChatMessage[] {\r\n if (typeof localStorage === 'undefined') return [];\r\n try {\r\n const raw = localStorage.getItem(storageKey(config));\r\n if (!raw) return [];\r\n const parsed = JSON.parse(raw);\r\n return Array.isArray(parsed) ? parsed : [];\r\n } catch {\r\n return [];\r\n }\r\n}\r\n","import { useCallback, useRef, useEffect } from 'react';\r\nimport { useChatContext } from '../context/ChatContext';\r\nimport { FlowEngine } from '../engine/FlowEngine';\r\nimport { uid, delay } from '../utils/helpers';\r\nimport type { ChatMessage } from '../types';\r\nimport type { FlowActionResult, ActionContext, KeywordRoute } from '../types/config';\r\nimport type { FlowStepInput } from '../types/flow';\r\nimport { useLiveAgent } from './useLiveAgent';\r\n\r\n/** Slash commands the user can type */\r\nconst COMMANDS: Record<string, string> = {\r\n '/help': 'Show available commands',\r\n '/cancel': 'Cancel current step and go back',\r\n '/back': 'Go back to the previous step',\r\n '/restart': 'Restart the conversation from the beginning',\r\n};\r\n\r\n/** Common greeting words for auto-detection */\r\nconst GREETING_PATTERNS = ['hi', 'hello', 'hey', 'howdy', 'hola', 'greetings', 'good morning', 'good afternoon', 'good evening', 'sup', 'yo', 'hii', 'hiii'];\r\n\r\n/** Match user text against a single keyword route */\r\nfunction matchesRoute(text: string, route: KeywordRoute): boolean {\r\n const compare = route.caseSensitive ? text : text.toLowerCase();\r\n for (const pattern of route.patterns) {\r\n const pat = route.caseSensitive ? pattern : pattern.toLowerCase();\r\n switch (route.matchType ?? 'contains') {\r\n case 'exact':\r\n if (compare === pat) return true;\r\n break;\r\n case 'startsWith':\r\n if (compare.startsWith(pat)) return true;\r\n break;\r\n case 'regex':\r\n try {\r\n if (new RegExp(pat, route.caseSensitive ? '' : 'i').test(text)) return true;\r\n } catch { /* invalid regex — skip */ }\r\n break;\r\n case 'contains':\r\n default:\r\n if (compare.includes(pat)) return true;\r\n break;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\n/** Find the best matching keyword route (highest priority) */\r\nfunction findKeywordMatch(text: string, keywords: KeywordRoute[]): KeywordRoute | undefined {\r\n const sorted = [...keywords].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\r\n return sorted.find((r) => matchesRoute(text, r));\r\n}\r\n\r\n/** Validate and transform user text for an input step */\r\nfunction validateInput(text: string, input: FlowStepInput): { valid: boolean; value: string; error?: string } {\r\n let value = text;\r\n if (input.transform) {\r\n switch (input.transform) {\r\n case 'trim': value = value.trim(); break;\r\n case 'lowercase': value = value.toLowerCase(); break;\r\n case 'uppercase': value = value.toUpperCase(); break;\r\n case 'email': value = value.trim().toLowerCase(); break;\r\n }\r\n }\r\n if (input.validation) {\r\n const v = input.validation;\r\n if (v.required && !value.trim()) return { valid: false, value, error: v.message ?? 'This field is required.' };\r\n if (v.minLength && value.length < v.minLength) return { valid: false, value, error: v.message ?? `Must be at least ${v.minLength} characters.` };\r\n if (v.maxLength && value.length > v.maxLength) return { valid: false, value, error: v.message ?? `Must be at most ${v.maxLength} characters.` };\r\n if (v.pattern) {\r\n try {\r\n if (!new RegExp(v.pattern).test(value)) return { valid: false, value, error: v.message ?? 'Invalid format.' };\r\n } catch { /* invalid pattern */ }\r\n }\r\n }\r\n return { valid: true, value };\r\n}\r\n\r\nexport function useChat() {\r\n const { state, dispatch, props, pluginManager } = useChatContext();\r\n const flowRef = useRef<FlowEngine | null>(null);\r\n const flowStartedRef = useRef(false);\r\n\r\n // Keep fresh references for use inside async callbacks (avoids stale closures)\r\n const stateRef = useRef(state);\r\n stateRef.current = state;\r\n const propsRef = useRef(props);\r\n propsRef.current = props;\r\n\r\n // Live agent hook\r\n const { sendLiveMessage, sendTyping, requestTransfer } = useLiveAgent({\r\n config: props.liveAgent,\r\n dispatch,\r\n messages: state.messages,\r\n });\r\n\r\n // Initialize flow engine\r\n useEffect(() => {\r\n if (props.flow) {\r\n flowRef.current = new FlowEngine(props.flow);\r\n flowStartedRef.current = false;\r\n }\r\n }, [props.flow]);\r\n\r\n const addBotMessage = useCallback(\r\n async (text: string, extras?: Partial<ChatMessage>) => {\r\n dispatch({ type: 'SET_TYPING', payload: true });\r\n await delay(400);\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'bot',\r\n text,\r\n timestamp: Date.now(),\r\n ...extras,\r\n };\r\n // Let plugins see bot messages too\r\n const finalMsg = pluginManager ? await pluginManager.onMessage(msg) : msg;\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n dispatch({ type: 'ADD_MESSAGE', payload: finalMsg });\r\n propsRef.current.callbacks?.onMessageReceive?.(finalMsg);\r\n },\r\n [dispatch, pluginManager],\r\n );\r\n\r\n const addSystemMessage = useCallback(\r\n (text: string) => {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'system', text, timestamp: Date.now() },\r\n });\r\n },\r\n [dispatch],\r\n );\r\n\r\n // Use a ref so processFlowStep can call itself recursively without stale closure\r\n const processFlowStepRef = useRef<(stepId: string) => Promise<void>>(async () => {});\r\n processFlowStepRef.current = async (stepId: string) => {\r\n const engine = flowRef.current;\r\n if (!engine) return;\r\n\r\n const step = engine.getStep(stepId);\r\n if (!step) return;\r\n\r\n // Track step history for /back navigation\r\n engine.pushHistory(stepId);\r\n\r\n dispatch({ type: 'SET_STEP', payload: stepId });\r\n pluginManager?.emitEvent('stepChange', { stepId });\r\n dispatch({ type: 'SET_TYPING', payload: true });\r\n await delay(step.delay ?? 500);\r\n\r\n const messages = engine.buildMessages(step);\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n dispatch({ type: 'ADD_MESSAGES', payload: messages });\r\n\r\n messages.forEach((m) => propsRef.current.callbacks?.onMessageReceive?.(m));\r\n\r\n // Handle async action (API calls, verification, etc.)\r\n if (step.asyncAction) {\r\n const handler = propsRef.current.actionHandlers?.[step.asyncAction.handler];\r\n if (handler) {\r\n const statusMsgId = uid();\r\n // Show loading/status message\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: {\r\n id: statusMsgId,\r\n sender: 'bot',\r\n text: step.asyncAction.loadingMessage ?? 'Processing...',\r\n timestamp: Date.now(),\r\n },\r\n });\r\n\r\n const ctx: ActionContext = {\r\n updateMessage: (text: string) => {\r\n dispatch({ type: 'UPDATE_MESSAGE', payload: { id: statusMsgId, updates: { text } } });\r\n },\r\n };\r\n\r\n try {\r\n const result = await handler(engine.getData(), ctx);\r\n\r\n // Merge result data into collected data\r\n if (result.data) {\r\n engine.mergeData(result.data);\r\n dispatch({ type: 'SET_DATA', payload: result.data });\r\n }\r\n\r\n // Update status message with final text\r\n const finalMsg =\r\n result.message ??\r\n (result.status === 'success'\r\n ? (step.asyncAction.successMessage ?? 'Done!')\r\n : (step.asyncAction.errorMessage ?? 'Something went wrong.'));\r\n dispatch({ type: 'UPDATE_MESSAGE', payload: { id: statusMsgId, updates: { text: finalMsg } } });\r\n\r\n // Route based on result\r\n const nextStepId = resolveAsyncRoute(step, result);\r\n if (nextStepId) {\r\n await delay(600);\r\n processFlowStepRef.current(nextStepId);\r\n }\r\n } catch {\r\n dispatch({\r\n type: 'UPDATE_MESSAGE',\r\n payload: { id: statusMsgId, updates: { text: step.asyncAction.errorMessage ?? '❌ Something went wrong.' } },\r\n });\r\n if (step.asyncAction.onError) {\r\n await delay(600);\r\n processFlowStepRef.current(step.asyncAction.onError);\r\n }\r\n }\r\n return; // async action handles routing — don't auto-advance\r\n }\r\n }\r\n\r\n // If step has a custom component, wait for onComplete — don't auto-advance\r\n if (step.component && propsRef.current.components?.[step.component]) {\r\n return;\r\n }\r\n\r\n // Auto-advance if no user input required\r\n if (!step.quickReplies && !step.form && !step.input && step.next) {\r\n await delay(300);\r\n processFlowStepRef.current(step.next);\r\n }\r\n };\r\n\r\n /** Determine next step from async action result */\r\n function resolveAsyncRoute(\r\n step: { asyncAction?: { onSuccess?: string; onError?: string; routes?: Record<string, string> }; next?: string },\r\n result: FlowActionResult,\r\n ): string | undefined {\r\n // 1. Explicit next from result\r\n if (result.next) return result.next;\r\n // 2. Routes map\r\n if (step.asyncAction?.routes?.[result.status]) return step.asyncAction.routes[result.status];\r\n // 3. Success/error defaults\r\n if (result.status === 'success' && step.asyncAction?.onSuccess) return step.asyncAction.onSuccess;\r\n if (result.status === 'error' && step.asyncAction?.onError) return step.asyncAction.onError;\r\n // 4. Fallback\r\n return step.next;\r\n }\r\n\r\n const processFlowStep = useCallback(\r\n (stepId: string) => processFlowStepRef.current(stepId),\r\n [],\r\n );\r\n\r\n /** Go back to the previous step */\r\n const goBack = useCallback(() => {\r\n const engine = flowRef.current;\r\n if (!engine || !engine.canGoBack()) {\r\n addSystemMessage('There is no previous step to go back to.');\r\n return;\r\n }\r\n dispatch({ type: 'CLEAR_QUICK_REPLIES' });\r\n const prevStepId = engine.popHistory();\r\n if (prevStepId) {\r\n processFlowStep(prevStepId);\r\n } else {\r\n addSystemMessage('There is no previous step to go back to.');\r\n }\r\n }, [dispatch, processFlowStep, addSystemMessage]);\r\n\r\n /** Restart the entire conversation */\r\n const restartSession = useCallback(() => {\r\n const engine = flowRef.current;\r\n if (engine) {\r\n engine.reset();\r\n }\r\n flowStartedRef.current = false;\r\n dispatch({ type: 'RESET_CHAT' });\r\n // Re-start the flow after reset\r\n if (engine) {\r\n flowStartedRef.current = true;\r\n processFlowStep(engine.getStartStepId());\r\n }\r\n }, [dispatch, processFlowStep]);\r\n\r\n /** Handle slash commands. Returns true if the text was a command. */\r\n const handleCommandRef = useRef<(text: string) => boolean>(() => false);\r\n handleCommandRef.current = (text: string): boolean => {\r\n const cmd = text.trim().toLowerCase();\r\n if (!cmd.startsWith('/')) return false;\r\n\r\n switch (cmd) {\r\n case '/help': {\r\n const lines = Object.entries(COMMANDS)\r\n .map(([k, v]) => `**${k}** — ${v}`)\r\n .join('\\n');\r\n addSystemMessage(`Available commands:\\n${lines}`);\r\n return true;\r\n }\r\n case '/cancel':\r\n case '/back': {\r\n goBack();\r\n return true;\r\n }\r\n case '/restart': {\r\n restartSession();\r\n return true;\r\n }\r\n default:\r\n addSystemMessage(`Unknown command: ${cmd}. Type /help for available commands.`);\r\n return true;\r\n }\r\n };\r\n\r\n /** Handle completion from a custom component rendered in a step */\r\n const handleComponentComplete = useCallback(\r\n (result?: FlowActionResult) => {\r\n const engine = flowRef.current;\r\n const currentStepId = stateRef.current.currentStepId;\r\n if (!engine || !currentStepId) return;\r\n\r\n const step = engine.getStep(currentStepId);\r\n if (!step) return;\r\n\r\n // Merge result data\r\n if (result?.data) {\r\n engine.mergeData(result.data);\r\n dispatch({ type: 'SET_DATA', payload: result.data });\r\n }\r\n\r\n // Show optional message\r\n if (result?.message) {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'bot', text: result.message, timestamp: Date.now() },\r\n });\r\n }\r\n\r\n // Determine next step\r\n const nextStepId = result?.next ?? step.next;\r\n if (nextStepId) {\r\n processFlowStep(nextStepId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', engine.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(engine.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n },\r\n [dispatch, processFlowStep, pluginManager],\r\n );\r\n\r\n const sendMessage = useCallback(\r\n async (text: string) => {\r\n // Check for slash commands first\r\n if (handleCommandRef.current(text)) return;\r\n\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'user',\r\n text,\r\n timestamp: Date.now(),\r\n };\r\n\r\n // Let plugins transform the message before dispatching\r\n const finalMsg = pluginManager ? await pluginManager.onMessage(msg) : msg;\r\n dispatch({ type: 'ADD_MESSAGE', payload: finalMsg });\r\n propsRef.current.callbacks?.onMessageSend?.(finalMsg);\r\n propsRef.current.callbacks?.onSubmit?.({ message: finalMsg.text });\r\n\r\n // ── Live agent mode — forward to server ──────────────────\r\n if (stateRef.current.isLiveAgent) {\r\n sendLiveMessage(finalMsg.text ?? '');\r\n return;\r\n }\r\n\r\n const currentStepId = stateRef.current.currentStepId;\r\n const typingMs = propsRef.current.typingDelay ?? 0;\r\n\r\n // ── Active flow step ─────────────────────────────────────\r\n if (flowRef.current && currentStepId) {\r\n const step = flowRef.current.getStep(currentStepId);\r\n if (step) {\r\n // Block text input during async action or component steps\r\n if (step.asyncAction || step.component) {\r\n addBotMessage(\"Please wait, I'm still processing. You can type /back to go back.\");\r\n return;\r\n }\r\n // If this step has quick replies, try to match user text\r\n if (flowRef.current.stepExpectsQuickReply(step)) {\r\n const matched = flowRef.current.matchQuickReply(step, text);\r\n if (matched) {\r\n dispatch({ type: 'CLEAR_QUICK_REPLIES' });\r\n flowRef.current.setData(step.id, matched.value);\r\n const nextId = flowRef.current.resolveNext(step, matched.value);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n } else {\r\n addBotMessage(\r\n \"I didn't quite get that. Please choose one of the options below:\",\r\n { quickReplies: step.quickReplies },\r\n );\r\n }\r\n } else if (flowRef.current.stepExpectsForm(step)) {\r\n addBotMessage(\"Please fill out the form above to continue.\");\r\n } else if (step.input) {\r\n // ── Input step with validation ──\r\n const result = validateInput(text, step.input);\r\n if (!result.valid) {\r\n addBotMessage(result.error ?? 'Invalid input. Please try again.');\r\n return;\r\n }\r\n flowRef.current.setData(step.id, result.value);\r\n const nextId = flowRef.current.resolveNext(step, result.value);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n addBotMessage(\"Thanks for your message! Our team will get back to you soon. \\u{1F64C}\");\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n } else {\r\n // Normal text input step\r\n flowRef.current.setData(step.id, text);\r\n const nextId = flowRef.current.resolveNext(step, text);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n addBotMessage(\"Thanks for your message! Our team will get back to you soon. \\u{1F64C}\");\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // ── No active flow step — try keyword / greeting / fallback ──\r\n\r\n // Build runtime keyword list (user keywords + greeting shortcut)\r\n const keywords: KeywordRoute[] = [...(propsRef.current.keywords ?? [])];\r\n if (propsRef.current.greetingResponse) {\r\n keywords.push({\r\n patterns: GREETING_PATTERNS,\r\n response: propsRef.current.greetingResponse,\r\n matchType: 'exact',\r\n priority: -1,\r\n });\r\n }\r\n\r\n if (keywords.length > 0) {\r\n const match = findKeywordMatch(text.trim(), keywords);\r\n if (match) {\r\n // Jump to flow step if route specifies `next`\r\n if (match.next && flowRef.current) {\r\n if (typingMs > 0) await delay(typingMs);\r\n processFlowStep(match.next);\r\n return;\r\n }\r\n // Send response message\r\n if (match.response) {\r\n if (typingMs > 0) {\r\n await delay(typingMs);\r\n }\r\n await addBotMessage(match.response);\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Fallback\r\n const fb = propsRef.current.fallbackMessage;\r\n if (fb) {\r\n const fbText = typeof fb === 'function' ? fb(text) : fb;\r\n if (fbText) {\r\n if (typingMs > 0) {\r\n await delay(typingMs);\r\n }\r\n await addBotMessage(fbText);\r\n return;\r\n }\r\n }\r\n\r\n // Nothing handled it — fire unhandled callback\r\n propsRef.current.callbacks?.onUnhandledMessage?.(text, { currentStepId });\r\n },\r\n [dispatch, addBotMessage, processFlowStep, pluginManager],\r\n );\r\n\r\n const startFlow = useCallback(() => {\r\n const engine = flowRef.current;\r\n if (!engine || flowStartedRef.current) return;\r\n flowStartedRef.current = true;\r\n processFlowStep(engine.getStartStepId());\r\n }, [processFlowStep]);\r\n\r\n // Auto-start flow when all conditions are met\r\n useEffect(() => {\r\n if (\r\n props.flow &&\r\n !state.showWelcome &&\r\n state.isLoggedIn &&\r\n !flowStartedRef.current\r\n ) {\r\n startFlow();\r\n }\r\n }, [props.flow, state.showWelcome, state.isLoggedIn, startFlow]);\r\n\r\n const handleQuickReply = useCallback(\r\n async (value: string, label: string) => {\r\n dispatch({ type: 'CLEAR_QUICK_REPLIES' });\r\n // Add user message\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'user',\r\n text: label,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: msg });\r\n await pluginManager?.onMessage(msg);\r\n pluginManager?.emitEvent('quickReply', { value, label });\r\n propsRef.current.callbacks?.onQuickReply?.(value, label);\r\n\r\n // Continue flow\r\n const currentStepId = stateRef.current.currentStepId;\r\n if (flowRef.current && currentStepId) {\r\n const step = flowRef.current.getStep(currentStepId);\r\n if (step) {\r\n flowRef.current.setData(step.id, value);\r\n const nextId = flowRef.current.resolveNext(step, value);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n }\r\n }\r\n },\r\n [dispatch, processFlowStep, pluginManager],\r\n );\r\n\r\n const handleFormSubmit = useCallback(\r\n async (formId: string, data: Record<string, unknown>) => {\r\n dispatch({ type: 'SET_DATA', payload: data });\r\n if (flowRef.current) {\r\n flowRef.current.mergeData(data);\r\n }\r\n\r\n // Look up form config for friendly labels\r\n const formConfig =\r\n propsRef.current.loginForm?.id === formId\r\n ? propsRef.current.loginForm\r\n : flowRef.current && stateRef.current.currentStepId\r\n ? flowRef.current.getStep(stateRef.current.currentStepId)?.form\r\n : undefined;\r\n\r\n // Build field name → { label, optionMap } lookup\r\n const fieldMeta = new Map<string, { label: string; optionMap?: Map<string, string> }>();\r\n if (formConfig) {\r\n for (const f of formConfig.fields) {\r\n const optionMap = f.options\r\n ? new Map(f.options.map((o) => [o.value, o.label]))\r\n : undefined;\r\n fieldMeta.set(f.name, { label: f.label ?? f.name, optionMap });\r\n }\r\n }\r\n\r\n // Summary message with friendly labels\r\n const summaryLines = Object.entries(data)\r\n .filter(([, v]) => v !== undefined && v !== '')\r\n .map(([k, v]) => {\r\n const meta = fieldMeta.get(k);\r\n const displayKey = meta?.label ?? k;\r\n const raw = String(v);\r\n const displayVal = meta?.optionMap?.get(raw) ?? raw;\r\n return `${displayKey}: ${displayVal}`;\r\n })\r\n .join('\\n');\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'user',\r\n text: summaryLines,\r\n formData: data,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: msg });\r\n await pluginManager?.onMessage(msg);\r\n await pluginManager?.onSubmit(data);\r\n\r\n await propsRef.current.callbacks?.onFormSubmit?.(formId, data);\r\n\r\n // Advance flow\r\n const currentStepId = stateRef.current.currentStepId;\r\n if (flowRef.current && currentStepId) {\r\n const step = flowRef.current.getStep(currentStepId);\r\n if (step) {\r\n const nextId = flowRef.current.resolveNext(step);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n }\r\n }\r\n },\r\n [dispatch, processFlowStep, pluginManager],\r\n );\r\n\r\n const handleLogin = useCallback(\r\n async (data: Record<string, unknown>) => {\r\n await pluginManager?.onSubmit(data);\r\n pluginManager?.emitEvent('login', data);\r\n await propsRef.current.callbacks?.onLogin?.(data);\r\n dispatch({ type: 'SET_LOGGED_IN', payload: true });\r\n },\r\n [dispatch, pluginManager],\r\n );\r\n\r\n const toggleChat = useCallback(() => {\r\n const willOpen = !stateRef.current.isOpen;\r\n dispatch({ type: 'TOGGLE_OPEN' });\r\n if (willOpen) {\r\n pluginManager?.emitEvent('open');\r\n propsRef.current.callbacks?.onOpen?.();\r\n } else {\r\n pluginManager?.emitEvent('close');\r\n propsRef.current.callbacks?.onClose?.();\r\n }\r\n }, [dispatch, pluginManager]);\r\n\r\n const dismissWelcome = useCallback(() => {\r\n dispatch({ type: 'DISMISS_WELCOME' });\r\n }, [dispatch]);\r\n\r\n return {\r\n state,\r\n sendMessage,\r\n addBotMessage,\r\n handleQuickReply,\r\n handleFormSubmit,\r\n handleLogin,\r\n toggleChat,\r\n dismissWelcome,\r\n startFlow,\r\n processFlowStep,\r\n goBack,\r\n restartSession,\r\n handleComponentComplete,\r\n requestTransfer,\r\n sendTyping,\r\n };\r\n}\r\n","import React, { useCallback } from 'react';\r\nimport type { CSSProperties } from 'react';\r\nimport type { ChatStyles } from '../styles/theme';\r\nimport { ChatHeader } from './ChatHeader';\r\nimport { WelcomeScreen } from './WelcomeScreen';\r\nimport { LoginScreen } from './LoginScreen';\r\nimport { MessageList } from './MessageList';\r\nimport { ChatInput } from './ChatInput';\r\nimport { Branding } from './Branding';\r\nimport { useChat } from '../hooks/useChat';\r\nimport { useChatContext } from '../context/ChatContext';\r\nimport { resolveTheme } from '../styles/theme';\r\nimport { uid } from '../utils/helpers';\r\nimport type { MessageAttachment } from '../types/message';\r\n\r\ninterface ChatWindowProps {\r\n styles: ChatStyles;\r\n position: 'bottom-right' | 'bottom-left';\r\n zIndex?: number;\r\n hidden?: boolean;\r\n}\r\n\r\nexport const ChatWindow: React.FC<ChatWindowProps> = ({ styles, position, zIndex, hidden }) => {\r\n const { props, dispatch } = useChatContext();\r\n const theme = resolveTheme(props.theme);\r\n const isDark = theme.mode === 'dark';\r\n const {\r\n state,\r\n sendMessage,\r\n handleQuickReply,\r\n handleFormSubmit,\r\n handleLogin,\r\n toggleChat,\r\n dismissWelcome,\r\n restartSession,\r\n handleComponentComplete,\r\n } = useChat();\r\n\r\n const posStyle: CSSProperties =\r\n position === 'bottom-left'\r\n ? { bottom: '96px', left: '24px' }\r\n : { bottom: '96px', right: '24px' };\r\n\r\n const handleSendWithFiles = useCallback(\r\n (text: string, files?: File[]) => {\r\n if (files && files.length > 0) {\r\n const attachments: MessageAttachment[] = files.map((f) => ({\r\n name: f.name,\r\n url: URL.createObjectURL(f),\r\n type: f.type,\r\n size: f.size,\r\n }));\r\n if (text) {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: {\r\n id: uid(),\r\n sender: 'user',\r\n text,\r\n timestamp: Date.now(),\r\n attachments,\r\n },\r\n });\r\n sendMessage(text);\r\n } else {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: {\r\n id: uid(),\r\n sender: 'user',\r\n timestamp: Date.now(),\r\n attachments,\r\n },\r\n });\r\n }\r\n props.callbacks?.onFileUpload?.(files);\r\n } else if (text) {\r\n sendMessage(text);\r\n }\r\n },\r\n [sendMessage, dispatch, props.callbacks],\r\n );\r\n\r\n // Resolve configs from customizeChat slots\r\n const headerCfg = props.customizeChat?.header?.config ?? { title: 'Chat with us' };\r\n const brandingCfg = props.customizeChat?.branding?.config;\r\n const welcomeContent = props.customizeChat?.welcomeScreen?.content;\r\n\r\n // Default header element\r\n const defaultHeader = (\r\n <ChatHeader\r\n config={headerCfg}\r\n styles={styles}\r\n onClose={toggleChat}\r\n onRestart={restartSession}\r\n logo={brandingCfg?.logo}\r\n logoWidth={brandingCfg?.logoWidth}\r\n />\r\n );\r\n\r\n // Default input element\r\n const defaultInput = (\r\n <ChatInput\r\n onSend={handleSendWithFiles}\r\n placeholder={props.inputPlaceholder}\r\n primaryColor={theme.primaryColor}\r\n isDark={isDark}\r\n enableEmoji={props.enableEmoji}\r\n fileUpload={props.fileUpload}\r\n onFileUpload={props.callbacks?.onFileUpload}\r\n />\r\n );\r\n\r\n if (hidden) {\r\n // Keep component mounted (hooks alive) but invisible\r\n return <div style={{ display: 'none' }} />;\r\n }\r\n\r\n return (\r\n <div\r\n style={{\r\n ...styles.window,\r\n ...posStyle,\r\n ...(zIndex != null ? { zIndex } : {}),\r\n }}\r\n >\r\n {/* Header */}\r\n {props.customizeChat?.header?.component ?? defaultHeader}\r\n\r\n {/* Welcome Screen */}\r\n {state.showWelcome && welcomeContent ? (\r\n props.customizeChat?.welcomeScreen?.component\r\n ?? <WelcomeScreen\r\n content={welcomeContent}\r\n onDismiss={dismissWelcome}\r\n primaryColor={theme.primaryColor}\r\n />\r\n ) : /* Login Screen */\r\n !state.isLoggedIn && props.loginForm ? (\r\n props.customizeChat?.loginScreen?.component\r\n ?? <LoginScreen\r\n config={props.loginForm}\r\n onLogin={handleLogin}\r\n primaryColor={theme.primaryColor}\r\n renderFormField={props.renderFormField}\r\n />\r\n ) : (\r\n /* Chat Area */\r\n <>\r\n <MessageList\r\n messages={state.messages}\r\n isTyping={state.isTyping}\r\n styles={styles}\r\n primaryColor={theme.primaryColor}\r\n onQuickReply={handleQuickReply}\r\n onFormSubmit={handleFormSubmit}\r\n components={props.components}\r\n onComponentComplete={handleComponentComplete}\r\n collectedData={state.collectedData}\r\n currentStepId={state.currentStepId}\r\n renderFormField={props.renderFormField}\r\n customizeChat={props.customizeChat}\r\n />\r\n <div style={styles.inputArea}>\r\n {props.customizeChat?.input?.component ?? defaultInput}\r\n </div>\r\n {brandingCfg && (\r\n props.customizeChat?.branding?.component\r\n ?? <Branding config={brandingCfg} primaryColor={theme.primaryColor} />\r\n )}\r\n </>\r\n )}\r\n </div>\r\n );\r\n};\r\n","import type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\n/**\r\n * PluginManager — Manages plugin lifecycle (Open/Closed Principle)\r\n * Core is closed for modification, open for extension via plugins.\r\n */\r\nexport class PluginManager {\r\n private plugins: ChatPlugin[] = [];\r\n private context: PluginContext | null = null;\r\n private eventHandlers = new Map<string, Set<(...args: unknown[]) => void>>();\r\n\r\n register(plugins: ChatPlugin[]): void {\r\n this.plugins = [...plugins];\r\n }\r\n\r\n setContext(ctx: Omit<PluginContext, 'on' | 'emit'>): void {\r\n this.context = {\r\n ...ctx,\r\n on: (event, handler) => this.on(event, handler),\r\n emit: (event, ...args) => this.emit(event, ...args),\r\n };\r\n }\r\n\r\n getContext(): PluginContext | null {\r\n return this.context;\r\n }\r\n\r\n private on(event: string, handler: (...args: unknown[]) => void): void {\r\n if (!this.eventHandlers.has(event)) {\r\n this.eventHandlers.set(event, new Set());\r\n }\r\n this.eventHandlers.get(event)!.add(handler);\r\n }\r\n\r\n private emit(event: string, ...args: unknown[]): void {\r\n const handlers = this.eventHandlers.get(event);\r\n if (handlers) {\r\n handlers.forEach((handler) => handler(...args));\r\n }\r\n }\r\n\r\n async init(): Promise<void> {\r\n if (!this.context) return;\r\n const ctx = this.context;\r\n await Promise.allSettled(\r\n this.plugins.map(async (plugin) => {\r\n try {\r\n await plugin.onInit?.(ctx);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onInit error:`, err);\r\n }\r\n }),\r\n );\r\n }\r\n\r\n async onMessage(message: ChatMessage): Promise<ChatMessage> {\r\n if (!this.context) return message;\r\n let msg = message;\r\n for (const plugin of this.plugins) {\r\n try {\r\n const result = await plugin.onMessage?.(msg, this.context);\r\n if (result && typeof result === 'object' && 'id' in result) {\r\n msg = result;\r\n }\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onMessage error:`, err);\r\n }\r\n }\r\n this.dispatchEvent({ type: 'message', payload: msg, timestamp: Date.now() });\r\n return msg;\r\n }\r\n\r\n async onSubmit(data: Record<string, unknown>): Promise<void> {\r\n if (!this.context) return;\r\n for (const plugin of this.plugins) {\r\n try {\r\n await plugin.onSubmit?.(data, this.context);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onSubmit error:`, err);\r\n }\r\n }\r\n this.dispatchEvent({ type: 'submit', payload: data, timestamp: Date.now() });\r\n }\r\n\r\n /** Emit a lifecycle event to all plugins (open, close, flowEnd, stepChange, quickReply, etc.) */\r\n emitEvent(type: string, payload?: unknown): void {\r\n this.dispatchEvent({ type, payload, timestamp: Date.now() });\r\n this.emit(type, payload);\r\n }\r\n\r\n async destroy(): Promise<void> {\r\n if (!this.context) return;\r\n for (const plugin of this.plugins) {\r\n try {\r\n await plugin.onDestroy?.(this.context);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onDestroy error:`, err);\r\n }\r\n }\r\n this.eventHandlers.clear();\r\n this.plugins = [];\r\n }\r\n\r\n private dispatchEvent(event: ChatPluginEvent): void {\r\n if (!this.context) return;\r\n for (const plugin of this.plugins) {\r\n try {\r\n plugin.onEvent?.(event, this.context);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onEvent error:`, err);\r\n }\r\n }\r\n }\r\n}\r\n","import React, { useReducer, useEffect, useRef, useCallback } from 'react';\r\nimport type { ChatBotProps } from '../types';\r\nimport { ChatContext, chatReducer, initialState } from '../context/ChatContext';\r\nimport { resolveTheme, buildStyles, buildCSSVariables } from '../styles/theme';\r\nimport { Launcher } from './Launcher';\r\nimport { ChatWindow } from './ChatWindow';\r\nimport { PluginManager } from '../core/PluginManager';\r\nimport { uid } from '../utils/helpers';\r\n\r\nconst GLOBAL_STYLES = `\r\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\r\n\r\n@keyframes cb-window-enter {\r\n 0% { opacity: 0; transform: translateY(16px) scale(0.96); }\r\n 100% { opacity: 1; transform: translateY(0) scale(1); }\r\n}\r\n\r\n@keyframes cb-launcher-pulse {\r\n 0%, 100% { box-shadow: 0 6px 24px rgba(108, 92, 231, 0.4), 0 2px 8px rgba(0,0,0,0.1); }\r\n 50% { box-shadow: 0 8px 32px rgba(108, 92, 231, 0.55), 0 4px 12px rgba(0,0,0,0.15); }\r\n}\r\n\r\n@keyframes cb-fade-in {\r\n 0% { opacity: 0; transform: translateY(6px); }\r\n 100% { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n@keyframes cb-typing-bounce {\r\n 0%, 80%, 100% { transform: scale(0.6); opacity: 0.3; }\r\n 40% { transform: scale(1); opacity: 1; }\r\n}\r\n\r\n@keyframes cb-slide-up {\r\n 0% { opacity: 0; transform: translateY(10px); }\r\n 100% { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n.cb-scrollbar::-webkit-scrollbar {\r\n width: 5px;\r\n}\r\n.cb-scrollbar::-webkit-scrollbar-track {\r\n background: transparent;\r\n}\r\n.cb-scrollbar::-webkit-scrollbar-thumb {\r\n background: rgba(108, 92, 231, 0.2);\r\n border-radius: 10px;\r\n}\r\n.cb-scrollbar::-webkit-scrollbar-thumb:hover {\r\n background: rgba(108, 92, 231, 0.35);\r\n}\r\n`;\r\n\r\n// Inject styles globally once per document, not per component instance\r\nlet globalStyleInjected = false;\r\nfunction ensureGlobalStyles() {\r\n if (globalStyleInjected) return;\r\n if (typeof document === 'undefined') return;\r\n if (document.querySelector('style[data-chatbot-styles]')) {\r\n globalStyleInjected = true;\r\n return;\r\n }\r\n const style = document.createElement('style');\r\n style.setAttribute('data-chatbot-styles', '');\r\n style.textContent = GLOBAL_STYLES;\r\n document.head.appendChild(style);\r\n globalStyleInjected = true;\r\n}\r\n\r\nexport const ChatBot: React.FC<ChatBotProps> = (props) => {\r\n const [state, dispatch] = useReducer(chatReducer, props, initialState);\r\n const theme = resolveTheme(props.theme);\r\n const styles = buildStyles(theme, props.style);\r\n const cssVars = buildCSSVariables(theme);\r\n const position = props.position ?? 'bottom-right';\r\n const showLauncher = props.showLauncher !== false;\r\n const pluginManagerRef = useRef<PluginManager | null>(null);\r\n\r\n // Use refs so plugin context always reads fresh state\r\n const stateRef = useRef(state);\r\n stateRef.current = state;\r\n\r\n // Inject global styles once\r\n useEffect(() => {\r\n ensureGlobalStyles();\r\n }, []);\r\n\r\n // Initialize plugins\r\n useEffect(() => {\r\n if (props.plugins && props.plugins.length > 0) {\r\n const pm = new PluginManager();\r\n pm.register(props.plugins);\r\n pm.setContext({\r\n sendMessage: (text) => {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'user', text, timestamp: Date.now() },\r\n });\r\n },\r\n addBotMessage: (text) => {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'bot', text, timestamp: Date.now() },\r\n });\r\n },\r\n getMessages: () => stateRef.current.messages,\r\n getData: () => stateRef.current.collectedData,\r\n setData: (key, value) => dispatch({ type: 'SET_DATA', payload: { [key]: value } }),\r\n });\r\n pm.init();\r\n pluginManagerRef.current = pm;\r\n\r\n return () => {\r\n pm.destroy();\r\n };\r\n }\r\n }, [props.plugins]);\r\n\r\n const handleToggle = useCallback(() => {\r\n const willOpen = !state.isOpen;\r\n dispatch({ type: 'TOGGLE_OPEN' });\r\n if (willOpen) props.callbacks?.onOpen?.();\r\n else props.callbacks?.onClose?.();\r\n }, [state.isOpen, props.callbacks]);\r\n\r\n return (\r\n <ChatContext.Provider value={{ state, dispatch, props, pluginManager: pluginManagerRef.current }}>\r\n <div style={{ ...styles.root, ...cssVars as React.CSSProperties }} className={props.className}>\r\n <ChatWindow styles={styles} position={position} zIndex={props.zIndex} hidden={!state.isOpen} />\r\n {showLauncher && (\r\n props.customizeChat?.launcher?.component\r\n ?? <Launcher\r\n onClick={handleToggle}\r\n isOpen={state.isOpen}\r\n position={position}\r\n styles={styles}\r\n icon={props.launcherIcon}\r\n closeIcon={props.closeIcon}\r\n zIndex={props.zIndex}\r\n />\r\n )}\r\n </div>\r\n </ChatContext.Provider>\r\n );\r\n};\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Analytics Plugin — tracks sessions, messages, forms, timing, and step progression\r\n */\r\nexport function analyticsPlugin(options?: {\r\n onTrack?: (event: string, data?: unknown) => void;\r\n sessionId?: string;\r\n}): ChatPlugin {\r\n const sessionId = options?.sessionId ?? `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\r\n let messageCount = 0;\r\n let formSubmissions = 0;\r\n let startTime = 0;\r\n const stepTimings: Record<string, number> = {};\r\n let lastStepTime = 0;\r\n\r\n const track = (event: string, data?: unknown) => {\r\n options?.onTrack?.(event, { sessionId, ...(data as Record<string, unknown>) });\r\n };\r\n\r\n return {\r\n name: 'analytics',\r\n\r\n onInit() {\r\n messageCount = 0;\r\n formSubmissions = 0;\r\n startTime = Date.now();\r\n lastStepTime = startTime;\r\n track('chatbot:init', { timestamp: startTime });\r\n },\r\n\r\n onMessage(message) {\r\n messageCount++;\r\n track('chatbot:message', {\r\n sender: message.sender,\r\n messageCount,\r\n elapsed: Date.now() - startTime,\r\n });\r\n },\r\n\r\n onSubmit(data) {\r\n formSubmissions++;\r\n track('chatbot:submit', {\r\n formSubmissions,\r\n fields: Object.keys(data),\r\n elapsed: Date.now() - startTime,\r\n });\r\n },\r\n\r\n onEvent(event) {\r\n switch (event.type) {\r\n case 'open':\r\n track('chatbot:open');\r\n break;\r\n case 'close':\r\n track('chatbot:close', { duration: Date.now() - startTime });\r\n break;\r\n case 'stepChange': {\r\n const now = Date.now();\r\n const payload = event.payload as { stepId: string } | undefined;\r\n if (payload?.stepId) {\r\n stepTimings[payload.stepId] = now - lastStepTime;\r\n lastStepTime = now;\r\n track('chatbot:step', { stepId: payload.stepId, stepDuration: stepTimings[payload.stepId] });\r\n }\r\n break;\r\n }\r\n case 'flowEnd':\r\n track('chatbot:flowEnd', {\r\n totalMessages: messageCount,\r\n totalForms: formSubmissions,\r\n duration: Date.now() - startTime,\r\n stepTimings,\r\n });\r\n break;\r\n case 'quickReply':\r\n track('chatbot:quickReply', event.payload);\r\n break;\r\n }\r\n },\r\n\r\n onDestroy() {\r\n track('chatbot:destroy', {\r\n totalMessages: messageCount,\r\n totalFormSubmissions: formSubmissions,\r\n sessionDuration: Date.now() - startTime,\r\n stepTimings,\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\r\nconst LEVELS: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };\r\n\r\n/**\r\n * Logger Plugin — logs all chatbot events for debugging or auditing\r\n */\r\nexport function loggerPlugin(options?: {\r\n level?: LogLevel;\r\n prefix?: string;\r\n logger?: Pick<Console, 'debug' | 'info' | 'warn' | 'error'>;\r\n}): ChatPlugin {\r\n const minLevel = LEVELS[options?.level ?? 'debug'];\r\n const prefix = options?.prefix ?? '[ChatBot]';\r\n const log = options?.logger ?? console;\r\n\r\n const write = (level: LogLevel, ...args: unknown[]) => {\r\n if (LEVELS[level] >= minLevel) log[level](prefix, ...args);\r\n };\r\n\r\n return {\r\n name: 'logger',\r\n\r\n onInit() {\r\n write('info', 'Initialized');\r\n },\r\n\r\n onMessage(message) {\r\n write('debug', `Message [${message.sender}]:`, message.text ?? '(no text)');\r\n },\r\n\r\n onSubmit(data) {\r\n write('info', 'Form submitted:', Object.keys(data));\r\n },\r\n\r\n onEvent(event) {\r\n write('debug', `Event [${event.type}]:`, event.payload ?? '');\r\n },\r\n\r\n onDestroy() {\r\n write('info', 'Destroyed');\r\n },\r\n };\r\n}\r\n","/** Shared HTTP utility for plugins that communicate with external endpoints */\r\nexport interface HttpOptions {\r\n url: string;\r\n headers?: Record<string, string>;\r\n timeout?: number;\r\n}\r\n\r\nexport async function postJSON(\r\n url: string,\r\n body: unknown,\r\n headers?: Record<string, string>,\r\n timeout = 10000,\r\n): Promise<Response> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n return await fetch(url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...headers },\r\n body: JSON.stringify(body),\r\n signal: controller.signal,\r\n });\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n}\r\n\r\nexport async function getJSON<T = unknown>(\r\n url: string,\r\n headers?: Record<string, string>,\r\n timeout = 10000,\r\n): Promise<T> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const res = await fetch(url, {\r\n headers: { 'Accept': 'application/json', ...headers },\r\n signal: controller.signal,\r\n });\r\n return (await res.json()) as T;\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\ntype WebhookEventType = 'message' | 'submit' | 'init' | 'destroy' | 'open' | 'close' | 'flowEnd' | 'stepChange' | 'quickReply' | 'login';\r\n\r\n/**\r\n * Webhook Plugin — sends messages/submissions/lifecycle events to an external endpoint\r\n */\r\nexport function webhookPlugin(options: {\r\n url: string;\r\n headers?: Record<string, string>;\r\n events?: WebhookEventType[];\r\n timeout?: number;\r\n}): ChatPlugin {\r\n const events = new Set<string>(options.events ?? ['message', 'submit']);\r\n const timeout = options.timeout ?? 10000;\r\n\r\n const send = async (type: string, payload: unknown) => {\r\n try {\r\n await postJSON(options.url, { type, payload, timestamp: Date.now() }, options.headers, timeout);\r\n } catch (err) {\r\n console.error(`[webhook] Failed to send ${type}:`, err);\r\n }\r\n };\r\n\r\n return {\r\n name: 'webhook',\r\n\r\n async onInit() {\r\n if (events.has('init')) await send('init', {});\r\n },\r\n\r\n async onMessage(message) {\r\n if (events.has('message')) await send('message', message);\r\n },\r\n\r\n async onSubmit(data) {\r\n if (events.has('submit')) await send('submit', data);\r\n },\r\n\r\n onEvent(event) {\r\n if (events.has(event.type)) {\r\n send(event.type, event.payload);\r\n }\r\n },\r\n\r\n async onDestroy() {\r\n if (events.has('destroy')) await send('destroy', {});\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * CRM Plugin — pushes user/lead data to CRM systems\r\n */\r\nexport function crmPlugin(options: {\r\n endpoint: string;\r\n provider?: string;\r\n headers?: Record<string, string>;\r\n mapFields?: (data: Record<string, unknown>) => Record<string, unknown>;\r\n events?: ('submit' | 'flowEnd' | 'login')[];\r\n}): ChatPlugin {\r\n const events = new Set(options.events ?? ['submit', 'flowEnd']);\r\n\r\n const push = async (type: string, data: Record<string, unknown>) => {\r\n const mapped = options.mapFields ? options.mapFields(data) : data;\r\n try {\r\n await postJSON(options.endpoint, {\r\n provider: options.provider,\r\n type,\r\n data: mapped,\r\n timestamp: Date.now(),\r\n }, options.headers);\r\n } catch (err) {\r\n console.error('[crm] Push failed:', err);\r\n }\r\n };\r\n\r\n return {\r\n name: 'crm',\r\n\r\n async onSubmit(data) {\r\n if (events.has('submit')) await push('submit', data);\r\n },\r\n\r\n onEvent(event) {\r\n if (events.has(event.type as string) && event.payload) {\r\n push(event.type, event.payload as Record<string, unknown>);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * Email Plugin — triggers emails via external API when chat events occur\r\n */\r\nexport function emailPlugin(options: {\r\n endpoint: string;\r\n template?: string;\r\n headers?: Record<string, string>;\r\n triggers?: ('submit' | 'flowEnd' | 'login')[];\r\n mapPayload?: (data: Record<string, unknown>) => Record<string, unknown>;\r\n}): ChatPlugin {\r\n const triggers = new Set(options.triggers ?? ['flowEnd']);\r\n\r\n const send = async (trigger: string, data: Record<string, unknown>) => {\r\n const payload = options.mapPayload ? options.mapPayload(data) : data;\r\n try {\r\n await postJSON(options.endpoint, {\r\n template: options.template,\r\n trigger,\r\n data: payload,\r\n timestamp: Date.now(),\r\n }, options.headers);\r\n } catch (err) {\r\n console.error('[email] Send failed:', err);\r\n }\r\n };\r\n\r\n return {\r\n name: 'email',\r\n\r\n async onSubmit(data) {\r\n if (triggers.has('submit')) await send('submit', data);\r\n },\r\n\r\n onEvent(event) {\r\n if (triggers.has(event.type) && event.payload) {\r\n send(event.type, event.payload as Record<string, unknown>);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * AI Plugin — generates bot responses using external AI providers (OpenAI, Anthropic, custom)\r\n */\r\nexport function aiPlugin(options: {\r\n provider?: 'openai' | 'anthropic' | 'custom';\r\n endpoint?: string;\r\n apiKey?: string;\r\n model?: string;\r\n systemPrompt?: string;\r\n headers?: Record<string, string>;\r\n shouldRespond?: (text: string) => boolean;\r\n parseResponse?: (response: unknown) => string;\r\n timeout?: number;\r\n}): ChatPlugin {\r\n const endpoint = options.endpoint ?? (\r\n options.provider === 'openai' ? 'https://api.openai.com/v1/chat/completions'\r\n : options.provider === 'anthropic' ? 'https://api.anthropic.com/v1/messages'\r\n : options.endpoint ?? ''\r\n );\r\n const model = options.model ?? (options.provider === 'openai' ? 'gpt-3.5-turbo' : 'claude-3-haiku-20240307');\r\n const conversationHistory: Array<{ role: string; content: string }> = [];\r\n\r\n if (options.systemPrompt) {\r\n conversationHistory.push({ role: 'system', content: options.systemPrompt });\r\n }\r\n\r\n const buildHeaders = (): Record<string, string> => {\r\n const h: Record<string, string> = { ...options.headers };\r\n if (options.apiKey) {\r\n if (options.provider === 'anthropic') {\r\n h['x-api-key'] = options.apiKey;\r\n h['anthropic-version'] = '2023-06-01';\r\n } else {\r\n h['Authorization'] = `Bearer ${options.apiKey}`;\r\n }\r\n }\r\n return h;\r\n };\r\n\r\n const buildBody = () => {\r\n if (options.provider === 'anthropic') {\r\n return {\r\n model,\r\n max_tokens: 1024,\r\n system: options.systemPrompt,\r\n messages: conversationHistory.filter(m => m.role !== 'system'),\r\n };\r\n }\r\n return { model, messages: conversationHistory };\r\n };\r\n\r\n const defaultParse = (res: unknown): string => {\r\n const data = res as Record<string, unknown>;\r\n // OpenAI format\r\n if (data.choices) {\r\n const choices = data.choices as Array<{ message?: { content?: string } }>;\r\n return choices[0]?.message?.content ?? '';\r\n }\r\n // Anthropic format\r\n if (data.content) {\r\n const blocks = data.content as Array<{ text?: string }>;\r\n return blocks[0]?.text ?? '';\r\n }\r\n return String(data);\r\n };\r\n\r\n return {\r\n name: 'ai',\r\n\r\n async onMessage(message, ctx: PluginContext) {\r\n if (message.sender !== 'user' || !message.text) return;\r\n if (options.shouldRespond && !options.shouldRespond(message.text)) return;\r\n if (!endpoint) return;\r\n\r\n conversationHistory.push({ role: 'user', content: message.text });\r\n\r\n try {\r\n const res = await postJSON(endpoint, buildBody(), buildHeaders(), options.timeout ?? 30000);\r\n const json = await res.json();\r\n const text = options.parseResponse ? options.parseResponse(json) : defaultParse(json);\r\n\r\n if (text) {\r\n conversationHistory.push({ role: 'assistant', content: text });\r\n ctx.addBotMessage(text);\r\n }\r\n } catch (err) {\r\n console.error('[ai] Response failed:', err);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\ninterface IntentRule {\r\n intent: string;\r\n patterns: string[];\r\n matchType?: 'contains' | 'exact' | 'regex';\r\n}\r\n\r\n/**\r\n * Intent Plugin — detects user intent from text and emits intent events for dynamic routing\r\n */\r\nexport function intentPlugin(options?: {\r\n rules?: IntentRule[];\r\n onIntentDetected?: (intent: string, text: string, ctx: PluginContext) => void;\r\n fallbackIntent?: string;\r\n}): ChatPlugin {\r\n const rules = options?.rules ?? [];\r\n const fallback = options?.fallbackIntent ?? 'unknown';\r\n\r\n const detectIntent = (text: string): string => {\r\n const lower = text.toLowerCase().trim();\r\n for (const rule of rules) {\r\n for (const pattern of rule.patterns) {\r\n const pat = pattern.toLowerCase();\r\n switch (rule.matchType ?? 'contains') {\r\n case 'exact':\r\n if (lower === pat) return rule.intent;\r\n break;\r\n case 'regex':\r\n try { if (new RegExp(pat, 'i').test(text)) return rule.intent; } catch { /* skip */ }\r\n break;\r\n case 'contains':\r\n default:\r\n if (lower.includes(pat)) return rule.intent;\r\n break;\r\n }\r\n }\r\n }\r\n return fallback;\r\n };\r\n\r\n return {\r\n name: 'intent',\r\n\r\n onMessage(message, ctx) {\r\n if (message.sender !== 'user' || !message.text) return;\r\n const intent = detectIntent(message.text);\r\n ctx.emit('intent:detected', { intent, text: message.text });\r\n options?.onIntentDetected?.(intent, message.text, ctx);\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Typing Plugin — adds configurable typing delay before bot messages are shown\r\n */\r\nexport function typingPlugin(options?: {\r\n delay?: number;\r\n onTypingStart?: () => void;\r\n onTypingEnd?: () => void;\r\n}): ChatPlugin {\r\n const typingDelay = options?.delay ?? 1000;\r\n\r\n return {\r\n name: 'typing',\r\n\r\n async onMessage(message, ctx) {\r\n if (message.sender === 'bot') {\r\n options?.onTypingStart?.();\r\n ctx.emit('typing:start', {});\r\n await new Promise((resolve) => setTimeout(resolve, typingDelay));\r\n options?.onTypingEnd?.();\r\n ctx.emit('typing:end', {});\r\n }\r\n },\r\n };\r\n}\r\n","/** Shared timer utility for plugins that schedule delayed actions */\r\nexport class TimerManager {\r\n private timers = new Map<string, ReturnType<typeof setTimeout>>();\r\n private intervals = new Map<string, ReturnType<typeof setInterval>>();\r\n\r\n setTimeout(id: string, fn: () => void, ms: number): void {\r\n this.clearTimeout(id);\r\n this.timers.set(id, setTimeout(() => { this.timers.delete(id); fn(); }, ms));\r\n }\r\n\r\n setInterval(id: string, fn: () => void, ms: number): void {\r\n this.clearInterval(id);\r\n this.intervals.set(id, setInterval(fn, ms));\r\n }\r\n\r\n clearTimeout(id: string): void {\r\n const t = this.timers.get(id);\r\n if (t) { clearTimeout(t); this.timers.delete(id); }\r\n }\r\n\r\n clearInterval(id: string): void {\r\n const i = this.intervals.get(id);\r\n if (i) { clearInterval(i); this.intervals.delete(id); }\r\n }\r\n\r\n destroy(): void {\r\n this.timers.forEach((t) => clearTimeout(t));\r\n this.intervals.forEach((i) => clearInterval(i));\r\n this.timers.clear();\r\n this.intervals.clear();\r\n }\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\n/**\r\n * Auto Reply Plugin — sends automated replies when user is inactive\r\n */\r\nexport function autoReplyPlugin(options?: {\r\n timeout?: number;\r\n message?: string;\r\n maxReplies?: number;\r\n onlyWhenOpen?: boolean;\r\n}): ChatPlugin {\r\n const timeout = options?.timeout ?? 30000;\r\n const message = options?.message ?? \"Are you still there? Let me know if you need help! 👋\";\r\n const maxReplies = options?.maxReplies ?? 2;\r\n const timers = new TimerManager();\r\n let replyCount = 0;\r\n let active = false;\r\n\r\n return {\r\n name: 'autoReply',\r\n\r\n onInit(ctx) {\r\n active = true;\r\n replyCount = 0;\r\n },\r\n\r\n onMessage(msg, ctx) {\r\n // Reset timer on any user message\r\n if (msg.sender === 'user') {\r\n replyCount = 0;\r\n timers.clearTimeout('idle');\r\n if (active) {\r\n timers.setTimeout('idle', () => {\r\n if (replyCount < maxReplies) {\r\n replyCount++;\r\n ctx.addBotMessage(message);\r\n }\r\n }, timeout);\r\n }\r\n }\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'open') {\r\n active = true;\r\n } else if (event.type === 'close') {\r\n active = false;\r\n timers.clearTimeout('idle');\r\n }\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\ntype Validator = (text: string) => string | null;\r\n\r\n/**\r\n * Validation Plugin — adds advanced validation rules for user inputs\r\n */\r\nexport function validationPlugin(options?: {\r\n validators?: Record<string, Validator>;\r\n sanitize?: boolean;\r\n blockProfanity?: boolean;\r\n profanityList?: string[];\r\n onValidationFail?: (text: string, error: string) => void;\r\n}): ChatPlugin {\r\n const validators = options?.validators ?? {};\r\n const sanitize = options?.sanitize ?? true;\r\n const profanityList = options?.profanityList ?? [];\r\n\r\n const sanitizeHtml = (text: string): string => {\r\n return text.replace(/[<>&\"']/g, (c) => {\r\n const map: Record<string, string> = { '<': '&lt;', '>': '&gt;', '&': '&amp;', '\"': '&quot;', \"'\": '&#39;' };\r\n return map[c] ?? c;\r\n });\r\n };\r\n\r\n const checkProfanity = (text: string): string | null => {\r\n if (!options?.blockProfanity || !profanityList.length) return null;\r\n const lower = text.toLowerCase();\r\n for (const word of profanityList) {\r\n if (lower.includes(word.toLowerCase())) return 'Message contains inappropriate content.';\r\n }\r\n return null;\r\n };\r\n\r\n return {\r\n name: 'validation',\r\n\r\n onMessage(message: ChatMessage, ctx: PluginContext) {\r\n if (message.sender !== 'user' || !message.text) return;\r\n\r\n // Profanity check\r\n const profanityError = checkProfanity(message.text);\r\n if (profanityError) {\r\n options?.onValidationFail?.(message.text, profanityError);\r\n ctx.emit('validation:fail', { text: message.text, error: profanityError });\r\n return { ...message, text: '***' };\r\n }\r\n\r\n // Custom validators\r\n for (const [name, validate] of Object.entries(validators)) {\r\n const error = validate(message.text);\r\n if (error) {\r\n options?.onValidationFail?.(message.text, error);\r\n ctx.emit('validation:fail', { text: message.text, validator: name, error });\r\n }\r\n }\r\n\r\n // Sanitize\r\n if (sanitize) {\r\n return { ...message, text: sanitizeHtml(message.text) };\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Upload Plugin — handles file uploads to external storage (S3, etc.)\r\n */\r\nexport function uploadPlugin(options: {\r\n endpoint: string;\r\n storage?: 's3' | 'gcs' | 'azure' | 'custom';\r\n headers?: Record<string, string>;\r\n maxSize?: number;\r\n allowedTypes?: string[];\r\n onUploadStart?: (file: { name: string; size: number }) => void;\r\n onUploadComplete?: (file: { name: string; url: string }) => void;\r\n onUploadError?: (file: { name: string }, error: Error) => void;\r\n}): ChatPlugin {\r\n return {\r\n name: 'upload',\r\n\r\n onInit(ctx) {\r\n ctx.on('file:upload', async (...args: unknown[]) => {\r\n const files = args[0] as File[];\r\n if (!files?.length) return;\r\n\r\n for (const file of files) {\r\n // Validate size\r\n if (options.maxSize && file.size > options.maxSize) {\r\n options.onUploadError?.({ name: file.name }, new Error(`File too large: ${file.size} > ${options.maxSize}`));\r\n continue;\r\n }\r\n\r\n // Validate type\r\n if (options.allowedTypes?.length && !options.allowedTypes.some(t => file.type.startsWith(t))) {\r\n options.onUploadError?.({ name: file.name }, new Error(`File type not allowed: ${file.type}`));\r\n continue;\r\n }\r\n\r\n options.onUploadStart?.({ name: file.name, size: file.size });\r\n\r\n try {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n formData.append('storage', options.storage ?? 'custom');\r\n\r\n const res = await fetch(options.endpoint, {\r\n method: 'POST',\r\n headers: options.headers,\r\n body: formData,\r\n });\r\n\r\n if (!res.ok) throw new Error(`Upload failed: ${res.status}`);\r\n\r\n const result = await res.json() as { url?: string };\r\n options.onUploadComplete?.({ name: file.name, url: result.url ?? '' });\r\n ctx.emit('file:uploaded', { name: file.name, url: result.url });\r\n } catch (err) {\r\n options.onUploadError?.({ name: file.name }, err as Error);\r\n }\r\n }\r\n });\r\n },\r\n };\r\n}\r\n","/** Shared browser storage utility for plugins */\r\nexport interface StorageAdapter {\r\n get(key: string): string | null;\r\n set(key: string, value: string): void;\r\n remove(key: string): void;\r\n}\r\n\r\nexport function createStorageAdapter(type: 'local' | 'session' = 'local'): StorageAdapter {\r\n const store = type === 'session' ? sessionStorage : localStorage;\r\n return {\r\n get: (key) => { try { return store.getItem(key); } catch { return null; } },\r\n set: (key, value) => { try { store.setItem(key, value); } catch { /* storage full */ } },\r\n remove: (key) => { try { store.removeItem(key); } catch { /* noop */ } },\r\n };\r\n}\r\n\r\nexport function safeJsonParse<T>(raw: string | null, fallback: T): T {\r\n if (!raw) return fallback;\r\n try { return JSON.parse(raw) as T; } catch { return fallback; }\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\nimport { createStorageAdapter, safeJsonParse } from './utils/storage';\r\n\r\n/**\r\n * Persistence Plugin — saves/restores full chat history via browser storage\r\n */\r\nexport function persistencePlugin(options?: {\r\n storageKey?: string;\r\n storage?: 'local' | 'session';\r\n maxMessages?: number;\r\n ttl?: number;\r\n}): ChatPlugin {\r\n const key = options?.storageKey ?? 'chatbot_history';\r\n const store = createStorageAdapter(options?.storage ?? 'local');\r\n const max = options?.maxMessages ?? 100;\r\n const ttl = options?.ttl ?? 0;\r\n\r\n interface Snapshot { messages: ChatMessage[]; savedAt: number }\r\n\r\n const save = (ctx: PluginContext) => {\r\n const messages = ctx.getMessages().slice(-max);\r\n const snapshot: Snapshot = { messages, savedAt: Date.now() };\r\n store.set(key, JSON.stringify(snapshot));\r\n };\r\n\r\n return {\r\n name: 'persistence',\r\n\r\n onInit(ctx) {\r\n const raw = store.get(key);\r\n const snapshot = safeJsonParse<Snapshot | null>(raw, null);\r\n if (!snapshot?.messages?.length) return;\r\n\r\n if (ttl > 0 && Date.now() - snapshot.savedAt > ttl) {\r\n store.remove(key);\r\n return;\r\n }\r\n\r\n for (const msg of snapshot.messages) {\r\n if (msg.sender === 'bot' && msg.text) {\r\n ctx.addBotMessage(msg.text);\r\n }\r\n }\r\n },\r\n\r\n onMessage(_message, ctx) {\r\n save(ctx);\r\n },\r\n\r\n onSubmit(_data, ctx) {\r\n save(ctx);\r\n },\r\n\r\n onDestroy(ctx) {\r\n save(ctx);\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON, getJSON } from './utils/http';\r\n\r\n/**\r\n * Sync Plugin — syncs chat data with a backend endpoint\r\n */\r\nexport function syncPlugin(options: {\r\n endpoint: string;\r\n headers?: Record<string, string>;\r\n syncInterval?: number;\r\n sessionKey?: string;\r\n}): ChatPlugin {\r\n const interval = options.syncInterval ?? 0;\r\n const sessionKey = options.sessionKey ?? `chat_${Date.now()}`;\r\n let timer: ReturnType<typeof setInterval> | null = null;\r\n\r\n const push = (ctx: PluginContext) => {\r\n postJSON(options.endpoint, {\r\n session: sessionKey,\r\n messages: ctx.getMessages(),\r\n data: ctx.getData(),\r\n timestamp: Date.now(),\r\n }, options.headers).catch(() => { /* silent */ });\r\n };\r\n\r\n return {\r\n name: 'sync',\r\n\r\n async onInit(ctx) {\r\n // Load remote state\r\n try {\r\n const data = await getJSON<{ messages?: unknown[]; data?: Record<string, unknown> }>(\r\n `${options.endpoint}?session=${encodeURIComponent(sessionKey)}`,\r\n options.headers,\r\n );\r\n if (data.data) {\r\n for (const [k, v] of Object.entries(data.data)) {\r\n ctx.setData(k, v);\r\n }\r\n }\r\n } catch { /* no remote state */ }\r\n\r\n // Periodic sync\r\n if (interval > 0) {\r\n timer = setInterval(() => push(ctx), interval);\r\n }\r\n },\r\n\r\n onSubmit(_data, ctx) {\r\n push(ctx);\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'flowEnd') push(ctx);\r\n },\r\n\r\n onDestroy(ctx) {\r\n if (timer) clearInterval(timer);\r\n push(ctx);\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\n/**\r\n * Auth Plugin — manages user authentication and identity\r\n */\r\nexport function authPlugin(options: {\r\n type?: 'jwt' | 'session' | 'custom';\r\n tokenKey?: string;\r\n storage?: 'local' | 'session';\r\n onAuth?: (token: string, ctx: PluginContext) => void;\r\n onAuthExpired?: (ctx: PluginContext) => void;\r\n validateToken?: (token: string) => boolean;\r\n}): ChatPlugin {\r\n const tokenKey = options.tokenKey ?? 'chatbot_auth_token';\r\n const store = createStorageAdapter(options.storage ?? 'local');\r\n\r\n const isExpired = (token: string): boolean => {\r\n if (options.validateToken) return !options.validateToken(token);\r\n if (options.type === 'jwt') {\r\n try {\r\n const payload = JSON.parse(atob(token.split('.')[1]!));\r\n return payload.exp ? payload.exp * 1000 < Date.now() : false;\r\n } catch { return true; }\r\n }\r\n return false;\r\n };\r\n\r\n return {\r\n name: 'auth',\r\n\r\n onInit(ctx) {\r\n const token = store.get(tokenKey);\r\n if (token) {\r\n if (isExpired(token)) {\r\n store.remove(tokenKey);\r\n options.onAuthExpired?.(ctx);\r\n ctx.emit('auth:expired', {});\r\n } else {\r\n ctx.setData('authToken', token);\r\n options.onAuth?.(token, ctx);\r\n ctx.emit('auth:restored', { token });\r\n }\r\n }\r\n\r\n // Listen for login events\r\n ctx.on('auth:login', (...args: unknown[]) => {\r\n const token = args[0] as string;\r\n if (token) {\r\n store.set(tokenKey, token);\r\n ctx.setData('authToken', token);\r\n ctx.emit('auth:success', { token });\r\n }\r\n });\r\n\r\n ctx.on('auth:logout', () => {\r\n store.remove(tokenKey);\r\n ctx.setData('authToken', undefined);\r\n ctx.emit('auth:loggedOut', {});\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Rate Limit Plugin — prevents spam by limiting message frequency\r\n */\r\nexport function rateLimitPlugin(options?: {\r\n limit?: number;\r\n window?: number;\r\n onLimited?: (remaining: number) => void;\r\n warningMessage?: string;\r\n}): ChatPlugin {\r\n const limit = options?.limit ?? 10;\r\n const window = options?.window ?? 60000;\r\n const timestamps: number[] = [];\r\n\r\n return {\r\n name: 'rateLimit',\r\n\r\n onMessage(message, ctx) {\r\n if (message.sender !== 'user') return;\r\n\r\n const now = Date.now();\r\n // Remove expired timestamps\r\n while (timestamps.length > 0 && now - timestamps[0]! > window) {\r\n timestamps.shift();\r\n }\r\n\r\n timestamps.push(now);\r\n\r\n if (timestamps.length > limit) {\r\n const remaining = Math.ceil((timestamps[0]! + window - now) / 1000);\r\n options?.onLimited?.(remaining);\r\n ctx.emit('rateLimit:exceeded', { remaining });\r\n ctx.addBotMessage(options?.warningMessage ?? `Too many messages. Please wait ${remaining}s.`);\r\n return { ...message, text: '' };\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Push Plugin — sends browser push notifications for new messages\r\n */\r\nexport function pushPlugin(options?: {\r\n title?: string;\r\n icon?: string;\r\n onlyWhenHidden?: boolean;\r\n requestPermission?: boolean;\r\n}): ChatPlugin {\r\n const title = options?.title ?? 'New Message';\r\n const onlyWhenHidden = options?.onlyWhenHidden ?? true;\r\n\r\n const canNotify = () =>\r\n typeof Notification !== 'undefined' && Notification.permission === 'granted';\r\n\r\n const shouldNotify = () =>\r\n canNotify() && (!onlyWhenHidden || document.hidden);\r\n\r\n return {\r\n name: 'push',\r\n\r\n async onInit() {\r\n if (options?.requestPermission !== false && typeof Notification !== 'undefined' && Notification.permission === 'default') {\r\n await Notification.requestPermission();\r\n }\r\n },\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text && shouldNotify()) {\r\n new Notification(title, {\r\n body: message.text.slice(0, 200),\r\n icon: options?.icon,\r\n tag: 'chatbot-msg',\r\n });\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Sound Plugin — plays sound on new bot messages\r\n */\r\nexport function soundPlugin(options?: {\r\n src?: string;\r\n volume?: number;\r\n onlyWhenHidden?: boolean;\r\n}): ChatPlugin {\r\n const volume = Math.min(1, Math.max(0, options?.volume ?? 0.5));\r\n const onlyWhenHidden = options?.onlyWhenHidden ?? false;\r\n let audioCtx: AudioContext | null = null;\r\n\r\n const playDefault = () => {\r\n if (!audioCtx) audioCtx = new AudioContext();\r\n const osc = audioCtx.createOscillator();\r\n const gain = audioCtx.createGain();\r\n osc.connect(gain);\r\n gain.connect(audioCtx.destination);\r\n osc.frequency.value = 800;\r\n gain.gain.value = volume * 0.3;\r\n osc.start();\r\n gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.15);\r\n osc.stop(audioCtx.currentTime + 0.15);\r\n };\r\n\r\n const play = () => {\r\n if (onlyWhenHidden && !document.hidden) return;\r\n if (options?.src) {\r\n const audio = new Audio(options.src);\r\n audio.volume = volume;\r\n audio.play().catch(() => { /* user hasn't interacted yet */ });\r\n } else {\r\n try { playDefault(); } catch { /* AudioContext not available */ }\r\n }\r\n };\r\n\r\n return {\r\n name: 'sound',\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot') play();\r\n },\r\n\r\n onDestroy() {\r\n audioCtx?.close().catch(() => {});\r\n audioCtx = null;\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\n/**\r\n * Agent Plugin — enables live human agent takeover of the chat\r\n */\r\nexport function agentPlugin(options: {\r\n socketUrl?: string;\r\n onAgentConnect?: (ctx: PluginContext) => void;\r\n onAgentDisconnect?: (ctx: PluginContext) => void;\r\n onAgentMessage?: (text: string, ctx: PluginContext) => void;\r\n connectMessage?: string;\r\n disconnectMessage?: string;\r\n}): ChatPlugin {\r\n let ws: WebSocket | null = null;\r\n let isAgentMode = false;\r\n\r\n return {\r\n name: 'agent',\r\n\r\n onInit(ctx) {\r\n // Listen for agent handoff trigger\r\n ctx.on('agent:connect', () => {\r\n if (ws || !options.socketUrl) return;\r\n\r\n ws = new WebSocket(options.socketUrl);\r\n ws.onopen = () => {\r\n isAgentMode = true;\r\n ctx.addBotMessage(options.connectMessage ?? '🧑‍💼 You are now connected to a live agent.');\r\n options.onAgentConnect?.(ctx);\r\n ctx.emit('agent:connected', {});\r\n };\r\n\r\n ws.onmessage = (event) => {\r\n const text = typeof event.data === 'string' ? event.data : '';\r\n if (text) {\r\n ctx.addBotMessage(text);\r\n options.onAgentMessage?.(text, ctx);\r\n }\r\n };\r\n\r\n ws.onclose = () => {\r\n isAgentMode = false;\r\n ws = null;\r\n ctx.addBotMessage(options.disconnectMessage ?? '🧑‍💼 The agent has disconnected.');\r\n options.onAgentDisconnect?.(ctx);\r\n ctx.emit('agent:disconnected', {});\r\n };\r\n\r\n ws.onerror = () => {\r\n ws?.close();\r\n };\r\n });\r\n\r\n ctx.on('agent:disconnect', () => {\r\n ws?.close();\r\n });\r\n },\r\n\r\n onMessage(message) {\r\n // Forward user messages to agent via websocket\r\n if (isAgentMode && message.sender === 'user' && ws?.readyState === WebSocket.OPEN) {\r\n ws.send(JSON.stringify({ type: 'message', text: message.text, timestamp: Date.now() }));\r\n }\r\n },\r\n\r\n onDestroy() {\r\n ws?.close();\r\n ws = null;\r\n isAgentMode = false;\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * Transfer Plugin — transfers chat to different departments/agents via API\r\n */\r\nexport function transferPlugin(options: {\r\n endpoint: string;\r\n headers?: Record<string, string>;\r\n departments?: string[];\r\n onTransfer?: (department: string, ctx: PluginContext) => void;\r\n onTransferComplete?: (result: unknown, ctx: PluginContext) => void;\r\n transferMessage?: string;\r\n}): ChatPlugin {\r\n return {\r\n name: 'transfer',\r\n\r\n onInit(ctx) {\r\n ctx.on('transfer:request', async (...args: unknown[]) => {\r\n const department = (args[0] as string) ?? 'default';\r\n\r\n ctx.addBotMessage(\r\n options.transferMessage ?? `Transferring you to **${department}**. Please hold...`,\r\n );\r\n options.onTransfer?.(department, ctx);\r\n\r\n try {\r\n const res = await postJSON(options.endpoint, {\r\n department,\r\n messages: ctx.getMessages(),\r\n data: ctx.getData(),\r\n timestamp: Date.now(),\r\n }, options.headers);\r\n\r\n const result = await res.json();\r\n options.onTransferComplete?.(result, ctx);\r\n ctx.emit('transfer:complete', { department, result });\r\n } catch (err) {\r\n console.error('[transfer] Failed:', err);\r\n ctx.addBotMessage('Transfer failed. Please try again later.');\r\n ctx.emit('transfer:error', { department, error: String(err) });\r\n }\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\n/**\r\n * Theme Plugin — switches themes dynamically and persists preference\r\n */\r\nexport function themePlugin(options?: {\r\n defaultMode?: 'light' | 'dark';\r\n storageKey?: string;\r\n onThemeChange?: (mode: string, ctx: PluginContext) => void;\r\n cssVariable?: string;\r\n}): ChatPlugin {\r\n const storageKey = options?.storageKey ?? 'chatbot_theme';\r\n const store = createStorageAdapter('local');\r\n const cssVar = options?.cssVariable ?? '--cb-theme-mode';\r\n\r\n const applyTheme = (mode: string) => {\r\n if (typeof document !== 'undefined') {\r\n document.documentElement.style.setProperty(cssVar, mode);\r\n document.documentElement.setAttribute('data-chatbot-theme', mode);\r\n }\r\n };\r\n\r\n return {\r\n name: 'theme',\r\n\r\n onInit(ctx) {\r\n const saved = store.get(storageKey) ?? options?.defaultMode ?? 'light';\r\n applyTheme(saved);\r\n ctx.setData('theme', saved);\r\n\r\n ctx.on('theme:set', (...args: unknown[]) => {\r\n const mode = args[0] as string;\r\n if (mode) {\r\n store.set(storageKey, mode);\r\n applyTheme(mode);\r\n ctx.setData('theme', mode);\r\n options?.onThemeChange?.(mode, ctx);\r\n ctx.emit('theme:changed', { mode });\r\n }\r\n });\r\n\r\n ctx.on('theme:toggle', () => {\r\n const current = store.get(storageKey) ?? 'light';\r\n const next = current === 'light' ? 'dark' : 'light';\r\n ctx.emit('theme:set', next);\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\n/**\r\n * Component Plugin — injects custom component messages into chat via events\r\n */\r\nexport function componentPlugin(options?: {\r\n components?: Record<string, string>;\r\n onRender?: (componentKey: string, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n return {\r\n name: 'component',\r\n\r\n onInit(ctx) {\r\n // Listen for component injection requests\r\n ctx.on('component:inject', (...args: unknown[]) => {\r\n const key = args[0] as string;\r\n if (key) {\r\n ctx.emit('component:render', { component: key });\r\n options?.onRender?.(key, ctx);\r\n }\r\n });\r\n\r\n // Allow aliased rendering\r\n if (options?.components) {\r\n for (const [alias, componentKey] of Object.entries(options.components)) {\r\n ctx.on(`component:${alias}`, () => {\r\n ctx.emit('component:render', { component: componentKey });\r\n });\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\n/**\r\n * Lead Plugin — captures and stores user information as leads\r\n */\r\nexport function leadPlugin(options: {\r\n fields?: string[];\r\n endpoint?: string;\r\n headers?: Record<string, string>;\r\n storageKey?: string;\r\n onLeadCaptured?: (lead: Record<string, unknown>, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n const fields = new Set(options.fields ?? ['name', 'email', 'phone']);\r\n const store = createStorageAdapter('local');\r\n const storageKey = options.storageKey ?? 'chatbot_lead';\r\n\r\n const checkAndCapture = (data: Record<string, unknown>, ctx: PluginContext) => {\r\n const lead: Record<string, unknown> = {};\r\n let hasFields = false;\r\n\r\n for (const [key, value] of Object.entries(data)) {\r\n if (fields.has(key) && value) {\r\n lead[key] = value;\r\n hasFields = true;\r\n }\r\n }\r\n\r\n if (hasFields) {\r\n // Merge with existing lead data\r\n const existing = JSON.parse(store.get(storageKey) ?? '{}');\r\n const merged = { ...existing, ...lead, updatedAt: Date.now() };\r\n store.set(storageKey, JSON.stringify(merged));\r\n options.onLeadCaptured?.(merged, ctx);\r\n ctx.emit('lead:captured', merged);\r\n\r\n // Send to endpoint if configured\r\n if (options.endpoint) {\r\n postJSON(options.endpoint, merged, options.headers).catch(() => {});\r\n }\r\n }\r\n };\r\n\r\n return {\r\n name: 'lead',\r\n\r\n onSubmit(data, ctx) {\r\n checkAndCapture(data, ctx);\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'flowEnd' && event.payload) {\r\n checkAndCapture(event.payload as Record<string, unknown>, ctx);\r\n }\r\n if (event.type === 'login' && event.payload) {\r\n checkAndCapture(event.payload as Record<string, unknown>, ctx);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\ntype TriggerType = 'exitIntent' | 'idle' | 'scroll' | 'pageLoad' | 'custom';\r\n\r\ninterface CampaignConfig {\r\n trigger: TriggerType;\r\n message: string;\r\n delay?: number;\r\n maxShows?: number;\r\n flowStep?: string;\r\n}\r\n\r\n/**\r\n * Campaign Plugin — starts flows or shows messages based on user behavior triggers\r\n */\r\nexport function campaignPlugin(options: {\r\n campaigns: CampaignConfig[];\r\n onTrigger?: (campaign: CampaignConfig, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n const timers = new TimerManager();\r\n const showCounts = new Map<number, number>();\r\n\r\n return {\r\n name: 'campaign',\r\n\r\n onInit(ctx) {\r\n options.campaigns.forEach((campaign, idx) => {\r\n const maxShows = campaign.maxShows ?? 1;\r\n\r\n const fire = () => {\r\n const count = showCounts.get(idx) ?? 0;\r\n if (count >= maxShows) return;\r\n showCounts.set(idx, count + 1);\r\n\r\n ctx.addBotMessage(campaign.message);\r\n if (campaign.flowStep) {\r\n ctx.emit('campaign:flowStart', { step: campaign.flowStep });\r\n }\r\n options.onTrigger?.(campaign, ctx);\r\n ctx.emit('campaign:triggered', { trigger: campaign.trigger, idx });\r\n };\r\n\r\n switch (campaign.trigger) {\r\n case 'pageLoad':\r\n timers.setTimeout(`campaign_${idx}`, fire, campaign.delay ?? 0);\r\n break;\r\n\r\n case 'idle':\r\n timers.setTimeout(`campaign_${idx}`, fire, campaign.delay ?? 30000);\r\n break;\r\n\r\n case 'exitIntent':\r\n if (typeof document !== 'undefined') {\r\n const handler = (e: MouseEvent) => {\r\n if (e.clientY <= 0) {\r\n fire();\r\n document.removeEventListener('mouseleave', handler);\r\n }\r\n };\r\n document.addEventListener('mouseleave', handler);\r\n }\r\n break;\r\n\r\n case 'scroll':\r\n if (typeof window !== 'undefined') {\r\n const handler = () => {\r\n const scrollPct = window.scrollY / (document.body.scrollHeight - window.innerHeight);\r\n if (scrollPct > 0.7) {\r\n fire();\r\n window.removeEventListener('scroll', handler);\r\n }\r\n };\r\n window.addEventListener('scroll', handler, { passive: true });\r\n }\r\n break;\r\n\r\n case 'custom':\r\n ctx.on(`campaign:fire:${idx}`, fire);\r\n break;\r\n }\r\n });\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\ninterface ScheduledMessage {\r\n message: string;\r\n delay: number;\r\n repeat?: boolean;\r\n interval?: number;\r\n}\r\n\r\n/**\r\n * Scheduler Plugin — triggers bot messages at scheduled times or intervals\r\n */\r\nexport function schedulerPlugin(options: {\r\n messages?: ScheduledMessage[];\r\n onScheduledMessage?: (message: string) => void;\r\n}): ChatPlugin {\r\n const timers = new TimerManager();\r\n\r\n return {\r\n name: 'scheduler',\r\n\r\n onInit(ctx) {\r\n options.messages?.forEach((item, idx) => {\r\n const id = `sched_${idx}`;\r\n\r\n if (item.repeat && item.interval) {\r\n // Initial delay then repeat\r\n timers.setTimeout(`${id}_init`, () => {\r\n ctx.addBotMessage(item.message);\r\n options.onScheduledMessage?.(item.message);\r\n timers.setInterval(id, () => {\r\n ctx.addBotMessage(item.message);\r\n options.onScheduledMessage?.(item.message);\r\n }, item.interval!);\r\n }, item.delay);\r\n } else {\r\n // One-shot\r\n timers.setTimeout(id, () => {\r\n ctx.addBotMessage(item.message);\r\n options.onScheduledMessage?.(item.message);\r\n }, item.delay);\r\n }\r\n });\r\n\r\n // Allow dynamic scheduling\r\n ctx.on('scheduler:add', (...args: unknown[]) => {\r\n const msg = args[0] as ScheduledMessage;\r\n if (msg) {\r\n const id = `sched_dyn_${Date.now()}`;\r\n timers.setTimeout(id, () => {\r\n ctx.addBotMessage(msg.message);\r\n options.onScheduledMessage?.(msg.message);\r\n }, msg.delay);\r\n }\r\n });\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\n/**\r\n * Reminder Plugin — sends reminder messages to users after configured delays\r\n */\r\nexport function reminderPlugin(options?: {\r\n reminders?: Array<{ message: string; delay: number; condition?: string }>;\r\n onReminder?: (message: string) => void;\r\n}): ChatPlugin {\r\n const timers = new TimerManager();\r\n\r\n return {\r\n name: 'reminder',\r\n\r\n onInit(ctx) {\r\n // Static reminders\r\n options?.reminders?.forEach((r, idx) => {\r\n timers.setTimeout(`reminder_${idx}`, () => {\r\n ctx.addBotMessage(r.message);\r\n options?.onReminder?.(r.message);\r\n ctx.emit('reminder:sent', { message: r.message, idx });\r\n }, r.delay);\r\n });\r\n\r\n // Dynamic reminders via events\r\n ctx.on('reminder:set', (...args: unknown[]) => {\r\n const config = args[0] as { id: string; message: string; delay: number } | undefined;\r\n if (config) {\r\n timers.setTimeout(`reminder_${config.id}`, () => {\r\n ctx.addBotMessage(config.message);\r\n options?.onReminder?.(config.message);\r\n ctx.emit('reminder:sent', config);\r\n }, config.delay);\r\n }\r\n });\r\n\r\n ctx.on('reminder:cancel', (...args: unknown[]) => {\r\n const id = args[0] as string;\r\n if (id) timers.clearTimeout(`reminder_${id}`);\r\n });\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\ntype Translations = Record<string, Record<string, string>>;\r\n\r\n/**\r\n * i18n Plugin — supports multiple languages with dynamic switching\r\n */\r\nexport function i18nPlugin(options: {\r\n defaultLocale?: string;\r\n translations: Translations;\r\n storageKey?: string;\r\n onLocaleChange?: (locale: string, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n const store = createStorageAdapter('local');\r\n const storageKey = options.storageKey ?? 'chatbot_locale';\r\n let currentLocale = options.defaultLocale ?? 'en';\r\n\r\n const t = (key: string): string => {\r\n return options.translations[currentLocale]?.[key]\r\n ?? options.translations[options.defaultLocale ?? 'en']?.[key]\r\n ?? key;\r\n };\r\n\r\n return {\r\n name: 'i18n',\r\n\r\n onInit(ctx) {\r\n const saved = store.get(storageKey);\r\n if (saved && options.translations[saved]) {\r\n currentLocale = saved;\r\n }\r\n ctx.setData('locale', currentLocale);\r\n ctx.setData('t', t);\r\n\r\n ctx.on('i18n:setLocale', (...args: unknown[]) => {\r\n const locale = args[0] as string;\r\n if (locale && options.translations[locale]) {\r\n currentLocale = locale;\r\n store.set(storageKey, locale);\r\n ctx.setData('locale', locale);\r\n options.onLocaleChange?.(locale, ctx);\r\n ctx.emit('i18n:localeChanged', { locale });\r\n }\r\n });\r\n\r\n ctx.on('i18n:translate', (...args: unknown[]) => {\r\n const key = args[0] as string;\r\n if (key) ctx.emit('i18n:translated', { key, value: t(key) });\r\n });\r\n },\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text) {\r\n // Auto-translate bot messages that look like translation keys\r\n const translated = message.text.replace(/\\{\\{t:([^}]+)\\}\\}/g, (_:any, key: string) => t(key));\r\n if (translated !== message.text) {\r\n return { ...message, text: translated };\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Debug Plugin — exposes internal state and flow progression for debugging\r\n */\r\nexport function debugPlugin(options?: {\r\n logState?: boolean;\r\n logEvents?: boolean;\r\n logMessages?: boolean;\r\n groupName?: string;\r\n}): ChatPlugin {\r\n const group = options?.groupName ?? '🐛 ChatBot Debug';\r\n const logState = options?.logState ?? true;\r\n const logEvents = options?.logEvents ?? true;\r\n const logMessages = options?.logMessages ?? true;\r\n let eventLog: Array<{ type: string; payload?: unknown; time: number }> = [];\r\n\r\n return {\r\n name: 'debug',\r\n\r\n onInit(ctx) {\r\n console.group(group);\r\n console.log('Initialized');\r\n if (logState) {\r\n console.log('Messages:', ctx.getMessages().length);\r\n console.log('Data:', ctx.getData());\r\n }\r\n console.groupEnd();\r\n\r\n // Expose debug helpers on window for dev console access\r\n if (typeof window !== 'undefined') {\r\n (window as unknown as Record<string, unknown>).__chatbotDebug = {\r\n getMessages: () => ctx.getMessages(),\r\n getData: () => ctx.getData(),\r\n getEventLog: () => [...eventLog],\r\n sendMessage: (text: string) => ctx.sendMessage(text),\r\n emit: (event: string, ...args: unknown[]) => ctx.emit(event, ...args),\r\n };\r\n console.log(`${group}: window.__chatbotDebug available`);\r\n }\r\n },\r\n\r\n onMessage(message) {\r\n if (logMessages) {\r\n console.log(`${group} [${message.sender}]:`, message.text ?? '(no text)', message);\r\n }\r\n },\r\n\r\n onEvent(event) {\r\n eventLog.push({ type: event.type, payload: event.payload, time: Date.now() });\r\n if (logEvents) {\r\n console.log(`${group} Event [${event.type}]:`, event.payload ?? '');\r\n }\r\n },\r\n\r\n onDestroy() {\r\n console.log(`${group}: Destroyed. Total events logged:`, eventLog.length);\r\n eventLog = [];\r\n if (typeof window !== 'undefined') {\r\n delete (window as unknown as Record<string, unknown>).__chatbotDebug;\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\ninterface DevToolsState {\r\n messages: number;\r\n events: Array<{ type: string; time: number }>;\r\n data: Record<string, unknown>;\r\n currentStep: string | null;\r\n}\r\n\r\n/**\r\n * DevTools Plugin — provides a visual debugging panel overlay\r\n */\r\nexport function devtoolsPlugin(options?: {\r\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\r\n shortcutKey?: string;\r\n onStateUpdate?: (state: DevToolsState) => void;\r\n}): ChatPlugin {\r\n const pos = options?.position ?? 'top-right';\r\n const shortcutKey = options?.shortcutKey ?? 'F2';\r\n let panel: HTMLDivElement | null = null;\r\n let visible = false;\r\n let state: DevToolsState = { messages: 0, events: [], data: {}, currentStep: null };\r\n\r\n const createPanel = (): HTMLDivElement => {\r\n const div = document.createElement('div');\r\n div.id = 'chatbot-devtools';\r\n const posStyles: Record<string, string> = {\r\n 'top-left': 'top:8px;left:8px',\r\n 'top-right': 'top:8px;right:8px',\r\n 'bottom-left': 'bottom:80px;left:8px',\r\n 'bottom-right': 'bottom:80px;right:8px',\r\n };\r\n div.style.cssText = `\r\n position:fixed;${posStyles[pos]};z-index:99999;width:320px;max-height:400px;overflow:auto;\r\n background:#1a1a2e;color:#e0e0e0;font-family:monospace;font-size:11px;\r\n border-radius:8px;padding:12px;box-shadow:0 4px 20px rgba(0,0,0,0.4);display:none;\r\n `;\r\n document.body.appendChild(div);\r\n return div;\r\n };\r\n\r\n const renderPanel = () => {\r\n if (!panel) return;\r\n panel.innerHTML = `\r\n <div style=\"font-size:13px;font-weight:bold;margin-bottom:8px;color:#6c5ce7;\">🔧 ChatBot DevTools</div>\r\n <div style=\"margin-bottom:6px;\"><b>Messages:</b> ${state.messages}</div>\r\n <div style=\"margin-bottom:6px;\"><b>Step:</b> ${state.currentStep ?? 'none'}</div>\r\n <div style=\"margin-bottom:6px;\"><b>Data:</b><pre style=\"margin:4px 0;white-space:pre-wrap;font-size:10px;\">${JSON.stringify(state.data, null, 2)}</pre></div>\r\n <div><b>Recent Events (${state.events.length}):</b></div>\r\n ${state.events.slice(-10).map(e => `<div style=\"color:#888;font-size:10px;\">${e.type}</div>`).join('')}\r\n `;\r\n };\r\n\r\n const toggle = () => {\r\n if (!panel) panel = createPanel();\r\n visible = !visible;\r\n panel.style.display = visible ? 'block' : 'none';\r\n if (visible) renderPanel();\r\n };\r\n\r\n let keyHandler: ((e: KeyboardEvent) => void) | null = null;\r\n\r\n return {\r\n name: 'devtools',\r\n\r\n onInit(ctx) {\r\n if (typeof document === 'undefined') return;\r\n\r\n keyHandler = (e: KeyboardEvent) => {\r\n if (e.key === shortcutKey) toggle();\r\n };\r\n document.addEventListener('keydown', keyHandler);\r\n\r\n state.messages = ctx.getMessages().length;\r\n state.data = ctx.getData();\r\n },\r\n\r\n onMessage(message, ctx) {\r\n state.messages = ctx.getMessages().length + 1;\r\n state.data = ctx.getData();\r\n options?.onStateUpdate?.(state);\r\n if (visible) renderPanel();\r\n },\r\n\r\n onEvent(event, ctx: PluginContext) {\r\n state.events.push({ type: event.type, time: event.timestamp });\r\n if (event.type === 'stepChange') {\r\n state.currentStep = (event.payload as { stepId?: string })?.stepId ?? null;\r\n }\r\n state.data = ctx.getData();\r\n options?.onStateUpdate?.(state);\r\n if (visible) renderPanel();\r\n },\r\n\r\n onDestroy() {\r\n if (keyHandler) {\r\n document.removeEventListener('keydown', keyHandler);\r\n keyHandler = null;\r\n }\r\n panel?.remove();\r\n panel = null;\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\n/**\r\n * Media Plugin — adds support for rich media messages (images, videos, cards)\r\n */\r\nexport function mediaPlugin(options?: {\r\n allowedTypes?: ('image' | 'video' | 'audio' | 'card')[];\r\n onMediaRender?: (type: string, url: string) => void;\r\n}): ChatPlugin {\r\n const allowed = new Set(options?.allowedTypes ?? ['image', 'video', 'audio', 'card']);\r\n\r\n const processMediaTags = (text: string): string => {\r\n if (!text) return text;\r\n\r\n let result = text;\r\n\r\n // [image:url] → HTML img\r\n if (allowed.has('image')) {\r\n result = result.replace(/\\[image:([^\\]]+)\\]/g, (_, url: string) => {\r\n options?.onMediaRender?.('image', url);\r\n return `![image](${url})`;\r\n });\r\n }\r\n\r\n // [video:url] → markdown video link\r\n if (allowed.has('video')) {\r\n result = result.replace(/\\[video:([^\\]]+)\\]/g, (_, url: string) => {\r\n options?.onMediaRender?.('video', url);\r\n return `🎬 [Watch video](${url})`;\r\n });\r\n }\r\n\r\n // [audio:url] → markdown audio link\r\n if (allowed.has('audio')) {\r\n result = result.replace(/\\[audio:([^\\]]+)\\]/g, (_, url: string) => {\r\n options?.onMediaRender?.('audio', url);\r\n return `🔊 [Listen](${url})`;\r\n });\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return {\r\n name: 'media',\r\n\r\n onInit(ctx) {\r\n // Allow programmatic media injection\r\n ctx.on('media:send', (...args: unknown[]) => {\r\n const config = args[0] as { type: string; url: string; caption?: string } | undefined;\r\n if (config && allowed.has(config.type as 'image')) {\r\n const text = config.caption\r\n ? `${processMediaTags(`[${config.type}:${config.url}]`)}\\n${config.caption}`\r\n : processMediaTags(`[${config.type}:${config.url}]`);\r\n ctx.addBotMessage(text);\r\n }\r\n });\r\n },\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text) {\r\n const processed = processMediaTags(message.text);\r\n if (processed !== message.text) {\r\n return { ...message, text: processed };\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Markdown Plugin — transforms markdown syntax in bot messages to HTML\r\n * Lightweight built-in renderer, no external dependencies.\r\n */\r\nexport function markdownPlugin(options?: {\r\n enableBold?: boolean;\r\n enableItalic?: boolean;\r\n enableCode?: boolean;\r\n enableLinks?: boolean;\r\n enableLists?: boolean;\r\n enableLineBreaks?: boolean;\r\n}): ChatPlugin {\r\n const cfg = {\r\n bold: options?.enableBold ?? true,\r\n italic: options?.enableItalic ?? true,\r\n code: options?.enableCode ?? true,\r\n links: options?.enableLinks ?? true,\r\n lists: options?.enableLists ?? true,\r\n lineBreaks: options?.enableLineBreaks ?? true,\r\n };\r\n\r\n const renderMarkdown = (text: string): string => {\r\n let result = text;\r\n\r\n // Code blocks (``` ```)\r\n if (cfg.code) {\r\n result = result.replace(/```([^`]+)```/g, '<pre><code>$1</code></pre>');\r\n result = result.replace(/`([^`]+)`/g, '<code>$1</code>');\r\n }\r\n\r\n // Bold (**text** or __text__)\r\n if (cfg.bold) {\r\n result = result.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>');\r\n result = result.replace(/__([^_]+)__/g, '<strong>$1</strong>');\r\n }\r\n\r\n // Italic (*text* or _text_)\r\n if (cfg.italic) {\r\n result = result.replace(/\\*([^*]+)\\*/g, '<em>$1</em>');\r\n result = result.replace(/(?<!\\w)_([^_]+)_(?!\\w)/g, '<em>$1</em>');\r\n }\r\n\r\n // Links [text](url)\r\n if (cfg.links) {\r\n result = result.replace(\r\n /\\[([^\\]]+)\\]\\((https?:\\/\\/[^)]+)\\)/g,\r\n '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>',\r\n );\r\n }\r\n\r\n // Unordered lists\r\n if (cfg.lists) {\r\n result = result.replace(/^[•\\-\\*]\\s+(.+)$/gm, '<li>$1</li>');\r\n result = result.replace(/(<li>.*<\\/li>\\n?)+/g, '<ul>$&</ul>');\r\n }\r\n\r\n // Line breaks\r\n if (cfg.lineBreaks) {\r\n result = result.replace(/\\n/g, '<br>');\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return {\r\n name: 'markdown',\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text) {\r\n const rendered = renderMarkdown(message.text);\r\n if (rendered !== message.text) {\r\n return { ...message, text: rendered };\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport type { LiveAgentConfig, AgentInfo } from '../types/liveAgent';\r\nimport { LiveAgentAdapter } from '../core/LiveAgentAdapter';\r\nimport { WsDriver } from '../core/drivers/WsDriver';\r\nimport { SioDriver } from '../core/drivers/SioDriver';\r\nimport { DEFAULT_LIVE_AGENT_EVENTS } from '../types/liveAgent';\r\n\r\n/**\r\n * Live Agent Plugin — alternative to the `liveAgent` prop.\r\n * Use this when you prefer the plugin-based API over the built-in prop.\r\n *\r\n * @example\r\n * ```ts\r\n * import { io } from 'socket.io-client'\r\n *\r\n * const socket = io('https://support.example.com')\r\n *\r\n * plugins={[\r\n * liveAgentPlugin({\r\n * type: 'socketio',\r\n * instance: socket,\r\n * sessionId: 'user_123',\r\n * }),\r\n * ]}\r\n * ```\r\n */\r\nexport function liveAgentPlugin(config: LiveAgentConfig): ChatPlugin {\r\n let adapter: LiveAgentAdapter | null = null;\r\n\r\n const events = { ...DEFAULT_LIVE_AGENT_EVENTS, ...config.events };\r\n const sessionId = config.sessionId ?? `live_${Date.now()}`;\r\n\r\n return {\r\n name: 'liveAgent',\r\n\r\n onInit(ctx: PluginContext) {\r\n const driver =\r\n config.type === 'socketio'\r\n ? new SioDriver(config.instance)\r\n : new WsDriver(config.instance);\r\n\r\n adapter = new LiveAgentAdapter(driver, events, sessionId);\r\n\r\n // Listen for agent messages\r\n adapter.on(events.agentMessage, (data) => {\r\n const d = data as { text?: string; agent?: AgentInfo };\r\n if (d.text) {\r\n ctx.addBotMessage(d.text);\r\n }\r\n });\r\n\r\n // Listen for agent joined\r\n adapter.on(events.agentJoined, (data) => {\r\n const d = data as AgentInfo;\r\n ctx.addBotMessage(`🟢 ${d.name} joined the chat`);\r\n config.onAgentJoined?.(d);\r\n });\r\n\r\n // Listen for agent left\r\n adapter.on(events.agentLeft, (data) => {\r\n const d = data as AgentInfo;\r\n ctx.addBotMessage(`🔴 ${d.name} left the chat`);\r\n config.onAgentLeft?.(d);\r\n });\r\n\r\n // Listen for queue update\r\n adapter.on(events.queueUpdate, (data) => {\r\n const d = data as { position: number; estimatedWait?: number };\r\n const waitText = d.estimatedWait ? ` Estimated wait: ~${d.estimatedWait} min.` : '';\r\n ctx.addBotMessage(`You are #${d.position} in queue.${waitText}`);\r\n config.onQueueUpdate?.(d.position, d.estimatedWait);\r\n });\r\n\r\n // Listen for transfer request via plugin event bus\r\n ctx.on('liveAgent:transfer', (...args: unknown[]) => {\r\n const department = args[0] as string | undefined;\r\n adapter?.requestTransfer(department);\r\n });\r\n\r\n // Listen for send message via plugin event bus\r\n ctx.on('liveAgent:send', (...args: unknown[]) => {\r\n const text = args[0] as string;\r\n if (text) adapter?.sendUserMessage(text);\r\n });\r\n\r\n adapter.initSession();\r\n config.onConnect?.();\r\n },\r\n\r\n onMessage(message, ctx) {\r\n // Forward user messages to live agent\r\n if (message.sender === 'user' && adapter?.isConnected()) {\r\n adapter.sendUserMessage(message.text ?? '');\r\n }\r\n return message;\r\n },\r\n\r\n onDestroy() {\r\n adapter?.destroy();\r\n adapter = null;\r\n config.onDisconnect?.();\r\n },\r\n };\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/context/ChatContext.ts","../src/styles/theme.ts","../src/components/icons/Icons.tsx","../src/components/Launcher.tsx","../src/components/ChatHeader.tsx","../src/components/WelcomeScreen.tsx","../src/components/forms/TextField.tsx","../src/components/forms/SelectField.tsx","../src/components/forms/RadioField.tsx","../src/components/forms/CheckboxField.tsx","../src/components/forms/FileUploadField.tsx","../src/components/forms/DynamicForm.tsx","../src/components/LoginScreen.tsx","../src/utils/markdown.tsx","../src/components/MessageBubble.tsx","../src/components/QuickReplies.tsx","../src/components/TypingIndicator.tsx","../src/components/CarouselCards.tsx","../src/components/MessageList.tsx","../src/components/EmojiPicker.tsx","../src/components/FileUpload.tsx","../src/components/ChatInput.tsx","../src/components/Branding.tsx","../src/utils/helpers.ts","../src/engine/FlowEngine.ts","../src/core/LiveAgentAdapter.ts","../src/core/drivers/WsDriver.ts","../src/core/drivers/SioDriver.ts","../src/types/liveAgent.ts","../src/hooks/useLiveAgent.ts","../src/hooks/useChat.ts","../src/components/ChatWindow.tsx","../src/core/PluginManager.ts","../src/components/ChatBot.tsx","../src/core/EventBus.ts","../src/core/HeadlessBot.ts","../src/plugins/analyticsPlugin.ts","../src/plugins/loggerPlugin.ts","../src/plugins/utils/http.ts","../src/plugins/webhookPlugin.ts","../src/plugins/crmPlugin.ts","../src/plugins/emailPlugin.ts","../src/plugins/aiPlugin.ts","../src/plugins/intentPlugin.ts","../src/plugins/typingPlugin.ts","../src/plugins/utils/timer.ts","../src/plugins/autoReplyPlugin.ts","../src/plugins/validationPlugin.ts","../src/plugins/uploadPlugin.ts","../src/plugins/utils/storage.ts","../src/plugins/persistencePlugin.ts","../src/plugins/syncPlugin.ts","../src/plugins/authPlugin.ts","../src/plugins/rateLimitPlugin.ts","../src/plugins/pushPlugin.ts","../src/plugins/soundPlugin.ts","../src/plugins/agentPlugin.ts","../src/plugins/transferPlugin.ts","../src/plugins/themePlugin.ts","../src/plugins/componentPlugin.ts","../src/plugins/leadPlugin.ts","../src/plugins/campaignPlugin.ts","../src/plugins/schedulerPlugin.ts","../src/plugins/reminderPlugin.ts","../src/plugins/i18nPlugin.ts","../src/plugins/debugPlugin.ts","../src/plugins/devtoolsPlugin.ts","../src/plugins/mediaPlugin.ts","../src/plugins/markdownPlugin.ts","../src/plugins/liveAgentPlugin.ts","../src/plugins/tagsPlugin.ts","../src/plugins/ratingPlugin.ts","../src/plugins/offlinePlugin.ts","../src/plugins/proactivePlugin.ts","../src/plugins/personaPlugin.ts","../src/plugins/pinPlugin.ts","../src/plugins/themeTogglePlugin.ts","../src/plugins/confettiPlugin.ts","../src/plugins/priorityPlugin.ts","../src/plugins/whisperPlugin.ts","../src/plugins/messageSchedulePlugin.ts","../src/plugins/notificationBadgePlugin.ts","../src/plugins/summaryPlugin.ts","../src/plugins/knowledgeBasePlugin.ts","../src/plugins/translationPlugin.ts","../src/plugins/transcriptExportPlugin.ts","../src/plugins/codeHighlightPlugin.ts","../src/plugins/pollPlugin.ts","../src/plugins/paymentPlugin.ts","../src/plugins/bookingPlugin.ts","../src/plugins/locationPlugin.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\r\nimport type { ChatMessage, ChatBotProps } from '../types';\r\nimport type { PluginManager } from '../core/PluginManager';\r\nimport type { AgentInfo } from '../types/liveAgent';\r\n\r\nexport interface ChatState {\r\n isOpen: boolean;\r\n messages: ChatMessage[];\r\n isTyping: boolean;\r\n showWelcome: boolean;\r\n currentStepId: string | null;\r\n collectedData: Record<string, unknown>;\r\n isLoggedIn: boolean;\r\n isLiveAgent: boolean;\r\n agentInfo: AgentInfo | null;\r\n}\r\n\r\nexport type ChatAction =\r\n | { type: 'TOGGLE_OPEN' }\r\n | { type: 'SET_OPEN'; payload: boolean }\r\n | { type: 'ADD_MESSAGE'; payload: ChatMessage }\r\n | { type: 'ADD_MESSAGES'; payload: ChatMessage[] }\r\n | { type: 'SET_TYPING'; payload: boolean }\r\n | { type: 'DISMISS_WELCOME' }\r\n | { type: 'SET_STEP'; payload: string | null }\r\n | { type: 'SET_DATA'; payload: Record<string, unknown> }\r\n | { type: 'SET_LOGGED_IN'; payload: boolean }\r\n | { type: 'CLEAR_QUICK_REPLIES' }\r\n | { type: 'RESET_CHAT' }\r\n | { type: 'UPDATE_MESSAGE'; payload: { id: string; updates: Partial<ChatMessage> } }\r\n | { type: 'SET_LIVE_AGENT'; payload: boolean }\r\n | { type: 'SET_AGENT_INFO'; payload: AgentInfo | null };\r\n\r\nexport function chatReducer(state: ChatState, action: ChatAction): ChatState {\r\n switch (action.type) {\r\n case 'TOGGLE_OPEN':\r\n return { ...state, isOpen: !state.isOpen };\r\n case 'SET_OPEN':\r\n return { ...state, isOpen: action.payload };\r\n case 'ADD_MESSAGE':\r\n return { ...state, messages: [...state.messages, action.payload] };\r\n case 'ADD_MESSAGES':\r\n return { ...state, messages: [...state.messages, ...action.payload] };\r\n case 'SET_TYPING':\r\n return { ...state, isTyping: action.payload };\r\n case 'DISMISS_WELCOME':\r\n return { ...state, showWelcome: false };\r\n case 'SET_STEP':\r\n return { ...state, currentStepId: action.payload };\r\n case 'SET_DATA':\r\n return { ...state, collectedData: { ...state.collectedData, ...action.payload } };\r\n case 'SET_LOGGED_IN':\r\n return { ...state, isLoggedIn: action.payload };\r\n case 'CLEAR_QUICK_REPLIES': {\r\n // Only clear quick replies from the last message that has them\r\n let lastIdx = -1;\r\n for (let i = state.messages.length - 1; i >= 0; i--) {\r\n if (state.messages[i].quickReplies) { lastIdx = i; break; }\r\n }\r\n if (lastIdx === -1) return state;\r\n const updated = [...state.messages];\r\n updated[lastIdx] = { ...updated[lastIdx], quickReplies: undefined };\r\n return { ...state, messages: updated };\r\n }\r\n case 'RESET_CHAT':\r\n return {\r\n ...state,\r\n messages: [],\r\n isTyping: false,\r\n currentStepId: null,\r\n collectedData: {},\r\n };\r\n case 'UPDATE_MESSAGE':\r\n return {\r\n ...state,\r\n messages: state.messages.map((m) =>\r\n m.id === action.payload.id ? { ...m, ...action.payload.updates } : m,\r\n ),\r\n };\r\n case 'SET_LIVE_AGENT':\r\n return { ...state, isLiveAgent: action.payload };\r\n case 'SET_AGENT_INFO':\r\n return { ...state, agentInfo: action.payload };\r\n default:\r\n return state;\r\n }\r\n}\r\n\r\nexport const initialState = (props: ChatBotProps): ChatState => ({\r\n isOpen: props.defaultOpen ?? false,\r\n messages: props.initialMessages ?? [],\r\n isTyping: false,\r\n showWelcome: !!props.customizeChat?.welcomeScreen?.content,\r\n currentStepId: null,\r\n collectedData: {},\r\n isLoggedIn: !props.loginForm,\r\n isLiveAgent: false,\r\n agentInfo: null,\r\n});\r\n\r\ninterface ChatContextValue {\r\n state: ChatState;\r\n dispatch: React.Dispatch<ChatAction>;\r\n props: ChatBotProps;\r\n pluginManager?: PluginManager | null;\r\n}\r\n\r\nexport const ChatContext = createContext<ChatContextValue | null>(null);\r\n\r\nexport function useChatContext(): ChatContextValue {\r\n const ctx = useContext(ChatContext);\r\n if (!ctx) throw new Error('useChatContext must be used within ChatProvider');\r\n return ctx;\r\n}\r\n","import type { ChatTheme, ChatStyle } from '../types';\r\nimport type { CSSProperties } from 'react';\r\n\r\n// ─── Resolved Style Map ─────────────────────────────────────────\r\n\r\n/** Resolved inline styles for each chatbot section. Uses `CSSProperties` by reference\r\n * so the emitted `.d.ts` stays compact instead of expanding every CSS property. */\r\nexport interface ChatStyles {\r\n root: CSSProperties;\r\n launcher: CSSProperties;\r\n window: CSSProperties;\r\n header: CSSProperties;\r\n messageList: CSSProperties;\r\n inputArea: CSSProperties;\r\n botBubble: CSSProperties;\r\n userBubble: CSSProperties;\r\n}\r\n\r\n// ─── Light Mode Defaults ─────────────────────────────────────────\r\n\r\nconst lightDefaults: Required<ChatTheme> = {\r\n primaryColor: '#6C5CE7',\r\n headerBg: 'linear-gradient(135deg, #6C5CE7 0%, #A29BFE 100%)',\r\n headerText: '#FFFFFF',\r\n bubbleBg: 'rgba(241, 243, 249, 0.85)',\r\n bubbleText: '#2D3436',\r\n userBubbleBg: 'linear-gradient(135deg, #6C5CE7 0%, #A29BFE 100%)',\r\n userBubbleText: '#FFFFFF',\r\n fontFamily: '\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: '14px',\r\n borderRadius: '20px',\r\n windowWidth: '400px',\r\n windowHeight: '600px',\r\n mode: 'light',\r\n};\r\n\r\n// ─── Dark Mode Overrides ─────────────────────────────────────────\r\n\r\nconst darkOverrides: Partial<ChatTheme> = {\r\n headerBg: 'linear-gradient(135deg, #2D1B69 0%, #4A3298 100%)',\r\n headerText: '#F0F0FF',\r\n bubbleBg: 'rgba(45, 45, 70, 0.85)',\r\n bubbleText: '#E8E8F0',\r\n userBubbleBg: 'linear-gradient(135deg, #6C5CE7 0%, #A29BFE 100%)',\r\n userBubbleText: '#FFFFFF',\r\n};\r\n\r\nexport function resolveTheme(theme?: ChatTheme): Required<ChatTheme> {\r\n const base = { ...lightDefaults, ...theme };\r\n if (base.mode === 'dark') {\r\n return { ...base, ...darkOverrides, ...theme };\r\n }\r\n return base;\r\n}\r\n\r\n// ─── CSS Custom Properties ───────────────────────────────────────\r\n\r\nexport function buildCSSVariables(theme: Required<ChatTheme>): Record<string, string> {\r\n return {\r\n '--cb-primary': theme.primaryColor,\r\n '--cb-header-bg': theme.headerBg,\r\n '--cb-header-text': theme.headerText,\r\n '--cb-bubble-bg': theme.bubbleBg,\r\n '--cb-bubble-text': theme.bubbleText,\r\n '--cb-user-bubble-bg': theme.userBubbleBg,\r\n '--cb-user-bubble-text': theme.userBubbleText,\r\n '--cb-font-family': theme.fontFamily,\r\n '--cb-font-size': theme.fontSize,\r\n '--cb-border-radius': theme.borderRadius,\r\n '--cb-window-width': theme.windowWidth,\r\n '--cb-window-height': theme.windowHeight,\r\n '--cb-bg': theme.mode === 'dark' ? 'rgba(22, 22, 40, 0.95)' : 'rgba(255, 255, 255, 0.92)',\r\n '--cb-border': theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)',\r\n '--cb-input-bg': theme.mode === 'dark' ? 'rgba(40, 40, 65, 0.8)' : 'rgba(245, 247, 252, 0.9)',\r\n '--cb-input-border': theme.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.06)',\r\n '--cb-input-text': theme.mode === 'dark' ? '#E0E0E0' : '#2D3436',\r\n '--cb-branding-bg': theme.mode === 'dark' ? 'rgba(20, 20, 35, 0.8)' : 'rgba(250, 250, 255, 0.8)',\r\n };\r\n}\r\n\r\n// ─── Inline Styles Builder ───────────────────────────────────────\r\n\r\nexport function buildStyles(\r\n theme: Required<ChatTheme>,\r\n overrides?: ChatStyle,\r\n): ChatStyles {\r\n const isDark = theme.mode === 'dark';\r\n\r\n const styles = {\r\n root: {\r\n fontFamily: theme.fontFamily,\r\n fontSize: theme.fontSize,\r\n lineHeight: '1.5',\r\n } satisfies CSSProperties,\r\n\r\n launcher: {\r\n position: 'fixed',\r\n width: '62px',\r\n height: '62px',\r\n borderRadius: '50%',\r\n background: theme.headerBg,\r\n color: '#fff',\r\n border: 'none',\r\n cursor: 'pointer',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n boxShadow: `0 6px 24px rgba(108, 92, 231, 0.4), 0 2px 8px rgba(0,0,0,0.1)`,\r\n transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\r\n zIndex: 9998,\r\n ...overrides?.launcher,\r\n } satisfies CSSProperties,\r\n\r\n window: {\r\n position: 'fixed',\r\n width: theme.windowWidth,\r\n height: theme.windowHeight,\r\n maxHeight: '85vh',\r\n borderRadius: theme.borderRadius,\r\n overflow: 'hidden',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n boxShadow: isDark\r\n ? '0 20px 60px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.05)'\r\n : '0 20px 60px rgba(108, 92, 231, 0.15), 0 8px 24px rgba(0,0,0,0.08), 0 0 0 1px rgba(0,0,0,0.04)',\r\n backgroundColor: isDark ? 'rgba(22, 22, 40, 0.95)' : 'rgba(255, 255, 255, 0.95)',\r\n backdropFilter: 'blur(20px)',\r\n WebkitBackdropFilter: 'blur(20px)',\r\n border: isDark ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(255,255,255,0.8)',\r\n zIndex: 9999,\r\n animation: 'cb-window-enter 0.35s cubic-bezier(0.4, 0, 0.2, 1)',\r\n ...overrides?.window,\r\n } satisfies CSSProperties,\r\n\r\n header: {\r\n background: theme.headerBg,\r\n color: theme.headerText,\r\n padding: '18px 20px',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n gap: '12px',\r\n flexShrink: 0,\r\n position: 'relative',\r\n overflow: 'hidden',\r\n ...overrides?.header,\r\n } satisfies CSSProperties,\r\n\r\n messageList: {\r\n flex: 1,\r\n overflowY: 'auto',\r\n padding: '20px 16px',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: '10px',\r\n background: isDark\r\n ? 'linear-gradient(180deg, rgba(22, 22, 40, 0.98) 0%, rgba(30, 30, 50, 0.98) 100%)'\r\n : 'linear-gradient(180deg, rgba(248, 249, 254, 0.95) 0%, rgba(255, 255, 255, 0.95) 100%)',\r\n ...overrides?.messageList,\r\n } satisfies CSSProperties,\r\n\r\n inputArea: {\r\n padding: '12px 16px 14px',\r\n borderTop: `1px solid ${isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.05)'}`,\r\n backgroundColor: isDark ? 'rgba(20, 20, 38, 0.9)' : 'rgba(255, 255, 255, 0.95)',\r\n backdropFilter: 'blur(12px)',\r\n WebkitBackdropFilter: 'blur(12px)',\r\n flexShrink: 0,\r\n ...overrides?.inputArea,\r\n } satisfies CSSProperties,\r\n\r\n botBubble: {\r\n background: isDark ? 'rgba(45, 45, 70, 0.7)' : 'rgba(241, 243, 249, 0.9)',\r\n color: isDark ? '#E8E8F0' : '#2D3436',\r\n padding: '12px 16px',\r\n borderRadius: '18px 18px 18px 4px',\r\n maxWidth: '82%',\r\n alignSelf: 'flex-start',\r\n wordBreak: 'break-word',\r\n whiteSpace: 'pre-wrap',\r\n backdropFilter: 'blur(8px)',\r\n WebkitBackdropFilter: 'blur(8px)',\r\n border: isDark ? '1px solid rgba(255,255,255,0.06)' : '1px solid rgba(0,0,0,0.04)',\r\n boxShadow: isDark\r\n ? '0 2px 8px rgba(0,0,0,0.2)'\r\n : '0 2px 8px rgba(0,0,0,0.04)',\r\n fontSize: '14px',\r\n lineHeight: '1.55',\r\n letterSpacing: '0.01em',\r\n } satisfies CSSProperties,\r\n\r\n userBubble: {\r\n background: theme.userBubbleBg,\r\n color: theme.userBubbleText,\r\n padding: '12px 16px',\r\n borderRadius: '18px 18px 4px 18px',\r\n maxWidth: '82%',\r\n alignSelf: 'flex-end',\r\n wordBreak: 'break-word',\r\n whiteSpace: 'pre-wrap',\r\n boxShadow: '0 4px 14px rgba(108, 92, 231, 0.25)',\r\n fontSize: '14px',\r\n lineHeight: '1.55',\r\n letterSpacing: '0.01em',\r\n } satisfies CSSProperties,\r\n };\r\n\r\n return styles;\r\n}\r\n\r\n\r\n","import React from 'react';\n\ninterface IconProps {\n size?: number;\n color?: string;\n className?: string;\n}\n\nexport const SendIcon: React.FC<IconProps> = ({ size = 18, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill={color}>\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" />\n </svg>\n);\n\nexport const ChatBubbleIcon: React.FC<IconProps> = ({ size = 28, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\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\" />\n </svg>\n);\n\nexport const CloseIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\nexport const MinimizeIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n);\n\nexport const EmojiIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 14s1.5 2 4 2 4-2 4-2\" />\n <line x1=\"9\" y1=\"9\" x2=\"9.01\" y2=\"9\" />\n <line x1=\"15\" y1=\"9\" x2=\"15.01\" y2=\"9\" />\n </svg>\n);\n\nexport const AttachmentIcon: React.FC<IconProps> = ({ size = 20, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48\" />\n </svg>\n);\n\nexport const FileIcon: React.FC<IconProps> = ({ size = 16, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\" />\n <polyline points=\"14 2 14 8 20 8\" />\n </svg>\n);\n\nexport const ImageIcon: React.FC<IconProps> = ({ size = 16, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n);\n\nexport const RemoveIcon: React.FC<IconProps> = ({ size = 14, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\" />\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\" />\n </svg>\n);\n\nexport const RestartIcon: React.FC<IconProps> = ({ size = 16, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"1 4 1 10 7 10\" />\n <path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\" />\n </svg>\n);\n\nexport const SearchIcon: React.FC<IconProps> = ({ size = 16, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\" />\n </svg>\n);\n\nexport const MicIcon: React.FC<IconProps> = ({ size = 18, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\" />\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\" />\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\" />\n </svg>\n);\n\nexport const StarIcon: React.FC<IconProps> = ({ size = 18, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\" />\n </svg>\n);\n\nexport const EditIcon: React.FC<IconProps> = ({ size = 14, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" />\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\" />\n </svg>\n);\n\nexport const TrashIcon: React.FC<IconProps> = ({ size = 14, color = 'currentColor' }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </svg>\n);\n","import React from 'react';\nimport type { CSSProperties } from 'react';\nimport type { ChatStyles } from '../styles/theme';\nimport { ChatBubbleIcon, CloseIcon } from './icons';\nimport { useChatContext } from '../context/ChatContext';\n\ninterface LauncherProps {\n onClick: () => void;\n isOpen: boolean;\n position: 'bottom-right' | 'bottom-left';\n styles: ChatStyles;\n icon?: React.ReactNode;\n closeIcon?: React.ReactNode;\n zIndex?: number;\n}\n\nexport const Launcher: React.FC<LauncherProps> = ({\n onClick,\n isOpen,\n position,\n styles,\n icon,\n closeIcon,\n zIndex,\n}) => {\n const { props: chatProps } = useChatContext();\n const icons = chatProps.icons;\n const posStyle: CSSProperties =\n position === 'bottom-left'\n ? { bottom: '24px', left: '24px' }\n : { bottom: '24px', right: '24px' };\n\n return (\n <button\n onClick={onClick}\n aria-label={isOpen ? 'Close chat' : 'Open chat'}\n style={{\n ...styles.launcher,\n ...posStyle,\n ...(zIndex != null ? { zIndex } : {}),\n transform: isOpen ? 'scale(0.92) rotate(90deg)' : 'scale(1)',\n animation: isOpen ? 'none' : 'cb-launcher-pulse 3s ease-in-out infinite',\n }}\n >\n {isOpen\n ? closeIcon ?? icons?.close ?? <CloseIcon size={22} />\n : icon ?? icons?.chatBubble ?? <ChatBubbleIcon size={26} />}\n </button>\n );\n};\n","import React, { useState } from 'react';\nimport type { HeaderConfig } from '../types';\nimport type { ChatStyles as ThemeStyles } from '../styles/theme';\nimport { CloseIcon, MinimizeIcon, RestartIcon, SearchIcon } from './icons';\nimport { useChatContext } from '../context/ChatContext';\n\ninterface ChatHeaderProps {\n config: HeaderConfig;\n styles: ThemeStyles;\n onClose: () => void;\n onRestart?: () => void;\n logo?: string;\n logoWidth?: string;\n onSearchChange?: (query: string) => void;\n}\n\nexport const ChatHeader: React.FC<ChatHeaderProps> = ({ config, styles, onClose, onRestart, logo, logoWidth, onSearchChange }) => {\n const { props: chatProps } = useChatContext();\n const icons = chatProps.icons;\n const [searchOpen, setSearchOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n\n const toggleSearch = () => {\n const next = !searchOpen;\n setSearchOpen(next);\n if (!next) { setSearchQuery(''); onSearchChange?.(''); }\n };\n\n const handleSearchInput = (val: string) => {\n setSearchQuery(val);\n onSearchChange?.(val);\n };\n\n return (\n <div style={styles.header}>\n {/* Decorative glow overlay */}\n <div\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: 'linear-gradient(135deg, rgba(255,255,255,0.12) 0%, transparent 50%)',\n pointerEvents: 'none',\n }}\n />\n <div style={{ display: 'flex', alignItems: 'center', gap: '12px', flex: 1, position: 'relative', zIndex: 1 }}>\n {config.avatar && (\n <div style={{ position: 'relative' }}>\n <img\n src={config.avatar}\n alt=\"\"\n style={{\n width: '40px',\n height: '40px',\n borderRadius: '50%',\n objectFit: 'cover',\n border: '2px solid rgba(255,255,255,0.3)',\n }}\n />\n <span\n style={{\n position: 'absolute',\n bottom: '1px',\n right: '1px',\n width: '10px',\n height: '10px',\n backgroundColor: '#2ECC71',\n borderRadius: '50%',\n border: '2px solid rgba(255,255,255,0.8)',\n }}\n />\n </div>\n )}\n {logo && !config.avatar && (\n <img\n src={logo}\n alt=\"\"\n style={{ width: logoWidth ?? '36px', height: 'auto', objectFit: 'contain', filter: 'brightness(1.1)' }}\n />\n )}\n {!config.avatar && !logo && (\n <div\n style={{\n width: '40px',\n height: '40px',\n borderRadius: '50%',\n background: 'rgba(255,255,255,0.2)',\n backdropFilter: 'blur(4px)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '18px',\n fontWeight: 700,\n color: 'rgba(255,255,255,0.9)',\n border: '1px solid rgba(255,255,255,0.2)',\n }}\n >\n {(config.title ?? 'C').charAt(0).toUpperCase()}\n </div>\n )}\n <div>\n <div style={{ fontWeight: 600, fontSize: '16px', letterSpacing: '-0.01em' }}>\n {config.title ?? 'Chat with us'}\n </div>\n {config.subtitle && (\n <div style={{\n fontSize: '12px',\n opacity: 0.8,\n display: 'flex',\n alignItems: 'center',\n gap: '5px',\n marginTop: '1px',\n }}>\n <span style={{\n width: '6px',\n height: '6px',\n borderRadius: '50%',\n backgroundColor: '#2ECC71',\n display: 'inline-block',\n }} />\n {config.subtitle}\n </div>\n )}\n </div>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '2px', position: 'relative', zIndex: 1 }}>\n {chatProps.enableSearch && (\n <button\n onClick={toggleSearch}\n aria-label=\"Search messages\"\n title=\"Search messages\"\n style={{\n background: searchOpen ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.1)',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '8px',\n transition: 'background 0.2s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.2)')}\n onMouseLeave={(e) => (e.currentTarget.style.background = searchOpen ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.1)')}\n >\n {icons?.search ?? <SearchIcon size={15} />}\n </button>\n )}\n {config.showRestart && onRestart && (\n <button\n onClick={onRestart}\n aria-label=\"Restart conversation\"\n title=\"Restart conversation\"\n style={{\n background: 'rgba(255,255,255,0.1)',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '8px',\n transition: 'background 0.2s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.2)')}\n onMouseLeave={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.1)')}\n >\n {icons?.restart ?? <RestartIcon size={16} />}\n </button>\n )}\n {config.showMinimize && (\n <button\n onClick={onClose}\n aria-label=\"Minimize chat\"\n style={{\n background: 'rgba(255,255,255,0.1)',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '8px',\n transition: 'background 0.2s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.2)')}\n onMouseLeave={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.1)')}\n >\n {icons?.minimize ?? <MinimizeIcon size={16} />}\n </button>\n )}\n {config.showClose !== false && (\n <button\n onClick={onClose}\n aria-label=\"Close chat\"\n style={{\n background: 'rgba(255,255,255,0.1)',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '8px',\n transition: 'background 0.2s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.2)')}\n onMouseLeave={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.1)')}\n >\n {icons?.close ?? <CloseIcon size={18} />}\n </button>\n )}\n </div>\n {/* Search bar */}\n {searchOpen && (\n <div style={{ position: 'absolute', bottom: '-40px', left: 0, right: 0, padding: '6px 12px', background: 'inherit', zIndex: 5 }}>\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => handleSearchInput(e.target.value)}\n placeholder=\"Search messages...\"\n autoFocus\n style={{\n width: '100%', padding: '6px 10px', borderRadius: '8px',\n border: '1px solid rgba(255,255,255,0.2)', background: 'rgba(255,255,255,0.15)',\n color: 'inherit', fontSize: '13px', outline: 'none',\n }}\n />\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport type { ReactNode } from 'react';\n\ninterface WelcomeScreenProps {\n content: ReactNode;\n onDismiss: () => void;\n primaryColor: string;\n}\n\nexport const WelcomeScreen: React.FC<WelcomeScreenProps> = ({ content, onDismiss, primaryColor }) => {\n return (\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'auto',\n background: 'linear-gradient(180deg, rgba(248, 249, 254, 0.95) 0%, rgba(255, 255, 255, 0.98) 100%)',\n }}\n >\n <div style={{ flex: 1, padding: '28px 24px', overflow: 'auto' }}>\n {content}\n </div>\n <div\n style={{\n padding: '16px 20px',\n borderTop: '1px solid rgba(0,0,0,0.05)',\n backdropFilter: 'blur(12px)',\n WebkitBackdropFilter: 'blur(12px)',\n flexShrink: 0,\n }}\n >\n <button\n onClick={onDismiss}\n style={{\n width: '100%',\n padding: '14px',\n background: `linear-gradient(135deg, ${primaryColor} 0%, ${adjustColor(primaryColor, 30)} 100%)`,\n color: '#fff',\n border: 'none',\n borderRadius: '14px',\n fontSize: '15px',\n fontWeight: 600,\n cursor: 'pointer',\n fontFamily: 'inherit',\n letterSpacing: '0.02em',\n boxShadow: `0 6px 20px ${primaryColor}44`,\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.transform = 'translateY(-1px)';\n e.currentTarget.style.boxShadow = `0 8px 28px ${primaryColor}55`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.transform = 'translateY(0)';\n e.currentTarget.style.boxShadow = `0 6px 20px ${primaryColor}44`;\n }}\n >\n Start Chat\n </button>\n </div>\n </div>\n );\n};\n\nfunction adjustColor(hex: string, amount: number): string {\n const num = parseInt(hex.replace('#', ''), 16);\n const r = Math.min(255, ((num >> 16) & 0xff) + amount);\n const g = Math.min(255, ((num >> 8) & 0xff) + amount);\n const b = Math.min(255, (num & 0xff) + amount);\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;\n}\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface TextFieldProps {\n field: FormFieldConfig;\n value: string;\n onChange: (value: string) => void;\n error?: string;\n}\n\nexport const TextField: React.FC<TextFieldProps> = ({ field, value, onChange, error }) => {\n const isTextarea = field.type === 'textarea';\n const inputType = field.type === 'textarea' ? undefined : field.type === 'datetime' ? 'datetime-local' : field.type;\n\n const baseStyle: React.CSSProperties = {\n width: '100%',\n padding: '10px 14px',\n border: `1.5px solid ${error ? 'rgba(229, 62, 62, 0.5)' : 'rgba(0,0,0,0.08)'}`,\n borderRadius: '12px',\n fontSize: '13px',\n fontFamily: 'inherit',\n outline: 'none',\n boxSizing: 'border-box',\n transition: 'all 0.2s ease',\n backgroundColor: 'rgba(245, 247, 252, 0.6)',\n color: '#2D3436',\n letterSpacing: '0.01em',\n };\n\n return (\n <div style={{ marginBottom: '14px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500, color: '#2D3436' }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '3px' }}>*</span>}\n </label>\n )}\n {isTextarea ? (\n <textarea\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={field.placeholder}\n required={field.required}\n rows={3}\n style={{ ...baseStyle, resize: 'vertical' }}\n minLength={field.validation?.minLength}\n maxLength={field.validation?.maxLength}\n />\n ) : (\n <input\n type={inputType}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={field.placeholder}\n required={field.required}\n style={baseStyle}\n min={field.validation?.min}\n max={field.validation?.max}\n minLength={field.validation?.minLength}\n maxLength={field.validation?.maxLength}\n pattern={field.validation?.pattern}\n />\n )}\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface SelectFieldProps {\n field: FormFieldConfig;\n value: string | string[];\n onChange: (value: string | string[]) => void;\n error?: string;\n}\n\nexport const SelectField: React.FC<SelectFieldProps> = ({ field, value, onChange, error }) => {\n const isMulti = field.type === 'multiselect' || field.multiple;\n\n const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\n if (isMulti) {\n const selected = Array.from(e.target.selectedOptions, (opt) => opt.value);\n onChange(selected);\n } else {\n onChange(e.target.value);\n }\n };\n\n const selectValue = isMulti\n ? Array.isArray(value) ? value : [value].filter(Boolean)\n : typeof value === 'string' ? value : '';\n\n return (\n <div style={{ marginBottom: '14px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500, color: '#2D3436' }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '3px' }}>*</span>}\n </label>\n )}\n <select\n value={selectValue}\n onChange={handleChange}\n multiple={isMulti}\n required={field.required}\n style={{\n width: '100%',\n padding: '10px 14px',\n border: `1.5px solid ${error ? 'rgba(229, 62, 62, 0.5)' : 'rgba(0,0,0,0.08)'}`,\n borderRadius: '12px',\n fontSize: '13px',\n fontFamily: 'inherit',\n outline: 'none',\n backgroundColor: 'rgba(245, 247, 252, 0.6)',\n color: '#2D3436',\n boxSizing: 'border-box',\n transition: 'all 0.2s ease',\n ...(isMulti ? { minHeight: '80px' } : {}),\n }}\n >\n {!isMulti && <option value=\"\">Select...</option>}\n {field.options?.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n {isMulti && (\n <div style={{ fontSize: '11px', color: '#888', marginTop: '2px' }}>\n Hold Ctrl/Cmd to select multiple\n </div>\n )}\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface RadioFieldProps {\n field: FormFieldConfig;\n value: string;\n onChange: (value: string) => void;\n error?: string;\n}\n\nexport const RadioField: React.FC<RadioFieldProps> = ({ field, value, onChange, error }) => {\n return (\n <div style={{ marginBottom: '12px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500 }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '2px' }}>*</span>}\n </label>\n )}\n <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>\n {field.options?.map((opt) => (\n <label\n key={opt.value}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n <input\n type=\"radio\"\n name={field.name}\n value={opt.value}\n checked={value === opt.value}\n onChange={() => onChange(opt.value)}\n style={{ margin: 0 }}\n />\n {opt.label}\n </label>\n ))}\n </div>\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface CheckboxFieldProps {\n field: FormFieldConfig;\n value: string[];\n onChange: (value: string[]) => void;\n error?: string;\n}\n\nexport const CheckboxField: React.FC<CheckboxFieldProps> = ({ field, value, onChange, error }) => {\n const handleToggle = (optValue: string) => {\n if (value.includes(optValue)) {\n onChange(value.filter((v) => v !== optValue));\n } else {\n onChange([...value, optValue]);\n }\n };\n\n return (\n <div style={{ marginBottom: '12px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: 500 }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '2px' }}>*</span>}\n </label>\n )}\n <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>\n {field.options?.map((opt) => (\n <label\n key={opt.value}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: 'pointer',\n fontSize: '13px',\n }}\n >\n <input\n type=\"checkbox\"\n checked={value.includes(opt.value)}\n onChange={() => handleToggle(opt.value)}\n style={{ margin: 0 }}\n />\n {opt.label}\n </label>\n ))}\n </div>\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React, { useRef } from 'react';\nimport type { FormFieldConfig } from '../../types';\n\ninterface FileUploadFieldProps {\n field: FormFieldConfig;\n value: FileList | null;\n onChange: (files: FileList | null) => void;\n error?: string;\n primaryColor: string;\n}\n\nexport const FileUploadField: React.FC<FileUploadFieldProps> = ({\n field,\n value,\n onChange,\n error,\n primaryColor,\n}) => {\n const inputRef = useRef<HTMLInputElement>(null);\n\n const fileNames = value ? Array.from(value).map((f) => f.name).join(', ') : '';\n\n return (\n <div style={{ marginBottom: '12px' }}>\n {field.label && (\n <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px', fontWeight: 500 }}>\n {field.label}\n {field.required && <span style={{ color: '#E53E3E', marginLeft: '2px' }}>*</span>}\n </label>\n )}\n <input\n ref={inputRef}\n type=\"file\"\n accept={field.accept}\n multiple={field.multiple}\n onChange={(e) => onChange(e.target.files)}\n style={{ display: 'none' }}\n />\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n style={{\n padding: '8px 16px',\n border: `1px dashed ${error ? '#E53E3E' : '#D1D5DB'}`,\n borderRadius: '8px',\n backgroundColor: '#FAFAFA',\n cursor: 'pointer',\n fontSize: '13px',\n color: '#555',\n width: '100%',\n textAlign: 'left',\n }}\n >\n {fileNames || field.placeholder || 'Choose file(s)...'}\n </button>\n {fileNames && (\n <div style={{ fontSize: '12px', color: primaryColor, marginTop: '4px' }}>\n {Array.from(value!).length} file(s) selected\n </div>\n )}\n {error && <div style={{ color: '#E53E3E', fontSize: '12px', marginTop: '2px' }}>{error}</div>}\n </div>\n );\n};\n","import React, { useState, useCallback } from 'react';\r\nimport type { FormConfig, FormFieldConfig } from '../../types';\r\nimport type { FormFieldRenderMap } from '../../types/form';\r\nimport { TextField } from './TextField';\r\nimport { SelectField } from './SelectField';\r\nimport { RadioField } from './RadioField';\r\nimport { CheckboxField } from './CheckboxField';\r\nimport { FileUploadField } from './FileUploadField';\r\n\r\ninterface DynamicFormProps {\r\n config: FormConfig;\r\n onSubmit: (data: Record<string, unknown>) => void;\r\n primaryColor: string;\r\n renderFormField?: FormFieldRenderMap;\r\n}\r\n\r\nexport const DynamicForm: React.FC<DynamicFormProps> = ({ config, onSubmit, primaryColor, renderFormField }) => {\r\n const [values, setValues] = useState<Record<string, unknown>>(() => {\r\n const init: Record<string, unknown> = {};\r\n for (const field of config.fields) {\r\n if (field.defaultValue !== undefined) {\r\n init[field.name] = field.defaultValue;\r\n } else if (field.type === 'checkbox' || field.type === 'multiselect') {\r\n init[field.name] = [];\r\n } else if (field.type === 'file') {\r\n init[field.name] = null;\r\n } else {\r\n init[field.name] = '';\r\n }\r\n }\r\n return init;\r\n });\r\n\r\n const [errors, setErrors] = useState<Record<string, string>>({});\r\n const [submitted, setSubmitted] = useState(false);\r\n\r\n const setValue = useCallback((name: string, value: unknown) => {\r\n setValues((prev) => ({ ...prev, [name]: value }));\r\n setErrors((prev) => {\r\n const next = { ...prev };\r\n delete next[name];\r\n return next;\r\n });\r\n }, []);\r\n\r\n const validate = (): boolean => {\r\n const newErrors: Record<string, string> = {};\r\n\r\n for (const field of config.fields) {\r\n const val = values[field.name];\r\n\r\n // Required check\r\n if (field.required) {\r\n if (\r\n val === '' ||\r\n val === null ||\r\n val === undefined ||\r\n (Array.isArray(val) && val.length === 0)\r\n ) {\r\n newErrors[field.name] = field.validation?.message ?? `${field.label || field.name} is required`;\r\n continue;\r\n }\r\n }\r\n\r\n // Pattern check\r\n if (field.validation?.pattern && typeof val === 'string' && val) {\r\n try {\r\n const regex = new RegExp(field.validation.pattern);\r\n if (!regex.test(val)) {\r\n newErrors[field.name] = field.validation.message ?? 'Invalid format';\r\n }\r\n } catch {\r\n // Invalid regex pattern in config — skip validation\r\n }\r\n }\r\n }\r\n\r\n setErrors(newErrors);\r\n return Object.keys(newErrors).length === 0;\r\n };\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (!validate()) return;\r\n setSubmitted(true);\r\n onSubmit(values);\r\n };\r\n\r\n if (submitted) {\r\n return (\r\n <div\r\n style={{\r\n padding: '16px',\r\n background: 'linear-gradient(135deg, rgba(46, 213, 115, 0.1), rgba(46, 213, 115, 0.05))',\r\n borderRadius: '14px',\r\n fontSize: '14px',\r\n color: '#2ecc71',\r\n textAlign: 'center',\r\n fontWeight: 500,\r\n border: '1px solid rgba(46, 213, 115, 0.15)',\r\n animation: 'cb-fade-in 0.3s ease-out',\r\n }}\r\n >\r\n ✓ Submitted successfully\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <form\r\n onSubmit={handleSubmit}\r\n style={{\r\n background: 'rgba(255, 255, 255, 0.7)',\r\n backdropFilter: 'blur(12px)',\r\n WebkitBackdropFilter: 'blur(12px)',\r\n borderRadius: '16px',\r\n padding: '20px',\r\n border: '1px solid rgba(0,0,0,0.06)',\r\n boxShadow: '0 4px 16px rgba(0,0,0,0.04)',\r\n animation: 'cb-slide-up 0.35s ease-out',\r\n }}\r\n >\r\n {config.title && (\r\n <div style={{ fontWeight: 600, fontSize: '15px', marginBottom: '4px', color: '#2D3436', letterSpacing: '-0.01em' }}>\r\n {config.title}\r\n </div>\r\n )}\r\n {config.description && (\r\n <div style={{ fontSize: '12px', color: 'rgba(0,0,0,0.45)', marginBottom: '16px', lineHeight: '1.5' }}>\r\n {config.description}\r\n </div>\r\n )}\r\n\r\n {config.fields.map((field) => (\r\n <FormField\r\n key={field.name}\r\n field={field}\r\n value={values[field.name]}\r\n onChange={(v) => setValue(field.name, v)}\r\n error={errors[field.name]}\r\n primaryColor={primaryColor}\r\n renderFormField={renderFormField}\r\n />\r\n ))}\r\n\r\n <button\r\n type=\"submit\"\r\n style={{\r\n width: '100%',\r\n padding: '12px',\r\n background: `linear-gradient(135deg, ${primaryColor} 0%, ${adjustColor(primaryColor, 30)} 100%)`,\r\n color: '#fff',\r\n border: 'none',\r\n borderRadius: '12px',\r\n fontSize: '14px',\r\n fontWeight: 600,\r\n cursor: 'pointer',\r\n marginTop: '8px',\r\n fontFamily: 'inherit',\r\n letterSpacing: '0.02em',\r\n boxShadow: `0 4px 14px ${primaryColor}33`,\r\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = 'translateY(-1px)';\r\n e.currentTarget.style.boxShadow = `0 6px 20px ${primaryColor}44`;\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = 'translateY(0)';\r\n e.currentTarget.style.boxShadow = `0 4px 14px ${primaryColor}33`;\r\n }}\r\n >\r\n {config.submitLabel ?? 'Submit'}\r\n </button>\r\n </form>\r\n );\r\n};\r\n\r\n// ─── Field Router ────────────────────────────────────────────────\r\n\r\ninterface FormFieldProps {\r\n field: FormFieldConfig;\r\n value: unknown;\r\n onChange: (value: unknown) => void;\r\n error?: string;\r\n primaryColor: string;\r\n renderFormField?: FormFieldRenderMap;\r\n}\r\n\r\nconst FormField: React.FC<FormFieldProps> = ({ field, value, onChange, error, primaryColor, renderFormField }) => {\r\n // Check for custom renderer override\r\n const customRenderer = renderFormField?.[field.type as keyof FormFieldRenderMap];\r\n\r\n switch (field.type) {\r\n case 'text':\r\n case 'email':\r\n case 'password':\r\n case 'number':\r\n case 'tel':\r\n case 'url':\r\n case 'textarea':\r\n case 'date':\r\n case 'time':\r\n case 'datetime':\r\n case 'color':\r\n case 'range': {\r\n const typedProps = { type: field.type as 'text', field, value: String(value ?? ''), onChange: onChange as (v: string) => void, error };\r\n const defaultEl = <TextField field={field} value={String(value ?? '')} onChange={onChange as (v: string) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'select':\r\n case 'multiselect': {\r\n const typedProps = { type: field.type as 'select', field, value: value as string | string[], onChange: onChange as (v: string | string[]) => void, error };\r\n const defaultEl = <SelectField field={field} value={value as string | string[]} onChange={onChange as (v: string | string[]) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'radio': {\r\n const typedProps = { type: 'radio' as const, field, value: String(value ?? ''), onChange: onChange as (v: string) => void, error };\r\n const defaultEl = <RadioField field={field} value={String(value ?? '')} onChange={onChange as (v: string) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'checkbox': {\r\n const typedProps = { type: 'checkbox' as const, field, value: ((value as string[]) ?? []), onChange: onChange as (v: string[]) => void, error };\r\n const defaultEl = <CheckboxField field={field} value={(value as string[]) ?? []} onChange={onChange as (v: string[]) => void} error={error} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'file': {\r\n const typedProps = { type: 'file' as const, field, value: value as FileList | null, onChange: onChange as (v: FileList | null) => void, error, primaryColor };\r\n const defaultEl = <FileUploadField field={field} value={value as FileList | null} onChange={onChange as (v: FileList | null) => void} error={error} primaryColor={primaryColor} />;\r\n if (customRenderer) return <>{(customRenderer as (p: typeof typedProps, d: React.ReactNode) => React.ReactNode)(typedProps, defaultEl)}</>;\r\n return defaultEl;\r\n }\r\n case 'hidden':\r\n return <input type=\"hidden\" name={field.name} value={String(value ?? '')} />;\r\n default:\r\n return null;\r\n }\r\n};\r\n\r\nfunction adjustColor(hex: string, amount: number): string {\r\n const num = parseInt(hex.replace('#', ''), 16);\r\n const r = Math.min(255, ((num >> 16) & 0xff) + amount);\r\n const g = Math.min(255, ((num >> 8) & 0xff) + amount);\r\n const b = Math.min(255, (num & 0xff) + amount);\r\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;\r\n}\r\n","import React from 'react';\nimport type { FormConfig } from '../types';\nimport type { FormFieldRenderMap } from '../types/form';\nimport { DynamicForm } from './forms/DynamicForm';\n\ninterface LoginScreenProps {\n config: FormConfig;\n onLogin: (data: Record<string, unknown>) => void;\n primaryColor: string;\n renderFormField?: FormFieldRenderMap;\n}\n\nexport const LoginScreen: React.FC<LoginScreenProps> = ({ config, onLogin, primaryColor, renderFormField }) => {\n return (\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n padding: '24px',\n overflow: 'auto',\n background: 'linear-gradient(180deg, rgba(248, 249, 254, 0.95) 0%, rgba(255, 255, 255, 0.98) 100%)',\n }}\n >\n <DynamicForm config={config} onSubmit={onLogin} primaryColor={primaryColor} renderFormField={renderFormField} />\n </div>\n );\n};\n","import React from 'react';\r\nimport type { MarkdownOptions } from '../types/config';\r\n\r\n/**\r\n * Lightweight markdown-to-JSX renderer. No external dependencies.\r\n * Supports: bold, italic, code (inline + block), links, lists,\r\n * strikethrough, headings, and line breaks.\r\n */\r\nexport function renderMarkdown(\r\n text: string,\r\n options: MarkdownOptions = {},\r\n): React.ReactNode {\r\n const cfg: Required<MarkdownOptions> = {\r\n bold: options.bold ?? true,\r\n italic: options.italic ?? true,\r\n code: options.code ?? true,\r\n links: options.links ?? true,\r\n lists: options.lists ?? true,\r\n strikethrough: options.strikethrough ?? true,\r\n headings: options.headings ?? false,\r\n };\r\n\r\n // Split by code blocks first to avoid processing markdown inside them\r\n const parts = text.split(/(```[\\s\\S]*?```)/g);\r\n const elements: React.ReactNode[] = [];\r\n\r\n parts.forEach((part, idx) => {\r\n if (cfg.code && part.startsWith('```') && part.endsWith('```')) {\r\n const code = part.slice(3, -3).replace(/^\\w*\\n/, ''); // strip optional language tag\r\n elements.push(\r\n <pre key={idx} style={{ background: 'rgba(0,0,0,0.06)', borderRadius: '6px', padding: '8px 12px', overflowX: 'auto', fontSize: '13px', margin: '4px 0' }}>\r\n <code>{code}</code>\r\n </pre>,\r\n );\r\n } else {\r\n elements.push(...renderBlock(part, cfg, idx));\r\n }\r\n });\r\n\r\n return <>{elements}</>;\r\n}\r\n\r\nfunction renderBlock(\r\n text: string,\r\n cfg: Required<MarkdownOptions>,\r\n blockKey: number,\r\n): React.ReactNode[] {\r\n const lines = text.split('\\n');\r\n const result: React.ReactNode[] = [];\r\n let listItems: React.ReactNode[] = [];\r\n let listKey = 0;\r\n\r\n const flushList = () => {\r\n if (listItems.length > 0) {\r\n result.push(\r\n <ul key={`${blockKey}-ul-${listKey++}`} style={{ margin: '4px 0', paddingLeft: '20px' }}>\r\n {listItems}\r\n </ul>,\r\n );\r\n listItems = [];\r\n }\r\n };\r\n\r\n lines.forEach((line, lineIdx) => {\r\n const key = `${blockKey}-${lineIdx}`;\r\n\r\n // Headings\r\n if (cfg.headings) {\r\n const headingMatch = line.match(/^(#{1,3})\\s+(.+)$/);\r\n if (headingMatch) {\r\n flushList();\r\n const level = headingMatch[1].length as 1 | 2 | 3;\r\n const content = renderInline(headingMatch[2], cfg);\r\n const sizes = { 1: '1.4em', 2: '1.2em', 3: '1.05em' };\r\n result.push(\r\n <div key={key} style={{ fontWeight: 700, fontSize: sizes[level], margin: '6px 0 2px' }}>\r\n {content}\r\n </div>,\r\n );\r\n return;\r\n }\r\n }\r\n\r\n // List items\r\n if (cfg.lists && /^[\\-\\*•]\\s+/.test(line)) {\r\n const content = line.replace(/^[\\-\\*•]\\s+/, '');\r\n listItems.push(<li key={key}>{renderInline(content, cfg)}</li>);\r\n return;\r\n }\r\n\r\n // Ordered list\r\n if (cfg.lists && /^\\d+\\.\\s+/.test(line)) {\r\n flushList(); // flush unordered first\r\n const content = line.replace(/^\\d+\\.\\s+/, '');\r\n // Wrap single ordered items inline for now\r\n result.push(\r\n <div key={key} style={{ paddingLeft: '20px' }}>\r\n {line.match(/^\\d+/)![0]}. {renderInline(content, cfg)}\r\n </div>,\r\n );\r\n return;\r\n }\r\n\r\n flushList();\r\n\r\n // Empty line = paragraph break\r\n if (line.trim() === '') {\r\n result.push(<br key={key} />);\r\n return;\r\n }\r\n\r\n // Normal line\r\n result.push(\r\n <span key={key} style={{ display: 'block' }}>\r\n {renderInline(line, cfg)}\r\n </span>,\r\n );\r\n });\r\n\r\n flushList();\r\n return result;\r\n}\r\n\r\n/** Parses inline markdown (bold, italic, code, links, strikethrough) into React nodes. */\r\nfunction renderInline(text: string, cfg: Required<MarkdownOptions>): React.ReactNode {\r\n // Build a regex that matches all inline patterns we support\r\n const patterns: string[] = [];\r\n\r\n if (cfg.code) patterns.push('`([^`]+)`');\r\n if (cfg.bold) patterns.push('\\\\*\\\\*([^*]+)\\\\*\\\\*', '__([^_]+)__');\r\n if (cfg.strikethrough) patterns.push('~~([^~]+)~~');\r\n if (cfg.italic) patterns.push('\\\\*([^*]+)\\\\*', '(?<!\\\\w)_([^_]+)_(?!\\\\w)');\r\n if (cfg.links) patterns.push('\\\\[([^\\\\]]+)\\\\]\\\\((https?:\\\\/\\\\/[^)]+)\\\\)');\r\n\r\n if (patterns.length === 0) return text;\r\n\r\n const combined = new RegExp(patterns.join('|'), 'g');\r\n const parts: React.ReactNode[] = [];\r\n let lastIndex = 0;\r\n let key = 0;\r\n let match: RegExpExecArray | null;\r\n\r\n while ((match = combined.exec(text)) !== null) {\r\n // Push preceding text\r\n if (match.index > lastIndex) {\r\n parts.push(text.slice(lastIndex, match.index));\r\n }\r\n\r\n const full = match[0];\r\n\r\n if (cfg.code && full.startsWith('`') && full.endsWith('`')) {\r\n parts.push(\r\n <code key={key++} style={{ background: 'rgba(0,0,0,0.06)', borderRadius: '3px', padding: '1px 4px', fontSize: '0.9em' }}>\r\n {full.slice(1, -1)}\r\n </code>,\r\n );\r\n } else if (cfg.bold && (full.startsWith('**') || full.startsWith('__'))) {\r\n const inner = full.startsWith('**') ? full.slice(2, -2) : full.slice(2, -2);\r\n parts.push(<strong key={key++}>{renderInline(inner, { ...cfg, bold: false })}</strong>);\r\n } else if (cfg.strikethrough && full.startsWith('~~')) {\r\n parts.push(<del key={key++}>{renderInline(full.slice(2, -2), { ...cfg, strikethrough: false })}</del>);\r\n } else if (cfg.italic && (full.startsWith('*') || full.startsWith('_'))) {\r\n const inner = full.slice(1, -1);\r\n parts.push(<em key={key++}>{renderInline(inner, { ...cfg, italic: false })}</em>);\r\n } else if (cfg.links && full.startsWith('[')) {\r\n const linkMatch = full.match(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^)]+)\\)/);\r\n if (linkMatch) {\r\n parts.push(\r\n <a key={key++} href={linkMatch[2]} target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: 'inherit', textDecoration: 'underline' }}>\r\n {linkMatch[1]}\r\n </a>,\r\n );\r\n }\r\n } else {\r\n parts.push(full);\r\n }\r\n\r\n lastIndex = match.index + full.length;\r\n }\r\n\r\n if (lastIndex < text.length) {\r\n parts.push(text.slice(lastIndex));\r\n }\r\n\r\n return parts.length === 1 ? parts[0] : <>{parts}</>;\r\n}\r\n","import React, { useState } from 'react';\r\nimport type { ChatMessage, MessageAttachment } from '../types';\r\nimport type { ChatStyles } from '../styles/theme';\r\nimport { FileIcon, EditIcon, TrashIcon } from './icons';\r\nimport { useChatContext } from '../context/ChatContext';\r\nimport { renderMarkdown } from '../utils/markdown';\r\n\r\ninterface MessageBubbleProps {\r\n message: ChatMessage;\r\n styles: ChatStyles;\r\n}\r\n\r\nexport const MessageBubble: React.FC<MessageBubbleProps> = ({ message, styles }) => {\r\n const { props: chatProps } = useChatContext();\r\n const isBot = message.sender === 'bot';\r\n const isAgent = message.sender === 'agent';\r\n const isSystem = message.sender === 'system';\r\n const bubbleStyle = isBot || isSystem || isAgent ? styles.botBubble : styles.userBubble;\r\n\r\n const hasContent = message.text || (message.attachments && message.attachments.length > 0);\r\n if (!hasContent) return null;\r\n\r\n const agentName = message.agentName ?? (message.metadata?.agentName as string | undefined);\r\n\r\n const systemStyle: React.CSSProperties = isSystem\r\n ? {\r\n background: 'transparent',\r\n border: 'none',\r\n boxShadow: 'none',\r\n color: '#999',\r\n fontSize: '12px',\r\n alignSelf: 'center',\r\n padding: '6px 12px',\r\n backdropFilter: 'none',\r\n WebkitBackdropFilter: 'none',\r\n }\r\n : {};\r\n\r\n return (\r\n <div\r\n style={{\r\n ...bubbleStyle,\r\n ...systemStyle,\r\n animation: 'cb-fade-in 0.3s ease-out',\r\n }}\r\n >\r\n {isAgent && agentName && (\r\n <div style={{ fontSize: '11px', fontWeight: 600, opacity: 0.7, marginBottom: '4px' }}>\r\n {agentName}\r\n </div>\r\n )}\r\n {message.text && (\r\n <span style={{ display: 'block' }}>\r\n {chatProps.markdown\r\n ? renderMarkdown(message.text, chatProps.markdown === true ? {} : chatProps.markdown)\r\n : message.text}\r\n </span>\r\n )}\r\n {message.attachments && message.attachments.length > 0 && (\r\n <div style={{ marginTop: message.text ? '10px' : 0, display: 'flex', flexDirection: 'column', gap: '6px' }}>\r\n {message.attachments.map((attachment, i) => (\r\n <AttachmentPreview key={i} attachment={attachment} isBot={isBot} />\r\n ))}\r\n </div>\r\n )}\r\n {/* Reactions */}\r\n {chatProps.enableReactions && (isBot || isAgent) && !isSystem && (\r\n <MessageReactions message={message} />\r\n )}\r\n {/* Read receipts */}\r\n {chatProps.showReadReceipts && message.sender === 'user' && message.status && (\r\n <div style={{ fontSize: '10px', opacity: 0.5, textAlign: 'right', marginTop: '2px' }}>\r\n {message.status === 'sent' && '✓'}\r\n {message.status === 'delivered' && '✓✓'}\r\n {message.status === 'read' && <span style={{ color: '#3B82F6' }}>✓✓</span>}\r\n </div>\r\n )}\r\n {/* Edit/Delete */}\r\n {chatProps.allowMessageEdit && message.sender === 'user' && !isSystem && (\r\n <MessageActions message={message} />\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\n// ─── Attachment Preview ──────────────────────────────────────────\r\n\r\ninterface AttachmentPreviewProps {\r\n attachment: MessageAttachment;\r\n isBot: boolean;\r\n}\r\n\r\nconst AttachmentPreview: React.FC<AttachmentPreviewProps> = ({ attachment, isBot }) => {\r\n const { props: chatProps } = useChatContext();\r\n const icons = chatProps.icons;\r\n const isImage = attachment.type.startsWith('image/');\r\n\r\n if (isImage && attachment.url) {\r\n return (\r\n <div style={{ borderRadius: '12px', overflow: 'hidden', maxWidth: '220px' }}>\r\n <img\r\n src={attachment.url}\r\n alt={attachment.name}\r\n style={{\r\n width: '100%',\r\n height: 'auto',\r\n display: 'block',\r\n borderRadius: '12px',\r\n }}\r\n />\r\n <div style={{ fontSize: '11px', padding: '4px 0', opacity: 0.6 }}>{attachment.name}</div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <a\r\n href={attachment.url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '8px',\r\n padding: '8px 12px',\r\n backgroundColor: isBot ? 'rgba(0,0,0,0.04)' : 'rgba(255,255,255,0.15)',\r\n borderRadius: '10px',\r\n textDecoration: 'none',\r\n color: 'inherit',\r\n fontSize: '13px',\r\n border: isBot ? '1px solid rgba(0,0,0,0.06)' : '1px solid rgba(255,255,255,0.15)',\r\n transition: 'background 0.2s ease',\r\n }}\r\n >\r\n {icons?.file ?? <FileIcon size={16} />}\r\n <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>\r\n {attachment.name}\r\n </span>\r\n {attachment.size && (\r\n <span style={{ fontSize: '11px', opacity: 0.5, flexShrink: 0 }}>\r\n {formatFileSize(attachment.size)}\r\n </span>\r\n )}\r\n </a>\r\n );\r\n};\r\n\r\nfunction formatFileSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes}B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\r\n}\r\n\r\n// ─── Message Reactions ───────────────────────────────────────────\r\n\r\nconst DEFAULT_REACTIONS = ['👍', '👎', '❤️', '😂', '😮'];\r\n\r\nconst MessageReactions: React.FC<{ message: ChatMessage }> = ({ message }) => {\r\n const { props: chatProps, dispatch } = useChatContext();\r\n const [showPicker, setShowPicker] = useState(false);\r\n\r\n const emojis = Array.isArray(chatProps.enableReactions)\r\n ? chatProps.enableReactions\r\n : DEFAULT_REACTIONS;\r\n\r\n const reactions = message.reactions ?? [];\r\n\r\n const handleReact = (emoji: string) => {\r\n const existing = reactions.find((r) => r.emoji === emoji);\r\n const reacted = !existing?.reacted;\r\n const updated = existing\r\n ? reactions.map((r) => r.emoji === emoji ? { ...r, count: reacted ? r.count + 1 : Math.max(0, r.count - 1), reacted } : r)\r\n : [...reactions, { emoji, count: 1, reacted: true }];\r\n\r\n dispatch({ type: 'UPDATE_MESSAGE', payload: { id: message.id, updates: { reactions: updated.filter((r) => r.count > 0) } } });\r\n chatProps.callbacks?.onReaction?.(message.id, emoji, reacted);\r\n setShowPicker(false);\r\n };\r\n\r\n return (\r\n <div style={{ display: 'flex', alignItems: 'center', gap: '4px', marginTop: '6px', flexWrap: 'wrap' }}>\r\n {reactions.map((r) => (\r\n <button\r\n key={r.emoji}\r\n onClick={() => handleReact(r.emoji)}\r\n style={{\r\n display: 'inline-flex', alignItems: 'center', gap: '3px',\r\n padding: '2px 6px', borderRadius: '12px', border: r.reacted ? '1px solid rgba(108,92,231,0.4)' : '1px solid rgba(0,0,0,0.08)',\r\n background: r.reacted ? 'rgba(108,92,231,0.1)' : 'rgba(0,0,0,0.03)',\r\n cursor: 'pointer', fontSize: '12px', transition: 'all 0.15s ease',\r\n }}\r\n >\r\n <span>{r.emoji}</span>\r\n {r.count > 0 && <span style={{ fontSize: '11px', opacity: 0.7 }}>{r.count}</span>}\r\n </button>\r\n ))}\r\n <div style={{ position: 'relative' }}>\r\n <button\r\n onClick={() => setShowPicker(!showPicker)}\r\n style={{\r\n width: '22px', height: '22px', borderRadius: '50%', border: '1px solid rgba(0,0,0,0.08)',\r\n background: 'rgba(0,0,0,0.03)', cursor: 'pointer', fontSize: '11px',\r\n display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.15s ease',\r\n }}\r\n aria-label=\"React\"\r\n >\r\n +\r\n </button>\r\n {showPicker && (\r\n <div style={{\r\n position: 'absolute', bottom: '100%', left: 0, marginBottom: '4px',\r\n display: 'flex', gap: '2px', padding: '4px 6px', borderRadius: '16px',\r\n background: '#fff', boxShadow: '0 2px 12px rgba(0,0,0,0.15)', border: '1px solid rgba(0,0,0,0.08)',\r\n zIndex: 10,\r\n }}>\r\n {emojis.map((emoji) => (\r\n <button\r\n key={emoji}\r\n onClick={() => handleReact(emoji)}\r\n style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: '16px', padding: '2px', borderRadius: '4px', transition: 'transform 0.1s' }}\r\n onMouseEnter={(e) => (e.currentTarget.style.transform = 'scale(1.3)')}\r\n onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}\r\n >\r\n {emoji}\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\n// ─── Message Edit/Delete Actions ─────────────────────────────────\r\n\r\nconst MessageActions: React.FC<{ message: ChatMessage }> = ({ message }) => {\r\n const { dispatch, props: chatProps } = useChatContext();\r\n const [editing, setEditing] = useState(false);\r\n const [editText, setEditText] = useState(message.text ?? '');\r\n const icons = chatProps.icons;\r\n\r\n const handleSaveEdit = () => {\r\n if (editText.trim()) {\r\n dispatch({ type: 'UPDATE_MESSAGE', payload: { id: message.id, updates: { text: editText.trim(), metadata: { ...message.metadata, edited: true } } } });\r\n }\r\n setEditing(false);\r\n };\r\n\r\n const handleDelete = () => {\r\n dispatch({ type: 'UPDATE_MESSAGE', payload: { id: message.id, updates: { text: undefined, metadata: { ...message.metadata, deleted: true } } } });\r\n };\r\n\r\n if (editing) {\r\n return (\r\n <div style={{ marginTop: '4px', display: 'flex', gap: '4px' }}>\r\n <input\r\n value={editText}\r\n onChange={(e) => setEditText(e.target.value)}\r\n onKeyDown={(e) => e.key === 'Enter' && handleSaveEdit()}\r\n autoFocus\r\n style={{ flex: 1, padding: '4px 8px', borderRadius: '8px', border: '1px solid rgba(0,0,0,0.1)', fontSize: '13px', outline: 'none' }}\r\n />\r\n <button onClick={handleSaveEdit} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: '12px', color: '#10B981' }}>✓</button>\r\n <button onClick={() => setEditing(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: '12px', color: '#999' }}>✕</button>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div style={{ display: 'flex', gap: '4px', marginTop: '4px', opacity: 0.4, transition: 'opacity 0.15s' }}\r\n onMouseEnter={(e) => (e.currentTarget.style.opacity = '1')}\r\n onMouseLeave={(e) => (e.currentTarget.style.opacity = '0.4')}\r\n >\r\n <button onClick={() => setEditing(true)} style={{ background: 'none', border: 'none', cursor: 'pointer', display: 'flex', padding: '2px' }} aria-label=\"Edit\">\r\n {icons?.edit ?? <EditIcon size={12} />}\r\n </button>\r\n <button onClick={handleDelete} style={{ background: 'none', border: 'none', cursor: 'pointer', display: 'flex', padding: '2px', color: '#EF4444' }} aria-label=\"Delete\">\r\n {icons?.trash ?? <TrashIcon size={12} />}\r\n </button>\r\n {!!message.metadata?.edited && <span style={{ fontSize: '10px', opacity: 0.5 }}>(edited)</span>}\r\n </div>\r\n );\r\n};\r\n","import React from 'react';\nimport type { FlowQuickReply } from '../types';\n\ninterface QuickRepliesProps {\n replies: FlowQuickReply[];\n onSelect: (value: string, label: string) => void;\n primaryColor: string;\n}\n\nexport const QuickReplies: React.FC<QuickRepliesProps> = ({ replies, onSelect, primaryColor }) => {\n return (\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '8px',\n alignSelf: 'flex-start',\n maxWidth: '90%',\n animation: 'cb-slide-up 0.35s ease-out',\n padding: '4px 0',\n }}\n >\n {replies.map((reply) => (\n <button\n key={reply.value}\n onClick={() => onSelect(reply.value, reply.label)}\n style={{\n padding: '8px 18px',\n borderRadius: '22px',\n border: `1.5px solid ${primaryColor}`,\n backgroundColor: 'rgba(108, 92, 231, 0.06)',\n color: primaryColor,\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 500,\n fontFamily: 'inherit',\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\n backdropFilter: 'blur(4px)',\n WebkitBackdropFilter: 'blur(4px)',\n letterSpacing: '0.01em',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = primaryColor;\n e.currentTarget.style.color = '#fff';\n e.currentTarget.style.transform = 'translateY(-1px)';\n e.currentTarget.style.boxShadow = `0 4px 14px ${primaryColor}44`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(108, 92, 231, 0.06)';\n e.currentTarget.style.color = primaryColor;\n e.currentTarget.style.transform = 'translateY(0)';\n e.currentTarget.style.boxShadow = 'none';\n }}\n >\n {reply.label}\n </button>\n ))}\n </div>\n );\n};\n","import React from 'react';\n\ninterface TypingIndicatorProps {\n color: string;\n}\n\nexport const TypingIndicator: React.FC<TypingIndicatorProps> = ({ color }) => {\n const dotStyle: React.CSSProperties = {\n width: '7px',\n height: '7px',\n borderRadius: '50%',\n backgroundColor: color,\n opacity: 0.35,\n animation: 'cb-typing-bounce 1.4s infinite ease-in-out',\n };\n\n return (\n <div\n style={{\n display: 'flex',\n gap: '5px',\n padding: '14px 18px',\n background: 'rgba(241, 243, 249, 0.8)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n borderRadius: '18px 18px 18px 4px',\n alignSelf: 'flex-start',\n alignItems: 'center',\n border: '1px solid rgba(0,0,0,0.04)',\n boxShadow: '0 2px 8px rgba(0,0,0,0.04)',\n animation: 'cb-fade-in 0.3s ease-out',\n }}\n >\n <span style={{ ...dotStyle, animationDelay: '0s' }} />\n <span style={{ ...dotStyle, animationDelay: '0.2s' }} />\n <span style={{ ...dotStyle, animationDelay: '0.4s' }} />\n </div>\n );\n};\n","import React from 'react';\r\nimport type { CarouselCard } from '../types';\r\n\r\ninterface CarouselCardsProps {\r\n cards: CarouselCard[];\r\n primaryColor: string;\r\n onCardAction?: (value: string) => void;\r\n}\r\n\r\nexport const CarouselCards: React.FC<CarouselCardsProps> = ({ cards, primaryColor, onCardAction }) => {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n gap: '10px',\r\n overflowX: 'auto',\r\n padding: '4px 2px 8px',\r\n scrollSnapType: 'x mandatory',\r\n WebkitOverflowScrolling: 'touch',\r\n maxWidth: '100%',\r\n }}\r\n >\r\n {cards.map((card, idx) => (\r\n <div\r\n key={idx}\r\n style={{\r\n minWidth: '200px',\r\n maxWidth: '220px',\r\n borderRadius: '12px',\r\n border: '1px solid rgba(0,0,0,0.08)',\r\n overflow: 'hidden',\r\n flexShrink: 0,\r\n scrollSnapAlign: 'start',\r\n background: 'rgba(255,255,255,0.95)',\r\n boxShadow: '0 2px 8px rgba(0,0,0,0.06)',\r\n }}\r\n >\r\n {card.image && (\r\n <img\r\n src={card.image}\r\n alt={card.title}\r\n style={{ width: '100%', height: '120px', objectFit: 'cover', display: 'block' }}\r\n />\r\n )}\r\n <div style={{ padding: '10px 12px' }}>\r\n <div style={{ fontWeight: 600, fontSize: '13px', marginBottom: '2px', color: '#1a1a2e' }}>\r\n {card.title}\r\n </div>\r\n {card.subtitle && (\r\n <div style={{ fontSize: '12px', color: '#666', lineHeight: '1.4' }}>\r\n {card.subtitle}\r\n </div>\r\n )}\r\n {card.buttons && card.buttons.length > 0 && (\r\n <div style={{ display: 'flex', flexDirection: 'column', gap: '4px', marginTop: '8px' }}>\r\n {card.buttons.map((btn, bIdx) => (\r\n <button\r\n key={bIdx}\r\n onClick={() => {\r\n if (btn.url) window.open(btn.url, '_blank', 'noopener,noreferrer');\r\n else if (btn.value || btn.next) onCardAction?.(btn.value ?? btn.next ?? '');\r\n }}\r\n style={{\r\n padding: '6px 10px',\r\n borderRadius: '8px',\r\n border: `1px solid ${primaryColor}`,\r\n background: 'transparent',\r\n color: primaryColor,\r\n fontSize: '12px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n transition: 'all 0.15s ease',\r\n textAlign: 'center',\r\n }}\r\n onMouseEnter={(e) => { e.currentTarget.style.background = primaryColor; e.currentTarget.style.color = '#fff'; }}\r\n onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = primaryColor; }}\r\n >\r\n {btn.label}\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n};\r\n","import React, { useRef, useEffect } from 'react';\r\nimport type { ComponentType } from 'react';\r\nimport type { ChatMessage } from '../types';\r\nimport type { StepComponentProps, FlowActionResult, ChatCustomizeChat } from '../types/config';\r\nimport type { FormFieldRenderMap } from '../types/form';\r\nimport type { ChatStyles } from '../styles/theme';\r\nimport { MessageBubble } from './MessageBubble';\r\nimport { QuickReplies } from './QuickReplies';\r\nimport { TypingIndicator } from './TypingIndicator';\r\nimport { DynamicForm } from './forms/DynamicForm';\r\nimport { CarouselCards } from './CarouselCards';\r\n\r\ninterface MessageListProps {\r\n messages: ChatMessage[];\r\n isTyping: boolean;\r\n styles: ChatStyles;\r\n primaryColor: string;\r\n onQuickReply: (value: string, label: string) => void;\r\n onFormSubmit: (formId: string, data: Record<string, unknown>) => void;\r\n /** Map of custom step components */\r\n components?: Record<string, ComponentType<StepComponentProps>>;\r\n /** Called when a custom component completes */\r\n onComponentComplete?: (result?: FlowActionResult) => void;\r\n /** Collected flow data — passed to custom components */\r\n collectedData?: Record<string, unknown>;\r\n /** Current step ID */\r\n currentStepId?: string | null;\r\n /** Custom form field renderers per field type */\r\n renderFormField?: FormFieldRenderMap;\r\n /** Slot overrides from customizeChat */\r\n customizeChat?: ChatCustomizeChat;\r\n /** Search filter query */\r\n searchQuery?: string;\r\n}\r\n\r\nexport const MessageList: React.FC<MessageListProps> = ({\r\n messages,\r\n isTyping,\r\n styles,\r\n primaryColor,\r\n onQuickReply,\r\n onFormSubmit,\r\n components,\r\n onComponentComplete,\r\n collectedData,\r\n currentStepId,\r\n renderFormField,\r\n customizeChat,\r\n searchQuery,\r\n}) => {\r\n const bottomRef = useRef<HTMLDivElement>(null);\r\n\r\n useEffect(() => {\r\n bottomRef.current?.scrollIntoView({ behavior: 'smooth' });\r\n }, [messages, isTyping]);\r\n\r\n const Bubble = customizeChat?.messageBubble?.component ?? MessageBubble;\r\n const QR = customizeChat?.quickReplies?.component ?? QuickReplies;\r\n const Typing = customizeChat?.typingIndicator?.component ?? TypingIndicator;\r\n\r\n const filteredMessages = searchQuery\r\n ? messages.filter((m) => m.text?.toLowerCase().includes(searchQuery.toLowerCase()))\r\n : messages;\r\n\r\n return (\r\n <div style={styles.messageList} className=\"cb-scrollbar\">\r\n {filteredMessages.map((msg) => (\r\n <React.Fragment key={msg.id}>\r\n <Bubble message={msg} styles={styles} />\r\n {msg.cards && msg.cards.length > 0 && (\r\n <CarouselCards\r\n cards={msg.cards}\r\n primaryColor={primaryColor}\r\n onCardAction={(val) => onQuickReply(val, val)}\r\n />\r\n )}\r\n {msg.quickReplies && msg.quickReplies.length > 0 && (\r\n <QR\r\n replies={msg.quickReplies}\r\n onSelect={onQuickReply}\r\n primaryColor={primaryColor}\r\n />\r\n )}\r\n {msg.form && (\r\n <div style={{ alignSelf: 'flex-start', width: '92%', animation: 'cb-slide-up 0.35s ease-out' }}>\r\n <DynamicForm\r\n config={msg.form}\r\n onSubmit={(data) => onFormSubmit(msg.form!.id, data)}\r\n primaryColor={primaryColor}\r\n renderFormField={renderFormField}\r\n />\r\n </div>\r\n )}\r\n {msg.component && components?.[msg.component] && (\r\n <div style={{ alignSelf: 'flex-start', width: '92%', animation: 'cb-slide-up 0.35s ease-out' }}>\r\n {React.createElement(components[msg.component], {\r\n stepId: currentStepId ?? '',\r\n data: collectedData ?? {},\r\n onComplete: (result?: FlowActionResult) => onComponentComplete?.(result),\r\n })}\r\n </div>\r\n )}\r\n </React.Fragment>\r\n ))}\r\n {isTyping && <Typing color={primaryColor} />}\r\n <div ref={bottomRef} />\r\n </div>\r\n );\r\n};\r\n","import React, { useState, useRef, useEffect } from 'react';\n\nconst EMOJI_CATEGORIES = [\n {\n name: 'Smileys',\n emojis: ['😀', '😃', '😄', '😁', '😅', '😂', '🤣', '😊', '😇', '🙂', '😉', '😍', '🥰', '😘', '😋', '😜', '🤪', '🤗', '🤔', '🤫', '🤭', '😏', '😐', '😑', '😶', '😌', '😴', '🤤', '😷', '🤒'],\n },\n {\n name: 'Gestures',\n emojis: ['👍', '👎', '👌', '✌️', '🤞', '🤟', '🤘', '👋', '🤚', '✋', '🖖', '👏', '🙌', '🤝', '🙏', '💪', '🖐️', '☝️', '👆', '👇', '👈', '👉', '🤙', '🫡', '🫶', '🫰', '🫳', '🫴', '🫲', '🫱'],\n },\n {\n name: 'Hearts',\n emojis: ['❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔', '❣️', '💕', '💞', '💓', '💗', '💖', '💘', '💝', '💟', '♥️', '🫀', '💌', '💐', '🌹', '🌺', '🌸', '🌼', '🌻', '🌷', '💮'],\n },\n {\n name: 'Objects',\n emojis: ['🔥', '⭐', '✨', '💯', '🎉', '🎊', '🎯', '🚀', '💡', '📌', '📎', '🔗', '💻', '📱', '☎️', '📧', '📝', '📋', '📊', '📈', '🗂️', '📁', '🔒', '🔑', '⚙️', '🛠️', '🔧', '📦', '🏷️', '✅'],\n },\n];\n\ninterface EmojiPickerProps {\n onSelect: (emoji: string) => void;\n onClose: () => void;\n primaryColor: string;\n}\n\nexport const EmojiPicker: React.FC<EmojiPickerProps> = ({ onSelect, onClose, primaryColor }) => {\n const [activeCategory, setActiveCategory] = useState(0);\n const pickerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (pickerRef.current && !pickerRef.current.contains(e.target as Node)) {\n onClose();\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [onClose]);\n\n const currentEmojis = EMOJI_CATEGORIES[activeCategory]?.emojis ?? [];\n\n return (\n <div\n ref={pickerRef}\n style={{\n position: 'absolute',\n bottom: '100%',\n left: 0,\n width: '280px',\n backgroundColor: 'rgba(255, 255, 255, 0.92)',\n backdropFilter: 'blur(20px)',\n WebkitBackdropFilter: 'blur(20px)',\n borderRadius: '16px',\n boxShadow: '0 8px 32px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04)',\n border: '1px solid rgba(255,255,255,0.8)',\n overflow: 'hidden',\n zIndex: 10,\n marginBottom: '8px',\n animation: 'cb-slide-up 0.25s ease-out',\n }}\n >\n {/* Category tabs */}\n <div\n style={{\n display: 'flex',\n borderBottom: '1px solid rgba(0,0,0,0.06)',\n padding: '6px',\n gap: '3px',\n }}\n >\n {EMOJI_CATEGORIES.map((cat, idx) => (\n <button\n key={cat.name}\n onClick={() => setActiveCategory(idx)}\n title={cat.name}\n style={{\n flex: 1,\n padding: '6px 4px',\n border: 'none',\n borderRadius: '8px',\n cursor: 'pointer',\n fontSize: '11px',\n fontWeight: 600,\n fontFamily: 'inherit',\n letterSpacing: '0.02em',\n background: idx === activeCategory\n ? `linear-gradient(135deg, ${primaryColor}, ${primaryColor}CC)`\n : 'transparent',\n color: idx === activeCategory ? '#fff' : 'rgba(0,0,0,0.4)',\n transition: 'all 0.2s ease',\n boxShadow: idx === activeCategory ? `0 2px 8px ${primaryColor}33` : 'none',\n }}\n >\n {cat.name}\n </button>\n ))}\n </div>\n\n {/* Emoji Grid */}\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(8, 1fr)',\n gap: '2px',\n padding: '8px',\n maxHeight: '180px',\n overflowY: 'auto',\n }}\n >\n {currentEmojis.map((emoji) => (\n <button\n key={emoji}\n onClick={() => {\n onSelect(emoji);\n onClose();\n }}\n style={{\n width: '30px',\n height: '30px',\n border: 'none',\n backgroundColor: 'transparent',\n cursor: 'pointer',\n fontSize: '18px',\n borderRadius: '6px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = 'rgba(108, 92, 231, 0.08)')}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = 'transparent')}\n >\n {emoji}\n </button>\n ))}\n </div>\n </div>\n );\n};\n","import React, { useRef } from 'react';\r\nimport type { FileUploadConfig } from '../types/config';\r\nimport { AttachmentIcon, RemoveIcon, FileIcon, ImageIcon } from './icons';\r\nimport { useChatContext } from '../context/ChatContext';\r\n\r\ninterface FileUploadButtonProps {\r\n config: FileUploadConfig;\r\n onFiles: (files: File[]) => void;\r\n selectedFiles: File[];\r\n onRemoveFile: (index: number) => void;\r\n primaryColor: string;\r\n}\r\n\r\nexport const FileUploadButton: React.FC<FileUploadButtonProps> = ({\r\n config,\r\n onFiles,\r\n selectedFiles,\r\n onRemoveFile,\r\n primaryColor,\r\n}) => {\r\n const { props: chatProps } = useChatContext();\r\n const icons = chatProps.icons;\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n const files = e.target.files;\r\n if (!files || files.length === 0) return;\r\n\r\n const fileArr = Array.from(files);\r\n\r\n // Validate file size\r\n if (config.maxSize) {\r\n const oversized = fileArr.filter((f) => f.size > config.maxSize!);\r\n if (oversized.length > 0) {\r\n alert(`File(s) too large. Max size: ${formatSize(config.maxSize)}`);\r\n return;\r\n }\r\n }\r\n\r\n // Validate max count\r\n const maxFiles = config.maxFiles ?? 5;\r\n if (selectedFiles.length + fileArr.length > maxFiles) {\r\n alert(`Maximum ${maxFiles} files allowed`);\r\n return;\r\n }\r\n\r\n onFiles(fileArr);\r\n // Reset input to allow re-selecting same file\r\n if (inputRef.current) inputRef.current.value = '';\r\n };\r\n\r\n return (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n type=\"file\"\r\n accept={config.accept}\r\n multiple={config.multiple !== false}\r\n onChange={handleChange}\r\n style={{ display: 'none' }}\r\n />\r\n <button\r\n type=\"button\"\r\n onClick={() => inputRef.current?.click()}\r\n aria-label=\"Attach file\"\r\n title=\"Attach file\"\r\n style={{\r\n background: 'none',\r\n border: 'none',\r\n cursor: 'pointer',\r\n padding: '6px',\r\n display: 'flex',\r\n alignItems: 'center',\r\n color: '#999',\r\n borderRadius: '6px',\r\n transition: 'color 0.15s ease',\r\n }}\r\n onMouseEnter={(e) => (e.currentTarget.style.color = primaryColor)}\r\n onMouseLeave={(e) => (e.currentTarget.style.color = '#999')}\r\n >\r\n {icons?.attachment ?? <AttachmentIcon size={20} />}\r\n </button>\r\n </div>\r\n );\r\n};\r\n\r\n// ─── File Preview List ───────────────────────────────────────────\r\n\r\ninterface FilePreviewListProps {\r\n files: File[];\r\n onRemove: (index: number) => void;\r\n primaryColor: string;\r\n}\r\n\r\nexport const FilePreviewList: React.FC<FilePreviewListProps> = ({\r\n files,\r\n onRemove,\r\n primaryColor,\r\n}) => {\r\n if (files.length === 0) return null;\r\n\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n flexWrap: 'wrap',\r\n gap: '6px',\r\n padding: '8px 12px 0',\r\n }}\r\n >\r\n {files.map((file, idx) => (\r\n <FilePreviewChip\r\n key={`${file.name}-${idx}`}\r\n file={file}\r\n onRemove={() => onRemove(idx)}\r\n primaryColor={primaryColor}\r\n />\r\n ))}\r\n </div>\r\n );\r\n};\r\n\r\n// ─── Single File Chip ────────────────────────────────────────────\r\n\r\ninterface FilePreviewChipProps {\r\n file: File;\r\n onRemove: () => void;\r\n primaryColor: string;\r\n}\r\n\r\nconst FilePreviewChip: React.FC<FilePreviewChipProps> = ({ file, onRemove, primaryColor }) => {\r\n const { props: chatProps } = useChatContext();\r\n const icons = chatProps.icons;\r\n const isImage = file.type.startsWith('image/');\r\n\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '6px',\r\n padding: '4px 8px',\r\n backgroundColor: '#F3F4F6',\r\n borderRadius: '8px',\r\n fontSize: '12px',\r\n maxWidth: '200px',\r\n }}\r\n >\r\n <span style={{ color: primaryColor, flexShrink: 0 }}>\r\n {isImage ? (icons?.image ?? <ImageIcon size={14} />) : (icons?.file ?? <FileIcon size={14} />)}\r\n </span>\r\n <span\r\n style={{\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n color: '#555',\r\n }}\r\n >\r\n {file.name}\r\n </span>\r\n <span style={{ color: '#999', fontSize: '11px', flexShrink: 0 }}>\r\n {formatSize(file.size)}\r\n </span>\r\n <button\r\n onClick={onRemove}\r\n style={{\r\n background: 'none',\r\n border: 'none',\r\n cursor: 'pointer',\r\n padding: '0',\r\n display: 'flex',\r\n color: '#999',\r\n flexShrink: 0,\r\n }}\r\n >\r\n {icons?.remove ?? <RemoveIcon size={14} />}\r\n </button>\r\n </div>\r\n );\r\n};\r\n\r\n// ─── Utils ───────────────────────────────────────────────────────\r\n\r\nfunction formatSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes}B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\r\n}\r\n","import React, { useState, useRef, useCallback } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { FileUploadConfig } from '../types/config';\nimport { SendIcon, EmojiIcon, MicIcon } from './icons';\nimport { EmojiPicker } from './EmojiPicker';\nimport { FileUploadButton, FilePreviewList } from './FileUpload';\nimport { useChatContext } from '../context/ChatContext';\n\ninterface ChatInputProps {\n onSend: (text: string, files?: File[]) => void;\n placeholder?: string;\n primaryColor: string;\n isDark?: boolean;\n disabled?: boolean;\n styleOverride?: CSSProperties;\n enableEmoji?: boolean;\n fileUpload?: FileUploadConfig;\n onFileUpload?: (files: File[]) => void;\n}\n\nexport const ChatInput: React.FC<ChatInputProps> = ({\n onSend,\n placeholder = 'Type a message...',\n primaryColor,\n isDark = false,\n disabled,\n styleOverride,\n enableEmoji = false,\n fileUpload,\n onFileUpload,\n}) => {\n const { props: chatProps } = useChatContext();\n const icons = chatProps.icons;\n const [text, setText] = useState('');\n const [showEmoji, setShowEmoji] = useState(false);\n const [attachedFiles, setAttachedFiles] = useState<File[]>([]);\n const [isListening, setIsListening] = useState(false);\n const inputRef = useRef<HTMLTextAreaElement>(null);\n const recognitionRef = useRef<unknown>(null);\n\n const voiceCfg = chatProps.enableVoice;\n const voiceEnabled = !!voiceCfg && typeof window !== 'undefined' && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window);\n\n const toggleVoice = useCallback(() => {\n if (!voiceEnabled) return;\n if (isListening) {\n (recognitionRef.current as { stop: () => void })?.stop?.();\n setIsListening(false);\n return;\n }\n const SpeechRecognition = (window as unknown as Record<string, unknown>).SpeechRecognition ?? (window as unknown as Record<string, unknown>).webkitSpeechRecognition;\n if (!SpeechRecognition) return;\n const recognition = new (SpeechRecognition as new () => {\n lang: string; continuous: boolean; interimResults: boolean;\n onresult: ((e: { results: { transcript: string; isFinal: boolean }[][] }) => void) | null;\n onend: (() => void) | null;\n onerror: (() => void) | null;\n start: () => void; stop: () => void;\n })();\n const lang = typeof voiceCfg === 'object' ? voiceCfg.lang : undefined;\n const continuous = typeof voiceCfg === 'object' ? voiceCfg.continuous : false;\n recognition.lang = lang ?? navigator.language;\n recognition.continuous = continuous ?? false;\n recognition.interimResults = true;\n recognition.onresult = (e) => {\n const transcript = Array.from(e.results).map((r) => r[0].transcript).join('');\n setText(transcript);\n };\n recognition.onend = () => setIsListening(false);\n recognition.onerror = () => setIsListening(false);\n recognition.start();\n recognitionRef.current = recognition;\n setIsListening(true);\n }, [voiceEnabled, voiceCfg, isListening]);\n\n const handleSend = useCallback(() => {\n const trimmed = text.trim();\n if (!trimmed && attachedFiles.length === 0) return;\n onSend(trimmed, attachedFiles.length > 0 ? attachedFiles : undefined);\n setText('');\n setAttachedFiles([]);\n inputRef.current?.focus();\n }, [text, attachedFiles, onSend]);\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n };\n\n // Typing indicator — debounce typing events\n const typingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const handleTextChange = (val: string) => {\n setText(val);\n if (chatProps.showUserTyping && chatProps.callbacks?.onUserTyping) {\n chatProps.callbacks.onUserTyping(true);\n if (typingTimerRef.current) clearTimeout(typingTimerRef.current);\n typingTimerRef.current = setTimeout(() => chatProps.callbacks?.onUserTyping?.(false), 2000);\n }\n };\n\n const handleEmojiSelect = (emoji: string) => {\n setText((prev) => prev + emoji);\n inputRef.current?.focus();\n };\n\n const handleFiles = (files: File[]) => {\n setAttachedFiles((prev) => [...prev, ...files]);\n onFileUpload?.(files);\n };\n\n const handleRemoveFile = (index: number) => {\n setAttachedFiles((prev) => prev.filter((_, i) => i !== index));\n };\n\n const hasContent = text.trim() || attachedFiles.length > 0;\n\n return (\n <div style={{ position: 'relative', ...styleOverride }}>\n {/* File preview above input */}\n {attachedFiles.length > 0 && (\n <FilePreviewList\n files={attachedFiles}\n onRemove={handleRemoveFile}\n primaryColor={primaryColor}\n />\n )}\n\n {/* Emoji picker */}\n {showEmoji && (\n <EmojiPicker\n onSelect={handleEmojiSelect}\n onClose={() => setShowEmoji(false)}\n primaryColor={primaryColor}\n />\n )}\n\n <div\n style={{\n display: 'flex',\n gap: '8px',\n alignItems: 'flex-end',\n background: isDark ? 'rgba(40, 40, 65, 0.5)' : 'rgba(245, 247, 252, 0.7)',\n borderRadius: '16px',\n border: `1px solid ${isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)'}`,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n padding: '6px 6px 6px 12px',\n }}\n >\n {/* Action buttons */}\n <div style={{ display: 'flex', alignItems: 'center', gap: '2px', flexShrink: 0, paddingBottom: '2px' }}>\n {enableEmoji && (\n <button\n type=\"button\"\n onClick={() => setShowEmoji(!showEmoji)}\n aria-label=\"Emoji\"\n title=\"Emoji\"\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n color: showEmoji ? primaryColor : (isDark ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.3)'),\n borderRadius: '8px',\n transition: 'all 0.2s ease',\n }}\n >\n {icons?.emoji ?? <EmojiIcon size={20} />}\n </button>\n )}\n\n {fileUpload?.enabled && (\n <FileUploadButton\n config={fileUpload}\n onFiles={handleFiles}\n selectedFiles={attachedFiles}\n onRemoveFile={handleRemoveFile}\n primaryColor={primaryColor}\n />\n )}\n\n {voiceEnabled && (\n <button\n type=\"button\"\n onClick={toggleVoice}\n aria-label={isListening ? 'Stop listening' : 'Voice input'}\n title={isListening ? 'Stop listening' : 'Voice input'}\n style={{\n background: isListening ? primaryColor : 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '6px',\n display: 'flex',\n color: isListening ? '#fff' : (isDark ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.3)'),\n borderRadius: '8px',\n transition: 'all 0.2s ease',\n animation: isListening ? 'cb-pulse 1.5s infinite' : 'none',\n }}\n >\n {icons?.mic ?? <MicIcon size={18} />}\n </button>\n )}\n </div>\n\n {/* Text Input */}\n <textarea\n ref={inputRef}\n value={text}\n onChange={(e) => handleTextChange(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n style={{\n flex: 1,\n padding: '8px 2px',\n border: 'none',\n borderRadius: '12px',\n outline: 'none',\n resize: 'none',\n fontFamily: 'inherit',\n fontSize: '14px',\n lineHeight: '1.45',\n maxHeight: '100px',\n overflowY: 'auto',\n backgroundColor: 'transparent',\n color: isDark ? '#E0E0E0' : '#2D3436',\n letterSpacing: '0.01em',\n }}\n />\n\n {/* Send Button */}\n <button\n onClick={handleSend}\n disabled={disabled || !hasContent}\n aria-label=\"Send message\"\n style={{\n width: '36px',\n height: '36px',\n borderRadius: '12px',\n background: hasContent\n ? `linear-gradient(135deg, ${primaryColor} 0%, ${adjustColor(primaryColor, 30)} 100%)`\n : (isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)'),\n color: hasContent ? '#fff' : (isDark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.2)'),\n border: 'none',\n cursor: hasContent ? 'pointer' : 'default',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',\n boxShadow: hasContent ? `0 4px 12px ${primaryColor}44` : 'none',\n }}\n >\n {icons?.send ?? <SendIcon size={16} />}\n </button>\n </div>\n </div>\n );\n};\n\nfunction adjustColor(hex: string, amount: number): string {\n const num = parseInt(hex.replace('#', ''), 16);\n const r = Math.min(255, ((num >> 16) & 0xff) + amount);\n const g = Math.min(255, ((num >> 8) & 0xff) + amount);\n const b = Math.min(255, (num & 0xff) + amount);\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;\n}\n","import React from 'react';\nimport type { BrandingConfig } from '../types/config';\n\ninterface BrandingProps {\n config: BrandingConfig;\n primaryColor: string;\n}\n\nexport const Branding: React.FC<BrandingProps> = ({ config, primaryColor }) => {\n if (config.showBranding === false) return null;\n\n const text = config.poweredBy ?? 'React ChatBot';\n\n return (\n <div\n style={{\n padding: '8px 16px',\n textAlign: 'center',\n fontSize: '11px',\n color: 'rgba(0,0,0,0.35)',\n background: 'rgba(250, 250, 255, 0.7)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n borderTop: '1px solid rgba(0,0,0,0.04)',\n flexShrink: 0,\n letterSpacing: '0.02em',\n }}\n >\n Powered by{' '}\n {config.poweredByUrl ? (\n <a\n href={config.poweredByUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{\n color: primaryColor,\n textDecoration: 'none',\n fontWeight: 600,\n transition: 'opacity 0.2s ease',\n }}\n >\n {text}\n </a>\n ) : (\n <span style={{ color: primaryColor, fontWeight: 600 }}>{text}</span>\n )}\n </div>\n );\n};\n","let counter = 0;\n\nexport const uid = (): string => `msg_${Date.now()}_${++counter}`;\n\nexport const classNames = (...args: (string | false | null | undefined)[]): string =>\n args.filter(Boolean).join(' ');\n\nexport const delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n","import type { FlowConfig, FlowStep, ChatMessage, FlowQuickReply } from '../types';\r\nimport { uid } from '../utils/helpers';\r\n\r\nexport class FlowEngine {\r\n private steps: Map<string, FlowStep>;\r\n private startStep: string;\r\n private collectedData: Record<string, unknown> = {};\r\n private stepHistory: string[] = [];\r\n\r\n constructor(flow: FlowConfig) {\r\n this.startStep = flow.startStep;\r\n this.steps = new Map(flow.steps.map((s) => [s.id, s]));\r\n }\r\n\r\n getStartStepId(): string {\r\n return this.startStep;\r\n }\r\n\r\n getStep(id: string): FlowStep | undefined {\r\n return this.steps.get(id);\r\n }\r\n\r\n getData(): Record<string, unknown> {\r\n return { ...this.collectedData };\r\n }\r\n\r\n setData(key: string, value: unknown): void {\r\n this.collectedData[key] = value;\r\n }\r\n\r\n mergeData(data: Record<string, unknown>): void {\r\n Object.assign(this.collectedData, data);\r\n }\r\n\r\n /** Push a step onto the history stack (called when entering a step) */\r\n pushHistory(stepId: string): void {\r\n this.stepHistory.push(stepId);\r\n }\r\n\r\n /** Pop and return the previous step (go back) */\r\n popHistory(): string | undefined {\r\n // Remove current step\r\n this.stepHistory.pop();\r\n // Return previous step\r\n return this.stepHistory.pop();\r\n }\r\n\r\n /** Check if there's a previous step to go back to */\r\n canGoBack(): boolean {\r\n return this.stepHistory.length > 1;\r\n }\r\n\r\n /** Reset the engine to initial state */\r\n reset(): void {\r\n this.collectedData = {};\r\n this.stepHistory = [];\r\n }\r\n\r\n resolveNext(step: FlowStep, userValue?: string): string | undefined {\r\n // Conditional branching\r\n if (step.condition) {\r\n const { field, operator, value, then: thenStep, else: elseStep } = step.condition;\r\n const fieldVal = this.collectedData[field];\r\n const match = this.evaluate(fieldVal, operator, value);\r\n return match ? thenStep : elseStep;\r\n }\r\n\r\n // Quick-reply selected → find the reply's next\r\n if (userValue && step.quickReplies) {\r\n const reply = step.quickReplies.find((r) => r.value === userValue);\r\n if (reply?.next) return reply.next;\r\n }\r\n\r\n return step.next;\r\n }\r\n\r\n /** Check if a step should be visible based on its `visibleIf` condition */\r\n isStepVisible(step: FlowStep): boolean {\r\n if (!step.visibleIf) return true;\r\n const { field, operator, value } = step.visibleIf;\r\n return this.evaluate(this.collectedData[field], operator, value);\r\n }\r\n\r\n /** Resolve a sub-flow's start step (for flow composition) */\r\n getSubFlowStartStep(step: FlowStep): FlowStep | undefined {\r\n if (!step.subFlow) return undefined;\r\n return step.subFlow.steps.find((s) => s.id === step.subFlow!.startStep);\r\n }\r\n\r\n /** Returns true if the step expects a quick reply (not free text) */\r\n stepExpectsQuickReply(step: FlowStep): boolean {\r\n return !!(step.quickReplies && step.quickReplies.length > 0);\r\n }\r\n\r\n /** Returns true if the step expects a form submission */\r\n stepExpectsForm(step: FlowStep): boolean {\r\n return !!step.form;\r\n }\r\n\r\n /** Try to fuzzy-match user text against quick reply labels */\r\n matchQuickReply(step: FlowStep, text: string): FlowQuickReply | undefined {\r\n if (!step.quickReplies) return undefined;\r\n const lower = text.toLowerCase().trim();\r\n if (!lower) return undefined;\r\n // Exact value match\r\n const exact = step.quickReplies.find((r) => r.value.toLowerCase() === lower);\r\n if (exact) return exact;\r\n // Exact label match \r\n const labelMatch = step.quickReplies.find((r) => r.label.toLowerCase().replace(/[^\\w\\s]/g, '').trim() === lower);\r\n if (labelMatch) return labelMatch;\r\n // Contains match\r\n const contains = step.quickReplies.find((r) => lower.includes(r.value.toLowerCase()) || r.label.toLowerCase().includes(lower));\r\n return contains;\r\n }\r\n\r\n buildMessages(step: FlowStep): ChatMessage[] {\r\n const messages: ChatMessage[] = [];\r\n\r\n const texts = step.messages ?? (step.message ? [step.message] : []);\r\n for (const text of texts) {\r\n messages.push({\r\n id: uid(),\r\n sender: 'bot',\r\n text,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n // Attach quick replies to the last message\r\n if (step.quickReplies && messages.length > 0) {\r\n messages[messages.length - 1]!.quickReplies = step.quickReplies;\r\n }\r\n\r\n // If step has a form, create a form message\r\n if (step.form) {\r\n messages.push({\r\n id: uid(),\r\n sender: 'bot',\r\n timestamp: Date.now(),\r\n form: step.form,\r\n });\r\n }\r\n\r\n // If step has a custom component, create a component message\r\n if (step.component) {\r\n messages.push({\r\n id: uid(),\r\n sender: 'bot',\r\n timestamp: Date.now(),\r\n component: step.component,\r\n });\r\n }\r\n\r\n return messages;\r\n }\r\n\r\n private evaluate(\r\n fieldVal: unknown,\r\n operator: string,\r\n value: string | number,\r\n ): boolean {\r\n switch (operator) {\r\n case 'eq':\r\n return String(fieldVal) === String(value);\r\n case 'neq':\r\n return String(fieldVal) !== String(value);\r\n case 'contains':\r\n return String(fieldVal).includes(String(value));\r\n case 'gt':\r\n return Number(fieldVal) > Number(value);\r\n case 'lt':\r\n return Number(fieldVal) < Number(value);\r\n default:\r\n return false;\r\n }\r\n }\r\n}\r\n\r\nexport function createQuickReplyMessage(\r\n replies: FlowQuickReply[],\r\n): ChatMessage {\r\n return {\r\n id: uid(),\r\n sender: 'bot',\r\n timestamp: Date.now(),\r\n quickReplies: replies,\r\n };\r\n}\r\n","// ─── Live Agent Adapter ──────────────────────────────────────────\r\n// Protocol-agnostic wrapper over WebSocket / Socket.IO\r\n\r\nimport type { ResolvedLiveAgentEvents, LiveAgentConfig, DEFAULT_LIVE_AGENT_EVENTS } from '../types/liveAgent';\r\n\r\nexport type DriverListener = (data: unknown) => void;\r\n\r\n/** Uniform interface implemented by WsDriver and SioDriver */\r\nexport interface LiveAgentDriver {\r\n send(event: string, data: unknown): void;\r\n on(event: string, handler: DriverListener): void;\r\n off(event: string, handler: DriverListener): void;\r\n isConnected(): boolean;\r\n destroy(): void;\r\n}\r\n\r\nexport class LiveAgentAdapter {\r\n private driver: LiveAgentDriver;\r\n readonly events: ResolvedLiveAgentEvents;\r\n readonly sessionId: string;\r\n\r\n constructor(\r\n driver: LiveAgentDriver,\r\n events: ResolvedLiveAgentEvents,\r\n sessionId: string,\r\n ) {\r\n this.driver = driver;\r\n this.events = events;\r\n this.sessionId = sessionId;\r\n }\r\n\r\n /** Send a user message to the server */\r\n sendUserMessage(text: string, attachments?: unknown[]): void {\r\n this.driver.send(this.events.userMessage, {\r\n text,\r\n sessionId: this.sessionId,\r\n timestamp: Date.now(),\r\n ...(attachments?.length ? { attachments } : {}),\r\n });\r\n }\r\n\r\n /** Send user typing indicator */\r\n sendUserTyping(): void {\r\n this.driver.send(this.events.userTyping, {\r\n sessionId: this.sessionId,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n /** Request transfer to a live agent */\r\n requestTransfer(department?: string): void {\r\n this.driver.send(this.events.transferRequest, {\r\n sessionId: this.sessionId,\r\n department,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n /** Initialize or restore a session */\r\n initSession(): void {\r\n this.driver.send(this.events.sessionInit, {\r\n sessionId: this.sessionId,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n /** Subscribe to a server event */\r\n on(event: string, handler: DriverListener): void {\r\n this.driver.on(event, handler);\r\n }\r\n\r\n /** Unsubscribe from a server event */\r\n off(event: string, handler: DriverListener): void {\r\n this.driver.off(event, handler);\r\n }\r\n\r\n isConnected(): boolean {\r\n return this.driver.isConnected();\r\n }\r\n\r\n destroy(): void {\r\n this.driver.destroy();\r\n }\r\n}\r\n","// ─── WebSocket Driver ────────────────────────────────────────────\r\n// Wraps native WebSocket in the LiveAgentDriver interface.\r\n// Messages are JSON-encoded with { event, data } envelope.\r\n\r\nimport type { LiveAgentDriver, DriverListener } from '../LiveAgentAdapter';\r\n\r\nexport class WsDriver implements LiveAgentDriver {\r\n private ws: WebSocket;\r\n private listeners = new Map<string, Set<DriverListener>>();\r\n private boundOnMessage: (e: MessageEvent) => void;\r\n\r\n constructor(ws: WebSocket) {\r\n this.ws = ws;\r\n this.boundOnMessage = this.handleMessage.bind(this);\r\n this.ws.addEventListener('message', this.boundOnMessage);\r\n }\r\n\r\n send(event: string, data: unknown): void {\r\n if (this.ws.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify({ event, data }));\r\n }\r\n }\r\n\r\n on(event: string, handler: DriverListener): void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(handler);\r\n }\r\n\r\n off(event: string, handler: DriverListener): void {\r\n this.listeners.get(event)?.delete(handler);\r\n }\r\n\r\n isConnected(): boolean {\r\n return this.ws.readyState === WebSocket.OPEN;\r\n }\r\n\r\n destroy(): void {\r\n this.ws.removeEventListener('message', this.boundOnMessage);\r\n this.listeners.clear();\r\n }\r\n\r\n private handleMessage(e: MessageEvent): void {\r\n try {\r\n const parsed = JSON.parse(String(e.data)) as { event?: string; data?: unknown };\r\n if (parsed.event) {\r\n const handlers = this.listeners.get(parsed.event);\r\n if (handlers) {\r\n handlers.forEach((h) => h(parsed.data));\r\n }\r\n }\r\n } catch {\r\n // Non-JSON message — ignore\r\n }\r\n }\r\n}\r\n","// ─── Socket.IO Driver ────────────────────────────────────────────\r\n// Wraps a Socket.IO client socket in the LiveAgentDriver interface.\r\n\r\nimport type { LiveAgentDriver, DriverListener } from '../LiveAgentAdapter';\r\n\r\n/** Minimal Socket.IO client interface (avoids hard dependency) */\r\ninterface SioSocket {\r\n emit(event: string, ...args: unknown[]): void;\r\n on(event: string, handler: (...args: unknown[]) => void): void;\r\n off(event: string, handler: (...args: unknown[]) => void): void;\r\n connected: boolean;\r\n}\r\n\r\nexport class SioDriver implements LiveAgentDriver {\r\n private socket: SioSocket;\r\n private registeredHandlers: Array<{ event: string; handler: DriverListener }> = [];\r\n\r\n constructor(socket: SioSocket) {\r\n this.socket = socket;\r\n }\r\n\r\n send(event: string, data: unknown): void {\r\n this.socket.emit(event, data);\r\n }\r\n\r\n on(event: string, handler: DriverListener): void {\r\n this.socket.on(event, handler as (...args: unknown[]) => void);\r\n this.registeredHandlers.push({ event, handler });\r\n }\r\n\r\n off(event: string, handler: DriverListener): void {\r\n this.socket.off(event, handler as (...args: unknown[]) => void);\r\n this.registeredHandlers = this.registeredHandlers.filter(\r\n (h) => !(h.event === event && h.handler === handler),\r\n );\r\n }\r\n\r\n isConnected(): boolean {\r\n return this.socket.connected;\r\n }\r\n\r\n destroy(): void {\r\n for (const { event, handler } of this.registeredHandlers) {\r\n this.socket.off(event, handler as (...args: unknown[]) => void);\r\n }\r\n this.registeredHandlers = [];\r\n }\r\n}\r\n","// ─── Live Agent Types ────────────────────────────────────────────\r\n// WebSocket / Socket.IO real-time agent chat support\r\n\r\nexport interface AgentInfo {\r\n name: string;\r\n avatar?: string;\r\n department?: string;\r\n}\r\n\r\nexport interface LiveAgentEvents {\r\n /** Server → client: new message from agent (default: 'agent:message') */\r\n agentMessage?: string;\r\n /** Client → server: user sends message (default: 'user:message') */\r\n userMessage?: string;\r\n /** Server → client: agent joined (default: 'agent:joined') */\r\n agentJoined?: string;\r\n /** Server → client: agent left / disconnected (default: 'agent:left') */\r\n agentLeft?: string;\r\n /** Server → client: agent is typing (default: 'agent:typing') */\r\n agentTyping?: string;\r\n /** Client → server: user is typing (default: 'user:typing') */\r\n userTyping?: string;\r\n /** Client → server: session init / restore (default: 'session:init') */\r\n sessionInit?: string;\r\n /** Server → client: session history restored (default: 'session:history') */\r\n sessionHistory?: string;\r\n /** Client → server: request agent transfer (default: 'transfer:request') */\r\n transferRequest?: string;\r\n /** Server → client: transfer accepted (default: 'transfer:accepted') */\r\n transferAccepted?: string;\r\n /** Server → client: queue position update (default: 'queue:update') */\r\n queueUpdate?: string;\r\n}\r\n\r\nexport interface LiveAgentConfig {\r\n /** Transport type — 'ws' for native WebSocket, 'socketio' for Socket.IO client */\r\n type: 'ws' | 'socketio';\r\n\r\n /** The pre-created instance — a WebSocket or Socket.IO socket */\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n instance: any;\r\n\r\n /** Session ID for persistence across refreshes (auto-generated if omitted) */\r\n sessionId?: string;\r\n\r\n /** Customize server event names */\r\n events?: LiveAgentEvents;\r\n\r\n /** Persist session messages to localStorage across refreshes (default: true) */\r\n persistSession?: boolean;\r\n\r\n /** Storage key prefix (default: 'chatbot_live_') */\r\n storageKey?: string;\r\n\r\n /** Callbacks */\r\n onAgentJoined?: (agent: AgentInfo) => void;\r\n onAgentLeft?: (agent: AgentInfo) => void;\r\n onQueueUpdate?: (position: number, estimatedWait?: number) => void;\r\n onSessionRestored?: (messageCount: number) => void;\r\n onError?: (error: Error) => void;\r\n onConnect?: () => void;\r\n onDisconnect?: () => void;\r\n}\r\n\r\n/** Resolved event names with defaults applied */\r\nexport interface ResolvedLiveAgentEvents {\r\n agentMessage: string;\r\n userMessage: string;\r\n agentJoined: string;\r\n agentLeft: string;\r\n agentTyping: string;\r\n userTyping: string;\r\n sessionInit: string;\r\n sessionHistory: string;\r\n transferRequest: string;\r\n transferAccepted: string;\r\n queueUpdate: string;\r\n}\r\n\r\nexport const DEFAULT_LIVE_AGENT_EVENTS: ResolvedLiveAgentEvents = {\r\n agentMessage: 'agent:message',\r\n userMessage: 'user:message',\r\n agentJoined: 'agent:joined',\r\n agentLeft: 'agent:left',\r\n agentTyping: 'agent:typing',\r\n userTyping: 'user:typing',\r\n sessionInit: 'session:init',\r\n sessionHistory: 'session:history',\r\n transferRequest: 'transfer:request',\r\n transferAccepted: 'transfer:accepted',\r\n queueUpdate: 'queue:update',\r\n};\r\n","// ─── useLiveAgent Hook ───────────────────────────────────────────\r\n// Initializes the LiveAgentAdapter, listens for server events,\r\n// dispatches messages, and handles session persistence.\r\n\r\nimport { useEffect, useRef, useCallback } from 'react';\r\nimport { LiveAgentAdapter } from '../core/LiveAgentAdapter';\r\nimport { WsDriver } from '../core/drivers/WsDriver';\r\nimport { SioDriver } from '../core/drivers/SioDriver';\r\nimport { DEFAULT_LIVE_AGENT_EVENTS } from '../types/liveAgent';\r\nimport type { LiveAgentConfig, ResolvedLiveAgentEvents, AgentInfo } from '../types/liveAgent';\r\nimport type { ChatMessage } from '../types/message';\r\nimport type { ChatAction } from '../context/ChatContext';\r\nimport { uid } from '../utils/helpers';\r\n\r\ninterface UseLiveAgentOptions {\r\n config: LiveAgentConfig | undefined;\r\n dispatch: React.Dispatch<ChatAction>;\r\n messages: ChatMessage[];\r\n}\r\n\r\nfunction resolveEvents(custom?: LiveAgentConfig['events']): ResolvedLiveAgentEvents {\r\n return { ...DEFAULT_LIVE_AGENT_EVENTS, ...custom };\r\n}\r\n\r\nfunction resolveSessionId(config: LiveAgentConfig): string {\r\n if (config.sessionId) return config.sessionId;\r\n const storageKey = (config.storageKey ?? 'chatbot_live_') + 'session_id';\r\n if (typeof localStorage !== 'undefined') {\r\n const existing = localStorage.getItem(storageKey);\r\n if (existing) return existing;\r\n const id = uid();\r\n localStorage.setItem(storageKey, id);\r\n return id;\r\n }\r\n return uid();\r\n}\r\n\r\nexport function useLiveAgent({ config, dispatch, messages }: UseLiveAgentOptions) {\r\n const adapterRef = useRef<LiveAgentAdapter | null>(null);\r\n const isLiveRef = useRef(false);\r\n const agentInfoRef = useRef<AgentInfo | null>(null);\r\n const typingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const messagesRef = useRef(messages);\r\n messagesRef.current = messages;\r\n\r\n // ── Build adapter on mount ──────────────────────────────────\r\n useEffect(() => {\r\n if (!config) return;\r\n\r\n const events = resolveEvents(config.events);\r\n const sessionId = resolveSessionId(config);\r\n\r\n const driver =\r\n config.type === 'socketio'\r\n ? new SioDriver(config.instance)\r\n : new WsDriver(config.instance);\r\n\r\n const adapter = new LiveAgentAdapter(driver, events, sessionId);\r\n adapterRef.current = adapter;\r\n\r\n // ── Listen: agent message ───────────────────────────────\r\n const onAgentMessage = (data: unknown) => {\r\n const d = data as { text?: string; agent?: AgentInfo; attachments?: ChatMessage['attachments'] };\r\n if (!d.text && !d.attachments?.length) return;\r\n\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'agent' as ChatMessage['sender'],\r\n text: d.text,\r\n timestamp: Date.now(),\r\n ...(d.attachments?.length ? { attachments: d.attachments } : {}),\r\n metadata: d.agent ? { agentName: d.agent.name, agentAvatar: d.agent.avatar } : undefined,\r\n };\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n dispatch({ type: 'ADD_MESSAGE', payload: msg });\r\n persistMessages([...messagesRef.current, msg], config);\r\n };\r\n\r\n // ── Listen: agent joined ────────────────────────────────\r\n const onAgentJoined = (data: unknown) => {\r\n const d = data as AgentInfo;\r\n agentInfoRef.current = d;\r\n isLiveRef.current = true;\r\n dispatch({ type: 'SET_LIVE_AGENT', payload: true } as ChatAction);\r\n dispatch({ type: 'SET_AGENT_INFO', payload: d } as ChatAction);\r\n\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: `${d.name} joined the chat`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n config.onAgentJoined?.(d);\r\n };\r\n\r\n // ── Listen: agent left ──────────────────────────────────\r\n const onAgentLeft = (data: unknown) => {\r\n const d = data as AgentInfo;\r\n agentInfoRef.current = null;\r\n isLiveRef.current = false;\r\n dispatch({ type: 'SET_LIVE_AGENT', payload: false } as ChatAction);\r\n dispatch({ type: 'SET_AGENT_INFO', payload: null } as ChatAction);\r\n\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: `${d.name} left the chat`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n config.onAgentLeft?.(d);\r\n };\r\n\r\n // ── Listen: agent typing ────────────────────────────────\r\n const onAgentTyping = () => {\r\n dispatch({ type: 'SET_TYPING', payload: true });\r\n // Auto-clear after 3s if no new typing event\r\n if (typingTimerRef.current) clearTimeout(typingTimerRef.current);\r\n typingTimerRef.current = setTimeout(() => {\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n }, 3000);\r\n };\r\n\r\n // ── Listen: transfer accepted ───────────────────────────\r\n const onTransferAccepted = (data: unknown) => {\r\n const d = data as { agent?: AgentInfo; message?: string };\r\n if (d.agent) {\r\n agentInfoRef.current = d.agent;\r\n isLiveRef.current = true;\r\n dispatch({ type: 'SET_LIVE_AGENT', payload: true } as ChatAction);\r\n dispatch({ type: 'SET_AGENT_INFO', payload: d.agent } as ChatAction);\r\n }\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: d.message ?? `Connected to ${d.agent?.name ?? 'an agent'}`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n };\r\n\r\n // ── Listen: queue update ────────────────────────────────\r\n const onQueueUpdate = (data: unknown) => {\r\n const d = data as { position: number; estimatedWait?: number };\r\n const waitText = d.estimatedWait ? ` Estimated wait: ~${d.estimatedWait} min.` : '';\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: `You are #${d.position} in queue.${waitText}`,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n config.onQueueUpdate?.(d.position, d.estimatedWait);\r\n };\r\n\r\n // ── Listen: session history ─────────────────────────────\r\n const onSessionHistory = (data: unknown) => {\r\n const d = data as { messages?: ChatMessage[] };\r\n if (d.messages?.length) {\r\n dispatch({ type: 'ADD_MESSAGES', payload: d.messages });\r\n config.onSessionRestored?.(d.messages.length);\r\n }\r\n };\r\n\r\n // Register all listeners\r\n adapter.on(events.agentMessage, onAgentMessage);\r\n adapter.on(events.agentJoined, onAgentJoined);\r\n adapter.on(events.agentLeft, onAgentLeft);\r\n adapter.on(events.agentTyping, onAgentTyping);\r\n adapter.on(events.transferAccepted, onTransferAccepted);\r\n adapter.on(events.queueUpdate, onQueueUpdate);\r\n adapter.on(events.sessionHistory, onSessionHistory);\r\n\r\n // Restore from local storage if available\r\n if (config.persistSession !== false) {\r\n const stored = loadPersistedMessages(config);\r\n if (stored.length) {\r\n dispatch({ type: 'ADD_MESSAGES', payload: stored });\r\n config.onSessionRestored?.(stored.length);\r\n }\r\n }\r\n\r\n // Init session with server\r\n adapter.initSession();\r\n config.onConnect?.();\r\n\r\n return () => {\r\n adapter.off(events.agentMessage, onAgentMessage);\r\n adapter.off(events.agentJoined, onAgentJoined);\r\n adapter.off(events.agentLeft, onAgentLeft);\r\n adapter.off(events.agentTyping, onAgentTyping);\r\n adapter.off(events.transferAccepted, onTransferAccepted);\r\n adapter.off(events.queueUpdate, onQueueUpdate);\r\n adapter.off(events.sessionHistory, onSessionHistory);\r\n if (typingTimerRef.current) clearTimeout(typingTimerRef.current);\r\n adapter.destroy();\r\n adapterRef.current = null;\r\n config.onDisconnect?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [config, dispatch]);\r\n\r\n // ── Public: send message via adapter ────────────────────────\r\n const sendLiveMessage = useCallback(\r\n (text: string, attachments?: unknown[]) => {\r\n adapterRef.current?.sendUserMessage(text, attachments);\r\n },\r\n [],\r\n );\r\n\r\n // ── Public: send typing indicator (throttled) ──────────────\r\n const sendTyping = useCallback(() => {\r\n adapterRef.current?.sendUserTyping();\r\n }, []);\r\n\r\n // ── Public: request transfer ───────────────────────────────\r\n const requestTransfer = useCallback(\r\n (department?: string) => {\r\n adapterRef.current?.requestTransfer(department);\r\n const sysMsg: ChatMessage = {\r\n id: uid(),\r\n sender: 'system',\r\n text: department\r\n ? `Requesting transfer to ${department}…`\r\n : 'Requesting a live agent…',\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: sysMsg });\r\n },\r\n [dispatch],\r\n );\r\n\r\n return {\r\n adapter: adapterRef,\r\n sendLiveMessage,\r\n sendTyping,\r\n requestTransfer,\r\n isLive: isLiveRef,\r\n agentInfo: agentInfoRef,\r\n };\r\n}\r\n\r\n// ─── Local Storage Persistence Helpers ───────────────────────────\r\n\r\nfunction storageKey(config: LiveAgentConfig): string {\r\n const prefix = config.storageKey ?? 'chatbot_live_';\r\n const sid = config.sessionId ?? 'default';\r\n return `${prefix}${sid}`;\r\n}\r\n\r\nfunction persistMessages(messages: ChatMessage[], config: LiveAgentConfig): void {\r\n if (config.persistSession === false) return;\r\n if (typeof localStorage === 'undefined') return;\r\n try {\r\n localStorage.setItem(storageKey(config), JSON.stringify(messages));\r\n } catch { /* quota exceeded — silent */ }\r\n}\r\n\r\nfunction loadPersistedMessages(config: LiveAgentConfig): ChatMessage[] {\r\n if (typeof localStorage === 'undefined') return [];\r\n try {\r\n const raw = localStorage.getItem(storageKey(config));\r\n if (!raw) return [];\r\n const parsed = JSON.parse(raw);\r\n return Array.isArray(parsed) ? parsed : [];\r\n } catch {\r\n return [];\r\n }\r\n}\r\n","import { useCallback, useRef, useEffect } from 'react';\r\nimport { useChatContext } from '../context/ChatContext';\r\nimport { FlowEngine } from '../engine/FlowEngine';\r\nimport { uid, delay } from '../utils/helpers';\r\nimport type { ChatMessage } from '../types';\r\nimport type { FlowActionResult, ActionContext, KeywordRoute } from '../types/config';\r\nimport type { FlowStepInput, FlowMiddleware } from '../types/flow';\r\nimport { useLiveAgent } from './useLiveAgent';\r\n\r\n/** Slash commands the user can type */\r\nconst COMMANDS: Record<string, string> = {\r\n '/help': 'Show available commands',\r\n '/cancel': 'Cancel current step and go back',\r\n '/back': 'Go back to the previous step',\r\n '/restart': 'Restart the conversation from the beginning',\r\n};\r\n\r\n/** Common greeting words for auto-detection */\r\nconst GREETING_PATTERNS = ['hi', 'hello', 'hey', 'howdy', 'hola', 'greetings', 'good morning', 'good afternoon', 'good evening', 'sup', 'yo', 'hii', 'hiii'];\r\n\r\n/** Match user text against a single keyword route */\r\nfunction matchesRoute(text: string, route: KeywordRoute): boolean {\r\n const compare = route.caseSensitive ? text : text.toLowerCase();\r\n for (const pattern of route.patterns) {\r\n const pat = route.caseSensitive ? pattern : pattern.toLowerCase();\r\n switch (route.matchType ?? 'contains') {\r\n case 'exact':\r\n if (compare === pat) return true;\r\n break;\r\n case 'startsWith':\r\n if (compare.startsWith(pat)) return true;\r\n break;\r\n case 'regex':\r\n try {\r\n if (new RegExp(pat, route.caseSensitive ? '' : 'i').test(text)) return true;\r\n } catch { /* invalid regex — skip */ }\r\n break;\r\n case 'contains':\r\n default:\r\n if (compare.includes(pat)) return true;\r\n break;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\n/** Find the best matching keyword route (highest priority) */\r\nfunction findKeywordMatch(text: string, keywords: KeywordRoute[]): KeywordRoute | undefined {\r\n const sorted = [...keywords].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\r\n return sorted.find((r) => matchesRoute(text, r));\r\n}\r\n\r\n/** Run middleware pipeline — returns final message or null if blocked */\r\nfunction runMiddleware(\r\n message: ChatMessage,\r\n data: Record<string, unknown>,\r\n middlewares: FlowMiddleware[],\r\n): ChatMessage | null {\r\n let current = message;\r\n let blocked = false;\r\n for (const mw of middlewares) {\r\n let called = false;\r\n const result = mw(current, data, () => { called = true; });\r\n if (result) current = result;\r\n if (!called) { blocked = true; break; }\r\n }\r\n return blocked ? null : current;\r\n}\r\n\r\n/** Validate and transform user text for an input step */\r\nfunction validateInput(text: string, input: FlowStepInput): { valid: boolean; value: string; error?: string } {\r\n let value = text;\r\n if (input.transform) {\r\n switch (input.transform) {\r\n case 'trim': value = value.trim(); break;\r\n case 'lowercase': value = value.toLowerCase(); break;\r\n case 'uppercase': value = value.toUpperCase(); break;\r\n case 'email': value = value.trim().toLowerCase(); break;\r\n }\r\n }\r\n if (input.validation) {\r\n const v = input.validation;\r\n if (v.required && !value.trim()) return { valid: false, value, error: v.message ?? 'This field is required.' };\r\n if (v.minLength && value.length < v.minLength) return { valid: false, value, error: v.message ?? `Must be at least ${v.minLength} characters.` };\r\n if (v.maxLength && value.length > v.maxLength) return { valid: false, value, error: v.message ?? `Must be at most ${v.maxLength} characters.` };\r\n if (v.pattern) {\r\n try {\r\n if (!new RegExp(v.pattern).test(value)) return { valid: false, value, error: v.message ?? 'Invalid format.' };\r\n } catch { /* invalid pattern */ }\r\n }\r\n }\r\n return { valid: true, value };\r\n}\r\n\r\nexport function useChat() {\r\n const { state, dispatch, props, pluginManager } = useChatContext();\r\n const flowRef = useRef<FlowEngine | null>(null);\r\n const flowStartedRef = useRef(false);\r\n\r\n // Keep fresh references for use inside async callbacks (avoids stale closures)\r\n const stateRef = useRef(state);\r\n stateRef.current = state;\r\n const propsRef = useRef(props);\r\n propsRef.current = props;\r\n\r\n // Live agent hook\r\n const { sendLiveMessage, sendTyping, requestTransfer } = useLiveAgent({\r\n config: props.liveAgent,\r\n dispatch,\r\n messages: state.messages,\r\n });\r\n\r\n // Initialize flow engine\r\n useEffect(() => {\r\n if (props.flow) {\r\n flowRef.current = new FlowEngine(props.flow);\r\n flowStartedRef.current = false;\r\n }\r\n }, [props.flow]);\r\n\r\n const addBotMessage = useCallback(\r\n async (text: string, extras?: Partial<ChatMessage>) => {\r\n dispatch({ type: 'SET_TYPING', payload: true });\r\n await delay(400);\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'bot',\r\n text,\r\n timestamp: Date.now(),\r\n ...extras,\r\n };\r\n // Let plugins see bot messages too\r\n const finalMsg = pluginManager ? await pluginManager.onMessage(msg) : msg;\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n dispatch({ type: 'ADD_MESSAGE', payload: finalMsg });\r\n propsRef.current.callbacks?.onMessageReceive?.(finalMsg);\r\n },\r\n [dispatch, pluginManager],\r\n );\r\n\r\n const addSystemMessage = useCallback(\r\n (text: string) => {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'system', text, timestamp: Date.now() },\r\n });\r\n },\r\n [dispatch],\r\n );\r\n\r\n // Use a ref so processFlowStep can call itself recursively without stale closure\r\n const processFlowStepRef = useRef<(stepId: string) => Promise<void>>(async () => {});\r\n processFlowStepRef.current = async (stepId: string) => {\r\n const engine = flowRef.current;\r\n if (!engine) return;\r\n\r\n const step = engine.getStep(stepId);\r\n if (!step) return;\r\n\r\n // Conditional rendering: skip step if visibleIf condition is not met\r\n if (!engine.isStepVisible(step)) {\r\n const nextId = engine.resolveNext(step);\r\n if (nextId) {\r\n await processFlowStepRef.current(nextId);\r\n }\r\n return;\r\n }\r\n\r\n // Sub-flow composition: enter sub-flow if defined\r\n if (step.subFlow) {\r\n const subStart = engine.getSubFlowStartStep(step);\r\n if (subStart) {\r\n // Register sub-flow steps temporarily\r\n step.subFlow.steps.forEach((s) => {\r\n engine['steps'].set(s.id, {\r\n ...s,\r\n // Wire sub-flow's terminal step back to parent's onSubFlowComplete\r\n next: s.next ?? step.onSubFlowComplete,\r\n });\r\n });\r\n await processFlowStepRef.current(step.subFlow.startStep);\r\n return;\r\n }\r\n }\r\n\r\n // Track step history for /back navigation\r\n engine.pushHistory(stepId);\r\n\r\n dispatch({ type: 'SET_STEP', payload: stepId });\r\n pluginManager?.emitEvent('stepChange', { stepId });\r\n dispatch({ type: 'SET_TYPING', payload: true });\r\n await delay(step.delay ?? 500);\r\n\r\n const messages = engine.buildMessages(step);\r\n dispatch({ type: 'SET_TYPING', payload: false });\r\n dispatch({ type: 'ADD_MESSAGES', payload: messages });\r\n\r\n messages.forEach((m) => propsRef.current.callbacks?.onMessageReceive?.(m));\r\n\r\n // Handle async action (API calls, verification, etc.)\r\n if (step.asyncAction) {\r\n const handler = propsRef.current.actionHandlers?.[step.asyncAction.handler];\r\n if (handler) {\r\n const statusMsgId = uid();\r\n // Show loading/status message\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: {\r\n id: statusMsgId,\r\n sender: 'bot',\r\n text: step.asyncAction.loadingMessage ?? 'Processing...',\r\n timestamp: Date.now(),\r\n },\r\n });\r\n\r\n const ctx: ActionContext = {\r\n updateMessage: (text: string) => {\r\n dispatch({ type: 'UPDATE_MESSAGE', payload: { id: statusMsgId, updates: { text } } });\r\n },\r\n };\r\n\r\n try {\r\n const result = await handler(engine.getData(), ctx);\r\n\r\n // Merge result data into collected data\r\n if (result.data) {\r\n engine.mergeData(result.data);\r\n dispatch({ type: 'SET_DATA', payload: result.data });\r\n }\r\n\r\n // Update status message with final text\r\n const finalMsg =\r\n result.message ??\r\n (result.status === 'success'\r\n ? (step.asyncAction.successMessage ?? 'Done!')\r\n : (step.asyncAction.errorMessage ?? 'Something went wrong.'));\r\n dispatch({ type: 'UPDATE_MESSAGE', payload: { id: statusMsgId, updates: { text: finalMsg } } });\r\n\r\n // Route based on result\r\n const nextStepId = resolveAsyncRoute(step, result);\r\n if (nextStepId) {\r\n await delay(600);\r\n processFlowStepRef.current(nextStepId);\r\n }\r\n } catch {\r\n dispatch({\r\n type: 'UPDATE_MESSAGE',\r\n payload: { id: statusMsgId, updates: { text: step.asyncAction.errorMessage ?? '❌ Something went wrong.' } },\r\n });\r\n if (step.asyncAction.onError) {\r\n await delay(600);\r\n processFlowStepRef.current(step.asyncAction.onError);\r\n }\r\n }\r\n return; // async action handles routing — don't auto-advance\r\n }\r\n }\r\n\r\n // If step has a custom component, wait for onComplete — don't auto-advance\r\n if (step.component && propsRef.current.components?.[step.component]) {\r\n return;\r\n }\r\n\r\n // Auto-advance if no user input required\r\n if (!step.quickReplies && !step.form && !step.input && step.next) {\r\n await delay(300);\r\n processFlowStepRef.current(step.next);\r\n }\r\n };\r\n\r\n /** Determine next step from async action result */\r\n function resolveAsyncRoute(\r\n step: { asyncAction?: { onSuccess?: string; onError?: string; routes?: Record<string, string> }; next?: string },\r\n result: FlowActionResult,\r\n ): string | undefined {\r\n // 1. Explicit next from result\r\n if (result.next) return result.next;\r\n // 2. Routes map\r\n if (step.asyncAction?.routes?.[result.status]) return step.asyncAction.routes[result.status];\r\n // 3. Success/error defaults\r\n if (result.status === 'success' && step.asyncAction?.onSuccess) return step.asyncAction.onSuccess;\r\n if (result.status === 'error' && step.asyncAction?.onError) return step.asyncAction.onError;\r\n // 4. Fallback\r\n return step.next;\r\n }\r\n\r\n const processFlowStep = useCallback(\r\n (stepId: string) => processFlowStepRef.current(stepId),\r\n [],\r\n );\r\n\r\n /** Go back to the previous step */\r\n const goBack = useCallback(() => {\r\n const engine = flowRef.current;\r\n if (!engine || !engine.canGoBack()) {\r\n addSystemMessage('There is no previous step to go back to.');\r\n return;\r\n }\r\n dispatch({ type: 'CLEAR_QUICK_REPLIES' });\r\n const prevStepId = engine.popHistory();\r\n if (prevStepId) {\r\n processFlowStep(prevStepId);\r\n } else {\r\n addSystemMessage('There is no previous step to go back to.');\r\n }\r\n }, [dispatch, processFlowStep, addSystemMessage]);\r\n\r\n /** Restart the entire conversation */\r\n const restartSession = useCallback(() => {\r\n const engine = flowRef.current;\r\n if (engine) {\r\n engine.reset();\r\n }\r\n flowStartedRef.current = false;\r\n dispatch({ type: 'RESET_CHAT' });\r\n // Re-start the flow after reset\r\n if (engine) {\r\n flowStartedRef.current = true;\r\n processFlowStep(engine.getStartStepId());\r\n }\r\n }, [dispatch, processFlowStep]);\r\n\r\n /** Handle slash commands. Returns true if the text was a command. */\r\n const handleCommandRef = useRef<(text: string) => boolean>(() => false);\r\n handleCommandRef.current = (text: string): boolean => {\r\n const cmd = text.trim().toLowerCase();\r\n if (!cmd.startsWith('/')) return false;\r\n\r\n switch (cmd) {\r\n case '/help': {\r\n const lines = Object.entries(COMMANDS)\r\n .map(([k, v]) => `**${k}** — ${v}`)\r\n .join('\\n');\r\n addSystemMessage(`Available commands:\\n${lines}`);\r\n return true;\r\n }\r\n case '/cancel':\r\n case '/back': {\r\n goBack();\r\n return true;\r\n }\r\n case '/restart': {\r\n restartSession();\r\n return true;\r\n }\r\n default:\r\n addSystemMessage(`Unknown command: ${cmd}. Type /help for available commands.`);\r\n return true;\r\n }\r\n };\r\n\r\n /** Handle completion from a custom component rendered in a step */\r\n const handleComponentComplete = useCallback(\r\n (result?: FlowActionResult) => {\r\n const engine = flowRef.current;\r\n const currentStepId = stateRef.current.currentStepId;\r\n if (!engine || !currentStepId) return;\r\n\r\n const step = engine.getStep(currentStepId);\r\n if (!step) return;\r\n\r\n // Merge result data\r\n if (result?.data) {\r\n engine.mergeData(result.data);\r\n dispatch({ type: 'SET_DATA', payload: result.data });\r\n }\r\n\r\n // Show optional message\r\n if (result?.message) {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'bot', text: result.message, timestamp: Date.now() },\r\n });\r\n }\r\n\r\n // Determine next step\r\n const nextStepId = result?.next ?? step.next;\r\n if (nextStepId) {\r\n processFlowStep(nextStepId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', engine.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(engine.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n },\r\n [dispatch, processFlowStep, pluginManager],\r\n );\r\n\r\n const sendMessage = useCallback(\r\n async (text: string) => {\r\n // Check for slash commands first\r\n if (handleCommandRef.current(text)) return;\r\n\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'user',\r\n text,\r\n timestamp: Date.now(),\r\n };\r\n\r\n // Run middleware pipeline if configured\r\n const middlewares = propsRef.current.middleware;\r\n if (middlewares && middlewares.length > 0) {\r\n const data = flowRef.current?.getData() ?? {};\r\n const result = runMiddleware(msg, data, middlewares);\r\n if (!result) return; // Middleware blocked the message\r\n Object.assign(msg, result);\r\n }\r\n\r\n // Let plugins transform the message before dispatching\r\n const finalMsg = pluginManager ? await pluginManager.onMessage(msg) : msg;\r\n dispatch({ type: 'ADD_MESSAGE', payload: finalMsg });\r\n propsRef.current.callbacks?.onMessageSend?.(finalMsg);\r\n propsRef.current.callbacks?.onSubmit?.({ message: finalMsg.text });\r\n\r\n // ── Live agent mode — forward to server ──────────────────\r\n if (stateRef.current.isLiveAgent) {\r\n sendLiveMessage(finalMsg.text ?? '');\r\n return;\r\n }\r\n\r\n const currentStepId = stateRef.current.currentStepId;\r\n const typingMs = propsRef.current.typingDelay ?? 0;\r\n\r\n // ── Active flow step ─────────────────────────────────────\r\n if (flowRef.current && currentStepId) {\r\n const step = flowRef.current.getStep(currentStepId);\r\n if (step) {\r\n // Block text input during async action or component steps\r\n if (step.asyncAction || step.component) {\r\n addBotMessage(\"Please wait, I'm still processing. You can type /back to go back.\");\r\n return;\r\n }\r\n // If this step has quick replies, try to match user text\r\n if (flowRef.current.stepExpectsQuickReply(step)) {\r\n const matched = flowRef.current.matchQuickReply(step, text);\r\n if (matched) {\r\n dispatch({ type: 'CLEAR_QUICK_REPLIES' });\r\n flowRef.current.setData(step.id, matched.value);\r\n const nextId = flowRef.current.resolveNext(step, matched.value);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n } else {\r\n addBotMessage(\r\n \"I didn't quite get that. Please choose one of the options below:\",\r\n { quickReplies: step.quickReplies },\r\n );\r\n }\r\n } else if (flowRef.current.stepExpectsForm(step)) {\r\n addBotMessage(\"Please fill out the form above to continue.\");\r\n } else if (step.input) {\r\n // ── Input step with validation ──\r\n const result = validateInput(text, step.input);\r\n if (!result.valid) {\r\n addBotMessage(result.error ?? 'Invalid input. Please try again.');\r\n return;\r\n }\r\n flowRef.current.setData(step.id, result.value);\r\n const nextId = flowRef.current.resolveNext(step, result.value);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n addBotMessage(\"Thanks for your message! Our team will get back to you soon. \\u{1F64C}\");\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n } else {\r\n // Normal text input step\r\n flowRef.current.setData(step.id, text);\r\n const nextId = flowRef.current.resolveNext(step, text);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n addBotMessage(\"Thanks for your message! Our team will get back to you soon. \\u{1F64C}\");\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // ── No active flow step — try keyword / greeting / fallback ──\r\n\r\n // Build runtime keyword list (user keywords + greeting shortcut)\r\n const keywords: KeywordRoute[] = [...(propsRef.current.keywords ?? [])];\r\n if (propsRef.current.greetingResponse) {\r\n keywords.push({\r\n patterns: GREETING_PATTERNS,\r\n response: propsRef.current.greetingResponse,\r\n matchType: 'exact',\r\n priority: -1,\r\n });\r\n }\r\n\r\n if (keywords.length > 0) {\r\n const match = findKeywordMatch(text.trim(), keywords);\r\n if (match) {\r\n // Jump to flow step if route specifies `next`\r\n if (match.next && flowRef.current) {\r\n if (typingMs > 0) await delay(typingMs);\r\n processFlowStep(match.next);\r\n return;\r\n }\r\n // Send response message\r\n if (match.response) {\r\n if (typingMs > 0) {\r\n await delay(typingMs);\r\n }\r\n await addBotMessage(match.response);\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Fallback\r\n const fb = propsRef.current.fallbackMessage;\r\n if (fb) {\r\n const fbText = typeof fb === 'function' ? fb(text) : fb;\r\n if (fbText) {\r\n if (typingMs > 0) {\r\n await delay(typingMs);\r\n }\r\n await addBotMessage(fbText);\r\n return;\r\n }\r\n }\r\n\r\n // Nothing handled it — fire unhandled callback\r\n propsRef.current.callbacks?.onUnhandledMessage?.(text, { currentStepId });\r\n },\r\n [dispatch, addBotMessage, processFlowStep, pluginManager],\r\n );\r\n\r\n const startFlow = useCallback(() => {\r\n const engine = flowRef.current;\r\n if (!engine || flowStartedRef.current) return;\r\n flowStartedRef.current = true;\r\n processFlowStep(engine.getStartStepId());\r\n }, [processFlowStep]);\r\n\r\n // Auto-start flow when all conditions are met\r\n useEffect(() => {\r\n if (\r\n props.flow &&\r\n !state.showWelcome &&\r\n state.isLoggedIn &&\r\n !flowStartedRef.current\r\n ) {\r\n startFlow();\r\n }\r\n }, [props.flow, state.showWelcome, state.isLoggedIn, startFlow]);\r\n\r\n const handleQuickReply = useCallback(\r\n async (value: string, label: string) => {\r\n dispatch({ type: 'CLEAR_QUICK_REPLIES' });\r\n // Add user message\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'user',\r\n text: label,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: msg });\r\n await pluginManager?.onMessage(msg);\r\n pluginManager?.emitEvent('quickReply', { value, label });\r\n propsRef.current.callbacks?.onQuickReply?.(value, label);\r\n\r\n // Continue flow\r\n const currentStepId = stateRef.current.currentStepId;\r\n if (flowRef.current && currentStepId) {\r\n const step = flowRef.current.getStep(currentStepId);\r\n if (step) {\r\n flowRef.current.setData(step.id, value);\r\n const nextId = flowRef.current.resolveNext(step, value);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n }\r\n }\r\n },\r\n [dispatch, processFlowStep, pluginManager],\r\n );\r\n\r\n const handleFormSubmit = useCallback(\r\n async (formId: string, data: Record<string, unknown>) => {\r\n dispatch({ type: 'SET_DATA', payload: data });\r\n if (flowRef.current) {\r\n flowRef.current.mergeData(data);\r\n }\r\n\r\n // Look up form config for friendly labels\r\n const formConfig =\r\n propsRef.current.loginForm?.id === formId\r\n ? propsRef.current.loginForm\r\n : flowRef.current && stateRef.current.currentStepId\r\n ? flowRef.current.getStep(stateRef.current.currentStepId)?.form\r\n : undefined;\r\n\r\n // Build field name → { label, optionMap } lookup\r\n const fieldMeta = new Map<string, { label: string; optionMap?: Map<string, string> }>();\r\n if (formConfig) {\r\n for (const f of formConfig.fields) {\r\n const optionMap = f.options\r\n ? new Map(f.options.map((o) => [o.value, o.label]))\r\n : undefined;\r\n fieldMeta.set(f.name, { label: f.label ?? f.name, optionMap });\r\n }\r\n }\r\n\r\n // Summary message with friendly labels\r\n const summaryLines = Object.entries(data)\r\n .filter(([, v]) => v !== undefined && v !== '')\r\n .map(([k, v]) => {\r\n const meta = fieldMeta.get(k);\r\n const displayKey = meta?.label ?? k;\r\n const raw = String(v);\r\n const displayVal = meta?.optionMap?.get(raw) ?? raw;\r\n return `${displayKey}: ${displayVal}`;\r\n })\r\n .join('\\n');\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'user',\r\n text: summaryLines,\r\n formData: data,\r\n timestamp: Date.now(),\r\n };\r\n dispatch({ type: 'ADD_MESSAGE', payload: msg });\r\n await pluginManager?.onMessage(msg);\r\n await pluginManager?.onSubmit(data);\r\n\r\n await propsRef.current.callbacks?.onFormSubmit?.(formId, data);\r\n\r\n // Advance flow\r\n const currentStepId = stateRef.current.currentStepId;\r\n if (flowRef.current && currentStepId) {\r\n const step = flowRef.current.getStep(currentStepId);\r\n if (step) {\r\n const nextId = flowRef.current.resolveNext(step);\r\n if (nextId) {\r\n processFlowStep(nextId);\r\n } else {\r\n pluginManager?.emitEvent('flowEnd', flowRef.current.getData());\r\n propsRef.current.callbacks?.onFlowEnd?.(flowRef.current.getData());\r\n dispatch({ type: 'SET_STEP', payload: null });\r\n }\r\n }\r\n }\r\n },\r\n [dispatch, processFlowStep, pluginManager],\r\n );\r\n\r\n const handleLogin = useCallback(\r\n async (data: Record<string, unknown>) => {\r\n await pluginManager?.onSubmit(data);\r\n pluginManager?.emitEvent('login', data);\r\n await propsRef.current.callbacks?.onLogin?.(data);\r\n dispatch({ type: 'SET_LOGGED_IN', payload: true });\r\n },\r\n [dispatch, pluginManager],\r\n );\r\n\r\n const toggleChat = useCallback(() => {\r\n const willOpen = !stateRef.current.isOpen;\r\n dispatch({ type: 'TOGGLE_OPEN' });\r\n if (willOpen) {\r\n pluginManager?.emitEvent('open');\r\n propsRef.current.callbacks?.onOpen?.();\r\n } else {\r\n pluginManager?.emitEvent('close');\r\n propsRef.current.callbacks?.onClose?.();\r\n }\r\n }, [dispatch, pluginManager]);\r\n\r\n const dismissWelcome = useCallback(() => {\r\n dispatch({ type: 'DISMISS_WELCOME' });\r\n }, [dispatch]);\r\n\r\n return {\r\n state,\r\n sendMessage,\r\n addBotMessage,\r\n handleQuickReply,\r\n handleFormSubmit,\r\n handleLogin,\r\n toggleChat,\r\n dismissWelcome,\r\n startFlow,\r\n processFlowStep,\r\n goBack,\r\n restartSession,\r\n handleComponentComplete,\r\n requestTransfer,\r\n sendTyping,\r\n };\r\n}\r\n","import React, { useCallback, useState } from 'react';\r\nimport type { CSSProperties } from 'react';\r\nimport type { ChatStyles } from '../styles/theme';\r\nimport { ChatHeader } from './ChatHeader';\r\nimport { WelcomeScreen } from './WelcomeScreen';\r\nimport { LoginScreen } from './LoginScreen';\r\nimport { MessageList } from './MessageList';\r\nimport { ChatInput } from './ChatInput';\r\nimport { Branding } from './Branding';\r\nimport { useChat } from '../hooks/useChat';\r\nimport { useChatContext } from '../context/ChatContext';\r\nimport { resolveTheme } from '../styles/theme';\r\nimport { uid } from '../utils/helpers';\r\nimport type { MessageAttachment } from '../types/message';\r\n\r\ninterface ChatWindowProps {\r\n styles: ChatStyles;\r\n position: 'bottom-right' | 'bottom-left';\r\n zIndex?: number;\r\n hidden?: boolean;\r\n}\r\n\r\nexport const ChatWindow: React.FC<ChatWindowProps> = ({ styles, position, zIndex, hidden }) => {\r\n const { props, dispatch } = useChatContext();\r\n const theme = resolveTheme(props.theme);\r\n const isDark = theme.mode === 'dark';\r\n const {\r\n state,\r\n sendMessage,\r\n handleQuickReply,\r\n handleFormSubmit,\r\n handleLogin,\r\n toggleChat,\r\n dismissWelcome,\r\n restartSession,\r\n handleComponentComplete,\r\n } = useChat();\r\n\r\n const posStyle: CSSProperties =\r\n position === 'bottom-left'\r\n ? { bottom: '96px', left: '24px' }\r\n : { bottom: '96px', right: '24px' };\r\n\r\n const handleSendWithFiles = useCallback(\r\n (text: string, files?: File[]) => {\r\n if (files && files.length > 0) {\r\n const attachments: MessageAttachment[] = files.map((f) => ({\r\n name: f.name,\r\n url: URL.createObjectURL(f),\r\n type: f.type,\r\n size: f.size,\r\n }));\r\n if (text) {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: {\r\n id: uid(),\r\n sender: 'user',\r\n text,\r\n timestamp: Date.now(),\r\n attachments,\r\n },\r\n });\r\n sendMessage(text);\r\n } else {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: {\r\n id: uid(),\r\n sender: 'user',\r\n timestamp: Date.now(),\r\n attachments,\r\n },\r\n });\r\n }\r\n props.callbacks?.onFileUpload?.(files);\r\n } else if (text) {\r\n sendMessage(text);\r\n }\r\n },\r\n [sendMessage, dispatch, props.callbacks],\r\n );\r\n\r\n // Resolve configs from customizeChat slots\r\n const headerCfg = props.customizeChat?.header?.config ?? { title: 'Chat with us' };\r\n const brandingCfg = props.customizeChat?.branding?.config;\r\n const welcomeContent = props.customizeChat?.welcomeScreen?.content;\r\n const [searchQuery, setSearchQuery] = useState('');\r\n\r\n // Default header element\r\n const defaultHeader = (\r\n <ChatHeader\r\n config={headerCfg}\r\n styles={styles}\r\n onClose={toggleChat}\r\n onRestart={restartSession}\r\n logo={brandingCfg?.logo}\r\n logoWidth={brandingCfg?.logoWidth}\r\n onSearchChange={setSearchQuery}\r\n />\r\n );\r\n\r\n // Default input element\r\n const defaultInput = (\r\n <ChatInput\r\n onSend={handleSendWithFiles}\r\n placeholder={props.inputPlaceholder}\r\n primaryColor={theme.primaryColor}\r\n isDark={isDark}\r\n enableEmoji={props.enableEmoji}\r\n fileUpload={props.fileUpload}\r\n onFileUpload={props.callbacks?.onFileUpload}\r\n />\r\n );\r\n\r\n if (hidden) {\r\n // Keep component mounted (hooks alive) but invisible\r\n return <div style={{ display: 'none' }} />;\r\n }\r\n\r\n return (\r\n <div\r\n style={{\r\n ...styles.window,\r\n ...posStyle,\r\n ...(zIndex != null ? { zIndex } : {}),\r\n }}\r\n >\r\n {/* Header */}\r\n {props.customizeChat?.header?.component ?? defaultHeader}\r\n\r\n {/* Welcome Screen */}\r\n {state.showWelcome && welcomeContent ? (\r\n props.customizeChat?.welcomeScreen?.component\r\n ?? <WelcomeScreen\r\n content={welcomeContent}\r\n onDismiss={dismissWelcome}\r\n primaryColor={theme.primaryColor}\r\n />\r\n ) : /* Login Screen */\r\n !state.isLoggedIn && props.loginForm ? (\r\n props.customizeChat?.loginScreen?.component\r\n ?? <LoginScreen\r\n config={props.loginForm}\r\n onLogin={handleLogin}\r\n primaryColor={theme.primaryColor}\r\n renderFormField={props.renderFormField}\r\n />\r\n ) : (\r\n /* Chat Area */\r\n <>\r\n <MessageList\r\n messages={state.messages}\r\n isTyping={state.isTyping}\r\n styles={styles}\r\n primaryColor={theme.primaryColor}\r\n onQuickReply={handleQuickReply}\r\n onFormSubmit={handleFormSubmit}\r\n components={props.components}\r\n onComponentComplete={handleComponentComplete}\r\n collectedData={state.collectedData}\r\n currentStepId={state.currentStepId}\r\n renderFormField={props.renderFormField}\r\n customizeChat={props.customizeChat}\r\n searchQuery={searchQuery}\r\n />\r\n <div style={styles.inputArea}>\r\n {props.customizeChat?.input?.component ?? defaultInput}\r\n </div>\r\n {brandingCfg && (\r\n props.customizeChat?.branding?.component\r\n ?? <Branding config={brandingCfg} primaryColor={theme.primaryColor} />\r\n )}\r\n </>\r\n )}\r\n </div>\r\n );\r\n};\r\n","import type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\n/**\r\n * PluginManager — Manages plugin lifecycle (Open/Closed Principle)\r\n * Core is closed for modification, open for extension via plugins.\r\n */\r\nexport class PluginManager {\r\n private plugins: ChatPlugin[] = [];\r\n private context: PluginContext | null = null;\r\n private eventHandlers = new Map<string, Set<(...args: unknown[]) => void>>();\r\n\r\n register(plugins: ChatPlugin[]): void {\r\n this.plugins = [...plugins];\r\n }\r\n\r\n setContext(ctx: Omit<PluginContext, 'on' | 'emit'>): void {\r\n this.context = {\r\n ...ctx,\r\n on: (event, handler) => this.on(event, handler),\r\n emit: (event, ...args) => this.emit(event, ...args),\r\n };\r\n }\r\n\r\n getContext(): PluginContext | null {\r\n return this.context;\r\n }\r\n\r\n private on(event: string, handler: (...args: unknown[]) => void): void {\r\n if (!this.eventHandlers.has(event)) {\r\n this.eventHandlers.set(event, new Set());\r\n }\r\n this.eventHandlers.get(event)!.add(handler);\r\n }\r\n\r\n private emit(event: string, ...args: unknown[]): void {\r\n const handlers = this.eventHandlers.get(event);\r\n if (handlers) {\r\n handlers.forEach((handler) => handler(...args));\r\n }\r\n }\r\n\r\n async init(): Promise<void> {\r\n if (!this.context) return;\r\n const ctx = this.context;\r\n await Promise.allSettled(\r\n this.plugins.map(async (plugin) => {\r\n try {\r\n await plugin.onInit?.(ctx);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onInit error:`, err);\r\n }\r\n }),\r\n );\r\n }\r\n\r\n async onMessage(message: ChatMessage): Promise<ChatMessage> {\r\n if (!this.context) return message;\r\n let msg = message;\r\n for (const plugin of this.plugins) {\r\n try {\r\n const result = await plugin.onMessage?.(msg, this.context);\r\n if (result && typeof result === 'object' && 'id' in result) {\r\n msg = result;\r\n }\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onMessage error:`, err);\r\n }\r\n }\r\n this.dispatchEvent({ type: 'message', payload: msg, timestamp: Date.now() });\r\n return msg;\r\n }\r\n\r\n async onSubmit(data: Record<string, unknown>): Promise<void> {\r\n if (!this.context) return;\r\n for (const plugin of this.plugins) {\r\n try {\r\n await plugin.onSubmit?.(data, this.context);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onSubmit error:`, err);\r\n }\r\n }\r\n this.dispatchEvent({ type: 'submit', payload: data, timestamp: Date.now() });\r\n }\r\n\r\n /** Emit a lifecycle event to all plugins (open, close, flowEnd, stepChange, quickReply, etc.) */\r\n emitEvent(type: string, payload?: unknown): void {\r\n this.dispatchEvent({ type, payload, timestamp: Date.now() });\r\n this.emit(type, payload);\r\n }\r\n\r\n async destroy(): Promise<void> {\r\n if (!this.context) return;\r\n for (const plugin of this.plugins) {\r\n try {\r\n await plugin.onDestroy?.(this.context);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onDestroy error:`, err);\r\n }\r\n }\r\n this.eventHandlers.clear();\r\n this.plugins = [];\r\n }\r\n\r\n private dispatchEvent(event: ChatPluginEvent): void {\r\n if (!this.context) return;\r\n for (const plugin of this.plugins) {\r\n try {\r\n plugin.onEvent?.(event, this.context);\r\n } catch (err) {\r\n console.error(`[Plugin:${plugin.name}] onEvent error:`, err);\r\n }\r\n }\r\n }\r\n}\r\n","import React, { useReducer, useEffect, useRef, useCallback } from 'react';\r\nimport type { ChatBotProps } from '../types';\r\nimport { ChatContext, chatReducer, initialState } from '../context/ChatContext';\r\nimport { resolveTheme, buildStyles, buildCSSVariables } from '../styles/theme';\r\nimport { Launcher } from './Launcher';\r\nimport { ChatWindow } from './ChatWindow';\r\nimport { PluginManager } from '../core/PluginManager';\r\nimport { uid } from '../utils/helpers';\r\n\r\nconst GLOBAL_STYLES = `\r\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\r\n\r\n@keyframes cb-window-enter {\r\n 0% { opacity: 0; transform: translateY(16px) scale(0.96); }\r\n 100% { opacity: 1; transform: translateY(0) scale(1); }\r\n}\r\n\r\n@keyframes cb-launcher-pulse {\r\n 0%, 100% { box-shadow: 0 6px 24px rgba(108, 92, 231, 0.4), 0 2px 8px rgba(0,0,0,0.1); }\r\n 50% { box-shadow: 0 8px 32px rgba(108, 92, 231, 0.55), 0 4px 12px rgba(0,0,0,0.15); }\r\n}\r\n\r\n@keyframes cb-fade-in {\r\n 0% { opacity: 0; transform: translateY(6px); }\r\n 100% { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n@keyframes cb-typing-bounce {\r\n 0%, 80%, 100% { transform: scale(0.6); opacity: 0.3; }\r\n 40% { transform: scale(1); opacity: 1; }\r\n}\r\n\r\n@keyframes cb-slide-up {\r\n 0% { opacity: 0; transform: translateY(10px); }\r\n 100% { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n.cb-scrollbar::-webkit-scrollbar {\r\n width: 5px;\r\n}\r\n.cb-scrollbar::-webkit-scrollbar-track {\r\n background: transparent;\r\n}\r\n.cb-scrollbar::-webkit-scrollbar-thumb {\r\n background: rgba(108, 92, 231, 0.2);\r\n border-radius: 10px;\r\n}\r\n.cb-scrollbar::-webkit-scrollbar-thumb:hover {\r\n background: rgba(108, 92, 231, 0.35);\r\n}\r\n`;\r\n\r\n// Inject styles globally once per document, not per component instance\r\nlet globalStyleInjected = false;\r\nfunction ensureGlobalStyles() {\r\n if (globalStyleInjected) return;\r\n if (typeof document === 'undefined') return;\r\n if (document.querySelector('style[data-chatbot-styles]')) {\r\n globalStyleInjected = true;\r\n return;\r\n }\r\n const style = document.createElement('style');\r\n style.setAttribute('data-chatbot-styles', '');\r\n style.textContent = GLOBAL_STYLES;\r\n document.head.appendChild(style);\r\n globalStyleInjected = true;\r\n}\r\n\r\nexport const ChatBot: React.FC<ChatBotProps> = (props) => {\r\n const [state, dispatch] = useReducer(chatReducer, props, initialState);\r\n const theme = resolveTheme(props.theme);\r\n const styles = buildStyles(theme, props.style);\r\n const cssVars = buildCSSVariables(theme);\r\n const position = props.position ?? 'bottom-right';\r\n const showLauncher = props.showLauncher !== false;\r\n const pluginManagerRef = useRef<PluginManager | null>(null);\r\n\r\n // Use refs so plugin context always reads fresh state\r\n const stateRef = useRef(state);\r\n stateRef.current = state;\r\n\r\n // Inject global styles once\r\n useEffect(() => {\r\n ensureGlobalStyles();\r\n }, []);\r\n\r\n // Initialize plugins\r\n useEffect(() => {\r\n if (props.plugins && props.plugins.length > 0) {\r\n const pm = new PluginManager();\r\n pm.register(props.plugins);\r\n pm.setContext({\r\n sendMessage: (text) => {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'user', text, timestamp: Date.now() },\r\n });\r\n },\r\n addBotMessage: (text) => {\r\n dispatch({\r\n type: 'ADD_MESSAGE',\r\n payload: { id: uid(), sender: 'bot', text, timestamp: Date.now() },\r\n });\r\n },\r\n getMessages: () => stateRef.current.messages,\r\n getData: () => stateRef.current.collectedData,\r\n setData: (key, value) => dispatch({ type: 'SET_DATA', payload: { [key]: value } }),\r\n });\r\n pm.init();\r\n pluginManagerRef.current = pm;\r\n\r\n return () => {\r\n pm.destroy();\r\n };\r\n }\r\n }, [props.plugins]);\r\n\r\n const handleToggle = useCallback(() => {\r\n const willOpen = !state.isOpen;\r\n dispatch({ type: 'TOGGLE_OPEN' });\r\n if (willOpen) props.callbacks?.onOpen?.();\r\n else props.callbacks?.onClose?.();\r\n }, [state.isOpen, props.callbacks]);\r\n\r\n // Headless mode — no UI rendered, only engine/plugins run\r\n if (props.headless) return null;\r\n\r\n return (\r\n <ChatContext.Provider value={{ state, dispatch, props, pluginManager: pluginManagerRef.current }}>\r\n <div style={{ ...styles.root, ...cssVars as React.CSSProperties }} className={props.className}>\r\n <ChatWindow styles={styles} position={position} zIndex={props.zIndex} hidden={!state.isOpen} />\r\n {showLauncher && (\r\n props.customizeChat?.launcher?.component\r\n ?? <Launcher\r\n onClick={handleToggle}\r\n isOpen={state.isOpen}\r\n position={position}\r\n styles={styles}\r\n icon={props.launcherIcon}\r\n closeIcon={props.closeIcon}\r\n zIndex={props.zIndex}\r\n />\r\n )}\r\n </div>\r\n </ChatContext.Provider>\r\n );\r\n};\r\n","// ─── Event Bus ───────────────────────────────────────────────────\r\n// A lightweight pub/sub event bus for decoupled communication between\r\n// plugins, components, and external code.\r\n\r\nexport type EventHandler = (...args: unknown[]) => void;\r\n\r\nexport interface EventBus {\r\n /** Subscribe to an event */\r\n on(event: string, handler: EventHandler): () => void;\r\n /** Subscribe once — auto-removes after first call */\r\n once(event: string, handler: EventHandler): () => void;\r\n /** Emit an event to all subscribers */\r\n emit(event: string, ...args: unknown[]): void;\r\n /** Remove a specific handler */\r\n off(event: string, handler: EventHandler): void;\r\n /** Remove all handlers for an event (or all events if no event specified) */\r\n clear(event?: string): void;\r\n}\r\n\r\n/**\r\n * Create a standalone event bus instance.\r\n *\r\n * @example\r\n * ```ts\r\n * import { createEventBus } from '@enjoys/react-chatbot-plugin';\r\n *\r\n * const bus = createEventBus();\r\n * bus.on('user:login', (data) => console.log(data));\r\n * bus.emit('user:login', { name: 'Alice' });\r\n * ```\r\n */\r\nexport function createEventBus(): EventBus {\r\n const handlers = new Map<string, Set<EventHandler>>();\r\n\r\n const getSet = (event: string): Set<EventHandler> => {\r\n if (!handlers.has(event)) handlers.set(event, new Set());\r\n return handlers.get(event)!;\r\n };\r\n\r\n return {\r\n on(event, handler) {\r\n getSet(event).add(handler);\r\n return () => getSet(event).delete(handler);\r\n },\r\n\r\n once(event, handler) {\r\n const wrapper: EventHandler = (...args) => {\r\n getSet(event).delete(wrapper);\r\n handler(...args);\r\n };\r\n getSet(event).add(wrapper);\r\n return () => getSet(event).delete(wrapper);\r\n },\r\n\r\n emit(event, ...args) {\r\n const set = handlers.get(event);\r\n if (set) set.forEach((fn) => fn(...args));\r\n },\r\n\r\n off(event, handler) {\r\n getSet(event).delete(handler);\r\n },\r\n\r\n clear(event?) {\r\n if (event) handlers.delete(event);\r\n else handlers.clear();\r\n },\r\n };\r\n}\r\n","// ─── Headless Mode ───────────────────────────────────────────────\r\n// Run the chatbot engine without any UI — useful for testing,\r\n// server-side flows, CLI bots, or custom rendering.\r\n\r\nimport type { ChatMessage } from '../types/message';\r\nimport type { FlowConfig } from '../types/flow';\r\nimport type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { FlowEngine } from '../engine/FlowEngine';\r\nimport { createEventBus, type EventBus } from './EventBus';\r\nimport { uid } from '../utils/helpers';\r\n\r\nexport interface HeadlessBotOptions {\r\n flow?: FlowConfig;\r\n plugins?: ChatPlugin[];\r\n initialMessages?: ChatMessage[];\r\n onMessage?: (message: ChatMessage) => void;\r\n}\r\n\r\nexport interface HeadlessBot {\r\n /** Send a user message into the bot */\r\n sendMessage: (text: string) => Promise<void>;\r\n /** Programmatically add a bot message */\r\n addBotMessage: (text: string) => void;\r\n /** Get all messages in the conversation */\r\n getMessages: () => ChatMessage[];\r\n /** Get collected data */\r\n getData: () => Record<string, unknown>;\r\n /** Set a data field */\r\n setData: (key: string, value: unknown) => void;\r\n /** Get the current step ID (null if no flow) */\r\n getCurrentStep: () => string | null;\r\n /** Go to a specific flow step */\r\n goToStep: (stepId: string) => Promise<void>;\r\n /** Reset the bot state */\r\n reset: () => void;\r\n /** The event bus for this instance */\r\n bus: EventBus;\r\n /** Destroy the bot — cleans up plugins */\r\n destroy: () => Promise<void>;\r\n}\r\n\r\n/**\r\n * Create a headless chatbot instance (no UI).\r\n *\r\n * @example\r\n * ```ts\r\n * import { createHeadlessBot } from '@enjoys/react-chatbot-plugin';\r\n *\r\n * const bot = createHeadlessBot({\r\n * flow: myFlow,\r\n * plugins: [loggerPlugin()],\r\n * onMessage: (msg) => console.log(msg.sender, msg.text),\r\n * });\r\n *\r\n * await bot.sendMessage('hello');\r\n * console.log(bot.getMessages());\r\n * ```\r\n */\r\nexport function createHeadlessBot(options: HeadlessBotOptions = {}): HeadlessBot {\r\n const bus = createEventBus();\r\n const messages: ChatMessage[] = [...(options.initialMessages ?? [])];\r\n const data: Record<string, unknown> = {};\r\n let currentStepId: string | null = null;\r\n let engine: FlowEngine | null = null;\r\n\r\n if (options.flow) {\r\n engine = new FlowEngine(options.flow);\r\n }\r\n\r\n // Build plugin context\r\n const ctx: PluginContext = {\r\n sendMessage: (text) => bot.sendMessage(text),\r\n addBotMessage: (text) => bot.addBotMessage(text),\r\n getMessages: () => [...messages],\r\n getData: () => ({ ...data }),\r\n setData: (key, value) => { data[key] = value; },\r\n on: (event, handler) => { bus.on(event, handler); },\r\n emit: (event, ...args) => { bus.emit(event, ...args); },\r\n };\r\n\r\n // Initialize plugins\r\n const plugins = options.plugins ?? [];\r\n const initPlugins = async () => {\r\n for (const plugin of plugins) {\r\n await plugin.onInit?.(ctx);\r\n }\r\n };\r\n\r\n const addMessage = (msg: ChatMessage) => {\r\n messages.push(msg);\r\n options.onMessage?.(msg);\r\n bus.emit('message', msg);\r\n };\r\n\r\n const processStep = async (stepId: string) => {\r\n if (!engine) return;\r\n const step = engine.getStep(stepId);\r\n if (!step) return;\r\n\r\n // visibleIf check\r\n if (!engine.isStepVisible(step)) {\r\n const nextId = engine.resolveNext(step);\r\n if (nextId) await processStep(nextId);\r\n return;\r\n }\r\n\r\n currentStepId = stepId;\r\n engine.pushHistory(stepId);\r\n bus.emit('stepChange', { stepId });\r\n\r\n const botMessages = engine.buildMessages(step);\r\n for (const m of botMessages) {\r\n addMessage(m);\r\n }\r\n };\r\n\r\n const bot: HeadlessBot = {\r\n async sendMessage(text: string) {\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'user',\r\n text,\r\n timestamp: Date.now(),\r\n };\r\n\r\n // Let plugins transform message\r\n let finalMsg = msg;\r\n for (const plugin of plugins) {\r\n if (plugin.onMessage) {\r\n const result = await plugin.onMessage(finalMsg, ctx);\r\n if (result) finalMsg = result;\r\n }\r\n }\r\n\r\n addMessage(finalMsg);\r\n\r\n // Process flow\r\n if (engine && currentStepId) {\r\n const step = engine.getStep(currentStepId);\r\n if (step) {\r\n if (step.input) {\r\n engine.setData(step.id, text);\r\n } else if (step.quickReplies) {\r\n const matched = engine.matchQuickReply(step, text);\r\n if (matched) engine.setData(step.id, matched.value);\r\n }\r\n const nextId = engine.resolveNext(step, text);\r\n if (nextId) await processStep(nextId);\r\n }\r\n }\r\n\r\n // Emit to plugins\r\n for (const plugin of plugins) {\r\n plugin.onEvent?.({ type: 'message:sent', payload: finalMsg, timestamp: Date.now() }, ctx);\r\n }\r\n },\r\n\r\n addBotMessage(text: string) {\r\n const msg: ChatMessage = {\r\n id: uid(),\r\n sender: 'bot',\r\n text,\r\n timestamp: Date.now(),\r\n };\r\n addMessage(msg);\r\n },\r\n\r\n getMessages: () => [...messages],\r\n getData: () => ({ ...data, ...(engine?.getData() ?? {}) }),\r\n setData: (key, value) => {\r\n data[key] = value;\r\n engine?.setData(key, value);\r\n },\r\n getCurrentStep: () => currentStepId,\r\n\r\n async goToStep(stepId: string) {\r\n await processStep(stepId);\r\n },\r\n\r\n reset() {\r\n messages.length = 0;\r\n Object.keys(data).forEach((k) => delete data[k]);\r\n currentStepId = null;\r\n engine?.reset();\r\n },\r\n\r\n bus,\r\n\r\n async destroy() {\r\n for (const plugin of plugins) {\r\n await plugin.onDestroy?.(ctx);\r\n }\r\n bus.clear();\r\n },\r\n };\r\n\r\n // Start: init plugins and enter first flow step\r\n initPlugins().then(() => {\r\n if (engine) {\r\n processStep(engine.getStartStepId());\r\n }\r\n });\r\n\r\n return bot;\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Analytics Plugin — tracks sessions, messages, forms, timing, and step progression\r\n */\r\nexport function analyticsPlugin(options?: {\r\n onTrack?: (event: string, data?: unknown) => void;\r\n sessionId?: string;\r\n}): ChatPlugin {\r\n const sessionId = options?.sessionId ?? `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\r\n let messageCount = 0;\r\n let formSubmissions = 0;\r\n let startTime = 0;\r\n const stepTimings: Record<string, number> = {};\r\n let lastStepTime = 0;\r\n\r\n const track = (event: string, data?: unknown) => {\r\n options?.onTrack?.(event, { sessionId, ...(data as Record<string, unknown>) });\r\n };\r\n\r\n return {\r\n name: 'analytics',\r\n\r\n onInit() {\r\n messageCount = 0;\r\n formSubmissions = 0;\r\n startTime = Date.now();\r\n lastStepTime = startTime;\r\n track('chatbot:init', { timestamp: startTime });\r\n },\r\n\r\n onMessage(message) {\r\n messageCount++;\r\n track('chatbot:message', {\r\n sender: message.sender,\r\n messageCount,\r\n elapsed: Date.now() - startTime,\r\n });\r\n },\r\n\r\n onSubmit(data) {\r\n formSubmissions++;\r\n track('chatbot:submit', {\r\n formSubmissions,\r\n fields: Object.keys(data),\r\n elapsed: Date.now() - startTime,\r\n });\r\n },\r\n\r\n onEvent(event) {\r\n switch (event.type) {\r\n case 'open':\r\n track('chatbot:open');\r\n break;\r\n case 'close':\r\n track('chatbot:close', { duration: Date.now() - startTime });\r\n break;\r\n case 'stepChange': {\r\n const now = Date.now();\r\n const payload = event.payload as { stepId: string } | undefined;\r\n if (payload?.stepId) {\r\n stepTimings[payload.stepId] = now - lastStepTime;\r\n lastStepTime = now;\r\n track('chatbot:step', { stepId: payload.stepId, stepDuration: stepTimings[payload.stepId] });\r\n }\r\n break;\r\n }\r\n case 'flowEnd':\r\n track('chatbot:flowEnd', {\r\n totalMessages: messageCount,\r\n totalForms: formSubmissions,\r\n duration: Date.now() - startTime,\r\n stepTimings,\r\n });\r\n break;\r\n case 'quickReply':\r\n track('chatbot:quickReply', event.payload);\r\n break;\r\n }\r\n },\r\n\r\n onDestroy() {\r\n track('chatbot:destroy', {\r\n totalMessages: messageCount,\r\n totalFormSubmissions: formSubmissions,\r\n sessionDuration: Date.now() - startTime,\r\n stepTimings,\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\r\nconst LEVELS: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };\r\n\r\n/**\r\n * Logger Plugin — logs all chatbot events for debugging or auditing\r\n */\r\nexport function loggerPlugin(options?: {\r\n level?: LogLevel;\r\n prefix?: string;\r\n logger?: Pick<Console, 'debug' | 'info' | 'warn' | 'error'>;\r\n}): ChatPlugin {\r\n const minLevel = LEVELS[options?.level ?? 'debug'];\r\n const prefix = options?.prefix ?? '[ChatBot]';\r\n const log = options?.logger ?? console;\r\n\r\n const write = (level: LogLevel, ...args: unknown[]) => {\r\n if (LEVELS[level] >= minLevel) log[level](prefix, ...args);\r\n };\r\n\r\n return {\r\n name: 'logger',\r\n\r\n onInit() {\r\n write('info', 'Initialized');\r\n },\r\n\r\n onMessage(message) {\r\n write('debug', `Message [${message.sender}]:`, message.text ?? '(no text)');\r\n },\r\n\r\n onSubmit(data) {\r\n write('info', 'Form submitted:', Object.keys(data));\r\n },\r\n\r\n onEvent(event) {\r\n write('debug', `Event [${event.type}]:`, event.payload ?? '');\r\n },\r\n\r\n onDestroy() {\r\n write('info', 'Destroyed');\r\n },\r\n };\r\n}\r\n","/** Shared HTTP utility for plugins that communicate with external endpoints */\r\nexport interface HttpOptions {\r\n url: string;\r\n headers?: Record<string, string>;\r\n timeout?: number;\r\n}\r\n\r\nexport async function postJSON(\r\n url: string,\r\n body: unknown,\r\n headers?: Record<string, string>,\r\n timeout = 10000,\r\n): Promise<Response> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n return await fetch(url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...headers },\r\n body: JSON.stringify(body),\r\n signal: controller.signal,\r\n });\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n}\r\n\r\nexport async function getJSON<T = unknown>(\r\n url: string,\r\n headers?: Record<string, string>,\r\n timeout = 10000,\r\n): Promise<T> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const res = await fetch(url, {\r\n headers: { 'Accept': 'application/json', ...headers },\r\n signal: controller.signal,\r\n });\r\n return (await res.json()) as T;\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\ntype WebhookEventType = 'message' | 'submit' | 'init' | 'destroy' | 'open' | 'close' | 'flowEnd' | 'stepChange' | 'quickReply' | 'login';\r\n\r\n/**\r\n * Webhook Plugin — sends messages/submissions/lifecycle events to an external endpoint\r\n */\r\nexport function webhookPlugin(options: {\r\n url: string;\r\n headers?: Record<string, string>;\r\n events?: WebhookEventType[];\r\n timeout?: number;\r\n}): ChatPlugin {\r\n const events = new Set<string>(options.events ?? ['message', 'submit']);\r\n const timeout = options.timeout ?? 10000;\r\n\r\n const send = async (type: string, payload: unknown) => {\r\n try {\r\n await postJSON(options.url, { type, payload, timestamp: Date.now() }, options.headers, timeout);\r\n } catch (err) {\r\n console.error(`[webhook] Failed to send ${type}:`, err);\r\n }\r\n };\r\n\r\n return {\r\n name: 'webhook',\r\n\r\n async onInit() {\r\n if (events.has('init')) await send('init', {});\r\n },\r\n\r\n async onMessage(message) {\r\n if (events.has('message')) await send('message', message);\r\n },\r\n\r\n async onSubmit(data) {\r\n if (events.has('submit')) await send('submit', data);\r\n },\r\n\r\n onEvent(event) {\r\n if (events.has(event.type)) {\r\n send(event.type, event.payload);\r\n }\r\n },\r\n\r\n async onDestroy() {\r\n if (events.has('destroy')) await send('destroy', {});\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * CRM Plugin — pushes user/lead data to CRM systems\r\n */\r\nexport function crmPlugin(options: {\r\n endpoint: string;\r\n provider?: string;\r\n headers?: Record<string, string>;\r\n mapFields?: (data: Record<string, unknown>) => Record<string, unknown>;\r\n events?: ('submit' | 'flowEnd' | 'login')[];\r\n}): ChatPlugin {\r\n const events = new Set(options.events ?? ['submit', 'flowEnd']);\r\n\r\n const push = async (type: string, data: Record<string, unknown>) => {\r\n const mapped = options.mapFields ? options.mapFields(data) : data;\r\n try {\r\n await postJSON(options.endpoint, {\r\n provider: options.provider,\r\n type,\r\n data: mapped,\r\n timestamp: Date.now(),\r\n }, options.headers);\r\n } catch (err) {\r\n console.error('[crm] Push failed:', err);\r\n }\r\n };\r\n\r\n return {\r\n name: 'crm',\r\n\r\n async onSubmit(data) {\r\n if (events.has('submit')) await push('submit', data);\r\n },\r\n\r\n onEvent(event) {\r\n if (events.has(event.type as string) && event.payload) {\r\n push(event.type, event.payload as Record<string, unknown>);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * Email Plugin — triggers emails via external API when chat events occur\r\n */\r\nexport function emailPlugin(options: {\r\n endpoint: string;\r\n template?: string;\r\n headers?: Record<string, string>;\r\n triggers?: ('submit' | 'flowEnd' | 'login')[];\r\n mapPayload?: (data: Record<string, unknown>) => Record<string, unknown>;\r\n}): ChatPlugin {\r\n const triggers = new Set(options.triggers ?? ['flowEnd']);\r\n\r\n const send = async (trigger: string, data: Record<string, unknown>) => {\r\n const payload = options.mapPayload ? options.mapPayload(data) : data;\r\n try {\r\n await postJSON(options.endpoint, {\r\n template: options.template,\r\n trigger,\r\n data: payload,\r\n timestamp: Date.now(),\r\n }, options.headers);\r\n } catch (err) {\r\n console.error('[email] Send failed:', err);\r\n }\r\n };\r\n\r\n return {\r\n name: 'email',\r\n\r\n async onSubmit(data) {\r\n if (triggers.has('submit')) await send('submit', data);\r\n },\r\n\r\n onEvent(event) {\r\n if (triggers.has(event.type) && event.payload) {\r\n send(event.type, event.payload as Record<string, unknown>);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * AI Plugin — generates bot responses using external AI providers (OpenAI, Anthropic, custom)\r\n */\r\nexport function aiPlugin(options: {\r\n provider?: 'openai' | 'anthropic' | 'custom';\r\n endpoint?: string;\r\n apiKey?: string;\r\n model?: string;\r\n systemPrompt?: string;\r\n headers?: Record<string, string>;\r\n shouldRespond?: (text: string) => boolean;\r\n parseResponse?: (response: unknown) => string;\r\n timeout?: number;\r\n}): ChatPlugin {\r\n const endpoint = options.endpoint ?? (\r\n options.provider === 'openai' ? 'https://api.openai.com/v1/chat/completions'\r\n : options.provider === 'anthropic' ? 'https://api.anthropic.com/v1/messages'\r\n : options.endpoint ?? ''\r\n );\r\n const model = options.model ?? (options.provider === 'openai' ? 'gpt-3.5-turbo' : 'claude-3-haiku-20240307');\r\n const conversationHistory: Array<{ role: string; content: string }> = [];\r\n\r\n if (options.systemPrompt) {\r\n conversationHistory.push({ role: 'system', content: options.systemPrompt });\r\n }\r\n\r\n const buildHeaders = (): Record<string, string> => {\r\n const h: Record<string, string> = { ...options.headers };\r\n if (options.apiKey) {\r\n if (options.provider === 'anthropic') {\r\n h['x-api-key'] = options.apiKey;\r\n h['anthropic-version'] = '2023-06-01';\r\n } else {\r\n h['Authorization'] = `Bearer ${options.apiKey}`;\r\n }\r\n }\r\n return h;\r\n };\r\n\r\n const buildBody = () => {\r\n if (options.provider === 'anthropic') {\r\n return {\r\n model,\r\n max_tokens: 1024,\r\n system: options.systemPrompt,\r\n messages: conversationHistory.filter(m => m.role !== 'system'),\r\n };\r\n }\r\n return { model, messages: conversationHistory };\r\n };\r\n\r\n const defaultParse = (res: unknown): string => {\r\n const data = res as Record<string, unknown>;\r\n // OpenAI format\r\n if (data.choices) {\r\n const choices = data.choices as Array<{ message?: { content?: string } }>;\r\n return choices[0]?.message?.content ?? '';\r\n }\r\n // Anthropic format\r\n if (data.content) {\r\n const blocks = data.content as Array<{ text?: string }>;\r\n return blocks[0]?.text ?? '';\r\n }\r\n return String(data);\r\n };\r\n\r\n return {\r\n name: 'ai',\r\n\r\n async onMessage(message, ctx: PluginContext) {\r\n if (message.sender !== 'user' || !message.text) return;\r\n if (options.shouldRespond && !options.shouldRespond(message.text)) return;\r\n if (!endpoint) return;\r\n\r\n conversationHistory.push({ role: 'user', content: message.text });\r\n\r\n try {\r\n const res = await postJSON(endpoint, buildBody(), buildHeaders(), options.timeout ?? 30000);\r\n const json = await res.json();\r\n const text = options.parseResponse ? options.parseResponse(json) : defaultParse(json);\r\n\r\n if (text) {\r\n conversationHistory.push({ role: 'assistant', content: text });\r\n ctx.addBotMessage(text);\r\n }\r\n } catch (err) {\r\n console.error('[ai] Response failed:', err);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\ninterface IntentRule {\r\n intent: string;\r\n patterns: string[];\r\n matchType?: 'contains' | 'exact' | 'regex';\r\n}\r\n\r\n/**\r\n * Intent Plugin — detects user intent from text and emits intent events for dynamic routing\r\n */\r\nexport function intentPlugin(options?: {\r\n rules?: IntentRule[];\r\n onIntentDetected?: (intent: string, text: string, ctx: PluginContext) => void;\r\n fallbackIntent?: string;\r\n}): ChatPlugin {\r\n const rules = options?.rules ?? [];\r\n const fallback = options?.fallbackIntent ?? 'unknown';\r\n\r\n const detectIntent = (text: string): string => {\r\n const lower = text.toLowerCase().trim();\r\n for (const rule of rules) {\r\n for (const pattern of rule.patterns) {\r\n const pat = pattern.toLowerCase();\r\n switch (rule.matchType ?? 'contains') {\r\n case 'exact':\r\n if (lower === pat) return rule.intent;\r\n break;\r\n case 'regex':\r\n try { if (new RegExp(pat, 'i').test(text)) return rule.intent; } catch { /* skip */ }\r\n break;\r\n case 'contains':\r\n default:\r\n if (lower.includes(pat)) return rule.intent;\r\n break;\r\n }\r\n }\r\n }\r\n return fallback;\r\n };\r\n\r\n return {\r\n name: 'intent',\r\n\r\n onMessage(message, ctx) {\r\n if (message.sender !== 'user' || !message.text) return;\r\n const intent = detectIntent(message.text);\r\n ctx.emit('intent:detected', { intent, text: message.text });\r\n options?.onIntentDetected?.(intent, message.text, ctx);\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Typing Plugin — adds configurable typing delay before bot messages are shown\r\n */\r\nexport function typingPlugin(options?: {\r\n delay?: number;\r\n onTypingStart?: () => void;\r\n onTypingEnd?: () => void;\r\n}): ChatPlugin {\r\n const typingDelay = options?.delay ?? 1000;\r\n\r\n return {\r\n name: 'typing',\r\n\r\n async onMessage(message, ctx) {\r\n if (message.sender === 'bot') {\r\n options?.onTypingStart?.();\r\n ctx.emit('typing:start', {});\r\n await new Promise((resolve) => setTimeout(resolve, typingDelay));\r\n options?.onTypingEnd?.();\r\n ctx.emit('typing:end', {});\r\n }\r\n },\r\n };\r\n}\r\n","/** Shared timer utility for plugins that schedule delayed actions */\r\nexport class TimerManager {\r\n private timers = new Map<string, ReturnType<typeof setTimeout>>();\r\n private intervals = new Map<string, ReturnType<typeof setInterval>>();\r\n\r\n setTimeout(id: string, fn: () => void, ms: number): void {\r\n this.clearTimeout(id);\r\n this.timers.set(id, setTimeout(() => { this.timers.delete(id); fn(); }, ms));\r\n }\r\n\r\n setInterval(id: string, fn: () => void, ms: number): void {\r\n this.clearInterval(id);\r\n this.intervals.set(id, setInterval(fn, ms));\r\n }\r\n\r\n clearTimeout(id: string): void {\r\n const t = this.timers.get(id);\r\n if (t) { clearTimeout(t); this.timers.delete(id); }\r\n }\r\n\r\n clearInterval(id: string): void {\r\n const i = this.intervals.get(id);\r\n if (i) { clearInterval(i); this.intervals.delete(id); }\r\n }\r\n\r\n destroy(): void {\r\n this.timers.forEach((t) => clearTimeout(t));\r\n this.intervals.forEach((i) => clearInterval(i));\r\n this.timers.clear();\r\n this.intervals.clear();\r\n }\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\n/**\r\n * Auto Reply Plugin — sends automated replies when user is inactive\r\n */\r\nexport function autoReplyPlugin(options?: {\r\n timeout?: number;\r\n message?: string;\r\n maxReplies?: number;\r\n onlyWhenOpen?: boolean;\r\n}): ChatPlugin {\r\n const timeout = options?.timeout ?? 30000;\r\n const message = options?.message ?? \"Are you still there? Let me know if you need help! 👋\";\r\n const maxReplies = options?.maxReplies ?? 2;\r\n const timers = new TimerManager();\r\n let replyCount = 0;\r\n let active = false;\r\n\r\n return {\r\n name: 'autoReply',\r\n\r\n onInit(ctx) {\r\n active = true;\r\n replyCount = 0;\r\n },\r\n\r\n onMessage(msg, ctx) {\r\n // Reset timer on any user message\r\n if (msg.sender === 'user') {\r\n replyCount = 0;\r\n timers.clearTimeout('idle');\r\n if (active) {\r\n timers.setTimeout('idle', () => {\r\n if (replyCount < maxReplies) {\r\n replyCount++;\r\n ctx.addBotMessage(message);\r\n }\r\n }, timeout);\r\n }\r\n }\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'open') {\r\n active = true;\r\n } else if (event.type === 'close') {\r\n active = false;\r\n timers.clearTimeout('idle');\r\n }\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\ntype Validator = (text: string) => string | null;\r\n\r\n/**\r\n * Validation Plugin — adds advanced validation rules for user inputs\r\n */\r\nexport function validationPlugin(options?: {\r\n validators?: Record<string, Validator>;\r\n sanitize?: boolean;\r\n blockProfanity?: boolean;\r\n profanityList?: string[];\r\n onValidationFail?: (text: string, error: string) => void;\r\n}): ChatPlugin {\r\n const validators = options?.validators ?? {};\r\n const sanitize = options?.sanitize ?? true;\r\n const profanityList = options?.profanityList ?? [];\r\n\r\n const sanitizeHtml = (text: string): string => {\r\n return text.replace(/[<>&\"']/g, (c) => {\r\n const map: Record<string, string> = { '<': '&lt;', '>': '&gt;', '&': '&amp;', '\"': '&quot;', \"'\": '&#39;' };\r\n return map[c] ?? c;\r\n });\r\n };\r\n\r\n const checkProfanity = (text: string): string | null => {\r\n if (!options?.blockProfanity || !profanityList.length) return null;\r\n const lower = text.toLowerCase();\r\n for (const word of profanityList) {\r\n if (lower.includes(word.toLowerCase())) return 'Message contains inappropriate content.';\r\n }\r\n return null;\r\n };\r\n\r\n return {\r\n name: 'validation',\r\n\r\n onMessage(message: ChatMessage, ctx: PluginContext) {\r\n if (message.sender !== 'user' || !message.text) return;\r\n\r\n // Profanity check\r\n const profanityError = checkProfanity(message.text);\r\n if (profanityError) {\r\n options?.onValidationFail?.(message.text, profanityError);\r\n ctx.emit('validation:fail', { text: message.text, error: profanityError });\r\n return { ...message, text: '***' };\r\n }\r\n\r\n // Custom validators\r\n for (const [name, validate] of Object.entries(validators)) {\r\n const error = validate(message.text);\r\n if (error) {\r\n options?.onValidationFail?.(message.text, error);\r\n ctx.emit('validation:fail', { text: message.text, validator: name, error });\r\n }\r\n }\r\n\r\n // Sanitize\r\n if (sanitize) {\r\n return { ...message, text: sanitizeHtml(message.text) };\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Upload Plugin — handles file uploads to external storage (S3, etc.)\r\n */\r\nexport function uploadPlugin(options: {\r\n endpoint: string;\r\n storage?: 's3' | 'gcs' | 'azure' | 'custom';\r\n headers?: Record<string, string>;\r\n maxSize?: number;\r\n allowedTypes?: string[];\r\n onUploadStart?: (file: { name: string; size: number }) => void;\r\n onUploadComplete?: (file: { name: string; url: string }) => void;\r\n onUploadError?: (file: { name: string }, error: Error) => void;\r\n}): ChatPlugin {\r\n return {\r\n name: 'upload',\r\n\r\n onInit(ctx) {\r\n ctx.on('file:upload', async (...args: unknown[]) => {\r\n const files = args[0] as File[];\r\n if (!files?.length) return;\r\n\r\n for (const file of files) {\r\n // Validate size\r\n if (options.maxSize && file.size > options.maxSize) {\r\n options.onUploadError?.({ name: file.name }, new Error(`File too large: ${file.size} > ${options.maxSize}`));\r\n continue;\r\n }\r\n\r\n // Validate type\r\n if (options.allowedTypes?.length && !options.allowedTypes.some(t => file.type.startsWith(t))) {\r\n options.onUploadError?.({ name: file.name }, new Error(`File type not allowed: ${file.type}`));\r\n continue;\r\n }\r\n\r\n options.onUploadStart?.({ name: file.name, size: file.size });\r\n\r\n try {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n formData.append('storage', options.storage ?? 'custom');\r\n\r\n const res = await fetch(options.endpoint, {\r\n method: 'POST',\r\n headers: options.headers,\r\n body: formData,\r\n });\r\n\r\n if (!res.ok) throw new Error(`Upload failed: ${res.status}`);\r\n\r\n const result = await res.json() as { url?: string };\r\n options.onUploadComplete?.({ name: file.name, url: result.url ?? '' });\r\n ctx.emit('file:uploaded', { name: file.name, url: result.url });\r\n } catch (err) {\r\n options.onUploadError?.({ name: file.name }, err as Error);\r\n }\r\n }\r\n });\r\n },\r\n };\r\n}\r\n","/** Shared browser storage utility for plugins */\r\nexport interface StorageAdapter {\r\n get(key: string): string | null;\r\n set(key: string, value: string): void;\r\n remove(key: string): void;\r\n}\r\n\r\nexport function createStorageAdapter(type: 'local' | 'session' = 'local'): StorageAdapter {\r\n const store = type === 'session' ? sessionStorage : localStorage;\r\n return {\r\n get: (key) => { try { return store.getItem(key); } catch { return null; } },\r\n set: (key, value) => { try { store.setItem(key, value); } catch { /* storage full */ } },\r\n remove: (key) => { try { store.removeItem(key); } catch { /* noop */ } },\r\n };\r\n}\r\n\r\nexport function safeJsonParse<T>(raw: string | null, fallback: T): T {\r\n if (!raw) return fallback;\r\n try { return JSON.parse(raw) as T; } catch { return fallback; }\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\nimport { createStorageAdapter, safeJsonParse } from './utils/storage';\r\n\r\n/**\r\n * Persistence Plugin — saves/restores full chat history via browser storage\r\n */\r\nexport function persistencePlugin(options?: {\r\n storageKey?: string;\r\n storage?: 'local' | 'session';\r\n maxMessages?: number;\r\n ttl?: number;\r\n}): ChatPlugin {\r\n const key = options?.storageKey ?? 'chatbot_history';\r\n const store = createStorageAdapter(options?.storage ?? 'local');\r\n const max = options?.maxMessages ?? 100;\r\n const ttl = options?.ttl ?? 0;\r\n\r\n interface Snapshot { messages: ChatMessage[]; savedAt: number }\r\n\r\n const save = (ctx: PluginContext) => {\r\n const messages = ctx.getMessages().slice(-max);\r\n const snapshot: Snapshot = { messages, savedAt: Date.now() };\r\n store.set(key, JSON.stringify(snapshot));\r\n };\r\n\r\n return {\r\n name: 'persistence',\r\n\r\n onInit(ctx) {\r\n const raw = store.get(key);\r\n const snapshot = safeJsonParse<Snapshot | null>(raw, null);\r\n if (!snapshot?.messages?.length) return;\r\n\r\n if (ttl > 0 && Date.now() - snapshot.savedAt > ttl) {\r\n store.remove(key);\r\n return;\r\n }\r\n\r\n for (const msg of snapshot.messages) {\r\n if (msg.sender === 'bot' && msg.text) {\r\n ctx.addBotMessage(msg.text);\r\n }\r\n }\r\n },\r\n\r\n onMessage(_message, ctx) {\r\n save(ctx);\r\n },\r\n\r\n onSubmit(_data, ctx) {\r\n save(ctx);\r\n },\r\n\r\n onDestroy(ctx) {\r\n save(ctx);\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON, getJSON } from './utils/http';\r\n\r\n/**\r\n * Sync Plugin — syncs chat data with a backend endpoint\r\n */\r\nexport function syncPlugin(options: {\r\n endpoint: string;\r\n headers?: Record<string, string>;\r\n syncInterval?: number;\r\n sessionKey?: string;\r\n}): ChatPlugin {\r\n const interval = options.syncInterval ?? 0;\r\n const sessionKey = options.sessionKey ?? `chat_${Date.now()}`;\r\n let timer: ReturnType<typeof setInterval> | null = null;\r\n\r\n const push = (ctx: PluginContext) => {\r\n postJSON(options.endpoint, {\r\n session: sessionKey,\r\n messages: ctx.getMessages(),\r\n data: ctx.getData(),\r\n timestamp: Date.now(),\r\n }, options.headers).catch(() => { /* silent */ });\r\n };\r\n\r\n return {\r\n name: 'sync',\r\n\r\n async onInit(ctx) {\r\n // Load remote state\r\n try {\r\n const data = await getJSON<{ messages?: unknown[]; data?: Record<string, unknown> }>(\r\n `${options.endpoint}?session=${encodeURIComponent(sessionKey)}`,\r\n options.headers,\r\n );\r\n if (data.data) {\r\n for (const [k, v] of Object.entries(data.data)) {\r\n ctx.setData(k, v);\r\n }\r\n }\r\n } catch { /* no remote state */ }\r\n\r\n // Periodic sync\r\n if (interval > 0) {\r\n timer = setInterval(() => push(ctx), interval);\r\n }\r\n },\r\n\r\n onSubmit(_data, ctx) {\r\n push(ctx);\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'flowEnd') push(ctx);\r\n },\r\n\r\n onDestroy(ctx) {\r\n if (timer) clearInterval(timer);\r\n push(ctx);\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\n/**\r\n * Auth Plugin — manages user authentication and identity\r\n */\r\nexport function authPlugin(options: {\r\n type?: 'jwt' | 'session' | 'custom';\r\n tokenKey?: string;\r\n storage?: 'local' | 'session';\r\n onAuth?: (token: string, ctx: PluginContext) => void;\r\n onAuthExpired?: (ctx: PluginContext) => void;\r\n validateToken?: (token: string) => boolean;\r\n}): ChatPlugin {\r\n const tokenKey = options.tokenKey ?? 'chatbot_auth_token';\r\n const store = createStorageAdapter(options.storage ?? 'local');\r\n\r\n const isExpired = (token: string): boolean => {\r\n if (options.validateToken) return !options.validateToken(token);\r\n if (options.type === 'jwt') {\r\n try {\r\n const payload = JSON.parse(atob(token.split('.')[1]!));\r\n return payload.exp ? payload.exp * 1000 < Date.now() : false;\r\n } catch { return true; }\r\n }\r\n return false;\r\n };\r\n\r\n return {\r\n name: 'auth',\r\n\r\n onInit(ctx) {\r\n const token = store.get(tokenKey);\r\n if (token) {\r\n if (isExpired(token)) {\r\n store.remove(tokenKey);\r\n options.onAuthExpired?.(ctx);\r\n ctx.emit('auth:expired', {});\r\n } else {\r\n ctx.setData('authToken', token);\r\n options.onAuth?.(token, ctx);\r\n ctx.emit('auth:restored', { token });\r\n }\r\n }\r\n\r\n // Listen for login events\r\n ctx.on('auth:login', (...args: unknown[]) => {\r\n const token = args[0] as string;\r\n if (token) {\r\n store.set(tokenKey, token);\r\n ctx.setData('authToken', token);\r\n ctx.emit('auth:success', { token });\r\n }\r\n });\r\n\r\n ctx.on('auth:logout', () => {\r\n store.remove(tokenKey);\r\n ctx.setData('authToken', undefined);\r\n ctx.emit('auth:loggedOut', {});\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Rate Limit Plugin — prevents spam by limiting message frequency\r\n */\r\nexport function rateLimitPlugin(options?: {\r\n limit?: number;\r\n window?: number;\r\n onLimited?: (remaining: number) => void;\r\n warningMessage?: string;\r\n}): ChatPlugin {\r\n const limit = options?.limit ?? 10;\r\n const window = options?.window ?? 60000;\r\n const timestamps: number[] = [];\r\n\r\n return {\r\n name: 'rateLimit',\r\n\r\n onMessage(message, ctx) {\r\n if (message.sender !== 'user') return;\r\n\r\n const now = Date.now();\r\n // Remove expired timestamps\r\n while (timestamps.length > 0 && now - timestamps[0]! > window) {\r\n timestamps.shift();\r\n }\r\n\r\n timestamps.push(now);\r\n\r\n if (timestamps.length > limit) {\r\n const remaining = Math.ceil((timestamps[0]! + window - now) / 1000);\r\n options?.onLimited?.(remaining);\r\n ctx.emit('rateLimit:exceeded', { remaining });\r\n ctx.addBotMessage(options?.warningMessage ?? `Too many messages. Please wait ${remaining}s.`);\r\n return { ...message, text: '' };\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Push Plugin — sends browser push notifications for new messages\r\n */\r\nexport function pushPlugin(options?: {\r\n title?: string;\r\n icon?: string;\r\n onlyWhenHidden?: boolean;\r\n requestPermission?: boolean;\r\n}): ChatPlugin {\r\n const title = options?.title ?? 'New Message';\r\n const onlyWhenHidden = options?.onlyWhenHidden ?? true;\r\n\r\n const canNotify = () =>\r\n typeof Notification !== 'undefined' && Notification.permission === 'granted';\r\n\r\n const shouldNotify = () =>\r\n canNotify() && (!onlyWhenHidden || document.hidden);\r\n\r\n return {\r\n name: 'push',\r\n\r\n async onInit() {\r\n if (options?.requestPermission !== false && typeof Notification !== 'undefined' && Notification.permission === 'default') {\r\n await Notification.requestPermission();\r\n }\r\n },\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text && shouldNotify()) {\r\n new Notification(title, {\r\n body: message.text.slice(0, 200),\r\n icon: options?.icon,\r\n tag: 'chatbot-msg',\r\n });\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Sound Plugin — plays sound on new bot messages\r\n */\r\nexport function soundPlugin(options?: {\r\n src?: string;\r\n volume?: number;\r\n onlyWhenHidden?: boolean;\r\n}): ChatPlugin {\r\n const volume = Math.min(1, Math.max(0, options?.volume ?? 0.5));\r\n const onlyWhenHidden = options?.onlyWhenHidden ?? false;\r\n let audioCtx: AudioContext | null = null;\r\n\r\n const playDefault = () => {\r\n if (!audioCtx) audioCtx = new AudioContext();\r\n const osc = audioCtx.createOscillator();\r\n const gain = audioCtx.createGain();\r\n osc.connect(gain);\r\n gain.connect(audioCtx.destination);\r\n osc.frequency.value = 800;\r\n gain.gain.value = volume * 0.3;\r\n osc.start();\r\n gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.15);\r\n osc.stop(audioCtx.currentTime + 0.15);\r\n };\r\n\r\n const play = () => {\r\n if (onlyWhenHidden && !document.hidden) return;\r\n if (options?.src) {\r\n const audio = new Audio(options.src);\r\n audio.volume = volume;\r\n audio.play().catch(() => { /* user hasn't interacted yet */ });\r\n } else {\r\n try { playDefault(); } catch { /* AudioContext not available */ }\r\n }\r\n };\r\n\r\n return {\r\n name: 'sound',\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot') play();\r\n },\r\n\r\n onDestroy() {\r\n audioCtx?.close().catch(() => {});\r\n audioCtx = null;\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\n/**\r\n * Agent Plugin — enables live human agent takeover of the chat\r\n */\r\nexport function agentPlugin(options: {\r\n socketUrl?: string;\r\n onAgentConnect?: (ctx: PluginContext) => void;\r\n onAgentDisconnect?: (ctx: PluginContext) => void;\r\n onAgentMessage?: (text: string, ctx: PluginContext) => void;\r\n connectMessage?: string;\r\n disconnectMessage?: string;\r\n}): ChatPlugin {\r\n let ws: WebSocket | null = null;\r\n let isAgentMode = false;\r\n\r\n return {\r\n name: 'agent',\r\n\r\n onInit(ctx) {\r\n // Listen for agent handoff trigger\r\n ctx.on('agent:connect', () => {\r\n if (ws || !options.socketUrl) return;\r\n\r\n ws = new WebSocket(options.socketUrl);\r\n ws.onopen = () => {\r\n isAgentMode = true;\r\n ctx.addBotMessage(options.connectMessage ?? '🧑‍💼 You are now connected to a live agent.');\r\n options.onAgentConnect?.(ctx);\r\n ctx.emit('agent:connected', {});\r\n };\r\n\r\n ws.onmessage = (event) => {\r\n const text = typeof event.data === 'string' ? event.data : '';\r\n if (text) {\r\n ctx.addBotMessage(text);\r\n options.onAgentMessage?.(text, ctx);\r\n }\r\n };\r\n\r\n ws.onclose = () => {\r\n isAgentMode = false;\r\n ws = null;\r\n ctx.addBotMessage(options.disconnectMessage ?? '🧑‍💼 The agent has disconnected.');\r\n options.onAgentDisconnect?.(ctx);\r\n ctx.emit('agent:disconnected', {});\r\n };\r\n\r\n ws.onerror = () => {\r\n ws?.close();\r\n };\r\n });\r\n\r\n ctx.on('agent:disconnect', () => {\r\n ws?.close();\r\n });\r\n },\r\n\r\n onMessage(message) {\r\n // Forward user messages to agent via websocket\r\n if (isAgentMode && message.sender === 'user' && ws?.readyState === WebSocket.OPEN) {\r\n ws.send(JSON.stringify({ type: 'message', text: message.text, timestamp: Date.now() }));\r\n }\r\n },\r\n\r\n onDestroy() {\r\n ws?.close();\r\n ws = null;\r\n isAgentMode = false;\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\n\r\n/**\r\n * Transfer Plugin — transfers chat to different departments/agents via API\r\n */\r\nexport function transferPlugin(options: {\r\n endpoint: string;\r\n headers?: Record<string, string>;\r\n departments?: string[];\r\n onTransfer?: (department: string, ctx: PluginContext) => void;\r\n onTransferComplete?: (result: unknown, ctx: PluginContext) => void;\r\n transferMessage?: string;\r\n}): ChatPlugin {\r\n return {\r\n name: 'transfer',\r\n\r\n onInit(ctx) {\r\n ctx.on('transfer:request', async (...args: unknown[]) => {\r\n const department = (args[0] as string) ?? 'default';\r\n\r\n ctx.addBotMessage(\r\n options.transferMessage ?? `Transferring you to **${department}**. Please hold...`,\r\n );\r\n options.onTransfer?.(department, ctx);\r\n\r\n try {\r\n const res = await postJSON(options.endpoint, {\r\n department,\r\n messages: ctx.getMessages(),\r\n data: ctx.getData(),\r\n timestamp: Date.now(),\r\n }, options.headers);\r\n\r\n const result = await res.json();\r\n options.onTransferComplete?.(result, ctx);\r\n ctx.emit('transfer:complete', { department, result });\r\n } catch (err) {\r\n console.error('[transfer] Failed:', err);\r\n ctx.addBotMessage('Transfer failed. Please try again later.');\r\n ctx.emit('transfer:error', { department, error: String(err) });\r\n }\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\n/**\r\n * Theme Plugin — switches themes dynamically and persists preference\r\n */\r\nexport function themePlugin(options?: {\r\n defaultMode?: 'light' | 'dark';\r\n storageKey?: string;\r\n onThemeChange?: (mode: string, ctx: PluginContext) => void;\r\n cssVariable?: string;\r\n}): ChatPlugin {\r\n const storageKey = options?.storageKey ?? 'chatbot_theme';\r\n const store = createStorageAdapter('local');\r\n const cssVar = options?.cssVariable ?? '--cb-theme-mode';\r\n\r\n const applyTheme = (mode: string) => {\r\n if (typeof document !== 'undefined') {\r\n document.documentElement.style.setProperty(cssVar, mode);\r\n document.documentElement.setAttribute('data-chatbot-theme', mode);\r\n }\r\n };\r\n\r\n return {\r\n name: 'theme',\r\n\r\n onInit(ctx) {\r\n const saved = store.get(storageKey) ?? options?.defaultMode ?? 'light';\r\n applyTheme(saved);\r\n ctx.setData('theme', saved);\r\n\r\n ctx.on('theme:set', (...args: unknown[]) => {\r\n const mode = args[0] as string;\r\n if (mode) {\r\n store.set(storageKey, mode);\r\n applyTheme(mode);\r\n ctx.setData('theme', mode);\r\n options?.onThemeChange?.(mode, ctx);\r\n ctx.emit('theme:changed', { mode });\r\n }\r\n });\r\n\r\n ctx.on('theme:toggle', () => {\r\n const current = store.get(storageKey) ?? 'light';\r\n const next = current === 'light' ? 'dark' : 'light';\r\n ctx.emit('theme:set', next);\r\n });\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\n/**\r\n * Component Plugin — injects custom component messages into chat via events\r\n */\r\nexport function componentPlugin(options?: {\r\n components?: Record<string, string>;\r\n onRender?: (componentKey: string, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n return {\r\n name: 'component',\r\n\r\n onInit(ctx) {\r\n // Listen for component injection requests\r\n ctx.on('component:inject', (...args: unknown[]) => {\r\n const key = args[0] as string;\r\n if (key) {\r\n ctx.emit('component:render', { component: key });\r\n options?.onRender?.(key, ctx);\r\n }\r\n });\r\n\r\n // Allow aliased rendering\r\n if (options?.components) {\r\n for (const [alias, componentKey] of Object.entries(options.components)) {\r\n ctx.on(`component:${alias}`, () => {\r\n ctx.emit('component:render', { component: componentKey });\r\n });\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { postJSON } from './utils/http';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\n/**\r\n * Lead Plugin — captures and stores user information as leads\r\n */\r\nexport function leadPlugin(options: {\r\n fields?: string[];\r\n endpoint?: string;\r\n headers?: Record<string, string>;\r\n storageKey?: string;\r\n onLeadCaptured?: (lead: Record<string, unknown>, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n const fields = new Set(options.fields ?? ['name', 'email', 'phone']);\r\n const store = createStorageAdapter('local');\r\n const storageKey = options.storageKey ?? 'chatbot_lead';\r\n\r\n const checkAndCapture = (data: Record<string, unknown>, ctx: PluginContext) => {\r\n const lead: Record<string, unknown> = {};\r\n let hasFields = false;\r\n\r\n for (const [key, value] of Object.entries(data)) {\r\n if (fields.has(key) && value) {\r\n lead[key] = value;\r\n hasFields = true;\r\n }\r\n }\r\n\r\n if (hasFields) {\r\n // Merge with existing lead data\r\n const existing = JSON.parse(store.get(storageKey) ?? '{}');\r\n const merged = { ...existing, ...lead, updatedAt: Date.now() };\r\n store.set(storageKey, JSON.stringify(merged));\r\n options.onLeadCaptured?.(merged, ctx);\r\n ctx.emit('lead:captured', merged);\r\n\r\n // Send to endpoint if configured\r\n if (options.endpoint) {\r\n postJSON(options.endpoint, merged, options.headers).catch(() => {});\r\n }\r\n }\r\n };\r\n\r\n return {\r\n name: 'lead',\r\n\r\n onSubmit(data, ctx) {\r\n checkAndCapture(data, ctx);\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'flowEnd' && event.payload) {\r\n checkAndCapture(event.payload as Record<string, unknown>, ctx);\r\n }\r\n if (event.type === 'login' && event.payload) {\r\n checkAndCapture(event.payload as Record<string, unknown>, ctx);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\ntype TriggerType = 'exitIntent' | 'idle' | 'scroll' | 'pageLoad' | 'custom';\r\n\r\ninterface CampaignConfig {\r\n trigger: TriggerType;\r\n message: string;\r\n delay?: number;\r\n maxShows?: number;\r\n flowStep?: string;\r\n}\r\n\r\n/**\r\n * Campaign Plugin — starts flows or shows messages based on user behavior triggers\r\n */\r\nexport function campaignPlugin(options: {\r\n campaigns: CampaignConfig[];\r\n onTrigger?: (campaign: CampaignConfig, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n const timers = new TimerManager();\r\n const showCounts = new Map<number, number>();\r\n\r\n return {\r\n name: 'campaign',\r\n\r\n onInit(ctx) {\r\n options.campaigns.forEach((campaign, idx) => {\r\n const maxShows = campaign.maxShows ?? 1;\r\n\r\n const fire = () => {\r\n const count = showCounts.get(idx) ?? 0;\r\n if (count >= maxShows) return;\r\n showCounts.set(idx, count + 1);\r\n\r\n ctx.addBotMessage(campaign.message);\r\n if (campaign.flowStep) {\r\n ctx.emit('campaign:flowStart', { step: campaign.flowStep });\r\n }\r\n options.onTrigger?.(campaign, ctx);\r\n ctx.emit('campaign:triggered', { trigger: campaign.trigger, idx });\r\n };\r\n\r\n switch (campaign.trigger) {\r\n case 'pageLoad':\r\n timers.setTimeout(`campaign_${idx}`, fire, campaign.delay ?? 0);\r\n break;\r\n\r\n case 'idle':\r\n timers.setTimeout(`campaign_${idx}`, fire, campaign.delay ?? 30000);\r\n break;\r\n\r\n case 'exitIntent':\r\n if (typeof document !== 'undefined') {\r\n const handler = (e: MouseEvent) => {\r\n if (e.clientY <= 0) {\r\n fire();\r\n document.removeEventListener('mouseleave', handler);\r\n }\r\n };\r\n document.addEventListener('mouseleave', handler);\r\n }\r\n break;\r\n\r\n case 'scroll':\r\n if (typeof window !== 'undefined') {\r\n const handler = () => {\r\n const scrollPct = window.scrollY / (document.body.scrollHeight - window.innerHeight);\r\n if (scrollPct > 0.7) {\r\n fire();\r\n window.removeEventListener('scroll', handler);\r\n }\r\n };\r\n window.addEventListener('scroll', handler, { passive: true });\r\n }\r\n break;\r\n\r\n case 'custom':\r\n ctx.on(`campaign:fire:${idx}`, fire);\r\n break;\r\n }\r\n });\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\ninterface ScheduledMessage {\r\n message: string;\r\n delay: number;\r\n repeat?: boolean;\r\n interval?: number;\r\n}\r\n\r\n/**\r\n * Scheduler Plugin — triggers bot messages at scheduled times or intervals\r\n */\r\nexport function schedulerPlugin(options: {\r\n messages?: ScheduledMessage[];\r\n onScheduledMessage?: (message: string) => void;\r\n}): ChatPlugin {\r\n const timers = new TimerManager();\r\n\r\n return {\r\n name: 'scheduler',\r\n\r\n onInit(ctx) {\r\n options.messages?.forEach((item, idx) => {\r\n const id = `sched_${idx}`;\r\n\r\n if (item.repeat && item.interval) {\r\n // Initial delay then repeat\r\n timers.setTimeout(`${id}_init`, () => {\r\n ctx.addBotMessage(item.message);\r\n options.onScheduledMessage?.(item.message);\r\n timers.setInterval(id, () => {\r\n ctx.addBotMessage(item.message);\r\n options.onScheduledMessage?.(item.message);\r\n }, item.interval!);\r\n }, item.delay);\r\n } else {\r\n // One-shot\r\n timers.setTimeout(id, () => {\r\n ctx.addBotMessage(item.message);\r\n options.onScheduledMessage?.(item.message);\r\n }, item.delay);\r\n }\r\n });\r\n\r\n // Allow dynamic scheduling\r\n ctx.on('scheduler:add', (...args: unknown[]) => {\r\n const msg = args[0] as ScheduledMessage;\r\n if (msg) {\r\n const id = `sched_dyn_${Date.now()}`;\r\n timers.setTimeout(id, () => {\r\n ctx.addBotMessage(msg.message);\r\n options.onScheduledMessage?.(msg.message);\r\n }, msg.delay);\r\n }\r\n });\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\nimport { TimerManager } from './utils/timer';\r\n\r\n/**\r\n * Reminder Plugin — sends reminder messages to users after configured delays\r\n */\r\nexport function reminderPlugin(options?: {\r\n reminders?: Array<{ message: string; delay: number; condition?: string }>;\r\n onReminder?: (message: string) => void;\r\n}): ChatPlugin {\r\n const timers = new TimerManager();\r\n\r\n return {\r\n name: 'reminder',\r\n\r\n onInit(ctx) {\r\n // Static reminders\r\n options?.reminders?.forEach((r, idx) => {\r\n timers.setTimeout(`reminder_${idx}`, () => {\r\n ctx.addBotMessage(r.message);\r\n options?.onReminder?.(r.message);\r\n ctx.emit('reminder:sent', { message: r.message, idx });\r\n }, r.delay);\r\n });\r\n\r\n // Dynamic reminders via events\r\n ctx.on('reminder:set', (...args: unknown[]) => {\r\n const config = args[0] as { id: string; message: string; delay: number } | undefined;\r\n if (config) {\r\n timers.setTimeout(`reminder_${config.id}`, () => {\r\n ctx.addBotMessage(config.message);\r\n options?.onReminder?.(config.message);\r\n ctx.emit('reminder:sent', config);\r\n }, config.delay);\r\n }\r\n });\r\n\r\n ctx.on('reminder:cancel', (...args: unknown[]) => {\r\n const id = args[0] as string;\r\n if (id) timers.clearTimeout(`reminder_${id}`);\r\n });\r\n },\r\n\r\n onDestroy() {\r\n timers.destroy();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport { createStorageAdapter } from './utils/storage';\r\n\r\ntype Translations = Record<string, Record<string, string>>;\r\n\r\n/**\r\n * i18n Plugin — supports multiple languages with dynamic switching\r\n */\r\nexport function i18nPlugin(options: {\r\n defaultLocale?: string;\r\n translations: Translations;\r\n storageKey?: string;\r\n onLocaleChange?: (locale: string, ctx: PluginContext) => void;\r\n}): ChatPlugin {\r\n const store = createStorageAdapter('local');\r\n const storageKey = options.storageKey ?? 'chatbot_locale';\r\n let currentLocale = options.defaultLocale ?? 'en';\r\n\r\n const t = (key: string): string => {\r\n return options.translations[currentLocale]?.[key]\r\n ?? options.translations[options.defaultLocale ?? 'en']?.[key]\r\n ?? key;\r\n };\r\n\r\n return {\r\n name: 'i18n',\r\n\r\n onInit(ctx) {\r\n const saved = store.get(storageKey);\r\n if (saved && options.translations[saved]) {\r\n currentLocale = saved;\r\n }\r\n ctx.setData('locale', currentLocale);\r\n ctx.setData('t', t);\r\n\r\n ctx.on('i18n:setLocale', (...args: unknown[]) => {\r\n const locale = args[0] as string;\r\n if (locale && options.translations[locale]) {\r\n currentLocale = locale;\r\n store.set(storageKey, locale);\r\n ctx.setData('locale', locale);\r\n options.onLocaleChange?.(locale, ctx);\r\n ctx.emit('i18n:localeChanged', { locale });\r\n }\r\n });\r\n\r\n ctx.on('i18n:translate', (...args: unknown[]) => {\r\n const key = args[0] as string;\r\n if (key) ctx.emit('i18n:translated', { key, value: t(key) });\r\n });\r\n },\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text) {\r\n // Auto-translate bot messages that look like translation keys\r\n const translated = message.text.replace(/\\{\\{t:([^}]+)\\}\\}/g, (_:any, key: string) => t(key));\r\n if (translated !== message.text) {\r\n return { ...message, text: translated };\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Debug Plugin — exposes internal state and flow progression for debugging\r\n */\r\nexport function debugPlugin(options?: {\r\n logState?: boolean;\r\n logEvents?: boolean;\r\n logMessages?: boolean;\r\n groupName?: string;\r\n}): ChatPlugin {\r\n const group = options?.groupName ?? '🐛 ChatBot Debug';\r\n const logState = options?.logState ?? true;\r\n const logEvents = options?.logEvents ?? true;\r\n const logMessages = options?.logMessages ?? true;\r\n let eventLog: Array<{ type: string; payload?: unknown; time: number }> = [];\r\n\r\n return {\r\n name: 'debug',\r\n\r\n onInit(ctx) {\r\n console.group(group);\r\n console.log('Initialized');\r\n if (logState) {\r\n console.log('Messages:', ctx.getMessages().length);\r\n console.log('Data:', ctx.getData());\r\n }\r\n console.groupEnd();\r\n\r\n // Expose debug helpers on window for dev console access\r\n if (typeof window !== 'undefined') {\r\n (window as unknown as Record<string, unknown>).__chatbotDebug = {\r\n getMessages: () => ctx.getMessages(),\r\n getData: () => ctx.getData(),\r\n getEventLog: () => [...eventLog],\r\n sendMessage: (text: string) => ctx.sendMessage(text),\r\n emit: (event: string, ...args: unknown[]) => ctx.emit(event, ...args),\r\n };\r\n console.log(`${group}: window.__chatbotDebug available`);\r\n }\r\n },\r\n\r\n onMessage(message) {\r\n if (logMessages) {\r\n console.log(`${group} [${message.sender}]:`, message.text ?? '(no text)', message);\r\n }\r\n },\r\n\r\n onEvent(event) {\r\n eventLog.push({ type: event.type, payload: event.payload, time: Date.now() });\r\n if (logEvents) {\r\n console.log(`${group} Event [${event.type}]:`, event.payload ?? '');\r\n }\r\n },\r\n\r\n onDestroy() {\r\n console.log(`${group}: Destroyed. Total events logged:`, eventLog.length);\r\n eventLog = [];\r\n if (typeof window !== 'undefined') {\r\n delete (window as unknown as Record<string, unknown>).__chatbotDebug;\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\ninterface DevToolsState {\r\n messages: number;\r\n events: Array<{ type: string; time: number }>;\r\n data: Record<string, unknown>;\r\n currentStep: string | null;\r\n}\r\n\r\n/**\r\n * DevTools Plugin — provides a visual debugging panel overlay\r\n */\r\nexport function devtoolsPlugin(options?: {\r\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\r\n shortcutKey?: string;\r\n onStateUpdate?: (state: DevToolsState) => void;\r\n}): ChatPlugin {\r\n const pos = options?.position ?? 'top-right';\r\n const shortcutKey = options?.shortcutKey ?? 'F2';\r\n let panel: HTMLDivElement | null = null;\r\n let visible = false;\r\n let state: DevToolsState = { messages: 0, events: [], data: {}, currentStep: null };\r\n\r\n const createPanel = (): HTMLDivElement => {\r\n const div = document.createElement('div');\r\n div.id = 'chatbot-devtools';\r\n const posStyles: Record<string, string> = {\r\n 'top-left': 'top:8px;left:8px',\r\n 'top-right': 'top:8px;right:8px',\r\n 'bottom-left': 'bottom:80px;left:8px',\r\n 'bottom-right': 'bottom:80px;right:8px',\r\n };\r\n div.style.cssText = `\r\n position:fixed;${posStyles[pos]};z-index:99999;width:320px;max-height:400px;overflow:auto;\r\n background:#1a1a2e;color:#e0e0e0;font-family:monospace;font-size:11px;\r\n border-radius:8px;padding:12px;box-shadow:0 4px 20px rgba(0,0,0,0.4);display:none;\r\n `;\r\n document.body.appendChild(div);\r\n return div;\r\n };\r\n\r\n const renderPanel = () => {\r\n if (!panel) return;\r\n panel.innerHTML = `\r\n <div style=\"font-size:13px;font-weight:bold;margin-bottom:8px;color:#6c5ce7;\">🔧 ChatBot DevTools</div>\r\n <div style=\"margin-bottom:6px;\"><b>Messages:</b> ${state.messages}</div>\r\n <div style=\"margin-bottom:6px;\"><b>Step:</b> ${state.currentStep ?? 'none'}</div>\r\n <div style=\"margin-bottom:6px;\"><b>Data:</b><pre style=\"margin:4px 0;white-space:pre-wrap;font-size:10px;\">${JSON.stringify(state.data, null, 2)}</pre></div>\r\n <div><b>Recent Events (${state.events.length}):</b></div>\r\n ${state.events.slice(-10).map(e => `<div style=\"color:#888;font-size:10px;\">${e.type}</div>`).join('')}\r\n `;\r\n };\r\n\r\n const toggle = () => {\r\n if (!panel) panel = createPanel();\r\n visible = !visible;\r\n panel.style.display = visible ? 'block' : 'none';\r\n if (visible) renderPanel();\r\n };\r\n\r\n let keyHandler: ((e: KeyboardEvent) => void) | null = null;\r\n\r\n return {\r\n name: 'devtools',\r\n\r\n onInit(ctx) {\r\n if (typeof document === 'undefined') return;\r\n\r\n keyHandler = (e: KeyboardEvent) => {\r\n if (e.key === shortcutKey) toggle();\r\n };\r\n document.addEventListener('keydown', keyHandler);\r\n\r\n state.messages = ctx.getMessages().length;\r\n state.data = ctx.getData();\r\n },\r\n\r\n onMessage(message, ctx) {\r\n state.messages = ctx.getMessages().length + 1;\r\n state.data = ctx.getData();\r\n options?.onStateUpdate?.(state);\r\n if (visible) renderPanel();\r\n },\r\n\r\n onEvent(event, ctx: PluginContext) {\r\n state.events.push({ type: event.type, time: event.timestamp });\r\n if (event.type === 'stepChange') {\r\n state.currentStep = (event.payload as { stepId?: string })?.stepId ?? null;\r\n }\r\n state.data = ctx.getData();\r\n options?.onStateUpdate?.(state);\r\n if (visible) renderPanel();\r\n },\r\n\r\n onDestroy() {\r\n if (keyHandler) {\r\n document.removeEventListener('keydown', keyHandler);\r\n keyHandler = null;\r\n }\r\n panel?.remove();\r\n panel = null;\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\n\r\n/**\r\n * Media Plugin — adds support for rich media messages (images, videos, cards)\r\n */\r\nexport function mediaPlugin(options?: {\r\n allowedTypes?: ('image' | 'video' | 'audio' | 'card')[];\r\n onMediaRender?: (type: string, url: string) => void;\r\n}): ChatPlugin {\r\n const allowed = new Set(options?.allowedTypes ?? ['image', 'video', 'audio', 'card']);\r\n\r\n const processMediaTags = (text: string): string => {\r\n if (!text) return text;\r\n\r\n let result = text;\r\n\r\n // [image:url] → HTML img\r\n if (allowed.has('image')) {\r\n result = result.replace(/\\[image:([^\\]]+)\\]/g, (_, url: string) => {\r\n options?.onMediaRender?.('image', url);\r\n return `![image](${url})`;\r\n });\r\n }\r\n\r\n // [video:url] → markdown video link\r\n if (allowed.has('video')) {\r\n result = result.replace(/\\[video:([^\\]]+)\\]/g, (_, url: string) => {\r\n options?.onMediaRender?.('video', url);\r\n return `🎬 [Watch video](${url})`;\r\n });\r\n }\r\n\r\n // [audio:url] → markdown audio link\r\n if (allowed.has('audio')) {\r\n result = result.replace(/\\[audio:([^\\]]+)\\]/g, (_, url: string) => {\r\n options?.onMediaRender?.('audio', url);\r\n return `🔊 [Listen](${url})`;\r\n });\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return {\r\n name: 'media',\r\n\r\n onInit(ctx) {\r\n // Allow programmatic media injection\r\n ctx.on('media:send', (...args: unknown[]) => {\r\n const config = args[0] as { type: string; url: string; caption?: string } | undefined;\r\n if (config && allowed.has(config.type as 'image')) {\r\n const text = config.caption\r\n ? `${processMediaTags(`[${config.type}:${config.url}]`)}\\n${config.caption}`\r\n : processMediaTags(`[${config.type}:${config.url}]`);\r\n ctx.addBotMessage(text);\r\n }\r\n });\r\n },\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text) {\r\n const processed = processMediaTags(message.text);\r\n if (processed !== message.text) {\r\n return { ...message, text: processed };\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Markdown Plugin — transforms markdown syntax in bot messages to HTML\r\n * Lightweight built-in renderer, no external dependencies.\r\n */\r\nexport function markdownPlugin(options?: {\r\n enableBold?: boolean;\r\n enableItalic?: boolean;\r\n enableCode?: boolean;\r\n enableLinks?: boolean;\r\n enableLists?: boolean;\r\n enableLineBreaks?: boolean;\r\n}): ChatPlugin {\r\n const cfg = {\r\n bold: options?.enableBold ?? true,\r\n italic: options?.enableItalic ?? true,\r\n code: options?.enableCode ?? true,\r\n links: options?.enableLinks ?? true,\r\n lists: options?.enableLists ?? true,\r\n lineBreaks: options?.enableLineBreaks ?? true,\r\n };\r\n\r\n const renderMarkdown = (text: string): string => {\r\n let result = text;\r\n\r\n // Code blocks (``` ```)\r\n if (cfg.code) {\r\n result = result.replace(/```([^`]+)```/g, '<pre><code>$1</code></pre>');\r\n result = result.replace(/`([^`]+)`/g, '<code>$1</code>');\r\n }\r\n\r\n // Bold (**text** or __text__)\r\n if (cfg.bold) {\r\n result = result.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>');\r\n result = result.replace(/__([^_]+)__/g, '<strong>$1</strong>');\r\n }\r\n\r\n // Italic (*text* or _text_)\r\n if (cfg.italic) {\r\n result = result.replace(/\\*([^*]+)\\*/g, '<em>$1</em>');\r\n result = result.replace(/(?<!\\w)_([^_]+)_(?!\\w)/g, '<em>$1</em>');\r\n }\r\n\r\n // Links [text](url)\r\n if (cfg.links) {\r\n result = result.replace(\r\n /\\[([^\\]]+)\\]\\((https?:\\/\\/[^)]+)\\)/g,\r\n '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>',\r\n );\r\n }\r\n\r\n // Unordered lists\r\n if (cfg.lists) {\r\n result = result.replace(/^[•\\-\\*]\\s+(.+)$/gm, '<li>$1</li>');\r\n result = result.replace(/(<li>.*<\\/li>\\n?)+/g, '<ul>$&</ul>');\r\n }\r\n\r\n // Line breaks\r\n if (cfg.lineBreaks) {\r\n result = result.replace(/\\n/g, '<br>');\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return {\r\n name: 'markdown',\r\n\r\n onMessage(message) {\r\n if (message.sender === 'bot' && message.text) {\r\n const rendered = renderMarkdown(message.text);\r\n if (rendered !== message.text) {\r\n return { ...message, text: rendered };\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport type { LiveAgentConfig, AgentInfo } from '../types/liveAgent';\r\nimport { LiveAgentAdapter } from '../core/LiveAgentAdapter';\r\nimport { WsDriver } from '../core/drivers/WsDriver';\r\nimport { SioDriver } from '../core/drivers/SioDriver';\r\nimport { DEFAULT_LIVE_AGENT_EVENTS } from '../types/liveAgent';\r\n\r\n/**\r\n * Live Agent Plugin — alternative to the `liveAgent` prop.\r\n * Use this when you prefer the plugin-based API over the built-in prop.\r\n *\r\n * @example\r\n * ```ts\r\n * import { io } from 'socket.io-client'\r\n *\r\n * const socket = io('https://support.example.com')\r\n *\r\n * plugins={[\r\n * liveAgentPlugin({\r\n * type: 'socketio',\r\n * instance: socket,\r\n * sessionId: 'user_123',\r\n * }),\r\n * ]}\r\n * ```\r\n */\r\nexport function liveAgentPlugin(config: LiveAgentConfig): ChatPlugin {\r\n let adapter: LiveAgentAdapter | null = null;\r\n\r\n const events = { ...DEFAULT_LIVE_AGENT_EVENTS, ...config.events };\r\n const sessionId = config.sessionId ?? `live_${Date.now()}`;\r\n\r\n return {\r\n name: 'liveAgent',\r\n\r\n onInit(ctx: PluginContext) {\r\n const driver =\r\n config.type === 'socketio'\r\n ? new SioDriver(config.instance)\r\n : new WsDriver(config.instance);\r\n\r\n adapter = new LiveAgentAdapter(driver, events, sessionId);\r\n\r\n // Listen for agent messages\r\n adapter.on(events.agentMessage, (data) => {\r\n const d = data as { text?: string; agent?: AgentInfo };\r\n if (d.text) {\r\n ctx.addBotMessage(d.text);\r\n }\r\n });\r\n\r\n // Listen for agent joined\r\n adapter.on(events.agentJoined, (data) => {\r\n const d = data as AgentInfo;\r\n ctx.addBotMessage(`🟢 ${d.name} joined the chat`);\r\n config.onAgentJoined?.(d);\r\n });\r\n\r\n // Listen for agent left\r\n adapter.on(events.agentLeft, (data) => {\r\n const d = data as AgentInfo;\r\n ctx.addBotMessage(`🔴 ${d.name} left the chat`);\r\n config.onAgentLeft?.(d);\r\n });\r\n\r\n // Listen for queue update\r\n adapter.on(events.queueUpdate, (data) => {\r\n const d = data as { position: number; estimatedWait?: number };\r\n const waitText = d.estimatedWait ? ` Estimated wait: ~${d.estimatedWait} min.` : '';\r\n ctx.addBotMessage(`You are #${d.position} in queue.${waitText}`);\r\n config.onQueueUpdate?.(d.position, d.estimatedWait);\r\n });\r\n\r\n // Listen for transfer request via plugin event bus\r\n ctx.on('liveAgent:transfer', (...args: unknown[]) => {\r\n const department = args[0] as string | undefined;\r\n adapter?.requestTransfer(department);\r\n });\r\n\r\n // Listen for send message via plugin event bus\r\n ctx.on('liveAgent:send', (...args: unknown[]) => {\r\n const text = args[0] as string;\r\n if (text) adapter?.sendUserMessage(text);\r\n });\r\n\r\n adapter.initSession();\r\n config.onConnect?.();\r\n },\r\n\r\n onMessage(message, ctx) {\r\n // Forward user messages to live agent\r\n if (message.sender === 'user' && adapter?.isConnected()) {\r\n adapter.sendUserMessage(message.text ?? '');\r\n }\r\n return message;\r\n },\r\n\r\n onDestroy() {\r\n adapter?.destroy();\r\n adapter = null;\r\n config.onDisconnect?.();\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Tags Plugin — allows tagging conversations by topic for multi-flow bots.\r\n * Tags are stored in metadata and can be used for routing/analytics.\r\n */\r\nexport function tagsPlugin(options?: {\r\n /** Predefined tags users can assign */\r\n availableTags?: string[];\r\n /** Storage key for persisting tags */\r\n storageKey?: string;\r\n /** Called when a tag is added */\r\n onTagAdded?: (tag: string, messageId?: string) => void;\r\n /** Called when a tag is removed */\r\n onTagRemoved?: (tag: string) => void;\r\n}): ChatPlugin {\r\n const tags = new Set<string>();\r\n const storageKey = options?.storageKey ?? 'cb_tags';\r\n\r\n return {\r\n name: 'tags',\r\n\r\n onInit(ctx) {\r\n // Restore tags from storage\r\n try {\r\n const stored = localStorage.getItem(storageKey);\r\n if (stored) JSON.parse(stored).forEach((t: string) => tags.add(t));\r\n } catch { /* ignore */ }\r\n ctx.emit('tags:loaded', Array.from(tags));\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'tags:add' && typeof event.payload === 'string') {\r\n tags.add(event.payload);\r\n localStorage.setItem(storageKey, JSON.stringify(Array.from(tags)));\r\n options?.onTagAdded?.(event.payload);\r\n ctx.emit('tags:updated', Array.from(tags));\r\n }\r\n if (event.type === 'tags:remove' && typeof event.payload === 'string') {\r\n tags.delete(event.payload);\r\n localStorage.setItem(storageKey, JSON.stringify(Array.from(tags)));\r\n options?.onTagRemoved?.(event.payload);\r\n ctx.emit('tags:updated', Array.from(tags));\r\n }\r\n if (event.type === 'tags:get') {\r\n ctx.emit('tags:current', Array.from(tags));\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Rating Plugin — shows a satisfaction survey at end of conversation.\r\n * Triggered on flow end or agent disconnect.\r\n */\r\nexport function ratingPlugin(options?: {\r\n /** Rating scale (default: 5) */\r\n scale?: number;\r\n /** Prompt text shown to user */\r\n prompt?: string;\r\n /** Trigger events that show the rating (default: ['flowEnd']) */\r\n triggers?: ('flowEnd' | 'agentDisconnect' | 'custom')[];\r\n /** Called when user submits a rating */\r\n onRate?: (rating: number, feedback?: string) => void;\r\n /** Endpoint to POST rating data */\r\n endpoint?: string;\r\n /** Custom headers for the POST */\r\n headers?: Record<string, string>;\r\n}): ChatPlugin {\r\n const cfg = {\r\n scale: options?.scale ?? 5,\r\n prompt: options?.prompt ?? 'How was your experience?',\r\n triggers: options?.triggers ?? ['flowEnd'],\r\n };\r\n\r\n return {\r\n name: 'rating',\r\n\r\n onEvent(event, ctx) {\r\n const shouldTrigger =\r\n (cfg.triggers.includes('flowEnd') && event.type === 'flowEnd') ||\r\n (cfg.triggers.includes('agentDisconnect') && event.type === 'agent:left') ||\r\n (cfg.triggers.includes('custom') && event.type === 'rating:show');\r\n\r\n if (shouldTrigger) {\r\n ctx.addBotMessage(`${cfg.prompt} (Rate 1-${cfg.scale})`);\r\n }\r\n\r\n if (event.type === 'rating:submit' && typeof event.payload === 'number') {\r\n options?.onRate?.(event.payload);\r\n if (options?.endpoint) {\r\n fetch(options.endpoint, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...options.headers },\r\n body: JSON.stringify({ rating: event.payload, timestamp: Date.now() }),\r\n }).catch(() => { /* silent */ });\r\n }\r\n ctx.addBotMessage(`Thanks for your ${event.payload}/${cfg.scale} rating! 🙏`);\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\n/**\r\n * Offline Plugin — queues messages when device is offline and sends them on reconnect.\r\n */\r\nexport function offlinePlugin(options?: {\r\n /** Storage key for queued messages (default: 'cb_offline_queue') */\r\n storageKey?: string;\r\n /** Show indicator when offline */\r\n showOfflineIndicator?: boolean;\r\n /** Called when messages are flushed on reconnect */\r\n onFlush?: (count: number) => void;\r\n}): ChatPlugin {\r\n const storageKey = options?.storageKey ?? 'cb_offline_queue';\r\n let queue: Array<{ text: string; timestamp: number }> = [];\r\n\r\n const loadQueue = () => {\r\n try {\r\n const stored = localStorage.getItem(storageKey);\r\n if (stored) queue = JSON.parse(stored);\r\n } catch { /* ignore */ }\r\n };\r\n\r\n const saveQueue = () => {\r\n localStorage.setItem(storageKey, JSON.stringify(queue));\r\n };\r\n\r\n return {\r\n name: 'offline',\r\n\r\n onInit(ctx) {\r\n loadQueue();\r\n\r\n // Flush queue on reconnect\r\n const handleOnline = () => {\r\n if (queue.length > 0) {\r\n queue.forEach((msg) => ctx.sendMessage(msg.text));\r\n options?.onFlush?.(queue.length);\r\n queue = [];\r\n saveQueue();\r\n }\r\n if (options?.showOfflineIndicator) {\r\n ctx.addBotMessage('🟢 Back online!');\r\n }\r\n };\r\n\r\n const handleOffline = () => {\r\n if (options?.showOfflineIndicator) {\r\n ctx.addBotMessage('🔴 You are offline. Messages will be sent when you reconnect.');\r\n }\r\n };\r\n\r\n window.addEventListener('online', handleOnline);\r\n window.addEventListener('offline', handleOffline);\r\n },\r\n\r\n onMessage(message, ctx) {\r\n if (message.sender === 'user' && !navigator.onLine) {\r\n queue.push({ text: message.text ?? '', timestamp: Date.now() });\r\n saveQueue();\r\n ctx.addBotMessage('📥 Message queued — will send when online.');\r\n return undefined;\r\n }\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\nexport interface ProactiveRule {\r\n /** Trigger type */\r\n trigger: 'idle' | 'scroll' | 'exitIntent' | 'pageLoad' | 'custom';\r\n /** Message to show */\r\n message: string;\r\n /** Delay in ms before showing (default: 5000) */\r\n delay?: number;\r\n /** Max number of times to show (default: 1) */\r\n maxShows?: number;\r\n /** Jump to flow step when user clicks */\r\n flowStep?: string;\r\n}\r\n\r\n/**\r\n * Proactive Plugin — triggers bot messages based on user behavior.\r\n * Supports: idle, scroll depth, exit intent, page load, custom events.\r\n */\r\nexport function proactivePlugin(options: {\r\n rules: ProactiveRule[];\r\n /** Called when a proactive message is triggered */\r\n onTrigger?: (rule: ProactiveRule) => void;\r\n}): ChatPlugin {\r\n const showCounts = new Map<number, number>();\r\n const timers: ReturnType<typeof setTimeout>[] = [];\r\n\r\n return {\r\n name: 'proactive',\r\n\r\n onInit(ctx) {\r\n options.rules.forEach((rule, idx) => {\r\n showCounts.set(idx, 0);\r\n const maxShows = rule.maxShows ?? 1;\r\n const delay = rule.delay ?? 5000;\r\n\r\n const fire = () => {\r\n const count = showCounts.get(idx) ?? 0;\r\n if (count >= maxShows) return;\r\n showCounts.set(idx, count + 1);\r\n options.onTrigger?.(rule);\r\n ctx.addBotMessage(rule.message);\r\n if (rule.flowStep) ctx.emit('flow:goto', rule.flowStep);\r\n };\r\n\r\n if (typeof window === 'undefined') return;\r\n\r\n switch (rule.trigger) {\r\n case 'pageLoad':\r\n timers.push(setTimeout(fire, delay));\r\n break;\r\n\r\n case 'idle': {\r\n let idleTimer: ReturnType<typeof setTimeout>;\r\n const resetIdle = () => {\r\n clearTimeout(idleTimer);\r\n idleTimer = setTimeout(fire, delay);\r\n };\r\n ['mousemove', 'keydown', 'scroll', 'touchstart'].forEach((evt) =>\r\n document.addEventListener(evt, resetIdle, { passive: true }),\r\n );\r\n idleTimer = setTimeout(fire, delay);\r\n break;\r\n }\r\n\r\n case 'scroll': {\r\n const handleScroll = () => {\r\n const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;\r\n if (scrollPercent >= 50) fire();\r\n };\r\n window.addEventListener('scroll', handleScroll, { passive: true });\r\n break;\r\n }\r\n\r\n case 'exitIntent': {\r\n const handleMouseLeave = (e: MouseEvent) => {\r\n if (e.clientY <= 0) fire();\r\n };\r\n document.addEventListener('mouseleave', handleMouseLeave);\r\n break;\r\n }\r\n\r\n case 'custom':\r\n // Fired via event bus: emit('proactive:trigger', ruleIndex)\r\n break;\r\n }\r\n });\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'proactive:trigger' && typeof event.payload === 'number') {\r\n const rule = options.rules[event.payload];\r\n if (rule) {\r\n const count = showCounts.get(event.payload) ?? 0;\r\n if (count < (rule.maxShows ?? 1)) {\r\n showCounts.set(event.payload, count + 1);\r\n options.onTrigger?.(rule);\r\n ctx.addBotMessage(rule.message);\r\n }\r\n }\r\n }\r\n },\r\n\r\n onDestroy() {\r\n timers.forEach(clearTimeout);\r\n },\r\n };\r\n}\r\n","import type { ChatPlugin } from '../types/plugin';\r\n\r\nexport interface BotPersona {\r\n /** Unique persona identifier */\r\n id: string;\r\n /** Display name */\r\n name: string;\r\n /** Avatar URL */\r\n avatar?: string;\r\n /** Greeting message when switching to this persona */\r\n greeting?: string;\r\n /** Theme overrides */\r\n theme?: { primaryColor?: string; headerBg?: string };\r\n /** Flow config ID to activate */\r\n flowId?: string;\r\n}\r\n\r\n/**\r\n * Persona Plugin — switch between different bot personalities in one widget.\r\n * Each persona has a name, avatar, greeting, and optionally a different theme/flow.\r\n */\r\nexport function personaPlugin(options: {\r\n personas: BotPersona[];\r\n /** Default persona ID (defaults to first) */\r\n defaultPersona?: string;\r\n /** Storage key for current persona (default: 'cb_persona') */\r\n storageKey?: string;\r\n /** Called when persona switches */\r\n onSwitch?: (persona: BotPersona) => void;\r\n}): ChatPlugin {\r\n const storageKey = options.storageKey ?? 'cb_persona';\r\n let currentPersona: BotPersona = options.personas.find((p) => p.id === options.defaultPersona) ?? options.personas[0];\r\n\r\n return {\r\n name: 'persona',\r\n\r\n onInit(ctx) {\r\n // Restore saved persona\r\n try {\r\n const savedId = localStorage.getItem(storageKey);\r\n if (savedId) {\r\n const found = options.personas.find((p) => p.id === savedId);\r\n if (found) currentPersona = found;\r\n }\r\n } catch { /* ignore */ }\r\n\r\n ctx.emit('persona:current', currentPersona);\r\n },\r\n\r\n onEvent(event, ctx) {\r\n if (event.type === 'persona:switch' && typeof event.payload === 'string') {\r\n const persona = options.personas.find((p) => p.id === event.payload);\r\n if (persona) {\r\n currentPersona = persona;\r\n localStorage.setItem(storageKey, persona.id);\r\n options.onSwitch?.(persona);\r\n ctx.emit('persona:current', persona);\r\n\r\n if (persona.greeting) {\r\n ctx.addBotMessage(persona.greeting);\r\n }\r\n if (persona.flowId) {\r\n ctx.emit('flow:goto', persona.flowId);\r\n }\r\n }\r\n }\r\n\r\n if (event.type === 'persona:get') {\r\n ctx.emit('persona:current', currentPersona);\r\n }\r\n\r\n if (event.type === 'persona:list') {\r\n ctx.emit('persona:all', options.personas);\r\n }\r\n },\r\n\r\n onMessage(message) {\r\n // Tag bot messages with current persona info\r\n if (message.sender === 'bot') {\r\n return {\r\n ...message,\r\n metadata: {\r\n ...message.metadata,\r\n persona: currentPersona.id,\r\n personaName: currentPersona.name,\r\n personaAvatar: currentPersona.avatar,\r\n },\r\n };\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Pin Plugin ──────────────────────────────────────────────────\r\n// Pin/unpin messages. Pinned messages are stored and accessible via events.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface PinPluginOptions {\r\n /** Max pinned messages (default: 10) */\r\n maxPins?: number;\r\n /** Persist pins in localStorage */\r\n persist?: boolean;\r\n /** Storage key */\r\n storageKey?: string;\r\n /** Callback when a message is pinned */\r\n onPin?: (messageId: string) => void;\r\n /** Callback when a message is unpinned */\r\n onUnpin?: (messageId: string) => void;\r\n}\r\n\r\nexport function pinPlugin(options: PinPluginOptions = {}): ChatPlugin {\r\n const maxPins = options.maxPins ?? 10;\r\n const storageKey = options.storageKey ?? 'chatbot_pinned';\r\n let pinned: string[] = [];\r\n\r\n const load = () => {\r\n if (options.persist) {\r\n try {\r\n pinned = JSON.parse(localStorage.getItem(storageKey) ?? '[]');\r\n } catch { pinned = []; }\r\n }\r\n };\r\n\r\n const save = () => {\r\n if (options.persist) {\r\n localStorage.setItem(storageKey, JSON.stringify(pinned));\r\n }\r\n };\r\n\r\n return {\r\n name: 'pin',\r\n\r\n onInit(_ctx: PluginContext) {\r\n load();\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'pin:add' && typeof event.payload === 'string') {\r\n if (!pinned.includes(event.payload) && pinned.length < maxPins) {\r\n pinned.push(event.payload);\r\n save();\r\n options.onPin?.(event.payload);\r\n ctx.emit('pin:updated', [...pinned]);\r\n }\r\n }\r\n\r\n if (event.type === 'pin:remove' && typeof event.payload === 'string') {\r\n pinned = pinned.filter((id) => id !== event.payload);\r\n save();\r\n options.onUnpin?.(event.payload);\r\n ctx.emit('pin:updated', [...pinned]);\r\n }\r\n\r\n if (event.type === 'pin:list') {\r\n ctx.emit('pin:current', [...pinned]);\r\n }\r\n\r\n if (event.type === 'pin:clear') {\r\n pinned = [];\r\n save();\r\n ctx.emit('pin:updated', []);\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Theme Toggle Plugin ─────────────────────────────────────────\r\n// Allows user to toggle between light/dark mode in-chat.\r\n// Activated via prop `enableThemeToggle: true`.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface ThemeTogglePluginOptions {\r\n /** Default mode (default: 'light') */\r\n defaultMode?: 'light' | 'dark';\r\n /** Persist preference in localStorage */\r\n persist?: boolean;\r\n /** Storage key */\r\n storageKey?: string;\r\n /** Callback on toggle */\r\n onToggle?: (mode: 'light' | 'dark') => void;\r\n}\r\n\r\nexport function themeTogglePlugin(options: ThemeTogglePluginOptions = {}): ChatPlugin {\r\n const storageKey = options.storageKey ?? 'chatbot_theme_mode';\r\n let mode: 'light' | 'dark' = options.defaultMode ?? 'light';\r\n\r\n return {\r\n name: 'themeToggle',\r\n\r\n onInit(ctx: PluginContext) {\r\n if (options.persist !== false) {\r\n const saved = localStorage.getItem(storageKey);\r\n if (saved === 'light' || saved === 'dark') mode = saved;\r\n }\r\n ctx.setData('__themeMode', mode);\r\n ctx.emit('theme:mode', mode);\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'theme:toggle') {\r\n mode = mode === 'light' ? 'dark' : 'light';\r\n if (options.persist !== false) localStorage.setItem(storageKey, mode);\r\n ctx.setData('__themeMode', mode);\r\n options.onToggle?.(mode);\r\n ctx.emit('theme:mode', mode);\r\n }\r\n\r\n if (event.type === 'theme:set' && (event.payload === 'light' || event.payload === 'dark')) {\r\n mode = event.payload;\r\n if (options.persist !== false) localStorage.setItem(storageKey, mode);\r\n ctx.setData('__themeMode', mode);\r\n options.onToggle?.(mode);\r\n ctx.emit('theme:mode', mode);\r\n }\r\n\r\n if (event.type === 'theme:get') {\r\n ctx.emit('theme:mode', mode);\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Confetti Plugin ─────────────────────────────────────────────\r\n// Trigger celebration animations on flow completion, milestones, etc.\r\n// Lightweight CSS-based confetti — no external dependencies.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface ConfettiPluginOptions {\r\n /** Events that auto-trigger confetti (default: ['flowEnd']) */\r\n triggers?: string[];\r\n /** Duration in ms (default: 3000) */\r\n duration?: number;\r\n /** Number of particles (default: 50) */\r\n particleCount?: number;\r\n /** Custom colors */\r\n colors?: string[];\r\n /** Callback when confetti fires */\r\n onFire?: () => void;\r\n}\r\n\r\nexport function confettiPlugin(options: ConfettiPluginOptions = {}): ChatPlugin {\r\n const triggers = options.triggers ?? ['flowEnd'];\r\n const duration = options.duration ?? 3000;\r\n const particleCount = options.particleCount ?? 50;\r\n const colors = options.colors ?? ['#6C5CE7', '#A29BFE', '#fd79a8', '#fdcb6e', '#00b894', '#0984e3'];\r\n\r\n const fireConfetti = () => {\r\n if (typeof document === 'undefined') return;\r\n options.onFire?.();\r\n\r\n const container = document.createElement('div');\r\n container.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:999999;overflow:hidden;';\r\n document.body.appendChild(container);\r\n\r\n for (let i = 0; i < particleCount; i++) {\r\n const particle = document.createElement('div');\r\n const color = colors[Math.floor(Math.random() * colors.length)];\r\n const x = Math.random() * 100;\r\n const delay = Math.random() * 500;\r\n const size = Math.random() * 8 + 4;\r\n const rotation = Math.random() * 360;\r\n\r\n particle.style.cssText = `\r\n position:absolute;top:-10px;left:${x}%;\r\n width:${size}px;height:${size}px;\r\n background:${color};border-radius:${Math.random() > 0.5 ? '50%' : '0'};\r\n transform:rotate(${rotation}deg);\r\n animation:confetti-fall ${duration}ms ease-in ${delay}ms forwards;\r\n opacity:0.9;\r\n `;\r\n container.appendChild(particle);\r\n }\r\n\r\n // Inject keyframes if not present\r\n if (!document.getElementById('confetti-style')) {\r\n const style = document.createElement('style');\r\n style.id = 'confetti-style';\r\n style.textContent = `\r\n @keyframes confetti-fall {\r\n 0% { transform: translateY(0) rotate(0deg); opacity: 1; }\r\n 100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n }\r\n\r\n setTimeout(() => container.remove(), duration + 1000);\r\n };\r\n\r\n return {\r\n name: 'confetti',\r\n\r\n onEvent(event: ChatPluginEvent, _ctx: PluginContext) {\r\n if (triggers.includes(event.type)) {\r\n fireConfetti();\r\n }\r\n if (event.type === 'confetti:fire') {\r\n fireConfetti();\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Priority Plugin ─────────────────────────────────────────────\r\n// Assign priority levels and labels to conversations.\r\n// Useful for support queues, agent routing, and ticket management.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport type ConversationPriority = 'low' | 'medium' | 'high' | 'urgent';\r\n\r\nexport interface PriorityPluginOptions {\r\n /** Default priority (default: 'medium') */\r\n defaultPriority?: ConversationPriority;\r\n /** Max labels per conversation (default: 5) */\r\n maxLabels?: number;\r\n /** Persist in localStorage */\r\n persist?: boolean;\r\n /** Storage key */\r\n storageKey?: string;\r\n /** Callback on priority change */\r\n onPriorityChange?: (priority: ConversationPriority) => void;\r\n /** Callback on label change */\r\n onLabelsChange?: (labels: string[]) => void;\r\n /** Webhook URL to notify on priority change */\r\n webhookUrl?: string;\r\n}\r\n\r\nexport function priorityPlugin(options: PriorityPluginOptions = {}): ChatPlugin {\r\n const storageKey = options.storageKey ?? 'chatbot_priority';\r\n const maxLabels = options.maxLabels ?? 5;\r\n let priority: ConversationPriority = options.defaultPriority ?? 'medium';\r\n let labels: string[] = [];\r\n\r\n const load = () => {\r\n if (options.persist !== false) {\r\n try {\r\n const saved = JSON.parse(localStorage.getItem(storageKey) ?? '{}');\r\n if (saved.priority) priority = saved.priority;\r\n if (saved.labels) labels = saved.labels;\r\n } catch { /* ignore */ }\r\n }\r\n };\r\n\r\n const save = () => {\r\n if (options.persist !== false) {\r\n localStorage.setItem(storageKey, JSON.stringify({ priority, labels }));\r\n }\r\n };\r\n\r\n const notify = () => {\r\n if (options.webhookUrl) {\r\n fetch(options.webhookUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ priority, labels, timestamp: Date.now() }),\r\n }).catch(() => { /* silent */ });\r\n }\r\n };\r\n\r\n return {\r\n name: 'priority',\r\n\r\n onInit(ctx: PluginContext) {\r\n load();\r\n ctx.setData('__priority', priority);\r\n ctx.setData('__labels', labels);\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'priority:set' && typeof event.payload === 'string') {\r\n const valid: ConversationPriority[] = ['low', 'medium', 'high', 'urgent'];\r\n if (valid.includes(event.payload as ConversationPriority)) {\r\n priority = event.payload as ConversationPriority;\r\n save();\r\n notify();\r\n options.onPriorityChange?.(priority);\r\n ctx.setData('__priority', priority);\r\n ctx.emit('priority:changed', priority);\r\n }\r\n }\r\n\r\n if (event.type === 'label:add' && typeof event.payload === 'string') {\r\n if (!labels.includes(event.payload) && labels.length < maxLabels) {\r\n labels.push(event.payload);\r\n save();\r\n options.onLabelsChange?.([...labels]);\r\n ctx.setData('__labels', [...labels]);\r\n ctx.emit('labels:updated', [...labels]);\r\n }\r\n }\r\n\r\n if (event.type === 'label:remove' && typeof event.payload === 'string') {\r\n labels = labels.filter((l) => l !== event.payload);\r\n save();\r\n options.onLabelsChange?.([...labels]);\r\n ctx.setData('__labels', [...labels]);\r\n ctx.emit('labels:updated', [...labels]);\r\n }\r\n\r\n if (event.type === 'priority:get') {\r\n ctx.emit('priority:current', { priority, labels });\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Whisper Plugin ──────────────────────────────────────────────\r\n// Supervisor whisper mode — internal notes visible only to agents.\r\n// Messages with `metadata.whisper: true` are filtered from user view.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\nexport interface WhisperPluginOptions {\r\n /** Role that can see whispers (default: 'agent') */\r\n visibleTo?: string;\r\n /** Current viewer role — set to 'agent' for agents, 'user' for end-users */\r\n viewerRole?: 'agent' | 'supervisor' | 'user';\r\n /** Callback when whisper is sent */\r\n onWhisper?: (message: ChatMessage) => void;\r\n /** Webhook to forward whispers to */\r\n webhookUrl?: string;\r\n}\r\n\r\nexport function whisperPlugin(options: WhisperPluginOptions = {}): ChatPlugin {\r\n const viewerRole = options.viewerRole ?? 'user';\r\n\r\n return {\r\n name: 'whisper',\r\n\r\n onMessage(message: ChatMessage, ctx: PluginContext) {\r\n // If message is a whisper and viewer is user, hide it\r\n const meta = message.metadata as Record<string, unknown> | undefined;\r\n if (meta?.whisper === true && viewerRole === 'user') {\r\n return undefined; // Block message from display\r\n }\r\n return message;\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'whisper:send' && typeof event.payload === 'string') {\r\n const whisperMsg: Partial<ChatMessage> = {\r\n sender: 'bot',\r\n text: event.payload,\r\n metadata: { whisper: true, from: 'supervisor' },\r\n };\r\n\r\n // Only show to agents\r\n if (viewerRole !== 'user') {\r\n ctx.addBotMessage(`🔇 ${event.payload}`);\r\n }\r\n\r\n options.onWhisper?.(whisperMsg as ChatMessage);\r\n\r\n if (options.webhookUrl) {\r\n fetch(options.webhookUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ type: 'whisper', text: event.payload, timestamp: Date.now() }),\r\n }).catch(() => { /* silent */ });\r\n }\r\n\r\n ctx.emit('whisper:sent', whisperMsg);\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Message Schedule Plugin ─────────────────────────────────────\r\n// Schedule messages to be sent at a specific time.\r\n// Useful for reminders, follow-ups, and delayed responses.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface ScheduledMessage {\r\n id: string;\r\n text: string;\r\n scheduledAt: number; // Unix timestamp (ms)\r\n sender?: 'bot' | 'user';\r\n}\r\n\r\nexport interface MessageSchedulePluginOptions {\r\n /** Polling interval in ms (default: 1000) */\r\n checkInterval?: number;\r\n /** Max scheduled messages (default: 20) */\r\n maxScheduled?: number;\r\n /** Persist in localStorage */\r\n persist?: boolean;\r\n /** Storage key */\r\n storageKey?: string;\r\n /** Callback when a scheduled message fires */\r\n onFire?: (msg: ScheduledMessage) => void;\r\n}\r\n\r\nexport function messageSchedulePlugin(options: MessageSchedulePluginOptions = {}): ChatPlugin {\r\n const interval = options.checkInterval ?? 1000;\r\n const maxScheduled = options.maxScheduled ?? 20;\r\n const storageKey = options.storageKey ?? 'chatbot_scheduled';\r\n let scheduled: ScheduledMessage[] = [];\r\n let timer: ReturnType<typeof setInterval> | null = null;\r\n\r\n const load = () => {\r\n if (options.persist !== false) {\r\n try {\r\n scheduled = JSON.parse(localStorage.getItem(storageKey) ?? '[]');\r\n } catch { scheduled = []; }\r\n }\r\n };\r\n\r\n const save = () => {\r\n if (options.persist !== false) {\r\n localStorage.setItem(storageKey, JSON.stringify(scheduled));\r\n }\r\n };\r\n\r\n return {\r\n name: 'messageSchedule',\r\n\r\n onInit(ctx: PluginContext) {\r\n load();\r\n\r\n timer = setInterval(() => {\r\n const now = Date.now();\r\n const due = scheduled.filter((m) => m.scheduledAt <= now);\r\n if (due.length === 0) return;\r\n\r\n scheduled = scheduled.filter((m) => m.scheduledAt > now);\r\n save();\r\n\r\n for (const msg of due) {\r\n if (msg.sender === 'user') {\r\n ctx.sendMessage(msg.text);\r\n } else {\r\n ctx.addBotMessage(msg.text);\r\n }\r\n options.onFire?.(msg);\r\n ctx.emit('schedule:fired', msg);\r\n }\r\n }, interval);\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'schedule:add') {\r\n const payload = event.payload as ScheduledMessage | undefined;\r\n if (payload?.text && payload?.scheduledAt && scheduled.length < maxScheduled) {\r\n scheduled.push({\r\n id: payload.id ?? `sched_${Date.now()}`,\r\n text: payload.text,\r\n scheduledAt: payload.scheduledAt,\r\n sender: payload.sender ?? 'bot',\r\n });\r\n save();\r\n ctx.emit('schedule:added', payload);\r\n }\r\n }\r\n\r\n if (event.type === 'schedule:remove' && typeof event.payload === 'string') {\r\n scheduled = scheduled.filter((m) => m.id !== event.payload);\r\n save();\r\n ctx.emit('schedule:updated', [...scheduled]);\r\n }\r\n\r\n if (event.type === 'schedule:list') {\r\n ctx.emit('schedule:current', [...scheduled]);\r\n }\r\n\r\n if (event.type === 'schedule:clear') {\r\n scheduled = [];\r\n save();\r\n ctx.emit('schedule:updated', []);\r\n }\r\n },\r\n\r\n onDestroy(_ctx: PluginContext) {\r\n if (timer) clearInterval(timer);\r\n },\r\n };\r\n}\r\n","// ─── Notification Badge Plugin ───────────────────────────────────\r\n// Track unread messages and emit badge count.\r\n// Works with the `showNotificationBadge` prop on the Launcher.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\nexport interface NotificationBadgePluginOptions {\r\n /** Play sound on new message (default: false) */\r\n playSound?: boolean;\r\n /** Sound URL */\r\n soundUrl?: string;\r\n /** Show browser notification (default: false) */\r\n browserNotification?: boolean;\r\n /** Update document.title with count (default: false) */\r\n updateTitle?: boolean;\r\n /** Original page title */\r\n originalTitle?: string;\r\n}\r\n\r\nexport function notificationBadgePlugin(options: NotificationBadgePluginOptions = {}): ChatPlugin {\r\n let unreadCount = 0;\r\n let isOpen = false;\r\n const originalTitle = options.originalTitle ?? (typeof document !== 'undefined' ? document.title : '');\r\n\r\n const updateDocTitle = () => {\r\n if (options.updateTitle && typeof document !== 'undefined') {\r\n document.title = unreadCount > 0 ? `(${unreadCount}) ${originalTitle}` : originalTitle;\r\n }\r\n };\r\n\r\n const playNotifSound = () => {\r\n if (options.playSound && options.soundUrl && typeof Audio !== 'undefined') {\r\n const audio = new Audio(options.soundUrl);\r\n audio.volume = 0.5;\r\n audio.play().catch(() => { /* blocked by browser */ });\r\n }\r\n };\r\n\r\n return {\r\n name: 'notificationBadge',\r\n\r\n onInit(ctx: PluginContext) {\r\n ctx.setData('__unreadCount', 0);\r\n\r\n ctx.on('chatOpen', () => {\r\n isOpen = true;\r\n unreadCount = 0;\r\n ctx.setData('__unreadCount', 0);\r\n ctx.emit('badge:count', 0);\r\n updateDocTitle();\r\n });\r\n\r\n ctx.on('chatClose', () => {\r\n isOpen = false;\r\n });\r\n },\r\n\r\n onMessage(message: ChatMessage, ctx: PluginContext) {\r\n if (message.sender === 'bot' && !isOpen) {\r\n unreadCount++;\r\n ctx.setData('__unreadCount', unreadCount);\r\n ctx.emit('badge:count', unreadCount);\r\n updateDocTitle();\r\n playNotifSound();\r\n\r\n if (options.browserNotification && typeof Notification !== 'undefined' && Notification.permission === 'granted') {\r\n new Notification('New message', { body: message.text ?? 'You have a new message' });\r\n }\r\n }\r\n return message;\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'badge:reset') {\r\n unreadCount = 0;\r\n ctx.setData('__unreadCount', 0);\r\n ctx.emit('badge:count', 0);\r\n updateDocTitle();\r\n }\r\n\r\n if (event.type === 'badge:get') {\r\n ctx.emit('badge:count', unreadCount);\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Summary Plugin ──────────────────────────────────────────────\r\n// Generate conversation summaries — AI-powered or keyword extraction.\r\n// Useful for support handoffs, ticket creation, and analytics.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\nexport interface SummaryPluginOptions {\r\n /** AI endpoint for summarization (POST with { messages } body) */\r\n endpoint?: string;\r\n /** Request headers */\r\n headers?: Record<string, string>;\r\n /** Fallback: extract key points from messages locally (default: true) */\r\n localFallback?: boolean;\r\n /** Max messages to include in summary request (default: 50) */\r\n maxMessages?: number;\r\n /** Callback with generated summary */\r\n onSummary?: (summary: string) => void;\r\n}\r\n\r\nfunction localSummarize(messages: ChatMessage[]): string {\r\n const userMsgs = messages.filter((m) => m.sender === 'user' && m.text);\r\n const botMsgs = messages.filter((m) => m.sender === 'bot' && m.text);\r\n\r\n const lines: string[] = [];\r\n lines.push(`📊 Conversation Summary (${messages.length} messages)`);\r\n lines.push(`• User messages: ${userMsgs.length}`);\r\n lines.push(`• Bot messages: ${botMsgs.length}`);\r\n\r\n if (userMsgs.length > 0) {\r\n lines.push(`• First user message: \"${userMsgs[0]!.text!.slice(0, 60)}...\"`);\r\n lines.push(`• Last user message: \"${userMsgs[userMsgs.length - 1]!.text!.slice(0, 60)}...\"`);\r\n }\r\n\r\n // Extract keywords (simple word frequency)\r\n const words = userMsgs\r\n .flatMap((m) => (m.text ?? '').toLowerCase().split(/\\s+/))\r\n .filter((w) => w.length > 3);\r\n const freq = new Map<string, number>();\r\n words.forEach((w) => freq.set(w, (freq.get(w) ?? 0) + 1));\r\n const topWords = [...freq.entries()]\r\n .sort((a, b) => b[1] - a[1])\r\n .slice(0, 5)\r\n .map(([w]) => w);\r\n\r\n if (topWords.length > 0) {\r\n lines.push(`• Key topics: ${topWords.join(', ')}`);\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\nexport function summaryPlugin(options: SummaryPluginOptions = {}): ChatPlugin {\r\n const maxMessages = options.maxMessages ?? 50;\r\n\r\n return {\r\n name: 'summary',\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'summary:generate') {\r\n const messages = ctx.getMessages().slice(-maxMessages);\r\n\r\n if (options.endpoint) {\r\n fetch(options.endpoint, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...options.headers },\r\n body: JSON.stringify({ messages: messages.map((m) => ({ sender: m.sender, text: m.text })) }),\r\n })\r\n .then((r) => r.json())\r\n .then((data: { summary?: string }) => {\r\n const summary = data.summary ?? 'Unable to generate summary.';\r\n options.onSummary?.(summary);\r\n ctx.addBotMessage(summary);\r\n ctx.emit('summary:result', summary);\r\n })\r\n .catch(() => {\r\n if (options.localFallback !== false) {\r\n const summary = localSummarize(messages);\r\n options.onSummary?.(summary);\r\n ctx.addBotMessage(summary);\r\n ctx.emit('summary:result', summary);\r\n }\r\n });\r\n } else if (options.localFallback !== false) {\r\n const summary = localSummarize(messages);\r\n options.onSummary?.(summary);\r\n ctx.addBotMessage(summary);\r\n ctx.emit('summary:result', summary);\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Knowledge Base Plugin ───────────────────────────────────────\r\n// Search FAQ/docs inline and surface answers before human handoff.\r\n// Supports local articles array or remote API endpoint.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\nexport interface KBArticle {\r\n id: string;\r\n title: string;\r\n content: string;\r\n tags?: string[];\r\n url?: string;\r\n}\r\n\r\nexport interface KnowledgeBasePluginOptions {\r\n /** Local articles for fuzzy search */\r\n articles?: KBArticle[];\r\n /** Remote search endpoint (GET with ?q= param) */\r\n endpoint?: string;\r\n /** Request headers */\r\n headers?: Record<string, string>;\r\n /** Auto-search user messages against KB (default: false) */\r\n autoSearch?: boolean;\r\n /** Minimum confidence to show result (0-1, default: 0.3) */\r\n threshold?: number;\r\n /** Max results to show (default: 3) */\r\n maxResults?: number;\r\n /** Callback on search result */\r\n onResult?: (articles: KBArticle[]) => void;\r\n}\r\n\r\nfunction fuzzyMatch(query: string, text: string): number {\r\n const q = query.toLowerCase();\r\n const t = text.toLowerCase();\r\n if (t.includes(q)) return 1;\r\n const words = q.split(/\\s+/);\r\n const matched = words.filter((w) => t.includes(w));\r\n return matched.length / words.length;\r\n}\r\n\r\nfunction searchLocal(query: string, articles: KBArticle[], threshold: number, max: number): KBArticle[] {\r\n return articles\r\n .map((a) => ({\r\n article: a,\r\n score: Math.max(\r\n fuzzyMatch(query, a.title),\r\n fuzzyMatch(query, a.content),\r\n ...(a.tags ?? []).map((t) => fuzzyMatch(query, t)),\r\n ),\r\n }))\r\n .filter((r) => r.score >= threshold)\r\n .sort((a, b) => b.score - a.score)\r\n .slice(0, max)\r\n .map((r) => r.article);\r\n}\r\n\r\nexport function knowledgeBasePlugin(options: KnowledgeBasePluginOptions = {}): ChatPlugin {\r\n const threshold = options.threshold ?? 0.3;\r\n const maxResults = options.maxResults ?? 3;\r\n\r\n const doSearch = async (query: string, ctx: PluginContext) => {\r\n let results: KBArticle[] = [];\r\n\r\n if (options.endpoint) {\r\n try {\r\n const url = `${options.endpoint}?q=${encodeURIComponent(query)}&limit=${maxResults}`;\r\n const res = await fetch(url, { headers: options.headers });\r\n const data = await res.json();\r\n results = (data.articles ?? data.results ?? data) as KBArticle[];\r\n } catch { /* fallback to local */ }\r\n }\r\n\r\n if (results.length === 0 && options.articles) {\r\n results = searchLocal(query, options.articles, threshold, maxResults);\r\n }\r\n\r\n if (results.length > 0) {\r\n options.onResult?.(results);\r\n const text = results\r\n .map((a, i) => `${i + 1}. **${a.title}**\\n ${a.content.slice(0, 100)}${a.url ? `\\n [Read more](${a.url})` : ''}`)\r\n .join('\\n\\n');\r\n ctx.addBotMessage(`📚 I found these articles:\\n\\n${text}`);\r\n ctx.emit('kb:results', results);\r\n }\r\n };\r\n\r\n return {\r\n name: 'knowledgeBase',\r\n\r\n onMessage(message: ChatMessage, ctx: PluginContext) {\r\n if (options.autoSearch && message.sender === 'user' && message.text) {\r\n doSearch(message.text, ctx);\r\n }\r\n return message;\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'kb:search' && typeof event.payload === 'string') {\r\n doSearch(event.payload, ctx);\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Translation Plugin ──────────────────────────────────────────\r\n// Auto-translate messages between languages in real-time.\r\n// Supports any translation API (Google, DeepL, LibreTranslate, etc.).\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\nexport interface TranslationPluginOptions {\r\n /** Translation API endpoint (POST with { text, from, to } body) */\r\n endpoint: string;\r\n /** Request headers (e.g., API key) */\r\n headers?: Record<string, string>;\r\n /** Source language (default: 'auto') */\r\n sourceLang?: string;\r\n /** Target language (default: 'en') */\r\n targetLang?: string;\r\n /** Translate bot messages for user (default: true) */\r\n translateIncoming?: boolean;\r\n /** Translate user messages for bot/agent (default: false) */\r\n translateOutgoing?: boolean;\r\n /** Show original text alongside translation (default: false) */\r\n showOriginal?: boolean;\r\n /** Callback with translation result */\r\n onTranslate?: (original: string, translated: string, lang: string) => void;\r\n}\r\n\r\nexport function translationPlugin(options: TranslationPluginOptions): ChatPlugin {\r\n let targetLang = options.targetLang ?? 'en';\r\n const sourceLang = options.sourceLang ?? 'auto';\r\n\r\n const translate = async (text: string): Promise<string> => {\r\n try {\r\n const res = await fetch(options.endpoint, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...options.headers },\r\n body: JSON.stringify({ text, from: sourceLang, to: targetLang }),\r\n });\r\n const data = await res.json();\r\n return data.translatedText ?? data.translation ?? data.text ?? text;\r\n } catch {\r\n return text;\r\n }\r\n };\r\n\r\n return {\r\n name: 'translation',\r\n\r\n async onMessage(message: ChatMessage, ctx: PluginContext) {\r\n if (!message.text) return message;\r\n\r\n const shouldTranslate =\r\n (message.sender === 'bot' && options.translateIncoming !== false) ||\r\n (message.sender === 'user' && options.translateOutgoing === true);\r\n\r\n if (!shouldTranslate) return message;\r\n\r\n const translated = await translate(message.text);\r\n options.onTranslate?.(message.text, translated, targetLang);\r\n\r\n if (options.showOriginal) {\r\n return { ...message, text: `${translated}\\n\\n_Original: ${message.text}_` };\r\n }\r\n\r\n return { ...message, text: translated };\r\n },\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'translation:setLang' && typeof event.payload === 'string') {\r\n targetLang = event.payload;\r\n ctx.setData('__targetLang', targetLang);\r\n ctx.emit('translation:langChanged', targetLang);\r\n }\r\n\r\n if (event.type === 'translation:getLang') {\r\n ctx.emit('translation:currentLang', targetLang);\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Transcript Export Plugin ────────────────────────────────────\r\n// Export chat history as text, JSON, or downloadable file.\r\n// Enable via `enableTranscriptExport` prop or directly as plugin.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\nexport type TranscriptFormat = 'text' | 'json' | 'csv' | 'html';\r\n\r\nexport interface TranscriptExportPluginOptions {\r\n /** Export format (default: 'text') */\r\n format?: TranscriptFormat;\r\n /** Filename prefix (default: 'chat-transcript') */\r\n filename?: string;\r\n /** Include timestamps (default: true) */\r\n includeTimestamps?: boolean;\r\n /** Callback with exported content */\r\n onExport?: (content: string, format: TranscriptFormat) => void;\r\n /** Custom header text in exported file */\r\n header?: string;\r\n}\r\n\r\nfunction formatTimestamp(ts: number): string {\r\n return new Date(ts).toLocaleString();\r\n}\r\n\r\nfunction toText(messages: ChatMessage[], opts: TranscriptExportPluginOptions): string {\r\n const lines: string[] = [];\r\n if (opts.header) lines.push(opts.header, '');\r\n lines.push(`Chat Transcript — ${formatTimestamp(Date.now())}`, '─'.repeat(40), '');\r\n for (const msg of messages) {\r\n const time = opts.includeTimestamps !== false ? `[${formatTimestamp(msg.timestamp)}] ` : '';\r\n const sender = msg.sender === 'user' ? 'You' : msg.sender === 'bot' ? 'Bot' : 'System';\r\n lines.push(`${time}${sender}: ${msg.text ?? '[attachment]'}`);\r\n }\r\n return lines.join('\\n');\r\n}\r\n\r\nfunction toCSV(messages: ChatMessage[], opts: TranscriptExportPluginOptions): string {\r\n const rows = ['timestamp,sender,text'];\r\n for (const msg of messages) {\r\n const text = (msg.text ?? '').replace(/\"/g, '\"\"');\r\n rows.push(`\"${formatTimestamp(msg.timestamp)}\",\"${msg.sender}\",\"${text}\"`);\r\n }\r\n return rows.join('\\n');\r\n}\r\n\r\nfunction toHTML(messages: ChatMessage[], opts: TranscriptExportPluginOptions): string {\r\n const rows = messages.map((msg) => {\r\n const time = opts.includeTimestamps !== false ? `<span style=\"color:#888\">[${formatTimestamp(msg.timestamp)}]</span> ` : '';\r\n const sender = msg.sender === 'user' ? '<b>You</b>' : msg.sender === 'bot' ? '<b>Bot</b>' : '<b>System</b>';\r\n return `<p>${time}${sender}: ${msg.text ?? '<i>[attachment]</i>'}</p>`;\r\n }).join('\\n');\r\n return `<!DOCTYPE html><html><head><title>Chat Transcript</title></head><body><h1>Chat Transcript</h1><p>${formatTimestamp(Date.now())}</p><hr/>${rows}</body></html>`;\r\n}\r\n\r\nfunction download(content: string, filename: string, mime: string) {\r\n if (typeof document === 'undefined') return;\r\n const blob = new Blob([content], { type: mime });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = filename;\r\n a.click();\r\n URL.revokeObjectURL(url);\r\n}\r\n\r\nexport function transcriptExportPlugin(options: TranscriptExportPluginOptions = {}): ChatPlugin {\r\n const format = options.format ?? 'text';\r\n const filenamePrefix = options.filename ?? 'chat-transcript';\r\n\r\n return {\r\n name: 'transcriptExport',\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'transcript:export') {\r\n const fmt = (typeof event.payload === 'string' ? event.payload : format) as TranscriptFormat;\r\n const messages = ctx.getMessages();\r\n let content: string;\r\n let mime: string;\r\n let ext: string;\r\n\r\n switch (fmt) {\r\n case 'json':\r\n content = JSON.stringify(messages, null, 2);\r\n mime = 'application/json';\r\n ext = 'json';\r\n break;\r\n case 'csv':\r\n content = toCSV(messages, options);\r\n mime = 'text/csv';\r\n ext = 'csv';\r\n break;\r\n case 'html':\r\n content = toHTML(messages, options);\r\n mime = 'text/html';\r\n ext = 'html';\r\n break;\r\n default:\r\n content = toText(messages, options);\r\n mime = 'text/plain';\r\n ext = 'txt';\r\n }\r\n\r\n options.onExport?.(content, fmt);\r\n download(content, `${filenamePrefix}-${Date.now()}.${ext}`, mime);\r\n ctx.emit('transcript:exported', { format: fmt, size: content.length });\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Code Highlight Plugin ───────────────────────────────────────\r\n// Syntax-highlight code blocks in messages.\r\n// Adds CSS classes for styling — bring your own highlight.js/prism theme or use built-in.\r\n\r\nimport type { ChatPlugin, PluginContext } from '../types/plugin';\r\nimport type { ChatMessage } from '../types/message';\r\n\r\nexport interface CodeHighlightPluginOptions {\r\n /** Theme: 'dark' | 'light' (default: 'dark') — applies CSS class */\r\n theme?: 'dark' | 'light';\r\n /** Add copy button to code blocks (default: true) */\r\n copyButton?: boolean;\r\n /** Max height for code blocks in px (default: 300) */\r\n maxHeight?: number;\r\n /** Languages to auto-detect (informational) */\r\n languages?: string[];\r\n}\r\n\r\nconst CODE_BLOCK_REGEX = /```(\\w*)\\n([\\s\\S]*?)```/g;\r\nconst INLINE_CODE_REGEX = /`([^`]+)`/g;\r\n\r\nfunction escapeHtml(str: string): string {\r\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\r\n}\r\n\r\nexport function codeHighlightPlugin(options: CodeHighlightPluginOptions = {}): ChatPlugin {\r\n const theme = options.theme ?? 'dark';\r\n const copyBtn = options.copyButton !== false;\r\n const maxHeight = options.maxHeight ?? 300;\r\n let styleInjected = false;\r\n\r\n const injectStyles = () => {\r\n if (styleInjected || typeof document === 'undefined') return;\r\n styleInjected = true;\r\n\r\n const bg = theme === 'dark' ? '#1e1e2e' : '#f5f5f5';\r\n const fg = theme === 'dark' ? '#cdd6f4' : '#333';\r\n const border = theme === 'dark' ? '#313244' : '#ddd';\r\n\r\n const style = document.createElement('style');\r\n style.id = 'chatbot-code-highlight';\r\n style.textContent = `\r\n .chatbot-code-block {\r\n position: relative;\r\n background: ${bg};\r\n color: ${fg};\r\n border: 1px solid ${border};\r\n border-radius: 6px;\r\n padding: 12px;\r\n margin: 4px 0;\r\n font-family: 'Fira Code', 'JetBrains Mono', monospace;\r\n font-size: 13px;\r\n line-height: 1.5;\r\n overflow-x: auto;\r\n max-height: ${maxHeight}px;\r\n overflow-y: auto;\r\n white-space: pre;\r\n }\r\n .chatbot-code-lang {\r\n position: absolute;\r\n top: 4px;\r\n right: ${copyBtn ? '40px' : '8px'};\r\n font-size: 10px;\r\n opacity: 0.6;\r\n text-transform: uppercase;\r\n }\r\n .chatbot-code-copy {\r\n position: absolute;\r\n top: 4px;\r\n right: 8px;\r\n background: transparent;\r\n border: 1px solid ${border};\r\n border-radius: 4px;\r\n color: ${fg};\r\n font-size: 11px;\r\n padding: 2px 6px;\r\n cursor: pointer;\r\n opacity: 0.7;\r\n }\r\n .chatbot-code-copy:hover { opacity: 1; }\r\n .chatbot-code-inline {\r\n background: ${bg};\r\n color: ${fg};\r\n padding: 2px 5px;\r\n border-radius: 3px;\r\n font-family: monospace;\r\n font-size: 0.9em;\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n };\r\n\r\n return {\r\n name: 'codeHighlight',\r\n\r\n onInit(_ctx: PluginContext) {\r\n injectStyles();\r\n },\r\n\r\n onMessage(message: ChatMessage, _ctx: PluginContext) {\r\n if (!message.text || message.sender === 'user') return message;\r\n\r\n let text = message.text;\r\n\r\n // Replace fenced code blocks\r\n text = text.replace(CODE_BLOCK_REGEX, (_match, lang: string, code: string) => {\r\n const escaped = escapeHtml(code.trim());\r\n const langLabel = lang ? `<span class=\"chatbot-code-lang\">${lang}</span>` : '';\r\n const copyButton = copyBtn\r\n ? `<button class=\"chatbot-code-copy\" onclick=\"navigator.clipboard.writeText(this.parentElement.querySelector('code').textContent)\">Copy</button>`\r\n : '';\r\n return `<div class=\"chatbot-code-block\">${langLabel}${copyButton}<code>${escaped}</code></div>`;\r\n });\r\n\r\n // Replace inline code\r\n text = text.replace(INLINE_CODE_REGEX, (_match, code: string) => {\r\n return `<span class=\"chatbot-code-inline\">${escapeHtml(code)}</span>`;\r\n });\r\n\r\n return { ...message, text };\r\n },\r\n };\r\n}\r\n","// ─── Poll Plugin ─────────────────────────────────────────────────\r\n// Create inline polls in chat. Users vote by clicking options.\r\n// Results are tracked and emitted via events.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface PollOption {\r\n label: string;\r\n value: string;\r\n}\r\n\r\nexport interface PollConfig {\r\n id: string;\r\n question: string;\r\n options: PollOption[];\r\n /** Allow multiple votes (default: false) */\r\n multiSelect?: boolean;\r\n /** Auto-close after N votes (0 = never) */\r\n closeAfter?: number;\r\n}\r\n\r\nexport interface PollResult {\r\n pollId: string;\r\n votes: Record<string, number>;\r\n totalVotes: number;\r\n userVote?: string | string[];\r\n}\r\n\r\nexport interface PollPluginOptions {\r\n /** Callback when vote is cast */\r\n onVote?: (pollId: string, value: string) => void;\r\n /** Callback when poll closes */\r\n onClose?: (result: PollResult) => void;\r\n /** Webhook to report results */\r\n webhookUrl?: string;\r\n}\r\n\r\nexport function pollPlugin(options: PollPluginOptions = {}): ChatPlugin {\r\n const polls = new Map<string, PollConfig>();\r\n const results = new Map<string, Record<string, number>>();\r\n const userVotes = new Map<string, string[]>();\r\n\r\n return {\r\n name: 'poll',\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n // Create a new poll\r\n if (event.type === 'poll:create') {\r\n const config = event.payload as PollConfig | undefined;\r\n if (!config?.id || !config?.question || !config?.options?.length) return;\r\n\r\n polls.set(config.id, config);\r\n results.set(config.id, Object.fromEntries(config.options.map((o) => [o.value, 0])));\r\n userVotes.set(config.id, []);\r\n\r\n const optionsText = config.options.map((o, i) => ` ${i + 1}. ${o.label}`).join('\\n');\r\n ctx.addBotMessage(`📊 **Poll: ${config.question}**\\n\\n${optionsText}\\n\\n_Reply with the option number or value to vote._`);\r\n ctx.emit('poll:created', config);\r\n }\r\n\r\n // Cast a vote\r\n if (event.type === 'poll:vote') {\r\n const payload = event.payload as { pollId: string; value: string } | undefined;\r\n if (!payload?.pollId || !payload?.value) return;\r\n\r\n const poll = polls.get(payload.pollId);\r\n const pollResults = results.get(payload.pollId);\r\n if (!poll || !pollResults) return;\r\n\r\n const validOption = poll.options.find((o) => o.value === payload.value || o.label === payload.value);\r\n if (!validOption) return;\r\n\r\n const votes = userVotes.get(payload.pollId) ?? [];\r\n if (!poll.multiSelect && votes.length > 0) return; // Already voted\r\n\r\n pollResults[validOption.value] = (pollResults[validOption.value] ?? 0) + 1;\r\n votes.push(validOption.value);\r\n userVotes.set(payload.pollId, votes);\r\n\r\n options.onVote?.(payload.pollId, validOption.value);\r\n ctx.emit('poll:voted', { pollId: payload.pollId, value: validOption.value });\r\n\r\n const total = Object.values(pollResults).reduce((a, b) => a + b, 0);\r\n\r\n // Auto-close\r\n if (poll.closeAfter && total >= poll.closeAfter) {\r\n const result: PollResult = { pollId: poll.id, votes: pollResults, totalVotes: total, userVote: votes };\r\n options.onClose?.(result);\r\n ctx.emit('poll:closed', result);\r\n\r\n const resultText = poll.options\r\n .map((o) => {\r\n const count = pollResults[o.value] ?? 0;\r\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\r\n const bar = '█'.repeat(Math.round(pct / 10)) + '░'.repeat(10 - Math.round(pct / 10));\r\n return ` ${o.label}: ${bar} ${pct}% (${count})`;\r\n })\r\n .join('\\n');\r\n ctx.addBotMessage(`📊 **Poll Results: ${poll.question}**\\n\\n${resultText}\\n\\nTotal votes: ${total}`);\r\n\r\n if (options.webhookUrl) {\r\n fetch(options.webhookUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(result),\r\n }).catch(() => { /* silent */ });\r\n }\r\n }\r\n }\r\n\r\n // Get results\r\n if (event.type === 'poll:results' && typeof event.payload === 'string') {\r\n const pollResults = results.get(event.payload);\r\n if (pollResults) {\r\n const total = Object.values(pollResults).reduce((a, b) => a + b, 0);\r\n ctx.emit('poll:result', { pollId: event.payload, votes: pollResults, totalVotes: total });\r\n }\r\n }\r\n\r\n // Close poll manually\r\n if (event.type === 'poll:close' && typeof event.payload === 'string') {\r\n const poll = polls.get(event.payload);\r\n const pollResults = results.get(event.payload);\r\n if (poll && pollResults) {\r\n const total = Object.values(pollResults).reduce((a, b) => a + b, 0);\r\n const result: PollResult = { pollId: poll.id, votes: pollResults, totalVotes: total };\r\n options.onClose?.(result);\r\n ctx.emit('poll:closed', result);\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Payment Plugin ──────────────────────────────────────────────\r\n// Collect payments inline via Stripe, Razorpay, or custom gateway.\r\n// Emits payment events for flow routing (success → next step, failure → error step).\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface PaymentRequest {\r\n amount: number;\r\n currency: string;\r\n description?: string;\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\nexport interface PaymentResult {\r\n success: boolean;\r\n transactionId?: string;\r\n error?: string;\r\n amount: number;\r\n currency: string;\r\n}\r\n\r\nexport interface PaymentPluginOptions {\r\n /** Payment gateway: 'stripe' | 'razorpay' | 'custom' */\r\n gateway: 'stripe' | 'razorpay' | 'custom';\r\n /** API endpoint for creating payment intent/order */\r\n endpoint: string;\r\n /** Request headers (API keys etc.) */\r\n headers?: Record<string, string>;\r\n /** Stripe publishable key (if gateway = 'stripe') */\r\n stripeKey?: string;\r\n /** Razorpay key ID (if gateway = 'razorpay') */\r\n razorpayKey?: string;\r\n /** Callback on successful payment */\r\n onSuccess?: (result: PaymentResult) => void;\r\n /** Callback on failed payment */\r\n onError?: (error: string) => void;\r\n /** Flow step to go to on success */\r\n successStep?: string;\r\n /** Flow step to go to on failure */\r\n errorStep?: string;\r\n}\r\n\r\nexport function paymentPlugin(options: PaymentPluginOptions): ChatPlugin {\r\n return {\r\n name: 'payment',\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'payment:request') {\r\n const request = event.payload as PaymentRequest | undefined;\r\n if (!request?.amount || !request?.currency) return;\r\n\r\n ctx.addBotMessage(\r\n `💳 Payment request: **${request.currency.toUpperCase()} ${request.amount.toFixed(2)}**${request.description ? `\\n${request.description}` : ''}\\n\\n_Processing payment..._`\r\n );\r\n\r\n // Create payment intent/order via backend\r\n fetch(options.endpoint, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...options.headers },\r\n body: JSON.stringify({\r\n amount: request.amount,\r\n currency: request.currency,\r\n gateway: options.gateway,\r\n metadata: request.metadata,\r\n }),\r\n })\r\n .then((r) => r.json())\r\n .then((data: { clientSecret?: string; orderId?: string; checkoutUrl?: string; error?: string }) => {\r\n if (data.error) {\r\n throw new Error(data.error);\r\n }\r\n\r\n if (data.checkoutUrl) {\r\n // Redirect-based payment\r\n ctx.addBotMessage(`💳 [Click here to complete payment](${data.checkoutUrl})`);\r\n ctx.emit('payment:redirect', data.checkoutUrl);\r\n } else {\r\n // Client-side confirmation (Stripe/Razorpay SDK handles UI)\r\n ctx.emit('payment:confirm', {\r\n clientSecret: data.clientSecret,\r\n orderId: data.orderId,\r\n gateway: options.gateway,\r\n amount: request.amount,\r\n currency: request.currency,\r\n });\r\n }\r\n })\r\n .catch((err: Error) => {\r\n const result: PaymentResult = { success: false, error: err.message, amount: request.amount, currency: request.currency };\r\n options.onError?.(err.message);\r\n ctx.addBotMessage(`❌ Payment failed: ${err.message}`);\r\n ctx.emit('payment:failed', result);\r\n if (options.errorStep) ctx.emit('flow:goto', options.errorStep);\r\n });\r\n }\r\n\r\n // Payment confirmed (called after client-side SDK confirmation)\r\n if (event.type === 'payment:confirmed') {\r\n const result = event.payload as PaymentResult | undefined;\r\n if (result?.success) {\r\n options.onSuccess?.(result);\r\n ctx.addBotMessage(`✅ Payment successful! Transaction: ${result.transactionId ?? 'confirmed'}`);\r\n ctx.emit('payment:success', result);\r\n if (options.successStep) ctx.emit('flow:goto', options.successStep);\r\n } else {\r\n options.onError?.(result?.error ?? 'Unknown error');\r\n ctx.addBotMessage(`❌ Payment failed: ${result?.error ?? 'Unknown error'}`);\r\n ctx.emit('payment:failed', result);\r\n if (options.errorStep) ctx.emit('flow:goto', options.errorStep);\r\n }\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Booking Plugin ──────────────────────────────────────────────\r\n// Calendar-based appointment booking within chat.\r\n// Fetches available slots from API and confirms booking.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface TimeSlot {\r\n id: string;\r\n date: string; // ISO date string\r\n time: string; // e.g., '10:00 AM'\r\n available: boolean;\r\n}\r\n\r\nexport interface BookingConfirmation {\r\n bookingId: string;\r\n date: string;\r\n time: string;\r\n status: 'confirmed' | 'pending' | 'cancelled';\r\n}\r\n\r\nexport interface BookingPluginOptions {\r\n /** API endpoint to fetch available slots (GET ?date=YYYY-MM-DD) */\r\n slotsEndpoint: string;\r\n /** API endpoint to confirm booking (POST) */\r\n bookEndpoint: string;\r\n /** Request headers */\r\n headers?: Record<string, string>;\r\n /** Callback on successful booking */\r\n onBooked?: (confirmation: BookingConfirmation) => void;\r\n /** Callback on cancelled booking */\r\n onCancelled?: (bookingId: string) => void;\r\n /** Flow step after booking confirmed */\r\n successStep?: string;\r\n /** Custom message format for available slots */\r\n slotsMessage?: (slots: TimeSlot[]) => string;\r\n}\r\n\r\nexport function bookingPlugin(options: BookingPluginOptions): ChatPlugin {\r\n return {\r\n name: 'booking',\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n // Fetch available slots for a date\r\n if (event.type === 'booking:getSlots') {\r\n const date = typeof event.payload === 'string' ? event.payload : new Date().toISOString().split('T')[0];\r\n\r\n ctx.addBotMessage(`📅 Checking available slots for ${date}...`);\r\n\r\n fetch(`${options.slotsEndpoint}?date=${date}`, {\r\n headers: options.headers,\r\n })\r\n .then((r) => r.json())\r\n .then((data: { slots?: TimeSlot[] }) => {\r\n const slots = (data.slots ?? data as unknown as TimeSlot[]).filter((s: TimeSlot) => s.available);\r\n\r\n if (slots.length === 0) {\r\n ctx.addBotMessage('📅 No available slots for this date. Please try another day.');\r\n ctx.emit('booking:noSlots', date);\r\n return;\r\n }\r\n\r\n const msg = options.slotsMessage\r\n ? options.slotsMessage(slots)\r\n : `📅 Available slots for ${date}:\\n\\n${slots.map((s, i) => ` ${i + 1}. ${s.time}`).join('\\n')}\\n\\n_Reply with the slot number or time to book._`;\r\n\r\n ctx.addBotMessage(msg);\r\n ctx.setData('__availableSlots', slots);\r\n ctx.emit('booking:slots', slots);\r\n })\r\n .catch((err: Error) => {\r\n ctx.addBotMessage('📅 Unable to fetch available slots. Please try again.');\r\n ctx.emit('booking:error', err.message);\r\n });\r\n }\r\n\r\n // Confirm a booking\r\n if (event.type === 'booking:confirm') {\r\n const payload = event.payload as { slotId?: string; date?: string; time?: string; name?: string; email?: string } | undefined;\r\n if (!payload) return;\r\n\r\n ctx.addBotMessage('📅 Confirming your appointment...');\r\n\r\n fetch(options.bookEndpoint, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...options.headers },\r\n body: JSON.stringify(payload),\r\n })\r\n .then((r) => r.json())\r\n .then((data: BookingConfirmation) => {\r\n options.onBooked?.(data);\r\n ctx.addBotMessage(`✅ Appointment confirmed!\\n\\n📅 Date: ${data.date}\\n⏰ Time: ${data.time}\\n🆔 Booking ID: ${data.bookingId}`);\r\n ctx.setData('__lastBooking', data);\r\n ctx.emit('booking:confirmed', data);\r\n if (options.successStep) ctx.emit('flow:goto', options.successStep);\r\n })\r\n .catch((err: Error) => {\r\n ctx.addBotMessage(`❌ Booking failed: ${err.message}. Please try again.`);\r\n ctx.emit('booking:error', err.message);\r\n });\r\n }\r\n\r\n // Cancel a booking\r\n if (event.type === 'booking:cancel' && typeof event.payload === 'string') {\r\n fetch(`${options.bookEndpoint}/${event.payload}`, {\r\n method: 'DELETE',\r\n headers: options.headers,\r\n })\r\n .then(() => {\r\n options.onCancelled?.(event.payload as string);\r\n ctx.addBotMessage(`🗑️ Booking ${event.payload} has been cancelled.`);\r\n ctx.emit('booking:cancelled', event.payload);\r\n })\r\n .catch(() => {\r\n ctx.addBotMessage('❌ Unable to cancel booking. Please try again.');\r\n });\r\n }\r\n },\r\n };\r\n}\r\n","// ─── Location Plugin ─────────────────────────────────────────────\r\n// Share GPS location in chat via Geolocation API.\r\n// Renders coordinates and optional map link.\r\n\r\nimport type { ChatPlugin, PluginContext, ChatPluginEvent } from '../types/plugin';\r\n\r\nexport interface LocationPluginOptions {\r\n /** Map provider for link generation (default: 'google') */\r\n mapProvider?: 'google' | 'openstreetmap' | 'apple';\r\n /** High accuracy GPS (default: false) */\r\n highAccuracy?: boolean;\r\n /** Timeout in ms (default: 10000) */\r\n timeout?: number;\r\n /** Callback with location data */\r\n onLocation?: (lat: number, lng: number) => void;\r\n /** Custom message format */\r\n messageFormat?: (lat: number, lng: number, url: string) => string;\r\n}\r\n\r\nfunction getMapUrl(lat: number, lng: number, provider: string): string {\r\n switch (provider) {\r\n case 'openstreetmap':\r\n return `https://www.openstreetmap.org/?mlat=${lat}&mlon=${lng}#map=15/${lat}/${lng}`;\r\n case 'apple':\r\n return `https://maps.apple.com/?ll=${lat},${lng}`;\r\n default:\r\n return `https://www.google.com/maps?q=${lat},${lng}`;\r\n }\r\n}\r\n\r\nexport function locationPlugin(options: LocationPluginOptions = {}): ChatPlugin {\r\n const provider = options.mapProvider ?? 'google';\r\n const timeout = options.timeout ?? 10000;\r\n\r\n return {\r\n name: 'location',\r\n\r\n onEvent(event: ChatPluginEvent, ctx: PluginContext) {\r\n if (event.type === 'location:share') {\r\n if (typeof navigator === 'undefined' || !navigator.geolocation) {\r\n ctx.addBotMessage('📍 Location sharing is not supported in this browser.');\r\n return;\r\n }\r\n\r\n navigator.geolocation.getCurrentPosition(\r\n (position) => {\r\n const { latitude: lat, longitude: lng } = position.coords;\r\n const url = getMapUrl(lat, lng, provider);\r\n\r\n options.onLocation?.(lat, lng);\r\n\r\n const text = options.messageFormat\r\n ? options.messageFormat(lat, lng, url)\r\n : `📍 Location shared: [${lat.toFixed(5)}, ${lng.toFixed(5)}](${url})`;\r\n\r\n ctx.sendMessage(text);\r\n ctx.emit('location:shared', { lat, lng, url });\r\n },\r\n (error) => {\r\n ctx.addBotMessage(`📍 Unable to get location: ${error.message}`);\r\n ctx.emit('location:error', error.message);\r\n },\r\n {\r\n enableHighAccuracy: options.highAccuracy ?? false,\r\n timeout,\r\n maximumAge: 0,\r\n },\r\n );\r\n }\r\n },\r\n };\r\n}\r\n"],"mappings":"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"}