diff --git a/web/app/components/plugins/marketplace/search-box/index.tsx b/web/app/components/plugins/marketplace/search-box/index.tsx index 5c6f04e3bd..11d7081d17 100644 --- a/web/app/components/plugins/marketplace/search-box/index.tsx +++ b/web/app/components/plugins/marketplace/search-box/index.tsx @@ -1,5 +1,5 @@ 'use client' -import { RiCloseLine, RiSearchLine } from '@remixicon/react' +import { RiCloseCircleFill, RiSearchLine } from '@remixicon/react' import TagsFilter from './tags-filter' import ActionButton from '@/app/components/base/action-button' import cn from '@/utils/classnames' @@ -48,7 +48,7 @@ const SearchBox = ({ { @@ -58,10 +58,8 @@ const SearchBox = ({ /> { search && ( -
- onSearchChange('')}> - - +
onSearchChange('')}> +
) } diff --git a/web/app/components/workflow/block-selector/all-start-blocks.tsx b/web/app/components/workflow/block-selector/all-start-blocks.tsx index 40c6e5e96f..37378bf334 100644 --- a/web/app/components/workflow/block-selector/all-start-blocks.tsx +++ b/web/app/components/workflow/block-selector/all-start-blocks.tsx @@ -1,5 +1,5 @@ 'use client' -import { useRef } from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import type { BlockEnum } from '../types' import type { ToolDefaultValue } from './types' @@ -10,12 +10,15 @@ import cn from '@/utils/classnames' import Link from 'next/link' import { RiArrowRightUpLine } from '@remixicon/react' import { getMarketplaceUrl } from '@/utils/var' +import Button from '@/app/components/base/button' +import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' type AllStartBlocksProps = { className?: string searchText: string onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void availableBlocksTypes?: BlockEnum[] + tags?: string[] } const AllStartBlocks = ({ @@ -23,26 +26,68 @@ const AllStartBlocks = ({ searchText, onSelect, availableBlocksTypes, + tags = [], }: AllStartBlocksProps) => { const { t } = useTranslation() const wrapElemRef = useRef(null) + const [hasStartBlocksContent, setHasStartBlocksContent] = useState(false) + const [hasPluginContent, setHasPluginContent] = useState(false) + + const handleStartBlocksContentChange = useCallback((hasContent: boolean) => { + setHasStartBlocksContent(hasContent) + }, []) + + const handlePluginContentChange = useCallback((hasContent: boolean) => { + setHasPluginContent(hasContent) + }, []) + + const hasAnyContent = hasStartBlocksContent || hasPluginContent + const shouldShowEmptyState = searchText && !hasAnyContent return (
- + {shouldShowEmptyState && ( +
+ +
+ {t('workflow.tabs.noPluginsFound')} +
+ + + +
+ )} - + {!shouldShowEmptyState && ( + <> + + + + + )}
{/* Footer - Same as Tools tab marketplace footer */} diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx index f56d922d23..0eb92f1e16 100644 --- a/web/app/components/workflow/block-selector/index.tsx +++ b/web/app/components/workflow/block-selector/index.tsx @@ -143,7 +143,18 @@ const NodeSelector: FC = ({ onActiveTabChange={handleActiveTabChange} filterElem={
e.stopPropagation()}> - {(activeTab === TabsEnum.Start || activeTab === TabsEnum.Blocks) && ( + {activeTab === TabsEnum.Start && ( + + )} + {activeTab === TabsEnum.Blocks && ( = ({ tags={tags} onTagsChange={setTags} size='small' - placeholder={t('plugin.searchTools')!} + placeholder={searchPlaceholder} inputClassName='grow' /> )} diff --git a/web/app/components/workflow/block-selector/start-blocks.tsx b/web/app/components/workflow/block-selector/start-blocks.tsx index 35d6aefeb7..8cbbb110a0 100644 --- a/web/app/components/workflow/block-selector/start-blocks.tsx +++ b/web/app/components/workflow/block-selector/start-blocks.tsx @@ -1,6 +1,7 @@ import { memo, useCallback, + useEffect, useMemo, } from 'react' import { useNodes } from 'reactflow' @@ -17,12 +18,14 @@ type StartBlocksProps = { searchText: string onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void availableBlocksTypes?: BlockEnum[] + onContentStateChange?: (hasContent: boolean) => void } const StartBlocks = ({ searchText, onSelect, availableBlocksTypes = [], + onContentStateChange, }: StartBlocksProps) => { const { t } = useTranslation() const nodes = useNodes() @@ -48,6 +51,10 @@ const StartBlocks = ({ const isEmpty = filteredBlocks.length === 0 + useEffect(() => { + onContentStateChange?.(!isEmpty) + }, [isEmpty, onContentStateChange]) + const renderBlock = useCallback((block: typeof START_BLOCKS[0]) => ( ), [nodesExtraData, onSelect, t]) + if (isEmpty) + return null + return ( -
- {isEmpty && ( -
- {t('workflow.tabs.noResult')} -
- )} - {!isEmpty && ( -
- {filteredBlocks.map((block, index) => ( -
- {renderBlock(block)} - {block.type === BlockEnumValues.Start && index < filteredBlocks.length - 1 && ( -
-
-
- )} -
- ))} -
- )} +
+
+ {filteredBlocks.map((block, index) => ( +
+ {renderBlock(block)} + {block.type === BlockEnumValues.Start && index < filteredBlocks.length - 1 && ( +
+
+
+ )} +
+ ))} +
) } diff --git a/web/app/components/workflow/block-selector/tabs.tsx b/web/app/components/workflow/block-selector/tabs.tsx index 3d628688c4..3842dfa6c2 100644 --- a/web/app/components/workflow/block-selector/tabs.tsx +++ b/web/app/components/workflow/block-selector/tabs.tsx @@ -72,6 +72,7 @@ const Tabs: FC = ({ searchText={searchText} onSelect={onSelect} availableBlocksTypes={availableBlocksTypes} + tags={tags} />
) diff --git a/web/app/components/workflow/block-selector/trigger-plugin-selector.tsx b/web/app/components/workflow/block-selector/trigger-plugin-selector.tsx index bdb96da55a..141bf318ec 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin-selector.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin-selector.tsx @@ -7,16 +7,22 @@ import type { ToolDefaultValue } from './types' type TriggerPluginSelectorProps = { onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void searchText: string + onContentStateChange?: (hasContent: boolean) => void + tags?: string[] } const TriggerPluginSelector = ({ onSelect, searchText, + onContentStateChange, + tags = [], }: TriggerPluginSelectorProps) => { return ( ) } 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 293a516f3f..4bcb93a6aa 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin/list.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin/list.tsx @@ -1,5 +1,5 @@ 'use client' -import { memo, useMemo } from 'react' +import { memo, useEffect, useMemo } from 'react' import { useAllBuiltInTools } from '@/service/use-tools' import TriggerPluginItem from './item' import type { BlockEnum } from '../../types' @@ -9,11 +9,15 @@ import { useGetLanguage } from '@/context/i18n' type TriggerPluginListProps = { onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void searchText: string + onContentStateChange?: (hasContent: boolean) => void + tags?: string[] } const TriggerPluginList = ({ onSelect, searchText, + onContentStateChange, + tags = [], }: TriggerPluginListProps) => { const { data: buildInTools = [] } = useAllBuiltInTools() const language = useGetLanguage() @@ -22,16 +26,26 @@ const TriggerPluginList = ({ return buildInTools.filter((toolWithProvider) => { if (toolWithProvider.tools.length === 0) return false - if (!searchText) return true + // Filter by search text + if (searchText) { + const matchesSearch = toolWithProvider.name.toLowerCase().includes(searchText.toLowerCase()) + || toolWithProvider.tools.some(tool => + tool.label[language].toLowerCase().includes(searchText.toLowerCase()), + ) + if (!matchesSearch) return false + } - return toolWithProvider.name.toLowerCase().includes(searchText.toLowerCase()) - || toolWithProvider.tools.some(tool => - tool.label[language].toLowerCase().includes(searchText.toLowerCase()), - ) + return true }) }, [buildInTools, searchText, language]) - if (!triggerPlugins.length) + const hasContent = triggerPlugins.length > 0 + + useEffect(() => { + onContentStateChange?.(hasContent) + }, [hasContent, onContentStateChange]) + + if (!hasContent) return null return ( diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 6683496578..8e0e0c55e6 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -246,6 +246,8 @@ const translation = { 'transform': 'Transform', 'utilities': 'Utilities', 'noResult': 'No match found', + 'noPluginsFound': 'No plugins were found', + 'requestToCommunity': 'Requests to the community', 'agent': 'Agent Strategy', 'allAdded': 'All added', 'addAll': 'Add all', diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index 93d2e2bf01..28a29dc610 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -244,6 +244,8 @@ const translation = { 'transform': '変換', 'utilities': 'ツール', 'noResult': '該当なし', + 'noPluginsFound': 'プラグインが見つかりません', + 'requestToCommunity': 'コミュニティにリクエスト', 'plugin': 'プラグイン', 'agent': 'エージェント戦略', 'addAll': 'すべてを追加する', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index b49af48482..16c5347c82 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -245,6 +245,8 @@ const translation = { 'transform': '转换', 'utilities': '工具', 'noResult': '未找到匹配项', + 'noPluginsFound': '未找到插件', + 'requestToCommunity': '向社区反馈', 'agent': 'Agent 策略', 'allAdded': '已添加全部', 'addAll': '添加全部',