Installation
npx shadcn@latest add @pixeldosa/run-inboxRequires `cn` at `@/lib/utils`. Phrase `note` as the thing a person needs to decide or know, not as a status code.
Usage
"use client";
import { RunInbox } from "@/components/ui/run-inbox";
<RunInbox runs={runs} onOpen={open} onStop={stop} />
Why it exists
The way people work with background agents now is: give it a job, close the laptop, come back later. An inbox is the familiar shape for that — and its familiar failure is strict chronology, where the run that has been waiting three hours for an answer sits below the one that finished a minute ago.
Features
- Grouped by what the run needs, not when it happened. Waiting on you comes first, whatever its age, then problems, then finished, then scheduled.
- Counts per group, so the size of the pile is visible before you scroll.
- Only running rows move. Rows waiting on you hold still, in the waiting colour.
- Stop appears only on running rows, so it can't be aimed at a finished one.
- Unread changes weight and is announced, rather than creating a separate "new" section.
Props
| Prop | Type | Notes |
|---|---|---|
runs | { id, title, state, agent?, note?, time?, unread? }[] | state is needsYou, running, scheduled, done or failed. time is formatted by you. |
onOpen | (id: string) => void | Required. |
onStop | (id: string) => void | Shown on running rows. |
emptyLabel | string | Shown when there are no runs. |
title | string | Defaults to "Runs". |
headingLevel | 2 | 3 | 4 | 5 | 6 | Defaults to 3. |
Accessibility
- A labelled
<section>; each group is a heading with its count. - Each run's title is a button, so keyboard users move through runs, not decorations.
- "Not opened yet" is announced for unread rows rather than shown only as weight.
- The running dot's pulse stops under
prefers-reduced-motion.
When not to use
- The steps inside one run: use
Agent Planwith statuses. - A notification feed: this is a work queue, not an activity stream.
- Hundreds of runs: at that size you need search and filters around it.
why it's built this way
Engineering Notes
Grouping by state rather than sorting by time is the whole component, and it is why this is not a generic list: the research on agent management interfaces names the inbox's scaling problem exactly, and a pile ordered by recency buries the only rows that block progress. Counts sit on the group headers instead of a single unread badge, because 'four waiting on you' and 'four finished' are different feelings and one number cannot carry both. `unread` only changes weight and adds a screen-reader phrase rather than driving a separate 'new' section, since a run's state matters more than whether it has been opened. Stop appears only on running rows, so the control cannot be aimed at a run that has already ended.
how it moves, and when it doesn't
Motion Notes
Motion means the machine is busy: only a running row's dot pulses. Rows waiting on a person hold completely still in the waiting colour, which is the turn-taking grammar applied to a list — the eye is drawn by stillness and position rather than by something blinking for attention. Under `prefers-reduced-motion` the pulse stops and state is carried by colour, position and words.