# Version history

Live, draft and past versions of something editable (an agent's settings, a knowledge page), newest first, with who changed it and why. Publish a draft, or restore a past version as a new draft — history is never rewritten.

- Registry name: `version-history` (`@candor/version-history`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/version-history

## Install

```bash
npx candor-ui add version-history
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/version-history`.
It brings in: badge, button, know, locale, relative-time, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import type { Version } from "@/lib/know"
import { VersionHistory } from "@/components/version-history"

const ago = (d: number) => new Date(Date.now() - d * 86_400_000).toISOString()

const initial: Version[] = [
  { id: "v5", label: "Version 5", state: "draft", at: ago(0), by: "You", note: "Dev agent may apply migrations on Staging without asking." },
  { id: "v4", label: "Version 4", state: "live", at: ago(3), by: "Priya", note: "Dev agent asks before applying migrations." },
  { id: "v3", label: "Version 3", state: "past", at: ago(12), by: "Maya", note: "Moved the Dev agent to a faster model." },
]

export default function Example() {
  const [versions, setVersions] = React.useState(initial)
  return (
    <VersionHistory
      className="max-w-xl"
      versions={versions}
      onPublish={(id) => setVersions((vs) => vs.map((v) => (v.id === id ? { ...v, state: "live", at: new Date().toISOString() } : v.state === "live" ? { ...v, state: "past" } : v)))}
      onRestore={(id) => {
        const from = versions.find((v) => v.id === id)!
        setVersions((vs) => {
          const n = Math.max(...vs.map((v) => Number(v.id.slice(1)))) + 1
          return [{ id: `v${n}`, label: `Version ${n}`, state: "draft", at: new Date().toISOString(), by: "You", note: `Restored from ${from.label}.` }, ...vs.filter((v) => v.state !== "draft")]
        })
      }}
    />
  )
}
```

## <VersionHistory>

Props:

- `versions: Version[]`: Every version: live, draft or past, with who changed it, when and why.
- `activeId?: string | undefined`: The version being viewed, highlighted.
- `onSelect?: ((id: string) => void) | undefined`: Makes each version's name a button; called with its id.
- `onPublish?: ((id: string) => void) | undefined`: Shows Publish on the draft; called with its id.
- `onRestore?: ((id: string) => void) | undefined`: Shows Restore on past versions; called with the version's id to copy into a new draft.
- `className?: string | undefined`

## Strings (`versionHistory`)

The fixed words, English by default. Override any of them under `versionHistory` in `<LocaleProvider strings={{ versionHistory: { … } }}>`; the table is exported as `versionHistoryStrings`.

- `versions`: Versions
- `live`: Live
- `draft`: Draft
- `publish`: Publish
- `restore`: Restore as draft

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
