refactor: mcp setting ui refactor

This commit is contained in:
Teo 2025-03-28 12:14:34 +08:00 committed by 亢奋猫
parent 29f7da1a4c
commit eddbae6f5e
2 changed files with 101 additions and 107 deletions

View File

@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingTitle } from '..' import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '..'
interface Props { interface Props {
server: MCPServer server: MCPServer
@ -163,70 +163,81 @@ const McpSettings: React.FC<Props> = ({ server }) => {
} }
return ( return (
<SettingContainer style={{ background: 'transparent' }}> <SettingContainer>
<SettingTitle> <SettingGroup style={{ marginBottom: 0 }}>
<Flex align="center" gap={8}> <SettingTitle>
<ServerName>{server?.name}</ServerName> <ServerName>{server?.name}</ServerName>
</Flex> <Flex align="center" gap={16}>
<Switch <Switch
value={server.isActive} value={server.isActive}
key={server.id} key={server.id}
loading={loadingServer === server.id} loading={loadingServer === server.id}
onChange={onToggleActive} onChange={onToggleActive}
/> />
</SettingTitle> <Button type="primary" size="small" onClick={onSave} loading={loading} disabled={!isFormChanged}>
<SettingDivider /> {t('common.save')}
<Form form={form} layout="vertical" onValuesChange={onFormValuesChange}> </Button>
<Form.Item name="name" label={t('settings.mcp.name')} rules={[{ required: true, message: '' }]}> </Flex>
<Input placeholder={t('common.name')} /> </SettingTitle>
</Form.Item> <SettingDivider />
<Form.Item name="description" label={t('settings.mcp.description')}> <Form
<TextArea rows={2} placeholder={t('common.description')} /> form={form}
</Form.Item> layout="vertical"
<Form.Item name="serverType" label={t('settings.mcp.type')} rules={[{ required: true }]} initialValue="stdio"> onValuesChange={onFormValuesChange}
<Radio.Group style={{
onChange={(e) => setServerType(e.target.value)} height: 'calc(100vh - var(--navbar-height) - 115px)',
options={[ overflowY: 'auto',
{ label: 'SSE', value: 'sse' }, width: 'calc(100% + 10px)',
{ label: 'STDIO', value: 'stdio' } paddingRight: '10px'
]} }}>
/> <Form.Item name="name" label={t('settings.mcp.name')} rules={[{ required: true, message: '' }]}>
</Form.Item> <Input placeholder={t('common.name')} />
{serverType === 'sse' && (
<Form.Item
name="baseUrl"
label={t('settings.mcp.url')}
rules={[{ required: serverType === 'sse', message: '' }]}
tooltip={t('settings.mcp.baseUrlTooltip')}>
<Input placeholder="http://localhost:3000/sse" />
</Form.Item> </Form.Item>
)} <Form.Item name="description" label={t('settings.mcp.description')}>
{serverType === 'stdio' && ( <TextArea rows={2} placeholder={t('common.description')} />
<> </Form.Item>
<Form.Item name="serverType" label={t('settings.mcp.type')} rules={[{ required: true }]} initialValue="stdio">
<Radio.Group
onChange={(e) => setServerType(e.target.value)}
options={[
{ label: 'SSE', value: 'sse' },
{ label: 'STDIO', value: 'stdio' }
]}
/>
</Form.Item>
{serverType === 'sse' && (
<Form.Item <Form.Item
name="command" name="baseUrl"
label={t('settings.mcp.command')} label={t('settings.mcp.url')}
rules={[{ required: serverType === 'stdio', message: '' }]}> rules={[{ required: serverType === 'sse', message: '' }]}
<Input placeholder="uvx or npx" /> tooltip={t('settings.mcp.baseUrlTooltip')}>
<Input placeholder="http://localhost:3000/sse" />
</Form.Item> </Form.Item>
)}
{serverType === 'stdio' && (
<>
<Form.Item
name="command"
label={t('settings.mcp.command')}
rules={[{ required: serverType === 'stdio', message: '' }]}>
<Input placeholder="uvx or npx" />
</Form.Item>
<Form.Item <Form.Item
name="args" name="args"
label={t('settings.mcp.args')} label={t('settings.mcp.args')}
tooltip={t('settings.mcp.argsTooltip')} tooltip={t('settings.mcp.argsTooltip')}
rules={[{ required: serverType === 'stdio', message: '' }]}> rules={[{ required: serverType === 'stdio', message: '' }]}>
<TextArea rows={3} placeholder={`arg1\narg2`} style={{ fontFamily: 'monospace' }} /> <TextArea rows={3} placeholder={`arg1\narg2`} style={{ fontFamily: 'monospace' }} />
</Form.Item> </Form.Item>
<Form.Item name="env" label={t('settings.mcp.env')} tooltip={t('settings.mcp.envTooltip')}> <Form.Item name="env" label={t('settings.mcp.env')} tooltip={t('settings.mcp.envTooltip')}>
<TextArea rows={3} placeholder={`KEY1=value1\nKEY2=value2`} style={{ fontFamily: 'monospace' }} /> <TextArea rows={3} placeholder={`KEY1=value1\nKEY2=value2`} style={{ fontFamily: 'monospace' }} />
</Form.Item> </Form.Item>
</> </>
)} )}
<Button type="primary" onClick={onSave} loading={loading} disabled={!isFormChanged}> </Form>
{t('common.save')} </SettingGroup>
</Button>
</Form>
</SettingContainer> </SettingContainer>
) )
} }

