style: improve code formatting and consistency in AgentAudit.tsx

This commit is contained in:
lintsinghua 2025-12-12 16:21:35 +08:00
parent b392e049e8
commit e13218a33e
1 changed files with 176 additions and 124 deletions

View File

@ -118,7 +118,7 @@ function SplashScreen({ onComplete }: { onComplete: () => void }) {
<div className="h-screen bg-[#0a0a0f] flex items-center justify-center"> <div className="h-screen bg-[#0a0a0f] flex items-center justify-center">
<div className="text-center space-y-6"> <div className="text-center space-y-6">
<pre className="text-primary font-mono text-xs sm:text-sm leading-tight select-none"> <pre className="text-primary font-mono text-xs sm:text-sm leading-tight select-none">
{` {`
@ -568,11 +568,10 @@ function StatsPanel({ task, findings }: { task: AgentTask | null; findings: Agen
<div className="pt-2 border-t border-gray-800"> <div className="pt-2 border-t border-gray-800">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="text-xs text-gray-500">Security Score</span> <span className="text-xs text-gray-500">Security Score</span>
<span className={`text-lg font-bold font-mono ${ <span className={`text-lg font-bold font-mono ${task.security_score >= 80 ? 'text-green-400' :
task.security_score >= 80 ? 'text-green-400' :
task.security_score >= 60 ? 'text-yellow-400' : task.security_score >= 60 ? 'text-yellow-400' :
'text-red-400' 'text-red-400'
}`}> }`}>
{task.security_score.toFixed(0)} {task.security_score.toFixed(0)}
</span> </span>
</div> </div>
@ -627,7 +626,8 @@ export default function AgentAuditPage() {
const currentThinkingId = useRef<string | null>(null); const currentThinkingId = useRef<string | null>(null);
const currentAgentName = useRef<string | null>(null); const currentAgentName = useRef<string | null>(null);
const isRunning = task?.status === "running"; // 任务是否可取消(包括 pending 和 running 状态)
const isRunning = task?.status === "running" || task?.status === "pending";
const isComplete = task?.status === "completed" || task?.status === "failed" || task?.status === "cancelled"; const isComplete = task?.status === "completed" || task?.status === "failed" || task?.status === "cancelled";
// 构建 Agent 树结构(将扁平列表转换为树) // 构建 Agent 树结构(将扁平列表转换为树)
@ -727,6 +727,36 @@ export default function AgentAuditPage() {
} }
}, [taskId]); }, [taskId]);
// 🔥 Agent 树刷新防抖 ref
const agentTreeRefreshTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const lastAgentTreeRefreshTime = useRef<number>(0);
// 🔥 防抖刷新 Agent 树
const debouncedLoadAgentTree = useCallback(() => {
const now = Date.now();
const minInterval = 500; // 最小刷新间隔 500ms
// 清除之前的定时器
if (agentTreeRefreshTimer.current) {
clearTimeout(agentTreeRefreshTimer.current);
}
// 如果距离上次刷新不到 minInterval延迟刷新
const timeSinceLastRefresh = now - lastAgentTreeRefreshTime.current;
if (timeSinceLastRefresh < minInterval) {
agentTreeRefreshTimer.current = setTimeout(() => {
lastAgentTreeRefreshTime.current = Date.now();
loadAgentTree();
}, minInterval - timeSinceLastRefresh);
} else {
// 立即刷新,但添加一个小延迟确保后端数据已更新
agentTreeRefreshTimer.current = setTimeout(() => {
lastAgentTreeRefreshTime.current = Date.now();
loadAgentTree();
}, 100);
}
}, [loadAgentTree]);
// 流式事件处理 // 流式事件处理
const streamOptions = useMemo(() => ({ const streamOptions = useMemo(() => ({
includeThinking: true, includeThinking: true,
@ -737,15 +767,18 @@ export default function AgentAuditPage() {
currentAgentName.current = event.metadata.agent_name; currentAgentName.current = event.metadata.agent_name;
} }
// 处理 dispatch 事件 // 🔥 处理 dispatch 相关事件 - 增加更多事件类型
if (event.type === 'dispatch' || event.type === 'dispatch_complete') { const dispatchEvents = ['dispatch', 'dispatch_complete', 'node_start', 'phase_start'];
addLog({ if (dispatchEvents.includes(event.type)) {
type: 'dispatch', if (event.type === 'dispatch' || event.type === 'dispatch_complete') {
title: event.message || `Agent dispatch: ${event.metadata?.agent || 'unknown'}`, addLog({
agentName: currentAgentName.current || undefined, type: 'dispatch',
}); title: event.message || `Agent dispatch: ${event.metadata?.agent || 'unknown'}`,
// 🔥 刷新 Agent 树,显示新创建的子 Agent agentName: currentAgentName.current || undefined,
loadAgentTree(); });
}
// 🔥 使用防抖刷新 Agent 树,显示新创建的子 Agent
debouncedLoadAgentTree();
} }
}, },
onThinkingStart: () => { onThinkingStart: () => {
@ -790,11 +823,11 @@ export default function AgentAuditPage() {
setLogs(prev => prev.map(log => setLogs(prev => prev.map(log =>
log.id === currentThinkingId.current log.id === currentThinkingId.current
? { ? {
...log, ...log,
title: cleanResponse.slice(0, 100) + (cleanResponse.length > 100 ? '...' : ''), title: cleanResponse.slice(0, 100) + (cleanResponse.length > 100 ? '...' : ''),
content: cleanResponse, content: cleanResponse,
isStreaming: false isStreaming: false
} }
: log : log
)); ));
currentThinkingId.current = null; currentThinkingId.current = null;
@ -859,7 +892,7 @@ export default function AgentAuditPage() {
onError: (err: string) => { onError: (err: string) => {
addLog({ type: 'error', title: `Error: ${err}` }); addLog({ type: 'error', title: `Error: ${err}` });
}, },
}), [addLog, loadTask, loadFindings, loadAgentTree]); }), [addLog, loadTask, loadFindings, loadAgentTree, debouncedLoadAgentTree]);
const { connect: connectStream, disconnect: disconnectStream, isConnected } = useAgentStream(taskId || null, streamOptions); const { connect: connectStream, disconnect: disconnectStream, isConnected } = useAgentStream(taskId || null, streamOptions);
@ -885,10 +918,10 @@ export default function AgentAuditPage() {
return () => disconnectStream(); return () => disconnectStream();
}, [taskId, task?.status, connectStream, disconnectStream, addLog]); }, [taskId, task?.status, connectStream, disconnectStream, addLog]);
// 定期刷新 Agent 树 // 定期刷新 Agent 树 - 每 2 秒刷新一次
useEffect(() => { useEffect(() => {
if (!taskId || !isRunning) return; if (!taskId || !isRunning) return;
const interval = setInterval(loadAgentTree, 3000); const interval = setInterval(loadAgentTree, 2000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [taskId, isRunning, loadAgentTree]); }, [taskId, isRunning, loadAgentTree]);
@ -906,15 +939,30 @@ export default function AgentAuditPage() {
} }
}, [logs, isAutoScroll]); }, [logs, isAutoScroll]);
// 取消状态
const [isCancelling, setIsCancelling] = useState(false);
// 取消任务 // 取消任务
const handleCancel = async () => { const handleCancel = async () => {
if (!taskId) return; if (!taskId || isCancelling) return;
setIsCancelling(true);
addLog({ type: 'info', title: '🛑 Requesting task cancellation...' });
try { try {
await cancelAgentTask(taskId); await cancelAgentTask(taskId);
toast.success("Task cancelled"); toast.success("Task cancellation requested");
loadTask(); addLog({ type: 'info', title: '🛑 Task cancellation confirmed' });
} catch { // 立即刷新任务状态
toast.error("Failed to cancel task"); await loadTask();
// 断开流连接
disconnectStream();
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
toast.error(`Failed to cancel task: ${errorMessage}`);
addLog({ type: 'error', title: `Failed to cancel: ${errorMessage}` });
} finally {
setIsCancelling(false);
} }
}; };
@ -978,10 +1026,15 @@ export default function AgentAuditPage() {
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={handleCancel} onClick={handleCancel}
className="text-red-400 hover:text-red-300 hover:bg-red-950/30" disabled={isCancelling}
className="text-red-400 hover:text-red-300 hover:bg-red-950/30 disabled:opacity-50"
> >
<Square className="w-4 h-4 mr-1" /> {isCancelling ? (
Stop <Loader2 className="w-4 h-4 mr-1 animate-spin" />
) : (
<Square className="w-4 h-4 mr-1" />
)}
{isCancelling ? 'Stopping...' : 'Stop'}
</Button> </Button>
)} )}
<Button <Button
@ -1018,9 +1071,8 @@ export default function AgentAuditPage() {
</div> </div>
<button <button
onClick={() => setIsAutoScroll(!isAutoScroll)} onClick={() => setIsAutoScroll(!isAutoScroll)}
className={`text-xs px-2 py-1 rounded transition-colors ${ className={`text-xs px-2 py-1 rounded transition-colors ${isAutoScroll ? 'bg-primary/20 text-primary' : 'text-gray-500 hover:text-gray-300'
isAutoScroll ? 'bg-primary/20 text-primary' : 'text-gray-500 hover:text-gray-300' }`}
}`}
> >
Auto-scroll {isAutoScroll ? 'ON' : 'OFF'} Auto-scroll {isAutoScroll ? 'ON' : 'OFF'}
</button> </button>