Files
MyGoNavi/frontend/src/components/ai/AIMCPClientStatusPanel.tsx
Syngnat c1d27448bc ♻️ refactor(ai): 拆分 MCP 客户端接入面板
- 将客户端选择和状态详情拆为独立组件

- 保留本机安装、远程桥接和快速配置展示行为

- 补充选择区和状态区渲染测试
2026-06-11 19:47:11 +08:00

171 lines
6.1 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 } from 'antd';
import { CopyOutlined, ReloadOutlined } from '@ant-design/icons';
import type { AIMCPClientInstallStatus } from '../../types';
import {
buildRemoteMCPClientQuickStart,
isRemoteMCPClientStatus,
} from '../../utils/mcpClientInstallStatus';
import type { OverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
import AIMCPRemoteQuickStartPanel from './AIMCPRemoteQuickStartPanel';
import {
getMCPClientStatusSummary,
getMCPClientStatusTone,
getSelectedMCPClientStateLine,
resolveMCPClientCommandName,
} from './mcpClientInstallPanelState';
interface AIMCPClientStatusPanelProps {
selectedStatus?: AIMCPClientInstallStatus;
selectedCommandText: string;
darkMode: boolean;
overlayTheme: OverlayWorkbenchTheme;
cardBorder: string;
statusLoading: boolean;
onRefreshStatus: () => void;
onCopyConfigPath: () => void;
onCopyLaunchCommand: () => void;
}
const AIMCPClientStatusPanel: React.FC<AIMCPClientStatusPanelProps> = ({
selectedStatus,
selectedCommandText,
darkMode,
overlayTheme,
cardBorder,
statusLoading,
onRefreshStatus,
onCopyConfigPath,
onCopyLaunchCommand,
}) => {
const selectedIsRemoteClient = isRemoteMCPClientStatus(selectedStatus);
const remoteQuickStart = selectedIsRemoteClient
? buildRemoteMCPClientQuickStart(selectedStatus)
: null;
return (
<div
style={{
padding: '12px 14px',
borderRadius: 12,
border: `1px solid ${cardBorder}`,
background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.78)',
display: 'flex',
flexDirection: 'column',
gap: 6,
}}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
<div style={{ fontWeight: 700, fontSize: 13, color: overlayTheme.titleText }}>
</div>
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.7 }}>
{selectedStatus?.displayName || '未选择客户端'}
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<div style={{ fontWeight: 700, fontSize: 13, color: overlayTheme.titleText }}>
{getMCPClientStatusSummary(selectedStatus)}
</div>
{selectedStatus && (
<div
style={{
padding: '3px 9px',
borderRadius: 999,
fontSize: 11,
fontWeight: 700,
color: getMCPClientStatusTone(selectedStatus, darkMode).color,
background: getMCPClientStatusTone(selectedStatus, darkMode).bg,
}}
>
{getMCPClientStatusTone(selectedStatus, darkMode).label}
</div>
)}
</div>
</div>
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.7 }}>
{getSelectedMCPClientStateLine(selectedStatus)}
</div>
{selectedIsRemoteClient && (
<div
style={{
padding: '10px 12px',
borderRadius: 10,
border: `1px solid ${darkMode ? 'rgba(56,189,248,0.22)' : 'rgba(14,165,233,0.18)'}`,
background: darkMode ? 'rgba(14,165,233,0.08)' : 'rgba(14,165,233,0.06)',
fontSize: 12,
color: overlayTheme.mutedText,
lineHeight: 1.7,
}}
>
Windows GoNavi Agent schema-only MCP DDL execute_sql使 GoNavi Streamable HTTP token
</div>
)}
{remoteQuickStart && (
<AIMCPRemoteQuickStartPanel
quickStart={remoteQuickStart}
darkMode={darkMode}
overlayTheme={overlayTheme}
cardBorder={cardBorder}
/>
)}
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.7 }}>
CLI {selectedIsRemoteClient
? `远程 Agent 不需要检测本机 ${resolveMCPClientCommandName(selectedStatus)} 命令`
: selectedStatus?.clientDetected
? `已检测到 ${resolveMCPClientCommandName(selectedStatus)}`
: `未检测到 ${resolveMCPClientCommandName(selectedStatus)},仍可先写配置`}
</div>
{selectedStatus?.clientPath && (
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6, fontFamily: 'var(--gn-font-mono)' }}>
{selectedStatus.clientPath}
</div>
)}
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.7 }}>
{selectedStatus?.message || '未检测到接入状态'}
</div>
{selectedStatus?.configPath && (
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6, fontFamily: 'var(--gn-font-mono)' }}>
{selectedStatus.configPath}
</div>
)}
{selectedCommandText && (
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6, fontFamily: 'var(--gn-font-mono)' }}>
{selectedCommandText}
</div>
)}
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<Button
size="small"
icon={<ReloadOutlined />}
loading={statusLoading}
onClick={onRefreshStatus}
style={{ borderRadius: 8 }}
>
</Button>
<Button
size="small"
icon={<CopyOutlined />}
disabled={!selectedStatus?.configPath}
onClick={onCopyConfigPath}
style={{ borderRadius: 8 }}
>
</Button>
<Button
size="small"
icon={<CopyOutlined />}
disabled={!selectedCommandText}
onClick={onCopyLaunchCommand}
style={{ borderRadius: 8 }}
>
</Button>
</div>
</div>
);
};
export default AIMCPClientStatusPanel;