diff --git a/ui/admin/app/components/tools/ToolCatalog.tsx b/ui/admin/app/components/tools/ToolCatalog.tsx
index 60eeecab2..b85b29ae2 100644
--- a/ui/admin/app/components/tools/ToolCatalog.tsx
+++ b/ui/admin/app/components/tools/ToolCatalog.tsx
@@ -1,19 +1,15 @@
import { AlertTriangleIcon, PlusIcon } from "lucide-react";
-import { useCallback } from "react";
import useSWR from "swr";
-import { ToolReference } from "~/lib/model/toolReferences";
import { ToolReferenceService } from "~/lib/service/api/toolreferenceService";
import { cn } from "~/lib/utils";
-import { ToolCategoryHeader } from "~/components/tools/ToolCategoryHeader";
-import { ToolItem } from "~/components/tools/ToolItem";
+import { ToolCatalogGroup } from "~/components/tools/ToolCatalogGroup";
import { LoadingSpinner } from "~/components/ui/LoadingSpinner";
import { Button } from "~/components/ui/button";
import {
Command,
CommandEmpty,
- CommandGroup,
CommandInput,
CommandList,
} from "~/components/ui/command";
@@ -45,34 +41,6 @@ export function ToolCatalog({
{ fallbackData: {} }
);
- const handleSelect = useCallback(
- (toolId: string) => {
- if (!tools.includes(toolId)) {
- onUpdateTools([...tools, toolId]);
- }
- },
- [tools, onUpdateTools]
- );
-
- const handleSelectBundle = useCallback(
- (bundleToolId: string, categoryTools: ToolReference[]) => {
- if (tools.includes(bundleToolId)) {
- onUpdateTools(tools.filter((tool) => tool !== bundleToolId));
- return;
- }
-
- const toolsToRemove = new Set(categoryTools.map((tool) => tool.id));
-
- const newTools = [
- ...tools.filter((tool) => !toolsToRemove.has(tool)),
- bundleToolId,
- ];
-
- onUpdateTools(newTools);
- },
- [tools, onUpdateTools]
- );
-
if (isLoading) return ;
return (
@@ -98,39 +66,17 @@ export function ToolCatalog({
No results found.
-
+ {" "}
{Object.entries(toolCategories).map(
([category, categoryTools]) => (
-
- }
- >
- {categoryTools.tools.map((categoryTool) => (
-
- handleSelect(categoryTool.id)
- }
- />
- ))}
-
+ category={category}
+ tools={categoryTools}
+ selectedTools={tools}
+ onUpdateTools={onUpdateTools}
+ />
)
)}
diff --git a/ui/admin/app/components/tools/ToolCatalogGroup.tsx b/ui/admin/app/components/tools/ToolCatalogGroup.tsx
new file mode 100644
index 000000000..28676cba6
--- /dev/null
+++ b/ui/admin/app/components/tools/ToolCatalogGroup.tsx
@@ -0,0 +1,91 @@
+import { useState } from "react";
+
+import { ToolCategory } from "~/lib/service/api/toolreferenceService";
+import { cn } from "~/lib/utils";
+
+import { ToolItem } from "~/components/tools/ToolItem";
+import { CommandGroup } from "~/components/ui/command";
+
+export function ToolCatalogGroup({
+ category,
+ tools,
+ selectedTools,
+ onUpdateTools,
+}: {
+ category: string;
+ tools: ToolCategory;
+ selectedTools: string[];
+ onUpdateTools: (tools: string[]) => void;
+}) {
+ const handleSelect = (toolId: string) => {
+ if (selectedTools.includes(toolId)) {
+ onUpdateTools(selectedTools.filter((tool) => tool !== toolId));
+ }
+
+ const newTools = selectedTools
+ .filter((tool) => tool !== tools.bundleTool?.id)
+ .concat(toolId);
+
+ onUpdateTools(newTools);
+ };
+
+ const handleSelectBundle = (bundleToolId: string) => {
+ if (selectedTools.includes(bundleToolId)) {
+ onUpdateTools(
+ selectedTools.filter((tool) => tool !== bundleToolId)
+ );
+ return;
+ }
+
+ const toolsToRemove = new Set(tools.tools.map((tool) => tool.id));
+
+ const newTools = [
+ ...selectedTools.filter((tool) => !toolsToRemove.has(tool)),
+ bundleToolId,
+ ];
+
+ onUpdateTools(newTools);
+ };
+
+ const [expanded, setExpanded] = useState(() => {
+ const set = new Set(tools.tools.map((tool) => tool.id));
+ return selectedTools.some((tool) => set.has(tool));
+ });
+
+ return (
+
+ {tools.bundleTool && (
+ handleSelectBundle(tools.bundleTool!.id)}
+ expanded={expanded}
+ onExpand={setExpanded}
+ isBundle
+ />
+ )}
+
+ {(expanded || !tools.bundleTool) &&
+ tools.tools.map((categoryTool) => (
+ handleSelect(categoryTool.id)}
+ />
+ ))}
+
+ );
+}
diff --git a/ui/admin/app/components/tools/ToolCategoryHeader.tsx b/ui/admin/app/components/tools/ToolCategoryHeader.tsx
deleted file mode 100644
index f6b97a4d2..000000000
--- a/ui/admin/app/components/tools/ToolCategoryHeader.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { CheckIcon } from "lucide-react";
-
-import { ToolReference } from "~/lib/model/toolReferences";
-import { ToolCategory } from "~/lib/service/api/toolreferenceService";
-
-import { ToolTooltip } from "~/components/tools/ToolTooltip";
-import { Switch } from "~/components/ui/switch";
-
-type ToolCategoryHeaderProps = {
- category: string;
- categoryTools: ToolCategory;
- tools: string[];
- onSelectBundle: (
- bundleToolId: string,
- categoryTools: ToolReference[]
- ) => void;
-};
-
-export function ToolCategoryHeader({
- category,
- categoryTools,
- tools,
- onSelectBundle,
-}: ToolCategoryHeaderProps) {
- return (
-
- {!categoryTools.bundleTool ? (
-
{category}
- ) : (
- <>
-
- {category}
- {tools.includes(categoryTools.bundleTool!.id) && (
-
- )}
-
-
-
-
- onSelectBundle(
- categoryTools.bundleTool!.id,
- categoryTools.tools
- )
- }
- />
-
-
- >
- )}
-
- );
-}
diff --git a/ui/admin/app/components/tools/ToolItem.tsx b/ui/admin/app/components/tools/ToolItem.tsx
index 23572c0e7..17d5af904 100644
--- a/ui/admin/app/components/tools/ToolItem.tsx
+++ b/ui/admin/app/components/tools/ToolItem.tsx
@@ -1,7 +1,10 @@
import { ToolReference } from "~/lib/model/toolReferences";
+import { cn } from "~/lib/utils";
import { ToolIcon } from "~/components/tools/ToolIcon";
import { ToolTooltip } from "~/components/tools/ToolTooltip";
+import { Button } from "~/components/ui/button";
+import { Checkbox } from "~/components/ui/checkbox";
import { CommandItem } from "~/components/ui/command";
type ToolItemProps = {
@@ -9,6 +12,10 @@ type ToolItemProps = {
isSelected: boolean;
isBundleSelected: boolean;
onSelect: () => void;
+ expanded?: boolean;
+ onExpand?: (expanded: boolean) => void;
+ className?: string;
+ isBundle?: boolean;
};
export function ToolItem({
@@ -16,29 +23,63 @@ export function ToolItem({
isSelected,
isBundleSelected,
onSelect,
+ expanded,
+ onExpand,
+ className,
+ isBundle,
}: ToolItemProps) {
return (
-
-
- {tool.name}
-
+
+
+
+
+
+
+ {tool.name}
+
+
+
+ {isBundle && (
+
+ )}
+
);
diff --git a/ui/admin/app/components/ui/button.tsx b/ui/admin/app/components/ui/button.tsx
index bd455b296..868574e1d 100644
--- a/ui/admin/app/components/ui/button.tsx
+++ b/ui/admin/app/components/ui/button.tsx
@@ -26,6 +26,8 @@ const buttonVariants = cva(
},
size: {
none: "",
+ link: "p-0",
+ "link-sm": "p-0 text-xs",
default: "h-9 px-4 py-2",
badge: "text-xs py-0.5 px-2",
sm: "h-8 px-3 text-xs",
diff --git a/ui/admin/app/lib/service/api/toolreferenceService.ts b/ui/admin/app/lib/service/api/toolreferenceService.ts
index 5605786ac..590711cff 100644
--- a/ui/admin/app/lib/service/api/toolreferenceService.ts
+++ b/ui/admin/app/lib/service/api/toolreferenceService.ts
@@ -47,7 +47,7 @@ async function getToolReferencesCategoryMap(type?: ToolReferenceType) {
};
}
- if (toolReference.metadata?.bundle) {
+ if (toolReference.metadata?.bundle === "true") {
result[category].bundleTool = toolReference;
} else {
result[category].tools.push(toolReference);