feat: add Radix UI radio group component and update MCP settings UI

- Introduced a new RadioGroup component using Radix UI for better UI consistency.
- Updated MCPProviderSettings to utilize the new RadioGroup and adjusted button styles for improved UX.
- Added Radix UI radio group dependency to package.json and yarn.lock.
This commit is contained in:
kangfenmao 2025-10-18 18:31:00 +08:00
parent 3417acafe2
commit d2b6433609
6 changed files with 87 additions and 14 deletions

View File

@ -49,6 +49,7 @@
"@dnd-kit/utilities": "^3.2.2",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-use-controllable-state": "^1.2.2",
"class-variance-authority": "^0.7.1",

View File

@ -0,0 +1,28 @@
import { cn } from '@cherrystudio/ui/utils/index'
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'
import { CircleIcon } from 'lucide-react'
import * as React from 'react'
function RadioGroup({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
return <RadioGroupPrimitive.Root data-slot="radio-group" className={cn('grid gap-3', className)} {...props} />
}
function RadioGroupItem({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
return (
<RadioGroupPrimitive.Item
data-slot="radio-group-item"
className={cn(
'border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className
)}
{...props}>
<RadioGroupPrimitive.Indicator
data-slot="radio-group-indicator"
className="relative flex items-center justify-center">
<CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
)
}
export { RadioGroup, RadioGroupItem }

View File

@ -1,7 +1,7 @@
import { Flex, RowFlex } from '@cherrystudio/ui'
import { Button, Flex, RowFlex } from '@cherrystudio/ui'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import type { MCPServer } from '@renderer/types'
import { Button, Divider, Input, Space } from 'antd'
import { Divider, Input, Space } from 'antd'
import Link from 'antd/es/typography/Link'
import { SquareArrowOutUpRight } from 'lucide-react'
import { useCallback, useEffect, useState } from 'react'
@ -17,7 +17,7 @@ interface Props {
}
const McpProviderSettings: React.FC<Props> = ({ provider, existingServers }) => {
const { addMCPServer, updateMCPServer } = useMCPServers()
const { addMCPServer } = useMCPServers()
const [isFetching, setIsFetching] = useState(false)
const [token, setToken] = useState<string>('')
const [availableServers, setAvailableServers] = useState<MCPServer[]>([])
@ -64,11 +64,13 @@ const McpProviderSettings: React.FC<Props> = ({ provider, existingServers }) =>
<ProviderName>{provider.name}</ProviderName>
{provider.discoverUrl && (
<Link target="_blank" href={provider.discoverUrl} style={{ display: 'flex' }}>
<Button type="text" size="small" icon={<SquareArrowOutUpRight size={14} />} />
<Button variant="flat" size="sm">
<SquareArrowOutUpRight size={14} />
</Button>
</Link>
)}
</Flex>
<Button type="primary" onClick={handleFetch} loading={isFetching} disabled={isFetchDisabled}>
<Button variant="solid" onClick={handleFetch} isLoading={isFetching} isDisabled={isFetchDisabled}>
{t('settings.mcp.fetch.button', 'Fetch Servers')}
</Button>
</ProviderHeader>
@ -105,19 +107,18 @@ const McpProviderSettings: React.FC<Props> = ({ provider, existingServers }) =>
<ServerDescription>{server.description}</ServerDescription>
</ServerInfo>
{(() => {
const isAlreadyAdded = existingServers.some(existing => existing.id === server.id)
const isAlreadyAdded = existingServers.some((existing) => existing.id === server.id)
return (
<Button
type={isAlreadyAdded ? 'default' : 'primary'}
size="small"
variant={isAlreadyAdded ? 'bordered' : 'solid'}
size="sm"
disabled={isAlreadyAdded}
onClick={() => {
if (!isAlreadyAdded) {
addMCPServer(server)
window.toast.success(t('settings.mcp.server.added', 'MCP server added'))
}
}}
>
}}>
{isAlreadyAdded ? t('settings.mcp.server.added', 'Added') : t('settings.mcp.add.server', 'Add')}
</Button>
)

View File

@ -1,9 +1,10 @@
import type { MCPServer } from '@renderer/types'
import { getAI302Token, saveAI302Token, syncAi302Servers } from './302ai'
import { getBailianToken, saveBailianToken, syncBailianServers } from './bailian'
import { getTokenLanYunToken, LANYUN_KEY_HOST, saveTokenLanYunToken, syncTokenLanYunServers } from './lanyun'
import { getModelScopeToken, MODELSCOPE_HOST, saveModelScopeToken, syncModelScopeServers } from './modelscope'
import { getTokenFluxToken, saveTokenFluxToken, syncTokenFluxServers, TOKENFLUX_HOST } from './tokenflux'
import type { MCPServer } from '@renderer/types'
export interface ProviderConfig {
key: string
@ -73,4 +74,4 @@ export const providers: ProviderConfig[] = [
saveToken: saveBailianToken,
syncServers: syncBailianServers
}
]
]

View File

@ -17,7 +17,7 @@ export const MCPRoutes = {
tokenflux: '/settings/mcp/tokenflux',
lanyun: '/settings/mcp/lanyun',
'302ai': '/settings/mcp/302ai',
bailian: '/settings/mcp/bailian',
bailian: '/settings/mcp/bailian'
} as const
/**
@ -48,4 +48,4 @@ export function getMCPServerSettingsRoute(serverId: string): string {
}
// 类型定义
export type MCPPage = keyof typeof MCPRoutes
export type MCPPage = keyof typeof MCPRoutes

View File

@ -1920,6 +1920,7 @@ __metadata:
"@heroui/react": "npm:^2.8.4"
"@radix-ui/react-dialog": "npm:^1.1.15"
"@radix-ui/react-popover": "npm:^1.1.15"
"@radix-ui/react-radio-group": "npm:^1.3.8"
"@radix-ui/react-slot": "npm:^1.2.3"
"@radix-ui/react-use-controllable-state": "npm:^1.2.2"
"@storybook/addon-docs": "npm:^9.1.6"
@ -7323,6 +7324,34 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-radio-group@npm:^1.3.8":
version: 1.3.8
resolution: "@radix-ui/react-radio-group@npm:1.3.8"
dependencies:
"@radix-ui/primitive": "npm:1.1.3"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-direction": "npm:1.1.1"
"@radix-ui/react-presence": "npm:1.1.5"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-roving-focus": "npm:1.1.11"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
"@radix-ui/react-use-previous": "npm:1.1.1"
"@radix-ui/react-use-size": "npm:1.1.1"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/23af8e8b833da1fc4aa4e67c3607dedee4fc5b39278d2e2b820bec7f7b3c0891b006a8a35c57ba436ddf18735bbd8dad9a598d14632a328753a875fde447975c
languageName: node
linkType: hard
"@radix-ui/react-roving-focus@npm:1.1.11":
version: 1.1.11
resolution: "@radix-ui/react-roving-focus@npm:1.1.11"
@ -7437,6 +7466,19 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-use-previous@npm:1.1.1":
version: 1.1.1
resolution: "@radix-ui/react-use-previous@npm:1.1.1"
peerDependencies:
"@types/react": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/52f1089d941491cd59b7f52a5679a14e9381711419a0557ce0f3bc9a4c117078224efec54dcced41a3653a13a386a7b6ec75435d61a273e8b9f5d00235f2b182
languageName: node
linkType: hard
"@radix-ui/react-use-rect@npm:1.1.1":
version: 1.1.1
resolution: "@radix-ui/react-use-rect@npm:1.1.1"