# Message content

A message's blocks, in order. Build them from text with `plainBlocks(text)` for a person's words (newlines kept, no markdown) and `markdownBlocks(text)` for an agent's answer, both from `safe-markdown`, which installs with this item. Import them from there: they run on the server too. A model's links show as text, never as clickable links.

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

## Install

```bash
npx candor-ui add message-content
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/message-content`.
It brings in: attachment-preview, card-registry, chat, citation, degraded-panel, entity-chip, locale, payload, safe-markdown, terms, tool-call, tooltip, utils.

## Example

```tsx
"use client"

import { MessageContent } from "@/components/message-content"

export default function Example() {
  return (
    <MessageContent
      className="max-w-lg"
      blocks={[
        { kind: "paragraph", nodes: [{ kind: "text", text: "Three orders are delayed. " }, { kind: "strong", text: "Two" }, { kind: "text", text: " are waiting on the same carrier." }] },
        { kind: "list", items: [[{ kind: "code", text: "#4821" }, { kind: "text", text: " — carrier delay" }], [{ kind: "code", text: "#4830" }, { kind: "text", text: " — carrier delay" }], [{ kind: "code", text: "#4833" }, { kind: "text", text: " — out of stock" }]] },
        { kind: "paragraph", nodes: [{ kind: "provisional", nodes: [{ kind: "text", text: "The carrier expects to catch up by Friday." }], reason: "From the carrier’s status page; not confirmed." }] },
      ]}
    />
  )
}
```

## <MessageContent>

Props:

- `blocks: MessageBlock[]`: The reply as typed blocks: paragraphs, lists, payloads, tool calls, cards, attachments.
- `sources?: MessageSource[] | undefined` (default `[]`): Sources the blocks cite by marker.
- `onOpenEntity?: ((entity: EntityRef) => void) | undefined`: Makes entity chips open their thing.
- `onNavigate?: ((to: string) => void) | undefined`: Follows in-app links; links from a model are never rendered.
- `onOpenAttachment?: ((attachment: Attachment) => void) | undefined`: Makes attachment tiles buttons that open their file.
- `bareCode?: boolean | undefined`: Code and other payload blocks without their frame and title bar (Payload's `bare`).
- `className?: string | undefined`

## <Inline>

Inline nodes. Every string here may come from a model or the user's system, so it is only ever rendered as a React text child: no markdown parsing, no HTML.

Props:

- `nodes: InlineNode[]`: Inline nodes: text, strong, code, entities, citations.

## Functions

### blocksToText

```ts
blocksToText(blocks: MessageBlock[], sources: MessageSource[] = [])
```

Plain text of a message, for copy. Citations become [n]; entities their API name.

## Strings (`messageContent`)

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

- `measured`: From the {environment}
- `measuredFrom`: From the {environment}: {source}
- `notVerified`:  (not verified)
- `provisional`: The agent hasn’t verified this against the {environment}.

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