Tool
Tool is a React and Tailwind CSS component from Prompt Kit, licensed MIT. Copy it and paste it into your project.
Prompt Kit
MIT
other
What it is
Tool is a React and Tailwind CSS component from Prompt Kit, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i lucide-react
Licence
This component comes from Prompt Kit and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Tool
"use client"
import { Button } from "@/components/ui/button"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
import { cn } from "@/lib/utils"
import {
CheckCircle,
ChevronDown,
Loader2,
Settings,
XCircle,
} from "lucide-react"
import { useState } from "react"
export type ToolPart = {
type: string
state:
| "input-streaming"
| "input-available"
| "output-available"
| "output-error"
input?: Record<string, unknown>
output?: Record<string, unknown>
toolCallId?: string
errorText?: string
}
export type ToolProps = {
toolPart: ToolPart
defaultOpen?: boolean
className?: string
}
const Tool = ({ toolPart, defaultOpen = false, className }: ToolProps) => {
const [isOpen, setIsOpen] = useState(defaultOpen)
const { state, input, output, toolCallId } = toolPart
const getStateIcon = () => {
switch (state) {
case "input-streaming":
return <Loader2 className="h-4 w-4 animate-spin text-blue-500" />
case "input-available":
return <Settings className="h-4 w-4 text-orange-500" />
case "output-available":
return <CheckCircle className="h-4 w-4 text-green-500" />
case "output-error":
return <XCircle className="h-4 w-4 text-red-500" />
default:
return <Settings className="text-muted-foreground h-4 w-4" />
}
}
const getStateBadge = () => {
const baseClasses = "px-2 py-1 rounded-full text-xs font-medium"
switch (state) {
case "input-streaming":
return (
<span
className={cn(
baseClasses,
"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"
)}
>
Processing
</span>
)
case "input-available":
return (
<span
className={cn(
baseClasses,
"bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400"
)}
>
Ready
</span>
)
case "output-available":
return (
<span
className={cn(
baseClasses,
"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400"
)}
>
Completed
</span>
)
case "output-error":
return (
<span
className={cn(
baseClasses,
"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
)}
>
Error
</span>
)
default:
return (
<span
className={cn(
baseClasses,
"bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
)}
>
Pending
</span>
)
}
}
const formatValue = (value: unknown): string => {
if (value === null) return "null"
if (value === undefined) return "undefined"
if (typeof value === "string") return value
if (typeof value === "object") {
return JSON.stringify(value, null, 2)
}
return String(value)
}
return (
<div
className={cn(
"border-border mt-3 overflow-hidden rounded-lg border",
className
)}
>
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger asChild>
<Button
variant="ghost"
className="bg-background h-auto w-full justify-between rounded-b-none px-3 py-2 font-normal"
>
<div className="flex items-center gap-2">
{getStateIcon()}
<span className="font-mono text-sm font-medium">
{toolPart.type}
</span>
{getStateBadge()}
</div>
<ChevronDown className={cn("h-4 w-4", isOpen && "rotate-180")} />
</Button>
</CollapsibleTrigger>
<CollapsibleContent
className={cn(
"border-border border-t",
"data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down overflow-hidden"
)}
>
<div className="bg-background space-y-3 p-3">
{input && Object.keys(input).length > 0 && (
<div>
<h4 className="text-muted-foreground mb-2 text-sm font-medium">
Input
</h4>
<div className="bg-background rounded border p-2 font-mono text-sm">
{Object.entries(input).map(([key, value]) => (
<div key={key} className="mb-1">
<span className="text-muted-foreground">{key}:</span>{" "}
<span>{formatValue(value)}</span>
</div>
))}
</div>
</div>
)}
{output && (
<div>
<h4 className="text-muted-foreground mb-2 text-sm font-medium">
Output
</h4>
<div className="bg-background max-h-60 overflow-auto rounded border p-2 font-mono text-sm">
<pre className="whitespace-pre-wrap">
{formatValue(output)}
</pre>
</div>
</div>
)}
{state === "output-error" && toolPart.errorText && (
<div>
<h4 className="mb-2 text-sm font-medium text-red-500">Error</h4>
<div className="bg-background rounded border border-red-200 p-2 text-sm dark:border-red-950 dark:bg-red-900/20">
{toolPart.errorText}
</div>
</div>
)}
{state === "input-streaming" && (
<div className="text-muted-foreground text-sm">
Processing tool call...
</div>
)}
{toolCallId && (
<div className="text-muted-foreground border-t border-blue-200 pt-2 text-xs">
<span className="font-mono">Call ID: {toolCallId}</span>
</div>
)}
</div>
</CollapsibleContent>
</Collapsible>
</div>
)
}
export { Tool }