Version Compare
A document's version history at full size — the selected version or its changes beside a day-grouped version list, with Restore and Copy pinned.
- Status
- Since
0.25.90- Accessibility pattern
- named preview region, version listbox, labelled switch
Last updated
Version history
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 Version Compare before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify version-compareInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/version-compareRun 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/button, @vegastack/diff-view, @vegastack/label, @vegastack/resizable, @vegastack/switch, @vegastack/use-media-query, @vegastack/version-list.
Usage
VersionCompare is a document's history at full size, for DialogContent size="fullscreen": the
selected version's text on the start side, the versions beside it, and the actions on the selected
version in a footer that stays put while both sides scroll.
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { VersionCompare } from "@/components/ui/version-compare";
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent size="fullscreen">
<DialogHeader>
<DialogTitle>History · Pricing v2</DialogTitle>
</DialogHeader>
<VersionCompare
versions={versions}
currentId={versions[0]?.id}
selectedId={selected}
onSelect={setSelected}
showChanges={showChanges}
onShowChangesChange={setShowChanges}
preview={<MarkdownView typeScale="document">{text}</MarkdownView>}
diff={showChanges ? { before: previousText, after: text } : null}
actions={
<>
<Button variant="outline" onClick={copy}>
Copy
</Button>
<Button onClick={restore}>Restore this version</Button>
</>
}
/>
</DialogContent>
</Dialog>;- The list is a
VersionListgrouped by day: "Current", the kind badges, an agent's versions under the agent avatar (author.kind: "agent"), Named only (namedOnly,onNamedOnlyChange) and scroll paging (loadMore) pass straight through. - The preview is
preview, the host's own rendering of the selected version (MarkdownView, a read-onlyTextEdit). With "Show changes" on (showChanges, its switch shown whenonShowChangesChangeis set) and adiffgiven, the preview is aDiffViewof the two texts instead; without adiffthe host'spreviewstays, so a host can show its own formatted diff (TextEdit'sdiffview). toolbarsits beside "Show changes": a compare-with toggle, "Name this version".actions— Restore this version, Copy — sit in the footer.
The preview and the list share a ResizablePanels group: drag the
handle to give the list more room (it keeps at least 240px). Below 640px one side shows at a time:
the list first; a tap (or Enter) on a version pushes its preview in, with the actions, and a
"Versions" button goes back.
A host that opens the history on one version — a "View changes" link to an agent's checkpoint —
sets selectedId, showChanges and defaultPhoneView="preview", so a phone opens on that
version's changes rather than the list; "Versions" still goes back. To drive the side itself, pass
phoneView with onPhoneViewChange, which reports "preview" on a pick and "list" from the back
button. A side the host sets moves no focus; a side the reader picks takes focus with it.
// Opened from "View changes" on a checkpoint: start on it, compared with now.
const [selected, setSelected] = React.useState(checkpointId);
const [showChanges, setShowChanges] = React.useState(true);
<VersionCompare
versions={versions}
selectedId={selected}
onSelect={setSelected}
showChanges={showChanges}
onShowChangesChange={setShowChanges}
diff={showChanges ? { before: textOf(selected), after: currentText } : null}
defaultPhoneView="preview"
/>;Version history
Examples
In a full-screen dialog
Its home: DialogContent size="fullscreen" fills the viewport less a 16px inset (the whole screen
on a phone), so the dialog's header stays at the top and VersionCompare fills the rest.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
versions* | readonly VersionItem[] | — | The versions, newest first — grouped by day in the list. |
actions | React.ReactNode | — | The actions on the selected version — Restore this version, Copy — in a footer that stays put while the preview and the list scroll. |
backLabel | string | "Versions" | The phone's back button, from the preview to the list. |
currentId | string | null | null | The version that is the document's current text: a "Current" badge. |
defaultPhoneView | "list" | "preview" | "list" | The side a phone shows first (uncontrolled). |
diff | { before: string; after: string; } | null | — | The two texts to compare while showChanges is on: the preview shows them through
DiffView (added and removed words in reading order). |
emptyState | React.ReactNode | "No versions yet" | Shown when there are no versions. |
heading | string | "Version history" | The list's heading ("History" where the host calls it that). |
loading | boolean | false | Show the skeleton instead of the list. |
loadMore | { hasMore: boolean; onLoadMore: () => void; loading?: boolean; } | — | More versions exist: the next page loads as the list's end scrolls into view (keyset lists have no totals). |
namedOnly | boolean | false | Show named versions only (controlled). |
now | number | — | Pin the relative times' clock (docs, tests). |
onNamedOnlyChange | ((namedOnly: boolean) => void) | — | Called from the header's "Named only" switch; the switch shows when set. |
onPhoneViewChange | ((view: "list" | "preview") => void) | — | Called when the reader moves between the sides on a phone: "preview" on picking a version,
"list" from the back button. |
onSelect | ((id: string) => void) | — | Called when a version is picked (a click, or the arrow keys in the list). |
onShowChangesChange | ((showChanges: boolean) => void) | — | Called from the "Show changes" switch; the switch shows when set. |
phoneView | "list" | "preview" | — | The side a phone shows (controlled) — "preview" opens straight on the selected version, as
a link to one checkpoint's changes does. Pair with onPhoneViewChange. Ignored at 640px and
up, where both sides show. |
preview | React.ReactNode | — | The selected version as it reads — the host's renderer (MarkdownView, a read-only
TextEdit), or its own diff rendering while showChanges is on and no diff is given. |
previewLabel | string | "Selected version" | The preview region's name. |
selectedId | string | null | null | The selected version, shown in the preview. |
showChanges | boolean | false | Show the selected version's changes (controlled). |
showChangesLabel | string | "Show changes" | The "Show changes" switch's label. |
toolbar | React.ReactNode | — | More controls beside "Show changes" (a compare-with toggle, Name this version). |
Data attributes and CSS variables on VersionCompare
| Attribute | Values |
|---|---|
data-slot | "version-compare" | "version-compare-actions" | "version-compare-list" | "version-compare-panels" | "version-compare-preview" | "version-compare-preview-body" | "version-compare-toolbar" |
data-view | mirrors a prop or state value |
Accessibility
- The preview is a region named "Selected version" (
previewLabel); the list is theVersionListlistbox, each day agroupnamed by its heading, one tab stop with the selection following the arrow keys. - "Show changes" is a
switchlabelled by its text; the resize handle is a focusable separator. - On a phone the back button is a labelled "Versions" button.
| Key | Action |
|---|---|
| ↑ / ↓ | Select the previous / next version |
| Enter | On a phone, open the selected version's preview |
| ← / → | On the focused handle, resize the preview and the list |
| Tab | Move through the toolbar, the preview, the list, the actions |
| Contract | States tested |
|---|---|
| Behaviour | selected, show-changes, diff, phone-list, phone-preview, loading |
| Accessibility | labeled, browser-accessibility-test, keyboard |
| Visual | default, show-changes, phone |
Do / Don't
Version List
A document's version history — time, author, name, a badge for how each version came to be, Named only and scroll paging — as a listbox.
Diff View
What changed between two texts — changed lines, then the words inside them, struck through or tinted — lazily loaded, line-only for large pages.