diff --git a/desktop/src/components/RightPanel.tsx b/desktop/src/components/RightPanel.tsx index 467620d..d753779 100644 --- a/desktop/src/components/RightPanel.tsx +++ b/desktop/src/components/RightPanel.tsx @@ -1,4 +1,5 @@ -import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; +import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useShallow } from 'zustand/react/shallow'; import { motion } from 'framer-motion'; import { getStoredGatewayUrl } from '../lib/gateway-client'; import { useConnectionStore } from '../store/connectionStore'; @@ -109,7 +110,24 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) { const workspaceInfo = useConfigStore((s) => s.workspaceInfo); const quickConfig = useConfigStore((s) => s.quickConfig); - const { messages, setCurrentAgent } = useChatStore(); + // Use shallow selector for message stats to avoid re-rendering during streaming. + // Counts only change when messages are added/removed, not when content is appended. + const setCurrentAgent = useChatStore((s) => s.setCurrentAgent); + const { messageCount, userMsgCount, assistantMsgCount, toolCallCount } = useChatStore( + useShallow((s) => ({ + messageCount: s.messages.length, + userMsgCount: s.messages.filter(m => m.role === 'user').length, + assistantMsgCount: s.messages.filter(m => m.role === 'assistant').length, + toolCallCount: s.messages.filter(m => m.role === 'tool').length, + })) + ); + // Stable messages ref for code block extraction — only updates when length changes + const rawMessages = useChatStore((s) => s.messages); + const stableMessagesRef = useRef(rawMessages); + if (rawMessages.length !== stableMessagesRef.current.length) { + stableMessagesRef.current = rawMessages; + } + const messages = stableMessagesRef.current; const currentModel = useConversationStore((s) => s.currentModel); const currentAgent = useConversationStore((s) => s.currentAgent); const [activeTab, setActiveTab] = useState<'status' | 'files' | 'agent' | 'memory' | 'reflection' | 'autonomy' | 'evolution' | 'butler'>('status'); @@ -226,9 +244,6 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) { } }, [activeTab, currentAgent?.id, loadSnapshots]); - const userMsgCount = messages.filter(m => m.role === 'user').length; - const assistantMsgCount = messages.filter(m => m.role === 'assistant').length; - const toolCallCount = messages.filter(m => m.role === 'tool').length; const runtimeSummary = connected ? '已连接' : connectionState === 'connecting' ? '连接中...' : connectionState === 'reconnecting' ? '重连中...' : '未连接'; const userNameDisplay = selectedClone?.userName || quickConfig.userName || 'User'; const userAddressing = selectedClone?.nickname || selectedClone?.userName || quickConfig.userName || 'User'; @@ -360,7 +375,7 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {