---
title: "TUI and Themes"
type: concept
tags: [tui, ui, foundational, user, well-established]
created: 2026-08-05
updated: 2026-08-05
sources: ["raw/github_doc-packages-coding-agent-docs-tui-md.md", "raw/github_doc-packages-coding-agent-docs-keybindings-md.md", "raw/github_doc-packages-coding-agent-docs-themes-md.md", "raw/github_doc-packages-tui-readme-md.md"]
confidence: high
prime_agent_version: "v0.7.0"
---

## Definition

Prime Agent's terminal user interface (TUI) is built on `packages/tui` (published as `prime-agent-tui`, source name `@earendil-works/pi-tui`), a minimal terminal UI framework with differential rendering and synchronized output for flicker-free interactive CLI applications. The interactive-mode screen it renders is composed, top to bottom, of a startup header, the message transcript, an input editor, and a footer. Themes are JSON files that supply the 51 color tokens the TUI's components use to style everything from borders to syntax highlighting, and keybindings are a namespaced, user-remappable id-to-key mapping that drives every keyboard interaction in that screen.

## How It Works

**Rendering.** The TUI uses three rendering strategies: first render (output all lines without clearing scrollback), width-changed-or-change-above-viewport (clear screen, full re-render), and normal update (move cursor to the first changed line, clear to end, render only changed lines). All updates are wrapped in synchronized output (`\x1b[?2026h` ... `\x1b[?2026l`) for atomic, flicker-free updates. The TUI works with any object implementing a `Terminal` interface (`ProcessTerminal` for real terminals, `VirtualTerminal` for tests using `@xterm/headless`).

**Components.** Every component implements `render(width): string[]`, optional `handleInput(data)`, optional `wantsKeyRelease`, and `invalidate()`. Each returned line must not exceed the given `width` or the TUI errors; helpers `visibleWidth()`, `truncateToWidth()`, and `wrapTextWithAnsi()` handle ANSI-aware sizing. Built-ins include `Text`, `TruncatedText`, `Box`, `Container`, `Spacer`, `Input`, `Editor`, `Markdown`, `Loader`/`CancellableLoader`, `SelectList`, `SettingsList`, and `Image` (Prime Agent renders compact image metadata rather than terminal graphics). Components needing a text cursor (for CJK IME support) implement the `Focusable` interface; the TUI scans rendered output for a zero-width `CURSOR_MARKER` and positions the hardware cursor there. Container components with embedded `Input`/`Editor` children must propagate `focused` down to the child or IME candidate windows appear in the wrong place.

**Overlays.** Overlays render on top of existing content without clearing the screen (`tui.showOverlay()` at the TUI-primitives layer, or `{ overlay: true }` passed to `ctx.ui.custom()` from an extension). Overlay placement supports anchor-based positioning (9 anchors: `center`, `top-left`, ..., `right-center`), percentage or absolute row/col, size as fixed or percentage with `minWidth`/`maxHeight` floors, margins, and a `visible(termWidth, termHeight)` callback for responsive hiding. Resolution order: `minWidth` floors width; absolute row/col beats percentage beats anchor; margin clamps the final position; `visible` is re-evaluated every frame. Overlay components are disposed on close — extensions must create fresh instances rather than reusing a stale reference to re-show one.

**Theming inside components.** Components accept `theme.fg(color, text)` and `theme.bg(color, text)` callbacks rather than importing a theme module directly. When the active theme changes, the TUI calls `invalidate()` on every component to clear render caches. A component that pre-bakes theme colors into cached strings (e.g., via `theme.fg()` stored in a child `Text`) must rebuild that content inside its own `invalidate()` override, or the old theme's ANSI codes remain baked in after a theme switch.

**Themes as data.** A theme is a JSON file with `name` (required, unique), optional `vars` (reusable named colors), and `colors` (must define all 51 required tokens across Core UI, Backgrounds & Content, Markdown, Tool Diffs, Syntax Highlighting, Thinking Level Borders, and Bash Mode categories — there are no optional colors). Color values are hex (`"#ff0000"`), a 256-color palette index (`0-255`), a `vars` reference, or `""` for the terminal's default color. An optional `export` section customizes `/export` HTML colors; if omitted, they derive from `userMessageBg`. Prime Agent uses 24-bit RGB color and falls back to nearest approximation on 256-color terminals (check with `echo $COLORTERM`).

**Theme discovery and hot reload.** Prime Agent loads themes from built-ins (`dark`, `light`), `~/.prime/agent/themes/*.json` (global), `.prime/agent/themes/*.json` (project), package `themes/` directories or `pi.themes` manifest entries, the `themes` array in settings, and repeatable `--theme <path>` CLI flags; `--no-themes` disables discovery. Select via `/settings` or `{"theme": "my-theme"}` in `settings.json`. On first run, Prime Agent detects the terminal background and defaults to `dark` or `light`. Editing the currently active custom theme file triggers an automatic hot reload for immediate visual feedback.

