Skip to content

Commit c1ce5ee

Browse files
committed
feat(market): support AgentRunner component filters
1 parent 844e5ee commit c1ce5ee

3 files changed

Lines changed: 89 additions & 42 deletions

File tree

web/src/app/home/plugins/components/plugin-installed/PluginComponentList.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
Book,
77
FileText,
88
PanelTop,
9+
Bot,
910
} from 'lucide-react';
1011
import { Badge } from '@/components/ui/badge';
1112

@@ -31,6 +32,7 @@ export default function PluginComponentList({
3132
KnowledgeEngine: <Book className="w-5 h-5" />,
3233
Parser: <FileText className="w-5 h-5" />,
3334
Page: <PanelTop className="w-5 h-5" />,
35+
AgentRunner: <Bot className="w-5 h-5" />,
3436
};
3537

3638
const componentKindList = Object.keys(components || {});

web/src/app/home/plugins/components/plugin-market/PluginComponentList.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import { Fragment } from 'react';
22
import { TFunction } from 'i18next';
3-
import { Wrench, AudioWaveform, Hash, Book, FileText } from 'lucide-react';
3+
import {
4+
Wrench,
5+
AudioWaveform,
6+
Hash,
7+
Book,
8+
FileText,
9+
PanelTop,
10+
Bot,
11+
} from 'lucide-react';
412
import { Badge } from '@/components/ui/badge';
513

614
export default function PluginComponentList({
@@ -24,6 +32,8 @@ export default function PluginComponentList({
2432
Command: <Hash className="w-5 h-5" />,
2533
KnowledgeEngine: <Book className="w-5 h-5" />,
2634
Parser: <FileText className="w-5 h-5" />,
35+
Page: <PanelTop className="w-5 h-5" />,
36+
AgentRunner: <Bot className="w-5 h-5" />,
2737
};
2838

2939
const componentKindList = Object.keys(components || {});

web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
Book,
2727
FileText,
2828
AppWindow,
29+
Bot,
2930
SlidersHorizontal,
3031
X,
3132
Info,
@@ -62,6 +63,26 @@ interface SortOption {
6263
// Persist the market filter conditions (type / component / tags / sort) across
6364
// visits via localStorage.
6465
const MARKET_FILTERS_KEY = 'langbot_market_filters';
66+
const MARKET_TYPE_VALUES = ['plugin', 'mcp', 'skill'];
67+
const MARKET_COMPONENT_VALUES = [
68+
'Tool',
69+
'Command',
70+
'EventListener',
71+
'KnowledgeEngine',
72+
'Parser',
73+
'Page',
74+
'AgentRunner',
75+
];
76+
77+
function getComponentFilterFromQuery(
78+
searchParams: Pick<URLSearchParams, 'get'>,
79+
): string | null {
80+
const component = searchParams.get('component');
81+
return component && MARKET_COMPONENT_VALUES.includes(component)
82+
? component
83+
: null;
84+
}
85+
6586
interface MarketFilters {
6687
typeFilter?: string;
6788
componentFilter?: string;
@@ -85,9 +106,7 @@ function MarketPageContent({
85106
headerActions?: React.ReactNode;
86107
}) {
87108
const { t } = useTranslation();
88-
const [searchParams] = useSearchParams();
89-
90-
const validTypes = ['plugin', 'mcp', 'skill'];
109+
const [searchParams, setSearchParams] = useSearchParams();
91110

92111
const extensionTypeOptions = [
93112
{ value: 'all', label: t('market.filters.allFormats'), icon: null },
@@ -98,15 +117,21 @@ function MarketPageContent({
98117

99118
const [searchQuery, setSearchQuery] = useState('');
100119
const [componentFilter, setComponentFilter] = useState<string>(
101-
() => loadMarketFilters().componentFilter ?? 'all',
120+
() =>
121+
getComponentFilterFromQuery(searchParams) ??
122+
loadMarketFilters().componentFilter ??
123+
'all',
102124
);
103125
const [typeFilter, setTypeFilter] = useState<string>(() => {
126+
if (getComponentFilterFromQuery(searchParams)) {
127+
return 'plugin';
128+
}
104129
const type = searchParams.get('type');
105-
if (type && validTypes.includes(type)) {
130+
if (type && MARKET_TYPE_VALUES.includes(type)) {
106131
return type;
107132
}
108133
const saved = loadMarketFilters().typeFilter;
109-
return saved && validTypes.includes(saved) ? saved : 'all';
134+
return saved && MARKET_TYPE_VALUES.includes(saved) ? saved : 'all';
110135
});
111136
const activeAdvancedFilters =
112137
(typeFilter === 'all' ? 0 : 1) + (componentFilter === 'all' ? 0 : 1);
@@ -204,6 +229,11 @@ function MarketPageContent({
204229
label: t('market.componentName.Page'),
205230
icon: AppWindow,
206231
},
232+
{
233+
value: 'AgentRunner',
234+
label: t('market.componentName.AgentRunner'),
235+
icon: Bot,
236+
},
207237
];
208238

209239
// 获取当前排序参数
@@ -429,44 +459,49 @@ function MarketPageContent({
429459
}, []);
430460

431461
// Handle type filter change
432-
const handleTypeFilterChange = useCallback((value: string) => {
433-
setTypeFilter(value);
434-
if (value !== 'plugin') {
435-
setComponentFilter('all');
436-
}
437-
setCurrentPage(1);
438-
setSelectedTags([]);
439-
setPlugins([]);
440-
441-
// Update URL query param to keep it in sync
442-
const params = new URLSearchParams(window.location.search);
443-
if (value === 'all') {
444-
params.delete('type');
445-
} else {
446-
params.set('type', value);
447-
}
448-
const newUrl = params.toString()
449-
? `${window.location.pathname}?${params.toString()}`
450-
: window.location.pathname;
451-
window.history.replaceState({}, '', newUrl);
452-
}, []);
462+
const handleTypeFilterChange = useCallback(
463+
(value: string) => {
464+
setTypeFilter(value);
465+
if (value !== 'plugin') {
466+
setComponentFilter('all');
467+
}
468+
setCurrentPage(1);
469+
setSelectedTags([]);
470+
setPlugins([]);
453471

454-
const handleComponentFilterChange = useCallback((value: string) => {
455-
setComponentFilter(value);
456-
setCurrentPage(1);
457-
setPlugins([]);
472+
// Update URL query param to keep it in sync
473+
const params = new URLSearchParams(searchParams);
474+
if (value === 'all') {
475+
params.delete('type');
476+
} else {
477+
params.set('type', value);
478+
}
479+
if (value !== 'plugin') {
480+
params.delete('component');
481+
}
482+
setSearchParams(params, { replace: true });
483+
},
484+
[searchParams, setSearchParams],
485+
);
458486

459-
if (value !== 'all') {
460-
setTypeFilter('plugin');
487+
const handleComponentFilterChange = useCallback(
488+
(value: string) => {
489+
setComponentFilter(value);
490+
setCurrentPage(1);
491+
setPlugins([]);
461492

462-
const params = new URLSearchParams(window.location.search);
463-
params.set('type', 'plugin');
464-
const newUrl = params.toString()
465-
? `${window.location.pathname}?${params.toString()}`
466-
: window.location.pathname;
467-
window.history.replaceState({}, '', newUrl);
468-
}
469-
}, []);
493+
const params = new URLSearchParams(searchParams);
494+
if (value === 'all') {
495+
params.delete('component');
496+
} else {
497+
setTypeFilter('plugin');
498+
params.set('type', 'plugin');
499+
params.set('component', value);
500+
}
501+
setSearchParams(params, { replace: true });
502+
},
503+
[searchParams, setSearchParams],
504+
);
470505

471506
// 当排序选项或组件筛选或类型筛选变化时重新加载数据
472507
useEffect(() => {

0 commit comments

Comments
 (0)