Tool Call Chip
An inline pill naming an in-flight tool call that resolves into a result summary.
Searching "tailwind v4 changelog"
"use client";
import * as React from "react";
import { AnimatePresence, motion } from "motion/react";
import { AlertCircle, Check, Code2, FileText, Globe, Loader2, Search } from "lucide-react";
import { cn } from "@/lib/utils";
const ICONS = {
search: Search,
file: FileText,
code: Code2,
network: Globe,
} as const;
export type ToolCallKind = keyof typeof ICONS;
export type ToolCallStatus = "running" | "success" | "error";
export interface ToolCallChipProps {
kind?: ToolCallKind;
verb: string;
target: string;
status: ToolCallStatus;
/** Replaces the verb + target label once status is "success" or "error". */
result?: string;
className?: string;
}
export function ToolCallChip({
kind = "search",
verb,
target,
status,
result,
className,
}: ToolCallChipProps) {
const Icon = ICONS[kind];
return (
<div
className={cn(
"inline-flex max-w-full items-center gap-2 rounded-full border bg-card px-3 py-1.5 text-xs",
className
)}
>
<Icon className="size-3.5 shrink-0 text-muted-foreground" aria-hidden />
<span aria-live="polite" className="min-w-0 flex-1 truncate">
{status === "running" ? (
<>
{verb} <span className="text-muted-foreground">"{target}"</span>
</>
) : (
(result ?? `${verb} "${target}"`)
)}
</span>
<AnimatePresence mode="wait" initial={false}>
<motion.span
key={status}
initial={{ opacity: 0, scale: 0.7 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.7 }}
transition={{ duration: 0.15 }}
className="shrink-0"
>
{status === "running" && (
<Loader2 className="size-3.5 animate-spin text-muted-foreground" aria-hidden />
)}
{status === "success" && (
<Check className="size-3.5 text-emerald-600 dark:text-emerald-400" aria-hidden />
)}
{status === "error" && <AlertCircle className="size-3.5 text-destructive" aria-hidden />}
</motion.span>
</AnimatePresence>
</div>
);
}
A UX spec for this pattern — written for agents implementing or reusing it, not the code.
# Tool Call Chip
## Summary
A compact inline pill shown while a specific, named tool call is in flight — a verb, its target, and a spinner — that resolves in place into a short result summary the instant the call finishes. Unlike the Thinking Loader's generic "the system is working" signal, this names the concrete action actually happening right now.
## When to use
- The agent is mid-call on a specific tool or function (a web search, a file read, a code execution, an API request) and the target is worth naming (the query, the filename, the endpoint).
- Inside an Expandable Trace or a message stream, as the live version of what becomes a completed step once resolved.
## When not to use
- For the overall "agent is working" state when no single tool call is active — use a Thinking Loader for that; don't invent a fake tool name to fill the gap.
- Stacked several deep for sub-steps of one logical call. One chip per call actually in flight — batch or fold finished ones into a trace instead of leaving a pile of chips on screen.
- When the target is sensitive and shouldn't be echoed verbatim (e.g. a raw credential) — summarize instead of printing it raw.
## Anatomy
- Icon: represents the tool's kind (search, file, code, network, ...).
- Label: a present-tense verb plus its target, e.g. `Searching "tailwind v4 changelog"`.
- State indicator: a small spinner while running; swaps to a check icon on success or an alert icon on failure.
- Resolved label: replaces the in-progress label with a brief result summary, e.g. `Searched — 4 results`.
## Behavior
- Appears the instant the call is dispatched — don't wait for a response to show that work has started.
- The target truncates with an ellipsis if it's long; never wraps to a second line.
- On completion, the spinner swaps to a check (or alert, on error) and the label updates to the result summary in the same chip — it does not get replaced by a new element.
- Once resolved, the chip stays visible as part of the record rather than vanishing; it commonly becomes one row of a subsequent Expandable Trace.
- Multiple sequential calls in one turn render as multiple chips in order, each independently transitioning from running to resolved.
## Content guidelines
- Verb + straight-quoted target, one line: `Reading "component.tsx"`, `Calling "get_weather"`.
- Keep the resolved summary equally short — a count or outcome, not a restatement of the whole result.
- Stay truthful: never show a tool name or target that doesn't match what's actually running.
## Accessibility
- Wrap the label and state indicator in an `aria-live="polite"` region so the running → resolved transition is announced.
- Don't rely on the spinner-to-check swap alone to signal completion — the label text change must carry the same meaning for non-visual users.
- Give the icon `aria-hidden` and let the text label carry the accessible name.
## Related patterns
- **Thinking Loader** — Use it instead when the agent is working but no specific tool call is active.
- **Expandable Trace** — Use it as well once the turn completes, where a sequence of resolved chips ends up.
- **Tool Approval** — Use it before the chip when the call needs permission to run.
- **Terminal Stream** — Use it instead when the call's full output matters and should be readable live.
Do’s and don’ts
Do use it for
- The agent is mid-call on a specific tool or function (a web search, a file read, a code execution, an API request) and the target is worth naming (the query, the filename, the endpoint).
- Inside an Expandable Trace or a message stream, as the live version of what becomes a completed step once resolved.
Don’t use it for
- For the overall "agent is working" state when no single tool call is active — use a Thinking Loader for that; don't invent a fake tool name to fill the gap.
- Stacked several deep for sub-steps of one logical call. One chip per call actually in flight — batch or fold finished ones into a trace instead of leaving a pile of chips on screen.
- When the target is sensitive and shouldn't be echoed verbatim (e.g. a raw credential) — summarize instead of printing it raw.
Related patterns
Thinking Loader
Use it instead when the agent is working but no specific tool call is active.
Expandable Trace
Use it as well once the turn completes, where a sequence of resolved chips ends up.
Tool Approval
Use it before the chip when the call needs permission to run.
Terminal Stream
Use it instead when the call's full output matters and should be readable live.