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}`);