diff --git a/src/renderer/src/pages/settings/AgentSettings/PluginSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/PluginSettings.tsx index 3477912410..0f141822ff 100644 --- a/src/renderer/src/pages/settings/AgentSettings/PluginSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/PluginSettings.tsx @@ -1,7 +1,8 @@ -import { Card, CardBody, Tab, Tabs } from '@heroui/react' import { useAvailablePlugins, useInstalledPlugins, usePluginActions } from '@renderer/hooks/usePlugins' import type { GetAgentResponse, GetAgentSessionResponse, UpdateAgentFunctionUnion } from '@renderer/types/agent' +import { Card, Tabs } from 'antd' import type { FC } from 'react' +import { useMemo } from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' @@ -54,24 +55,18 @@ const PluginSettings: FC = ({ agentBase }) => { [uninstall, t] ) - return ( - - - + const tabItems = useMemo(() => { + return [ + { + key: 'available', + label: t('agent.settings.plugins.available.title'), + children: (
{errorAvailable ? ( - - -

- {t('agent.settings.plugins.error.load')}: {errorAvailable} -

-
+ +

+ {t('agent.settings.plugins.error.load')}: {errorAvailable} +

) : ( = ({ agentBase }) => { /> )}
-
- - + ) + }, + { + key: 'installed', + label: t('agent.settings.plugins.installed.title'), + children: (
{errorInstalled ? ( - -

- {t('agent.settings.plugins.error.load')}: {errorInstalled} -

-
+

+ {t('agent.settings.plugins.error.load')}: {errorInstalled} +

) : ( = ({ agentBase }) => { /> )}
-
-
+ ) + } + ] + }, [ + agentBase.id, + agents, + commands, + errorAvailable, + errorInstalled, + handleInstall, + handleUninstall, + installing, + loadingAvailable, + loadingInstalled, + plugins, + skills, + t, + uninstalling + ]) + + return ( + + ) } diff --git a/src/renderer/src/pages/settings/AgentSettings/components/PluginBrowser.tsx b/src/renderer/src/pages/settings/AgentSettings/components/PluginBrowser.tsx index e84ef5108f..3fdbb1074f 100644 --- a/src/renderer/src/pages/settings/AgentSettings/components/PluginBrowser.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/components/PluginBrowser.tsx @@ -1,5 +1,6 @@ -import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Input, Tab, Tabs } from '@heroui/react' import type { InstalledPlugin, PluginMetadata } from '@renderer/types/plugin' +import { Button as AntButton, Dropdown as AntDropdown, Input as AntInput, Tabs as AntTabs } from 'antd' +import type { ItemType } from 'antd/es/menu/interface' import { Filter, Search } from 'lucide-react' import type { FC } from 'react' import { useEffect, useMemo, useRef, useState } from 'react' @@ -42,6 +43,7 @@ export const PluginBrowser: FC = ({ const [selectedPlugin, setSelectedPlugin] = useState(null) const [isModalOpen, setIsModalOpen] = useState(false) const observerTarget = useRef(null) + const [filterDropdownOpen, setFilterDropdownOpen] = useState(false) // Combine all plugins based on active type const allPlugins = useMemo(() => { @@ -92,6 +94,68 @@ export const PluginBrowser: FC = ({ return filteredPlugins.slice(0, displayCount) }, [filteredPlugins, displayCount]) + const pluginCategoryMenuItems = useMemo(() => { + const isSelected = (category: string): boolean => + category === 'all' ? selectedCategories.length === 0 : selectedCategories.includes(category) + const handleClick = (category: string) => { + if (category === 'all') { + handleCategoryChange(new Set(['all'])) + } else { + const newKeys = selectedCategories.includes(category) + ? new Set(selectedCategories.filter((c) => c !== category)) + : new Set([...selectedCategories, category]) + handleCategoryChange(newKeys) + } + } + + const itemLabel = (category: string) => ( +
+ {category} + {isSelected(category) && } +
+ ) + + return [ + { + key: 'all', + title: t('plugins.all_categories'), + label: itemLabel('all'), + onClick: () => handleClick('all') + }, + ...allCategories.map( + (category) => + ({ + key: category, + title: category, + label: itemLabel(category), + onClick: () => handleClick(category) + }) satisfies ItemType + ) + ] + }, [allCategories, selectedCategories, t]) + + const pluginTypeTabItems = useMemo( + () => [ + { + key: 'all', + label: t('plugins.all_types') + }, + { + key: 'agent', + label: t('plugins.agents') + }, + { + key: 'command', + label: t('plugins.commands') + }, + { + key: 'skill', + label: t('plugins.skills') + } + ], + [t] + ) + const hasMore = displayCount < filteredPlugins.length // Reset display count when filters change @@ -166,77 +230,74 @@ export const PluginBrowser: FC = ({ setSelectedPlugin(null) } + const handleOpenFilterDropdown = () => { + setFilterDropdownOpen(!filterDropdownOpen) + } + return (
{/* Search and Filter */}
- } - isClearable - size="md" - className="flex-1" - classNames={{ - inputWrapper: 'pr-12' - }} - /> - - - - - ({ key: category, label: category })) - ]}> - {(item) => { - const isSelected = - item.key === 'all' ? selectedCategories.length === 0 : selectedCategories.includes(item.key) - - return ( - { - if (item.key === 'all') { - handleCategoryChange(new Set(['all'])) - } else { - const newKeys = selectedCategories.includes(item.key) - ? new Set(selectedCategories.filter((c) => c !== item.key)) - : new Set([...selectedCategories, item.key]) - handleCategoryChange(newKeys) - } - }} - className={isSelected ? 'bg-primary-50' : ''}> - {item.label} - {isSelected && } - - ) + size="small" + style={{ + position: 'absolute', + top: '50%', + right: '8px', + transform: 'translateY(-50%)', + padding: '4px', + minWidth: '32px', + borderRadius: '4px' + }} + icon={} + onClick={handleOpenFilterDropdown} + /> + } + /> + { + setFilterDropdownOpen(nextOpen) + }}> + 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={} + /> +
{/* Type Tabs */} -
- - - - - - +
+
{/* Result Count */}