'use client' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import type { Collection } from './types' import Marketplace from './marketplace' import cn from '@/utils/classnames' import { useTabSearchParams } from '@/hooks/use-tab-searchparams' import TabSliderNew from '@/app/components/base/tab-slider-new' import LabelFilter from '@/app/components/tools/labels/filter' import Input from '@/app/components/base/input' import ProviderDetail from '@/app/components/tools/provider/detail' import Empty from '@/app/components/plugins/marketplace/empty' import CustomCreateCard from '@/app/components/tools/provider/custom-create-card' import WorkflowToolEmpty from '@/app/components/tools/add-tool-modal/empty' import Card from '@/app/components/plugins/card' import CardMoreInfo from '@/app/components/plugins/card/card-more-info' import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import MCPList from './mcp' import { useAllToolProviders } from '@/service/use-tools' import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' import { useGlobalPublicStore } from '@/context/global-public-context' import { ToolTypeEnum } from '../workflow/block-selector/types' import { useMarketplace } from './marketplace/hooks' const getToolType = (type: string) => { switch (type) { case 'builtin': return ToolTypeEnum.BuiltIn case 'api': return ToolTypeEnum.Custom case 'workflow': return ToolTypeEnum.Workflow case 'mcp': return ToolTypeEnum.MCP default: return ToolTypeEnum.BuiltIn } } const ProviderList = () => { // const searchParams = useSearchParams() // searchParams.get('category') === 'workflow' const { t } = useTranslation() const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) const containerRef = useRef(null) const [activeTab, setActiveTab] = useTabSearchParams({ defaultTab: 'builtin', }) const options = [ { value: 'builtin', text: t('tools.type.builtIn') }, { value: 'api', text: t('tools.type.custom') }, { value: 'workflow', text: t('tools.type.workflow') }, { value: 'mcp', text: 'MCP' }, ] const [tagFilterValue, setTagFilterValue] = useState([]) const handleTagsChange = (value: string[]) => { setTagFilterValue(value) } const [keywords, setKeywords] = useState('') const handleKeywordsChange = (value: string) => { setKeywords(value) } const { data: collectionList = [], refetch } = useAllToolProviders() const filteredCollectionList = useMemo(() => { return collectionList.filter((collection) => { if (collection.type !== activeTab) return false if (tagFilterValue.length > 0 && (!collection.labels || collection.labels.every(label => !tagFilterValue.includes(label)))) return false if (keywords) return Object.values(collection.label).some(value => value.toLowerCase().includes(keywords.toLowerCase())) return true }) }, [activeTab, tagFilterValue, keywords, collectionList]) const [currentProviderId, setCurrentProviderId] = useState() const currentProvider = useMemo(() => { return filteredCollectionList.find(collection => collection.id === currentProviderId) }, [currentProviderId, filteredCollectionList]) const { data: pluginList } = useInstalledPluginList() const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const currentPluginDetail = useMemo(() => { const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentProvider?.plugin_id) return detail }, [currentProvider?.plugin_id, pluginList?.plugins]) const toolListTailRef = useRef(null) const showMarketplacePanel = useCallback(() => { containerRef.current?.scrollTo({ top: toolListTailRef.current ? toolListTailRef.current?.offsetTop - 80 : 0, behavior: 'smooth', }) }, [toolListTailRef]) const marketplaceContext = useMarketplace(keywords, tagFilterValue) const { handleScroll, } = marketplaceContext const [isMarketplaceArrowVisible, setIsMarketplaceArrowVisible] = useState(true) const onContainerScroll = useMemo(() => { return (e: Event) => { handleScroll(e) if (containerRef.current && toolListTailRef.current) setIsMarketplaceArrowVisible(containerRef.current.scrollTop < (toolListTailRef.current?.offsetTop - 80)) } }, [handleScroll, containerRef, toolListTailRef, setIsMarketplaceArrowVisible]) useEffect(() => { const container = containerRef.current if (container) container.addEventListener('scroll', onContainerScroll) return () => { if (container) container.removeEventListener('scroll', onContainerScroll) } }, [onContainerScroll]) return ( <>
{ setActiveTab(state) if (state !== activeTab) setCurrentProviderId(undefined) }} options={options} />
{activeTab !== 'mcp' && ( )} handleKeywordsChange(e.target.value)} onClear={() => handleKeywordsChange('')} />
{activeTab !== 'mcp' && (
{activeTab === 'api' && } {filteredCollectionList.map(collection => (
setCurrentProviderId(collection.id)} > } />
))} {!filteredCollectionList.length && activeTab === 'workflow' &&
}
)} {!filteredCollectionList.length && activeTab === 'builtin' && ( )}
{enable_marketplace && activeTab === 'builtin' && ( )} {activeTab === 'mcp' && ( )}
{currentProvider && !currentProvider.plugin_id && ( setCurrentProviderId(undefined)} onRefreshData={refetch} /> )} invalidateInstalledPluginList()} onHide={() => setCurrentProviderId(undefined)} /> ) } ProviderList.displayName = 'ToolProviderList' export default ProviderList