refactor: AgentMonitor 改用 MUI Table 布局标签-值对

This commit is contained in:
thzxx
2026-06-27 22:39:59 +08:00
parent 8dd593e747
commit a5ef816763
+56 -34
View File
@@ -1,9 +1,11 @@
/** /**
* AgentMonitor — Agent 状态指示器 * AgentMonitor — Agent 状态指示器
*
* 完全使用 MUI 组件,Table 布局标签-值对。
*/ */
import { Box, Typography, Stack } from '@mui/material'; import { Box, Typography, Stack, Table, TableBody, TableRow, TableCell, Chip } from '@mui/material';
import { Activity, Cpu, Clock } from 'lucide-react'; import { Activity, Cpu } from 'lucide-react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useAgentStore, type AgentStatus } from '@renderer/stores/agent-store'; import { useAgentStore, type AgentStatus } from '@renderer/stores/agent-store';
import { AGENT_STATUS_COLORS, AGENT_STATUS_LABELS, PROVIDER_LABELS } from '@renderer/lib/constants'; import { AGENT_STATUS_COLORS, AGENT_STATUS_LABELS, PROVIDER_LABELS } from '@renderer/lib/constants';
@@ -20,7 +22,6 @@ export function AgentMonitor(): React.JSX.Element {
const traceSteps = useAgentStore((s) => s.traceSteps); const traceSteps = useAgentStore((s) => s.traceSteps);
const setMaxIterations = useAgentStore((s) => s.setMaxIterations); const setMaxIterations = useAgentStore((s) => s.setMaxIterations);
// 启动时从数据库读取 maxIterations
useEffect(() => { useEffect(() => {
if (window.metona?.config?.get) { if (window.metona?.config?.get) {
window.metona.config.get('agent.maxIterations').then((v) => { window.metona.config.get('agent.maxIterations').then((v) => {
@@ -29,47 +30,68 @@ export function AgentMonitor(): React.JSX.Element {
} }
}, [setMaxIterations]); }, [setMaxIterations]);
// 监听配置变更
useEffect(() => {
const interval = setInterval(() => {
if (window.metona?.config?.get) {
window.metona.config.get('agent.maxIterations').then((v) => {
if (typeof v === 'number' && v > 0) setMaxIterations(v);
}).catch(() => {});
}
}, 3000);
return () => clearInterval(interval);
}, [setMaxIterations]);
const StatusIcon = STATUS_ICONS[agentStatus]; const StatusIcon = STATUS_ICONS[agentStatus];
const statusColor = AGENT_STATUS_COLORS[agentStatus]; const statusColor = AGENT_STATUS_COLORS[agentStatus];
const statusLabel = AGENT_STATUS_LABELS[agentStatus]; const statusLabel = AGENT_STATUS_LABELS[agentStatus];
const totalDuration = traceSteps.reduce((sum, s) => sum + (s.completedAt ? s.completedAt - s.startedAt : 0), 0); const totalDuration = traceSteps.reduce((sum, s) => sum + (s.completedAt ? s.completedAt - s.startedAt : 0), 0);
const InfoRow = ({ icon: Icon, label, value }: { icon: typeof Activity; label: string; value: string }) => (
<Stack direction="row" alignItems="center" sx={{ gap: 1 }}>
<Stack direction="row" spacing={0.75} alignItems="center" sx={{ color: 'text.secondary', flexShrink: 0, minWidth: 60 }}>
<Icon size={10} /><Typography variant="caption">{label}</Typography>
</Stack>
<Typography variant="caption" sx={{ fontFamily: 'monospace', color: 'text.primary', flex: 1, textAlign: 'right', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{value}</Typography>
</Stack>
);
return ( return (
<Box sx={{ mt: 2, pt: 2, borderTop: 1, borderColor: 'divider', flexShrink: 0 }}> <Box sx={{ mt: 2, pt: 2, borderTop: 1, borderColor: 'divider', flexShrink: 0 }}>
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1.5 }}> <Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1.5 }}>
<Cpu size={14} style={{ color: '#818cf8' }} /> <Cpu size={14} style={{ color: '#818cf8' }} />
<Typography variant="caption" sx={{ fontWeight: 600, textTransform: 'uppercase', letterSpacing: 1, color: 'text.secondary' }}>Agent </Typography> <Typography variant="caption" sx={{ fontWeight: 600, textTransform: 'uppercase', letterSpacing: 1, color: 'text.secondary' }}>
</Stack> Agent
<Stack direction="row" spacing={1} alignItems="center" sx={{ px: 1.5, py: 1, borderRadius: 1.5, mb: 1, bgcolor: 'background.default' }}> </Typography>
<StatusIcon size={14} style={{ color: statusColor, animation: (agentStatus === 'thinking' || agentStatus === 'executing') ? 'pulse 2s infinite' : 'none' }} />
<Typography variant="caption" sx={{ fontWeight: 500, color: statusColor }}>{statusLabel}</Typography>
</Stack>
<Stack spacing={0.75}>
<InfoRow icon={Activity} label="Provider" value={PROVIDER_LABELS[provider] ?? provider} />
<InfoRow icon={Cpu} label="模型" value={model} />
<InfoRow icon={Activity} label="迭代" value={`${currentIteration} / ${maxIterations}`} />
{totalDuration > 0 && <InfoRow icon={Clock} label="总耗时" value={formatDuration(totalDuration)} />}
</Stack> </Stack>
{/* 状态指示 */}
<Box sx={{ px: 1.5, py: 1, borderRadius: 1.5, mb: 1.5, bgcolor: 'background.default', border: '1px solid', borderColor: 'divider' }}>
<Stack direction="row" spacing={1} alignItems="center">
<StatusIcon
size={14}
style={{
color: statusColor,
animation: (agentStatus === 'thinking' || agentStatus === 'executing') ? 'pulse 2s infinite' : 'none',
}}
/>
<Typography variant="caption" sx={{ fontWeight: 500, color: statusColor }}>{statusLabel}</Typography>
{(agentStatus === 'thinking' || agentStatus === 'executing') && (
<Box sx={{ width: 6, height: 6, borderRadius: '50%', bgcolor: statusColor, animation: 'pulse 1.5s infinite', ml: 'auto' }} />
)}
</Stack>
</Box>
{/* 详情表格 */}
<Table size="small" sx={{ '& .MuiTableCell-root': { border: 0, py: 0.5, px: 0.5 } }}>
<TableBody>
<TableRow>
<TableCell sx={{ color: 'text.secondary', fontSize: 11, width: '40%' }}>Provider</TableCell>
<TableCell sx={{ fontFamily: 'monospace', fontSize: 11, color: 'text.primary', textAlign: 'right', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 0 }}>
{PROVIDER_LABELS[provider] ?? provider}
</TableCell>
</TableRow>
<TableRow>
<TableCell sx={{ color: 'text.secondary', fontSize: 11 }}></TableCell>
<TableCell sx={{ fontFamily: 'monospace', fontSize: 11, color: 'text.primary', textAlign: 'right', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 0 }}>
{model}
</TableCell>
</TableRow>
<TableRow>
<TableCell sx={{ color: 'text.secondary', fontSize: 11 }}></TableCell>
<TableCell sx={{ fontFamily: 'monospace', fontSize: 11, color: 'text.secondary', textAlign: 'right' }}>
{currentIteration} / {maxIterations}
</TableCell>
</TableRow>
{totalDuration > 0 && (
<TableRow>
<TableCell sx={{ color: 'text.secondary', fontSize: 11 }}></TableCell>
<TableCell sx={{ fontFamily: 'monospace', fontSize: 11, color: 'text.secondary', textAlign: 'right' }}>
{formatDuration(totalDuration)}
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</Box> </Box>
); );
} }