import React, { useState, useEffect, useRef, useCallback } from 'react'; import { MessageSquare, Activity, ChevronRight, Plus } from 'lucide-react'; import apiClient from '../../api/client'; import type { ChatSession, Message } from '../../App'; import type { ChatMessageDB } from '../../types'; interface ChatPanelProps { chatSessions: ChatSession[]; setChatSessions: React.Dispatch>; activeSessionId: string | null; setActiveSessionId: React.Dispatch>; onSessionsChanged?: () => void; } export function ChatPanel({ chatSessions, setChatSessions, activeSessionId, setActiveSessionId, onSessionsChanged }: ChatPanelProps) { const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [mode, setMode] = useState<'chat' | 'deploy'>('chat'); const [loadingMessages, setLoadingMessages] = useState(false); 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 loadMessages = useCallback(async (chatId: string) => { setLoadingMessages(true); try { const response = await apiClient.get(`/api/v1/chat/${chatId}`); const dbMessages: ChatMessageDB[] = response.data?.messages || []; const mapped: Message[] = dbMessages.map((m) => ({ id: m.message_id, role: m.message_owner === 'user' ? 'user' : 'assistant', content: m.message, timestamp: new Date(m.created_at).getTime(), })); setChatSessions((prev) => prev.map((s) => (s.id === chatId ? { ...s, messages: mapped } : s)) ); } catch (error) { console.error('Failed to load messages', error); } finally { setLoadingMessages(false); } }, [setChatSessions]); useEffect(() => { if (activeSessionId) { const session = chatSessions.find((s) => s.id === activeSessionId); if (session && session.messages.length === 0) { loadMessages(activeSessionId); } } }, [activeSessionId]); const updateSessionMessages = (newMessages: Message[]) => { if (!activeSessionId) return; setChatSessions((prev) => prev.map((s) => s.id === activeSessionId ? { ...s, messages: newMessages, updatedAt: Date.now() } : s ) ); }; const handleNewChat = async () => { if (!onSessionsChanged) return; try { const response = await apiClient.post('/api/v1/chat', { title: '新对话', initial_message: '你好', }); const chatId: string = response.data.chat_id; const reply: string = response.data.reply || '你好!我是 kilostar 助手,有什么可以帮你的吗?'; const newSession: ChatSession = { id: chatId, title: '新对话', messages: [ { id: chatId + '_user', role: 'user', content: '你好', timestamp: Date.now() }, { id: chatId + '_ai', role: 'assistant', content: reply, timestamp: Date.now() }, ], updatedAt: Date.now(), }; setChatSessions((prev) => [newSession, ...prev]); setActiveSessionId(chatId); onSessionsChanged(); } catch (error) { console.error('Failed to create chat session', error); } }; 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(), }; const currentMessages = activeSession?.messages || []; updateSessionMessages([...currentMessages, userMessage]); setInput(''); setLoading(true); try { const response = await apiClient.post(`/api/v1/chat/${activeSessionId}/reply`, { message: userText, }); const replyContent: string = response.data?.reply || '收到你的消息。'; if (currentMessages.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: replyContent, timestamp: Date.now(), }; const updatedMessages = [...(currentMessages.length > 0 ? currentMessages : []), userMessage, aiMessage]; updateSessionMessages(updatedMessages); } catch (error) { console.error('Error sending message', error); const errorMessage: Message = { id: (Date.now() + 1).toString(), role: 'assistant', content: '抱歉,与服务器通信时出错。', timestamp: Date.now(), }; updateSessionMessages([...currentMessages, userMessage, errorMessage]); } finally { setLoading(false); } }; if (!activeSessionId) { return (

kilostar Assistant

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

); } return (

{activeSession?.title || 'Chat'}

{loadingMessages ? (
) : ( messages.map((msg) => (
{msg.role === 'assistant' && (
)}

{msg.content}

)) )} {loading && (
)}
setInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSendMessage()} placeholder="Ask kilostar 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" />
); }