Spaces:
Build error
Build error
| import { useState, useRef, useEffect } from 'react' | |
| import { motion, AnimatePresence } from 'framer-motion' | |
| import { FaRobot, FaUser, FaPaperPlane, FaMicrophone, FaStop, FaCopy, FaThumbsUp, FaThumbsDown, FaRedo, FaLanguage, FaBrain, FaGlobe, FaCheckCircle, FaExclamationTriangle } from 'react-icons/fa' | |
| import ReactMarkdown from 'react-markdown' | |
| import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' | |
| import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism' | |
| import toast from 'react-hot-toast' | |
| export default function ChatInterface({ language, theme }) { | |
| const [messages, setMessages] = useState([ | |
| { | |
| id: 1, | |
| type: 'assistant', | |
| content: language === 'fa' ? | |
| 'سلام! من GhadirSync-AI هستم، دستیار هوشمند شما. چطور میتوانم به شما کمک کنم؟' : | |
| 'Hello! I am GhadirSync-AI, your intelligent assistant. How can I help you?', | |
| timestamp: new Date(), | |
| sources: [], | |
| confidence: 0.95 | |
| } | |
| ]) | |
| const [input, setInput] = useState('') | |
| const [isTyping, setIsTyping] = useState(false) | |
| const [isListening, setIsListening] = useState(false) | |
| const [selectedModel, setSelectedModel] = useState('local') | |
| const [useInternet, setUseInternet] = useState(false) | |
| const messagesEndRef = useRef(null) | |
| const inputRef = useRef(null) | |
| const models = [ | |
| { id: 'local', name: language === 'fa' ? 'مدل محلی' : 'Local Model', icon: FaBrain, color: 'text-green-400' }, | |
| { id: 'gpt4', name: 'GPT-4', icon: FaGlobe, color: 'text-blue-400' }, | |
| { id: 'claude', name: 'Claude', icon: FaBrain, color: 'text-purple-400' }, | |
| ] | |
| const scrollToBottom = () => { | |
| messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) | |
| } | |
| useEffect(() => { | |
| scrollToBottom() | |
| }, [messages]) | |
| const handleSend = async () => { | |
| if (!input.trim()) return | |
| const userMessage = { | |
| id: Date.now(), | |
| type: 'user', | |
| content: input, | |
| timestamp: new Date(), | |
| } | |
| setMessages(prev => [...prev, userMessage]) | |
| setInput('') | |
| setIsTyping(true) | |
| // Simulate AI response | |
| setTimeout(() => { | |
| const aiResponse = { | |
| id: Date.now() + 1, | |
| type: 'assistant', | |
| content: generateAIResponse(input), | |
| timestamp: new Date(), | |
| sources: useInternet ? [ | |
| { title: 'Wikipedia', url: 'https://wikipedia.org' }, | |
| { title: 'Stack Overflow', url: 'https://stackoverflow.com' } | |
| ] : [], | |
| confidence: Math.random() * 0.3 + 0.7, | |
| model: selectedModel | |
| } | |
| setMessages(prev => [...prev, aiResponse]) | |
| setIsTyping(false) | |
| }, 1500) | |
| } | |
| const generateAIResponse = (userInput) => { | |
| const responses = { | |
| fa: [ | |
| `برای سوال "${userInput}"، من تحلیل کاملی ارائه میدهم:\n\n## تحلیل اصلی\n\n1. **نکته اول**: این یک موضوع مهم است\n2. **نکته دوم**: نیاز به بررسی بیشتر دارد\n3. **نکته سوم**: پیشنهادات عملی\n\n\`\`\`javascript\n// نمونه کد\nfunction example() {\n return "Hello World";\n}\n\`\`\`\n\n**نتیجهگیری**: بر اساس تحلیل، بهترین راهکار...`, | |
| `در پاسخ به "${userInput}":\n\n### اطلاعات کلیدی\n\n- 🎯 هدف اصلی\n- 📊 تحلیل دادهها\n- 💡 راهکارهای نوآورانه\n\n## پیشنهادات\n\n1. راهکار اول\n2. راهکار دوم\n3. راهکار سوم\n\n**توجه**: این اطلاعات بر اساس بهترین منابع موجود است.`, | |
| ], | |
| en: [ | |
| `For your question "${userInput}", here's my comprehensive analysis:\n\n## Main Analysis\n\n1. **First Point**: This is an important topic\n2. **Second Point**: Requires further investigation\n3. **Third Point**: Practical suggestions\n\n\`\`\`python\n# Sample code\ndef example():\n return "Hello World"\n\`\`\`\n\n**Conclusion**: Based on the analysis, the best solution is...`, | |
| `In response to "${userInput}":\n\n### Key Information\n\n- 🎯 Main objective\n- 📊 Data analysis\n- 💡 Innovative solutions\n\n## Recommendations\n\n1. First solution\n2. Second solution\n3. Third solution\n\n**Note**: This information is based on the best available sources.`, | |
| ] | |
| } | |
| const langResponses = responses[language] || responses.en | |
| return langResponses[Math.floor(Math.random() * langResponses.length)] | |
| } | |
| const handleVoiceInput = () => { | |
| if (isListening) { | |
| setIsListening(false) | |
| toast.success(language === 'fa' ? 'ضبط صدا متوقف شد' : 'Voice recording stopped') | |
| } else { | |
| setIsListening(true) | |
| toast.success(language === 'fa' ? 'در حال ضبط صدا...' : 'Recording voice...') | |
| // Simulate voice recognition | |
| setTimeout(() => { | |
| setInput(language === 'fa' ? 'این یک متن نمونه از تشخیص صدا است' : 'This is a sample text from voice recognition') | |
| setIsListening(false) | |
| }, 3000) | |
| } | |
| } | |
| const handleCopyMessage = (content) => { | |
| navigator.clipboard.writeText(content) | |
| toast.success(language === 'fa' ? 'کپی شد!' : 'Copied!') | |
| } | |
| const handleRegenerateResponse = (messageId) => { | |
| setIsTyping(true) | |
| setTimeout(() => { | |
| setMessages(prev => prev.map(msg => | |
| msg.id === messageId | |
| ? { | |
| ...msg, | |
| content: generateAIResponse(' regenerated response'), | |
| timestamp: new Date() | |
| } | |
| : msg | |
| )) | |
| setIsTyping(false) | |
| }, 1500) | |
| } | |
| return ( | |
| <div className="flex flex-col h-full space-y-4"> | |
| {/* Model Selection */} | |
| <div className="flex items-center justify-between p-4 bg-gray-800 rounded-lg"> | |
| <div className="flex items-center space-x-reverse space-x-4"> | |
| <span className="text-sm font-medium"> | |
| {language === 'fa' ? 'مدل هوش مصنوعی:' : 'AI Model:'} | |
| </span> | |
| <div className="flex space-x-reverse space-x-2"> | |
| {models.map(model => { | |
| const Icon = model.icon | |
| return ( | |
| <button | |
| key={model.id} | |
| onClick={() => setSelectedModel(model.id)} | |
| className={`flex items-center space-x-reverse space-x-2 px-3 py-2 rounded-lg transition-all ${ | |
| selectedModel === model.id | |
| ? 'bg-primary-600 text-white' | |
| : 'bg-gray-700 text-gray-300 hover:bg-gray-600' | |
| }`} | |
| > | |
| <Icon className={`w-4 h-4 ${model.color}`} /> | |
| <span className="text-sm">{model.name}</span> | |
| </button> | |
| ) | |
| })} | |
| </div> | |
| </div> | |
| <button | |
| onClick={() => setUseInternet(!useInternet)} | |
| className={`flex items-center space-x-reverse space-x-2 px-3 py-2 rounded-lg transition-all ${ | |
| useInternet | |
| ? 'bg-green-600 text-white' | |
| : 'bg-gray-700 text-gray-300 hover:bg-gray-600' | |
| }`} | |
| > | |
| <FaGlobe className="w-4 h-4" /> | |
| <span className="text-sm"> | |
| {language === 'fa' ? 'اینترنت' : 'Internet'} | |
| </span> | |
| </button> | |
| </div> | |
| {/* Messages */} | |
| <div className="flex-1 overflow-y-auto space-y-4 p-4 bg-gray-800 rounded-lg"> | |
| <AnimatePresence> | |
| {messages.map(message => ( | |
| <motion.div | |
| key={message.id} | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| exit={{ opacity: 0, y: -20 }} | |
| className={`flex ${message.type === 'user' ? 'justify-start' : 'justify-end'}`} | |
| > | |
| <div className={`flex space-x-reverse space-x-3 max-w-3xl ${ | |
| message.type === 'user' ? 'flex-row' : 'flex-row-reverse' | |
| }`}> | |
| <div className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${ | |
| message.type === 'user' ? 'bg-primary-500' : 'bg-secondary-500' | |
| }`}> | |
| {message.type === 'user' ? ( | |
| <FaUser className="w-5 h-5 text-white" /> | |
| ) : ( | |
| <FaRobot className="w-5 h-5 text-white" /> | |
| )} | |
| </div> | |
| <div className={`flex-1 p-4 rounded-lg ${ | |
| message.type === 'user' | |
| ? 'bg-primary-600 text-white' | |
| : 'bg-gray-700 text-gray-100' | |
| }`}> | |
| <div className="flex items-start justify-between mb-2"> | |
| <span className="text-xs opacity-75"> | |
| {message.timestamp.toLocaleTimeString()} | |
| </span> | |
| {message.type === 'assistant' && ( | |
| <div className="flex items-center space-x-reverse space-x-2"> | |
| {message.confidence && ( | |
| <span className="text-xs opacity-75"> | |
| {Math.round(message.confidence * 100)}% | |
| </span> | |
| )} | |
| <button | |
| onClick={() => handleCopyMessage(message.content)} | |
| className="opacity-50 hover:opacity-100 transition-opacity" | |
| > | |
| <FaCopy className="w-3 h-3" /> | |
| </button> | |
| <button | |
| onClick={() => handleRegenerateResponse(message.id)} | |
| className="opacity-50 hover:opacity-100 transition-opacity" | |
| > | |
| <FaRedo className="w-3 h-3" /> | |
| </button> | |
| </div> | |
| )} | |
| </div> | |
| <div className="prose prose-sm max-w-none prose-invert"> | |
| <ReactMarkdown | |
| components={{ | |
| code({ node, inline, className, children, ...props }) { | |
| const match = /language-(\w+)/.exec(className || '') | |
| return !inline && match ? ( | |
| <SyntaxHighlighter | |
| style={vscDarkPlus} | |
| language={match[1]} | |
| PreTag="div" | |
| className="rounded-lg" | |
| {...props} | |
| > | |
| {String(children).replace(/\n$/, '')} | |
| </SyntaxHighlighter> | |
| ) : ( | |
| <code className={className} {...props}> | |
| {children} | |
| </code> | |
| ) | |
| } | |
| > | |
| {message.content} | |
| </ReactMarkdown> | |
| </div> | |
| {message.sources && message.sources.length > 0 && ( | |
| <div className="mt-4 pt-4 border-t border-gray-600"> | |
| <p className="text-xs font-medium mb-2 opacity-75"> | |
| {language === 'fa' ? 'منابع:' : 'Sources:'} | |
| </p> | |
| <div className="space-y-1"> | |
| {message.sources.map((source, index) => ( | |
| <a | |
| key={index} | |
| href={source.url} |