From f5b84384cf2093dce970318d202f555fd65ecb21 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 30 Jan 2026 15:49:30 +0800 Subject: [PATCH] feat: support search tool after @ --- .../workflow/block-selector/tool-picker.tsx | 48 ++++++++++++------- .../plugins/tool-block/tool-picker-block.tsx | 17 +++++-- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/web/app/components/workflow/block-selector/tool-picker.tsx b/web/app/components/workflow/block-selector/tool-picker.tsx index 1d702e9931..1f6dca104f 100644 --- a/web/app/components/workflow/block-selector/tool-picker.tsx +++ b/web/app/components/workflow/block-selector/tool-picker.tsx @@ -54,6 +54,9 @@ type Props = { preventFocusLoss?: boolean hideFeaturedTool?: boolean hideSelectedInfo?: boolean + searchText?: string + onSearchTextChange?: (value: string) => void + hideSearchBox?: boolean } const ToolPicker: FC = ({ @@ -73,9 +76,20 @@ const ToolPicker: FC = ({ preventFocusLoss = false, hideFeaturedTool = false, hideSelectedInfo = false, + searchText: controlledSearchText, + onSearchTextChange, + hideSearchBox = false, }) => { const { t } = useTranslation() const [searchText, setSearchText] = useState('') + const isSearchControlled = controlledSearchText !== undefined + const effectiveSearchText = isSearchControlled ? controlledSearchText : searchText + const handleSearchTextChange = (value: string) => { + if (isSearchControlled) + onSearchTextChange?.(value) + else + setSearchText(value) + } const [tags, setTags] = useState([]) const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) @@ -190,26 +204,28 @@ const ToolPicker: FC = ({ e.preventDefault() }} > -
- -
+ {!hideSearchBox && ( +
+ +
+ )} { const [editor] = useLexicalComposerContext() const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('@', { minLength: 0, - maxLength: 0, + maxLength: 75, }) const storeApi = useWorkflowStore() const toolBlockContext = useToolBlockContext() const isUsingExternalMetadata = Boolean(toolBlockContext?.onMetadataChange) + const [queryString, setQueryString] = useState('') const options = useMemo(() => [new ToolPickerMenuOption()], []) @@ -132,7 +133,10 @@ const ToolPickerBlock = ({ scope = 'all' }: ToolPickerBlockProps) => { if (!anchorElementRef.current) return null - const closeMenu = () => selectOptionAndCleanUp(options[0]) + const closeMenu = () => { + setQueryString('') + selectOptionAndCleanUp(options[0]) + } return ReactDOM.createPortal( { insertTools(tools) closeMenu() }} + searchText={queryString} + onSearchTextChange={setQueryString} + hideSearchBox scope={scope} hideFeaturedTool preventFocusLoss />, anchorElementRef.current, ) - }, [insertTools, options, scope]) + }, [insertTools, options, queryString, scope]) return ( { }} - onQueryChange={() => { }} + onQueryChange={matchingString => setQueryString(matchingString || '')} menuRenderFn={renderMenu} triggerFn={checkForTriggerMatch} anchorClassName="z-[999999] translate-y-[calc(-100%-3px)]"