fix list align

This commit is contained in:
LaZzyMan 2026-03-06 14:13:57 +08:00
parent fae195eaa8
commit 892fe4f706
4 changed files with 88 additions and 65 deletions

View file

@ -407,7 +407,9 @@ export const MCPManagementDialog: React.FC<MCPManagementDialogProps> = ({
return (
<Box>
<Text bold>{headerText}</Text>
<Text color={theme.text.accent} bold>
{headerText}
</Text>
</Box>
);
}, [getCurrentStep, selectedServer, selectedTool]);
@ -415,11 +417,7 @@ export const MCPManagementDialog: React.FC<MCPManagementDialogProps> = ({
// Render step content
const renderStepContent = useCallback(() => {
if (isLoading) {
return (
<Box>
<Text color={theme.text.secondary}>{t('Loading...')}</Text>
</Box>
);
return <Text color={theme.text.secondary}>{t('Loading...')}</Text>;
}
const currentStep = getCurrentStep();

View file

@ -17,6 +17,9 @@ import {
formatServerCommand,
} from '../utils.js';
// 标签列宽度
const LABEL_WIDTH = 15;
type ServerAction = 'view-tools' | 'reconnect' | 'toggle-disable';
export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
@ -91,8 +94,10 @@ export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
{/* 服务器详情 */}
<Box flexDirection="column">
<Box>
<Text color={theme.text.primary}>{t('Status:')}</Text>
<Box marginLeft={2}>
<Box width={LABEL_WIDTH}>
<Text color={theme.text.primary}>{t('Status:')}</Text>
</Box>
<Box>
<Text
color={
statusColor === 'green'
@ -110,9 +115,11 @@ export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
</Box>
</Box>
<Box marginTop={1}>
<Text color={theme.text.primary}>{t('Source:')}</Text>
<Box marginLeft={2}>
<Box>
<Box width={LABEL_WIDTH}>
<Text color={theme.text.primary}>{t('Source:')}</Text>
</Box>
<Box>
<Text color={theme.text.secondary}>
{server.scope === 'user'
? t('User Settings')
@ -123,25 +130,31 @@ export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
</Box>
</Box>
<Box marginTop={1}>
<Text color={theme.text.primary}>{t('Command:')}</Text>
<Box marginLeft={2}>
<Box>
<Box width={LABEL_WIDTH}>
<Text color={theme.text.primary}>{t('Command:')}</Text>
</Box>
<Box>
<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}>
<Box>
<Box width={LABEL_WIDTH}>
<Text color={theme.text.primary}>{t('Working Directory:')}</Text>
</Box>
<Box>
<Text wrap="truncate">{server.config.cwd}</Text>
</Box>
</Box>
)}
<Box marginTop={1}>
<Text color={theme.text.primary}>{t('Capabilities:')}</Text>
<Box marginLeft={2}>
<Box>
<Box width={LABEL_WIDTH}>
<Text color={theme.text.primary}>{t('Capabilities:')}</Text>
</Box>
<Box>
<Text>
{server.toolCount > 0 ? t('tools') : ''}
{server.toolCount > 0 && server.promptCount > 0 ? ', ' : ''}
@ -150,9 +163,11 @@ export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
</Box>
</Box>
<Box marginTop={1}>
<Text color={theme.text.primary}>{t('Tools:')}</Text>
<Box marginLeft={2}>
<Box>
<Box width={LABEL_WIDTH}>
<Text color={theme.text.primary}>{t('Tools:')}</Text>
</Box>
<Box>
<Text>
{server.toolCount}{' '}
{server.toolCount === 1 ? t('tool') : t('tools')}
@ -168,9 +183,11 @@ export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
</Box>
{server.errorMessage && (
<Box marginTop={1}>
<Text color={theme.status.error}>{t('Error:')}</Text>
<Box marginLeft={2}>
<Box>
<Box width={LABEL_WIDTH}>
<Text color={theme.status.error}>{t('Error:')}</Text>
</Box>
<Box>
<Text color={theme.status.error} wrap="wrap">
{server.errorMessage}
</Text>
@ -180,7 +197,7 @@ export const ServerDetailStep: React.FC<ServerDetailStepProps> = ({
</Box>
{/* 操作列表 */}
<Box marginTop={1}>
<Box>
<RadioButtonSelect<ServerAction>
items={actions}
onHighlight={(value: ServerAction) => setSelectedAction(value)}

View file

@ -27,6 +27,14 @@ export const ServerListStep: React.FC<ServerListStepProps> = ({
[servers],
);
// 动态计算服务器名称列的最大宽度(基于实际内容)
const serverNameWidth = useMemo(() => {
if (servers.length === 0) return 20;
const maxLength = Math.max(...servers.map((s) => s.name.length));
// 最小 20最大 35留一些余量
return Math.min(Math.max(maxLength + 2, 20), 35);
}, [servers]);
// 计算扁平化的服务器列表用于导航
const flatServers = useMemo(() => {
const result: MCPServerDisplayInfo[] = [];
@ -119,13 +127,19 @@ export const ServerListStep: React.FC<ServerListStepProps> = ({
{isSelected ? '' : ' '}
</Text>
</Box>
<Text
color={isSelected ? theme.text.accent : theme.text.primary}
wrap="truncate"
>
{server.name}
</Text>
{/* 服务器名称 - 固定宽度 */}
<Box width={serverNameWidth}>
<Text
color={
isSelected ? theme.text.accent : theme.text.primary
}
wrap="truncate"
>
{server.name}
</Text>
</Box>
<Text color={theme.text.secondary}> · </Text>
{/* 状态图标和文本 */}
<Text
color={
statusColor === 'green'
@ -135,19 +149,7 @@ export const ServerListStep: React.FC<ServerListStepProps> = ({
: theme.status.error
}
>
{getStatusIcon(server.status)}
</Text>
<Text
color={
statusColor === 'green'
? theme.status.success
: statusColor === 'yellow'
? theme.status.warning
: theme.status.error
}
>
{' '}
{t(server.status)}
{getStatusIcon(server.status)} {t(server.status)}
</Text>
{/* 显示 Scope 和禁用状态 */}
<Text color={theme.text.secondary}> [{server.scope}]</Text>
@ -172,7 +174,7 @@ export const ServerListStep: React.FC<ServerListStepProps> = ({
{/* 提示信息 */}
{servers.some((s) => s.status === 'disconnected') && (
<Box marginTop={1}>
<Box>
<Text color={theme.status.warning}>
{t('Run qwen --debug to see error logs')}
</Text>

View file

@ -20,6 +20,14 @@ export const ToolListStep: React.FC<ToolListStepProps> = ({
}) => {
const [selectedIndex, setSelectedIndex] = useState(0);
// 动态计算工具名称列的最大宽度(基于实际内容)
const toolNameWidth = useMemo(() => {
if (tools.length === 0) return 30;
const maxLength = Math.max(...tools.map((t) => t.name.length));
// 最小 30最大 50留一些余量
return Math.min(Math.max(maxLength + 2, 30), 50);
}, [tools]);
// 计算可视区域的起始索引(滚动窗口)
const scrollOffset = useMemo(() => {
if (tools.length <= VISIBLE_TOOLS_COUNT) {
@ -97,6 +105,7 @@ export const ToolListStep: React.FC<ToolListStepProps> = ({
return (
<Box key={tool.name}>
{/* 选择器和序号 */}
<Box minWidth={4}>
<Text
color={isSelected ? theme.text.accent : theme.text.primary}
@ -105,28 +114,25 @@ export const ToolListStep: React.FC<ToolListStepProps> = ({
</Text>
<Text color={theme.text.secondary}>{actualIndex + 1}.</Text>
</Box>
<Text
color={isSelected ? theme.text.accent : theme.text.primary}
wrap="truncate"
>
{tool.name}
</Text>
{/* 工具名称 - 固定宽度 */}
<Box width={toolNameWidth}>
<Text
color={isSelected ? theme.text.accent : theme.text.primary}
wrap="truncate"
>
{tool.name}
</Text>
</Box>
{/* 显示无效工具警告 */}
{!tool.isValid && (
<>
<Text color={theme.text.secondary}> </Text>
<Text color={theme.status.warning}>
{t('invalid: {{reason}}', {
reason: tool.invalidReason || t('unknown'),
})}
</Text>
</>
<Text color={theme.status.warning}>
{t('invalid: {{reason}}', {
reason: tool.invalidReason || t('unknown'),
})}
</Text>
)}
{annotations && tool.isValid && (
<>
<Text color={theme.text.secondary}> </Text>
<Text color={theme.text.secondary}>{annotations}</Text>
</>
<Text color={theme.text.secondary}>{annotations}</Text>
)}
</Box>
);