# Candor provider

Wraps an app in Candor's providers from one config: LocaleProvider, TermsProvider, EntityKindsProvider, AgentRolesProvider, CardRegistryProvider and TooltipProvider. It always renders all six, whatever the config holds, so a setting that arrives after the first render (a locale, a preference) doesn't remount the app; a setting left out passes through what an outer provider set, or the default. It renders nothing of its own. Charts' ChartSizeProvider is not among them; nest it inside where charts need a width before they can measure.

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

## Install

```bash
npx candor-ui add candor-provider
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/candor-provider`.
It brings in: agent-avatar, card-registry, entity-chip, locale, terms, tokens, tooltip.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import { CandorProvider, type CandorConfig } from "@/components/candor-provider"
import { AgentBadge } from "@/components/agent-avatar"
import { Composer } from "@/components/composer"
import { EntityChip } from "@/components/entity-chip"
import { RelativeTime } from "@/components/relative-time"

/**
 * One object for the whole app. It is plain data, so a server component (a root layout) can build it, or a coding
 * agent can write it as JSON; every field is optional.
 */
const config: CandorConfig = {
  locale: "en-GB",
  timeZone: "Europe/London",
  terms: { environment: "workspace", environments: "workspaces", account: "team", product: "Acme" },
  entityKinds: { ticket: { noun: "Ticket", iconFrom: "record" } },
  agentRoles: { triager: { noun: "Triager", iconFrom: "reviewer" } },
  strings: { composer: { placeholder: "Ask the triager about a ticket" } },
  tooltip: { delayDuration: 300 },
}

const at = new Date(Date.now() - 12 * 60_000).toISOString()

export default function Example() {
  const [value, setValue] = React.useState("")
  return (
    <CandorProvider config={config}>
      <div className="flex max-w-lg flex-col gap-3">
        <p className="flex flex-wrap items-center gap-2 text-sm">
          <AgentBadge agent={{ id: "t1", name: "Intake", role: "triager" }} size="sm" /> picked up
          <EntityChip entity={{ kind: "ticket", name: "T-1042", label: "Refund not received" }} onOpen={() => {}} />
          <RelativeTime date={at} />
        </p>
        <Composer value={value} onChange={setValue} onSubmit={() => setValue("")} />
      </div>
    </CandorProvider>
  )
}
```

## <CandorProvider>

Props:

- `config?: CandorConfig | undefined`: The app's settings as plain data; every field is optional. A server component can pass it.
- `kinds?: Record<string, EntityKindInfo> | undefined`: Entity kinds with their own icons, such as a preset (`githubKinds`). Code, not data, so pass it from a client component. Applied after `config.entityKinds`, so it wins for an id both set.
- `roles?: Record<string, AgentRoleInfo> | undefined`: Agent roles with their own glyphs. Code, not data, like `kinds`; applied after `config.agentRoles`.
- `cards?: Record<string, AnyCard> | undefined`: The app's cards by name, made with defineCard. Code, not data, like `kinds`. Left out, a card registry set above still applies.
- `children: React.ReactNode`

## Types

### EntityKindConfig

An entity kind written as data: a noun, and the built-in kind whose icon it borrows.

```ts
{
  /** What one of these is called in the app: "Ticket", "Case". */
  noun: string;
  /**
   * A built-in kind whose icon this kind uses: "record", "document", "file", "table", "field", "person", "link",
   * "code", "workflow", "report" or "dashboard". Left out, a built-in id keeps its own icon; any other kind, or a name
   * that isn't one of those, gets the plain box icon an unregistered kind gets.
   */
  iconFrom?: string | undefined;
}
```

### AgentRoleConfig

An agent role written as data: a noun, the built-in role whose glyph it borrows, and its hue.

```ts
{
  /** What an agent in this role is called: "Triager". */
  noun: string;
  /**
   * A built-in role whose glyph (and, unless `hue` is set, colour) this role uses: "assistant", "orchestrator",
   * "planner", "researcher", "analyst", "designer", "coder", "reviewer" or "tester". Left out, a built-in id keeps its
   * own glyph; any other role, or a name that isn't one of those, is drawn like "assistant".
   */
  iconFrom?: string | undefined;
  /** A CSS colour for the role's tile, such as "var(--chart-2)". Left out, it is the `iconFrom` role's hue. */
  hue?: string | undefined;
}
```

### TooltipConfig

How tooltips open across the app.

```ts
{
  /** Milliseconds from hover to open. Left out, 0: Candor's tooltips open at once. */
  delayDuration?: number | undefined;
  /** Milliseconds after one tooltip closes during which the next opens with no delay. Left out, Radix's 300. */
  skipDelayDuration?: number | undefined;
  /** When true, the tooltip closes as the pointer leaves the trigger, even towards the tooltip. Left out, false. */
  disableHoverableContent?: boolean | undefined;
}
```

### CandorConfig

Everything CandorProvider sets, as plain data: no functions or components, so a server component (a Next.js root layout, say) can build it and pass it down, and a coding agent can write it as JSON. Every field is optional; a field left out leaves that setting at its default, or at whatever a provider above already set.

```ts
{
  /** A BCP 47 tag such as "en-GB", "fr" or "ar-EG", for dates, numbers and plural forms. Left out, the browser's own once the page has hydrated. */
  locale?: string | undefined;
  /** Text direction. Left out, it follows `locale`: "rtl" for Arabic, Hebrew, Persian, Urdu and the like. Set `dir` on `<html>` too. */
  dir?: TextDirection | undefined;
  /** An IANA time zone such as "Europe/Paris". With `locale`, server and browser format dates the same way. */
  timeZone?: string | undefined;
  /** Interface strings by component name, e.g. `{ thread: { jumpToLatest: "…" } }`; any left out stay English. */
  strings?: StringOverrides | undefined;
  /** The app's nouns: `{ environment: "workspace", account: "team" }`; any left out stay Candor's. */
  terms?: Partial<Terms> | undefined;
  /** The app's entity kinds by id, added to the built-in ones; an id that is already a kind replaces its noun. */
  entityKinds?: Record<string, EntityKindConfig> | undefined;
  /** The app's agent roles by id, added to the built-in ones; an id that is already a role replaces it. */
  agentRoles?: Record<string, AgentRoleConfig> | undefined;
  /** Tooltip timing for the whole app. A setting left out keeps what a TooltipProvider above set, or Candor's default. */
  tooltip?: TooltipConfig | undefined;
}
```

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