Inkbox

Component

Trace

An agent's steps as quiet 14px lines with a glyph, a running step that breathes and steps that open to their detail on a hairline.

npx -y github:anwarlemu/inkbox-brand-cli#release add trace
Preview
Read 3 new emails in acme@inkboxmail.com0.8s
Found @summarize and @translate, both public and accepting delegated tasks.
Sending the summary to ray@inkbox.aiCall +1 650 474 9720 with the result
Failed
Unlocked the vault0.2s
Retry scheduledin 30s
Linked

Usage

Use to show what an agent did or is doing: each TraceLine has a TraceIcon (a filled Phosphor glyph for the tool), a TraceTitle and an optional TraceDescription (a duration or a count). Set status running, pending or error. Put a step with detail in TraceCollapsible with a TraceCollapsibleTrigger around its line and the detail in TraceCollapsibleContent. Wrap a line in a link or button to make it open something.

Avoid

  • Bold or ink step titles
  • A spinner on every running step
  • Colors other than the muted purple and red
  • Detail that opens without its hairline

Exports: Trace, TraceCollapsible, TraceCollapsibleContent, TraceCollapsibleTrigger, TraceDescription, TraceIcon, TraceLine, TraceTitle. This component is a client component.

Dependencies: collapsible, @phosphor-icons/react.

Example

