# Card registry

Renders one card from the registry. Three honest fallbacks, each with the service's text summary: the app doesn't know this card; its data doesn't fit (and how); or it failed while drawing.

- Registry name: `card-registry` (`@candor/card-registry`)
- Tier: Free (MIT). No licence key needed.
- Docs: https://candor.themethodic.com/docs/card-registry

## Install

```bash
npx candor-ui add card-registry
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/card-registry`.
It brings in: collapsible, degraded-panel, locale.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import { CardRegistryProvider, defineCard } from "@/components/card-registry"
import { MessageContent } from "@/components/message-content"

// Your app's cards: what each one needs, checked before it draws.
const cards = {
  order: defineCard<{ id: string; status: string; total: string }>({
    label: "Order",
    check: (p) => (typeof p === "object" && p && "id" in p && "status" in p ? null : "it needs an id and a status"),
    render: (p) => (
      <div className="w-fit rounded-lg border bg-card px-3 py-2 text-sm">
        Order <span className="font-medium">#{p.id}</span> · {p.status} · {p.total}
      </div>
    ),
  }),
}

export default function Example() {
  return (
    <CardRegistryProvider cards={cards}>
      <MessageContent
        className="max-w-lg"
        blocks={[
          { kind: "card", card: "order", props: { id: "4821", status: "Shipped", total: "$42.00" }, summary: "Order #4821, shipped, $42.00" },
          { kind: "card", card: "order", props: { id: "4830" }, summary: "Order #4830 (incomplete data)" },
          { kind: "card", card: "map", props: {}, summary: "A delivery map this app can’t show" },
        ]}
      />
    </CardRegistryProvider>
  )
}
```

## <Card>

Props:

- `name: string`: Which card to draw, from the registry.
- `props: unknown`: The card's data, checked before it draws.
- `summary: string`: The plain-text version, shown when the card can't be.

## <CardRegistryProvider>

Makes cards available to every message below it. Cards not listed here fall back to their summary.

Props:

- `cards: Record<string, AnyCard>`: Your app's cards by name, made with defineCard.
- `children: React.ReactNode`

## Functions

### defineCard

```ts
defineCard(card: CardDefinition<P>)
```

Define a card with typed props; the check is what makes the cast safe.

## Strings (`cardRegistry`)

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

- `showAsText`: Show it as text
- `unknown`: This reply includes a “{name}” card this app can’t show.
- `invalid`: The {label} couldn’t be shown: {problem}
- `failed`: The {label} failed while drawing.

## Types

### CardDefinition

A card an agent's reply can carry. `check` decides whether the props are the shape `render` needs: it returns null when they are, or a sentence saying what's wrong. Cards are data from a model; nothing renders until the check passes.

```ts
{ /** What it is, in a few words, for fallbacks: "Query results". */ label: string check: (props: unknown) => string | null render: (props: P) => React.ReactNode }
```

## 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`.
