diff --git a/src/pages/TaskAssistant.tsx b/src/pages/TaskAssistant.tsx index dd31794..f71f7dd 100644 --- a/src/pages/TaskAssistant.tsx +++ b/src/pages/TaskAssistant.tsx @@ -38,7 +38,6 @@ function ChatPage() { const loadConversations = useCallback(async () => { try { setConversations(await listConversations()) } catch {} }, []) useEffect(() => { loadConversations() }, [loadConversations]) useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages]) - useEffect(() => { if (!activeId && conversations.length > 0) switchConversation(conversations[0].id) }, [conversations]) const switchConversation = useCallback(async (id: string) => { @@ -54,7 +53,11 @@ function ChatPage() { const handleDelete = (id: string) => modal.confirm({ title: '删除对话', okText: '删除', okType: 'danger', cancelText: '取消', - onOk: async () => { setDeleting(true); try { await deleteConversation(id); setConversations(prev => prev.filter(c => c.id !== id)); if (activeId === id) { setActiveId(null); setMessages([]) } } catch {}; setDeleting(false) }, + onOk: async () => { + setDeleting(true) + try { await deleteConversation(id); setConversations(prev => prev.filter(c => c.id !== id)); if (activeId === id) { setActiveId(null); setMessages([]) } } catch {} + setDeleting(false) + }, }) const startEdit = (conv: Conversation) => { setEditingId(conv.id); setEditTitle(conv.title); setTimeout(() => editInputRef.current?.focus(), 50) } @@ -80,18 +83,43 @@ function ChatPage() { try { await streamChat(prevMessages.map(m => ({ role: m.role, content: m.content })), activeId, (event: StreamEvent) => { switch (event.event) { - case 'meta': completedConvId = event.conversationId; if (event.conversationId && event.conversationId !== activeId) { setActiveId(event.conversationId); loadConversations() } break - case 'tool.started': currentTools.push({ tool: event.tool, preview: event.preview, done: false }); update({ toolCalls: [...currentTools] }) break - case 'tool.completed': { const i = currentTools.findIndex((t: any) => t.tool === event.tool && !t.done); if (i >= 0) { currentTools[i] = { ...currentTools[i], done: true, duration: event.duration, error: event.error }; update({ toolCalls: [...currentTools] }) } } break - case 'message.delta': currentContent += event.delta || ''; update({ content: currentContent, streaming: true }) break - case 'run.completed': if (event.output) currentContent = event.output; update({ content: currentContent, streaming: false }) break - case 'done': completedConvId = event.conversationId || completedConvId; update({ streaming: false }) break - case 'error': update({ content: `❌ ${event.error}`, streaming: false }) break + case 'meta': + completedConvId = event.conversationId + if (event.conversationId && event.conversationId !== activeId) { setActiveId(event.conversationId); loadConversations() } + break + case 'tool.started': + currentTools.push({ tool: event.tool, preview: event.preview, done: false }) + update({ toolCalls: [...currentTools] }) + break + case 'tool.completed': { + const i = currentTools.findIndex((t: any) => t.tool === event.tool && !t.done) + if (i >= 0) { + currentTools[i] = { ...currentTools[i], done: true, duration: event.duration, error: event.error } + update({ toolCalls: [...currentTools] }) + } + break + } + case 'message.delta': + currentContent += event.delta || '' + update({ content: currentContent, streaming: true }) + break + case 'run.completed': + if (event.output) currentContent = event.output + update({ content: currentContent, streaming: false }) + break + case 'done': + completedConvId = event.conversationId || completedConvId + update({ streaming: false }) + break + case 'error': + update({ content: 'Error: ' + event.error, streaming: false }) + break } }, controller.signal) if (completedConvId) loadConversations() } catch (err: any) { - if (err.name !== 'AbortError') update({ content: `❌ ${err.message}`, streaming: false }); else update({ streaming: false }) + if (err.name !== 'AbortError') update({ content: 'Error: ' + err.message, streaming: false }) + else update({ streaming: false }) } finally { setStreaming(false); abortRef.current = null } } @@ -100,8 +128,8 @@ function ChatPage() { return (
{/* Sidebar */} -
-
+
+
@@ -126,7 +154,6 @@ function ChatPage() { {/* Chat */}
- {/* Messages */}
{messages.length === 0 ? (
@@ -140,7 +167,6 @@ function ChatPage() {
{messages.map(msg => (
- {/* Role label */}
@@ -150,30 +176,27 @@ function ChatPage() { {msg.streaming && 执行中...}
- {/* Tool calls */} {msg.toolCalls && msg.toolCalls.length > 0 && (
{msg.toolCalls.map((t, i) => (
{t.preview || t.tool} {t.done - ? {t.error ? '失败' : `${t.duration?.toFixed(1)}s`} + ? {t.error ? '失败' : (t.duration ? t.duration.toFixed(1) + 's' : '')} : 执行中}
))}
)} - {/* Content */}
+ border: msg.role === 'assistant' ? '1px solid ' + token.colorBorderSecondary : 'none' }}> {msg.role === 'assistant' ? (msg.content ? : 思考中...) : msg.content} @@ -185,10 +208,9 @@ function ChatPage() { )}
- {/* Input */} -
+
-
+
setInput(e.target.value)} onCompositionStart={() => { composingRef.current = true }} onCompositionEnd={() => { composingRef.current = false }} onKeyDown={e => { if (composingRef.current) return; if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend() } }}