Skip to content
Tier

Digest

Pro

A periodic summary for one person, in sections in a fixed order: what needs them (each row with its link or action), health (each figure with a sparkline and its change against a named period), what was done (by agent or person, as the app names them) and notes in the app's groups. Every noun is the app's: the title, the period, the figures' labels, the groups' names. Empty sections are left out, and a digest with nothing in it says so.

It never makes a trend up: a figure with fewer points than minPoints says "Not enough data yet" and how many it has, not a flat line, and its change waits with it; a change always names its period, and one without a period isn't drawn; a figure that isn't known says "Not known", never 0. Notes and titles from an agent or a tool are drawn as text, and a note is named as its writer's. For the same digest by email, render DigestEmail with the same data.

Markdown
Version 5.4.0· other versions

This page is for 5.4.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1, 2.6.2, 2.7.0, 2.8.0, 2.9.0, 2.10.0, 3.0.0, 4.0.0, 4.0.1, 4.0.2, 4.1.0, 4.2.0, 4.3.0, 4.4.0, 4.5.0, 5.0.0, 5.1.0 and 5.2.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the 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/digest

Brings in: agent-avatar, bidi, chat, heading-level, heading-scope, link, locale, sparkline, tokens, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import { Digest, type DigestData } from "@/components/candor/digest"

const planner = { kind: "agent" as const, agent: { id: "planner", name: "Planning agent", role: "planner" } }
const reviewer = { kind: "agent" as const, agent: { id: "reviewer", name: "Review agent", role: "reviewer" } }

const digest: DigestData = {
  title: "This week",
  period: "6 to 12 November",
  needsYou: [
    { id: "n1", title: "Approve the supplier import", detail: "Waiting since Tuesday", href: "#approve" },
    { id: "n2", title: "Question from the Planning agent", detail: "Should the March dates move too?" },
  ],
  health: [
    { id: "h1", label: "Runs finished", value: 128, points: [96, 104, 99, 115, 121, 128], change: { value: 0.12, format: "percent", period: "since last week", good: "up" } },
    { id: "h2", label: "Time to review", value: 3.2, valueText: "3.2 h", points: [4.1, 3.9, 3.6, 3.2], change: { value: 0.4, text: "0.4 h", period: "since last week", good: "down" } },
    { id: "h3", label: "Reruns", value: 5, points: [7, 5], change: { value: -2, period: "since last week", good: "down" } },
    { id: "h4", label: "Hours saved", value: null, missing: "Not measured until the timer is on.", points: [] },
  ],
  done: [
    { id: "d1", title: "Drafted the quarterly summary", by: planner, href: "#summary" },
    { id: "d2", title: "Moved four dates after the delay", by: planner },
    { id: "d3", title: "Checked 212 sources", by: reviewer },
    { id: "d4", title: "Signed off the plan", by: { kind: "person", name: "Dana Reyes" } },
  ],
  notes: [
    { id: "g1", name: "Northwind", notes: [{ id: "x1", text: "The supplier's file arrived a day late; nothing downstream moved.", by: planner }] },
    { id: "g2", name: "Harbour", notes: [{ id: "x2", text: "Kick-off is on Thursday.", by: { kind: "person", name: "Dana Reyes" } }] },
  ],
}

export default function Example() {
  return <Digest className="max-w-lg" digest={digest} onOpen={() => {}} />
}

<Digest>

PropTypeDefaultDescription
digestrequiredDigestData—What the digest shows: its title and period, and the rows of each section.
minPointsnumber | undefineddefaultMinPointsThe fewest numbers a health row's points need before its trend and change are drawn, at least 2. Defaults to 4.
onOpen((id: string) => void) | undefined—Called with a needs-you row's id when it has no `href` and the person opens it.
headingLevelHeadingLevel | undefined—The level of the digest's title. Defaults to one below the nearest `HeadingScope`, else 2; the sections sit one below it.
classNamestring | undefined—Classes for the digest, merged with Candor's.

Strings

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

KeyEnglish
needsYouNeeds you
healthHealth
doneWhat was done
notesNotes
nothingNothing to report for this period.
notKnownNot known
notEnoughData{ one: "Not enough data yet: {count} point", other: "Not enough data yet: {count} points" }
upUp {amount} {period}
downDown {amount} {period}
sameNo change {period}
trend{label} trend
things{ one: "{count} thing", other: "{count} things" }
wroteWritten by {name}

Types

DigestActor
Who did something or wrote a note in a `Digest`: an agent, by the identity the app gives it (its name is the app's, never the model's), or a person, by the name the app gives them.
{ kind: "agent"; agent: AgentIdentity } | { kind: "person"; name: string }
DigestNeed
One thing in a `Digest` that waits on the person.
{ id: string; /** What waits, in the app's words or a tool's: drawn as text. */ title: string; /** A second line: what it is about, how long it has waited. */ detail?: string | undefined; /** Where to deal with it. Without it, the row is a button that calls `Digest`'s `onOpen`, or text when there is none. */ href?: string | undefined; }
DigestChange
A change in a health row's figure against a named period, as the app measured it. Candor never works one out.
{ /** How much the figure moved: above 0 is up, below 0 down, 0 no change. A fraction when `format` is "percent" (0.12 is 12 %). Not a finite number (NaN, Infinity), no change is drawn. */ value: number; /** The period it is measured against, in the app's words as they follow the amount: "since last week". */ period: string; /** How the amount is written: "number" (the default) or "percent". */ format?: "number" | "percent" | undefined; /** The amount as the app writes it, without its sign, taking the formatted number's place: "$1,200", "3 h". */ text?: string | undefined; /** Which way is good news, to colour the change: "up" or "down". Left out, the change is drawn without a tone. */ good?: "up" | "down" | undefined; }
DigestHealthRow
One figure in a `Digest`'s health section, with its trend.
{ id: string; /** What the figure is: "Runs finished", "Time to review". */ label: string; /** The latest figure, or `null` when it isn't known (it says so, never 0). */ value: number | null; /** The figure as the app writes it, taking the formatted number's place: money in its currency, "2.4 h". */ valueText?: string | undefined; /** Why the figure isn't known, shown after "Not known" when `value` is null. */ missing?: string | undefined; /** * The figure's past values in order, oldest first; `null` leaves a gap. With fewer numbers than the digest's * `minPoints`, the row says "Not enough data yet" with how many there are, in place of the line and the change. */ points: (number | null)[]; /** The change against a named period. Drawn only with enough points, a known figure and a period. */ change?: DigestChange | undefined; }
DigestDone
One thing that was done in the period, and who did it.
{ id: string; /** What was done, in the app's words or a tool's: drawn as text. */ title: string; /** Who did it. */ by: DigestActor; /** Where to see it. */ href?: string | undefined; }
DigestNote
A note in one of a `Digest`'s groups. Its text is drawn as text, never as HTML or a link, and named as its writer's.
{ id: string; /** What the note says. An agent's note is what the agent wrote, believed, not checked. */ text: string; /** Who wrote it. */ by: DigestActor; }
DigestNoteGroup
One of the app's groups of notes, by its own name.
{ id: string; /** The group's name, the app's word for it. */ name: string; /** Its notes; a group with none is left out. */ notes: DigestNote[]; }
DigestData
What a `Digest` (and `DigestEmail`) shows. Every section is optional, and an empty one is left out.
{ /** The digest's title, in the app's words: "This week". */ title: string; /** The period it covers, in the app's words: "6 to 12 November". */ period?: string | undefined; /** What waits on the person, first. */ needsYou?: DigestNeed[] | undefined; /** Figures and their trends. */ health?: DigestHealthRow[] | undefined; /** What was done, and by whom. */ done?: DigestDone[] | undefined; /** Notes, in the app's groups. */ notes?: DigestNoteGroup[] | undefined; }

Types from other items

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

HeadingLevel from @/lib/candor/heading-level
A heading's level in the page's outline: `1` is `<h1>`, `6` is `<h6>`. Every Candor item that draws a heading takes its `headingLevel` as this type; the level is where the heading sits in the outline, never how large it looks.
1 | 2 | 3 | 4 | 5 | 6
AgentIdentity from @/lib/candor/chat
{ id: string; /** The displayed name, e.g. "Architect agent". Never derived from model output. */ name: string; role: AgentRole; specialty?: string | undefined; }
AgentRole from @/lib/candor/chat
What an agent does: "assistant", "coder", "reviewer"… The app registers each role's glyph and hue (AgentRolesProvider).
string