import type { Dispatch, RefObject, SetStateAction, } from 'react' import { useEffect, useMemo, useRef, useState, } from 'react' import { useTranslation } from 'react-i18next' import type { BlockEnum, ToolWithProvider, } from '../types' import type { ToolDefaultValue, ToolValue } from './types' import { ToolTypeEnum } from './types' import Tools from './tools' import { useToolTabs } from './hooks' import ViewTypeSelect, { ViewType } from './view-type-select' import cn from '@/utils/classnames' import Button from '@/app/components/base/button' import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list' import type { Plugin } from '../../plugins/types' import { PluginCategoryEnum } from '../../plugins/types' import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' import { useGlobalPublicStore } from '@/context/global-public-context' import RAGToolSuggestions from './rag-tool-suggestions' import FeaturedTools from './featured-tools' import Link from 'next/link' import Divider from '@/app/components/base/divider' import { RiArrowRightUpLine } from '@remixicon/react' import { getMarketplaceUrl } from '@/utils/var' const marketplaceFooterClassName = 'system-sm-medium z-10 flex h-8 flex-none cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg' type AllToolsProps = { className?: string toolContentClassName?: string searchText: string tags: ListProps['tags'] buildInTools: ToolWithProvider[] customTools: ToolWithProvider[] workflowTools: ToolWithProvider[] mcpTools: ToolWithProvider[] onSelect: (type: BlockEnum, tool: ToolDefaultValue) => void canNotSelectMultiple?: boolean onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void selectedTools?: ToolValue[] canChooseMCPTool?: boolean onTagsChange?: Dispatch> isInRAGPipeline?: boolean featuredPlugins?: Plugin[] featuredLoading?: boolean showFeatured?: boolean onFeaturedInstallSuccess?: () => Promise | void } const DEFAULT_TAGS: AllToolsProps['tags'] = [] const AllTools = ({ className, toolContentClassName, searchText, tags = DEFAULT_TAGS, onSelect, canNotSelectMultiple, onSelectMultiple, buildInTools, workflowTools, customTools, mcpTools = [], selectedTools, canChooseMCPTool, onTagsChange, isInRAGPipeline = false, featuredPlugins = [], featuredLoading = false, showFeatured = false, onFeaturedInstallSuccess, }: AllToolsProps) => { const { t } = useTranslation() const tabs = useToolTabs() const [activeTab, setActiveTab] = useState(ToolTypeEnum.All) const [activeView, setActiveView] = useState(ViewType.flat) const trimmedSearchText = searchText.trim() const hasSearchText = trimmedSearchText.length > 0 const hasTags = tags.length > 0 const hasFilter = hasSearchText || hasTags const isMatchingKeywords = (text: string, keywords: string) => { return text.toLowerCase().includes(keywords.toLowerCase()) } const allProviders = useMemo(() => [...buildInTools, ...customTools, ...workflowTools, ...mcpTools], [buildInTools, customTools, workflowTools, mcpTools]) const providerMap = useMemo(() => { const map = new Map() allProviders.forEach((provider) => { const key = provider.plugin_id || provider.id if (key) map.set(key, provider) }) return map }, [allProviders]) const tools = useMemo(() => { let mergedTools: ToolWithProvider[] = [] if (activeTab === ToolTypeEnum.All) mergedTools = [...buildInTools, ...customTools, ...workflowTools, ...mcpTools] if (activeTab === ToolTypeEnum.BuiltIn) mergedTools = buildInTools if (activeTab === ToolTypeEnum.Custom) mergedTools = customTools if (activeTab === ToolTypeEnum.Workflow) mergedTools = workflowTools if (activeTab === ToolTypeEnum.MCP) mergedTools = mcpTools const normalizedSearch = trimmedSearchText.toLowerCase() if (!hasFilter || !normalizedSearch) return mergedTools.filter(toolWithProvider => toolWithProvider.tools.length > 0) return mergedTools.reduce((acc, toolWithProvider) => { const providerMatches = isMatchingKeywords(toolWithProvider.name, normalizedSearch) if (providerMatches) { if (toolWithProvider.tools.length > 0) acc.push(toolWithProvider) return acc } const matchedTools = toolWithProvider.tools.filter((tool) => { return tool.name.toLowerCase().includes(normalizedSearch) }) if (matchedTools.length > 0) { acc.push({ ...toolWithProvider, tools: matchedTools, }) } return acc }, []) }, [activeTab, buildInTools, customTools, workflowTools, mcpTools, trimmedSearchText, hasFilter]) const { queryPluginsWithDebounced: fetchPlugins, plugins: notInstalledPlugins = [], } = useMarketplacePlugins() const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) useEffect(() => { if (!enable_marketplace) return if (hasFilter) { fetchPlugins({ query: searchText, tags, category: PluginCategoryEnum.tool, }) } }, [searchText, tags, enable_marketplace, hasFilter, fetchPlugins]) const pluginRef = useRef(null) const wrapElemRef = useRef(null) const isSupportGroupView = [ToolTypeEnum.All, ToolTypeEnum.BuiltIn].includes(activeTab) const isShowRAGRecommendations = isInRAGPipeline && activeTab === ToolTypeEnum.All && !hasFilter const hasToolsListContent = tools.length > 0 || isShowRAGRecommendations const hasPluginContent = enable_marketplace && notInstalledPlugins.length > 0 const shouldShowEmptyState = hasFilter && !hasToolsListContent && !hasPluginContent const shouldShowFeatured = showFeatured && enable_marketplace && !isInRAGPipeline && activeTab === ToolTypeEnum.All && !hasFilter && (featuredLoading || featuredPlugins.length > 0) const shouldShowMarketplaceFooter = enable_marketplace && !hasFilter return (
{ tabs.map(tab => (
setActiveTab(tab.key)} > {tab.name}
)) }
{isSupportGroupView && ( )}
{isShowRAGRecommendations && onTagsChange && ( )} {shouldShowFeatured && ( <> { await onFeaturedInstallSuccess?.() }} />
)} {(hasToolsListContent || enable_marketplace) && ( <>
{t('tools.allTools')}
{hasToolsListContent && ( )} {enable_marketplace && ( } list={notInstalledPlugins} searchText={searchText} toolContentClassName={toolContentClassName} tags={tags} hideFindMoreFooter /> )} )}
{shouldShowEmptyState && (
{t('workflow.tabs.noPluginsFound')}
)}
{shouldShowMarketplaceFooter && ( {t('plugin.findMoreInMarketplace')} )}
) } export default AllTools