View File

@ -4,7 +4,7 @@ import { HStack } from '@renderer/components/Layout'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
import { useMCPServers } from '@renderer/hooks/useMCPServers' import { useMCPServers } from '@renderer/hooks/useMCPServers'
import type { MCPServer } from '@renderer/types' import type { MCPServer } from '@renderer/types'
import { Button, Input, Space, Spin, Table, Tag, Typography } from 'antd' import { Button, Card, Flex, Input, Space, Spin, Tag, Typography } from 'antd'
import { npxFinder } from 'npx-scope-finder' import { npxFinder } from 'npx-scope-finder'
import { type FC, useState } from 'react' import { type FC, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -128,48 +128,22 @@ const NpxSearch: FC = () => {
<Spin /> <Spin />
</div> </div>
) : searchResults.length > 0 ? ( ) : searchResults.length > 0 ? (
<Table searchResults.map((record) => (
dataSource={searchResults} <Card
columns={[ size="small"
{ key={record.npmLink}
title: t('settings.mcp.npx_list.package_name'), title={
dataIndex: 'name', <Typography.Title level={5} style={{ margin: 0 }}>
key: 'name', {record.name}
width: '200px' </Typography.Title>
}, }
{ extra={
title: t('settings.mcp.npx_list.description'), <Flex>
key: 'description', <Tag bordered={false} color="magenta">
ellipsis: true, v{record.version}
render: (_, record: SearchResult) => ( </Tag>
<Space direction="vertical" size="small" style={{ width: '100%' }}>
<Text ellipsis={{ tooltip: true }}>{record.description}</Text>
<Text ellipsis={{ tooltip: true }} type="secondary">
{t('settings.mcp.npx_list.usage')}: {record.usage}
</Text>
<Paragraph ellipsis={{ tooltip: true }}>
<Link href={record.npmLink} target="_blank" rel="noopener noreferrer">
{record.npmLink}
</Link>
</Paragraph>
</Space>
)
},
{
title: t('settings.mcp.npx_list.version'),
dataIndex: 'version',
key: 'version',
width: '100px',
align: 'center'
},
{
title: t('settings.mcp.npx_list.actions'),
key: 'actions',
width: '80px',
align: 'center',
render: (_, record: SearchResult) => (
<Button <Button
type="primary" type="text"
icon={<PlusOutlined />} icon={<PlusOutlined />}
size="small" size="small"
onClick={() => { onClick={() => {
@ -185,12 +159,21 @@ const NpxSearch: FC = () => {
addMCPServer(tempServer) addMCPServer(tempServer)
}} }}
/> />
) </Flex>
} }>
]} <Space direction="vertical" size="small">
pagination={false} <Text ellipsis={{ tooltip: true }}>{record.description}</Text>
bordered <Text ellipsis={{ tooltip: true }} type="secondary">
/> {t('settings.mcp.npx_list.usage')}: {record.usage}
</Text>
<Text ellipsis={{ tooltip: true }}>
<Link href={record.npmLink} target="_blank" rel="noopener noreferrer">
{record.npmLink}
</Link>
</Text>
</Space>
</Card>
))
) : null} ) : null}
</Space> </Space>
</SettingGroup> </SettingGroup>