Files
MyGoNavi/frontend/src/components/ai/AIMCPServerGuidePanel.tsx
Syngnat 156631c263 feat(ai): 完善 MCP 新增字段填写提示
- 增加 command、args、env、timeout 的应填与勿填对照

- 抽取 MCP 字段指南卡片复用速查与表单说明

- 补充 AI 设置 MCP 区域渲染测试
2026-06-10 18:19:10 +08:00

162 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
MCP_TROUBLESHOOTING_GUIDES,
} from '../../utils/mcpServerGuidance';
import AIMCPCommandDraftPreview from './AIMCPCommandDraftPreview';
import AIMCPFieldGuideCard from './AIMCPFieldGuideCard';
import { 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) => (
<AIMCPFieldGuideCard
key={item.key}
item={item}
cardBorder={cardBorder}
darkMode={darkMode}
overlayTheme={overlayTheme}
/>
))}
</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)}>
MCP
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))', gap: 10 }}>
{MCP_TROUBLESHOOTING_GUIDES.map((item) => (
<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={{ fontSize: 12, fontWeight: 700, color: overlayTheme.titleText }}>{item.symptom}</div>
<div style={{ fontSize: 12, lineHeight: 1.6, color: overlayTheme.titleText }}>
{item.likelyCause}
</div>
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>{item.fix}</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 / / Unix KEY=VALUE Windows PowerShell $env:KEY=VALUE; cmd set KEY=VALUE &&
</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 / $env:KEY=VALUE; / set 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;