refactor: mcp setting ui refactor
This commit is contained in:
parent
29f7da1a4c
commit
eddbae6f5e
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user