shadimage
scrimui
uiagent-statussource: ok · refreshed

Agent Status

Status for an AI agent working on its own — running, waiting on you, completed or failed, with what it did last.

npx shadcn add "https://scrimui.dev/r/agent-status.json"
free

Where it goes

stats

Interaction

animated-counter

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light

Registry dependencies

badgebuttoncard

Files1 · source cached 10/8/2026, 9:25:33 AM

src/showcase/agent-status/agent-status.tsx
"use client";

import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import * as React from "react";

/* ------------------------------------------------------------------ */
/* Types                                                               */
/* ------------------------------------------------------------------ */

export type AgentState = "running" | "waiting" | "completed" | "failed";

export type AgentStatusProps = {
  name: string;
  status: AgentState;
  action?: string;
  progress?: number;
  elapsed?: string;
  icon?: React.ReactNode;
  onStop?: () => void;
  onRetry?: () => void;
  className?: string;
};

/* ------------------------------------------------------------------ */
/* Icons                                                               */
/* ------------------------------------------------------------------ */

function BotIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="16" height="16" {...props}>
      <rect width="18" height="10" x="3" y="8" rx="2" />
      <path d="M12 8V4a2 2 0 0 1 2-2h2" />
      <circle cx="9" cy="13" r="0.5" fill="currentColor" />
      <circle cx="15" cy="13" r="0.5" fill="currentColor" />
      <path d="M5 11v6M19 11v6M9 17h6" />
    </svg>
  );
}

function CheckIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="12" height="12" {...props}>
      <path d="M20 6 9 17l-5-5" />
    </svg>
  );
}

function XIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="12" height="12" {...props}>
      <path d="M18 6 6 18M6 6l12 12" />
    </svg>
  );
}

function PauseIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg viewBox="0 0 24 24" fill="currentColor" width="11" height="11" {...props}>
      <rect x="6" y="5" width="4" height="14" rx="1" />
      <rect x="14" y="5" width="4" height="14" rx="1" />
    </svg>
  );
}

function StopIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg viewBox="0 0 24 24" fill="currentColor" width="10" height="10" {...props}>
      <rect x="6" y="6" width="12" height="12" rx="2" />
    </svg>
  );
}

/* ------------------------------------------------------------------ */
/* Status pill + progress                                              */
/* ------------------------------------------------------------------ */

const STATUS_STYLES: Record<AgentState, { pill: string; icon: React.ReactNode; label: string }> = {
  running: {
    pill: "bg-muted text-foreground",
    icon: (
      <span className="h-2.5 w-2.5 animate-spin rounded-full border-[1.5px] border-current border-t-transparent" />
    ),
    label: "Running",
  },
  waiting: {
    pill: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400",
    icon: <PauseIcon />,
    label: "Waiting",
  },
  completed: {
    pill: "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-400",
    icon: <CheckIcon />,
    label: "Completed",
  },
  failed: {
    pill: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-400",
    icon: <XIcon />,
    label: "Failed",
  },
};

/* ------------------------------------------------------------------ */
/* AgentStatus                                                         */
/* ------------------------------------------------------------------ */

export function AgentStatus({
  name,
  status,
  action,
  progress,
  elapsed,
  icon,
  onStop,
  onRetry,
  className = "",
}: AgentStatusProps) {
  const s = STATUS_STYLES[status];

  return (
    <Card
      className={`gap-0 py-0 rounded-xl border border-border bg-card p-3.5 transition-colors ${
        status === "failed"
          ? "border-red-200 dark:border-red-900/60"
          : status === "running"
            ? "border-border"
            : ""
      } ${className}`}
    >
      <div className="flex items-center gap-3">
        <span
          className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-lg ${
            status === "failed"
              ? "bg-red-100 text-red-600 dark:bg-red-900/40 dark:text-red-400"
              : status === "completed"
                ? "bg-emerald-100 text-emerald-600 dark:bg-emerald-900/40 dark:text-emerald-400"
                : "bg-muted text-muted-foreground"
          }`}
        >
          {icon ?? <BotIcon />}
        </span>

        <div className="min-w-0 flex-1">
          <div className="flex flex-wrap items-center gap-2">
            <span className="truncate text-sm font-medium text-foreground">
              {name}
            </span>
            <Badge variant="secondary" className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs ${s.pill}`}>
              {s.icon}
              {s.label}
            </Badge>
            {elapsed && <span className="text-xs tabular-nums text-muted-foreground">{elapsed}</span>}
          </div>
          {action && (
            <p className="mt-0.5 truncate text-sm text-muted-foreground">{action}</p>
          )}
        </div>

        {status === "running" && onStop && (
          <Button variant="outline" size="icon-sm"
            type="button"
            onClick={onStop}
            aria-label={`Stop ${name}`}
            className="min-h-6 min-w-6 flex h-8 w-7 shrink-0 items-center justify-center rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
          >
            <StopIcon />
          </Button>
        )}
        {status === "failed" && onRetry && (
          <Button variant="outline" size="sm"
            type="button"
            onClick={onRetry}
            className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-border px-2.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
          >
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="12" height="12">
              <path d="M21 12a9 9 0 1 1-2.64-6.36L21 8" />
              <path d="M21 3v5h-5" />
            </svg>
            Retry
          </Button>
        )}
      </div>

      {/* Progress bar */}
      {status === "running" && progress !== undefined && (
        <div className="mt-3 h-1 overflow-hidden rounded-full bg-muted">
          <div
            className="h-full rounded-full bg-primary transition-[color,background-color,border-color,box-shadow] duration-300"
            style={{ width: `${Math.min(100, Math.max(0, progress))}%` }}
          />
        </div>
      )}
    </Card>
  );
}

More from scrimui

Browse all components in this library →