UNPKG

ai-crw

Version:

ai Chat Widget is a lightweight, customizable React component that adds an AI-powered chat bubble to your website. It seamlessly integrates with OpenRouter to enable real-time conversations powered by leading LLMs like GPT-4 and Claude.

1 lines 15.5 kB
{"version":3,"sources":["../src/index.tsx","#style-inject:#style-inject","../src/index.css","../src/http.ts"],"sourcesContent":["import React, { useState, useRef, useEffect } from 'react';\nimport './index.css';\nimport '@fortawesome/fontawesome-free/css/all.min.css';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { sendMessageToAI } from './http';\n\nexport type PopChatProps = {\n contextData?: any;\n businessInfo?: string;\n chatHeadline?: string;\n aiCharacter?: string;\n direction?: 'ltr' | 'rtl';\n};\n\ntype Message = {\n role: 'user' | 'assistant';\n content: string;\n};\n\nexport const PopChat: React.FC<PopChatProps> = ({\n contextData,\n businessInfo,\n aiCharacter,\n chatHeadline = 'Chat with me',\n direction = 'ltr',\n}) => {\n const [messages, setMessages] = useState<Message[]>([]);\n const [chatOpen, setChatOpen] = useState<boolean>(false);\n const textRef = useRef<HTMLInputElement>(null);\n const msgAreaRef = useRef<HTMLDivElement>(null);\n const [isMobile, setIsMobile] = useState<boolean>(false);\n\n const handleSend = async () => {\n if (textRef.current) {\n const newMessage = textRef.current.value.trim();\n if (newMessage) {\n const userMessage: Message = { role: 'user', content: newMessage };\n setMessages((prev) => [...prev, userMessage]);\n textRef.current.value = '';\n\n try {\n const aiReply = await sendMessageToAI(\n [...messages, userMessage],\n contextData,\n businessInfo,\n aiCharacter,\n );\n const assistantMessage: Message = {\n role: 'assistant',\n content: aiReply,\n };\n setMessages((prev) => [...prev, assistantMessage]);\n } catch (error) {\n console.error('Error fetching AI reply:', error);\n setMessages((prev) => [\n ...prev,\n { role: 'assistant', content: '⚠️ Error fetching AI response' },\n ]);\n }\n }\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n handleSend();\n }\n };\n\n useEffect(() => {\n if (msgAreaRef.current) {\n msgAreaRef.current.scrollTop = msgAreaRef.current.scrollHeight;\n }\n }, [messages]);\n\n useEffect(() => {\n const handleResize = () => {\n setIsMobile(window.innerWidth <= 768);\n };\n handleResize();\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, []);\n\n return (\n <div id=\"chatCon\" dir={direction} className={chatOpen ? 'open' : ''}>\n <AnimatePresence>\n {chatOpen && (\n <motion.div\n className=\"chat-box\"\n initial={{ y: '100%', opacity: 0 }}\n animate={{ y: 0, opacity: 1 }}\n exit={{ y: '100%', opacity: 0 }}\n transition={{ duration: 0.4, ease: 'easeInOut' }}\n style={{\n position: isMobile ? 'fixed' : 'absolute',\n top: isMobile ? 0 : 'auto',\n bottom: isMobile ? 0 : '3%',\n right: isMobile ? 0 : '3%',\n left: isMobile ? 0 : 'auto',\n width: isMobile ? '100%' : '20%',\n height: isMobile ? '100%' : '30rem',\n minWidth: isMobile ? 'auto' : '18rem',\n borderRadius: isMobile ? '0' : '1.5625rem',\n background: '#eee',\n zIndex: 9999,\n }}\n >\n <div className=\"header\">\n {chatHeadline}\n <button\n className=\"close-button\"\n onClick={() => setChatOpen(false)}\n >\n X\n </button>\n </div>\n\n <div className=\"msg-area\" ref={msgAreaRef}>\n {messages.map((msg, i) =>\n msg.role === 'user' ? (\n <p key={i} className=\"left\">\n <span>{msg.content}</span>\n </p>\n ) : (\n <p key={i} className=\"right\">\n <span>{msg.content}</span>\n </p>\n ),\n )}\n </div>\n\n <div className=\"footer\">\n <input type=\"text\" ref={textRef} onKeyDown={handleKeyDown} />\n <button onClick={handleSend}>\n <i className=\"fa fa-paper-plane\" />\n </button>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* כפתור פתיחה */}\n <AnimatePresence>\n {!chatOpen && (\n <motion.div\n className=\"pop\"\n initial={{ opacity: 0, scale: 0 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0 }}\n transition={{ duration: 0.4 }}\n >\n <p>\n <motion.img\n onClick={() => setChatOpen(true)}\n src=\"https://ai-crw-server-861817b40609.herokuapp.com/blue_chat.png\"\n alt=\"Chat\"\n style={{ width: 60, height: 60, cursor: 'pointer' }}\n />\n </p>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n};\n\nexport default PopChat;\n","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\"@media (min-width: 768px){#chatCon{position:fixed;bottom:3%;right:3%;z-index:9999}#chatCon.open{inset:0;width:100vw;height:100vh;background:#0003;display:flex;flex-direction:column;align-items:center;justify-content:center}#chatCon .pop{position:fixed;bottom:3%;right:3%;z-index:10000;cursor:pointer}#chatCon .pop img{border-radius:50%;width:60px;height:60px}#chatCon .chat-box{display:flex;flex-direction:column;width:100%;max-width:380px;height:500px;border-radius:25px;background-color:#eee;position:relative;z-index:10001;overflow:hidden}#chatCon .chat-box .header{background-color:#257cb5;padding:1rem;border-radius:25px 25px 0 0;color:#fff;font-size:1.2rem;display:flex;justify-content:space-between;align-items:center}#chatCon .close-button{font-size:1.25rem;background:transparent;border:none;color:#fff;cursor:pointer}#chatCon .chat-box .msg-area{flex:1;overflow-y:auto;padding:1rem}#chatCon .chat-box .footer{padding:1rem;display:flex;align-items:center;background:#fff;justify-content:space-around}#chatCon .chat-box .footer input{border:1px solid #ccc;padding:.625rem;width:80%;border-radius:.9375rem}#chatCon .chat-box .footer input:focus{outline:none}#chatCon .chat-box .footer button{border:none;font-size:1.5rem;color:#257cb5;cursor:pointer;background:none}body.chat-open{overflow:hidden}#chatCon .chat-box .msg-area .left span,#chatCon .chat-box .msg-area .right span{display:inline-block;font-size:.85rem;border-radius:.9375rem;padding:.9375rem;margin-bottom:.5rem}#chatCon[dir=ltr] .chat-box .msg-area,#chatCon[dir=ltr] .chat-box .msg-area .left{text-align:left}#chatCon[dir=ltr] .chat-box .msg-area .right{text-align:right}#chatCon[dir=ltr] .chat-box .msg-area .left span{background-color:#ddd}#chatCon[dir=ltr] .chat-box .msg-area .right span{background-color:#257cb5;color:#fff;text-align:left}#chatCon[dir=ltr] .chat-box .footer{flex-direction:row}#chatCon[dir=ltr] .chat-box .footer input{text-align:left}#chatCon[dir=ltr] .close-button{margin-left:auto;margin-right:0}#chatCon[dir=rtl] .chat-box .msg-area,#chatCon[dir=rtl] .chat-box .msg-area .left{text-align:right}#chatCon[dir=rtl] .chat-box .msg-area .right{text-align:left}#chatCon[dir=rtl] .chat-box .msg-area .left span{background-color:#ddd}#chatCon[dir=rtl] .chat-box .msg-area .right span{background-color:#257cb5;color:#fff;text-align:right}#chatCon[dir=rtl] .chat-box .footer{flex-direction:row}#chatCon[dir=rtl] .chat-box .footer input{text-align:right}#chatCon[dir=rtl] .close-button{margin-right:auto;margin-left:0}}@media (max-width: 768px){#chatCon{position:fixed;bottom:3%;right:3%;z-index:9999}#chatCon.open{position:fixed;inset:0;width:100vw;height:100vh;background:#0003;display:flex;flex-direction:column;align-items:center;justify-content:center}#chatCon .chat-box{width:100%;height:100%;border-radius:0;background-color:#fff;display:flex;flex-direction:column}#chatCon .chat-box .header{background-color:#257cb5;padding:1rem;color:#fff;font-size:1.25rem;display:flex;justify-content:space-between;align-items:center}.close-button{font-size:1.5rem;background:transparent;border:none;color:#fff;cursor:pointer}#chatCon .chat-box .msg-area{flex:1;overflow-y:auto;padding:1rem}#chatCon .msg-area .left span,#chatCon .msg-area .right span{display:inline-block;font-size:1.09375rem;border-radius:.9375rem;padding:.9375rem;margin-bottom:.5rem}#chatCon[dir=ltr] .msg-area,#chatCon[dir=ltr] .msg-area .left{text-align:left}#chatCon[dir=ltr] .msg-area .right{text-align:right}#chatCon[dir=ltr] .msg-area .left span{background-color:#ddd}#chatCon[dir=ltr] .msg-area .right span{background-color:#257cb5;color:#fff;text-align:left}#chatCon[dir=ltr] .chat-box .footer{flex-direction:row}#chatCon[dir=ltr] .footer input{text-align:left}#chatCon[dir=rtl] .msg-area,#chatCon[dir=rtl] .msg-area .left{text-align:right}#chatCon[dir=rtl] .msg-area .right{text-align:left}#chatCon[dir=rtl] .msg-area .left span{background-color:#ddd}#chatCon[dir=rtl] .msg-area .right span{background-color:#257cb5;color:#fff;text-align:right}#chatCon[dir=rtl] .chat-box .footer{flex-direction:row}#chatCon[dir=rtl] .footer input{text-align:right}#chatCon .chat-box .footer{padding:1rem;display:flex;justify-content:space-between;align-items:center;background:#fff;justify-content:space-around}#chatCon .footer input{border:1px solid #ccc;padding:.625rem;width:80%;border-radius:.9375rem}#chatCon .footer input:focus{outline:none}#chatCon .footer button{border:none;font-size:1.5rem;color:#257cb5;cursor:pointer;background:none}#chatCon .pop{display:flex;justify-content:center;align-items:center}#chatCon.open .pop{display:none}#chatCon .pop img{border-radius:50%;width:60px;height:60px;cursor:pointer}body.chat-open{overflow:hidden}}\\n\")","export async function sendMessageToAI(\n messages: { role: 'user' | 'assistant'; content: string }[],\n contextData?: any,\n businessInfo?: string,\n aiCharacter?: string,\n) {\n const response = await fetch(\n 'https://ai-crw-server-861817b40609.herokuapp.com/api/chat',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n messages,\n contextData,\n businessInfo,\n aiCharacter,\n }),\n },\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch AI response');\n }\n\n const data = await response.json();\n return data.reply;\n}\n"],"mappings":"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","names":["index_exports","__export","PopChat","index_default","__toCommonJS","import_react","styleInject","css","insertAt","head","style","styleInject","import_all_min","import_framer_motion","sendMessageToAI","messages","contextData","businessInfo","aiCharacter","response","import_jsx_runtime","PopChat","contextData","businessInfo","aiCharacter","chatHeadline","direction","messages","setMessages","chatOpen","setChatOpen","textRef","msgAreaRef","isMobile","setIsMobile","handleSend","newMessage","userMessage","prev","assistantMessage","sendMessageToAI","error","handleKeyDown","e","handleResize","msg","i","index_default"]}