mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-16 17:36:07 +00:00
feat(market): support AgentRunner component filters
This commit is contained in:
committed by
huanghuoguoguo
parent
11bd5c6fd7
commit
29689962f3
@@ -6,6 +6,7 @@ import {
|
||||
Book,
|
||||
FileText,
|
||||
PanelTop,
|
||||
Bot,
|
||||
} from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
@@ -31,6 +32,7 @@ export default function PluginComponentList({
|
||||
KnowledgeEngine: <Book 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 || {});
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { Fragment } from 'react';
|
||||
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';
|
||||
|
||||
export default function PluginComponentList({
|
||||
@@ -24,6 +32,8 @@ export default function PluginComponentList({
|
||||
Command: <Hash className="w-5 h-5" />,
|
||||
KnowledgeEngine: <Book 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 || {});
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
Book,
|
||||
FileText,
|
||||
AppWindow,
|
||||
Bot,
|
||||
SlidersHorizontal,
|
||||
X,
|
||||
Info,
|
||||
@@ -62,6 +63,26 @@ interface SortOption {
|
||||
// Persist the market filter conditions (type / component / tags / sort) across
|
||||
// visits via localStorage.
|
||||
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 {
|
||||
typeFilter?: string;
|
||||
componentFilter?: string;
|
||||
@@ -85,9 +106,7 @@ function MarketPageContent({
|
||||
headerActions?: React.ReactNode;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const validTypes = ['plugin', 'mcp', 'skill'];
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const extensionTypeOptions = [
|
||||
{ value: 'all', label: t('market.filters.allFormats'), icon: null },
|
||||
@@ -98,15 +117,21 @@ function MarketPageContent({
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [componentFilter, setComponentFilter] = useState<string>(
|
||||
() => loadMarketFilters().componentFilter ?? 'all',
|
||||
() =>
|
||||
getComponentFilterFromQuery(searchParams) ??
|
||||
loadMarketFilters().componentFilter ??
|
||||
'all',
|
||||
);
|
||||
const [typeFilter, setTypeFilter] = useState<string>(() => {
|
||||
if (getComponentFilterFromQuery(searchParams)) {
|
||||
return 'plugin';
|
||||
}
|
||||
const type = searchParams.get('type');
|
||||
if (type && validTypes.includes(type)) {
|
||||
if (type && MARKET_TYPE_VALUES.includes(type)) {
|
||||
return type;
|
||||
}
|
||||
const saved = loadMarketFilters().typeFilter;
|
||||
return saved && validTypes.includes(saved) ? saved : 'all';
|
||||
return saved && MARKET_TYPE_VALUES.includes(saved) ? saved : 'all';
|
||||
});
|
||||
const activeAdvancedFilters =
|
||||
(typeFilter === 'all' ? 0 : 1) + (componentFilter === 'all' ? 0 : 1);
|
||||
@@ -204,6 +229,11 @@ function MarketPageContent({
|
||||
label: t('market.componentName.Page'),
|
||||
icon: AppWindow,
|
||||
},
|
||||
{
|
||||
value: 'AgentRunner',
|
||||
label: t('market.componentName.AgentRunner'),
|
||||
icon: Bot,
|
||||
},
|
||||
];
|
||||
|
||||
// 获取当前排序参数
|
||||
@@ -429,44 +459,49 @@ function MarketPageContent({
|
||||
}, []);
|
||||
|
||||
// Handle type filter change
|
||||
const handleTypeFilterChange = useCallback((value: string) => {
|
||||
setTypeFilter(value);
|
||||
if (value !== 'plugin') {
|
||||
setComponentFilter('all');
|
||||
}
|
||||
setCurrentPage(1);
|
||||
setSelectedTags([]);
|
||||
setPlugins([]);
|
||||
const handleTypeFilterChange = useCallback(
|
||||
(value: string) => {
|
||||
setTypeFilter(value);
|
||||
if (value !== 'plugin') {
|
||||
setComponentFilter('all');
|
||||
}
|
||||
setCurrentPage(1);
|
||||
setSelectedTags([]);
|
||||
setPlugins([]);
|
||||
|
||||
// Update URL query param to keep it in sync
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (value === 'all') {
|
||||
params.delete('type');
|
||||
} else {
|
||||
params.set('type', value);
|
||||
}
|
||||
const newUrl = params.toString()
|
||||
? `${window.location.pathname}?${params.toString()}`
|
||||
: window.location.pathname;
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
}, []);
|
||||
// Update URL query param to keep it in sync
|
||||
const params = new URLSearchParams(searchParams);
|
||||
if (value === 'all') {
|
||||
params.delete('type');
|
||||
} else {
|
||||
params.set('type', value);
|
||||
}
|
||||
if (value !== 'plugin') {
|
||||
params.delete('component');
|
||||
}
|
||||
setSearchParams(params, { replace: true });
|
||||
},
|
||||
[searchParams, setSearchParams],
|
||||
);
|
||||
|
||||
const handleComponentFilterChange = useCallback((value: string) => {
|
||||
setComponentFilter(value);
|
||||
setCurrentPage(1);
|
||||
setPlugins([]);
|
||||
const handleComponentFilterChange = useCallback(
|
||||
(value: string) => {
|
||||
setComponentFilter(value);
|
||||
setCurrentPage(1);
|
||||
setPlugins([]);
|
||||
|
||||
if (value !== 'all') {
|
||||
setTypeFilter('plugin');
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set('type', 'plugin');
|
||||
const newUrl = params.toString()
|
||||
? `${window.location.pathname}?${params.toString()}`
|
||||
: window.location.pathname;
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
}
|
||||
}, []);
|
||||
const params = new URLSearchParams(searchParams);
|
||||
if (value === 'all') {
|
||||
params.delete('component');
|
||||
} else {
|
||||
setTypeFilter('plugin');
|
||||
params.set('type', 'plugin');
|
||||
params.set('component', value);
|
||||
}
|
||||
setSearchParams(params, { replace: true });
|
||||
},
|
||||
[searchParams, setSearchParams],
|
||||
);
|
||||
|
||||
// 当排序选项或组件筛选或类型筛选变化时重新加载数据
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user