Agent Edit Banner
The strip under a document's header after an agent changed it — who, how many changes, when — with View changes, Undo and a dismiss.
- Status
- Since
0.25.38- Accessibility pattern
- polite live region, named buttons, decorative agent look
Last updated
Edited by Claude (via Manoj) 3 changes
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Agent Edit Banner before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify agent-edit-bannerInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/agent-edit-bannerRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/alert, @vegastack/button, @vegastack/person-avatar, @vegastack/popover, @vegastack/relative-time.
Usage
import { AgentEditBanner } from "@/components/ui/agent-edit-banner";
<AgentEditBanner
agent={{ name: "Claude", via: "Manoj" }}
changeCount={group.writes}
at={group.lastWriteAt}
status={undoing ? "undoing" : refused ? "refused" : "idle"}
onViewChanges={group.canDiff ? () => openDiff(group) : undefined}
onUndo={canEdit && group.canUndo ? () => undo(group) : undefined}
onOpenVersions={() => openVersions()}
onDismiss={() => dismiss(group.runIds)}
/>;The banner is presentational: the host owns the agent's runs, the undo, the refusal and the
dismiss memory, and passes the state back. agent is the agent as a Person reads it — name
("Claude") and via its owner ("Manoj") — so the line says "Claude (via Manoj)" and the refused
sentence uses the short name. The callbacks decide which buttons show: leave out onUndo and
onViewChanges when the agent's checkpoint is gone, and every callback but onDismiss for a
read-only viewer. When the undo succeeds, remove the banner and show a toast.
Scope
Pin it under a document's header, above the text, and reserve its space only while it shows.
Render at most two; pass the rest to the second one's moreAgents, which ends its line with "+1
more agent" and lists each in a popover with its own Undo. Every string comes in through labels
(and refusedReason for the refused line), with the English defaults below.
Examples
Edited by Claude (via Manoj) 3 changes
States
undoing spins Undo and disables both buttons. refused adds the reason line and swaps Undo for
Open versions; pass refusedReason for another reason ("…because the page was restored since.").
Without onUndo and onViewChanges (too old), or any action (a read-only viewer), the banner is
words and the ✕ — or words alone without onDismiss. kind="created" reads "Created by Claude
(via Manoj) · 5m ago".
Edited by Claude (via Manoj) 3 changes
Edited by Claude (via Manoj) 3 changes
Couldn't undo only Claude's changes because people edited the page since.
Edited by Claude (via Manoj) 3 changes
Edited by Claude (via Manoj) 3 changes
Created by Claude (via Manoj)
Stacked
The second banner ends with "+N more agents"; its popover lists the rest, each with its own Undo, spinner and refused line.
Edited by Claude (via Manoj) 3 changes
Edited by Codex (via Priya) 1 change
Narrow
Under a 42rem container the buttons take their own row under the text, which wraps beside the ✕; under 24rem Undo reads "Undo changes". On a coarse pointer every button has a 44px target.
Edited by Claude (via Manoj) 3 changes
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
agent* | AgentEditBannerAgent | — | The agent: { name: "Claude", via: "Manoj" } reads "Claude (via Manoj)". |
at* | string | number | Date | — | When the agent last changed (or created) the document. |
changeCount | number | — | How many changes, after the name; omit to leave the count out. |
kind | AgentEditBannerKind | "edited" | Edited the document ("· 3 changes"), or created it. |
labels | Partial<AgentEditBannerLabels> | — | Replace any of the strings (AgentEditBannerLabels). |
moreAgents | readonly AgentEditBannerMoreAgent[] | [] | The other agents, behind "+N more agents" at the line's end. |
now | number | — | Pin the relative times' clock (docs, tests). |
onDismiss | (() => void) | — | Dismiss the banner for this viewer; the ✕ shows when set. |
onOpenVersions | (() => void) | — | Open the version history; Open versions shows when set and refused. |
onUndo | (() => void) | — | Undo the agent's changes; Undo shows when set, except while refused. |
onViewChanges | (() => void) | — | Open the diff of the agent's changes; View changes shows when set. |
refusedReason | React.ReactNode | labels.refused(agent.name) | The line refused shows, in place of "Couldn't undo only Claude's changes because people
edited the page since." — for another reason, such as a restore. |
status | AgentEditBannerStatus | "idle" | Where the undo stands (AgentEditBannerStatus). |
Data attributes and CSS variables on AgentEditBanner
| Attribute | Values |
|---|---|
data-kind | mirrors a prop or state value |
data-slot | "agent-edit-banner" | "agent-edit-banner-actions" | "agent-edit-banner-body" | "agent-edit-banner-dismiss" | "agent-edit-banner-reason" | "agent-edit-banner-summary" |
data-status | mirrors a prop or state value |
AgentEditBannerMoreAgent
| Prop | Type | Default | Description |
|---|---|---|---|
agent* | AgentEditBannerAgent | — | The agent. |
at* | string | number | Date | — | When it last changed the document. |
id* | string | — | A stable key. |
changeCount | number | — | How many changes it made; omit to leave the count out. |
kind | AgentEditBannerKind | "edited" | Edited or created. |
onUndo | (() => void) | — | Undo this agent's changes; the row's Undo shows when set. |
refusedReason | React.ReactNode | labels.refused(agent.name) | The refused line in place of the default. |
status | AgentEditBannerStatus | "idle" | Where its undo stands; refused shows the reason in place of Undo. |
Accessibility
- The banner is an
Alertstatus region witharia-live="polite", so a banner that changes while on screen is announced without interrupting; the relative time inside it ticks silently. - The buttons are named for the agent: "View Claude (via Manoj)'s changes" and "Undo Claude (via Manoj)'s changes", so two stacked banners never offer two identical names.
- The purple bot is decoration (hidden from assistive tech); the words say it was an agent.
- While undoing, Undo is busy (
aria-busy) and both buttons stay focusable but inert.
| Key | Action |
|---|---|
| Tab | Move through the time, "+N more agents", the buttons and ✕. |
| Enter / Space | Activate a button, or open the list of the other agents. |
| Esc | Close that list and return focus to "+N more agents". |
| Contract | States tested |
|---|---|
| Behaviour | edited, undoing, refused, too-old, read-only, created, more-agents, labels |
| Accessibility | polite-live-region, named-buttons, decorative-avatar, silent-relative-time, browser-accessibility-test |
| Visual | edited, states, stacked, narrow |
Do / Don't
Approval Card
An agent asks its owner to approve a risky action — who asks, what, on which records, why, until when — with one Approve / Decline for the whole request.
Agent Panel
An agent on a record as a collaborator — whom it acts for, its state and actions, instructions, latest messages, a reply box and runs with Undo.