feat(MCPSettings): enhance server management with segmented control and improved layout
This commit is contained in:
parent
a530ce652e
commit
c884b11f01
@ -142,7 +142,7 @@ const McpSettings: React.FC<Props> = ({ server }) => {
|
||||
const mcpServer: MCPServer = {
|
||||
id: server.id,
|
||||
name: values.name,
|
||||
type: values.serverType,
|
||||
type: values.serverType || server.type,
|
||||
description: values.description,
|
||||
isActive: values.isActive,
|
||||
registryUrl: values.registryUrl
|
||||
@ -343,22 +343,26 @@ const McpSettings: React.FC<Props> = ({ server }) => {
|
||||
paddingRight: '10px'
|
||||
}}>
|
||||
<Form.Item name="name" label={t('settings.mcp.name')} rules={[{ required: true, message: '' }]}>
|
||||
<Input placeholder={t('common.name')} />
|
||||
<Input placeholder={t('common.name')} disabled={server.type === 'inMemory'} />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label={t('settings.mcp.description')}>
|
||||
<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)}
|
||||
disabled={server.type === 'inMemory'}
|
||||
options={[
|
||||
{ label: t('settings.mcp.stdio'), value: 'stdio' },
|
||||
{ label: t('settings.mcp.sse'), value: 'sse' },
|
||||
{ label: t('settings.mcp.inMemory'), value: 'inMemory' }
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
{server.type !== 'inMemory' && (
|
||||
<Form.Item
|
||||
name="serverType"
|
||||
label={t('settings.mcp.type')}
|
||||
rules={[{ required: true }]}
|
||||
initialValue="stdio">
|
||||
<Radio.Group
|
||||
onChange={(e) => setServerType(e.target.value)}
|
||||
options={[
|
||||
{ label: t('settings.mcp.stdio'), value: 'stdio' },
|
||||
{ label: t('settings.mcp.sse'), value: 'sse' }
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
{serverType === 'sse' && (
|
||||
<Form.Item
|
||||
name="baseUrl"
|
||||
|
||||
@ -2,7 +2,7 @@ import { CodeOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons'
|
||||
import { nanoid } from '@reduxjs/toolkit'
|
||||
import DragableList from '@renderer/components/DragableList'
|
||||
import IndicatorLight from '@renderer/components/IndicatorLight'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import { HStack, VStack } from '@renderer/components/Layout'
|
||||
import ListItem from '@renderer/components/ListItem'
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
@ -10,7 +10,7 @@ import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||
import { EventEmitter } from '@renderer/services/EventService'
|
||||
import { initializeMCPServers } from '@renderer/store/mcp'
|
||||
import { MCPServer } from '@renderer/types'
|
||||
import { Dropdown, MenuProps } from 'antd'
|
||||
import { Dropdown, MenuProps, Segmented } from 'antd'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -29,6 +29,17 @@ const MCPSettings: FC = () => {
|
||||
const [route, setRoute] = useState<'npx-search' | 'mcp-install' | null>(null)
|
||||
const { theme } = useTheme()
|
||||
const dispatch = useDispatch()
|
||||
const [mcpListType, setMcpListType] = useState<'system' | 'user'>('system')
|
||||
|
||||
const systemServers = mcpServers.filter((server) => {
|
||||
return server.type === 'inMemory'
|
||||
})
|
||||
|
||||
const userServers = mcpServers.filter((server) => {
|
||||
return server.type !== 'inMemory'
|
||||
})
|
||||
|
||||
const servers = mcpListType === 'system' ? systemServers : userServers
|
||||
|
||||
useEffect(() => {
|
||||
const unsubs = [
|
||||
@ -121,45 +132,63 @@ const MCPSettings: FC = () => {
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<McpList>
|
||||
<ListItem
|
||||
key="add"
|
||||
title={t('settings.mcp.addServer')}
|
||||
active={false}
|
||||
onClick={onAddMcpServer}
|
||||
icon={<PlusOutlined />}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
style={{ marginBottom: 5 }}
|
||||
/>
|
||||
<DragableList list={mcpServers} onUpdate={updateMcpServers}>
|
||||
{(server: MCPServer) => (
|
||||
<Dropdown menu={{ items: getMenuItems(server) }} trigger={['contextMenu']} key={server.id}>
|
||||
<div>
|
||||
<ListItem
|
||||
key={server.id}
|
||||
title={server.name}
|
||||
active={selectedMcpServer?.id === server.id}
|
||||
onClick={() => {
|
||||
setSelectedMcpServer(server)
|
||||
setRoute(null)
|
||||
}}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
icon={<CodeOutlined />}
|
||||
rightContent={
|
||||
<IndicatorLight
|
||||
size={6}
|
||||
color={server.isActive ? 'green' : 'var(--color-text-3)'}
|
||||
animation={server.isActive}
|
||||
shadow={false}
|
||||
style={{ marginRight: 4 }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Dropdown>
|
||||
<McpListContainer>
|
||||
<McpListHeader>
|
||||
<Segmented
|
||||
size="middle"
|
||||
style={{ width: '100%' }}
|
||||
block
|
||||
shape="round"
|
||||
value={mcpListType}
|
||||
options={[
|
||||
{ value: 'user', label: '我的' },
|
||||
{ value: 'system', label: '系统' }
|
||||
]}
|
||||
onChange={(value) => setMcpListType(value as 'system' | 'user')}
|
||||
/>
|
||||
</McpListHeader>
|
||||
<McpList>
|
||||
{mcpListType === 'user' && (
|
||||
<ListItem
|
||||
key="add"
|
||||
title={t('settings.mcp.addServer')}
|
||||
active={false}
|
||||
onClick={onAddMcpServer}
|
||||
icon={<PlusOutlined />}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
)}
|
||||
</DragableList>
|
||||
</McpList>
|
||||
<DragableList list={servers} onUpdate={updateMcpServers}>
|
||||
{(server: MCPServer) => (
|
||||
<Dropdown menu={{ items: getMenuItems(server) }} trigger={['contextMenu']} key={server.id}>
|
||||
<div>
|
||||
<ListItem
|
||||
key={server.id}
|
||||
title={server.name}
|
||||
active={selectedMcpServer?.id === server.id}
|
||||
onClick={() => {
|
||||
setSelectedMcpServer(server)
|
||||
setRoute(null)
|
||||
}}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
icon={<CodeOutlined />}
|
||||
rightContent={
|
||||
<IndicatorLight
|
||||
size={6}
|
||||
color={server.isActive ? 'green' : 'var(--color-text-3)'}
|
||||
animation={server.isActive}
|
||||
shadow={false}
|
||||
style={{ marginRight: 4 }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Dropdown>
|
||||
)}
|
||||
</DragableList>
|
||||
</McpList>
|
||||
</McpListContainer>
|
||||
{MainContent}
|
||||
</Container>
|
||||
)
|
||||
@ -169,14 +198,26 @@ const Container = styled(HStack)`
|
||||
flex: 1;
|
||||
`
|
||||
|
||||
const McpList = styled(Scrollbar)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
const McpListContainer = styled(VStack)`
|
||||
width: var(--settings-width);
|
||||
padding: 12px;
|
||||
border-right: 0.5px solid var(--color-border);
|
||||
height: calc(100vh - var(--navbar-height));
|
||||
`
|
||||
|
||||
const McpListHeader = styled(HStack)`
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
padding-bottom: 0;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
const McpList = styled(Scrollbar)`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
.iconfont {
|
||||
color: var(--color-text-2);
|
||||
line-height: 16px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user