refactor: 移除多余依赖,统一 MUI 为唯一 UI 库

- 移除 radix-ui、clsx、tailwind-merge、class-variance-authority、react-router-dom

- 前后端全面适配 MUI 组件,清理残留 Tailwind 工具类引用

- 优化 Agent Loop 引擎、IPC 处理器、Provider Adapter 等后端模块

- 新增 Agent 网络工具通用设计文档 v2
This commit is contained in:
thzxx
2026-07-05 19:15:48 +08:00
parent ba85328c4f
commit f4532a2bb2
50 changed files with 1474 additions and 2042 deletions
+14 -13
View File
@@ -22,7 +22,6 @@ interface ContextMenuItem {
}
interface ContextMenuProps {
type: ContextMenuType;
x: number;
y: number;
onClose: () => void;
@@ -31,13 +30,13 @@ interface ContextMenuProps {
export function ContextMenu({ x, y, onClose, items }: ContextMenuProps): React.JSX.Element {
return (
<Menu open onClose={onClose} anchorReference="anchorPosition" anchorPosition={{ top: y, left: x }} PaperProps={{ sx: { minWidth: 160 } }}>
<Menu open onClose={onClose} anchorReference="anchorPosition" anchorPosition={{ top: y, left: x }} slotProps={{ paper: { sx: { minWidth: 160 } } }}>
{items.map((item) => {
const Icon = item.icon;
return (
<MenuItem key={item.id} onClick={() => { if (!item.disabled) { item.action(); onClose(); } }} disabled={item.disabled} dense>
<ListItemIcon sx={{ minWidth: 28 }}><Icon size={12} /></ListItemIcon>
<ListItemText primaryTypographyProps={{ fontSize: 12 }}>{item.label}</ListItemText>
<ListItemText slotProps={{ primary: { sx: { fontSize: 12 } } }}>{item.label}</ListItemText>
</MenuItem>
);
})}
@@ -53,7 +52,7 @@ export function createContextMenuItems(type: ContextMenuType, data?: unknown): C
case 'message': {
const content = (data as { content?: string })?.content ?? '';
return [
{ id: 'copy', icon: Copy, label: '复制', action: () => navigator.clipboard.writeText(content).catch(() => {}) },
{ id: 'copy', icon: Copy, label: '复制', action: () => navigator.clipboard.writeText(content).catch((err) => console.error('[Clipboard]', err)) },
{ id: 'quote', icon: Quote, label: '引用回复', action: () => {
const input = document.querySelector<HTMLTextAreaElement>('[data-chat-input]');
if (input) { input.value = content.split('\n').map((l: string) => `> ${l}`).join('\n') + '\n\n'; input.dispatchEvent(new Event('input', { bubbles: true })); input.focus(); }
@@ -64,9 +63,9 @@ export function createContextMenuItems(type: ContextMenuType, data?: unknown): C
case 'tool-call': {
const tc = data as { args?: Record<string, unknown>; result?: unknown } | undefined;
return [
{ id: 'view-params', icon: Eye, label: '查看参数', action: () => { if (tc?.args) navigator.clipboard.writeText(JSON.stringify(tc.args, null, 2)).catch(() => {}); }},
{ id: 'view-result', icon: Eye, label: '查看完整结果', action: () => { if (tc?.result) navigator.clipboard.writeText(JSON.stringify(tc.result, null, 2)).catch(() => {}); }},
{ id: 'copy-result', icon: Copy, label: '复制结果', action: () => { if (tc?.result) navigator.clipboard.writeText(typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result)).catch(() => {}); }},
{ id: 'view-params', icon: Eye, label: '查看参数', action: () => { if (tc?.args) navigator.clipboard.writeText(JSON.stringify(tc.args, null, 2)).catch((err) => console.error('[Clipboard]', err)); }},
{ id: 'view-result', icon: Eye, label: '查看完整结果', action: () => { if (tc?.result) navigator.clipboard.writeText(JSON.stringify(tc.result, null, 2)).catch((err) => console.error('[Clipboard]', err)); }},
{ id: 'copy-result', icon: Copy, label: '复制结果', action: () => { if (tc?.result) navigator.clipboard.writeText(typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result)).catch((err) => console.error('[Clipboard]', err)); }},
{ id: 're-execute', icon: RotateCcw, label: '重新执行', action: () => {
const m = [...useAgentStore.getState().messages].reverse().find((m) => m.role === 'user');
if (m) useAgentStore.getState().sendMessage(m.content);
@@ -78,7 +77,8 @@ export function createContextMenuItems(type: ContextMenuType, data?: unknown): C
const sid = (data as { sessionId?: string })?.sessionId;
return [
{ id: 'rename', icon: Edit, label: '重命名', action: () => {
if (sid) { const t = prompt('新会话名称:'); if (t?.trim()) { window.metona?.sessions.rename(sid, t.trim()); useSessionStore.getState().updateSession(sid, { title: t.trim() }); } }
// window.prompt may be unreliable in Electron; fall back to sidebar double-click rename
if (sid) { try { const t = prompt('新会话名称:'); if (t?.trim()) { window.metona?.sessions.rename(sid, t.trim()); useSessionStore.getState().updateSession(sid, { title: t.trim() }); } } catch { /* prompt unavailable, user can rename via sidebar double-click */ } }
}},
{ id: 'pin', icon: Pin, label: '置顶', action: () => {
if (sid) { const s = useSessionStore.getState().sessions.find((x) => x.id === sid); if (s) { window.metona?.sessions.pin(sid, !s.pinned); useSessionStore.getState().pinSession(sid, !s.pinned); } }
@@ -88,10 +88,11 @@ export function createContextMenuItems(type: ContextMenuType, data?: unknown): C
if (sid) window.metona?.sessions.getMessages(sid).then((msgs) => {
const b = new Blob([JSON.stringify(msgs, null, 2)], { type: 'application/json' });
const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = `session-${sid}.json`; a.click();
}).catch(() => {});
}).catch((err) => console.error('[ContextMenu]', err));
}},
{ id: 'delete', icon: Trash2, label: '删除', action: () => {
if (sid && confirm('确定删除此会话?')) { window.metona?.sessions.delete(sid); useSessionStore.getState().removeSession(sid); }
// window.confirm may be unreliable in Electron
if (sid) { try { if (confirm('确定删除此会话?')) { window.metona?.sessions.delete(sid); useSessionStore.getState().removeSession(sid); } } catch { /* confirm unavailable */ } }
}},
];
}
@@ -99,7 +100,7 @@ export function createContextMenuItems(type: ContextMenuType, data?: unknown): C
case 'code-block': {
const code = (data as { code?: string })?.code;
return [
{ id: 'copy-code', icon: Copy, label: '复制代码', action: () => { if (code) navigator.clipboard.writeText(code).catch(() => {}); }},
{ id: 'copy-code', icon: Copy, label: '复制代码', action: () => { if (code) navigator.clipboard.writeText(code).catch((err) => console.error('[Clipboard]', err)); }},
{ id: 'open-editor', icon: Code, label: '在编辑器中打开', action: () => { if (code) window.open(URL.createObjectURL(new Blob([code], { type: 'text/plain' })), '_blank'); }},
];
}
@@ -107,9 +108,9 @@ export function createContextMenuItems(type: ContextMenuType, data?: unknown): C
case 'trace-step': {
const step = data as { thought?: string; toolCalls?: Array<{ name: string; args: Record<string, unknown> }> } | undefined;
return [
{ id: 'copy-thought', icon: Copy, label: '复制 Thought', action: () => { if (step?.thought) navigator.clipboard.writeText(step.thought).catch(() => {}); }},
{ id: 'copy-thought', icon: Copy, label: '复制 Thought', action: () => { if (step?.thought) navigator.clipboard.writeText(step.thought).catch((err) => console.error('[Clipboard]', err)); }},
{ id: 'copy-params', icon: Copy, label: '复制工具参数', action: () => {
if (step?.toolCalls) navigator.clipboard.writeText(step.toolCalls.map((tc) => `${tc.name}: ${JSON.stringify(tc.args, null, 2)}`).join('\n')).catch(() => {});
if (step?.toolCalls) navigator.clipboard.writeText(step.toolCalls.map((tc) => `${tc.name}: ${JSON.stringify(tc.args, null, 2)}`).join('\n')).catch((err) => console.error('[Clipboard]', err));
}},
{ id: 'export', icon: ExternalLink, label: '导出步骤详情', action: () => {
if (step) { const b = new Blob([JSON.stringify(step, null, 2)], { type: 'application/json' }); const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = `trace-step-${Date.now()}.json`; a.click(); }