From dc2c6b0315edf77c11616b2d33c8b09ff8c177e3 Mon Sep 17 00:00:00 2001 From: tusharmagar Date: Tue, 31 Mar 2026 12:02:59 +0530 Subject: [PATCH] Add tool search functionality to settings dialog Implemented a debounced search feature for tools within the toolkit, allowing users to filter tools based on a search query. Added state management for search results and loading status. Updated the UI to accommodate the search input and results display. Additionally, modified the API call to use 'query' instead of 'search' for consistency. --- .../src/components/settings-dialog.tsx | 139 ++++++++++++++++-- apps/x/packages/core/src/composio/client.ts | 2 +- 2 files changed, 125 insertions(+), 16 deletions(-) diff --git a/apps/x/apps/renderer/src/components/settings-dialog.tsx b/apps/x/apps/renderer/src/components/settings-dialog.tsx index 3e37cb6d..72df13a2 100644 --- a/apps/x/apps/renderer/src/components/settings-dialog.tsx +++ b/apps/x/apps/renderer/src/components/settings-dialog.tsx @@ -754,6 +754,12 @@ function ToolsLibrarySettings({ dialogOpen }: { dialogOpen: boolean }) { const [toolsLoading, setToolsLoading] = useState(null) const [enabledToolSlugs, setEnabledToolSlugs] = useState>(new Set()) + // Tool search state (per-toolkit, server-side via Composio API) + const [toolSearchQuery, setToolSearchQuery] = useState("") + const [toolSearchResults, setToolSearchResults] = useState(null) + const [toolSearchLoading, setToolSearchLoading] = useState(false) + const toolSearchTimerRef = React.useRef | null>(null) + // Check API key configuration const checkApiKey = useCallback(async () => { try { @@ -909,6 +915,53 @@ function ToolsLibrarySettings({ dialogOpen }: { dialogOpen: boolean }) { } } + // Search tools within a toolkit (debounced, server-side) + const handleToolSearch = useCallback((toolkitSlug: string, query: string) => { + setToolSearchQuery(query) + + // Clear pending timer + if (toolSearchTimerRef.current) { + clearTimeout(toolSearchTimerRef.current) + toolSearchTimerRef.current = null + } + + // Empty query: clear search results, show all tools + if (!query.trim()) { + setToolSearchResults(null) + setToolSearchLoading(false) + return + } + + setToolSearchLoading(true) + + // Debounce 350ms before hitting the API + toolSearchTimerRef.current = setTimeout(async () => { + try { + const result = await window.ipc.invoke("composio:list-toolkit-tools", { + toolkitSlug, + search: query.trim(), + }) + setToolSearchResults(result.items) + } catch { + toast.error("Search failed") + setToolSearchResults(null) + } finally { + setToolSearchLoading(false) + } + }, 350) + }, []) + + // Clear tool search when switching toolkits + useEffect(() => { + setToolSearchQuery("") + setToolSearchResults(null) + setToolSearchLoading(false) + if (toolSearchTimerRef.current) { + clearTimeout(toolSearchTimerRef.current) + toolSearchTimerRef.current = null + } + }, [expandedToolkit]) + // Toggle toolkit expansion const handleToggleToolkit = (toolkitSlug: string) => { if (expandedToolkit === toolkitSlug) { @@ -1059,7 +1112,7 @@ function ToolsLibrarySettings({ dialogOpen }: { dialogOpen: boolean }) { Loading toolkits... ) : ( -
+
{filteredToolkits.map((toolkit) => { const isConnected = connectedToolkits.has(toolkit.slug) const isConnecting = connectingToolkit === toolkit.slug @@ -1069,8 +1122,15 @@ function ToolsLibrarySettings({ dialogOpen }: { dialogOpen: boolean }) { const enabledCount = tools.filter(t => enabledToolSlugs.has(t.slug)).length const allEnabled = tools.length > 0 && enabledCount === tools.length + // Use search results when actively searching, otherwise show all tools + const displayTools = (isExpanded && toolSearchResults !== null) ? toolSearchResults : tools + const isSearching = isExpanded && toolSearchQuery.trim().length > 0 + return ( -
+
{/* Toolkit card header */} + )} +
+ )} + + {/* Search results summary */} + {isSearching && !toolSearchLoading && toolSearchResults !== null && ( +
+ {toolSearchResults.length} + {toolSearchResults.length === 1 ? 'tool' : 'tools'} matching “{toolSearchQuery}” +
+ )} + + {/* Tool list */} {isLoadingTools ? ( -
+
Loading tools...
- ) : tools.length === 0 ? ( -

No tools found

+ ) : displayTools.length === 0 ? ( +
+

+ {isSearching + ? `No tools found for "${toolSearchQuery}"` + : "No tools found" + } +

+ {isSearching && ( + + )} +
) : ( -
- {tools.map((tool) => { +
+ {displayTools.map((tool) => { const isEnabled = enabledToolSlugs.has(tool.slug) return (
{tool.name}
-
+
{tool.description}
diff --git a/apps/x/packages/core/src/composio/client.ts b/apps/x/packages/core/src/composio/client.ts index 8f0dc593..eb367e7c 100644 --- a/apps/x/packages/core/src/composio/client.ts +++ b/apps/x/packages/core/src/composio/client.ts @@ -314,7 +314,7 @@ export async function listToolkitToolsDetailed( url.searchParams.set('toolkit_slug', toolkitSlug); url.searchParams.set('limit', '200'); if (searchQuery) { - url.searchParams.set('search', searchQuery); + url.searchParams.set('query', searchQuery); } console.log(`[Composio] Listing tools (detailed) for toolkit: ${toolkitSlug}`);