---
name: build-pi-tui-component
description: Build or review a terminal UI component for a Pi extension with a proven pattern from the Pi TUI pattern catalog. Use when an extension needs its own rendering, such as a list or picker, an overlay or dialog, a status line or widget, an editor, key handling, animation or themed output. Covers picking a pattern by job, reading it, applying it with Pi's real APIs, and checking the result at 40, 60, 80 and 120 columns in dark and light themes.
---

# Build a Pi TUI component

This skill turns a UI job into a checked Pi component. The pattern catalog at https://pi-tui.ratstack.sh/ supplies the design. Pi's own docs supply the API.

## 1. Check whether Pi already does it

Read Pi's [terminal UI guide](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md) first. Pi's `ctx.ui` methods cover selection, confirmation, text input, notices, status text and widgets. Use them when they fit. Build a custom component only when the UI needs its own rendering, input, focus, layout or lifecycle.

## 2. Pick a pattern by job

Call `search({ query })` over MCP at https://pi-tui.ratstack.sh/mcp or `POST https://pi-tui.ratstack.sh/api/search`, or fetch [patterns.json](https://pi-tui.ratstack.sh/patterns.json) and match the words of your job against each pattern's `intent`. The [agent guide](https://pi-tui.ratstack.sh/llms.txt) lists the same patterns by family and category.

The catalog has 4 families:

- **Structural**: Fit and compose terminal surfaces without combining their separate content owners.
- **Behavioral**: Route selection, input, focus and timing through explicit interaction state.
- **Lifecycle**: Tie UI, drafts, resources and saved state to their owning interaction or session.
- **Presentation**: Project, cache and style visible output without changing the underlying work.

Most components combine patterns. Pick one primary pattern for the job, then add the patterns it names as related.

## 3. Read the pattern

Call `read({ id })`, or fetch `/patterns/<id>.md` or `/patterns/<id>.json`. Read these sections in order:

1. **Intent** and **Applicability** say what the pattern is for. Stop if they don't match your job.
2. **For agents: choose and check** lists the neighbours in its category. Read a neighbour whose intent fits your job more closely.
3. **Structure** and **Participants** name the parts and the Pi APIs that play them.
4. **Implementation** lists the mistakes the pattern exists to avoid.
5. **States** shows every state at every width and theme. A state marked as a counter-example shows the mistake on purpose. `states({ id, width, theme })` returns the same frames and verdicts as data.
6. **Related** and **Linked from** lead to the patterns it works with. `related({ id })` returns both lists.

## 4. Build it against Pi's real APIs

Participant and API names link to Pi's docs, pinned to Pi 1.0.3. Read those docs before you write code. Don't rely on remembered signatures.

Pi's component contract, from its docs:

- A component's `render(width)` returns an array of lines for that width.
- Every line must fit the width. Measure with `visibleWidth()`, and cut or wrap with `truncateToWidth()`, `sliceByColumn()` and `wrapTextWithAnsi()`. String length is not terminal width.
- Pi resets styling after every line, so apply styles on each line.
- After a state change, invalidate the component and call the injected `tui.requestRender()`.
- Take colours from the theme Pi passes in. Rebuild any cached coloured text from `invalidate()`.
- Use the keybindings manager Pi passes in for configurable actions.
- A `ctx.ui.custom()` component finishes through the completion callback it was given.

Each pattern page has **Sample code**: the story its frames were rendered from, with made-up data. Use it as a reference shape. **Known uses** link to exact lines in Nico Bailon's public repos at fixed commits. Read them to see the pattern in a real extension. Check a repo's license before you copy anything from it.

## 5. Check it at four widths and both themes

Render the component at 40, 60, 80 and 120 columns, in Pi's dark and light themes. Check every frame:

- **Width:** no line is wider than the terminal.
- **Style leak:** no line ends with colour, bold or a link still switched on.
- **Hard-coded colour:** every colour comes from the active theme.
- **Height:** the output fits in the rows the terminal has.

A width check needs only the component and Pi's width helper:

```ts
import { visibleWidth } from "@earendil-works/pi-tui";

for (const width of [40, 60, 80, 120]) {
  for (const [row, line] of component.render(width).entries()) {
    if (visibleWidth(line) > width) throw new Error(`row ${row} is wider than ${width} columns`);
  }
}
```

Then compare your output with the pattern's frames for the same state, width and theme. Your component should look like the good states and unlike the counter-examples. If the pattern animates, check every frame of the animation. Pi's guide also asks you to test resize, wide characters, theme changes and focus changes.

## 6. Report

Name the patterns you used and link their pages. List the widths and themes you checked and the result of each check. Say which checks you could not run.