**Keybindings.** All shortcuts are customizable via `~/.prime/agent/keybindings.json`, keyed by namespaced ids (e.g., `tui.editor.cursorUp`, `app.model.select`) that are the same ids extension authors use in `keyHint()` and the injected `keybindings` manager. Older pre-namespaced ids (like `cursorUp`) are migrated automatically on startup. Each action can bind to one key or an array of keys; user config overrides (not merges into) the default list per-action. Key format is `modifier+key` with combinable modifiers `ctrl`, `shift`, `alt` and keys spanning letters, digits, special keys (`escape`, `enter`, `tab`, arrows, `home`/`end`, `pageUp`/`pageDown`, etc.), function keys `f1`-`f12`, and symbols. After editing `keybindings.json`, run `/reload` to apply changes without restarting the session. `app.suspend` (Ctrl+Z) has no default binding on native Windows because Windows terminals lack Unix job control; binding it manually there just shows a status message instead of suspending (WSL keeps normal Linux behavior).

## Key Parameters

- **51 required color tokens**, grouped as: Core UI (11: `accent`, `border`, `borderAccent`, `borderMuted`, `success`, `error`, `warning`, `muted`, `dim`, `text`, `thinkingText`), Backgrounds & Content (12), Markdown (10), Tool Diffs (3: `toolDiffAdded`/`Removed`/`Context`), Syntax Highlighting (9), Thinking Level Borders (6: `thinkingOff` through `thinkingXhigh`), and Bash Mode (1: `bashMode`, the editor border color when a `!`-prefixed bash command is being entered).
- **Component interface contract**: `render(width): string[]` (line length must never exceed `width`), `handleInput?(data)`, `wantsKeyRelease?` (Kitty protocol key-release events, default `false`), `invalidate()`.
- **Overlay sizing/position keys**: `width`/`minWidth`/`maxHeight` (number or `%` string), `anchor`, `offsetX`/`offsetY`, `row`/`col` (percent or absolute), `margin`, `visible`, `nonCapturing`.
- **`PI_TUI_WRITE_LOG`**: env var that captures the raw ANSI stream written to stdout, for debugging renders.
- **Debug key**: Shift+Ctrl+D triggers `tui.onDebug`.

## When To Use

Reach for the TUI component/overlay system when building an extension that needs custom interactive UI — selection dialogs (`SelectList` + `DynamicBorder`), cancellable async operations (`BorderedLoader`), settings toggles (`SettingsList`), persistent status indicators (`ctx.ui.setStatus`), widgets above/below the editor (`ctx.ui.setWidget`), a custom footer (`ctx.ui.setFooter`), or a fully custom editor such as a vim-mode input (`CustomEditor` subclass via `ctx.ui.setEditorComponent`). Reach for the theming system when Prime Agent's default `dark`/`light` themes don't match a terminal or personal palette preference, or when shipping a themed extension/package. Reach for keybindings customization when the defaults conflict with terminal-level bindings (see [[concepts/platform-setup]] for terminal-specific Enter-key caveats) or to emulate emacs/vim editing conventions.

## Risks & Pitfalls

- Emitting multi-line styled text without reapplying ANSI codes per line: the TUI appends a full SGR/OSC-8 reset at the end of every rendered line, so styles never carry across lines — use `wrapTextWithAnsi()` for correctly-styled wrapped output.
- Forgetting the `Focusable` propagation pattern in container components (dialogs, selectors) with embedded `Input`/`Editor` children breaks IME candidate-window positioning for CJK input.
- Pre-baking theme colors into cached component state without overriding `invalidate()` to rebuild that content means a theme switch leaves stale ANSI codes on screen.
- Reusing a disposed overlay component reference (e.g., holding onto a `MenuComponent` instance after `close()`) is a dangling reference — always re-invoke the factory to show an overlay again.
- A theme file that omits any of the 51 required tokens is invalid — there is no partial/optional-token mode.
- On xfce4-terminal, terminator, and IntelliJ IDEA's integrated terminal, modifier-augmented Enter (`Ctrl+Enter`, `Shift+Enter`) can't be distinguished from plain `Enter`, which silently breaks any keybinding relying on that distinction (see [[concepts/platform-setup]]).

## Related Concepts

- [[concepts/settings-and-customization]] — where the active theme name, `editorPaddingX`, `autocompleteMaxVisible`, and other UI settings are configured
- [[concepts/platform-setup]] — terminal-specific configuration (Ghostty, WezTerm, Windows Terminal, tmux) required for reliable modifier-key detection that the keybinding system depends on
- [[concepts/extensions]] — extensions are the primary consumer of the TUI component, overlay, and custom-editor APIs described here
- [[concepts/daemon]] — the daemon delivers session/transcript state that the TUI renders; TUI invalidation and re-render is triggered by daemon-sourced events

## Sources

- raw/github_doc-packages-coding-agent-docs-tui-md.md
- raw/github_doc-packages-coding-agent-docs-keybindings-md.md
- raw/github_doc-packages-coding-agent-docs-themes-md.md
- raw/github_doc-packages-tui-readme-md.md
