feat(market): support AgentRunner component filters

This commit is contained in:
Junyan Qin
2026-07-11 18:04:42 +08:00
committed by huanghuoguoguo
parent 11bd5c6fd7
commit 29689962f3
3 changed files with 89 additions and 42 deletions
@@ -6,6 +6,7 @@ import {
Book, Book,
FileText, FileText,
PanelTop, PanelTop,
Bot,
} from 'lucide-react'; } from 'lucide-react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
@@ -31,6 +32,7 @@ export default function PluginComponentList({
KnowledgeEngine: <Book className="w-5 h-5" />, KnowledgeEngine: <Book className="w-5 h-5" />,
Parser: <FileText className="w-5 h-5" />, Parser: <FileText className="w-5 h-5" />,
Page: <PanelTop className="w-5 h-5" />, Page: <PanelTop className="w-5 h-5" />,
AgentRunner: <Bot className="w-5 h-5" />,
}; };
const componentKindList = Object.keys(components || {}); const componentKindList = Object.keys(components || {});
@@ -1,6 +1,14 @@
import { Fragment } from 'react'; import { Fragment } from 'react';
import { TFunction } from 'i18next'; import { TFunction } from 'i18next';
import { Wrench, AudioWaveform, Hash, Book, FileText } from 'lucide-react'; import {
Wrench,
AudioWaveform,
Hash,
Book,
FileText,
PanelTop,
Bot,
} from 'lucide-react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
export default function PluginComponentList({ export default function PluginComponentList({
@@ -24,6 +32,8 @@ export default function PluginComponentList({
Command: <Hash className="w-5 h-5" />, Command: <Hash className="w-5 h-5" />,
KnowledgeEngine: <Book className="w-5 h-5" />, KnowledgeEngine: <Book className="w-5 h-5" />,
Parser: <FileText className="w-5 h-5" />, Parser: <FileText className="w-5 h-5" />,
Page: <PanelTop className="w-5 h-5" />,
AgentRunner: <Bot className="w-5 h-5" />,
}; };
const componentKindList = Object.keys(components || {}); const componentKindList = Object.keys(components || {});
@@ -26,6 +26,7 @@ import {
Book, Book,
FileText, FileText,
AppWindow, AppWindow,
Bot,
SlidersHorizontal, SlidersHorizontal,
X, X,
Info, Info,
@@ -62,6 +63,26 @@ interface SortOption {
// Persist the market filter conditions (type / component / tags / sort) across // Persist the market filter conditions (type / component / tags / sort) across
// visits via localStorage. // visits via localStorage.
const MARKET_FILTERS_KEY = 'langbot_market_filters'; const MARKET_FILTERS_KEY = 'langbot_market_filters';
const MARKET_TYPE_VALUES = ['plugin', 'mcp', 'skill'];
const MARKET_COMPONENT_VALUES = [
'Tool',
'Command',
'EventListener',
'KnowledgeEngine',
'Parser',
'Page',
'AgentRunner',
];
function getComponentFilterFromQuery(
searchParams: Pick<URLSearchParams, 'get'>,
): string | null {
const component = searchParams.get('component');
return component && MARKET_COMPONENT_VALUES.includes(component)
? component
: null;
}
interface MarketFilters { interface MarketFilters {
typeFilter?: string; typeFilter?: string;
componentFilter?: string; componentFilter?: string;
@@ -85,9 +106,7 @@ function MarketPageContent({
headerActions?: React.ReactNode; headerActions?: React.ReactNode;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const validTypes = ['plugin', 'mcp', 'skill'];
const extensionTypeOptions = [ const extensionTypeOptions = [
{ value: 'all', label: t('market.filters.allFormats'), icon: null }, { value: 'all', label: t('market.filters.allFormats'), icon: null },
@@ -98,15 +117,21 @@ function MarketPageContent({
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [componentFilter, setComponentFilter] = useState<string>( const [componentFilter, setComponentFilter] = useState<string>(
() => loadMarketFilters().componentFilter ?? 'all', () =>
getComponentFilterFromQuery(searchParams) ??
loadMarketFilters().componentFilter ??
'all',
); );
const [typeFilter, setTypeFilter] = useState<string>(() => { const [typeFilter, setTypeFilter] = useState<string>(() => {
if (getComponentFilterFromQuery(searchParams)) {
return 'plugin';
}
const type = searchParams.get('type'); const type = searchParams.get('type');
if (type && validTypes.includes(type)) { if (type && MARKET_TYPE_VALUES.includes(type)) {
return type; return type;
} }
const saved = loadMarketFilters().typeFilter; const saved = loadMarketFilters().typeFilter;
return saved && validTypes.includes(saved) ? saved : 'all'; return saved && MARKET_TYPE_VALUES.includes(saved) ? saved : 'all';
}); });
const activeAdvancedFilters = const activeAdvancedFilters =
(typeFilter === 'all' ? 0 : 1) + (componentFilter === 'all' ? 0 : 1); (typeFilter === 'all' ? 0 : 1) + (componentFilter === 'all' ? 0 : 1);
@@ -204,6 +229,11 @@ function MarketPageContent({
label: t('market.componentName.Page'), label: t('market.componentName.Page'),
icon: AppWindow, icon: AppWindow,
}, },
{
value: 'AgentRunner',
label: t('market.componentName.AgentRunner'),
icon: Bot,
},
]; ];
// 获取当前排序参数 // 获取当前排序参数
@@ -429,44 +459,49 @@ function MarketPageContent({
}, []); }, []);
// Handle type filter change // Handle type filter change
const handleTypeFilterChange = useCallback((value: string) => { const handleTypeFilterChange = useCallback(
setTypeFilter(value); (value: string) => {
if (value !== 'plugin') { setTypeFilter(value);
setComponentFilter('all'); if (value !== 'plugin') {
} setComponentFilter('all');
setCurrentPage(1); }
setSelectedTags([]); setCurrentPage(1);
setPlugins([]); setSelectedTags([]);
setPlugins([]);
// Update URL query param to keep it in sync // Update URL query param to keep it in sync
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(searchParams);
if (value === 'all') { if (value === 'all') {
params.delete('type'); params.delete('type');
} else { } else {
params.set('type', value); params.set('type', value);
} }
const newUrl = params.toString() if (value !== 'plugin') {
? `${window.location.pathname}?${params.toString()}` params.delete('component');
: window.location.pathname; }
window.history.replaceState({}, '', newUrl); setSearchParams(params, { replace: true });
}, []); },
[searchParams, setSearchParams],
);
const handleComponentFilterChange = useCallback((value: string) => { const handleComponentFilterChange = useCallback(
setComponentFilter(value); (value: string) => {
setCurrentPage(1); setComponentFilter(value);
setPlugins([]); setCurrentPage(1);
setPlugins([]);
if (value !== 'all') { const params = new URLSearchParams(searchParams);
setTypeFilter('plugin'); if (value === 'all') {
params.delete('component');
const params = new URLSearchParams(window.location.search); } else {
params.set('type', 'plugin'); setTypeFilter('plugin');
const newUrl = params.toString() params.set('type', 'plugin');
? `${window.location.pathname}?${params.toString()}` params.set('component', value);
: window.location.pathname; }
window.history.replaceState({}, '', newUrl); setSearchParams(params, { replace: true });
} },
}, []); [searchParams, setSearchParams],
);
// 当排序选项或组件筛选或类型筛选变化时重新加载数据 // 当排序选项或组件筛选或类型筛选变化时重新加载数据
useEffect(() => { useEffect(() => {