@inappai/react 0.1.0 → 1.0.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,"sources":["../src/components/InAppAI.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport type { CustomStyles, Tool, InAppAIProps } from '../types';\nimport './themes.css';\nimport './InAppAI.css';\n\ninterface Message {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n timestamp: Date;\n usage?: {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n };\n}\n\n// Loading skeleton component\nfunction LoadingSkeleton() {\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n padding: '12px 16px',\n background: 'var(--inapp-ai-assistant-bg)',\n borderRadius: 'var(--inapp-ai-border-radius)',\n }}\n >\n <div\n style={{\n height: '14px',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '90%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.1s',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '75%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.2s',\n borderRadius: '4px',\n }}\n />\n <style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n </div>\n );\n}\n\n// Error message component with enhanced display\ninterface ErrorMessageProps {\n error: string;\n onDismiss: () => void;\n}\n\nfunction ErrorMessage({ error, onDismiss }: ErrorMessageProps) {\n // Determine error type based on message\n const getErrorType = (msg: string) => {\n if (msg.includes('not responding') || msg.includes('connection') || msg.includes('network')) {\n return { type: 'connection', icon: '🔌', title: 'Connection Error' };\n }\n if (msg.includes('timeout')) {\n return { type: 'timeout', icon: '⏱️', title: 'Request Timeout' };\n }\n if (msg.includes('rate limit')) {\n return { type: 'rateLimit', icon: '🚦', title: 'Rate Limit' };\n }\n if (msg.includes('authentication') || msg.includes('unauthorized')) {\n return { type: 'auth', icon: '🔒', title: 'Authentication Error' };\n }\n return { type: 'generic', icon: '⚠️', title: 'Error' };\n };\n\n const errorInfo = getErrorType(error);\n\n return (\n <div\n className=\"inapp-ai-error-banner\"\n role=\"alert\"\n aria-live=\"assertive\"\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: '12px',\n padding: '14px 16px',\n background: 'linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)',\n borderLeft: '4px solid #ff9800',\n borderRadius: '0',\n }}\n >\n <span style={{ fontSize: '20px' }}>{errorInfo.icon}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: 600, marginBottom: '4px', color: '#856404' }}>\n {errorInfo.title}\n </div>\n <div style={{ fontSize: '13px', color: '#856404', wordBreak: 'break-word' }}>\n {error}\n </div>\n {errorInfo.type === 'connection' && (\n <div style={{ fontSize: '12px', marginTop: '6px', color: '#997404' }}>\n 💡 Make sure the backend server is running on the correct port\n </div>\n )}\n </div>\n <button\n onClick={onDismiss}\n style={{\n background: 'none',\n border: 'none',\n color: '#856404',\n cursor: 'pointer',\n padding: '4px 8px',\n fontSize: '16px',\n lineHeight: 1,\n }}\n aria-label=\"Dismiss error\"\n >\n ✕\n </button>\n </div>\n );\n}\n\n// Code block component with syntax highlighting and copy button\ninterface CodeBlockProps {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction CodeBlock({ inline, className, children, ...props }: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n // Extract language from className (format: language-js, language-python, etc.)\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : '';\n\n const codeString = String(children).replace(/\\n$/, '');\n\n const handleCopy = () => {\n navigator.clipboard.writeText(codeString);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n // Inline code (backticks)\n if (inline) {\n return <code className={className} {...props}>{children}</code>;\n }\n\n // Code block with syntax highlighting\n return (\n <div style={{ position: 'relative', marginTop: '8px', marginBottom: '8px' }}>\n <button\n onClick={handleCopy}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n padding: '4px 8px',\n background: copied ? '#28a745' : 'rgba(255, 255, 255, 0.1)',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n zIndex: 1,\n transition: 'background 0.2s',\n }}\n aria-label=\"Copy code\"\n >\n {copied ? '✓ Copied!' : '📋 Copy'}\n </button>\n <SyntaxHighlighter\n language={language || 'text'}\n style={vscDarkPlus}\n customStyle={{\n borderRadius: '8px',\n padding: '16px',\n fontSize: '13px',\n marginTop: 0,\n marginBottom: 0,\n }}\n {...props}\n >\n {codeString}\n </SyntaxHighlighter>\n </div>\n );\n}\n\nexport function InAppAI({\n endpoint = 'http://localhost:3001',\n position = 'bottom-right',\n displayMode = 'popup',\n defaultFolded = false,\n theme = 'light',\n context,\n customStyles = {},\n tools = [],\n panelMinWidth = '20%',\n panelMaxWidth = '33.33%',\n panelDefaultWidth = '25%',\n onPanelResize\n}: InAppAIProps) {\n const [isOpen, setIsOpen] = useState(displayMode.startsWith('sidebar') || displayMode.startsWith('panel'));\n const [isFolded, setIsFolded] = useState(defaultFolded && (displayMode.startsWith('sidebar') || displayMode.startsWith('panel')));\n const [messages, setMessages] = useState<Message[]>([]);\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [panelWidth, setPanelWidth] = useState(panelDefaultWidth);\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const conversationId = useRef(`react-demo-${Date.now()}`);\n const resizeRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isSidebar = displayMode.startsWith('sidebar');\n const isPanel = displayMode.startsWith('panel');\n const isLeftSidebar = displayMode === 'sidebar-left';\n // const isRightSidebar = displayMode === 'sidebar-right';\n const isLeftPanel = displayMode === 'panel-left';\n // const isRightPanel = displayMode === 'panel-right';\n\n // For sidebar and panel mode, start open\n useEffect(() => {\n if (isSidebar || isPanel) {\n setIsOpen(true);\n }\n }, [isSidebar, isPanel]);\n\n // Panel resize handlers\n useEffect(() => {\n if (!isPanel || !isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n const container = resizeRef.current?.parentElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n let newWidth: number;\n\n if (isLeftPanel) {\n newWidth = e.clientX;\n } else {\n newWidth = containerRect.width - e.clientX;\n }\n\n // Convert to percentage\n const widthPercent = (newWidth / containerRect.width) * 100;\n\n // Parse min/max widths\n const minPercent = parseFloat(panelMinWidth);\n const maxPercent = parseFloat(panelMaxWidth);\n\n // Clamp width between min and max\n const clampedPercent = Math.max(minPercent, Math.min(maxPercent, widthPercent));\n const newWidthStr = `${clampedPercent}%`;\n\n setPanelWidth(newWidthStr);\n if (onPanelResize) {\n onPanelResize(newWidth);\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }, [isResizing, isPanel, isLeftPanel, panelMinWidth, panelMaxWidth, onPanelResize]);\n\n // Check backend connection\n useEffect(() => {\n const checkConnection = async () => {\n try {\n const response = await fetch(`${endpoint}/health`);\n if (response.ok) {\n setIsConnected(true);\n setError(null);\n } else {\n setError('Backend not responding');\n }\n } catch (err) {\n setError('Failed to connect to backend');\n setIsConnected(false);\n }\n };\n\n checkConnection();\n const interval = setInterval(checkConnection, 30000); // Check every 30s\n return () => clearInterval(interval);\n }, [endpoint]);\n\n // Auto-scroll to bottom\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n // Refocus input after loading completes\n useEffect(() => {\n if (!isLoading && isConnected) {\n // Small delay to ensure DOM has updated\n setTimeout(() => {\n inputRef.current?.focus();\n }, 100);\n }\n }, [isLoading, isConnected]);\n\n const sendMessage = async () => {\n const message = inputValue.trim();\n if (!message || isLoading) return;\n\n // Add user message\n const userMessage: Message = {\n id: `${Date.now()}-user`,\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n\n setMessages(prev => [...prev, userMessage]);\n setInputValue('');\n setIsLoading(true);\n setError(null);\n\n try {\n // Helper to get fresh context (supports both static and function contexts)\n const getContext = () => typeof context === 'function' ? context() : context;\n\n // Prepare tool definitions (without handlers) for backend in OpenAI format\n const toolDefinitions = tools.map(({ name, description, parameters }) => ({\n type: 'function' as const,\n function: {\n name,\n description,\n parameters,\n },\n }));\n\n const response = await fetch(`${endpoint}/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message,\n conversationId: conversationId.current,\n context: getContext(),\n tools: toolDefinitions.length > 0 ? toolDefinitions : undefined,\n disableCache: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error('Failed to get response');\n }\n\n const data = await response.json();\n\n // Check if AI returned tool calls\n if (data.toolCalls && data.toolCalls.length > 0) {\n // Execute tool handlers locally\n const results = await Promise.all(\n data.toolCalls.map(async (toolCall: any) => {\n // OpenAI format: {id, type, function: {name, arguments}}\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters;\n\n const tool = tools.find(t => t.name === toolName);\n if (!tool) {\n return { success: false, error: `Tool '${toolName}' not found` };\n }\n try {\n const result = await Promise.resolve(tool.handler(toolArgs));\n return result;\n } catch (error: any) {\n console.error(`Tool '${tool.name}' failed:`, error);\n return { success: false, error: error.message };\n }\n })\n );\n\n // Send tool results back to AI for natural language response\n const toolResultsMessage = results\n .map((r: any, idx: any) => {\n const toolCall = data.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n return `Tool \"${toolName}\" result: ${JSON.stringify(r)}`;\n })\n .join('\\n');\n\n // IMPORTANT: Get fresh context after tool execution\n // Tool handlers may have updated state (e.g., added/completed todos)\n // By calling getContext() again, we ensure the AI sees the updated state\n const followUpResponse = await fetch(`${endpoint}/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message: toolResultsMessage,\n conversationId: conversationId.current,\n context: getContext(), // Fresh context after tool execution\n disableCache: false,\n }),\n });\n\n if (!followUpResponse.ok) {\n throw new Error('Failed to get AI response for tool results');\n }\n\n const followUpData = await followUpResponse.json();\n\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: followUpData.message || 'I executed the tools successfully.',\n timestamp: new Date(),\n usage: followUpData.usage,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n } else {\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: data.message,\n timestamp: new Date(),\n usage: data.usage,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n }\n } catch (err) {\n console.error('❌ Error:', err);\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n };\n\n const clearMessages = () => {\n setMessages([]);\n conversationId.current = `react-demo-${Date.now()}`;\n };\n\n const toggleFolded = () => {\n setIsFolded(!isFolded);\n };\n\n const positionClass = `inapp-ai-${position}`;\n // Don't add a theme class for 'light' since it's the default\n const themeClass = theme && theme !== 'light' ? `inapp-ai-theme-${theme}` : '';\n const modeClass = isSidebar\n ? `inapp-ai-sidebar inapp-ai-${displayMode}`\n : isPanel\n ? `inapp-ai-panel inapp-ai-${displayMode}`\n : 'inapp-ai-popup';\n const foldedClass = isFolded ? 'inapp-ai-folded' : '';\n\n // Build custom button style\n const buttonStyle: React.CSSProperties = {\n ...(customStyles.buttonBackgroundColor && { background: customStyles.buttonBackgroundColor }),\n ...(customStyles.buttonTextColor && { color: customStyles.buttonTextColor }),\n ...(customStyles.buttonSize && {\n width: customStyles.buttonSize,\n height: customStyles.buttonSize,\n fontSize: `calc(${customStyles.buttonSize} * 0.5)`\n }),\n ...(customStyles.buttonBorderRadius && { borderRadius: customStyles.buttonBorderRadius }),\n ...(customStyles.boxShadow && { boxShadow: customStyles.boxShadow }),\n };\n\n // Build custom window style\n const windowStyle: React.CSSProperties = {\n ...(customStyles.windowWidth && !isSidebar && !isPanel && { width: customStyles.windowWidth }),\n ...(customStyles.windowHeight && !isSidebar && !isPanel && { height: customStyles.windowHeight }),\n ...(customStyles.windowBorderRadius && { borderRadius: customStyles.windowBorderRadius }),\n ...(customStyles.fontFamily && { fontFamily: customStyles.fontFamily }),\n ...(customStyles.fontSize && { fontSize: customStyles.fontSize }),\n ...(customStyles.sidebarWidth && isSidebar && !isFolded && { width: customStyles.sidebarWidth }),\n ...(customStyles.sidebarFoldedWidth && isSidebar && isFolded && { width: customStyles.sidebarFoldedWidth }),\n ...(isPanel && { width: panelWidth }),\n };\n\n return (\n <>\n {/* Chat Button (only for popup mode) */}\n {!isSidebar && !isPanel && (\n <button\n className={`inapp-ai-button ${positionClass} ${themeClass}`}\n style={buttonStyle}\n onClick={() => setIsOpen(!isOpen)}\n aria-label={isOpen ? \"Close AI Assistant\" : \"Open AI Assistant\"}\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n tabIndex={0}\n >\n {isOpen ? '✕' : (customStyles.buttonIcon || '🤖')}\n {!isConnected && (\n <span\n className=\"inapp-ai-offline-indicator\"\n role=\"status\"\n aria-label=\"Backend disconnected\"\n />\n )}\n </button>\n )}\n\n {/* Chat Window */}\n {isOpen && (\n <div\n ref={resizeRef}\n role=\"dialog\"\n aria-label=\"AI Assistant Chat\"\n aria-modal={!isSidebar && !isPanel ? \"true\" : undefined}\n className={`inapp-ai-window ${modeClass} ${isSidebar || isPanel ? '' : positionClass} ${themeClass} ${foldedClass}`}\n style={windowStyle}\n >\n {/* Resize Handle for Panel */}\n {isPanel && (\n <div\n className={`inapp-ai-resize-handle ${isLeftPanel ? 'inapp-ai-resize-handle-right' : 'inapp-ai-resize-handle-left'}`}\n onMouseDown={() => setIsResizing(true)}\n title=\"Drag to resize\"\n />\n )}\n\n {/* Folded State Content */}\n {(isSidebar || isPanel) && isFolded ? (\n <div\n className=\"inapp-ai-folded-content\"\n onClick={toggleFolded}\n style={{ cursor: 'pointer' }}\n title=\"Click to unfold\"\n >\n <div className=\"inapp-ai-folded-icon\">\n {customStyles.buttonIcon || '🤖'}\n </div>\n <div className=\"inapp-ai-folded-text\">\n AI\n </div>\n {messages.length > 0 && (\n <div className=\"inapp-ai-message-count\">\n {messages.length}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* Header */}\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n {/* Fold button for panels (in header top right) */}\n {(isSidebar || isPanel) && (\n <button\n className=\"inapp-ai-header-fold-btn\"\n onClick={toggleFolded}\n aria-label={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n title={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n >\n {(isLeftSidebar || isLeftPanel) ? '◀' : '▶'}\n </button>\n )}\n {/* Close button for popup mode */}\n {!isSidebar && !isPanel && (\n <button\n className=\"inapp-ai-close-btn\"\n onClick={() => setIsOpen(false)}\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>Ask me anything! I'm powered by OpenAI.</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp.toLocaleTimeString()}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n )}\n </>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","ReactMarkdown","SyntaxHighlighter","vscDarkPlus","Fragment","jsx","jsxs","LoadingSkeleton","ErrorMessage","error","onDismiss","errorInfo","msg","CodeBlock","inline","className","children","props","copied","setCopied","useState","match","language","codeString","handleCopy","SyntaxHighlighter","vscDarkPlus","InAppAI","endpoint","position","displayMode","defaultFolded","theme","context","customStyles","tools","panelMinWidth","panelMaxWidth","panelDefaultWidth","onPanelResize","isOpen","setIsOpen","isFolded","setIsFolded","messages","setMessages","inputValue","setInputValue","isLoading","setIsLoading","isConnected","setIsConnected","setError","panelWidth","setPanelWidth","isResizing","setIsResizing","messagesEndRef","useRef","conversationId","resizeRef","inputRef","isSidebar","isPanel","isLeftSidebar","isLeftPanel","useEffect","handleMouseMove","e","container","containerRect","newWidth","widthPercent","minPercent","maxPercent","newWidthStr","handleMouseUp","checkConnection","interval","sendMessage","message","userMessage","prev","getContext","toolDefinitions","name","description","parameters","response","data","toolResultsMessage","toolCall","toolName","toolArgs","tool","t","r","idx","followUpResponse","followUpData","assistantMessage","err","handleKeyPress","clearMessages","toggleFolded","positionClass","themeClass","modeClass","foldedClass","buttonStyle","windowStyle","ReactMarkdown"]}
1
+ {"version":3,"sources":["../src/components/InAppAI.tsx","../src/hooks/useTools.ts","../src/hooks/useToolRegistry.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport type { CustomStyles, Tool, InAppAIProps, Message } from '../types';\nimport './themes.css';\nimport './InAppAI.css';\n\n// Internal message type (extends exported Message with required timestamp)\ninterface InternalMessage extends Message {\n timestamp: Date;\n}\n\n// Loading skeleton component\nfunction LoadingSkeleton() {\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n padding: '12px 16px',\n background: 'var(--inapp-ai-assistant-bg)',\n borderRadius: 'var(--inapp-ai-border-radius)',\n }}\n >\n <div\n style={{\n height: '14px',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '90%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.1s',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '75%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.2s',\n borderRadius: '4px',\n }}\n />\n <style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n </div>\n );\n}\n\n// Error message component with enhanced display\ninterface ErrorMessageProps {\n error: string;\n onDismiss: () => void;\n}\n\nfunction ErrorMessage({ error, onDismiss }: ErrorMessageProps) {\n // Determine error type based on message\n const getErrorType = (msg: string) => {\n if (msg.includes('not responding') || msg.includes('connection') || msg.includes('network')) {\n return { type: 'connection', icon: '🔌', title: 'Connection Error' };\n }\n if (msg.includes('timeout')) {\n return { type: 'timeout', icon: '⏱️', title: 'Request Timeout' };\n }\n if (msg.includes('rate limit')) {\n return { type: 'rateLimit', icon: '🚦', title: 'Rate Limit' };\n }\n if (msg.includes('authentication') || msg.includes('unauthorized')) {\n return { type: 'auth', icon: '🔒', title: 'Authentication Error' };\n }\n return { type: 'generic', icon: '⚠️', title: 'Error' };\n };\n\n const errorInfo = getErrorType(error);\n\n return (\n <div\n className=\"inapp-ai-error-banner\"\n role=\"alert\"\n aria-live=\"assertive\"\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: '12px',\n padding: '14px 16px',\n background: 'linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)',\n borderLeft: '4px solid #ff9800',\n borderRadius: '0',\n }}\n >\n <span style={{ fontSize: '20px' }}>{errorInfo.icon}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: 600, marginBottom: '4px', color: '#856404' }}>\n {errorInfo.title}\n </div>\n <div style={{ fontSize: '13px', color: '#856404', wordBreak: 'break-word' }}>\n {error}\n </div>\n {errorInfo.type === 'connection' && (\n <div style={{ fontSize: '12px', marginTop: '6px', color: '#997404' }}>\n 💡 Make sure the backend server is running on the correct port\n </div>\n )}\n </div>\n <button\n onClick={onDismiss}\n style={{\n background: 'none',\n border: 'none',\n color: '#856404',\n cursor: 'pointer',\n padding: '4px 8px',\n fontSize: '16px',\n lineHeight: 1,\n }}\n aria-label=\"Dismiss error\"\n >\n ✕\n </button>\n </div>\n );\n}\n\n// Code block component with syntax highlighting and copy button\ninterface CodeBlockProps {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction CodeBlock({ inline, className, children, ...props }: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n // Extract language from className (format: language-js, language-python, etc.)\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : '';\n\n const codeString = String(children).replace(/\\n$/, '');\n\n const handleCopy = () => {\n navigator.clipboard.writeText(codeString);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n // Inline code (backticks)\n if (inline) {\n return <code className={className} {...props}>{children}</code>;\n }\n\n // Code block with syntax highlighting\n return (\n <div style={{ position: 'relative', marginTop: '8px', marginBottom: '8px' }}>\n <button\n onClick={handleCopy}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n padding: '4px 8px',\n background: copied ? '#28a745' : 'rgba(255, 255, 255, 0.1)',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n zIndex: 1,\n transition: 'background 0.2s',\n }}\n aria-label=\"Copy code\"\n >\n {copied ? '✓ Copied!' : '📋 Copy'}\n </button>\n {/* @ts-ignore - React 18 JSX type compatibility */}\n <SyntaxHighlighter\n language={language || 'text'}\n style={vscDarkPlus}\n customStyle={{\n borderRadius: '8px',\n padding: '16px',\n fontSize: '13px',\n marginTop: 0,\n marginBottom: 0,\n }}\n {...props}\n >\n {codeString}\n </SyntaxHighlighter>\n </div>\n );\n}\n\nexport function InAppAI({\n endpoint,\n agentId,\n position = 'bottom-right',\n displayMode = 'popup',\n defaultFolded = false,\n theme = 'light',\n context,\n customStyles = {},\n tools = [],\n panelMinWidth = '20%',\n panelMaxWidth = '33.33%',\n panelDefaultWidth = '25%',\n onPanelResize,\n // Controlled mode props\n conversationId: externalConversationId,\n messages: externalMessages,\n onMessagesChange,\n showHeader = true,\n}: InAppAIProps) {\n // Require controlled mode - messages and onMessagesChange are required\n if (externalMessages === undefined || onMessagesChange === undefined) {\n throw new Error(\n 'InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. ' +\n 'See documentation for usage examples.'\n );\n }\n\n // Determine API endpoint (environment variable > prop > default)\n const apiEndpoint = endpoint ||\n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_INAPPAI_ENDPOINT) ||\n (typeof (import.meta as any).env !== 'undefined' && (import.meta as any).env?.VITE_INAPPAI_ENDPOINT) ||\n 'https://api.inappai.com/api';\n\n // Keep a ref to the latest external messages to avoid stale closures\n const externalMessagesRef = useRef<Message[]>(externalMessages);\n useEffect(() => {\n externalMessagesRef.current = externalMessages;\n }, [externalMessages]);\n\n // Messages from props, setMessages calls onMessagesChange\n const messages = externalMessages;\n const setMessages = (updater: Message[] | ((prev: Message[]) => Message[])) => {\n const currentMessages = externalMessagesRef.current;\n const newMessages = typeof updater === 'function' ? updater(currentMessages) : updater;\n externalMessagesRef.current = newMessages; // Update ref immediately for chained calls\n onMessagesChange(newMessages);\n };\n\n const [isOpen, setIsOpen] = useState(displayMode.startsWith('sidebar') || displayMode.startsWith('panel') || displayMode === 'embedded');\n const [isFolded, setIsFolded] = useState(defaultFolded && (displayMode.startsWith('sidebar') || displayMode.startsWith('panel')));\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [panelWidth, setPanelWidth] = useState(panelDefaultWidth);\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const internalConversationId = useRef(`react-demo-${Date.now()}`);\n const conversationId = externalConversationId || internalConversationId.current;\n const resizeRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isEmbedded = displayMode === 'embedded';\n const isSidebar = displayMode.startsWith('sidebar');\n const isPanel = displayMode.startsWith('panel');\n const isLeftSidebar = displayMode === 'sidebar-left';\n // const isRightSidebar = displayMode === 'sidebar-right';\n const isLeftPanel = displayMode === 'panel-left';\n // const isRightPanel = displayMode === 'panel-right';\n\n // For sidebar, panel, and embedded mode, always stay open\n useEffect(() => {\n if (isSidebar || isPanel || isEmbedded) {\n setIsOpen(true);\n }\n }, [isSidebar, isPanel, isEmbedded]);\n\n // Panel resize handlers\n useEffect(() => {\n if (!isPanel || !isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n const container = resizeRef.current?.parentElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n let newWidth: number;\n\n if (isLeftPanel) {\n newWidth = e.clientX;\n } else {\n newWidth = containerRect.width - e.clientX;\n }\n\n // Convert to percentage\n const widthPercent = (newWidth / containerRect.width) * 100;\n\n // Parse min/max widths\n const minPercent = parseFloat(panelMinWidth);\n const maxPercent = parseFloat(panelMaxWidth);\n\n // Clamp width between min and max\n const clampedPercent = Math.max(minPercent, Math.min(maxPercent, widthPercent));\n const newWidthStr = `${clampedPercent}%`;\n\n setPanelWidth(newWidthStr);\n if (onPanelResize) {\n onPanelResize(newWidth);\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }, [isResizing, isPanel, isLeftPanel, panelMinWidth, panelMaxWidth, onPanelResize]);\n\n // Check backend connection\n useEffect(() => {\n const checkConnection = async () => {\n try {\n const response = await fetch(`${apiEndpoint}/${agentId}/health`);\n if (response.ok) {\n setIsConnected(true);\n setError(null);\n } else {\n setError('Backend not responding');\n }\n } catch (err) {\n setError('Failed to connect to backend');\n setIsConnected(false);\n }\n };\n\n checkConnection();\n const interval = setInterval(checkConnection, 30000); // Check every 30s\n return () => clearInterval(interval);\n }, [apiEndpoint, agentId]);\n\n // Auto-scroll to bottom\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n // Refocus input after loading completes\n useEffect(() => {\n if (!isLoading && isConnected) {\n // Small delay to ensure DOM has updated\n setTimeout(() => {\n inputRef.current?.focus();\n }, 100);\n }\n }, [isLoading, isConnected]);\n\n const sendMessage = async () => {\n const message = inputValue.trim();\n if (!message || isLoading) return;\n\n // Add user message\n const userMessage: Message = {\n id: `${Date.now()}-user`,\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n\n setMessages(prev => [...prev, userMessage]);\n setInputValue('');\n setIsLoading(true);\n setError(null);\n\n try {\n // Helper to get fresh context (supports both static and function contexts)\n const getContext = () => typeof context === 'function' ? context() : context;\n\n // Prepare tool definitions (without handlers) for backend in OpenAI format\n const toolDefinitions = tools.map(({ name, description, parameters }) => ({\n type: 'function' as const,\n function: {\n name,\n description,\n parameters,\n },\n }));\n\n const response = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message,\n conversationId,\n context: getContext(),\n tools: toolDefinitions.length > 0 ? toolDefinitions : undefined,\n disableCache: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error('Failed to get response');\n }\n\n const data = await response.json();\n\n // Check if AI returned tool calls\n if (data.toolCalls && data.toolCalls.length > 0) {\n // Execute tool handlers locally\n const results = await Promise.all(\n data.toolCalls.map(async (toolCall: any) => {\n // OpenAI format: {id, type, function: {name, arguments}}\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters;\n\n const tool = tools.find(t => t.name === toolName);\n if (!tool) {\n return { success: false, error: `Tool '${toolName}' not found` };\n }\n try {\n const result = await Promise.resolve(tool.handler(toolArgs));\n return result;\n } catch (error: any) {\n console.error(`Tool '${tool.name}' failed:`, error);\n return { success: false, error: error.message };\n }\n })\n );\n\n // Send tool results back to AI for natural language response\n const toolResultsMessage = results\n .map((r: any, idx: any) => {\n const toolCall = data.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n return `Tool \"${toolName}\" result: ${JSON.stringify(r)}`;\n })\n .join('\\n');\n\n // IMPORTANT: Get fresh context after tool execution\n // Tool handlers may have updated state (e.g., added/completed todos)\n // By calling getContext() again, we ensure the AI sees the updated state\n const followUpResponse = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message: toolResultsMessage,\n conversationId,\n context: getContext(), // Fresh context after tool execution\n disableCache: false,\n }),\n });\n\n if (!followUpResponse.ok) {\n throw new Error('Failed to get AI response for tool results');\n }\n\n const followUpData = await followUpResponse.json();\n\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: followUpData.message || 'I executed the tools successfully.',\n timestamp: new Date(),\n usage: followUpData.usage,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n } else {\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: data.message,\n timestamp: new Date(),\n usage: data.usage,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n }\n } catch (err) {\n console.error('❌ Error:', err);\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n };\n\n const clearMessages = () => {\n setMessages([]);\n };\n\n const toggleFolded = () => {\n setIsFolded(!isFolded);\n };\n\n const positionClass = `inapp-ai-${position}`;\n // Don't add a theme class for 'light' since it's the default\n const themeClass = theme && theme !== 'light' ? `inapp-ai-theme-${theme}` : '';\n const modeClass = isEmbedded\n ? 'inapp-ai-embedded'\n : isSidebar\n ? `inapp-ai-sidebar inapp-ai-${displayMode}`\n : isPanel\n ? `inapp-ai-panel inapp-ai-${displayMode}`\n : 'inapp-ai-popup';\n const foldedClass = isFolded ? 'inapp-ai-folded' : '';\n\n // Build custom button style\n const buttonStyle: React.CSSProperties = {\n ...(customStyles.buttonBackgroundColor && { background: customStyles.buttonBackgroundColor }),\n ...(customStyles.buttonTextColor && { color: customStyles.buttonTextColor }),\n ...(customStyles.buttonSize && {\n width: customStyles.buttonSize,\n height: customStyles.buttonSize,\n fontSize: `calc(${customStyles.buttonSize} * 0.5)`\n }),\n ...(customStyles.buttonBorderRadius && { borderRadius: customStyles.buttonBorderRadius }),\n ...(customStyles.boxShadow && { boxShadow: customStyles.boxShadow }),\n };\n\n // Build custom window style\n const windowStyle: React.CSSProperties = {\n ...(customStyles.windowWidth && !isSidebar && !isPanel && { width: customStyles.windowWidth }),\n ...(customStyles.windowHeight && !isSidebar && !isPanel && { height: customStyles.windowHeight }),\n ...(customStyles.windowBorderRadius && { borderRadius: customStyles.windowBorderRadius }),\n ...(customStyles.fontFamily && { fontFamily: customStyles.fontFamily }),\n ...(customStyles.fontSize && { fontSize: customStyles.fontSize }),\n ...(customStyles.sidebarWidth && isSidebar && !isFolded && { width: customStyles.sidebarWidth }),\n ...(customStyles.sidebarFoldedWidth && isSidebar && isFolded && { width: customStyles.sidebarFoldedWidth }),\n ...(isPanel && { width: panelWidth }),\n };\n\n // For embedded mode, return the chat window directly without fragment\n // This ensures proper flex layout inheritance from parent\n if (isEmbedded && isOpen) {\n return (\n <div\n ref={resizeRef}\n role=\"region\"\n aria-label=\"AI Assistant Chat\"\n className={`inapp-ai-window inapp-ai-embedded ${themeClass}`}\n style={windowStyle}\n >\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <>\n {/* Chat Button (only for popup mode, not embedded) */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className={`inapp-ai-button ${positionClass} ${themeClass}`}\n style={buttonStyle}\n onClick={() => setIsOpen(!isOpen)}\n aria-label={isOpen ? \"Close AI Assistant\" : \"Open AI Assistant\"}\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n tabIndex={0}\n >\n {isOpen ? '✕' : (customStyles.buttonIcon || '🤖')}\n {!isConnected && (\n <span\n className=\"inapp-ai-offline-indicator\"\n role=\"status\"\n aria-label=\"Backend disconnected\"\n />\n )}\n </button>\n )}\n\n {/* Chat Window */}\n {isOpen && (\n <div\n ref={resizeRef}\n role={isEmbedded ? \"region\" : \"dialog\"}\n aria-label=\"AI Assistant Chat\"\n aria-modal={!isSidebar && !isPanel && !isEmbedded ? \"true\" : undefined}\n className={`inapp-ai-window ${modeClass} ${isSidebar || isPanel || isEmbedded ? '' : positionClass} ${themeClass} ${foldedClass}`}\n style={windowStyle}\n >\n {/* Resize Handle for Panel */}\n {isPanel && (\n <div\n className={`inapp-ai-resize-handle ${isLeftPanel ? 'inapp-ai-resize-handle-right' : 'inapp-ai-resize-handle-left'}`}\n onMouseDown={() => setIsResizing(true)}\n title=\"Drag to resize\"\n />\n )}\n\n {/* Folded State Content */}\n {(isSidebar || isPanel) && isFolded ? (\n <div\n className=\"inapp-ai-folded-content\"\n onClick={toggleFolded}\n style={{ cursor: 'pointer' }}\n title=\"Click to unfold\"\n >\n <div className=\"inapp-ai-folded-icon\">\n {customStyles.buttonIcon || '🤖'}\n </div>\n <div className=\"inapp-ai-folded-text\">\n AI\n </div>\n {messages.length > 0 && (\n <div className=\"inapp-ai-message-count\">\n {messages.length}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n {/* Fold button for panels (in header top right) */}\n {(isSidebar || isPanel) && (\n <button\n className=\"inapp-ai-header-fold-btn\"\n onClick={toggleFolded}\n aria-label={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n title={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n >\n {(isLeftSidebar || isLeftPanel) ? '◀' : '▶'}\n </button>\n )}\n {/* Close button for popup mode */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className=\"inapp-ai-close-btn\"\n onClick={() => setIsOpen(false)}\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp?.toLocaleTimeString() || ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n )}\n </>\n );\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Options for the useTools hook\n */\nexport interface UseToolsOptions {\n /**\n * Initial tools to register\n */\n initialTools?: Tool[];\n\n /**\n * Whether to automatically cleanup tools on unmount\n * @default true\n */\n autoCleanup?: boolean;\n}\n\n/**\n * Return value from the useTools hook\n */\nexport interface UseToolsReturn {\n /**\n * Current array of registered tools\n */\n tools: Tool[];\n\n /**\n * Register a new tool\n * @param tool - Tool to register\n * @throws {Error} If tool with same name already exists\n */\n registerTool: (tool: Tool) => void;\n\n /**\n * Unregister a tool by name\n * @param name - Name of the tool to unregister\n */\n unregisterTool: (name: string) => void;\n\n /**\n * Clear all registered tools\n */\n clearTools: () => void;\n\n /**\n * Check if a tool with the given name exists\n * @param name - Tool name to check\n * @returns true if tool exists, false otherwise\n */\n hasTool: (name: string) => boolean;\n}\n\n/**\n * Hook for managing tools dynamically in React components\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { tools, registerTool, unregisterTool } = useTools();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registerTool({\n * name: 'addTodo',\n * description: 'Add a new todo',\n * parameters: {\n * type: 'object',\n * properties: {\n * task: { type: 'string' }\n * }\n * },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * });\n *\n * return () => unregisterTool('addTodo');\n * }, [registerTool, unregisterTool, setTodos]);\n *\n * return <InAppAI endpoint=\"...\" tools={tools} />;\n * }\n * ```\n *\n * @param options - Configuration options\n * @returns Tool management functions and current tools array\n */\nexport function useTools(options: UseToolsOptions = {}): UseToolsReturn {\n const { initialTools = [], autoCleanup = true } = options;\n\n // Use ref to track tool names for validation\n const toolNamesRef = useRef<Set<string>>(new Set());\n\n // State for tools array\n const [tools, setTools] = useState<Tool[]>(() => {\n // Initialize with initial tools\n initialTools.forEach(tool => {\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Duplicate tool name in initialTools: \"${tool.name}\". ` +\n `Only the first occurrence will be used.`\n );\n } else {\n toolNamesRef.current.add(tool.name);\n }\n });\n\n return initialTools.filter((tool, index) => {\n // Filter out duplicates\n return initialTools.findIndex(t => t.name === tool.name) === index;\n });\n });\n\n /**\n * Register a new tool\n */\n const registerTool = useCallback((tool: Tool) => {\n if (!tool.name) {\n throw new Error('[useTools] Tool must have a name');\n }\n\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Tool \"${tool.name}\" is already registered. ` +\n `Use unregisterTool() first if you want to replace it.`\n );\n return;\n }\n\n // Validate tool structure\n if (!tool.description) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing description`);\n }\n\n if (!tool.parameters) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing parameters schema`);\n }\n\n if (typeof tool.handler !== 'function') {\n throw new Error(`[useTools] Tool \"${tool.name}\" must have a handler function`);\n }\n\n toolNamesRef.current.add(tool.name);\n setTools(prev => [...prev, tool]);\n }, []);\n\n /**\n * Unregister a tool by name\n */\n const unregisterTool = useCallback((name: string) => {\n if (!toolNamesRef.current.has(name)) {\n console.warn(`[useTools] Tool \"${name}\" is not registered`);\n return;\n }\n\n toolNamesRef.current.delete(name);\n setTools(prev => prev.filter(tool => tool.name !== name));\n }, []);\n\n /**\n * Clear all tools\n */\n const clearTools = useCallback(() => {\n toolNamesRef.current.clear();\n setTools([]);\n }, []);\n\n /**\n * Check if a tool exists\n */\n const hasTool = useCallback((name: string): boolean => {\n return toolNamesRef.current.has(name);\n }, []);\n\n /**\n * Cleanup on unmount if autoCleanup is enabled\n */\n useEffect(() => {\n if (autoCleanup) {\n return () => {\n toolNamesRef.current.clear();\n };\n }\n }, [autoCleanup]);\n\n return {\n tools,\n registerTool,\n unregisterTool,\n clearTools,\n hasTool,\n };\n}\n","import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Tool registry interface\n */\nexport interface ToolRegistry {\n /**\n * Register tools under a namespace\n * @param namespace - Unique namespace identifier\n * @param tools - Array of tools to register\n */\n register(namespace: string, tools: Tool[]): void;\n\n /**\n * Unregister all tools for a namespace\n * @param namespace - Namespace to unregister\n */\n unregister(namespace: string): void;\n\n /**\n * Get tools for a specific namespace\n * @param namespace - Namespace to query\n * @returns Array of tools for the namespace, or empty array if not found\n */\n getTools(namespace: string): Tool[];\n\n /**\n * Get all registered tools from all namespaces\n * @returns Combined array of all tools\n */\n getAllTools(): Tool[];\n\n /**\n * Clear all registered tools from all namespaces\n */\n clear(): void;\n\n /**\n * Get all registered namespace names\n * @returns Array of namespace names\n */\n getNamespaces(): string[];\n}\n\n/**\n * Context for the tool registry\n */\nconst ToolRegistryContext = createContext<ToolRegistry | null>(null);\n\n/**\n * Props for ToolRegistryProvider\n */\nexport interface ToolRegistryProviderProps {\n /**\n * Child components\n */\n children: React.ReactNode;\n\n /**\n * Initial tools organized by namespace\n */\n initialTools?: Record<string, Tool[]>;\n}\n\n/**\n * Provider component for global tool registry\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <ToolRegistryProvider>\n * <MyApp />\n * </ToolRegistryProvider>\n * );\n * }\n * ```\n */\nexport function ToolRegistryProvider({\n children,\n initialTools = {},\n}: ToolRegistryProviderProps): React.ReactElement {\n // State: Map of namespace -> tools\n const [toolsMap, setToolsMap] = useState<Map<string, Tool[]>>(\n () => new Map(Object.entries(initialTools))\n );\n\n /**\n * Register tools under a namespace\n */\n const register = useCallback((namespace: string, tools: Tool[]) => {\n // Validate namespace\n if (!namespace || typeof namespace !== 'string') {\n throw new Error('[ToolRegistry] Namespace must be a non-empty string');\n }\n\n if (!/^[a-zA-Z0-9_-]+$/.test(namespace)) {\n throw new Error(\n `[ToolRegistry] Invalid namespace \"${namespace}\". ` +\n `Use only alphanumeric characters, hyphens, and underscores.`\n );\n }\n\n // Validate tools\n if (!Array.isArray(tools)) {\n throw new Error('[ToolRegistry] Tools must be an array');\n }\n\n // Check for duplicate tool names within the namespace\n const toolNames = new Set<string>();\n tools.forEach(tool => {\n if (toolNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Duplicate tool name in namespace \"${namespace}\": \"${tool.name}\"`\n );\n }\n toolNames.add(tool.name);\n });\n\n setToolsMap(prev => {\n const next = new Map(prev);\n next.set(namespace, tools);\n return next;\n });\n }, []);\n\n /**\n * Unregister a namespace\n */\n const unregister = useCallback((namespace: string) => {\n setToolsMap(prev => {\n if (!prev.has(namespace)) {\n console.warn(`[ToolRegistry] Namespace \"${namespace}\" is not registered`);\n return prev;\n }\n\n const next = new Map(prev);\n next.delete(namespace);\n return next;\n });\n }, []);\n\n /**\n * Get tools for a specific namespace\n */\n const getTools = useCallback(\n (namespace: string): Tool[] => {\n return toolsMap.get(namespace) || [];\n },\n [toolsMap]\n );\n\n /**\n * Get all tools from all namespaces\n */\n const getAllTools = useCallback((): Tool[] => {\n const allTools: Tool[] = [];\n const seenNames = new Set<string>();\n\n // Iterate through namespaces\n for (const [namespace, tools] of toolsMap.entries()) {\n for (const tool of tools) {\n // Warn about name conflicts across namespaces\n if (seenNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Tool name conflict: \"${tool.name}\" exists in multiple namespaces. ` +\n `Only the first occurrence will be used.`\n );\n continue;\n }\n\n seenNames.add(tool.name);\n allTools.push(tool);\n }\n }\n\n return allTools;\n }, [toolsMap]);\n\n /**\n * Clear all tools\n */\n const clear = useCallback(() => {\n setToolsMap(new Map());\n }, []);\n\n /**\n * Get all namespace names\n */\n const getNamespaces = useCallback((): string[] => {\n return Array.from(toolsMap.keys());\n }, [toolsMap]);\n\n // Memoize the registry value\n const registry = useMemo<ToolRegistry>(\n () => ({\n register,\n unregister,\n getTools,\n getAllTools,\n clear,\n getNamespaces,\n }),\n [register, unregister, getTools, getAllTools, clear, getNamespaces]\n );\n\n return (\n <ToolRegistryContext.Provider value={registry}>\n {children}\n </ToolRegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the tool registry\n *\n * @example\n * ```tsx\n * function TodoPage() {\n * const registry = useToolRegistry();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registry.register('todos', [\n * {\n * name: 'addTodo',\n * description: 'Add a todo',\n * parameters: { type: 'object', properties: { task: { type: 'string' } } },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * }\n * ]);\n *\n * return () => registry.unregister('todos');\n * }, [registry, todos, setTodos]);\n *\n * return <TodoList />;\n * }\n *\n * function ChatWidget() {\n * const registry = useToolRegistry();\n * const allTools = registry.getAllTools();\n *\n * return <InAppAI endpoint=\"...\" tools={allTools} />;\n * }\n * ```\n *\n * @throws {Error} If used outside of ToolRegistryProvider\n * @returns Tool registry instance\n */\nexport function useToolRegistry(): ToolRegistry {\n const registry = useContext(ToolRegistryContext);\n\n if (!registry) {\n throw new Error(\n '[useToolRegistry] must be used within a ToolRegistryProvider. ' +\n 'Wrap your component tree with <ToolRegistryProvider>.'\n );\n }\n\n return registry;\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","ReactMarkdown","SyntaxHighlighter","vscDarkPlus","Fragment","jsx","jsxs","LoadingSkeleton","ErrorMessage","error","onDismiss","errorInfo","msg","CodeBlock","inline","className","children","props","copied","setCopied","useState","match","language","codeString","handleCopy","SyntaxHighlighter","vscDarkPlus","InAppAI","endpoint","agentId","position","displayMode","defaultFolded","theme","context","customStyles","tools","panelMinWidth","panelMaxWidth","panelDefaultWidth","onPanelResize","externalConversationId","externalMessages","onMessagesChange","showHeader","apiEndpoint","externalMessagesRef","useRef","useEffect","messages","setMessages","updater","currentMessages","newMessages","isOpen","setIsOpen","isFolded","setIsFolded","inputValue","setInputValue","isLoading","setIsLoading","isConnected","setIsConnected","setError","panelWidth","setPanelWidth","isResizing","setIsResizing","messagesEndRef","internalConversationId","conversationId","resizeRef","inputRef","isEmbedded","isSidebar","isPanel","isLeftSidebar","isLeftPanel","handleMouseMove","e","container","containerRect","newWidth","widthPercent","minPercent","maxPercent","newWidthStr","handleMouseUp","checkConnection","interval","sendMessage","message","userMessage","prev","getContext","toolDefinitions","name","description","parameters","response","data","toolResultsMessage","toolCall","toolName","toolArgs","tool","t","r","idx","followUpResponse","followUpData","assistantMessage","err","handleKeyPress","clearMessages","toggleFolded","positionClass","themeClass","modeClass","foldedClass","buttonStyle","windowStyle","ReactMarkdown","useState","useCallback","useEffect","useRef","useTools","options","initialTools","autoCleanup","toolNamesRef","tools","setTools","tool","index","t","registerTool","prev","unregisterTool","name","clearTools","hasTool","createContext","useContext","useState","useCallback","useMemo","jsx","ToolRegistryContext","ToolRegistryProvider","children","initialTools","toolsMap","setToolsMap","register","namespace","tools","toolNames","tool","prev","next","unregister","getTools","getAllTools","allTools","seenNames","clear","getNamespaces","registry","useToolRegistry"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inappai/react",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Beautiful, customizable AI chat component for React applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -20,7 +20,12 @@
20
20
  "build": "tsup",
21
21
  "dev": "tsup --watch",
22
22
  "type-check": "tsc --noEmit",
23
- "clean": "rm -rf dist"
23
+ "clean": "rm -rf dist",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "test:ui": "vitest --ui",
27
+ "test:coverage": "vitest run --coverage",
28
+ "bench": "vitest bench"
24
29
  },
25
30
  "keywords": [
26
31
  "ai",
@@ -36,28 +41,37 @@
36
41
  "license": "MIT",
37
42
  "repository": {
38
43
  "type": "git",
39
- "url": "https://github.com/InAppAI/react.git",
40
- "directory": "packages/inapp-ai-react"
44
+ "url": "git+https://github.com/InAppAI/react.git",
45
+ "directory": "packages/inappai-react"
41
46
  },
42
47
  "bugs": {
43
48
  "url": "https://github.com/InAppAI/react/issues"
44
49
  },
45
50
  "homepage": "https://github.com/InAppAI/react#readme",
46
51
  "peerDependencies": {
47
- "react": "^18.0.0",
48
- "react-dom": "^18.0.0"
52
+ "react": "^18.0.0 || ^19.0.0",
53
+ "react-dom": "^18.0.0 || ^19.0.0"
49
54
  },
50
55
  "dependencies": {
51
- "react-markdown": "^10.1.0",
56
+ "react-markdown": "^9.1.0",
52
57
  "react-syntax-highlighter": "^16.1.0"
53
58
  },
54
59
  "devDependencies": {
55
- "@types/react": "^18.2.55",
56
- "@types/react-dom": "^18.2.19",
60
+ "@testing-library/jest-dom": "^6.9.1",
61
+ "@testing-library/react": "^16.3.0",
62
+ "@testing-library/user-event": "^14.6.1",
63
+ "@types/node": "^20.19.24",
64
+ "@types/react": "^18.3.18",
65
+ "@types/react-dom": "^18.3.5",
57
66
  "@types/react-syntax-highlighter": "^15.5.13",
58
- "react": "^18.2.0",
59
- "react-dom": "^18.2.0",
67
+ "@vitejs/plugin-react": "^5.1.1",
68
+ "@vitest/coverage-v8": "^4.0.9",
69
+ "@vitest/ui": "^4.0.9",
70
+ "jsdom": "^27.2.0",
71
+ "react": "^18.3.1",
72
+ "react-dom": "^18.3.1",
60
73
  "tsup": "^8.0.0",
61
- "typescript": "^5.3.3"
74
+ "typescript": "^5.9.3",
75
+ "vitest": "^4.0.9"
62
76
  }
63
77
  }