# Artifact chip

A chip in the thread for something that lives in a panel beside it: a diff, an approval, a form, a table or a plan the agent made. Pressing it calls `onOpen`, and the app opens that thing's tab in its panel; `active` marks the chip whose tab is showing. The thing's state is a word and an icon, never colour alone: "Being prepared", "Waiting for you", "Applied", "Rejected" or "Failed". Set "applied" only from the settled outcome, never on the person's press. The title is the model's text, drawn as text.

- Registry name: `artifact-chip` (`@candor/artifact-chip`)
- 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/artifact-chip

## Install

```bash
npx candor-ui add artifact-chip
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/artifact-chip`.
It brings in: badge, bidi, locale, status, tokens, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import { ArtifactChip } from "@/components/candor/artifact-chip"

const artifacts = [
  { id: "a1", kind: "diff", title: "Raise the refund limit to £500", state: "open" },
  { id: "a2", kind: "approval", title: "Refund three duplicate charges", state: "applied" },
  { id: "a3", kind: "table", title: "Orders charged twice in October", state: "pending" },
  { id: "a4", kind: "plan", title: "Move the team to the new rota", state: "rejected" },
] as const

export default function Example() {
  const [showing, setShowing] = React.useState<string>("a1")
  return (
    <div className="flex max-w-lg flex-col items-start gap-2">
      {artifacts.map((a) => (
        <ArtifactChip key={a.id} kind={a.kind} title={a.title} state={a.state} active={showing === a.id} onOpen={() => setShowing(a.id)} />
      ))}
      <p className="text-xs text-muted-foreground">The panel would show {artifacts.find((a) => a.id === showing)?.title}.</p>
    </div>
  )
}
```

## <ArtifactChip>

Props:

- `kind: string`: What it is: "diff", "approval", "form", "table" and "plan" have their own icon and word; any other kind a generic icon.
- `title: string`: Its title, often the agent's ("Raise the refund limit").
- `state: ArtifactState`: Where it stands: "pending" while the agent prepares it, "open" when it waits for the person, then "applied", "rejected" or "failed". "applied" only once the outcome has settled. The chip carries it as `data-artifact-state` for your styles (not `data-state`, which a Radix app's `[data-state=open]` styles would match).
- `onOpen: () => void`: Opens its tab in the app's panel.
- `active?: boolean | undefined`: Its tab is the one showing in the panel (`aria-current`).
- `className?: string | undefined`: Classes, merged with Candor's.

## Strings (`artifactChip`)

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

- `opens`: Opens in the panel
- `pending`: Being prepared
- `open`: Waiting for you
- `applied`: Applied
- `rejected`: Rejected
- `failed`: Failed
- `kind`: {kind}:
- `diff`: Diff
- `approval`: Approval
- `form`: Form
- `table`: Table
- `plan`: Plan

## Motion

- **tint** (tint): The chip's border darkens when the pointer is over it, and a ring fades in when it takes keyboard focus. Its state badge changes colour with its word when the state changes.

Set with `--cd-motion-artifact-chip-<moment>-*` in your brand file; the families are in the design reference.

## 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`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
