Checkpoint list
ProRestore points in a run, one per turn, with what each one holds and where the person is now. Each checkpoint says what happened, when, who made it, and what it captured (files, conversation or both, with counts when the app knows them), and offers a Restore that the app handles, usually by opening RestoreDialog. A checkpoint that can't be restored stays listed with its Restore locked and the reason beside it. Nothing is selected for the person, and a note says what checkpoints don't capture.
Preview
Install
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/checkpoint-list
Brings in: badge, button, locale, locked, relative-time, surface, utils · npm: lucide-react@^1.67.0
Usage
"use client"
import type { Checkpoint } from "@/components/checkpoint-list"
import { CheckpointList } from "@/components/checkpoint-list"
const minutesAgo = (m: number) => new Date(Date.now() - m * 60_000)
const checkpoints: Checkpoint[] = [
{ id: "cp-5", label: "Wired the rate limiter into the API routes", createdAt: minutesAgo(2), captured: "both", fileCount: 4, messageCount: 18, madeBy: { kind: "agent", name: "Build agent" } },
{ id: "cp-4", label: "Added tests for burst traffic", createdAt: minutesAgo(9), captured: "both", fileCount: 2, messageCount: 14, madeBy: { kind: "agent", name: "Build agent" } },
{ id: "cp-3", label: "Before renaming the config keys", createdAt: minutesAgo(21), captured: "files", fileCount: 31, madeBy: { kind: "person", name: "Priya Shah" } },
{ id: "cp-2", label: "Added the token-bucket limiter", createdAt: minutesAgo(34), captured: "both", messageCount: 6, madeBy: { kind: "agent", name: "Build agent" } },
{
id: "cp-1",
label: "Session start",
createdAt: minutesAgo(60 * 26),
captured: "both",
unavailable: "Its file snapshot was removed after 24 hours.",
},
]
export default function Example() {
return <CheckpointList className="max-w-xl" checkpoints={checkpoints} currentId="cp-5" onRestore={() => {}} />
}<CheckpointList>
| Prop | Type | Default | Description |
|---|---|---|---|
| checkpointsrequired | Checkpoint[] | — | The checkpoints in the order to show them; newest first is usual. |
| currentId | string | undefined | — | The checkpoint the work is at now. It's marked, and has no Restore. |
| onRestore | ((id: string) => void) | undefined | — | Called with a checkpoint's id when the person picks Restore. Without it, there are no Restore buttons. |
| note | string | null | undefined | — | What checkpoints don't capture, in the app's words. Defaults to shell commands and changes outside the agent; null hides it. |
| className | string | undefined | — | Extra classes for the outer element. |
Strings
The fixed words, in English by default. Change any of them under checkpointList in <LocaleProvider strings={{ … }}>; the table is exported as checkpointListStrings.
| Key | English |
|---|---|
| title | Checkpoints |
| count | { one: "{count} checkpoint", other: "{count} checkpoints" } |
| empty | No checkpoints yet. |
| current | You are here |
| restore | Restore |
| restoreTo | Restore to “{label}” |
| captured | Holds |
| files | files |
| conversation | conversation |
| both | files and conversation |
| fileCount | { one: "{count} file", other: "{count} files" } |
| messageCount | { one: "{count} message", other: "{count} messages" } |
| byAgent | By {name} (agent) |
| byPerson | By {name} |
| note | Checkpoints hold the agent’s own file edits and the conversation. Shell commands it ran and changes made outside it aren’t captured, so a restore doesn’t undo them. |
Types
- CheckpointCapture
- What a checkpoint holds: the files, the conversation, or both.
"files" | "conversation" | "both"- Checkpoint
- One restore point, usually one per turn.
{ id: string /** What happened at this point, in plain words: "Added the rate limiter". */ label: string /** When the checkpoint was made. */ createdAt: Date | string /** What it holds. A restore can only put back what was captured. */ captured: CheckpointCapture /** How many files it holds, when the app knows. Left out, no count is shown. */ fileCount?: number | undefined /** How many messages of