refactor: update PluginSettings and ToolingSettings for improved layout and functionality

This commit is contained in:
kangfenmao 2025-11-05 18:44:13 +08:00
parent fcb0020787
commit 6ae5f69163
3 changed files with 51 additions and 94 deletions

View File

@ -1,3 +1,4 @@
import Scrollbar from '@renderer/components/Scrollbar'
import { useAvailablePlugins, useInstalledPlugins, usePluginActions } from '@renderer/hooks/usePlugins' import { useAvailablePlugins, useInstalledPlugins, usePluginActions } from '@renderer/hooks/usePlugins'
import type { GetAgentResponse, GetAgentSessionResponse, UpdateAgentFunctionUnion } from '@renderer/types/agent' import type { GetAgentResponse, GetAgentSessionResponse, UpdateAgentFunctionUnion } from '@renderer/types/agent'
import { Card, Segmented } from 'antd' import { Card, Segmented } from 'antd'
@ -8,7 +9,6 @@ import { useTranslation } from 'react-i18next'
import { InstalledPluginsList } from './components/InstalledPluginsList' import { InstalledPluginsList } from './components/InstalledPluginsList'
import { PluginBrowser } from './components/PluginBrowser' import { PluginBrowser } from './components/PluginBrowser'
import { SettingsContainer } from './shared'
interface PluginSettingsProps { interface PluginSettingsProps {
agentBase: GetAgentResponse | GetAgentSessionResponse agentBase: GetAgentResponse | GetAgentSessionResponse
@ -131,14 +131,14 @@ const PluginSettings: FC<PluginSettingsProps> = ({ agentBase }) => {
]) ])
return ( return (
<SettingsContainer className="pt-0"> <Scrollbar>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex justify-center"> <div className="flex justify-center">
<Segmented options={segmentOptions} value={activeTab} onChange={(value) => setActiveTab(value as string)} /> <Segmented options={segmentOptions} value={activeTab} onChange={(value) => setActiveTab(value as string)} />
</div> </div>
{renderContent} {renderContent}
</div> </div>
</SettingsContainer> </Scrollbar>
) )
} }

View File

@ -14,7 +14,7 @@ import type {
import { AgentConfigurationSchema } from '@renderer/types' import { AgentConfigurationSchema } from '@renderer/types'
import { Modal, Tag } from 'antd' import { Modal, Tag } from 'antd'
import { Alert, Card, Input, Switch } from 'antd' import { Alert, Card, Input, Switch } from 'antd'
import { ShieldAlert, ShieldCheck, Wrench } from 'lucide-react' import { ShieldAlert, Wrench } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
import { useCallback, useMemo, useState } from 'react' import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -274,15 +274,15 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
return ( return (
<div <div
key={card.mode} key={card.mode}
className={`flex overflow-hidden rounded-lg border ${ className={`flex flex-col gap-3 overflow-hidden rounded-lg border p-4 transition-colors ${
isSelected ? 'border-primary' : 'border-default-200' isSelected
} ${disabled ? 'opacity-60' : ''}`} ? 'border-primary bg-primary-50/30 dark:bg-primary-950/20'
onClick={() => handleSelectPermissionMode(card.mode)}> : 'border-default-200 hover:bg-default-50 dark:hover:bg-default-900/20'
<Card } ${disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'}`}
variant={'borderless'} onClick={() => !disabled && handleSelectPermissionMode(card.mode)}>
title={ {/* Header */}
<div className="flex flex-row items-center justify-between gap-2 py-2"> <div className="flex items-start justify-between gap-3">
<div className="flex flex-col"> <div className="flex min-w-0 flex-1 flex-col gap-1">
<span className="whitespace-normal break-words text-left font-semibold text-sm"> <span className="whitespace-normal break-words text-left font-semibold text-sm">
{t(card.titleKey, card.titleFallback)} {t(card.titleKey, card.titleFallback)}
</span> </span>
@ -290,44 +290,31 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
{t(card.descriptionKey, card.descriptionFallback)} {t(card.descriptionKey, card.descriptionFallback)}
</span> </span>
</div> </div>
{disabled ? ( {disabled && <Tag color="warning">{t('common.coming_soon', 'Coming soon')}</Tag>}
<Tag color="warning">{t('common.coming_soon', 'Coming soon')}</Tag> {isSelected && !disabled && (
) : isSelected ? (
<Tag color="success"> <Tag color="success">
<div className="flex flex-col items-center py-2"> <div className="flex items-center gap-1">
<ShieldCheck size={14} />
<span>{t('common.selected', 'Selected')}</span> <span>{t('common.selected', 'Selected')}</span>
</div> </div>
</Tag> </Tag>
) : null} )}
</div> </div>
}
styles={{ {/* Body */}
header: { <div className="flex flex-col gap-2">
paddingLeft: '12px', <span className="text-foreground-600 text-xs">{t(card.behaviorKey, card.behaviorFallback)}</span>
paddingRight: '12px', {showCaution && (
borderBottom: 'none' <div className="flex items-start gap-2 rounded-md bg-danger-50 p-2 dark:bg-danger-950/30">
}, <ShieldAlert className="mt-0.5 flex-shrink-0 text-danger-600" size={16} />
body: { <span className="text-danger-600 text-xs">
paddingLeft: '12px',
paddingRight: '12px'
}
}}>
<div className="gap-2 text-left text-xs">
<span className="text-foreground-600">{t(card.behaviorKey, card.behaviorFallback)}</span>
{showCaution ? (
<div className="flex items-center gap-1">
<ShieldAlert className="text-danger-600" size={24} />
<span className="text-danger-600">
{t( {t(
'agent.settings.tooling.permissionMode.bypassPermissions.warning', 'agent.settings.tooling.permissionMode.bypassPermissions.warning',
'Use with caution — all tools will run without asking for approval.' 'Use with caution — all tools will run without asking for approval.'
)} )}
</span> </span>
</div> </div>
) : null} )}
</div> </div>
</Card>
</div> </div>
) )
})} })}
@ -414,7 +401,7 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
})} })}
checked={isApproved} checked={isApproved}
disabled={isAuto || isUpdatingTools} disabled={isAuto || isUpdatingTools}
size="default" size="small"
onChange={(checked) => handleToggleTool(tool.id, checked)} onChange={(checked) => handleToggleTool(tool.id, checked)}
/> />
</div> </div>
@ -485,7 +472,7 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
name: server.name name: server.name
})} })}
checked={isSelected} checked={isSelected}
size="default" size="small"
disabled={!server.isActive || isUpdatingMcp} disabled={!server.isActive || isUpdatingMcp}
onChange={(checked) => handleToggleMcp(server.id, checked)} onChange={(checked) => handleToggleMcp(server.id, checked)}
/> />

