diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index 1e7cc09642..01eaf74d0f 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -34,6 +34,7 @@ import Link from 'next/link' import Divider from '@/app/components/base/divider' import { RiArrowRightUpLine } from '@remixicon/react' import { getMarketplaceUrl } from '@/utils/var' +import { useGetLanguage } from '@/context/i18n' 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' @@ -83,6 +84,7 @@ const AllTools = ({ onFeaturedInstallSuccess, }: AllToolsProps) => { const { t } = useTranslation() + const language = useGetLanguage() const tabs = useToolTabs() const [activeTab, setActiveTab] = useState(ToolTypeEnum.All) const [activeView, setActiveView] = useState(ViewType.flat) @@ -117,12 +119,29 @@ const AllTools = ({ mergedTools = mcpTools const normalizedSearch = trimmedSearchText.toLowerCase() + const getLocalizedText = (text?: Record | null) => { + if (!text) + return '' + + if (text[language]) + return text[language] + + if (text['en-US']) + return text['en-US'] + + const firstValue = Object.values(text).find(Boolean) + return firstValue || '' + } if (!hasFilter || !normalizedSearch) return mergedTools.filter(toolWithProvider => toolWithProvider.tools.length > 0) return mergedTools.reduce((acc, toolWithProvider) => { - const providerMatches = isMatchingKeywords(toolWithProvider.name, normalizedSearch) + const providerLabel = getLocalizedText(toolWithProvider.label) + const providerMatches = [ + toolWithProvider.name, + providerLabel, + ].some(text => isMatchingKeywords(text || '', normalizedSearch)) if (providerMatches) { if (toolWithProvider.tools.length > 0) @@ -131,7 +150,11 @@ const AllTools = ({ } const matchedTools = toolWithProvider.tools.filter((tool) => { - return tool.name.toLowerCase().includes(normalizedSearch) + const toolLabel = getLocalizedText(tool.label) + return [ + tool.name, + toolLabel, + ].some(text => isMatchingKeywords(text || '', normalizedSearch)) }) if (matchedTools.length > 0) { @@ -143,7 +166,7 @@ const AllTools = ({ return acc }, []) - }, [activeTab, buildInTools, customTools, workflowTools, mcpTools, trimmedSearchText, hasFilter]) + }, [activeTab, buildInTools, customTools, workflowTools, mcpTools, trimmedSearchText, hasFilter, language]) const { queryPluginsWithDebounced: fetchPlugins, @@ -235,39 +258,37 @@ const AllTools = ({ )} - {(hasToolsListContent || enable_marketplace) && ( + {hasToolsListContent && ( <>
{t('tools.allTools')}
- {hasToolsListContent && ( - - )} - {enable_marketplace && ( - } - list={notInstalledPlugins} - searchText={searchText} - toolContentClassName={toolContentClassName} - tags={tags} - hideFindMoreFooter - /> - )} + )} + {enable_marketplace && ( + } + list={notInstalledPlugins} + searchText={searchText} + toolContentClassName={toolContentClassName} + tags={tags} + hideFindMoreFooter + /> + )} {shouldShowEmptyState && ( diff --git a/web/app/components/workflow/block-selector/start-blocks.tsx b/web/app/components/workflow/block-selector/start-blocks.tsx index c9be37bc36..4fdfd475d9 100644 --- a/web/app/components/workflow/block-selector/start-blocks.tsx +++ b/web/app/components/workflow/block-selector/start-blocks.tsx @@ -36,6 +36,13 @@ const StartBlocks = ({ const filteredBlocks = useMemo(() => { // Check if Start node already exists in workflow const hasStartNode = nodes.some(node => (node.data as CommonNodeType)?.type === BlockEnumValues.Start) + const normalizedSearch = searchText.toLowerCase() + const getDisplayName = (blockType: BlockEnum) => { + if (blockType === BlockEnumValues.TriggerWebhook) + return t('workflow.customWebhook') + + return t(`workflow.blocks.${blockType}`) + } return START_BLOCKS.filter((block) => { // Hide User Input (Start) if it already exists in workflow @@ -43,13 +50,14 @@ const StartBlocks = ({ return false // Filter by search text - if (!block.title.toLowerCase().includes(searchText.toLowerCase())) + const displayName = getDisplayName(block.type).toLowerCase() + if (!displayName.includes(normalizedSearch) && !block.title.toLowerCase().includes(normalizedSearch)) return false // availableBlocksTypes now contains properly filtered entry node types from parent return availableBlocksTypes.includes(block.type) }) - }, [searchText, availableBlocksTypes, nodes]) + }, [searchText, availableBlocksTypes, nodes, t]) const isEmpty = filteredBlocks.length === 0 diff --git a/web/app/components/workflow/block-selector/trigger-plugin/list.tsx b/web/app/components/workflow/block-selector/trigger-plugin/list.tsx index b7d43a2167..3caf1149dd 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin/list.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin/list.tsx @@ -4,6 +4,7 @@ import { useAllTriggerPlugins } from '@/service/use-triggers' import TriggerPluginItem from './item' import type { BlockEnum } from '../../types' import type { TriggerDefaultValue, TriggerWithProvider } from '../types' +import { useGetLanguage } from '@/context/i18n' type TriggerPluginListProps = { onSelect: (type: BlockEnum, trigger?: TriggerDefaultValue) => void @@ -18,10 +19,30 @@ const TriggerPluginList = ({ onContentStateChange, }: TriggerPluginListProps) => { const { data: triggerPluginsData } = useAllTriggerPlugins() + const language = useGetLanguage() const normalizedSearch = searchText.trim().toLowerCase() const triggerPlugins = useMemo(() => { const plugins = triggerPluginsData || [] + const getLocalizedText = (text?: Record | null) => { + if (!text) + return '' + + if (text[language]) + return text[language] + + if (text['en-US']) + return text['en-US'] + + const firstValue = Object.values(text).find(Boolean) + return (typeof firstValue === 'string') ? firstValue : '' + } + const getSearchableTexts = (name: string, label?: Record | null) => { + const localized = getLocalizedText(label) + const values = [localized, name].filter(Boolean) + return values.length > 0 ? values : [''] + } + const isMatchingKeywords = (value: string) => value.toLowerCase().includes(normalizedSearch) if (!normalizedSearch) return plugins.filter(triggerWithProvider => triggerWithProvider.events.length > 0) @@ -30,7 +51,10 @@ const TriggerPluginList = ({ if (triggerWithProvider.events.length === 0) return acc - const providerMatches = triggerWithProvider.name.toLowerCase().includes(normalizedSearch) + const providerMatches = getSearchableTexts( + triggerWithProvider.name, + triggerWithProvider.label, + ).some(text => isMatchingKeywords(text)) if (providerMatches) { acc.push(triggerWithProvider) @@ -38,7 +62,10 @@ const TriggerPluginList = ({ } const matchedEvents = triggerWithProvider.events.filter((event) => { - return event.name.toLowerCase().includes(normalizedSearch) + return getSearchableTexts( + event.name, + event.label, + ).some(text => isMatchingKeywords(text)) }) if (matchedEvents.length > 0) { @@ -50,7 +77,7 @@ const TriggerPluginList = ({ return acc }, []) - }, [triggerPluginsData, normalizedSearch]) + }, [triggerPluginsData, normalizedSearch, language]) const hasContent = triggerPlugins.length > 0