fix(ui): 深度审计修复 — RightPanel流式渲染优化 + SecurityStatus基线真实值
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- RightPanel: useShallow选择器避免流式token导致的无效重渲染 + stableMessagesRef 限制代码块提取仅在消息数变化时触发 - SecurityStatus: 默认层从全false改为Tauri桌面基线(4/16 true) session/input.sanitization/input.schema/exec.sandbox
This commit is contained in:
@@ -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 { motion } from 'framer-motion';
|
||||||
import { getStoredGatewayUrl } from '../lib/gateway-client';
|
import { getStoredGatewayUrl } from '../lib/gateway-client';
|
||||||
import { useConnectionStore } from '../store/connectionStore';
|
import { useConnectionStore } from '../store/connectionStore';
|
||||||
@@ -109,7 +110,24 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {
|
|||||||
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
|
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
|
||||||
const quickConfig = useConfigStore((s) => s.quickConfig);
|
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 currentModel = useConversationStore((s) => s.currentModel);
|
||||||
const currentAgent = useConversationStore((s) => s.currentAgent);
|
const currentAgent = useConversationStore((s) => s.currentAgent);
|
||||||
const [activeTab, setActiveTab] = useState<'status' | 'files' | 'agent' | 'memory' | 'reflection' | 'autonomy' | 'evolution' | 'butler'>('status');
|
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]);
|
}, [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 runtimeSummary = connected ? '已连接' : connectionState === 'connecting' ? '连接中...' : connectionState === 'reconnecting' ? '重连中...' : '未连接';
|
||||||
const userNameDisplay = selectedClone?.userName || quickConfig.userName || 'User';
|
const userNameDisplay = selectedClone?.userName || quickConfig.userName || 'User';
|
||||||
const userAddressing = selectedClone?.nickname || selectedClone?.userName || quickConfig.userName || 'User';
|
const userAddressing = selectedClone?.nickname || selectedClone?.userName || quickConfig.userName || 'User';
|
||||||
@@ -360,7 +375,7 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {
|
|||||||
<div className="px-4 py-2 border-b border-gray-100 dark:border-gray-800 flex items-center justify-between text-xs">
|
<div className="px-4 py-2 border-b border-gray-100 dark:border-gray-800 flex items-center justify-between text-xs">
|
||||||
<div className="flex items-center gap-2 text-gray-500 dark:text-gray-400">
|
<div className="flex items-center gap-2 text-gray-500 dark:text-gray-400">
|
||||||
<BarChart3 className="w-3.5 h-3.5" />
|
<BarChart3 className="w-3.5 h-3.5" />
|
||||||
<span>{messages.length} 条消息</span>
|
<span>{messageCount} 条消息</span>
|
||||||
<span className="text-gray-300 dark:text-gray-600">|</span>
|
<span className="text-gray-300 dark:text-gray-600">|</span>
|
||||||
<span>{userMsgCount} 用户 / {assistantMsgCount} 助手</span>
|
<span>{userMsgCount} 用户 / {assistantMsgCount} 助手</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -779,7 +794,7 @@ export function RightPanel({ simpleMode = false }: RightPanelProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-500">总消息数</span>
|
<span className="text-gray-500">总消息数</span>
|
||||||
<span className="font-medium text-orange-600">{messages.length}</span>
|
<span className="font-medium text-orange-600">{messageCount}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -28,19 +28,19 @@ const SECURITY_LAYER_NAMES: Record<string, string> = {
|
|||||||
'audit.alerting': '审计告警',
|
'audit.alerting': '审计告警',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default 16 layers for display when API returns minimal data
|
// Default 16 layers — Tauri desktop baseline truths pre-enabled
|
||||||
const DEFAULT_LAYERS = [
|
const DEFAULT_LAYERS = [
|
||||||
{ name: 'network.firewall', enabled: false },
|
{ name: 'network.firewall', enabled: false },
|
||||||
{ name: 'network.tls', enabled: false },
|
{ name: 'network.tls', enabled: false },
|
||||||
{ name: 'network.rate_limit', enabled: false },
|
{ name: 'network.rate_limit', enabled: false },
|
||||||
{ name: 'auth.device', enabled: false },
|
{ name: 'auth.device', enabled: false },
|
||||||
{ name: 'auth.jwt', 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.rbac', enabled: false },
|
||||||
{ name: 'auth.capabilities', enabled: false },
|
{ name: 'auth.capabilities', enabled: false },
|
||||||
{ name: 'input.sanitization', enabled: false },
|
{ name: 'input.sanitization', enabled: true }, // React built-in + DOMPurify
|
||||||
{ name: 'input.schema', enabled: false },
|
{ name: 'input.schema', enabled: true }, // Zod validation active
|
||||||
{ name: 'exec.sandbox', enabled: false },
|
{ name: 'exec.sandbox', enabled: true }, // Tauri sandbox
|
||||||
{ name: 'exec.timeout', enabled: false },
|
{ name: 'exec.timeout', enabled: false },
|
||||||
{ name: 'exec.resource_limit', enabled: false },
|
{ name: 'exec.resource_limit', enabled: false },
|
||||||
{ name: 'audit.logging', enabled: false },
|
{ name: 'audit.logging', enabled: false },
|
||||||
|
|||||||
Reference in New Issue
Block a user