examples/trace.tsx
import { EnvelopeSimpleIcon, LightningIcon, MagnifyingGlassIcon, PaperPlaneTiltIcon, PhoneIcon, VaultIcon, WarningCircleIcon } from "@phosphor-icons/react/ssr";​import { Trace, TraceCollapsible, TraceCollapsibleContent, TraceCollapsibleTrigger, TraceDescription, TraceIcon, TraceLine, TraceTitle } from "@/components/ui/trace";​export function Preview() {  return (    <Trace className="mx-auto max-w-120">      <TraceLine>        <TraceIcon>          <EnvelopeSimpleIcon weight="fill" />        </TraceIcon>        <TraceTitle>Read 3 new emails in acme@inkboxmail.com</TraceTitle>        <TraceDescription>0.8s</TraceDescription>      </TraceLine>      <TraceCollapsible defaultOpen>        <TraceCollapsibleTrigger>          <TraceLine>            <TraceIcon>              <MagnifyingGlassIcon weight="bold" />            </TraceIcon>            <TraceTitle>Searched the Agent2Agent directory</TraceTitle>            <TraceDescription>2 results</TraceDescription>          </TraceLine>        </TraceCollapsibleTrigger>        <TraceCollapsibleContent>          Found <span className="font-mono text-fg">@summarize</span> and <span className="font-mono text-fg">@translate</span>, both public and accepting delegated tasks.        </TraceCollapsibleContent>      </TraceCollapsible>      <TraceLine status="running">        <TraceIcon>          <PaperPlaneTiltIcon weight="fill" />        </TraceIcon>        <TraceTitle>Sending the summary to ray@inkbox.ai</TraceTitle>      </TraceLine>      <TraceLine status="pending">        <TraceIcon>          <PhoneIcon weight="fill" />        </TraceIcon>        <TraceTitle>Call +1 650 474 9720 with the result</TraceTitle>      </TraceLine>    </Trace>  );}​export function Failed() {  return (    <Trace className="mx-auto max-w-120">      <TraceLine>        <TraceIcon>          <VaultIcon weight="fill" />        </TraceIcon>        <TraceTitle>Unlocked the vault</TraceTitle>        <TraceDescription>0.2s</TraceDescription>      </TraceLine>      <TraceCollapsible>        <TraceCollapsibleTrigger>          <TraceLine status="error">            <TraceIcon>              <WarningCircleIcon weight="fill" />            </TraceIcon>            <TraceTitle>Webhook delivery to https://example.com/inkbox failed</TraceTitle>          </TraceLine>        </TraceCollapsibleTrigger>        <TraceCollapsibleContent>          The endpoint answered <span className="font-mono text-fg">503 Service Unavailable</span>. Inkbox logs the failure and keeps processing mail.        </TraceCollapsibleContent>      </TraceCollapsible>      <TraceLine>        <TraceIcon>          <LightningIcon weight="fill" />        </TraceIcon>        <TraceTitle>Retry scheduled</TraceTitle>        <TraceDescription>in 30s</TraceDescription>      </TraceLine>    </Trace>  );}​export function Linked() {  return (    <Trace className="mx-auto max-w-120">      <a href="#message">        <TraceLine>          <TraceIcon>            <EnvelopeSimpleIcon weight="fill" />          </TraceIcon>          <TraceTitle>Opened Inkbox A2A: publish your Agent Card</TraceTitle>        </TraceLine>      </a>      <button type="button">        <TraceLine>          <TraceIcon>            <PaperPlaneTiltIcon weight="fill" />          </TraceIcon>          <TraceTitle>Drafted a reply</TraceTitle>        </TraceLine>      </button>    </Trace>  );}

Source

components/ui/trace.tsx
"use client";​import * as React from "react";import { CaretRightIcon } from "@phosphor-icons/react/ssr";​import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";import { cn } from "@/lib/utils";​// An agent's steps as it works (extrapolated from the console, Figma 427:2715: the inbox's muted dates and the// "Advanced settings" disclosure). Each line is 14px muted purple with an 18px glyph 8px before it; a line that is a// link or button turns ink under the pointer. A running step breathes (1.6s, held still with reduced motion), a pending// one is faded, a failed one is red. A step that can open shows a caret after its title that turns a quarter when open;// its detail hangs from a hairline 16px in, in 14px secondary ink, and opens with its height (200ms, the strong ease-out).function Trace({ className, ...props }: React.ComponentProps<"div">) {  return (    <div      data-slot="trace"      className={cn(        "flex w-full min-w-0 flex-col gap-2 *:has-[>[data-slot=trace-line]]:flex *:has-[>[data-slot=trace-line]]:w-fit *:has-[>[data-slot=trace-line]]:max-w-full *:has-[>[data-slot=trace-line]]:min-w-0 *:has-[>[data-slot=trace-line]]:rounded-xs *:has-[>[data-slot=trace-line]]:text-left *:has-[>[data-slot=trace-line]]:outline-none *:has-[>[data-slot=trace-line]]:focus-visible:outline-2 *:has-[>[data-slot=trace-line]]:focus-visible:outline-offset-2 *:has-[>[data-slot=trace-line]]:focus-visible:outline-focus-ring",        className,      )}      {...props}    />  );}​function TraceLine({ className, status, ...props }: React.ComponentProps<"span"> & { status?: "running" | "error" | "pending" }) {  return (    <span      data-slot="trace-line"      data-status={status}      className={cn(        "flex w-fit max-w-full min-w-0 items-center gap-2 font-sans text-body leading-body text-fg-muted transition-colors duration-150 ease-out in-[a,button]:cursor-pointer in-[a:hover,button:hover]:text-fg data-[status=error]:text-danger data-[status=error]:in-[a:hover,button:hover]:text-danger-strong data-[status=pending]:opacity-60 data-[status=running]:*:data-[slot=trace-title]:animate-pulse-soft motion-reduce:transition-none motion-reduce:data-[status=running]:*:data-[slot=trace-title]:animate-none",        className,      )}      {...props}    />  );}​function TraceIcon({ className, ...props }: React.ComponentProps<"span">) {  return (    <span      data-slot="trace-icon"      aria-hidden="true"      className={cn("flex size-4.5 shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4.5 [[data-slot=trace-title]~&]:-ml-1", className)}      {...props}    />  );}​function TraceTitle({ className, ...props }: React.ComponentProps<"span">) {  return <span data-slot="trace-title" className={cn("min-w-0 truncate", className)} {...props} />;}​/** A step's quiet detail at its end: a duration or a count, in the faint purple with even figures. */function TraceDescription({ className, ...props }: React.ComponentProps<"span">) {  return <span data-slot="trace-description" className={cn("shrink-0 text-fg-subtle tabular-nums", className)} {...props} />;}​function TraceCollapsible({  className,  ...props}: Omit<React.ComponentProps<typeof Collapsible>, "asChild" | "open"> & ({ open?: undefined } | { open: boolean; onOpenChange: NonNullable<React.ComponentProps<typeof Collapsible>["onOpenChange"]> })) {  return <Collapsible data-slot="trace-collapsible" className={cn("flex w-full min-w-0 flex-col", className)} {...props} />;}​function TraceCollapsibleTrigger({ className, children, ...props }: Omit<React.ComponentProps<typeof CollapsibleTrigger>, "asChild">) {  return (    <CollapsibleTrigger      data-slot="trace-collapsible-trigger"      className={cn(        "group/trace-collapsible-trigger flex w-fit max-w-full min-w-0 cursor-pointer items-center gap-1 rounded-xs text-left text-fg-muted outline-none transition-colors duration-150 ease-out hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring has-[[data-status=error]]:text-danger motion-reduce:transition-none",        className,      )}      {...props}    >      {children}      <TraceIcon className="transition-transform duration-200 ease-out group-aria-expanded/trace-collapsible-trigger:rotate-90 motion-reduce:transition-none">        <CaretRightIcon weight="bold" className="size-3.5" />      </TraceIcon>    </CollapsibleTrigger>  );}​function TraceCollapsibleContent({ className, children, ...props }: Omit<React.ComponentProps<typeof CollapsibleContent>, "asChild">) {  return (    <CollapsibleContent data-slot="trace-collapsible-content" className="min-w-0" {...props}>      <div data-slot="trace-collapsible-content-inner" className={cn("mt-2 mb-1 ml-2 min-w-0 border-l border-line-field pr-1 pl-4 font-sans text-body leading-body [overflow-wrap:anywhere] text-fg-secondary", className)}>        {children}      </div>    </CollapsibleContent>  );}​export { Trace, TraceCollapsible, TraceCollapsibleContent, TraceCollapsibleTrigger, TraceDescription, TraceIcon, TraceLine, TraceTitle };

Reference: Extrapolated from the Inkbox console (Figma 427:2715): the inbox list's muted text and the Advanced settings disclosure.