refactor(frontend): 简化审计页面UI样式,移除多余动画效果

- 清理代理树节点和日志条目的冗余样式和动画
- 统一颜色和间距设计,优化暗黑模式支持
- 移除背景特效和多余装饰元素,提升性能
This commit is contained in:
lintsinghua 2025-12-18 23:11:43 +08:00
parent 0bfed4d7d4
commit 87c501b55c
3 changed files with 175 additions and 274 deletions

View File

@ -1,8 +1,6 @@
/**
* Agent Tree Node Component
* Elegant tree visualization with enhanced visual design
* Features: Animated connection lines, status indicators, smooth transitions
* Premium visual effects with depth and hierarchy
* Clean tree visualization with simple connection lines
*/
import { useState, memo } from "react";
@ -11,37 +9,29 @@ import { Badge } from "@/components/ui/badge";
import { AGENT_STATUS_CONFIG } from "../constants";
import type { AgentTreeNodeItemProps } from "../types";
// Agent type icons with enhanced colors and styling
// Agent type icons
const AGENT_TYPE_ICONS: Record<string, React.ReactNode> = {
orchestrator: <Cpu className="w-4 h-4 text-violet-500" />,
recon: <Scan className="w-4 h-4 text-teal-500" />,
analysis: <FileSearch className="w-4 h-4 text-amber-500" />,
verification: <ShieldCheck className="w-4 h-4 text-emerald-500" />,
orchestrator: <Cpu className="w-4 h-4 text-violet-600 dark:text-violet-500" />,
recon: <Scan className="w-4 h-4 text-teal-600 dark:text-teal-500" />,
analysis: <FileSearch className="w-4 h-4 text-amber-600 dark:text-amber-500" />,
verification: <ShieldCheck className="w-4 h-4 text-emerald-600 dark:text-emerald-500" />,
};
// Agent type background colors for icon container
// Agent type background colors
const AGENT_TYPE_BG: Record<string, string> = {
orchestrator: 'bg-violet-500/15 border-violet-500/30',
recon: 'bg-teal-500/15 border-teal-500/30',
analysis: 'bg-amber-500/15 border-amber-500/30',
verification: 'bg-emerald-500/15 border-emerald-500/30',
};
// Status colors for the glow effect
const STATUS_GLOW_COLORS: Record<string, string> = {
running: 'shadow-[0_0_15px_rgba(52,211,153,0.3)]',
completed: 'shadow-[0_0_10px_rgba(52,211,153,0.15)]',
failed: 'shadow-[0_0_12px_rgba(244,63,94,0.25)]',
waiting: 'shadow-[0_0_8px_rgba(251,191,36,0.2)]',
created: '',
orchestrator: 'bg-violet-100 dark:bg-violet-500/15 border-violet-300 dark:border-violet-500/30',
recon: 'bg-teal-100 dark:bg-teal-500/15 border-teal-300 dark:border-teal-500/30',
analysis: 'bg-amber-100 dark:bg-amber-500/15 border-amber-300 dark:border-amber-500/30',
verification: 'bg-emerald-100 dark:bg-emerald-500/15 border-emerald-300 dark:border-emerald-500/30',
};
export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
node,
depth = 0,
selectedId,
onSelect
}: AgentTreeNodeItemProps) {
onSelect,
isLast = false
}: AgentTreeNodeItemProps & { isLast?: boolean }) {
const [expanded, setExpanded] = useState(true);
const hasChildren = node.children && node.children.length > 0;
const isSelected = selectedId === node.agent_id;
@ -49,65 +39,62 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
const isCompleted = node.status === 'completed';
const isFailed = node.status === 'failed';
const statusConfig = AGENT_STATUS_CONFIG[node.status] || AGENT_STATUS_CONFIG.created;
const typeIcon = AGENT_TYPE_ICONS[node.agent_type] || <Bot className="w-3.5 h-3.5 text-muted-foreground" />;
const typeBg = AGENT_TYPE_BG[node.agent_type] || 'bg-muted/50 border-border/50';
const typeBg = AGENT_TYPE_BG[node.agent_type] || 'bg-muted border-border';
const indent = depth * 24;
return (
<div className="relative">
{/* Connection line to parent - vertical line with gradient */}
{depth > 0 && (
<div
className="absolute top-0 w-px bg-gradient-to-b from-border/80 via-border/50 to-border/30"
style={{
left: `${depth * 20 - 10}px`,
height: '22px',
}}
/>
)}
{/* Horizontal connector line with dot */}
{/* 树形连接线 */}
{depth > 0 && (
<>
{/* 垂直线 - 从父节点延伸下来 */}
<div
className="absolute top-[22px] h-px bg-gradient-to-r from-border/60 to-border/30"
className="absolute border-l-2 border-slate-300 dark:border-slate-600"
style={{
left: `${depth * 20 - 10}px`,
width: '10px',
left: `${indent - 12}px`,
top: 0,
height: isLast ? '20px' : '100%',
}}
/>
{/* 水平线 - 连接到当前节点 */}
<div
className="absolute top-[20px] w-1.5 h-1.5 rounded-full bg-border/60"
className="absolute border-t-2 border-slate-300 dark:border-slate-600"
style={{
left: `${depth * 20 - 11}px`,
left: `${indent - 12}px`,
top: '20px',
width: '12px',
}}
/>
</>
)}
{/* Node item with enhanced styling */}
{/* Node item */}
<div
className={`
group relative flex items-center gap-2.5 py-2.5 px-3 cursor-pointer rounded-lg
transition-all duration-300 ease-out backdrop-blur-sm
relative flex items-center gap-2 py-2 px-2 cursor-pointer rounded-md
${isSelected
? 'bg-primary/15 border border-primary/50 shadow-[0_0_20px_rgba(255,107,44,0.15)]'
: 'border border-transparent hover:bg-card/60 hover:border-border/50'
? 'bg-primary/15 border-2 border-primary shadow-[0_0_12px_rgba(255,95,31,0.4)]'
: isRunning
? 'bg-emerald-50 dark:bg-emerald-950/30 border-2 border-emerald-400 dark:border-emerald-500 shadow-[0_0_10px_rgba(52,211,153,0.3)]'
: isCompleted
? 'bg-slate-50 dark:bg-card border border-emerald-300 dark:border-emerald-600'
: isFailed
? 'bg-rose-50 dark:bg-rose-950/20 border border-rose-300 dark:border-rose-500'
: node.status === 'waiting'
? 'bg-amber-50 dark:bg-amber-950/20 border border-amber-300 dark:border-amber-500'
: 'bg-slate-50 dark:bg-card border border-slate-300 dark:border-slate-600 hover:border-slate-400 dark:hover:border-slate-500'
}
${STATUS_GLOW_COLORS[node.status] || ''}
`}
style={{ marginLeft: `${depth * 20}px` }}
style={{ marginLeft: `${indent}px` }}
onClick={() => onSelect(node.agent_id)}
>
{/* Expand/collapse button with enhanced styling */}
{/* Expand/collapse button */}
{hasChildren ? (
<button
onClick={(e) => { e.stopPropagation(); setExpanded(!expanded); }}
className={`
flex-shrink-0 w-6 h-6 flex items-center justify-center rounded-md
transition-all duration-300
${expanded ? 'bg-muted/80 border border-border/50' : 'hover:bg-muted/50'}
`}
className="flex-shrink-0 w-5 h-5 flex items-center justify-center rounded hover:bg-muted"
>
{expanded ? (
<ChevronDown className="w-4 h-4 text-muted-foreground" />
@ -116,81 +103,65 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
)}
</button>
) : (
<span className="w-6" />
<span className="w-5" />
)}
{/* Status indicator with enhanced glow */}
{/* Status indicator */}
<div className="relative flex-shrink-0">
<div className={`
w-3 h-3 rounded-full transition-all duration-300
${isRunning ? 'bg-emerald-400 shadow-[0_0_8px_rgba(52,211,153,0.6)]' : ''}
${isCompleted ? 'bg-emerald-500 shadow-[0_0_6px_rgba(52,211,153,0.4)]' : ''}
${isFailed ? 'bg-rose-400 shadow-[0_0_6px_rgba(244,63,94,0.4)]' : ''}
${node.status === 'waiting' ? 'bg-amber-400 shadow-[0_0_6px_rgba(251,191,36,0.4)]' : ''}
${node.status === 'created' ? 'bg-muted-foreground/50' : ''}
w-2.5 h-2.5 rounded-full
${isRunning ? 'bg-emerald-500' : ''}
${isCompleted ? 'bg-emerald-500' : ''}
${isFailed ? 'bg-rose-500' : ''}
${node.status === 'waiting' ? 'bg-amber-500' : ''}
${node.status === 'created' ? 'bg-slate-400' : ''}
`} />
{isRunning && (
<>
<div className="absolute inset-0 w-3 h-3 rounded-full bg-emerald-400 animate-ping opacity-40" />
<div className="absolute inset-[-2px] w-[calc(100%+4px)] h-[calc(100%+4px)] rounded-full border border-emerald-400/30 animate-pulse" />
</>
<div className="absolute inset-0 w-2.5 h-2.5 rounded-full bg-emerald-500 animate-ping opacity-50" />
)}
</div>
{/* Agent type icon with background */}
<div className={`flex-shrink-0 p-1.5 rounded-md border ${typeBg} transition-all duration-300 group-hover:scale-105`}>
{/* Agent type icon */}
<div className={`flex-shrink-0 p-1 rounded border ${typeBg}`}>
{typeIcon}
</div>
{/* Agent name with enhanced styling */}
{/* Agent name */}
<span className={`
text-sm font-mono truncate flex-1 transition-all duration-300
${isSelected ? 'text-foreground font-semibold' : 'text-foreground/90 group-hover:text-foreground'}
text-sm font-mono truncate flex-1
${isSelected ? 'text-foreground font-semibold' : 'text-foreground'}
`}>
{node.agent_name}
</span>
{/* Metrics badges with enhanced styling */}
<div className="flex items-center gap-2 flex-shrink-0">
{/* Iterations with icon */}
{/* Metrics */}
<div className="flex items-center gap-1.5 flex-shrink-0">
{(node.iterations ?? 0) > 0 && (
<div className="flex items-center gap-1 text-xs text-muted-foreground font-mono bg-muted/80 px-2 py-1 rounded-md border border-border/50">
<div className="flex items-center gap-1 text-xs text-muted-foreground font-mono bg-muted px-1.5 py-0.5 rounded border border-border">
<Zap className="w-3 h-3" />
<span>{node.iterations}</span>
</div>
)}
{/* Findings count - Only show for Orchestrator (root agent) */}
{!node.parent_agent_id && node.findings_count > 0 && (
<Badge className="h-6 px-2.5 text-xs bg-rose-500/20 text-rose-600 dark:text-rose-300 border border-rose-500/40 font-mono font-bold shadow-[0_0_10px_rgba(244,63,94,0.15)]">
{node.findings_count} findings
<Badge className="h-5 px-2 text-xs bg-rose-100 dark:bg-rose-500/20 text-rose-600 dark:text-rose-300 border border-rose-300 dark:border-rose-500/40 font-mono font-bold">
{node.findings_count}
</Badge>
)}
</div>
</div>
{/* Children with animated reveal */}
{/* Children */}
{expanded && hasChildren && (
<div
className="relative animate-in slide-in-from-top-1 duration-300"
>
{/* Vertical connection line for children with gradient */}
<div
className="absolute w-px bg-gradient-to-b from-border/60 via-border/40 to-transparent"
style={{
left: `${(depth + 1) * 20 - 10}px`,
top: '0',
height: `calc(100% - 24px)`,
}}
/>
{node.children.map((child) => (
<div className="relative">
{node.children.map((child, index) => (
<AgentTreeNodeItem
key={child.agent_id}
node={child}
depth={depth + 1}
selectedId={selectedId}
onSelect={onSelect}
isLast={index === node.children.length - 1}
/>
))}
</div>

View File

@ -85,45 +85,36 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
return (
<div
className={`
group relative mb-2 transition-all duration-300 ease-out
group relative transition-all duration-300 ease-out
${isCollapsible ? 'cursor-pointer' : ''}
`}
onClick={isCollapsible ? onToggle : undefined}
>
{/* Main card with enhanced styling */}
{/* Main card */}
<div className={`
relative rounded-lg border-l-3 overflow-hidden backdrop-blur-sm
relative rounded-lg border-l-3 overflow-hidden
${config.borderColor}
${isExpanded ? 'bg-card/80 shadow-lg' : 'bg-card/40'}
${isCollapsible ? 'hover:bg-card/60 hover:shadow-md' : ''}
${isFinding ? 'border border-rose-500/20 shadow-[0_0_15px_rgba(244,63,94,0.1)]' : 'border border-transparent'}
${isError ? 'border border-red-500/20 shadow-[0_0_15px_rgba(239,68,68,0.1)]' : ''}
${isDispatch ? 'border border-sky-500/20' : ''}
transition-all duration-300
${isExpanded ? 'bg-slate-100 dark:bg-card/80' : 'bg-slate-50 dark:bg-card/40'}
${isCollapsible ? 'hover:bg-slate-100 dark:hover:bg-card/60' : ''}
${isFinding ? 'border border-rose-500/30 dark:border-rose-500/20 !bg-rose-50 dark:!bg-rose-950/20' : 'border border-slate-200 dark:border-transparent'}
${isError ? 'border border-red-500/30 dark:border-red-500/20 !bg-red-50 dark:!bg-red-950/20' : ''}
${isDispatch ? 'border-sky-500/30 dark:border-sky-500/20 !bg-sky-50 dark:!bg-sky-950/20' : ''}
${isThinking ? '!bg-violet-50 dark:!bg-violet-950/20 border-violet-500/30 dark:border-violet-500/20' : ''}
${isTool ? '!bg-amber-50 dark:!bg-amber-950/20 border-amber-500/30 dark:border-amber-500/20' : ''}
`}>
{/* Enhanced gradient overlay */}
<div className={`absolute inset-0 opacity-30 pointer-events-none bg-gradient-to-r ${config.bgColor} to-transparent`} />
{/* Content */}
<div className="relative px-4 py-3">
{/* Header row */}
<div className="flex items-center gap-2.5">
{/* Type icon with glow effect */}
<div className="relative flex-shrink-0">
<div className={`${item.isStreaming ? 'animate-pulse' : ''} transition-transform duration-300 group-hover:scale-110`}>
{config.icon}
</div>
{item.isStreaming && (
<div className="absolute inset-0 blur-md opacity-60">
{config.icon}
</div>
)}
{/* Type icon */}
<div className="flex-shrink-0">
{config.icon}
</div>
{/* Type label with enhanced styling */}
{/* Type label */}
<span className={`
text-xs font-mono font-bold uppercase tracking-wider px-2 py-1 rounded-md
border transition-all duration-300
text-xs font-mono font-bold uppercase tracking-wider px-2 py-1 rounded-md border
${isThinking ? 'bg-violet-500/20 text-violet-600 dark:text-violet-300 border-violet-500/30' : ''}
${isTool ? 'bg-amber-500/20 text-amber-600 dark:text-amber-300 border-amber-500/30' : ''}
${isFinding ? 'bg-rose-500/20 text-rose-600 dark:text-rose-300 border-rose-500/30' : ''}
@ -138,12 +129,12 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
{LOG_TYPE_LABELS[item.type] || 'LOG'}
</span>
{/* Timestamp with subtle styling */}
<span className="text-xs text-muted-foreground/80 font-mono flex-shrink-0 tabular-nums">
{/* Timestamp */}
<span className="text-xs text-muted-foreground font-mono flex-shrink-0 tabular-nums">
{item.time}
</span>
{/* Separator with animation */}
{/* Separator */}
<Zap className="w-3 h-3 text-muted-foreground/50 flex-shrink-0" />
{/* Status icon for info messages */}
@ -151,36 +142,36 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
{/* Title - for non-thinking types */}
{!isThinking && (
<span className="text-sm text-foreground font-medium truncate flex-1 leading-relaxed">
<span className="text-sm text-foreground font-medium truncate flex-1">
{formattedTitle}
</span>
)}
{/* Streaming cursor with enhanced animation */}
{/* Streaming cursor */}
{item.isStreaming && (
<span className="w-2 h-5 bg-gradient-to-b from-violet-400 to-violet-600 animate-[blink_1s_ease-in-out_infinite] rounded-sm flex-shrink-0 shadow-[0_0_8px_rgba(139,92,246,0.5)]" />
<span className="w-2 h-5 bg-violet-500 rounded-sm flex-shrink-0" />
)}
{/* Tool status with enhanced styling */}
{/* Tool status */}
{item.tool?.status === 'running' && (
<div className="flex items-center gap-2 flex-shrink-0 bg-amber-500/15 px-2.5 py-1 rounded-md border border-amber-500/30">
<Loader2 className="w-3.5 h-3.5 animate-spin text-amber-400" />
<span className="text-xs text-amber-400 font-mono uppercase font-semibold">Running</span>
<Loader2 className="w-3.5 h-3.5 animate-spin text-amber-600 dark:text-amber-400" />
<span className="text-xs text-amber-600 dark:text-amber-400 font-mono uppercase font-semibold">Running</span>
</div>
)}
{item.tool?.status === 'completed' && (
<div className="flex items-center gap-1.5 flex-shrink-0 px-2 py-1 rounded-md bg-emerald-500/10 border border-emerald-500/30">
<CheckCircle2 className="w-3.5 h-3.5 text-emerald-500" />
<span className="text-xs text-emerald-500 font-mono uppercase">Done</span>
<CheckCircle2 className="w-3.5 h-3.5 text-emerald-600 dark:text-emerald-500" />
<span className="text-xs text-emerald-600 dark:text-emerald-500 font-mono uppercase">Done</span>
</div>
)}
{/* Agent badge with enhanced styling */}
{/* Agent badge */}
{item.agentName && (
<Badge
variant="outline"
className="h-6 px-2.5 text-xs uppercase tracking-wider border-primary/40 text-primary bg-primary/10 flex-shrink-0 font-semibold shadow-[0_0_10px_rgba(255,107,44,0.1)]"
className="h-6 px-2.5 text-xs uppercase tracking-wider border-primary/40 text-primary bg-primary/10 flex-shrink-0 font-semibold"
>
{item.agentName}
</Badge>
@ -188,14 +179,14 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
{/* Right side info */}
<div className="flex items-center gap-2.5 flex-shrink-0 ml-auto">
{/* Duration badge with enhanced styling */}
{/* Duration badge */}
{item.tool?.duration !== undefined && (
<span className="text-xs text-muted-foreground font-mono bg-muted/80 px-2 py-1 rounded-md border border-border/50 tabular-nums">
<span className="text-xs text-muted-foreground font-mono bg-muted px-2 py-1 rounded-md border border-border tabular-nums">
{item.tool.duration}ms
</span>
)}
{/* Severity badge with enhanced styling */}
{/* Severity badge */}
{item.severity && (
<Badge
className={`
@ -207,53 +198,53 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
</Badge>
)}
{/* Expand indicator with enhanced styling */}
{/* Expand indicator */}
{isCollapsible && (
<div className={`
w-6 h-6 flex items-center justify-center rounded-md transition-all duration-300
${isExpanded ? 'bg-primary/20 border border-primary/30' : 'bg-muted/50 border border-transparent group-hover:border-border/50'}
w-6 h-6 flex items-center justify-center rounded-md
${isExpanded ? 'bg-primary/20 border border-primary/30' : 'bg-muted border border-border'}
`}>
{isExpanded ? (
<ChevronUp className="w-4 h-4 text-primary" />
) : (
<ChevronDown className="w-4 h-4 text-muted-foreground group-hover:text-foreground transition-colors" />
<ChevronDown className="w-4 h-4 text-muted-foreground" />
)}
</div>
)}
</div>
</div>
{/* Thinking content - always visible with enhanced styling */}
{/* Thinking content - always visible */}
{isThinking && item.content && (
<div className="mt-3 relative">
<div className="absolute left-0 top-0 bottom-0 w-0.5 bg-gradient-to-b from-violet-500/60 via-violet-500/30 to-transparent rounded-full" />
<div className="pl-4 text-sm text-foreground/90 leading-relaxed whitespace-pre-wrap break-words">
<div className="absolute left-0 top-0 bottom-0 w-0.5 bg-violet-500/50 rounded-full" />
<div className="pl-4 text-sm text-foreground/90 whitespace-pre-wrap break-words">
{item.content}
</div>
</div>
)}
{/* Collapsible content with enhanced styling */}
{/* Collapsible content */}
{!isThinking && showContent && item.content && (
<div className="mt-3 overflow-hidden animate-in slide-in-from-top-2 duration-300">
<div className="bg-card/80 rounded-lg border border-border/50 overflow-hidden backdrop-blur-sm shadow-inner">
{/* Mini header with enhanced styling */}
<div className="flex items-center justify-between px-3 py-2 border-b border-border/50 bg-muted/50">
<div className="mt-3 overflow-hidden">
<div className="bg-card rounded-lg border border-border overflow-hidden">
{/* Mini header */}
<div className="flex items-center justify-between px-3 py-2 border-b border-border bg-muted/50">
<div className="flex items-center gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-muted-foreground/50" />
<span className="text-xs text-muted-foreground font-mono uppercase tracking-wider">
<span className="text-xs text-muted-foreground font-mono uppercase">
{isTool ? 'Output' : 'Details'}
</span>
</div>
{item.tool?.status === 'completed' && (
<div className="flex items-center gap-1.5 px-2 py-0.5 rounded bg-emerald-500/10 border border-emerald-500/20">
<CheckCircle2 className="w-3 h-3 text-emerald-500" />
<span className="text-xs text-emerald-500 font-mono">Complete</span>
<CheckCircle2 className="w-3 h-3 text-emerald-600 dark:text-emerald-500" />
<span className="text-xs text-emerald-600 dark:text-emerald-500 font-mono">Complete</span>
</div>
)}
</div>
{/* Content with enhanced styling */}
<pre className="p-4 text-sm font-mono text-foreground/85 max-h-64 overflow-y-auto custom-scrollbar whitespace-pre-wrap break-words leading-relaxed bg-gradient-to-b from-transparent to-muted/20">
{/* Content */}
<pre className="p-4 text-sm font-mono text-foreground/85 max-h-64 overflow-y-auto custom-scrollbar whitespace-pre-wrap break-words">
{item.content}
</pre>
</div>
@ -262,13 +253,6 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
</div>
</div>
{/* Inline styles for cursor blink */}
<style>{`
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
`}</style>
</div>
);
});

View File

@ -772,17 +772,6 @@ function AgentAuditPageContent() {
return (
<div className="h-screen bg-background flex flex-col overflow-hidden relative">
{/* Animated gradient background */}
<div className="absolute inset-0 bg-gradient-to-br from-background via-background to-primary/5 pointer-events-none" />
{/* Subtle grid background with animation */}
<div className="absolute inset-0 cyber-grid-subtle opacity-30 pointer-events-none animate-grid-pulse" />
{/* Scanline effect */}
<div className="absolute inset-0 scanline-overlay pointer-events-none opacity-30" />
{/* Corner accent lines */}
<div className="absolute top-0 left-0 w-32 h-px bg-gradient-to-r from-primary/50 to-transparent pointer-events-none" />
<div className="absolute top-0 left-0 w-px h-32 bg-gradient-to-b from-primary/50 to-transparent pointer-events-none" />
<div className="absolute bottom-0 right-0 w-32 h-px bg-gradient-to-l from-primary/50 to-transparent pointer-events-none" />
<div className="absolute bottom-0 right-0 w-px h-32 bg-gradient-to-t from-primary/50 to-transparent pointer-events-none" />
{/* Header */}
<Header
@ -797,33 +786,21 @@ function AgentAuditPageContent() {
{/* Main content */}
<div className="flex-1 flex overflow-hidden relative">
{/* Left Panel - Activity Log */}
<div className="w-3/4 flex flex-col border-r border-border/50 relative">
{/* Panel glow effect */}
<div className="absolute inset-y-0 right-0 w-px bg-gradient-to-b from-transparent via-primary/20 to-transparent pointer-events-none" />
<div className="w-3/4 flex flex-col border-r border-border relative">
{/* Log header */}
<div className="flex-shrink-0 h-12 border-b border-border/50 flex items-center justify-between px-5 bg-card/80 backdrop-blur-md relative overflow-hidden">
{/* Header accent line */}
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-primary/30 via-transparent to-primary/30" />
<div className="flex items-center gap-4 text-xs text-muted-foreground relative z-10">
<div className="flex-shrink-0 h-12 border-b border-border flex items-center justify-between px-5 bg-card">
<div className="flex items-center gap-4 text-xs text-muted-foreground">
<div className="flex items-center gap-2.5">
<div className="relative">
<Terminal className="w-4 h-4 text-primary" />
{isConnected && (
<div className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 bg-emerald-400 rounded-full animate-pulse" />
)}
</div>
<span className="uppercase font-bold tracking-[0.12em] text-foreground text-sm">Activity Log</span>
<Terminal className="w-4 h-4 text-primary" />
<span className="uppercase font-bold tracking-wider text-foreground text-sm">Activity Log</span>
</div>
{isConnected && (
<div className="flex items-center gap-2 px-2.5 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/30">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-400 shadow-[0_0_10px_rgba(52,211,153,0.6)]"></span>
</span>
<span className="text-xs font-mono uppercase tracking-wider text-emerald-400 font-semibold">Live</span>
<span className="w-2 h-2 rounded-full bg-emerald-500"></span>
<span className="text-xs font-mono uppercase tracking-wider text-emerald-600 dark:text-emerald-400 font-semibold">Live</span>
</div>
)}
<Badge variant="outline" className="h-6 px-2 text-xs border-border/60 text-muted-foreground font-mono bg-muted/50 backdrop-blur-sm">
<Badge variant="outline" className="h-6 px-2 text-xs border-border text-muted-foreground font-mono bg-muted">
{filteredLogs.length}{!showAllLogs && logs.length !== filteredLogs.length ? ` / ${logs.length}` : ''} entries
</Badge>
</div>
@ -831,36 +808,30 @@ function AgentAuditPageContent() {
<button
onClick={() => setAutoScroll(!isAutoScroll)}
className={`
relative flex items-center gap-2 text-xs px-3 py-1.5 rounded-md font-mono uppercase tracking-wider
transition-all duration-300 ease-out overflow-hidden
flex items-center gap-2 text-xs px-3 py-1.5 rounded-md font-mono uppercase tracking-wider
${isAutoScroll
? 'bg-primary/15 text-primary border border-primary/50 shadow-[0_0_15px_rgba(255,95,31,0.2)]'
: 'text-muted-foreground hover:text-foreground border border-border/50 hover:border-border hover:bg-muted/30'
? 'bg-primary/15 text-primary border border-primary/50'
: 'text-muted-foreground hover:text-foreground border border-border hover:bg-muted'
}
`}
>
{isAutoScroll && (
<div className="absolute inset-0 bg-gradient-to-r from-primary/10 via-primary/5 to-primary/10 animate-shimmer" />
)}
<ArrowDown className={`w-3.5 h-3.5 relative z-10 transition-transform duration-300 ${isAutoScroll ? 'animate-bounce' : ''}`} />
<span className="relative z-10">Auto-scroll</span>
<ArrowDown className="w-3.5 h-3.5" />
<span>Auto-scroll</span>
</button>
</div>
{/* Log content */}
<div className="flex-1 overflow-y-auto p-5 custom-scrollbar bg-gradient-to-b from-background/80 to-background/40">
<div className="flex-1 overflow-y-auto p-5 custom-scrollbar bg-muted/30">
{/* Filter indicator */}
{selectedAgentId && !showAllLogs && (
<div className="mb-4 px-4 py-2.5 bg-primary/10 border border-primary/30 rounded-lg flex items-center justify-between backdrop-blur-sm shadow-[0_0_20px_rgba(255,107,44,0.1)]">
<div className="mb-4 px-4 py-2.5 bg-primary/10 border border-primary/30 rounded-lg flex items-center justify-between">
<div className="flex items-center gap-2.5 text-sm text-primary">
<div className="p-1.5 rounded bg-primary/20">
<Filter className="w-3.5 h-3.5" />
</div>
<span className="tracking-wide font-medium">Filtering logs for selected agent</span>
<Filter className="w-3.5 h-3.5" />
<span className="font-medium">Filtering logs for selected agent</span>
</div>
<button
onClick={() => selectAgent(null)}
className="text-xs text-muted-foreground hover:text-primary transition-colors font-mono uppercase tracking-wider px-2 py-1 rounded hover:bg-primary/10"
className="text-xs text-muted-foreground hover:text-primary font-mono uppercase px-2 py-1 rounded hover:bg-primary/10"
>
Clear Filter
</button>
@ -890,7 +861,7 @@ function AgentAuditPageContent() {
</div>
</div>
) : (
<div className="space-y-0.5">
<div className="space-y-3">
{filteredLogs.map(item => (
<LogEntry
key={item.id}
@ -906,31 +877,26 @@ function AgentAuditPageContent() {
{/* Status bar */}
{task && (
<div className="flex-shrink-0 h-10 border-t border-border/50 flex items-center justify-between px-5 text-xs bg-card/80 backdrop-blur-md relative overflow-hidden">
<div className="flex-shrink-0 h-10 border-t border-border flex items-center justify-between px-5 text-xs bg-card relative overflow-hidden">
{/* Progress bar background */}
<div
className="absolute inset-0 bg-gradient-to-r from-primary/10 to-transparent transition-all duration-700 ease-out"
className="absolute inset-0 bg-primary/10"
style={{ width: `${task.progress_percentage || 0}%` }}
/>
{/* Top accent line */}
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-primary/30 via-transparent to-primary/30" />
<span className="relative z-10">
{isRunning ? (
<span className="flex items-center gap-2.5 text-emerald-400">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-400 shadow-[0_0_8px_rgba(52,211,153,0.6)]"></span>
</span>
<span className="font-mono tracking-wide font-semibold">{statusVerb}{'.'.repeat(statusDots)}</span>
<span className="flex items-center gap-2.5 text-emerald-600 dark:text-emerald-400">
<span className="w-2 h-2 rounded-full bg-emerald-500"></span>
<span className="font-mono font-semibold">{statusVerb}{'.'.repeat(statusDots)}</span>
</span>
) : isComplete ? (
<span className="flex items-center gap-2 text-muted-foreground font-mono tracking-wide">
<span className="flex items-center gap-2 text-muted-foreground font-mono">
<span className={`w-2 h-2 rounded-full ${task.status === 'completed' ? 'bg-emerald-500' : task.status === 'failed' ? 'bg-rose-500' : 'bg-amber-500'}`} />
AUDIT {task.status?.toUpperCase()}
</span>
) : (
<span className="text-muted-foreground font-mono tracking-wide">READY</span>
<span className="text-muted-foreground font-mono">READY</span>
)}
</span>
<div className="flex items-center gap-5 font-mono text-muted-foreground relative z-10">
@ -938,16 +904,16 @@ function AgentAuditPageContent() {
<span className="text-primary font-bold text-sm">{task.progress_percentage?.toFixed(0) || 0}</span>
<span className="text-muted-foreground text-xs">%</span>
</div>
<div className="w-px h-4 bg-border/50" />
<div className="w-px h-4 bg-border" />
<div className="flex items-center gap-1.5">
<span className="text-foreground font-semibold">{task.analyzed_files}</span>
<span className="text-muted-foreground">/ {task.total_files}</span>
<span className="text-muted-foreground/70 text-xs">files</span>
<span className="text-muted-foreground text-xs">files</span>
</div>
<div className="w-px h-4 bg-border/50" />
<div className="w-px h-4 bg-border" />
<div className="flex items-center gap-1.5">
<span className="text-foreground font-semibold">{task.tool_calls_count || 0}</span>
<span className="text-muted-foreground/70 text-xs">tools</span>
<span className="text-muted-foreground text-xs">tools</span>
</div>
</div>
</div>
@ -955,55 +921,50 @@ function AgentAuditPageContent() {
</div>
{/* Right Panel - Agent Tree + Stats */}
<div className="w-1/4 flex flex-col bg-gradient-to-b from-background to-background/95 relative">
{/* Panel left glow */}
<div className="absolute inset-y-0 left-0 w-px bg-gradient-to-b from-transparent via-primary/10 to-transparent pointer-events-none" />
<div className="w-1/4 flex flex-col bg-background relative">
{/* Agent Tree section */}
<div className="flex-1 flex flex-col border-b border-border/50 overflow-hidden">
<div className="flex-1 flex flex-col border-b border-border overflow-hidden">
{/* Tree header */}
<div className="flex-shrink-0 h-12 border-b border-border/50 flex items-center justify-between px-4 bg-card/80 backdrop-blur-md relative overflow-hidden">
{/* Header accent */}
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-violet-500/30 via-transparent to-violet-500/30" />
<div className="flex items-center gap-2.5 text-xs text-muted-foreground relative z-10">
<div className="relative">
<Bot className="w-4 h-4 text-violet-500" />
{agentTree && agentTree.running_agents > 0 && (
<div className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 bg-emerald-400 rounded-full animate-pulse" />
)}
</div>
<span className="uppercase font-bold tracking-[0.12em] text-foreground text-sm">Agent Tree</span>
{agentTree && (
<Badge variant="outline" className="h-5 px-2 text-xs border-violet-500/30 text-violet-500 font-mono bg-violet-500/10">
<div className="flex-shrink-0 h-12 border-b border-border flex items-center justify-between px-4 bg-card">
<div className="flex items-center gap-2.5 text-xs text-muted-foreground">
<Bot className="w-4 h-4 text-violet-600 dark:text-violet-500" />
<span className="uppercase font-bold tracking-wider text-foreground text-sm">
{selectedAgentId && !showAllLogs ? 'Agent Detail' : 'Agent Tree'}
</span>
{!selectedAgentId && agentTree && (
<Badge variant="outline" className="h-5 px-2 text-xs border-violet-500/30 text-violet-600 dark:text-violet-500 font-mono bg-violet-500/10">
{agentTree.total_agents}
</Badge>
)}
</div>
<div className="flex items-center gap-2 relative z-10">
<div className="flex items-center gap-2">
{selectedAgentId && !showAllLogs && (
<button
onClick={() => selectAgent(null)}
className="text-xs text-primary hover:text-primary/80 transition-colors font-mono uppercase tracking-wider px-2 py-1 rounded hover:bg-primary/10"
className="text-xs text-primary hover:text-primary/80 font-mono uppercase px-2 py-1 rounded hover:bg-primary/10"
>
Show All
Back
</button>
)}
{agentTree && agentTree.running_agents > 0 && (
{!selectedAgentId && agentTree && agentTree.running_agents > 0 && (
<div className="flex items-center gap-1.5 px-2 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/30">
<span className="relative flex h-1.5 w-1.5">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.5)]"></span>
</span>
<span className="text-xs font-mono text-emerald-400 font-semibold">{agentTree.running_agents}</span>
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
<span className="text-xs font-mono text-emerald-600 dark:text-emerald-400 font-semibold">{agentTree.running_agents}</span>
</div>
)}
</div>
</div>
{/* Tree content */}
<div className="flex-1 overflow-y-auto p-3 custom-scrollbar bg-gradient-to-b from-background/60 to-background/30">
{treeNodes.length > 0 ? (
{/* Tree content or Agent Detail */}
<div className="flex-1 overflow-y-auto p-3 custom-scrollbar bg-muted/20">
{selectedAgentId && !showAllLogs ? (
/* Agent Detail Panel - 覆盖整个内容区域 */
<AgentDetailPanel
agentId={selectedAgentId}
treeNodes={treeNodes}
onClose={() => selectAgent(null)}
/>
) : treeNodes.length > 0 ? (
<div className="space-y-0.5">
{treeNodes.map(node => (
<AgentTreeNodeItem
@ -1018,18 +979,13 @@ function AgentAuditPageContent() {
<div className="h-full flex items-center justify-center text-muted-foreground text-xs">
{isRunning ? (
<div className="flex flex-col items-center gap-3 p-6">
<div className="relative">
<Loader2 className="w-6 h-6 animate-spin text-violet-500" />
<div className="absolute inset-0 blur-md opacity-50">
<Loader2 className="w-6 h-6 animate-spin text-violet-500" />
</div>
</div>
<span className="font-mono tracking-wide text-center">INITIALIZING<br/>AGENTS...</span>
<Loader2 className="w-6 h-6 animate-spin text-violet-600 dark:text-violet-500" />
<span className="font-mono text-center">INITIALIZING<br/>AGENTS...</span>
</div>
) : (
<div className="flex flex-col items-center gap-2 p-6 text-center">
<Bot className="w-8 h-8 text-muted-foreground/50" />
<span className="font-mono tracking-wide">NO AGENTS YET</span>
<span className="font-mono">NO AGENTS YET</span>
</div>
)}
</div>
@ -1037,18 +993,8 @@ function AgentAuditPageContent() {
</div>
</div>
{/* Bottom section - Details + Stats */}
<div className="flex-shrink-0 p-4 space-y-3 bg-card/50 backdrop-blur-sm">
{/* Agent detail panel */}
{selectedAgentId && !showAllLogs && (
<AgentDetailPanel
agentId={selectedAgentId}
treeNodes={treeNodes}
onClose={() => selectAgent(null)}
/>
)}
{/* Stats panel */}
{/* Bottom section - Stats */}
<div className="flex-shrink-0 p-4 bg-card">
<StatsPanel task={task} findings={findings} />
</div>
</div>