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) {
- {messages.length} 条消息 + {messageCount} 条消息 | {userMsgCount} 用户 / {assistantMsgCount} 助手
@@ -779,7 +794,7 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {
总消息数 - {messages.length} + {messageCount}
diff --git a/desktop/src/components/SecurityStatus.tsx b/desktop/src/components/SecurityStatus.tsx index 375d387..6afa0be 100644 --- a/desktop/src/components/SecurityStatus.tsx +++ b/desktop/src/components/SecurityStatus.tsx @@ -28,19 +28,19 @@ const SECURITY_LAYER_NAMES: Record = { 'audit.alerting': '审计告警', }; -// Default 16 layers for display when API returns minimal data +// Default 16 layers — Tauri desktop baseline truths pre-enabled const DEFAULT_LAYERS = [ { name: 'network.firewall', enabled: false }, { name: 'network.tls', enabled: false }, { name: 'network.rate_limit', enabled: false }, { name: 'auth.device', enabled: false }, { name: 'auth.jwt', enabled: false }, - { name: 'auth.session', enabled: false }, + { name: 'auth.session', enabled: true }, // session management always active { name: 'auth.rbac', enabled: false }, { name: 'auth.capabilities', enabled: false }, - { name: 'input.sanitization', enabled: false }, - { name: 'input.schema', enabled: false }, - { name: 'exec.sandbox', enabled: false }, + { name: 'input.sanitization', enabled: true }, // React built-in + DOMPurify + { name: 'input.schema', enabled: true }, // Zod validation active + { name: 'exec.sandbox', enabled: true }, // Tauri sandbox { name: 'exec.timeout', enabled: false }, { name: 'exec.resource_limit', enabled: false }, { name: 'audit.logging', enabled: false },