feat: MetonaAI Desktop 初始项目
- Electron + React + TypeScript 架构 - 三栏布局: Sidebar | ChatPanel | DetailPanel - 9 个内置工具 (文件系统/网络/记忆/命令) - SQLite 持久化 (better-sqlite3) - MUI 暗色/亮色主题系统 - Agent Loop ReAct 状态机引擎 - DeepSeek / Agnes AI / Ollama Provider 适配器 - MCP 协议集成 - 系统托盘 + 全局快捷键 - Tailwind CSS v4 + Tailwind Merge - 修复: Sidebar 缺失 TextField 导入导致黑屏
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* ToolCallCard — 工具调用卡片
|
||||
*/
|
||||
|
||||
import { Box, Typography, Stack, Chip } from '@mui/material';
|
||||
import { Wrench, Clock, CheckCircle, XCircle, Ban, Loader2 } from 'lucide-react';
|
||||
import type { ToolCallInfo } from '@renderer/stores/agent-store';
|
||||
import { formatDuration } from '@renderer/lib/formatters';
|
||||
|
||||
interface ToolCallCardProps { toolCall: ToolCallInfo; }
|
||||
|
||||
const STATUS_ICONS = { pending: Clock, executing: Loader2, success: CheckCircle, error: XCircle, blocked: Ban };
|
||||
const STATUS_LABELS = { pending: '等待中', executing: '执行中', success: '成功', error: '失败', blocked: '已阻止' };
|
||||
const STATUS_COLORS: Record<string, string> = { pending: '#fbbf24', executing: '#a855f7', success: '#34d399', error: '#f87171', blocked: '#fb923c' };
|
||||
const CHIP_VARIANTS: Record<string, 'outlined' | 'filled'> = { pending: 'outlined', executing: 'filled', success: 'filled', error: 'filled', blocked: 'outlined' };
|
||||
|
||||
export function ToolCallCard({ toolCall }: ToolCallCardProps): React.JSX.Element {
|
||||
const Icon = STATUS_ICONS[toolCall.status];
|
||||
const color = STATUS_COLORS[toolCall.status];
|
||||
const label = STATUS_LABELS[toolCall.status];
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: 1.5, border: '1px solid', borderColor: 'divider', borderLeft: '3px solid', borderLeftColor: color,
|
||||
bgcolor: 'secondary.main', px: 1.5, py: 1.25, mb: 1,
|
||||
animation: toolCall.status === 'executing' ? 'pulse 2s infinite' : 'fadeInUp 300ms ease-out',
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 0.75 }}>
|
||||
<Wrench size={12} style={{ color: '#a855f7' }} />
|
||||
<Typography sx={{ fontSize: 12, fontWeight: 600, fontFamily: 'monospace', color: 'text.primary' }}>{toolCall.name}</Typography>
|
||||
<Chip
|
||||
icon={<Icon size={10} style={{ animation: toolCall.status === 'executing' ? 'spin 1s linear infinite' : 'none' }} />}
|
||||
label={label}
|
||||
size="small"
|
||||
variant={CHIP_VARIANTS[toolCall.status]}
|
||||
sx={{ height: 18, fontSize: 10, color, borderColor: color, '& .MuiChip-icon': { color } }}
|
||||
/>
|
||||
{toolCall.durationMs != null && (
|
||||
<Typography variant="caption" sx={{ ml: 'auto', color: 'text.secondary' }}>{formatDuration(toolCall.durationMs)}</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{Object.keys(toolCall.args).length > 0 && (
|
||||
<Box component="pre" sx={{ fontSize: 11, borderRadius: 1, px: 1, py: 0.75, overflowX: 'auto', bgcolor: 'background.default', color: 'text.secondary', fontFamily: "'SF Mono',monospace", maxHeight: 80, m: 0 }}>
|
||||
{JSON.stringify(toolCall.args, null, 2)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{toolCall.status === 'error' && toolCall.error && (
|
||||
<Typography variant="caption" sx={{ mt: 0.75, display: 'block', color: 'error.main' }}>{toolCall.error}</Typography>
|
||||
)}
|
||||
|
||||
{toolCall.status === 'success' && toolCall.result != null && (
|
||||
<Typography variant="caption" sx={{ mt: 0.75, display: 'block', color: 'text.secondary', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{typeof toolCall.result === 'string' ? toolCall.result.slice(0, 200) : JSON.stringify(toolCall.result).slice(0, 200)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user