import React, { useState, useEffect, useRef } from 'react'; import { MessageSquare, Activity, Terminal, ChevronRight, Plus } from 'lucide-react'; import apiClient from '../../api/client'; import type { ChatSession, Message } from '../../App'; interface ChatPanelProps { chatSessions: ChatSession[]; setChatSessions: React.Dispatch>; activeSessionId: string | null; setActiveSessionId: React.Dispatch>; } export function ChatPanel({ chatSessions, setChatSessions, activeSessionId, setActiveSessionId }: ChatPanelProps) { const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [mode, setMode] = useState<'chat' | 'deploy'>('chat'); const fileInputRef = useRef(null); const messagesEndRef = useRef(null); const activeSession = chatSessions.find((s) => s.id === activeSessionId) || null; const messages = activeSession ? activeSession.messages : []; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; useEffect(() => { scrollToBottom(); }, [messages]); const updateSessionMessages = (newMessages: Message[]) => { if (!activeSessionId) return; setChatSessions((prev) => prev.map((s) => s.id === activeSessionId ? { ...s, messages: newMessages, updatedAt: Date.now() } : s ) ); }; const handleFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file || !activeSessionId) return; const formData = new FormData(); formData.append('file', file); setLoading(true); try { const response = await apiClient.post('/api/v1/adapter/client/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); const aiMessage: Message = { id: Date.now().toString(), role: 'assistant', content: `已上传文件: ${response.data.filename}`, timestamp: Date.now(), }; updateSessionMessages([...messages, aiMessage]); } catch (error) { console.error('Error uploading file', error); const errorMessage: Message = { id: Date.now().toString(), role: 'assistant', content: '文件上传失败。', timestamp: Date.now(), }; updateSessionMessages([...messages, errorMessage]); } finally { setLoading(false); if (fileInputRef.current) { fileInputRef.current.value = ''; } } }; const handleSendMessage = async () => { if (!input.trim() || !activeSessionId) return; const userText = input; const userMessage: Message = { id: Date.now().toString(), role: 'user', content: userText, timestamp: Date.now(), }; updateSessionMessages([...messages, userMessage]); setInput(''); setLoading(true); try { const promptModifier = mode === 'deploy' ? '[DEPLOY TASK] ' : ''; const response = await apiClient.post('/api/v1/adapter/client', { message: promptModifier + userMessage.content, }); const responseData = response.data.message; let aiContent = responseData || 'I received your message.'; // Auto-update title if it's the first user message if (messages.length <= 1 && userText.length > 0) { setChatSessions((prev) => prev.map((s) => s.id === activeSessionId ? { ...s, title: userText.slice(0, 20) + (userText.length > 20 ? '...' : '') } : s ) ); } const aiMessage: Message = { id: (Date.now() + 1).toString(), role: 'assistant', content: aiContent, timestamp: Date.now(), }; updateSessionMessages([...messages, userMessage, aiMessage]); } catch (error) { console.error('Error sending message', error); const errorMessage: Message = { id: (Date.now() + 1).toString(), role: 'assistant', content: 'Sorry, I encountered an error communicating with the server.', timestamp: Date.now(), }; updateSessionMessages([...messages, userMessage, errorMessage]); } finally { setLoading(false); } }; if (!activeSessionId) { return (

Pretor Assistant

Select a chat history or create a new one to start.

); } // Notice we removed the outer padded div, since App.tsx is handling that layout now return (

{activeSession?.title || 'Chat'}

{/* Chat History */}
{messages.map((msg) => (
{msg.role === 'assistant' && (
)}

{msg.content}

{typeof msg.content === 'string' && msg.content.includes('-') && msg.role === 'assistant' && (msg.content.length === 36 || msg.content.includes('任务已创建')) && (
Task ID: {msg.content.substring(0, 36)}
)}
))} {loading && (
)}
{/* Chat Input */}
setInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSendMessage()} placeholder="Ask Pretor to do something..." className="w-full bg-slate-50 border border-slate-200 text-sm rounded-2xl pl-12 pr-12 py-3.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-400 transition-all" />
); }