mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 12:40:44 +00:00
feat: add mcp dialog
This commit is contained in:
parent
51fdf3c16a
commit
f64f08d8a1
17 changed files with 1453 additions and 9 deletions
183
packages/cli/src/ui/components/mcp/steps/ServerDetailStep.tsx
Normal file
183
packages/cli/src/ui/components/mcp/steps/ServerDetailStep.tsx
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { theme } from '../../../semantic-colors.js';
|
||||
import { useKeypress } from '../../../hooks/useKeypress.js';
|
||||
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
||||
import { t } from '../../../../i18n/index.js';
|
||||
import type { ServerDetailStepProps } from '../types.js';
|
||||
import {
|
||||
getStatusColor,
|
||||
getStatusIcon,
|
||||
formatServerCommand,
|
||||
} from '../utils.js';
|
||||
|
||||
type ServerAction = 'view-tools' | 'reconnect' | 'disable';
|
||||
|
||||
export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
|
||||
server,
|
||||
onViewTools,
|
||||
onReconnect,
|
||||
onDisable,
|
||||
onBack,
|
||||
}) => {
|
||||
const [selectedAction, setSelectedAction] =
|
||||
useState<ServerAction>('view-tools');
|
||||
|
||||
const statusColor = server ? getStatusColor(server.status) : 'gray';
|
||||
|
||||
const actions = [
|
||||
{
|
||||
key: 'view-tools',
|
||||
get label() {
|
||||
return t('View tools');
|
||||
},
|
||||
value: 'view-tools' as const,
|
||||
},
|
||||
{
|
||||
key: 'reconnect',
|
||||
get label() {
|
||||
return t('Reconnect');
|
||||
},
|
||||
value: 'reconnect' as const,
|
||||
},
|
||||
{
|
||||
key: 'disable',
|
||||
get label() {
|
||||
return t('Disable');
|
||||
},
|
||||
value: 'disable' as const,
|
||||
},
|
||||
];
|
||||
|
||||
useKeypress(
|
||||
(key) => {
|
||||
if (key.name === 'escape') {
|
||||
onBack();
|
||||
} else if (key.name === 'return') {
|
||||
switch (selectedAction) {
|
||||
case 'view-tools':
|
||||
onViewTools();
|
||||
break;
|
||||
case 'reconnect':
|
||||
onReconnect?.();
|
||||
break;
|
||||
case 'disable':
|
||||
onDisable?.();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ isActive: true },
|
||||
);
|
||||
|
||||
if (!server) {
|
||||
return (
|
||||
<Box>
|
||||
<Text color={theme.status.error}>{t('No server selected')}</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" gap={1}>
|
||||
{/* 服务器详情 */}
|
||||
<Box flexDirection="column">
|
||||
<Box>
|
||||
<Text color={theme.text.primary}>{t('Status:')}</Text>
|
||||
<Box marginLeft={2}>
|
||||
<Text
|
||||
color={
|
||||
statusColor === 'green'
|
||||
? theme.status.success
|
||||
: statusColor === 'yellow'
|
||||
? theme.status.warning
|
||||
: theme.status.error
|
||||
}
|
||||
>
|
||||
{getStatusIcon(server.status)} {t(server.status)}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.primary}>{t('Command:')}</Text>
|
||||
<Box marginLeft={2}>
|
||||
<Text wrap="truncate">{formatServerCommand(server)}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{server.config.cwd && (
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.primary}>{t('Working Directory:')}</Text>
|
||||
<Box marginLeft={2}>
|
||||
<Text wrap="truncate">{server.config.cwd}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.primary}>{t('Capabilities:')}</Text>
|
||||
<Box marginLeft={2}>
|
||||
<Text>
|
||||
{server.toolCount > 0 ? t('tools') : ''}
|
||||
{server.toolCount > 0 && server.promptCount > 0 ? ', ' : ''}
|
||||
{server.promptCount > 0 ? t('prompts') : ''}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.primary}>{t('Tools:')}</Text>
|
||||
<Box marginLeft={2}>
|
||||
<Text>
|
||||
{server.toolCount}{' '}
|
||||
{server.toolCount === 1 ? t('tool') : t('tools')}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{server.errorMessage && (
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.status.error}>{t('Error:')}</Text>
|
||||
<Box marginLeft={2}>
|
||||
<Text color={theme.status.error} wrap="wrap">
|
||||
{server.errorMessage}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* 操作列表 */}
|
||||
<Box marginTop={1}>
|
||||
<RadioButtonSelect<ServerAction>
|
||||
items={actions}
|
||||
onHighlight={(value: ServerAction) => setSelectedAction(value)}
|
||||
onSelect={(value: ServerAction) => {
|
||||
switch (value) {
|
||||
case 'view-tools':
|
||||
onViewTools();
|
||||
break;
|
||||
case 'reconnect':
|
||||
onReconnect?.();
|
||||
break;
|
||||
case 'disable':
|
||||
onDisable?.();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue