# Candor provider

Wraps an app in Candor's providers from one config: LocaleProvider (with the app's nouns), EntityKindsProvider, AgentRolesProvider, CardRegistryProvider and TooltipProvider. It always renders all five, 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, 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/candor-provider"
import { AgentBadge } from "@/components/candor/agent-avatar"
import { Composer } from "@/components/candor/composer"
import { EntityChip } from "@/components/candor/entity-chip"
import { useStrings } from "@/components/candor/locale"
import { RelativeTime } from "@/components/candor/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",
  nouns: { environment: { one: "workspace", other: "workspaces" }, 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: { delay: 300 },
}

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

/** `config.nouns` fills every message that names a noun, Candor's and the app's own, in the form the count picks. */
const scopeStrings = {
  scope: "{Product} keeps {count} {environment} for your {account}",
}

function Scope() {
  const t = useStrings("scope", scopeStrings)
  return <p className="text-sm text-muted-foreground">{t("scope", { count: 3 })}</p>
}

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>
        <Scope />
        <Composer value={value} onValueChange={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 and close across the app, in TooltipProvider's (Base UI's) names.

```ts
{
  /** Milliseconds from hover to open. Left out, 0: Candor's tooltips open at once. */
  delay?: number | undefined;
  /** Milliseconds from the pointer leaving to close. Left out, Base UI's 0. */
  closeDelay?: number | undefined;
  /** Milliseconds after one tooltip closes during which the next opens with no delay. Left out, Base UI's 400. */
  timeout?: number | undefined;
  /** When true, the tooltip closes as the pointer leaves the trigger, even towards the tooltip. Left out, false. */
  disableHoverablePopup?: 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, which fill `{environment}`, `{account}`, `{product}` and the like in every message:
   * `{ environment: { one: "workspace", other: "workspaces" }, account: "team" }`; any left out stay Candor's, and
   * `environments` defaults to the `other` form of an `environment` given without it. LocaleProvider's `nouns`.
   */
  nouns?: Partial<NounTable> | 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;
}
```

## Types from other items

The props above take these. Each is defined by another item; import it from the path shown.

### TextDirection

Import: `import type { TextDirection } from "@/components/candor/locale"` (item `locale`, https://candor.themethodic.com/docs/locale.md)

```ts
"ltr" | "rtl"
```

### StringOverrides

Import: `import type { StringOverrides } from "@/components/candor/locale"` (item `locale`, https://candor.themethodic.com/docs/locale.md)

Overrides by component name, e.g. `{ thread: { jumpToLatest: "…" }, composer: { send: "…" } }`.

```ts
Record<string, Partial<StringTable> | undefined>
```

### NounTable

Import: `import type { NounTable } from "@/lib/candor/fill-strings"` (item `fill-strings`, https://candor.themethodic.com/docs/fill-strings.md)

The app's nouns by name; a message names one as `{environment}`, or `{Environment}` at the start of a sentence.

```ts
Record<string, Noun>
```

### StringTable

Import: `import type { StringTable } from "@/components/candor/locale"` (item `locale`, https://candor.themethodic.com/docs/locale.md)

A component's strings: its English defaults, or your overrides.

```ts
Record<string, Phrase>
```

### Noun

Import: `import type { Noun } from "@/lib/candor/fill-strings"` (item `fill-strings`, https://candor.themethodic.com/docs/fill-strings.md)

One of the app's nouns: a word ("workspace"), or its plural forms (`{ one: "workspace", other: "workspaces" }`), with a `gender` for messages that select on it and a `start` for the start of a sentence where upper-casing the first letter isn't how the language does it.

```ts
string | (PluralPhrase & { gender?: string | undefined; start?: string | PluralPhrase | 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`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
