# Code listing

Code to read, with line numbers: a file the agent wrote, the lines a finding points at, a config before it changes. The language is named above it, and Copy gives the code exactly, without the line numbers, saying "Copied" only once the clipboard took it. Lines wrap by default, so the whole of each can be read on a phone; Wrap lines turns that off and the listing scrolls sideways instead.

`highlights` marks ranges of lines with the app's reason, shown under each range. Given a `previous` version, a Code / Changes switch shows what changed, line by line. The code is split into tokens rendered as text, never as HTML, so nothing in it can run or add markup; tokens are coloured when `highlight` is set, or anywhere under `data-syntax="highlight"`. For a short snippet with no line numbers, use `Payload`.

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

## Install

```bash
npx candor-ui add code-listing
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/code-listing`.
It brings in: bidi, diff, highlight, locale, tokens, use-copy, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import { CodeListing } from "@/components/candor/code-listing"

const before = `export async function refund(orderId: string) {
  const order = await db.orders.find(orderId)
  return payments.refund(order.chargeId, order.total)
}
`

const after = `export async function refund(orderId: string) {
  const order = await db.orders.find(orderId)
  // Only settled orders can be refunded
  if (order.status !== "settled") return { ok: false, reason: "not settled" }
  return payments.refund(order.chargeId, order.total)
}
`

const excerpt = `  const rows = await db.query(sql, [workspaceId])
  for (const row of rows) {
    await notify(row.ownerEmail, template.render(row))
  }`

export default function Example() {
  return (
    <div className="flex max-w-2xl flex-col gap-4">
      {/* previous adds the Code / Changes switch; highlights mark lines with the app's reason. */}
      <CodeListing
        highlight
        language="ts"
        title="src/refund.ts"
        code={after}
        previous={before}
        highlights={[{ from: 3, to: 4, reason: "The guard the agent added" }]}
      />
      {/* An excerpt from further down a file: startLine numbers it from there. */}
      <CodeListing language="ts" title="jobs/notify.ts" code={excerpt} startLine={118} highlights={[{ from: 120, reason: "One email per row, with no limit" }]} />
    </div>
  )
}
```

## <CodeListing>

Props:

- `code: string`: The code, shown as text and copied exactly.
- `language: string`: The language, named above the code and used for its colours: "ts", "python", "sql", "json". Never run.
- `title?: string | undefined`: What it is, e.g. "src/refund.ts" or "The query I ran".
- `startLine?: number | undefined` (default `1`): The number of the first line, for an excerpt from further down a file. Defaults to 1.
- `highlights?: CodeHighlight[] | undefined`: Ranges of lines to mark, each with the reason, shown under the range. Numbers count from `startLine`.
- `previous?: string | undefined`: The version before this one. Given, a Code / Changes switch shows the difference line by line.
- `defaultView?: "code" | "changes" | undefined` (default `"code"`): Which view opens first when there's a `previous`. Defaults to "code".
- `defaultWrap?: boolean | undefined` (default `true`): Whether long lines wrap when it opens; the person can switch. Defaults to true. False scrolls sideways instead.
- `highlight?: boolean | undefined`: Colour the code by its syntax. Left out, it follows the nearest `data-syntax` attribute (off by default).
- `className?: string | undefined`

## Functions

### codeLanguageLabel

```ts
codeLanguageLabel(language: string, t: (key: "shell" | "text") => string)
```

"json" → "JSON", "python" → "Python", short unknown ones upper-cased ("hcl" → "HCL"); "bash" and "text" from `t`.

### codeLines

```ts
codeLines(text: string, language: string)
```

The text's tokens split into lines; each line's texts joined give back that line exactly.

### codeLineNodes

```ts
codeLineNodes(tokens: Token[])
```

One line from `codeLines` as spans of text. Each token is a text child, so nothing in the code is parsed as markup.

## Strings (`codeListing`)

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

- `copy`: Copy
- `copied`: Copied
- `copyFailed`: Couldn’t copy
- `wrap`: Wrap lines
- `view`: View
- `code`: Code
- `changes`: Changes
- `shell`: Shell
- `text`: Text
- `note`: {lines}: {reason}
- `line`: Line {line}
- `lines`: Lines {from}–{to}
- `added`: Added: 
- `removed`: Removed: 
- `linesAdded`: { one: "{count} line added", other: "{count} lines added" }
- `linesRemoved`: { one: "{count} line removed", other: "{count} lines removed" }
- `noChanges`: No changes from the previous version.
- `tooLong`: Too long to compare line by line: the whole previous version is shown removed and this one added.

## Types

### CodeHighlight

A range of lines the app points at, with why. Line numbers are as shown, so they count from `startLine`.

```ts
{
  /** The first line of the range. */
  from: number;
  /** The last line; left out, the range is the one line `from`. */
  to?: number | undefined;
  /** Why these lines matter, in the app's words. Shown under the range as text. */
  reason: string;
}
```

## Types from other items

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

### Token

Import: `import type { Token } from "@/lib/candor/highlight"` (item `highlight`, https://candor.themethodic.com/docs/highlight.md)

```ts
{ type: TokenType; text: string }
```

### TokenType

Import: `import type { TokenType } from "@/lib/candor/highlight"` (item `highlight`, https://candor.themethodic.com/docs/highlight.md)

```ts
"keyword" | "string" | "number" | "comment" | "function" | "property" | "type" | "tag" | "variable" | "operator" | "plain"
```

## Motion

- **tint** (tint): The buttons' background and text colour change when the pointer is over them, and a ring fades in when one takes keyboard focus.

Set with `--cd-motion-code-listing-<moment>-*` in your brand file; the families are in the design reference.

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