import * as React from 'react'; import { ChatMessage, AIAssistantSettings } from '../types'; import { sendChatMessage, createAIChatSession } from '../services/geminiService'; import { Icon } from './ui/Icon'; import { Tabs } from './ui/Tabs'; import { useTranslation } from '../hooks/useI18n'; const ChatWindow: React.FC = () => { const { t } = useTranslation(); const [messages, setMessages] = React.useState([]); const [userInput, setUserInput] = React.useState(''); const [isLoading, setIsLoading] = React.useState(false); const messagesEndRef = React.useRef(null); React.useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, isLoading]); const handleSendMessage = React.useCallback(async () => { if (!userInput.trim()) return; const userMessage: ChatMessage = { id: `msg-user-${Date.now()}`, sender: 'user', text: userInput }; setMessages(prev => [...prev, userMessage]); const messageToSend = userInput; setUserInput(''); setIsLoading(true); const aiResponse = await sendChatMessage(messageToSend); setMessages(prev => [...prev, aiResponse]); setIsLoading(false); }, [userInput]); return (

{t('ai_assistant.test_title')}

{messages.map(msg => (
{msg.sender === 'ai' && (
)}

{msg.text}

{msg.sender === 'user' && (
)}
))} {isLoading && (
)}
setUserInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && !isLoading && handleSendMessage()} placeholder="Type your message..." className="flex-1 bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white p-2 rounded-md border-2 border-gray-300 dark:border-gray-600 focus:outline-none focus:border-brand-primary focus:ring-1 focus:ring-brand-primary/50" disabled={isLoading} />
); }; const sectionClasses = "bg-white dark:bg-gray-800/50 p-6 rounded-lg border border-gray-200 dark:border-gray-700/50"; const labelClasses = "block text-lg font-semibold text-gray-900 dark:text-white"; const descriptionClasses = "text-sm text-gray-500 dark:text-gray-400 mt-1 mb-4"; const inputBaseClasses = "w-full text-gray-900 dark:text-white p-3 rounded-md border focus:outline-none focus:border-brand-primary focus:ring-0 transition-colors"; const inputBgClasses = "bg-gray-100/50 dark:bg-black/20 border-gray-300 dark:border-white/10"; const tagColors = [ { bg: 'bg-red-500', text: 'text-white', closeButton: 'text-red-100 hover:text-white' }, { bg: 'bg-blue-600', text: 'text-white', closeButton: 'text-blue-100 hover:text-white' }, { bg: 'bg-green-600', text: 'text-white', closeButton: 'text-green-100 hover:text-white' }, { bg: 'bg-yellow-500', text: 'text-black', closeButton: 'text-yellow-800 hover:text-black' }, { bg: 'bg-purple-600', text: 'text-white', closeButton: 'text-purple-100 hover:text-white' }, { bg: 'bg-indigo-600', text: 'text-white', closeButton: 'text-indigo-100 hover:text-white' }, { bg: 'bg-pink-600', text: 'text-white', closeButton: 'text-pink-100 hover:text-white' }, { bg: 'bg-teal-500', text: 'text-white', closeButton: 'text-teal-100 hover:text-white' }, ]; const getColorForKeyword = (keyword: string) => { let hash = 0; for (let i = 0; i < keyword.length; i++) { hash = keyword.charCodeAt(i) + ((hash << 5) - hash); hash = hash & hash; } const index = Math.abs(hash % tagColors.length); return tagColors[index]; }; const KeywordInput: React.FC<{ label: string; description: string; keywords: string[]; onAdd: (keyword: string) => void; onRemove: (keyword: string) => void; }> = ({ label, description, keywords, onAdd, onRemove }) => { const { t } = useTranslation(); const [newKeyword, setNewKeyword] = React.useState(''); const handleAdd = () => { if (newKeyword.trim() && !keywords.includes(newKeyword.trim())) { onAdd(newKeyword.trim()); setNewKeyword(''); } }; return (

{description}

setNewKeyword(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleAdd()} className={`${inputBaseClasses} ${inputBgClasses}`} placeholder={`${t('add')} a keyword...`} />
{keywords.map(kw => { const color = getColorForKeyword(kw); return (
{kw}
); })}
); }; const KnowledgeBaseModal: React.FC<{ onClose: () => void; onAddFiles: (files: File[]) => void; onAddUrl: (url: string) => void; }> = ({ onClose, onAddFiles, onAddUrl }) => { const { t } = useTranslation(); const [activeTab, setActiveTab] = React.useState<'files' | 'web'>('files'); const [newUrl, setNewUrl] = React.useState(''); const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files) { onAddFiles(Array.from(e.target.files)); onClose(); } }; const handleAddUrl = () => { if (newUrl.trim()) { try { new URL(newUrl.trim()); onAddUrl(newUrl.trim()); onClose(); } catch (_) { alert('Please enter a valid URL.'); } } }; return (
e.stopPropagation()}>

{t('ai_assistant.add_knowledge')}

setActiveTab(t as 'files' | 'web')} />
{activeTab === 'files' ? (

or drag and drop

PDF, TXT, DOCX up to 10MB

) : (
setNewUrl(e.target.value)} onKeyPress={e => e.key === 'Enter' && handleAddUrl()} className={`${inputBaseClasses} ${inputBgClasses}`} placeholder="https://example.com/about" />
)}
); }; interface EditorProps { settings: AIAssistantSettings; updateSettings: (updates: Partial) => void; } const PersonaEditor: React.FC = ({ settings, updateSettings }) => { const { t } = useTranslation(); return (

{t('ai_assistant.system_instruction_desc')}