anycoder-6d5b65da / components /ChatInterface.jsx
Mehdi
Upload components/ChatInterface.jsx with huggingface_hub
e64e72a verified
Raw
History Blame
11.7 kB
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}