♻️ refactor(ai-mcp): 拆分 MCP 服务卡片并收敛表单逻辑

This commit is contained in:
Syngnat
2026-06-09 08:49:00 +08:00
parent 86095b5bf1
commit 8529fbd9e2
4 changed files with 476 additions and 349 deletions

View File

@@ -0,0 +1,156 @@
import React from 'react';
import { Button, Input } from 'antd';
import type { OverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
import type { ParseMCPCommandDraftResult } from '../../utils/mcpCommandDraft';
import {
MCP_COMMAND_EXAMPLES,
MCP_COMMAND_PARSE_EXAMPLE,
MCP_FIELD_GUIDES,
MCP_SERVER_FILL_STEPS,
} from '../../utils/mcpServerGuidance';
import AIMCPCommandDraftPreview from './AIMCPCommandDraftPreview';
import { buildMCPFieldTone, buildMCPHintStyle, mcpLabelStyle } from './AIMCPHelpBlock';
interface AIMCPServerGuidePanelProps {
cardBorder: string;
inputBg: string;
darkMode: boolean;
overlayTheme: OverlayWorkbenchTheme;
rawCommandDraft: string;
parsedCommandDraft: ParseMCPCommandDraftResult;
onApplyCommandDraft: () => void;
onRawCommandDraftChange: (value: string) => void;
}
const AIMCPServerGuidePanel: React.FC<AIMCPServerGuidePanelProps> = ({
cardBorder,
inputBg,
darkMode,
overlayTheme,
rawCommandDraft,
parsedCommandDraft,
onApplyCommandDraft,
onRawCommandDraftChange,
}) => (
<>
<div style={{ padding: '10px 12px', borderRadius: 10, border: `1px dashed ${cardBorder}`, background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.7)' }}>
<div style={{ ...mcpLabelStyle, color: overlayTheme.titleText }}></div>
<div style={{ ...buildMCPHintStyle(overlayTheme.mutedText), marginTop: 4 }}>
{' '}
<code style={{ fontFamily: 'var(--gn-font-mono)' }}>{MCP_COMMAND_EXAMPLES.join(' / ')}</code>
</div>
</div>
<div style={{ padding: '12px 14px', borderRadius: 12, border: `1px solid ${cardBorder}`, background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.76)', display: 'flex', flexDirection: 'column', gap: 8 }}>
<div style={{ ...mcpLabelStyle, color: overlayTheme.titleText }}></div>
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
{MCP_SERVER_FILL_STEPS.map((item) => (
<span
key={item.step}
style={{
padding: '4px 10px',
borderRadius: 999,
fontSize: 12,
color: overlayTheme.titleText,
background: darkMode ? 'rgba(255,255,255,0.06)' : 'rgba(15,23,42,0.05)',
}}
>
{item.step}. {item.title}
</span>
))}
</div>
</div>
<div style={{ padding: '12px 14px', borderRadius: 12, border: `1px solid ${cardBorder}`, background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.76)', display: 'flex', flexDirection: 'column', gap: 10 }}>
<div style={{ ...mcpLabelStyle, color: overlayTheme.titleText }}></div>
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))', gap: 10 }}>
{MCP_FIELD_GUIDES.map((item) => {
const tone = buildMCPFieldTone(item.fieldState, darkMode);
return (
<div
key={item.key}
style={{
padding: '10px 12px',
borderRadius: 10,
border: `1px solid ${cardBorder}`,
background: darkMode ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.78)',
display: 'flex',
flexDirection: 'column',
gap: 6,
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<div style={{ fontSize: 12, fontWeight: 700, color: overlayTheme.titleText }}>{item.title}</div>
<span
style={{
padding: '2px 8px',
borderRadius: 999,
fontSize: 11,
fontWeight: 700,
color: tone.color,
background: tone.bg,
}}
>
{tone.label}
</span>
</div>
<div style={{ fontSize: 12, lineHeight: 1.6, color: overlayTheme.titleText }}>{item.summary}</div>
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>{item.detail}</div>
{item.example ? (
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
{' '}
<code style={{ fontFamily: 'var(--gn-font-mono)' }}>{item.example}</code>
</div>
) : null}
</div>
);
})}
</div>
</div>
<div style={{ padding: '12px', borderRadius: 12, border: `1px solid ${cardBorder}`, background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.76)', display: 'flex', flexDirection: 'column', gap: 8 }}>
<div style={{ ...mcpLabelStyle, color: overlayTheme.titleText }}></div>
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
GoNavi / / README
</div>
<Input.TextArea
rows={2}
value={rawCommandDraft}
onChange={(event) => onRawCommandDraftChange(event.target.value)}
placeholder={`直接粘贴完整命令,例如:\n${MCP_COMMAND_PARSE_EXAMPLE}`}
style={{ borderRadius: 10, background: inputBg, border: `1px solid ${cardBorder}`, fontFamily: 'var(--gn-font-mono)' }}
/>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
<div style={{ ...buildMCPHintStyle(parsedCommandDraft.ok ? overlayTheme.mutedText : '#dc2626') }}>
{rawCommandDraft.trim()
? parsedCommandDraft.ok && parsedCommandDraft.draft
? `将解析为:命令 ${parsedCommandDraft.draft.command},参数 ${parsedCommandDraft.draft.args.length} 个,环境变量 ${Object.keys(parsedCommandDraft.draft.env).length} 个。`
: parsedCommandDraft.error
: '支持带引号路径、带空格参数,以及命令前缀的 KEY=VALUE 环境变量。'}
</div>
<Button onClick={onApplyCommandDraft} disabled={!parsedCommandDraft.ok} style={{ borderRadius: 10 }}>
</Button>
</div>
{parsedCommandDraft.ok && parsedCommandDraft.draft && rawCommandDraft.trim() && (
<AIMCPCommandDraftPreview
draft={parsedCommandDraft.draft}
darkMode={darkMode}
overlayTheme={overlayTheme}
cardBorder={cardBorder}
/>
)}
</div>
</>
);
export default AIMCPServerGuidePanel;