Elements

23 / 37 · Agents

Agent status

One pill that always answers: what is it doing, and for how long.

Refactoring composer0s

Installation

npx shadcn@latest add "@assistant-ui/elements-agent-status"

Usage

import { AgentStatus } from "@/components/elements/agent-status";

<AgentStatus state="working" label="Editing composer.tsx" elapsed="8s" />

Props

state*AgentStateVisual mode of the pill: working, waiting, or done with a check.
label*stringShort status line describing what the agent is doing.
elapsedstringElapsed time shown in mono when provided.
classNamestringExtra classes merged onto the root.

Source

agent-status.tsx
"use client";

import { CheckIcon, PauseIcon, RotateCcwIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { ghostButton, mono, paper } from "./surfaces";

export type AgentState = "working" | "waiting" | "done";

export interface StatusStep {
  state: AgentState;
  label: string;
}

export function AgentStatus({
  state,
  label,
  elapsed,
  className,
}: {
  state: AgentState;
  label: string;
  elapsed?: string;
  className?: string;
}) {
  return (
    <div
      className={cn(
        paper,
        "flex items-center gap-2.5 rounded-full py-1.5 ps-3.5 pe-1.5",
        className,
      )}
    >
      {state === "done" ? (
        <CheckIcon aria-hidden className="size-3 shrink-0 text-emerald-500" />
      ) : (
        <span
          aria-hidden
          className={cn(
            "size-1.5 shrink-0 rounded-full motion-reduce:animate-none",
            state === "working"
              ? "animate-pulse bg-blue-500 dark:bg-blue-400"
              : "bg-foreground/25 animate-pulse",
          )}
        />
      )}
      <span
        key={label}
        className="fade-in blur-in-[2px] animate-in max-w-44 truncate text-xs duration-300 motion-reduce:animate-none"
      >
        {label}
      </span>
      {elapsed !== undefined && state !== "done" && (
        <span className={cn(mono, "text-foreground/30 tabular-nums")}>
          {elapsed}
        </span>
      )}
      <button
        type="button"
        aria-label={state === "done" ? "Run again" : "Pause agent"}
        className={cn(ghostButton, "size-6")}
      >
        {state === "done" ? (
          <RotateCcwIcon className="size-3" />
        ) : (
          <PauseIcon className="size-3" />
        )}
      </button>
    </div>
  );
}