View File

@ -230,57 +230,27 @@ export const PluginBrowser: FC<PluginBrowserProps> = ({
setSelectedPlugin(null) setSelectedPlugin(null)
} }
const handleOpenFilterDropdown = () => {
setFilterDropdownOpen(!filterDropdownOpen)
}
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
{/* Search and Filter */} {/* Search and Filter */}
<div className="relative flex gap-0"> <div className="flex gap-2">
<AntInput <AntInput
placeholder={t('plugins.search_placeholder')} placeholder={t('plugins.search_placeholder')}
value={searchQuery} value={searchQuery}
onChange={(e) => handleSearchChange(e.target.value)} onChange={(e) => handleSearchChange(e.target.value)}
prefix={<Search className="h-4 w-4 text-default-400" />} prefix={<Search className="h-4 w-4 text-default-400" />}
suffix={
<AntButton
variant={selectedCategories.length > 0 ? 'filled' : 'text'}
color={selectedCategories.length > 0 ? 'primary' : 'default'}
size="small"
style={{
position: 'absolute',
top: '50%',
right: '8px',
transform: 'translateY(-50%)',
padding: '4px',
minWidth: '32px',
borderRadius: '4px'
}}
icon={<Filter className="h-4 w-4" />}
onClick={handleOpenFilterDropdown}
/>
}
/> />
<AntDropdown <AntDropdown
menu={{ items: pluginCategoryMenuItems }} menu={{ items: pluginCategoryMenuItems }}
trigger={['click']} trigger={['click']}
open={filterDropdownOpen} open={filterDropdownOpen}
placement="bottomRight"
onOpenChange={setFilterDropdownOpen}> onOpenChange={setFilterDropdownOpen}>
<AntButton <AntButton
variant={selectedCategories.length > 0 ? 'filled' : 'text'} variant={selectedCategories.length > 0 ? 'filled' : 'outlined'}
color={selectedCategories.length > 0 ? 'primary' : 'default'} color={selectedCategories.length > 0 ? 'primary' : 'default'}
size="small" size="middle"
style={{ icon={<Filter className="h-4 w-4" color="var(--color-text-2)" />}
position: 'absolute',
top: '50%',
right: '8px',
transform: 'translateY(-50%)',
padding: '4px',
minWidth: '32px',
borderRadius: '4px'
}}
icon={<Filter className="h-4 w-4" />}
/> />
</AntDropdown> </AntDropdown>
</div> </div>