diff --git a/apps/docs/content/components/(chatbot)/tool.mdx b/apps/docs/content/components/(chatbot)/tool.mdx index a56f31c4..ed33c886 100644 --- a/apps/docs/content/components/(chatbot)/tool.mdx +++ b/apps/docs/content/components/(chatbot)/tool.mdx @@ -176,6 +176,7 @@ export async function POST(req: Request) { ## Features - Collapsible interface for showing/hiding tool details +- Customizable tool icons, with support for mapping icons by tool name - Visual status indicators with icons and badges - Support for multiple tool execution states (pending, running, completed, error) - Formatted parameter display with JSON syntax highlighting @@ -211,6 +212,19 @@ Shows a tool that encountered an error during execution. Opens by default to dis +### Custom Icons + +Pass a function to the `icon` prop to render a custom icon. The function receives an object with: + +- `type`: the tool part type +- `state`: the current execution state +- `toolName`: the derived tool name +- `className`: default sizing and color styles + +Use `toolName` to map different icons by tool type. When the function returns `null`, the default wrench icon is used. + + + ## Props ### `` @@ -229,6 +243,10 @@ Shows a tool that encountered an error during execution. Opens by default to dis ReactNode)", + }, title: { description: "Custom title to display instead of the derived tool name.", type: "string", diff --git a/packages/elements/src/tool.tsx b/packages/elements/src/tool.tsx index 9a22010e..32959f40 100644 --- a/packages/elements/src/tool.tsx +++ b/packages/elements/src/tool.tsx @@ -1,5 +1,8 @@ "use client"; +import type { DynamicToolUIPart, ToolUIPart } from "ai"; +import type { ComponentProps, ReactNode } from "react"; + import { Badge } from "@repo/shadcn-ui/components/ui/badge"; import { Collapsible, @@ -7,7 +10,6 @@ import { CollapsibleTrigger, } from "@repo/shadcn-ui/components/ui/collapsible"; import { cn } from "@repo/shadcn-ui/lib/utils"; -import type { DynamicToolUIPart, ToolUIPart } from "ai"; import { CheckCircleIcon, ChevronDownIcon, @@ -16,7 +18,6 @@ import { WrenchIcon, XCircleIcon, } from "lucide-react"; -import type { ComponentProps, ReactNode } from "react"; import { isValidElement } from "react"; import { CodeBlock } from "./code-block"; @@ -32,7 +33,15 @@ export const Tool = ({ className, ...props }: ToolProps) => ( export type ToolPart = ToolUIPart | DynamicToolUIPart; +export type ToolIconProps = { + type: ToolPart["type"]; + state: ToolPart["state"]; + toolName: string; + className: string; +}; + export type ToolHeaderProps = { + icon?: ReactNode | ((props: ToolIconProps) => ReactNode); title?: string; className?: string; } & ( @@ -73,6 +82,7 @@ export const getStatusBadge = (status: ToolPart["state"]) => ( export const ToolHeader = ({ className, + icon, title, type, state, @@ -82,6 +92,12 @@ export const ToolHeader = ({ const derivedName = type === "dynamic-tool" ? toolName : type.split("-").slice(1).join("-"); + const iconClassName = "size-4 shrink-0 text-muted-foreground"; + const resolvedIcon = + typeof icon === "function" + ? icon({ type, state, toolName: derivedName, className: iconClassName }) + : icon; + return (
- + {resolvedIcon ?? } {title ?? derivedName} {getStatusBadge(state)}
diff --git a/packages/examples/src/tool-custom-icons.tsx b/packages/examples/src/tool-custom-icons.tsx new file mode 100644 index 00000000..f2029899 --- /dev/null +++ b/packages/examples/src/tool-custom-icons.tsx @@ -0,0 +1,77 @@ +"use client"; + +import type { ReactNode } from "react"; +import { Tool, ToolContent, ToolHeader, ToolInput } from "@repo/elements/tool"; +import { cn } from "@repo/shadcn-ui/lib/utils"; +import { DatabaseIcon, SearchIcon, WrenchIcon } from "lucide-react"; +import type { LucideProps } from "lucide-react"; + +const toolIcons: Record ReactNode> = { + database_query: DatabaseIcon, + web_search: SearchIcon, +}; + +const renderIcon = ({ toolName, className }: { toolName: string; className: string }) => { + const Icon = toolIcons[toolName]; + return Icon ? : null; +}; + +const Example = () => ( +
+ + + + = ?", + params: ["2024-01-01"], + }} + /> + + + + + + + + + + } + state="input-available" + title="custom_style" + type="tool-custom_style" + /> + + + + + + + + + + +
+); + +export default Example;