# Graph layout

Lays out a directed graph in layers, from the edges the data already has: each node names the nodes it comes after, and the layout works out where everything goes. It is the layered method in a small form: a loop is broken by leaving one of its edges out of the layering (it is still returned, marked as a loop), each node goes one layer past the last node it follows, an edge that skips layers bends through each one it crosses, nodes in a layer are ordered to cross as few edges as a few sweeps find, and each node sits as near the nodes it follows as its neighbours allow. A node can be pinned to a layer, a slot, or both; the rest are laid out around it.

Pure and synchronous, with no DOM, so it runs on the server and in tests. Fit for the size of graph a person reads (tens of nodes, not thousands).

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

## Install

```bash
npx candor-ui add graph-layout
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/graph-layout`.
It brings in: tokens.

## Functions

### layoutGraph

```ts
layoutGraph(nodes: GraphLayoutNode[])
```

## Types

### GraphLayoutNode

A node to lay out: its id, the ids it comes after, and optionally a fixed layer or slot.

```ts
{
  id: string;
  /** The nodes this one comes after: an edge from each of them to this one. An id that isn't in the graph is ignored. */
  after?: string[] | undefined;
  /** A fixed layer (0 is the first). Left out, it is one past the last node it comes after. */
  layer?: number | undefined;
  /** A fixed slot across its layer (0 is the first). Left out, it is placed to cross as few edges as it can. */
  slot?: number | undefined;
}
```

### GraphLayoutPoint

A place in the layout: the layer, and the position across it (may be fractional, so a node can sit between two).

```ts
{ layer: number; slot: number }
```

### GraphLayoutEdge

An edge as laid out: from one node to another, through a point in each layer it crosses.

```ts
{
  from: string;
  to: string;
  /** From the source's place to the target's, with a bend in each layer between. */
  points: GraphLayoutPoint[];
  /** Set when the edge closes a loop, or runs back to the same or an earlier layer: it was left out of the layering. */
  loop?: boolean | undefined;
}
```

### GraphLayout

```ts
{
  /** Each node's place, by id. */
  places: Map<string, GraphLayoutPoint>;
  /** How many layers there are. */
  layers: number;
  /** How wide the widest layer is, in slots (the last position plus one). */
  slots: number;
  edges: GraphLayoutEdge[];
  /** Edges naming a node that isn't in the graph, left out. */
  missing: {
    from: string;
    to: string;
  }[];
  /** True when some layer holds more than one node and there is at least one edge: the work branches. */
  branches: boolean;
}
```

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