Card registry

Free · MIT

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.

Markdown

Preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/card-registry

Brings in: collapsible, degraded-panel, locale · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"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>

PropTypeDefaultDescription
namerequiredstring—Which card to draw, from the registry.
propsrequiredunknown—The card's data, checked before it draws.
summaryrequiredstring—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.

PropTypeDefaultDescription
cardsrequiredRecord<string, AnyCard>—Your app's cards by name, made with defineCard.
childrenrequiredReact.ReactNode—What goes inside.

Functions

defineCard(card: CardDefinition<P>)

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

Strings

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

KeyEnglish
showAsTextShow it as text
unknownThis reply includes a “{name}” card this app can’t show.
invalidThe {label} couldn’t be shown: {problem}
failedThe {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.
{ /** What it is, in a few words, for fallbacks: "Query results". */ label: string check: (props: unknown) => string | null render: (props: P) => React.ReactNode }