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

View File

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

View File

@ -230,57 +230,27 @@ export const PluginBrowser: FC<PluginBrowserProps> = ({
setSelectedPlugin(null)
}
const handleOpenFilterDropdown = () => {
setFilterDropdownOpen(!filterDropdownOpen)
}
return (
<div className="flex flex-col gap-4">
{/* Search and Filter */}
<div className="relative flex gap-0">
<div className="flex gap-2">
<AntInput
placeholder={t('plugins.search_placeholder')}
value={searchQuery}
onChange={(e) => handleSearchChange(e.target.value)}
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
menu={{ items: pluginCategoryMenuItems }}
trigger={['click']}
open={filterDropdownOpen}
placement="bottomRight"
onOpenChange={setFilterDropdownOpen}>
<AntButton
variant={selectedCategories.length > 0 ? 'filled' : 'text'}
variant={selectedCategories.length > 0 ? 'filled' : 'outlined'}
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" />}
size="middle"
icon={<Filter className="h-4 w-4" color="var(--color-text-2)" />}
/>
</AntDropdown>
</div>