import { useState, useRef, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Badge } from '@/components/ui/badge'; import { Send, Sparkles, Plus, Search, Settings, MessageSquare, Copy, ThumbsUp, ThumbsDown, RotateCcw, Trash2, Loader2, } from 'lucide-react'; import { useConversations, useMessages, useCreateConversation, useSendMessage, useDeleteConversation, } from '@/hooks/useApi'; import type { Conversation, ChatMessage } from '@/api/client'; export function ChatPage() { const [selectedConv, setSelectedConv] = useState(null); const [input, setInput] = useState(''); const messagesEndRef = useRef(null); const [streamingContent, setStreamingContent] = useState(''); // API hooks const { data: conversations = [], isLoading: convsLoading } = useConversations(); const { data: messages = [], isLoading: msgsLoading } = useMessages(selectedConv); const createConv = useCreateConversation(); const sendMessage = useSendMessage(selectedConv || ''); const deleteConv = useDeleteConversation(); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; useEffect(() => { scrollToBottom(); }, [messages, streamingContent]); const handleNewChat = async () => { try { const conv = await createConv.mutateAsync({ title: '新对话' }); setSelectedConv(conv.id); } catch {} }; const handleSend = async () => { if (!input.trim() || !selectedConv) return; const content = input; setInput(''); setStreamingContent(''); try { // Try streaming first const stream = chatApi.streamMessage(selectedConv, { content }); let accumulated = ''; for await (const chunk of stream) { accumulated += chunk; setStreamingContent(accumulated); } setStreamingContent(''); // Refresh messages } catch { // Fallback to regular send await sendMessage.mutateAsync({ content }); } }; const handleDelete = async (id: string) => { try { await deleteConv.mutateAsync(id); if (selectedConv === id) setSelectedConv(null); } catch {} }; return (
{/* Conversation List */}
{convsLoading ? (
) : ( conversations.map((conv: Conversation) => (
)) )}
{/* Chat Area */}
{/* Messages */} {!selectedConv ? (

开始与 AI 对话吧

支持 Markdown 渲染和代码高亮,选择合适的模型开始聊天

) : msgsLoading ? (
) : (
{messages.map((msg: ChatMessage) => (
{msg.content}
{msg.role === 'assistant' && (
)}
))} {/* Streaming indicator */} {streamingContent && (
{streamingContent}
)}
)} {/* Input Area */}
setInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && !e.shiftKey && handleSend()} placeholder={selectedConv ? '输入消息... (Shift+Enter 换行)' : '请先选择或创建对话'} className="flex-1" disabled={!selectedConv || sendMessage.isPending} />
Auto(自动匹配)
); } // Import for streaming import { chatApi } from '@/api/client';