# Pi TUI patterns: full corpus

> For agents: start with the [agent guide](https://pi-tui.ratstack.sh/llms.txt). Every page here has a Markdown twin, and every pattern has a JSON record.

Every pattern's full text, in catalog order: 64 patterns. Each pattern's Markdown twin adds its frames, sample code and agent guidance.

## Column Gauge

`column-gauge` · Structural / Layout · [Markdown](https://pi-tui.ratstack.sh/patterns/column-gauge.md) · [JSON](https://pi-tui.ratstack.sh/patterns/column-gauge.json) · also known as Display-column fitting

### Intent

Fit styled text to terminal columns before padding or framing it.

### Motivation

Pi Intercom's session rows contain styled labels and paths that must fit fixed-width frames.

### Applicability

- Use this when rows contain ANSI styling, emoji or wide characters.

### Structure

```text
text -> measure -> fit -> pad
```

### Participants

- [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Measures rendered terminal columns rather than string length.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- [`sliceByColumn`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Slices text by terminal columns.
- [`wrapTextWithAnsi`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Wraps text while preserving styling across lines.
- `Row budget`: Allocates columns before content is padded or framed.

Pi component APIs: [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`sliceByColumn`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`wrapTextWithAnsi`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model)

### Consequences

- Styled and wide text aligns in display columns.
- Truncation or wrapping changes how much text is visible.

### Implementation

- String length does not measure terminal columns.
- Padding alone does not clip an oversized label.

### Known uses: seen in Nico's repos

- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Window session rows and truncate paths by visible width
  - [ui/session-list.ts:6-29](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/session-list.ts#L6-L29) @a5fad4df
  - [ui/session-list.ts:112-179](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/session-list.ts#L112-L179) @a5fad4df
- [**earendil-works/pi**](https://github.com/earendil-works/pi): Optionally pad truncated text to exact terminal width
  - [packages/tui/src/utils.ts:1-65](https://github.com/earendil-works/pi/blob/d29f268f4662fc138cc87b23b7e6c45a4e0fe57b/packages/tui/src/utils.ts#L1-L65) @d29f268f
- [**pi-autoresearch**](https://github.com/nicobailon/pi-autoresearch): Render to the available width and bound vertical output
  - [extensions/pi-autoresearch/index.ts:645-676](https://github.com/nicobailon/pi-autoresearch/blob/22bd30b19482f2be5936031b2c6b149115779f16/extensions/pi-autoresearch/index.ts#L645-L676) @22bd30b1
- [**pi-memory-workbench**](https://github.com/nicobailon/pi-memory-workbench): Render to the available width and bound vertical output
  - [todo-widget.ts:5-8](https://github.com/nicobailon/pi-memory-workbench/blob/92b4c9c3ad07841418d77118bf8bd02ad204f7c4/todo-widget.ts#L5-L8) @92b4c9c3
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Render to the available width and bound vertical output
  - [index.ts:1108-1150](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/index.ts#L1108-L1150) @859dee67

### Related

- [Hinge Panel](https://pi-tui.ratstack.sh/patterns/hinge-panel.md): supplies fitted rows

### States

- Styled row (`default`): Fit a coloured row containing wide glyphs and a long synthetic path.
- Exact-width row (`padded`): Pad short text to the same width as a clipped row.
- Don't: count characters (`dont-string-length`): Fit wide glyphs with string slicing so the row exceeds its display-column budget. Counter-example; fails width on purpose.

## Hinge Panel

`hinge-panel` · Structural / Layout · [Markdown](https://pi-tui.ratstack.sh/patterns/hinge-panel.md) · [JSON](https://pi-tui.ratstack.sh/patterns/hinge-panel.json) · also known as Responsive panel

### Intent

Switch between side-by-side and stacked sections as available space changes.

### Motivation

The MCP adapter's panel needs list and detail content in terminals too narrow for side-by-side panes.

### Applicability

- Use this when a panel must remain readable in narrow or short terminals.

### Structure

```text
width + rows -> layout
  wide -> list | detail
narrow -> list / detail
```

### Participants

- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Measures rendered terminal columns rather than string length.
- `Layout decision`: Chooses side-by-side or stacked content from available space.

Pi component APIs: [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model)

### Consequences

- The same panel remains usable across terminal sizes.
- Narrow layouts stack or omit visible detail.

### Implementation

- Use the supplied width rather than a fixed fallback.
- Reserve rows for framing and controls.

### Known uses: seen in Nico's repos

- [**pi-mcp-adapter**](https://github.com/nicobailon/pi-mcp-adapter): Responsive list/detail TUI panels
  - [mcp-setup-panel.ts:12-37](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/mcp-setup-panel.ts#L12-L37) @85db03d8
  - [mcp-setup-panel.ts:45-84](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/mcp-setup-panel.ts#L45-L84) @85db03d8
- [**pi-autoresearch**](https://github.com/nicobailon/pi-autoresearch): Render to the available width and bound vertical output
  - [extensions/pi-autoresearch/index.ts:645-676](https://github.com/nicobailon/pi-autoresearch/blob/22bd30b19482f2be5936031b2c6b149115779f16/extensions/pi-autoresearch/index.ts#L645-L676) @22bd30b1
- [**pi-memory-workbench**](https://github.com/nicobailon/pi-memory-workbench): Render to the available width and bound vertical output
  - [todo-widget.ts:5-8](https://github.com/nicobailon/pi-memory-workbench/blob/92b4c9c3ad07841418d77118bf8bd02ad204f7c4/todo-widget.ts#L5-L8) @92b4c9c3
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Render to the available width and bound vertical output
  - [index.ts:1108-1150](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/index.ts#L1108-L1150) @859dee67

### Related

- [Column Gauge](https://pi-tui.ratstack.sh/patterns/column-gauge.md): fits each composed row

### States

- Wide layout (`wide`): Show list and detail beside each other where their measured widths fit.
- Narrow layout (`narrow`): Stack the same sections and cap body rows.
- Don't: fixed width (`dont-fixed-width`): Return a fixed 74-column frame at narrower widths. Counter-example; fails width on purpose.

## Section Loom

`section-loom` · Structural / Layout · [Markdown](https://pi-tui.ratstack.sh/patterns/section-loom.md) · [JSON](https://pi-tui.ratstack.sh/patterns/section-loom.json) · also known as Section composition

### Intent

Compose a dashboard from focused render helpers under one layout budget.

### Motivation

Messenger's activity overlay combines status, workers, tasks, feed and detail from separate render helpers.

### Applicability

- Use this when several sections share formatting without sharing interaction state.

### Structure

```text
snapshot -> section helpers
helpers -> budget -> lines
```

### Participants

- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- `Section helpers`: Return focused sections to one budget-owning composer.

Pi component APIs: [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model)

### Consequences

- Sections share formatting without one large renderer.
- The composer still owns the combined row and column budget.

### Implementation

- Pure formatting does not mount or refresh a view.
- Budget width and height where sections are combined.

### Known uses: seen in Nico's repos

- [**pi-coordination**](https://github.com/nicobailon/pi-coordination): Centralize status and section formatting
  - [coordinate/render-utils.ts:1-65](https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/render-utils.ts#L1-L65) @7f32f5d9
- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Centralize status and section formatting
  - [overlay-render.ts:1-55](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/overlay-render.ts#L1-L55) @09937ed6
- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Messenger overlay composed by render helpers
  - [overlay.ts:576-635](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/overlay.ts#L576-L635) @09937ed6

### Related

- [Hinge Panel](https://pi-tui.ratstack.sh/patterns/hinge-panel.md): chooses the arrangement

### States

- Sections (`default`): Combine status, tasks and a feed with consistent labels.
- Compact sections (`compact`): Collapse secondary sections while keeping the same underlying snapshot.

## Status Ribbon

`status-ribbon` · Structural / Layout · [Markdown](https://pi-tui.ratstack.sh/patterns/status-ribbon.md) · [JSON](https://pi-tui.ratstack.sh/patterns/status-ribbon.json) · also known as Footer segments

### Intent

Compose a footer from independently visible and measured status segments.

### Motivation

Powerline Footer must order optional segments and move secondary content when the footer runs out of columns.

### Applicability

- Use this when several optional signals compete for one footer line.

### Structure

```text
footer data -> segments
segments -> measure -> footer
```

### Participants

- [`ctx.ui.setFooter`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Installs a footer factory with read-only footer data.
- [`ReadonlyFooterDataProvider`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#choose-an-integration-point): Provides branch, status and provider data without mutation.
- [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Measures rendered terminal columns rather than string length.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- `Segment list`: Keeps visibility, ordering and measured output separate.

Pi screen APIs: [`ctx.ui.setFooter`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ReadonlyFooterDataProvider`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#choose-an-integration-point), [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model)

### Consequences

- Independent segments support responsive visibility.
- Hidden segments and spacing need explicit width accounting.

### Implementation

- Hidden segments must not consume spacing.
- The read-only footer provider has no session-ID getter.

### Known uses: seen in Nico's repos

- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Compose a footer from independently rendered segments
  - [segments.ts:540-576](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/segments.ts#L540-L576) @859dee67
  - [index.ts:1085-1150](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/index.ts#L1085-L1150) @859dee67
- [**earendil-works/pi**](https://github.com/earendil-works/pi): Expose read-only footer data to custom footer components
  - [packages/coding-agent/src/core/footer-data-provider.ts:40-80](https://github.com/earendil-works/pi/blob/7b902612e96a8bf49cf6f34345f09a44e5ca6926/packages/coding-agent/src/core/footer-data-provider.ts#L40-L80) @7b902612
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Expose read-only footer data to custom footer components

### Related

- [Keyed Slot](https://pi-tui.ratstack.sh/patterns/keyed-slot.md): supplies keyed footer signals

### States

- All segments (`default`): Show branch, model and two keyed statuses in a synthetic footer.
- Secondary segments (`compact`): Move or omit secondary segments when the width cannot fit them.
- Hidden segment (`empty`): Hide an empty segment without leaving a gap.

## Row Window

`row-window` · Structural / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/row-window.md) · [JSON](https://pi-tui.ratstack.sh/patterns/row-window.json) · also known as Windowed list

### Intent

Keep the selected item inside a bounded moving list window.

### Motivation

Intercom's session picker shows multi-line entries inside a bounded selection window.

### Applicability

- Use this when a picker has more rows than its terminal view can show.

### Structure

```text
items + selection -> window
window -> fitted rows + hint
```

### Participants

- [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns a bounded selection list and selection callbacks.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Measures rendered terminal columns rather than string length.
- `Selection window`: Keeps the highlight inside its physical-row budget.

Pi component APIs: [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `matchesKey`

### Consequences

- The selection stays visible without drawing the entire list.
- Physical rows, descriptions and hints share a limited budget.

### Implementation

- Budget physical rows rather than counting multi-line items.
- Keep clipping and position hints inside the width budget.

### Known uses: seen in Nico's repos

- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Window session rows and truncate paths by visible width
  - [ui/session-list.ts:6-29](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/session-list.ts#L6-L29) @a5fad4df
  - [ui/session-list.ts:112-179](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/session-list.ts#L112-L179) @a5fad4df
- [**pi-skill-palette**](https://github.com/nicobailon/pi-skill-palette): Narrow palette results as the query grows
  - [index.ts:622-741](https://github.com/nicobailon/pi-skill-palette/blob/a5c4429b8c2e33ab903d07856497014f3d5ad34e/index.ts#L622-L741) @a5c4429b
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Rank selector choices by best matching field
  - [src/tui/render-helpers.ts:1-29](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/render-helpers.ts#L1-L29) @6826b054
  - [src/slash/selector.ts:85-117](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/slash/selector.ts#L85-L117) @6826b054

### Related

- [Identity Anchor](https://pi-tui.ratstack.sh/patterns/identity-anchor.md): preserves identity when items change

### States

- First window (`start`): Show the first selected item and a clipped-list position hint.
- Moving window (`middle`): Move selection into the middle of a longer list.
- Multi-line items (`multiline`): Fit descriptions and hints into a fixed row budget.
- Don't: count entries as rows (`dont-count-items`) Counter-example; fails height on purpose.

## Detail Lens

`detail-lens` · Structural / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/detail-lens.md) · [JSON](https://pi-tui.ratstack.sh/patterns/detail-lens.json) · also known as List-detail view

### Intent

Derive a selected item's detail sections from a read-only snapshot.

### Motivation

The Subagents fleet browser derives detail sections for a selected heterogeneous run.

### Applicability

- Use this when heterogeneous jobs or records need a browseable detail view.

### Structure

```text
snapshot -> selected item
selected item -> detail sections
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Resolves input against configurable action bindings.
- `Snapshot projection`: Derives detail for the currently selected item.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `SelectList`, `truncateToWidth`

### Consequences

- One selected snapshot drives consistent detail output.
- Projection must remain separate from job mutations and action callbacks.

### Implementation

- Do not mutate the underlying jobs during display projection.
- Pass actions and configurable keys into the mounted view.

### Known uses: seen in Nico's repos

- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Keep fleet selection stable while details are derived
  - [src/tui/fleet.ts:1-120](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet.ts#L1-L120) @6826b054
  - [src/tui/fleet.ts:1360-1463](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet.ts#L1360-L1463) @6826b054
- [**pi-mcp-adapter**](https://github.com/nicobailon/pi-mcp-adapter): Responsive list/detail TUI panels
  - [mcp-setup-panel.ts:12-37](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/mcp-setup-panel.ts#L12-L37) @85db03d8
  - [mcp-setup-panel.ts:45-84](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/mcp-setup-panel.ts#L45-L84) @85db03d8

### Related

- [Identity Anchor](https://pi-tui.ratstack.sh/patterns/identity-anchor.md): keeps the selected identity stable

### States

- Selected detail (`default`): Show a list and details for its highlighted synthetic run.
- Different selection (`changed`): Select another run and derive its own detail sections.
- Empty snapshot (`empty`): Show a no-runs view without stale details.

## Tab Deck

`tab-deck` · Structural / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/tab-deck.md) · [JSON](https://pi-tui.ratstack.sh/patterns/tab-deck.json) · also known as Tabbed view

### Intent

Keep separate keyboard-navigable data views behind a tab strip.

### Motivation

The usage extension puts several data views in one keyboard-driven dashboard.

### Applicability

- Use this when one compact dashboard has several related datasets.

### Structure

```text
keys -> active tab
active tab -> rows + selection
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Recognizes explicit terminal key combinations.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Active tab`: Selects which dataset and row highlight are visible.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `SelectList`, `truncateToWidth`

### Consequences

- Tabs keep related datasets in one compact view.
- Only the active tab is visible at a time.

### Implementation

- Request a render after tab or row changes.
- Resolve the custom interaction on dismissal.

### Known uses: seen in Nico's repos

- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Usage extension: navigable tabular dashboard
  - [usage-extension/index.ts:347-400](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/usage-extension/index.ts#L347-L400) @bca5070b
  - [usage-extension/index.ts:526-558](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/usage-extension/index.ts#L526-L558) @bca5070b

### Related

- [Action Compass](https://pi-tui.ratstack.sh/patterns/action-compass.md): resolves navigation actions

### States

- First tab (`first`): Show summary rows under the active tab.
- Second tab (`second`): Change tab and show its independent rows and highlight.

## Branch Fold

`branch-fold` · Structural / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/branch-fold.md) · [JSON](https://pi-tui.ratstack.sh/patterns/branch-fold.json) · also known as Foldable tree

### Intent

Retain validated fold identifiers while navigating a session tree.

### Motivation

Dot314's anycopy selector reopens a session tree with saved fold identifiers.

### Applicability

- Use this when reopening a tree should preserve its compact view.

### Structure

```text
tree + valid fold IDs
      -> visible branches
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`pi.appendEntry`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management): Persists typed custom data outside model context.
- [`ctx.sessionManager.getEntries`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management): Reads session-file entries for deliberate reconstruction.
- `Fold identifiers`: Name hidden branches after validation against current nodes.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`pi.appendEntry`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management), [`ctx.sessionManager.getEntries`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management), `TreeSelectorComponent`, `TreeSelectorComponent.getTreeList`, `setKeybindings`, `truncateToWidth`

### Consequences

- A reopened tree retains its compact view.
- Saved identifiers must be checked against the current nodes.

### Implementation

- Discard fold identifiers that no longer name a tree node.
- Check navigation capability before changing the session.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Persist fold state while reusing a session-tree selector
  - [extensions/anycopy/index.ts:971-1035](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/anycopy/index.ts#L971-L1035) @17cce138

### Related

- [Session Memento](https://pi-tui.ratstack.sh/patterns/session-memento.md): persists the fold identifiers

### States

- Expanded tree (`expanded`): Show several synthetic branches with the selected node.
- Folded branch (`folded`): Hide descendants while leaving the branch visible.
- Restored folds (`reopened`): Reopen from saved fold IDs and omit an invalid saved ID.

## Shared Shell

`shared-shell` · Structural / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/shared-shell.md) · [JSON](https://pi-tui.ratstack.sh/patterns/shared-shell.json) · also known as Modal frame

### Intent

Wrap specialized dialog content in a shared themed frame.

### Motivation

Tool Display's Zellij modal reuses framing around specialized settings content.

### Applicability

- Use this when several custom dialogs need the same title, border and controls.

### Structure

```text
frame(title, theme)
  + content(render, input, dispose)
  -> custom overlay
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`OverlayOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Defines overlay dimensions, placement and focus behavior.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- `Content contract`: Supplies specialized render, input and disposal behavior.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `Text`, `visibleWidth`

### Consequences

- Different dialogs share borders, titles and controls.
- The frame still needs a small content and disposal contract.

### Implementation

- Keep content state outside the shared frame.
- Clip titles before inserting them between borders.
- The source snapshot does not declare Pi 1.0.3 support.

### Known uses: seen in Nico's repos

- [**pi-tool-display**](https://github.com/nicobailon/pi-tool-display): Zellij modal: reusable frame and content contract
  - [src/zellij-modal.ts:240-275](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/zellij-modal.ts#L240-L275) @fca8c858
  - [src/zellij-modal.ts:720-755](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/zellij-modal.ts#L720-L755) @fca8c858

### Related

- [Settings Bench](https://pi-tui.ratstack.sh/patterns/settings-bench.md): supplies specialized settings content

### States

- First content (`first`): Show a framed synthetic settings view.
- Different content (`second`): Reuse the frame around an inspection view.
- Don't: unbounded title (`dont-long-title`): Place an untruncated long title between borders so it exceeds width. Counter-example; fails width on purpose.

## Keyed Slot

`keyed-slot` · Structural / Status and widgets · [Markdown](https://pi-tui.ratstack.sh/patterns/keyed-slot.md) · [JSON](https://pi-tui.ratstack.sh/patterns/keyed-slot.json) · also known as Keyed status slot

### Intent

Publish and clear compact state under one stable footer key.

### Motivation

Nico's status helpers need to clear one operation without clearing concurrent footer signals.

### Applicability

- Use this when an operation's state fits in one short label.

### Structure

```text
owner -> setStatus(key, text)
end -> setStatus(key, undefined)
```

### Participants

- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- `Slot owner`: Chooses a stable key and clears it when its state ends.

Pi screen APIs: [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user)

### Consequences

- Stable keys isolate compact status ownership.
- Every lifecycle exit must clear its own key.

### Implementation

- Clear the same key when its state ends.
- Clearing one key does not clear other operations.
- Reject updates from stale contexts.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Own and clear a named status slot
  - [extensions/rp-native-tools-lock/index.ts:105-118](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/rp-native-tools-lock/index.ts#L105-L118) @17cce138
  - [extensions/ephemeral-mode.ts:34-70](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/ephemeral-mode.ts#L34-L70) @17cce138
  - [extensions/pi-codex-goal/src/goal-runtime-status.ts:1-15](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-codex-goal/src/goal-runtime-status.ts#L1-L15) @17cce138
  - [extensions/stash/index.ts:34-72](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/stash/index.ts#L34-L72) @17cce138
  - [extensions/poly-notify/index.ts:315-335](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/poly-notify/index.ts#L315-L335) @17cce138
- [**pi-annotate**](https://github.com/nicobailon/pi-annotate): Own and clear a named status slot
  - [index.ts:54-57](https://github.com/nicobailon/pi-annotate/blob/cebb68deb28dcc6422d2846a2a36e5e20ca31725/index.ts#L54-L57) @cebb68de
- [**pi-boomerang**](https://github.com/nicobailon/pi-boomerang): Own and clear a named status slot
  - [index.ts:1422-1445](https://github.com/nicobailon/pi-boomerang/blob/1a5985b2d92cfa84ce1f470d100d02b368711a91/index.ts#L1422-L1445) @1a5985b2
  - [index.ts:1155-1170](https://github.com/nicobailon/pi-boomerang/blob/1a5985b2d92cfa84ce1f470d100d02b368711a91/index.ts#L1155-L1170) @1a5985b2
- [**pi-model-switch**](https://github.com/nicobailon/pi-model-switch): Own and clear a named status slot
  - [index.ts:202-205](https://github.com/nicobailon/pi-model-switch/blob/b254ece4fe90d34938fdd69c975379041dce9c53/index.ts#L202-L205) @b254ece4
- [**pi-prompt-template-model**](https://github.com/nicobailon/pi-prompt-template-model): Own and clear a named status slot
  - [index.ts:784-797](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/index.ts#L784-L797) @6da20591
  - [index.ts:1578-1590](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/index.ts#L1578-L1590) @6da20591
  - [index.ts:1757-1765](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/index.ts#L1757-L1765) @6da20591
- [**pi-review-loop**](https://github.com/nicobailon/pi-review-loop): Own and clear a named status slot
  - [index.ts:16-22](https://github.com/nicobailon/pi-review-loop/blob/878d1a50ceff4ae6d76b99ea0a715a4b2ba28a11/index.ts#L16-L22) @878d1a50
- [**pi-rewind-hook**](https://github.com/nicobailon/pi-rewind-hook): Own and clear a named status slot
  - [index.ts:440-450](https://github.com/nicobailon/pi-rewind-hook/blob/a62e7c2c89d130b3a02f7f799c15de62743d3fa8/index.ts#L440-L450) @a62e7c2c
- [**dot314**](https://github.com/nicobailon/dot314): Upstream-derived preset picker and status
  - [extensions/preset.ts:1-18](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/preset.ts#L1-L18) @17cce138

### Related

- [Notice Fuse](https://pi-tui.ratstack.sh/patterns/notice-fuse.md): adds a deadline to the slot

### States

- Owned status (`active`): Show two independent synthetic operation labels.
- One slot cleared (`cleared`): Remove one owned key while the other remains.

## Signal Pair

`signal-pair` · Structural / Status and widgets · [Markdown](https://pi-tui.ratstack.sh/patterns/signal-pair.md) · [JSON](https://pi-tui.ratstack.sh/patterns/signal-pair.json) · also known as Status-widget pair

### Intent

Pair a compact footer signal with a structured near-editor widget.

### Motivation

Memory Workbench and other extensions need a short status plus a larger task display.

### Applicability

- Use this when progress needs both a short signal and a task display.

### Structure

```text
operation -> status key
operation -> widget key
end -> clear both
```

### Participants

- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- `Paired keys`: Identify the compact signal and its structured detail.

Pi screen APIs: [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `Text`

### Consequences

- A compact signal can coexist with structured detail.
- Two owned keys require paired cleanup.

### Implementation

- Clear both owned keys when the operation ends.
- Guard terminal widget factories with TUI mode.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Separate compact status from structured detail
  - [extensions/plan-mode.ts:575-590](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/plan-mode.ts#L575-L590) @17cce138
  - [extensions/pi-codex-goal/src/goal-runtime-status.ts:42-63](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-codex-goal/src/goal-runtime-status.ts#L42-L63) @17cce138
  - [extensions/plan-mode.ts:1-18](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/plan-mode.ts#L1-L18) @17cce138
- [**pi-custom-compaction**](https://github.com/nicobailon/pi-custom-compaction): Separate compact status from structured detail
  - [runtime/session-state.ts:49-78](https://github.com/nicobailon/pi-custom-compaction/blob/a0e4700badb1c5c1c2dd12eeb250ff067fa67b7e/runtime/session-state.ts#L49-L78) @a0e4700b
  - [runtime/session-state.ts:105-183](https://github.com/nicobailon/pi-custom-compaction/blob/a0e4700badb1c5c1c2dd12eeb250ff067fa67b7e/runtime/session-state.ts#L105-L183) @a0e4700b
- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Separate compact status from structured detail
  - [ralph-wiggum/index.ts:190-215](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/ralph-wiggum/index.ts#L190-L215) @bca5070b
- [**pi-memory-workbench**](https://github.com/nicobailon/pi-memory-workbench): Separate compact status from structured detail
  - [index.ts:72-82](https://github.com/nicobailon/pi-memory-workbench/blob/92b4c9c3ad07841418d77118bf8bd02ad204f7c4/index.ts#L72-L82) @92b4c9c3
- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Separate compact status from structured detail
  - [index.ts:295-305](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/index.ts#L295-L305) @09937ed6

### Related

- [Widget Dock](https://pi-tui.ratstack.sh/patterns/widget-dock.md): provides the detail surface

### States

- Signal and detail (`active`): Show a short running status and a small task widget.
- Paired cleanup (`finished`): Clear both surfaces when the synthetic task ends.

## Widget Dock

`widget-dock` · Structural / Status and widgets · [Markdown](https://pi-tui.ratstack.sh/patterns/widget-dock.md) · [JSON](https://pi-tui.ratstack.sh/patterns/widget-dock.json) · also known as Keyed widget

### Intent

Mount and replace auxiliary content under a stable widget key.

### Motivation

Web Access and Powerline Footer install auxiliary content that must disappear when disabled.

### Applicability

- Use this when persistent content belongs above or below the editor.

### Structure

```text
owner -> widget key -> content
update -> replace content
end -> clear key
```

### Participants

- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- `Widget owner`: Keeps the keyed content and component reference in sync.

Pi screen APIs: [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `Text`

### Consequences

- Content can be replaced under one stable near-editor key.
- The key and local component reference must be cleared together.

### Implementation

- Clear the widget and its local component reference together.
- Fit rendered rows to current terminal dimensions.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Mount, update and remove keyed widgets
  - [extensions/command-center/index.ts:410-458](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/command-center/index.ts#L410-L458) @17cce138
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Mount, update and remove keyed widgets
  - [index.ts:3120-3185](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/index.ts#L3120-L3185) @859dee67
- [**pi-web-access**](https://github.com/nicobailon/pi-web-access): Mount, update and remove keyed widgets
  - [index.ts:641-641](https://github.com/nicobailon/pi-web-access/blob/9a0779976ba47350be18f8cfacaffbe2a407113e/index.ts#L641-L641) @9a077997
  - [index.ts:1278-1304](https://github.com/nicobailon/pi-web-access/blob/9a0779976ba47350be18f8cfacaffbe2a407113e/index.ts#L1278-L1304) @9a077997
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Show bounded background-session status below the editor
  - [background-widget.ts:24-106](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/background-widget.ts#L24-L106) @77df9a81

### Related

- [Result Relay](https://pi-tui.ratstack.sh/patterns/result-relay.md): replaces pending content with a result

### States

- Above editor (`above`): Mount a compact synthetic progress widget above the editor.
- Below editor (`below`): Show the same auxiliary content below the editor.
- Removed (`removed`): Clear its owned key without changing another widget.

## Editor Steward

`editor-steward` · Structural / Editors and drafts · [Markdown](https://pi-tui.ratstack.sh/patterns/editor-steward.md) · [JSON](https://pi-tui.ratstack.sh/patterns/editor-steward.json) · also known as Single-owner editor

### Intent

Compose editor enhancements behind one replacement factory.

### Motivation

Dot314's editor enhancements warn that competing setEditorComponent replacements cannot safely own the same editor.

### Applicability

- Use this when several features would otherwise compete to replace the main editor.

### Structure

```text
one factory -> CustomEditor
features -> shared input / render
```

### Participants

- [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Installs the single custom editor factory.
- [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Preserves application controls for editor replacements.
- `Composed features`: Share one editor factory rather than replacing each other.

Pi screen APIs: [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `Editor.setAutocompleteProvider`, `matchesKey`

### Consequences

- Several enhancements can preserve one base editor contract.
- They must cooperate under a single factory rather than install independently.

### Implementation

- Receive EditorTheme rather than the general Theme.
- Forward unowned keys to CustomEditor.
- Preserve autocomplete and restore the default factory when done.

### Known uses: seen in Nico's repos

- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Session-scoped editor component mount
  - [raw-paste/index.ts:95-104](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/raw-paste/index.ts#L95-L104) @bca5070b
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Editor replacement and chrome
  - [index.ts:3402-3425](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/index.ts#L3402-L3425) @859dee67
- [**dot314**](https://github.com/nicobailon/dot314): Compose editor enhancements behind one editor-component owner
  - [extensions/editor-enhancements/index.ts:5-11](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/editor-enhancements/index.ts#L5-L11) @17cce138
  - [extensions/editor-enhancements/index.ts:43-65](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/editor-enhancements/index.ts#L43-L65) @17cce138

### Related

- [Action Compass](https://pi-tui.ratstack.sh/patterns/action-compass.md): keeps base application controls

### States

- Base editor (`default`): Show an ordinary synthetic draft with autocomplete.
- Composed enhancements (`enhanced`): Show one owned editor with extra chrome and an explicit mode.
- Base controls retained (`forwarded`): Show an unowned key still using the base editor behavior.

## Elastic Overlay

`elastic-overlay` · Structural / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/elastic-overlay.md) · [JSON](https://pi-tui.ratstack.sh/patterns/elastic-overlay.json) · also known as Responsive overlay

### Intent

Resolve overlay size and placement from current terminal dimensions.

### Motivation

Nico's overlay positioning and recentering work prevents dialogs retaining stale mount-time coordinates after resize.

### Applicability

- Use this when a temporary view must remain positioned through resize.

### Structure

```text
terminal size + options -> bounds
bounds + component -> overlay
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`OverlayOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Defines overlay dimensions, placement and focus behavior.
- `Current terminal bounds`: Drive placement again after dimension changes.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), `OverlayHandle.getBounds`, `Box`, `DynamicBorder`

### Consequences

- Anchors, margins and percentages adapt a view to the current terminal.
- The component must still fit each line inside its supplied width.

### Implementation

- Do not cache mount-time coordinates.
- Overlay sizing does not excuse lines exceeding render(width).

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Configurable overlay sizing and placement
  - [packages/tui/src/tui.ts:71-104](https://github.com/earendil-works/pi/blob/0c0aac65990decf95ad5f49886ff5fccf1c09540/packages/tui/src/tui.ts#L71-L104) @0c0aac65
  - [packages/tui/src/tui.ts:87-120](https://github.com/earendil-works/pi/blob/a4ccff382c465fd789a318a981526b0b883630da/packages/tui/src/tui.ts#L87-L120) @a4ccff38
- [**earendil-works/pi**](https://github.com/earendil-works/pi): Recenter overlays after terminal resize
  - [packages/tui/src/tui.ts:262-280](https://github.com/earendil-works/pi/blob/c565fa9af8876b9f3db07d45ad99493fc4eb9d0b/packages/tui/src/tui.ts#L262-L280) @c565fa9a
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Interactive shell overlay lifecycle and terminal sizing
  - [overlay-component.ts:20-205](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L20-L205) @77df9a81
- [**dot314**](https://github.com/nicobailon/dot314): Configurable overlay sizing and placement
- [**pi-autoresearch**](https://github.com/nicobailon/pi-autoresearch): Configurable overlay sizing and placement
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Configurable overlay sizing and placement
- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Configurable overlay sizing and placement
- [**pi-mcp-adapter**](https://github.com/nicobailon/pi-mcp-adapter): Configurable overlay sizing and placement
- [**pi-memory-workbench**](https://github.com/nicobailon/pi-memory-workbench): Configurable overlay sizing and placement
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Configurable overlay sizing and placement
- [**pi-side-chat**](https://github.com/nicobailon/pi-side-chat): Configurable overlay sizing and placement
- [**pi-skill-palette**](https://github.com/nicobailon/pi-skill-palette): Configurable overlay sizing and placement
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Configurable overlay sizing and placement
- [**pi-tool-display**](https://github.com/nicobailon/pi-tool-display): Configurable overlay sizing and placement

### Related

- [Hinge Panel](https://pi-tui.ratstack.sh/patterns/hinge-panel.md): adapts content within those bounds

### States

- Centered view (`centered`): Show a synthetic centered dialog above host content.
- Recomputed bounds (`resized`): Resize the terminal and keep its bounds correctly centered.
- Anchored view (`anchored`): Show an edge-anchored panel with margins and responsive visibility.

## Match Ladder

`match-ladder` · Behavioral / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/match-ladder.md) · [JSON](https://pi-tui.ratstack.sh/patterns/match-ladder.json) · also known as Ranked picker

### Intent

Rank matching options across weighted label and description fields.

### Motivation

Subagents ranks matches across name, description and model rather than matching only one label.

### Applicability

- Use this when substring and ordered-character matches need different priorities.

### Structure

```text
query + fields -> scores
scores -> order -> selection
```

### Participants

- [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns single-line editing and cursor state.
- [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns a bounded selection list and selection callbacks.
- [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Resolves input against configurable action bindings.
- `Match scorer`: Weights fields and orders positive matches.

Pi component APIs: [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus)

### Consequences

- Field weights distinguish stronger matches from weaker ones.
- Ranking and stable selection require more logic than a plain filter.

### Implementation

- Subagents and skill-palette use different search algorithms.
- Keep the highlighted item stable where it remains in the results.

### Known uses: seen in Nico's repos

- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Rank selector choices by best matching field
  - [src/tui/render-helpers.ts:1-29](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/render-helpers.ts#L1-L29) @6826b054
  - [src/slash/selector.ts:85-117](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/slash/selector.ts#L85-L117) @6826b054

### Related

- [Shrinking Sieve](https://pi-tui.ratstack.sh/patterns/shrinking-sieve.md): narrows candidates without field ranking

### States

- Ranked matches (`query`): Show substring matches before weaker subsequence matches across synthetic fields.
- Keep selection (`navigation`): Move down without resetting selection to the first row.
- No matches (`empty`): Show an explicit empty-result row.

## Shrinking Sieve

`shrinking-sieve` · Behavioral / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/shrinking-sieve.md) · [JSON](https://pi-tui.ratstack.sh/patterns/shrinking-sieve.json) · also known as Incremental filter

### Intent

Narrow existing candidates while a search query grows.

### Motivation

Skill Palette filters its current candidates as a query grows but must rescan when that assumption stops holding.

### Applicability

- Use this when query extension can only remove candidates from a picker.

### Structure

```text
query extends -> narrow set
other edit -> full rescan
```

### Participants

- [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns single-line editing and cursor state.
- [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns a bounded selection list and selection callbacks.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Candidate set`: Narrows on query extension and resets for other edits.

Pi component APIs: [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model)

### Consequences

- Query extension reuses the narrowed candidate set.
- Backspace and other non-extension edits require a full rescan.

### Implementation

- Rescan all candidates when an edit does not extend the previous query.
- Reset selection on query changes rather than navigation.

### Known uses: seen in Nico's repos

- [**pi-skill-palette**](https://github.com/nicobailon/pi-skill-palette): Narrow palette results as the query grows
  - [index.ts:622-741](https://github.com/nicobailon/pi-skill-palette/blob/a5c4429b8c2e33ab903d07856497014f3d5ad34e/index.ts#L622-L741) @a5c4429b

### Related

- [Match Ladder](https://pi-tui.ratstack.sh/patterns/match-ladder.md): ranks rather than only narrowing

### States

- All candidates (`initial`): Show the full synthetic command list.
- Narrowed query (`extend`): Append query characters and narrow the current candidates.
- Rescan (`backspace`): Shorten the query and restore candidates from the full list.

## Identity Anchor

`identity-anchor` · Behavioral / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/identity-anchor.md) · [JSON](https://pi-tui.ratstack.sh/patterns/identity-anchor.json) · also known as Stable async selection

### Intent

Retain the highlighted item's identity as asynchronous results arrive.

### Motivation

Intercom's handover picker receives remote listings after local choices are already visible.

### Applicability

- Use this when a picker combines immediately available and remote options.

### Structure

```text
local + remote results
        -> merge by identity
        -> keep highlight
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- `Item identity`: Anchors the highlight through asynchronous insertions.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `SelectList`

### Consequences

- New arrivals do not move the highlight to a different item.
- Pending, failed and late responses need explicit handling.

### Implementation

- Ignore responses after the view closes.
- Do not present pending requests as completed entries.

### Known uses: seen in Nico's repos

- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Separate task-field focus and retain remote selection through async refresh
  - [ui/handover-picker.ts:61-180](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/handover-picker.ts#L61-L180) @a5fad4df
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Keep fleet selection stable while details are derived
  - [src/tui/fleet.ts:1-120](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet.ts#L1-L120) @6826b054
  - [src/tui/fleet.ts:1360-1463](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet.ts#L1360-L1463) @6826b054

### Related

- [Field Baton](https://pi-tui.ratstack.sh/patterns/field-baton.md): separates target and task input

### States

- Loading options (`loading`): Show local items and a visibly pending remote section.
- Selection retained (`arrived`): Insert remote results while preserving the highlighted item.
- Remote error (`error`): Show a settled error section while keeping local choices usable.

## Preview Basket

`preview-basket` · Behavioral / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/preview-basket.md) · [JSON](https://pi-tui.ratstack.sh/patterns/preview-basket.json) · also known as Multi-select preview

### Intent

Keep selection separate from thumbnail loading and zoom inspection.

### Motivation

Dot314's screenshot picker needs staged selections alongside thumbnail and zoom inspection.

### Applicability

- Use this when a picker stages several images before confirmation.

### Structure

```text
selected set -> staged widget
current item -> thumbnail / zoom
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Image`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#image): Renders an inline image or unsupported-terminal placeholder.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- `Staged set`: Retains selected items independently of zoom inspection.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Image`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#image), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `Component`, `Text`

### Consequences

- Inspection can change without changing the selected set.
- Loading, missing sources and staged-widget cleanup remain separate work.

### Implementation

- Handle unavailable images without changing selection.
- Clear the staged preview widget when dismissed.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Select and preview multiple screenshots in a custom picker
  - [extensions/screenshots-picker/index.ts:800-830](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/screenshots-picker/index.ts#L800-L830) @17cce138

### Related

- [Image Parachute](https://pi-tui.ratstack.sh/patterns/image-parachute.md): handles preview capability limits

### States

- Staged images (`selected`): Mark two synthetic images and show their staged previews.
- Zoom inspection (`zoom`): Inspect one image without changing the staged set.
- Unavailable image (`missing`): Show a text placeholder for an unavailable source.

## Warning Gate

`warning-gate` · Behavioral / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/warning-gate.md) · [JSON](https://pi-tui.ratstack.sh/patterns/warning-gate.json) · also known as Consequence confirmation

### Intent

Follow a consequential selection with a separate warning confirmation.

### Motivation

Dot314's tool-horizon selector adds a separate warning for broad restore choices.

### Applicability

- Use this when a broad restore or destructive choice needs an explicit warning.

### Structure

```text
choose boundary -> broad?
broad -> warning -> confirm
cancel -> no action
```

### Participants

- [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Returns a selected string or cancellation.
- [`ctx.ui.confirm`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Returns a confirmation decision.
- `Warning policy`: Chooses which consequential actions require another prompt.

Pi screen APIs: [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.confirm`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `ctx.ui.setWidget`, `ctx.ui.setStatus`

### Consequences

- A consequential choice gets a distinct confirmation step.
- The extra prompt and its thresholds are workflow-specific.

### Implementation

- The source's restore thresholds are not universal policy.
- A cancelled confirmation must not trigger the selected action.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Select restore boundaries with explicit warnings
  - [extensions/tool-horizon/index.ts:146-170](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/tool-horizon/index.ts#L146-L170) @17cce138
  - [extensions/tool-horizon/index.ts:40-90](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/tool-horizon/index.ts#L40-L90) @17cce138
  - [extensions/tool-horizon/boundary-picker.ts:460-500](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/tool-horizon/boundary-picker.ts#L460-L500) @17cce138

### Related

- [Action Sieve](https://pi-tui.ratstack.sh/patterns/action-sieve.md): filters choices before confirmation

### States

- Choose boundary (`select`): Show narrow and broad synthetic restore choices.
- Broad action warning (`warning`): Show a warning after the broad choice.
- No action (`cancelled`): Show an unchanged host after declining the warning.

## Abort Lantern

`abort-lantern` · Behavioral / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/abort-lantern.md) · [JSON](https://pi-tui.ratstack.sh/patterns/abort-lantern.json) · also known as Cancellable loader

### Intent

Settle foreground loading before opening a separate result view.

### Motivation

Dot314's session-ask operation must settle its foreground loader before showing analysis results.

### Applicability

- Use this when a long operation needs a visible cancel control.

### Structure

```text
work -> bordered loader
escape -> cancel -> done(null)
success -> done -> result view
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`BorderedLoader`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Presents cancellable foreground loading inside a border.
- `Owning operation`: Settles work before constructing a separate result view.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`BorderedLoader`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), `Container`, `ctx.ui.setWidget`

### Consequences

- Users can cancel long foreground work.
- Success, failure and null cancellation need separate settlement paths.

### Implementation

- Resolve success, failure and cancellation.
- Check the cancellation result before building the result view.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Separate cancellable loading from results
  - [extensions/session-ask/index.ts:1645-1665](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/session-ask/index.ts#L1645-L1665) @17cce138
  - [extensions/extension-stats.ts:1110-1152](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/extension-stats.ts#L1110-L1152) @17cce138

### Related

- [Abort Tether](https://pi-tui.ratstack.sh/patterns/abort-tether.md): cancels a prompt instead of a loader

### States

- Loading (`loading`): Show the animated loader and its cancel hint.
- Result view (`success`): Replace the completed loader with synthetic results.
- Cancelled (`cancelled`): Return to the host without opening results.
- Failure (`failed`): Settle loading and show a concise error.

## Dialog Fuse

`dialog-fuse` · Behavioral / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/dialog-fuse.md) · [JSON](https://pi-tui.ratstack.sh/patterns/dialog-fuse.json) · also known as Countdown dialog

### Intent

Show remaining time before a transient dialog automatically dismisses.

### Motivation

Nico's upstream dialog timeout adds a visible countdown before automatic dismissal.

### Applicability

- Use this when a prompt should expire without user input.

### Structure

```text
dialog + timeout -> countdown
zero -> cancellation result
```

### Participants

- [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Returns a selected string or cancellation.
- [`ExtensionUIDialogOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Carries a timeout or abort signal for a built-in dialog.
- `Deadline`: Ends the prompt with a cancellation result when time runs out.

Pi screen APIs: [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ExtensionUIDialogOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `ctx.ui.setWidget`

### Consequences

- Transient prompts can end without user input.
- Callers must treat timeout as a cancellation result.

### Implementation

- Handle the timeout result as cancellation.
- Clear timer resources when the view closes.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Auto-dismiss extension dialogs with a live countdown
  - [packages/coding-agent/src/core/extensions/types.ts:45-60](https://github.com/earendil-works/pi/blob/77477f6166be8e0eb1ca2f7ab9fc3c271fde6586/packages/coding-agent/src/core/extensions/types.ts#L45-L60) @77477f61
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Timed dismissible welcome overlay
  - [index.ts:3584-3648](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/index.ts#L3584-L3648) @859dee67

### Related

- [Idle Fuse](https://pi-tui.ratstack.sh/patterns/idle-fuse.md): expires from inactivity instead

### States

- Countdown starts (`initial`): Show a synthetic selection prompt with its timeout countdown.
- Time remaining (`ticking`): Advance the visible countdown without a selection.
- Timeout result (`expired`): Show the host receiving an undefined selection after expiry.

## Abort Tether

`abort-tether` · Behavioral / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/abort-tether.md) · [JSON](https://pi-tui.ratstack.sh/patterns/abort-tether.json) · also known as Abortable dialog

### Intent

Tie a built-in dialog's lifetime to an operation's abort signal.

### Motivation

Nico's upstream dialogs can outlive the task that requested them without a cancellation signal.

### Applicability

- Use this when the owning task can end before a user answers.

### Structure

```text
owner signal -> dialog
abort -> cancellation result
```

### Participants

- [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Returns a selected string or cancellation.
- [`ctx.ui.confirm`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Returns a confirmation decision.
- [`ExtensionUIDialogOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Carries a timeout or abort signal for a built-in dialog.
- `Abort signal`: Ties the pending prompt to its owning operation.

Pi screen APIs: [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.confirm`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ExtensionUIDialogOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user)

### Consequences

- The owning task can dismiss its pending prompt.
- Already-aborted and later-aborted signals both need cancellation handling.

### Implementation

- Handle an already-aborted signal.
- Do not leave the dialog promise pending on abort.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Cancel extension dialogs with AbortSignal
  - [packages/coding-agent/src/core/extensions/types.ts:50-64](https://github.com/earendil-works/pi/blob/9771fa1e447ca5f1d564bf49d2dbef2c7f79e334/packages/coding-agent/src/core/extensions/types.ts#L50-L64) @9771fa1e

### Related

- [Abort Lantern](https://pi-tui.ratstack.sh/patterns/abort-lantern.md): owns cancellation during work

### States

- Pending prompt (`open`): Show a prompt while its synthetic owner is active.
- Owner cancelled (`aborted`): Cancel the owner and show the dialog's cancellation result.
- No pending prompt (`already-aborted`): Pass an already-aborted signal and show immediate cancellation.

## Idle Fuse

`idle-fuse` · Behavioral / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/idle-fuse.md) · [JSON](https://pi-tui.ratstack.sh/patterns/idle-fuse.json) · also known as Idle dismissal

### Intent

Reset a component-owned inactivity timer after every input.

### Motivation

Skill Palette resets a 60-second timer on input and cancels the picker when the user stops interacting.

### Applicability

- Use this when an unattended picker should eventually close.

### Structure

```text
input -> reset idle timer
idle deadline -> done(cancel)
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Idle timer`: Resets on input and cancels the abandoned interaction.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `SelectList`, `Box`

### Consequences

- An abandoned picker does not remain open indefinitely.
- Every input and completion path must manage the owned timer.

### Implementation

- Clear the timer on completion and disposal.
- Do not confuse idle timeout with a fixed countdown.

### Known uses: seen in Nico's repos

- [**pi-skill-palette**](https://github.com/nicobailon/pi-skill-palette): Dismiss an idle palette and clean its timer
  - [index.ts:625-660](https://github.com/nicobailon/pi-skill-palette/blob/a5c4429b8c2e33ab903d07856497014f3d5ad34e/index.ts#L625-L660) @a5c4429b
  - [index.ts:829-842](https://github.com/nicobailon/pi-skill-palette/blob/a5c4429b8c2e33ab903d07856497014f3d5ad34e/index.ts#L829-L842) @a5c4429b

### Related

- [Dialog Fuse](https://pi-tui.ratstack.sh/patterns/dialog-fuse.md): uses a fixed expiry deadline

### States

- Idle timer (`waiting`): Show a synthetic palette and remaining idle time.
- Timer reset (`input`): Navigate once and reset the visible idle timer.
- Idle cancellation (`expired`): Close the palette after the next uninterrupted idle interval.

## Settings Bench

`settings-bench` · Behavioral / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/settings-bench.md) · [JSON](https://pi-tui.ratstack.sh/patterns/settings-bench.json) · also known as Settings panel

### Intent

Keep configuration interaction separate from the live activity view.

### Motivation

Messenger separates configuration from activity, while Subagents asks dependent admin choices.

### Applicability

- Use this when several settings need dependent choices or value cycling.

### Structure

```text
entity -> setting choices
choice -> save / reload boundary
```

### Participants

- [`SettingsList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns settings values, cycling and submenus.
- [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Returns a selected string or cancellation.
- [`ctx.ui.editor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Opens a multi-line text-editing dialog.
- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- `Settings controller`: Keeps displayed choices separate from persistence and reload.

Pi screen APIs: [`SettingsList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`ctx.ui.select`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.editor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), `SelectList`, `ctx.ui.setWidget`

### Consequences

- Configuration changes do not complicate the live activity view.
- Displayed choices, persisted writes and reload-only changes differ.

### Implementation

- Distinguish a displayed choice from a persisted write.
- Some tool ownership changes require reload rather than a live UI update.

### Known uses: seen in Nico's repos

- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Interactive configuration overlay
  - [config-overlay.ts:1-45](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/config-overlay.ts#L1-L45) @09937ed6
- [**pi-tool-display**](https://github.com/nicobailon/pi-tool-display): Tool display: responsive settings modal
  - [src/config-modal.ts:398-455](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/config-modal.ts#L398-L455) @fca8c858
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Layer admin choices as sequential selector steps
  - [src/slash/subagents-admin.ts:170-459](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/slash/subagents-admin.ts#L170-L459) @6826b054

### Related

- [Action Sieve](https://pi-tui.ratstack.sh/patterns/action-sieve.md): derives currently available choices

### States

- Settings list (`settings`): Show synthetic configurable values apart from the activity dashboard.
- Dependent choice (`dependent`): Choose an entity before showing its model or scope options.
- Saved selection (`saved`): Return to the dashboard with an explicit saved-setting notice.

## Action Sieve

`action-sieve` · Behavioral / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/action-sieve.md) · [JSON](https://pi-tui.ratstack.sh/patterns/action-sieve.json) · also known as Conditional actions

### Intent

Derive dialog choices from current control and completion state.

### Motivation

Interactive Shell's dialog omits return-to-agent unless the control state permits it.

### Applicability

- Use this when only some actions are valid for the selected job.

### Structure

```text
execution + control state
        -> valid choices
choice -> action
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns a bounded selection list and selection callbacks.
- `Control snapshot`: Determines which actions are currently applicable.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`SelectList`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), `Text`

### Consequences

- The chooser exposes only applicable actions.
- Choices must be rebuilt when control or execution state changes.

### Implementation

- Omit unavailable actions instead of allowing an invalid transition.
- Keep dialog choice separate from job execution state.

### Known uses: seen in Nico's repos

- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Represent completion choices as an explicit dialog state
  - [overlay-component.ts:600-730](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L600-L730) @77df9a81

### Related

- [Control Baton](https://pi-tui.ratstack.sh/patterns/control-baton.md): changes control ownership

### States

- Running job (`running`): Show takeover and background choices for a live synthetic job.
- Human control (`human`): Show return-to-agent only while the human owns control.
- Exited job (`exited`): Replace live controls with applicable completion choices.

## Retry Buffer

`retry-buffer` · Behavioral / Editors and drafts · [Markdown](https://pi-tui.ratstack.sh/patterns/retry-buffer.md) · [JSON](https://pi-tui.ratstack.sh/patterns/retry-buffer.json) · also known as Retryable draft

### Intent

Keep the local draft editable after a failed send.

### Motivation

Intercom's compose overlay can fail to send after the user has already typed a message.

### Applicability

- Use this when a compact compose view submits asynchronously.

### Structure

```text
draft -> sending
failure -> draft + error
retry -> sending
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns single-line editing and cursor state.
- [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Resolves input against configurable action bindings.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Send state`: Prevents duplicate submission while retaining text on failure.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `Focusable`

### Consequences

- A failed send leaves the draft available for retry.
- Blank text, duplicate sends and terminal escape sequences need separate guards.

### Implementation

- Reject blank drafts and duplicate sends.
- Ignore raw escape sequences as text input.

### Known uses: seen in Nico's repos

- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Compose with explicit send state and inline failure recovery
  - [ui/compose.ts:18-91](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/compose.ts#L18-L91) @a5fad4df

### Related

- [Draft Return](https://pi-tui.ratstack.sh/patterns/draft-return.md): restores text after temporary submission

### States

- Draft (`editing`): Show a synthetic compose draft.
- Send pending (`sending`): Disable duplicate submission while the send is pending.
- Retry available (`failed`): Show an inline error with the unchanged draft ready to retry.

## Draft Fence

`draft-fence` · Behavioral / Editors and drafts · [Markdown](https://pi-tui.ratstack.sh/patterns/draft-fence.md) · [JSON](https://pi-tui.ratstack.sh/patterns/draft-fence.json) · also known as Staged draft

### Intent

Keep inline draft edits separate from the committed queue snapshot.

### Motivation

Dot314's queue-steer timeline must show committed queue data while inline edits remain drafts.

### Applicability

- Use this when a timeline includes editable work that has not been committed.

### Structure

```text
queue snapshot -> timeline
draft -> editor
commit -> new snapshot
```

### Participants

- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Installs the single custom editor factory.
- [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Preserves application controls for editor replacements.
- `Queue snapshot`: Remains committed state while local drafts are edited.

Pi screen APIs: [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `setWidget`, `setEditorComponent`, `matchesKey`

### Consequences

- Uncommitted edits do not impersonate persisted queue values.
- Draft state and queue state require separate commit and cleanup paths.

### Implementation

- Do not render an unsaved draft as persisted queue state.
- Clear the queue widget when its queue becomes empty.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Compose queue drafts and timeline widgets
  - [extensions/pi-queue-steer/index.ts:268-304](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-queue-steer/index.ts#L268-L304) @17cce138
  - [extensions/pi-queue-steer/index.ts:525-545](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-queue-steer/index.ts#L525-L545) @17cce138

### Related

- [Retry Buffer](https://pi-tui.ratstack.sh/patterns/retry-buffer.md): keeps unsubmitted text local

### States

- Queue snapshot (`committed`): Show a synthetic queued item in the timeline.
- Uncommitted edit (`draft`): Edit its draft while the timeline still shows the committed value.
- Edit committed (`committed-edit`): Apply the draft and update the timeline snapshot.

## Prompt Trail

`prompt-trail` · Behavioral / Editors and drafts · [Markdown](https://pi-tui.ratstack.sh/patterns/prompt-trail.md) · [JSON](https://pi-tui.ratstack.sh/patterns/prompt-trail.json) · also known as Prompt history

### Intent

Browse submitted prompts while retaining the current editor draft.

### Motivation

Nico's editor history lets users revisit prompts without losing the current draft.

### Applicability

- Use this when previous prompts should be reusable inside the editor.

### Structure

```text
draft -> previous prompts
next through history -> draft
```

### Participants

- [`Editor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns multi-line editing, autocomplete and history.
- [`Editor.addToHistory`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/keybindings.md#cursor-movement): Adds submitted text to editor history.
- `Current draft`: Returns when forward history browsing ends.

Pi component APIs: [`Editor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`Editor.addToHistory`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/keybindings.md#cursor-movement), `Focusable`

### Consequences

- Earlier submissions remain reachable inside the editor.
- Arrow history depends on cursor boundaries unless dedicated actions are used.

### Implementation

- Boundary arrow navigation differs from interior cursor movement.
- Dedicated history actions do not require a cursor boundary.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Navigate submitted prompts from the editor
  - [packages/tui/src/components/editor.ts:51-116](https://github.com/earendil-works/pi/blob/c550ed2bcab8db29fd70e2096a390cf80d69cd91/packages/tui/src/components/editor.ts#L51-L116) @c550ed2b
  - [packages/tui/src/components/editor.ts:451-465](https://github.com/earendil-works/pi/blob/c550ed2bcab8db29fd70e2096a390cf80d69cd91/packages/tui/src/components/editor.ts#L451-L465) @c550ed2b

### Related

- [Editor Steward](https://pi-tui.ratstack.sh/patterns/editor-steward.md): preserves the base editor history

### States

- Current draft (`draft`): Show a synthetic draft with two saved prompts.
- Previous prompt (`previous`): Navigate history at an editor boundary.
- Draft retained (`returned`): Browse forward until the current draft returns.

## Action Compass

`action-compass` · Behavioral / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/action-compass.md) · [JSON](https://pi-tui.ratstack.sh/patterns/action-compass.json) · also known as Semantic key controls

### Intent

Resolve configurable actions through injected keybindings.

### Motivation

The Subagents fleet view receives keybindings so selection actions need not rely on literal arrow keys.

### Applicability

- Use this when picker controls should follow the user's bindings.

### Structure

```text
input -> keybinding action
action -> state + matching hints
```

### Participants

- [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Resolves input against configurable action bindings.
- [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Recognizes explicit terminal key combinations.
- [`pi.registerShortcut`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#choose-an-integration-point): Registers a dedicated extension shortcut.
- `Action hints`: Describe configured controls rather than stale literal keys.

Pi component APIs: [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), [`pi.registerShortcut`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#choose-an-integration-point), `KeybindingsManager.getKeys`, `SelectList`

### Consequences

- Configurable actions follow the user's keybindings.
- Dedicated extension shortcuts still need conflict checks.

### Implementation

- Check registered shortcut conflicts.
- Keep explicit extension shortcuts distinct from configurable selection actions.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Bind a mode action to a dedicated shortcut
  - [extensions/reverse-thinking.ts:1-19](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/reverse-thinking.ts#L1-L19) @17cce138
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Keep fleet selection stable while details are derived
  - [src/tui/fleet.ts:1-120](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet.ts#L1-L120) @6826b054
  - [src/tui/fleet.ts:1360-1463](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet.ts#L1360-L1463) @6826b054
- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Compose with explicit send state and inline failure recovery
  - [ui/compose.ts:18-91](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/compose.ts#L18-L91) @a5fad4df

### Related

- [Input Lease](https://pi-tui.ratstack.sh/patterns/input-lease.md): intercepts raw input instead

### States

- Default bindings (`default`): Show navigation and action hints using ordinary bindings.
- Remapped action (`remapped`): Use a synthetic remapping for selection and show matching hints.
- Dedicated action (`shortcut`): Toggle a small mode with an explicitly registered shortcut.

## Input Switch

`input-switch` · Behavioral / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/input-switch.md) · [JSON](https://pi-tui.ratstack.sh/patterns/input-switch.json) · also known as Submitted input interception

### Intent

Continue, transform or handle submitted input before normal processing.

### Motivation

Nico's upstream input event supports aliases and rewriting without replacing the editor.

### Applicability

- Use this when an alias or input rewrite should not replace the editor.

### Structure

```text
submission -> input handlers
  continue / transform / handled
```

### Participants

- [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events): Registers ordered extension event handlers.
- [`InputEventResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events): Declares continue, transform or handled input outcomes.
- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- `Input handlers`: Continue, transform or consume the submitted input in order.

Pi screen APIs: [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events), [`InputEventResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events), [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `CustomEditor`, `setWidget`, `setStatus`

### Consequences

- Submitted text can continue, transform or be handled locally.
- Handler ordering affects what later extensions receive.

### Implementation

- Handlers run in extension registration order.
- A handled result stops normal processing.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Allow extensions to intercept submitted input
  - [packages/coding-agent/src/core/extensions/types.ts:460-505](https://github.com/earendil-works/pi/blob/3e5d91f28775b2f2df21ef3f0ec3bd799610a447/packages/coding-agent/src/core/extensions/types.ts#L460-L505) @3e5d91f2
- [**dot314**](https://github.com/nicobailon/dot314): Allow extensions to intercept submitted input
- [**pi-boomerang**](https://github.com/nicobailon/pi-boomerang): Allow extensions to intercept submitted input
- [**pi-mcp-adapter**](https://github.com/nicobailon/pi-mcp-adapter): Allow extensions to intercept submitted input
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Allow extensions to intercept submitted input
- [**pi-review-loop**](https://github.com/nicobailon/pi-review-loop): Allow extensions to intercept submitted input
- [**pi-skill-palette**](https://github.com/nicobailon/pi-skill-palette): Allow extensions to intercept submitted input
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Allow extensions to intercept submitted input

### Related

- [Editor Steward](https://pi-tui.ratstack.sh/patterns/editor-steward.md): changes the editor rather than submission

### States

- Normal input (`continue`): Show the original synthetic submission reaching the host.
- Rewritten input (`transform`): Show an alias expanded into visible submitted text.
- Handled locally (`handled`): Consume an input and show a keyed status instead of starting a model turn.

## Focus Baton

`focus-baton` · Behavioral / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/focus-baton.md) · [JSON](https://pi-tui.ratstack.sh/patterns/focus-baton.json) · also known as Overlay focus handoff

### Intent

Move keyboard ownership without closing a persistent overlay.

### Motivation

Side Chat keeps a secondary overlay mounted while its shortcut changes keyboard ownership.

### Applicability

- Use this when a secondary view and the host editor need alternating input.

### Structure

```text
visible overlay -> focus()
editor <- unfocus(target)
interaction end -> done
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`OverlayHandle.focus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Gives the overlay keyboard ownership.
- [`OverlayHandle.unfocus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Releases input to fallback or a specified target.
- [`OverlayHandle.isFocused`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Reports the overlay's current input ownership.
- [`OverlayHandle.setHidden`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Temporarily changes visibility without completion.
- `Focus target`: Receives keyboard input while the overlay remains mounted.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayHandle.focus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayHandle.unfocus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayHandle.isFocused`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayHandle.setHidden`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), `custom`, `Input`, `Focusable`, `Box`, `DynamicBorder`

### Consequences

- The secondary view stays visible during focus handoff.
- Visibility and input ownership need separate handle operations.

### Implementation

- Visibility does not imply keyboard ownership.
- Finish custom UI through done rather than permanent handle removal.

### Known uses: seen in Nico's repos

- [**pi-side-chat**](https://github.com/nicobailon/pi-side-chat): Focus handoff for a persistent side-chat overlay
  - [index.ts:37-76](https://github.com/nicobailon/pi-side-chat/blob/9d59cc042634111c1ed633b7bacb9f74611aebbd/index.ts#L37-L76) @9d59cc04
  - [index.ts:138-218](https://github.com/nicobailon/pi-side-chat/blob/9d59cc042634111c1ed633b7bacb9f74611aebbd/index.ts#L138-L218) @9d59cc04
- [**earendil-works/pi**](https://github.com/earendil-works/pi): Harden overlay focus restoration after temporary UI changes
  - [packages/tui/src/tui.ts:1-100](https://github.com/earendil-works/pi/blob/91a2f8660099f41e7c8c71dbd3ae52e2ab3e81f5/packages/tui/src/tui.ts#L1-L100) @91a2f866

### Related

- [Ghost Overlay](https://pi-tui.ratstack.sh/patterns/ghost-overlay.md): starts without taking focus

### States

- Overlay focused (`overlay`): Show synthetic secondary content with its focus indicator.
- Editor focused (`editor`): Release the overlay to the editor while keeping it visible.
- Focus restored (`returned`): Take focus back after a temporary prompt.
- Temporarily hidden (`hidden`): Hide and restore through setHidden without settling the interaction.

## Ghost Overlay

`ghost-overlay` · Behavioral / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/ghost-overlay.md) · [JSON](https://pi-tui.ratstack.sh/patterns/ghost-overlay.json) · also known as Passive overlay

### Intent

Keep an overlay visible without automatically taking keyboard focus.

### Motivation

Nico's upstream non-capturing overlay work lets visible auxiliary content leave keyboard input with its underlying component.

### Applicability

- Use this when auxiliary content should not interrupt the underlying editor.

### Structure

```text
nonCapturing overlay -> visible
keyboard -> base component
focus() -> overlay
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`OverlayOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Defines overlay dimensions, placement and focus behavior.
- [`OverlayHandle.focus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Gives the overlay keyboard ownership.
- [`OverlayHandle.unfocus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Releases input to fallback or a specified target.
- `Underlying editor`: Keeps input until the overlay explicitly takes focus.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayOptions`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayHandle.focus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`OverlayHandle.unfocus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), `custom`, `setStatus`, `setWidget`, `Box`, `DynamicBorder`, `OverlayOptions.nonCapturing`, `CustomEditor`

### Consequences

- A passive view does not interrupt editor input.
- Interaction requires an explicit later focus transfer.

### Implementation

- Use nonCapturing explicitly.
- Request focus only when the auxiliary view needs interaction.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Non-capturing overlays with explicit focus control
  - [packages/tui/src/tui.ts:114-170](https://github.com/earendil-works/pi/blob/841c95ac9c7372b8f578c86d053d3b03b9ce2f20/packages/tui/src/tui.ts#L114-L170) @841c95ac
  - [packages/tui/src/tui.ts:186-228](https://github.com/earendil-works/pi/blob/735ccbd00ff6ce091dd6505f2d07c265b78a9092/packages/tui/src/tui.ts#L186-L228) @735ccbd0
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Non-capturing overlays with explicit focus control
- [**pi-side-chat**](https://github.com/nicobailon/pi-side-chat): Non-capturing overlays with explicit focus control

### Related

- [Focus Baton](https://pi-tui.ratstack.sh/patterns/focus-baton.md): transfers input after mounting

### States

- Editor keeps focus (`passive`): Show a passive synthetic panel while typing in the host editor.
- Explicit focus (`active`): Focus the same panel for one interaction.
- Focus released (`released`): Release input ownership while leaving the panel visible.

## Field Baton

`field-baton` · Behavioral / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/field-baton.md) · [JSON](https://pi-tui.ratstack.sh/patterns/field-baton.json) · also known as Field focus

### Intent

Switch keyboard focus between a choice list and an editable task field.

### Motivation

Intercom's handover picker combines target navigation with an optional editable task field.

### Applicability

- Use this when a picker combines a target and an optional text prompt.

### Structure

```text
Tab -> list <-> task Input
focused Input -> CURSOR_MARKER
```

### Participants

- [`Focusable`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Carries focused state to the cursor-owning component.
- [`CURSOR_MARKER`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Marks the hardware cursor position for IME placement.
- [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Owns single-line editing and cursor state.
- [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Recognizes explicit terminal key combinations.
- `Focus selector`: Routes list keys and task typing to their respective owners.

Pi component APIs: [`Focusable`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), [`CURSOR_MARKER`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), [`Input`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `SelectList`

### Consequences

- List movement and task typing remain separate interactions.
- The wrapper must propagate child focus for correct cursor positioning.

### Implementation

- Forward focused state to the child text input for cursor positioning.
- Keep list navigation separate from task text editing.

### Known uses: seen in Nico's repos

- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Separate task-field focus and retain remote selection through async refresh
  - [ui/handover-picker.ts:61-180](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/handover-picker.ts#L61-L180) @a5fad4df

### Related

- [Identity Anchor](https://pi-tui.ratstack.sh/patterns/identity-anchor.md): keeps target identity through refresh

### States

- List owns keys (`list`): Show the target list highlighted and the task field inactive.
- Task owns keys (`task`): Switch with Tab and show the text cursor in the task field.
- List focus returns (`returned`): Switch back without losing the typed draft.

## Control Baton

`control-baton` · Behavioral / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/control-baton.md) · [JSON](https://pi-tui.ratstack.sh/patterns/control-baton.json) · also known as Human takeover

### Intent

Pause automatic output updates while the user controls an interactive job.

### Motivation

Interactive Shell must stop automatic output updates when a human takes over its live terminal.

### Applicability

- Use this when long-running terminal work alternates between agent and human control.

### Structure

```text
agent updates -> flush
flush -> human control
return -> agent updates
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Control state`: Separates automatic updates from human terminal control.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `custom`, `Box`, `DynamicBorder`, `matchesKey`

### Consequences

- Agent and human control can alternate without closing the view.
- Takeover must flush pending output and respect update budgets.

### Implementation

- Flush pending output before takeover.
- Bound per-update and total output budgets.

### Known uses: seen in Nico's repos

- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Separate hands-free updates from user takeover
  - [overlay-component.ts:300-445](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L300-L445) @77df9a81

### Related

- [Action Sieve](https://pi-tui.ratstack.sh/patterns/action-sieve.md): offers only valid handoff actions

### States

- Automatic updates (`automatic`): Show a synthetic job with bounded agent progress updates.
- Human control (`takeover`): Flush pending output and show that automatic updates have stopped.
- Agent control (`returned`): Return control and resume bounded automatic updates.

## Pause Latch

`pause-latch` · Behavioral / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/pause-latch.md) · [JSON](https://pi-tui.ratstack.sh/patterns/pause-latch.json) · also known as Pause controls

### Intent

Keep pause, resume and quit controls inside the component input contract.

### Motivation

Nico's arcade components need to pause local activity without losing quit and resume controls.

### Applicability

- Use this when a live view must stop changing while remaining interactive.

### Structure

```text
running -> pause -> paused
paused -> resume -> running
any state -> quit
```

### Participants

- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Recognizes explicit terminal key combinations.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Pause state`: Stops local change without disabling resume and quit.

Pi component APIs: [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`matchesKey`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model)

### Consequences

- A live view can stop changing while remaining interactive.
- Input remains active and the cited games do not demonstrate release events.

### Implementation

- Expose control hints in the rendered view.
- The cited games do not demonstrate key-release handling.

### Known uses: seen in Nico's repos

- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Arcade: input handling and pause state
  - [arcade/tetris.ts:395-470](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/tetris.ts#L395-L470) @bca5070b
  - [arcade/ping.ts:349-426](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/ping.ts#L349-L426) @bca5070b
  - [arcade/picman.ts:248-263](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/picman.ts#L248-L263) @bca5070b
  - [arcade/spice-invaders.ts:834-910](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/spice-invaders.ts#L834-L910) @bca5070b
  - [arcade/badlogic-game/badlogic-game.ts:172-200](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/badlogic-game/badlogic-game.ts#L172-L200) @bca5070b

### Related

- [Tick Heart](https://pi-tui.ratstack.sh/patterns/tick-heart.md): advances the live state

### States

- Running view (`running`): Show a synthetic moving progress marker with pause and quit hints.
- Paused view (`paused`): Stop its movement while still accepting resume and quit.
- Resumed view (`resumed`): Resume movement without recreating the interaction.

## Tail Anchor

`tail-anchor` · Behavioral / Rendering and performance · [Markdown](https://pi-tui.ratstack.sh/patterns/tail-anchor.md) · [JSON](https://pi-tui.ratstack.sh/patterns/tail-anchor.json) · also known as Follow-tail view

### Intent

Follow new output only while the user remains at the bottom.

### Motivation

Interactive Shell's reattachment view follows the bottom only when the user has not scrolled up.

### Applicability

- Use this when a live output view must also permit reading earlier lines.

### Structure

```text
new output + at bottom -> follow
new output + scrolled up -> retain
return to bottom -> follow
```

### Participants

- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Resolves input against configurable action bindings.
- `Scroll position`: Decides whether appended output moves the viewport.

Pi component APIs: [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`KeybindingsManager.matches`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `matchesKey`

### Consequences

- Streaming output and manual history reading can coexist.
- The view must track whether tail-following is currently active.

### Implementation

- Preserve manual scroll position while output grows.
- Recompute terminal sizing on reattachment or resize.

### Known uses: seen in Nico's repos

- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Reattach overlay supports bounded selection and deferred refresh
  - [reattach-overlay.ts:18-115](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L18-L115) @77df9a81
  - [reattach-overlay.ts:311-448](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L311-L448) @77df9a81

### Related

- [Render Funnel](https://pi-tui.ratstack.sh/patterns/render-funnel.md): schedules redraws for appended output

### States

- Following output (`following`): Show synthetic appended lines with the viewport at the tail.
- Reading history (`scrolled`): Scroll up and retain that viewport while new lines arrive.
- Follow resumed (`resumed`): Return to the bottom and resume following appended output.

## Event Relay

`event-relay` · Behavioral / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/event-relay.md) · [JSON](https://pi-tui.ratstack.sh/patterns/event-relay.json) · also known as Event-driven view

### Intent

Update visible UI from named extension event channels.

### Motivation

Nico's extension bus coordinates tools and hooks through named channels used by progress UI.

### Applicability

- Use this when tools and hooks coordinate without direct module imports.

### Structure

```text
publisher -> named channel
channel -> validate -> visible state
```

### Participants

- [`pi.events`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#choose-an-integration-point): Exposes the shared extension event bus.
- [`EventBus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#choose-an-integration-point): Publishes unknown payloads on named channels.
- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Payload validator`: Checks unknown channel data before changing visible state.

Pi screen APIs: [`pi.events`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#choose-an-integration-point), [`EventBus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#choose-an-integration-point), [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `createEventBus`, `EventBus.on`, `EventBus.emit`, `setStatus`, `setWidget`

### Consequences

- A visible view can react without directly importing the publisher.
- Unknown payloads need validation and listeners need cleanup.

### Implementation

- Validate unknown event payloads.
- Unsubscribe when the owning view ends.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Add an event bus for tools and hooks
  - [packages/coding-agent/src/core/event-bus.ts:1-33](https://github.com/earendil-works/pi/blob/9c9e6822e3af7e0c17cd4116b8eab01d79eb69a8/packages/coding-agent/src/core/event-bus.ts#L1-L33) @9c9e6822
- [**dot314**](https://github.com/nicobailon/dot314): Bound input interception to foreground progress
  - [extensions/handover/index.ts:700-747](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/handover/index.ts#L700-L747) @17cce138
- [**pi-prompt-template-model**](https://github.com/nicobailon/pi-prompt-template-model): Bound input interception to foreground progress
  - [subagent-step.ts:443-462](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/subagent-step.ts#L443-L462) @6da20591
  - [subagent-step.ts:533-558](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/subagent-step.ts#L533-L558) @6da20591
  - [subagent-step.ts:748-762](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/subagent-step.ts#L748-L762) @6da20591
- [**pi-prune**](https://github.com/nicobailon/pi-prune): Bound input interception to foreground progress
  - [index.ts:63-75](https://github.com/nicobailon/pi-prune/blob/0194b7a3eb87db71648fef672effbfb65411a82a/index.ts#L63-L75) @0194b7a3
  - [index.ts:131-132](https://github.com/nicobailon/pi-prune/blob/0194b7a3eb87db71648fef672effbfb65411a82a/index.ts#L131-L132) @0194b7a3
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Bound input interception to foreground progress
  - [src/slash/slash-commands.ts:350-440](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/slash/slash-commands.ts#L350-L440) @6826b054
  - [src/slash/slash-commands.ts:770-835](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/slash/slash-commands.ts#L770-L835) @6826b054
- [**dot314**](https://github.com/nicobailon/dot314): Add an event bus for tools and hooks
- [**pi-coordination**](https://github.com/nicobailon/pi-coordination): Add an event bus for tools and hooks
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Add an event bus for tools and hooks
- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Add an event bus for tools and hooks
- [**pi-mcp-adapter**](https://github.com/nicobailon/pi-mcp-adapter): Add an event bus for tools and hooks
- [**pi-prompt-template-model**](https://github.com/nicobailon/pi-prompt-template-model): Add an event bus for tools and hooks
- [**pi-rewind-hook**](https://github.com/nicobailon/pi-rewind-hook): Add an event bus for tools and hooks
- [**pi-subagent-enhanced**](https://github.com/nicobailon/pi-subagent-enhanced): Add an event bus for tools and hooks
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Add an event bus for tools and hooks
- [**surf-cli**](https://github.com/nicobailon/surf-cli): Add an event bus for tools and hooks

### Related

- [Refresh Lease](https://pi-tui.ratstack.sh/patterns/refresh-lease.md): owns subscriptions and redraws

### States

- Waiting view (`subscribed`): Show a synthetic status awaiting a named event.
- Event applied (`event`): Publish a validated payload and update the status.
- Owner gone (`unsubscribed`): Show that a later event no longer updates the cleared slot.

## Tick Heart

`tick-heart` · Behavioral / Animation · [Markdown](https://pi-tui.ratstack.sh/patterns/tick-heart.md) · [JSON](https://pi-tui.ratstack.sh/patterns/tick-heart.json) · also known as Fixed-tick view

### Intent

Advance local view state and request redraws on a component-owned interval.

### Motivation

Nico's arcade components own intervals that advance local state independently of keyboard input.

### Applicability

- Use this when animation should continue independently of user input.

### Structure

```text
interval -> local state -> redraw
input -> local state
dispose -> stop interval
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Owned interval`: Advances local state until disposal stops it.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `Box`

### Consequences

- A live view can animate without a model or user event.
- The interval must stop on disposal and resizing can affect behavior.

### Implementation

- Clear the interval on disposal.
- Keep resize-dependent behavior explicit.
- The story demonstrates reusable ticking rather than copying a game.

### Known uses: seen in Nico's repos

- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Arcade: fixed-tick full-screen component
  - [arcade/tetris.ts:187-233](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/tetris.ts#L187-L233) @bca5070b
  - [arcade/ping.ts:86-139](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/ping.ts#L86-L139) @bca5070b
  - [arcade/picman.ts:214-245](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/picman.ts#L214-L245) @bca5070b
  - [arcade/spice-invaders.ts:295-345](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/spice-invaders.ts#L295-L345) @bca5070b
  - [arcade/badlogic-game/badlogic-game.ts:22-96](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/badlogic-game/badlogic-game.ts#L22-L96) @bca5070b
- [**pi-autoresearch**](https://github.com/nicobailon/pi-autoresearch): Pair live refresh with owned timer cleanup
  - [extensions/pi-autoresearch/index.ts:1428-1445](https://github.com/nicobailon/pi-autoresearch/blob/22bd30b19482f2be5936031b2c6b149115779f16/extensions/pi-autoresearch/index.ts#L1428-L1445) @22bd30b1
- [**pi-coordination**](https://github.com/nicobailon/pi-coordination): Pair live refresh with owned timer cleanup
  - [coordinate/dashboard.ts:910-917](https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/dashboard.ts#L910-L917) @7f32f5d9
  - [coordinate/dashboard.ts:1015-1018](https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/dashboard.ts#L1015-L1018) @7f32f5d9
- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Pair live refresh with owned timer cleanup
  - [overlay-coordinator.ts:85-119](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/overlay-coordinator.ts#L85-L119) @09937ed6

### Related

- [Pause Latch](https://pi-tui.ratstack.sh/patterns/pause-latch.md): suspends change while preserving input

### States

- Ticking (`running`): Animate a synthetic progress marker at a fixed cadence.
- Narrow view (`narrow`): Resize and show the view's explicit compact or paused behavior.
- Disposed (`disposed`): Close the interaction and stop further ticks.

## Done Contract

`done-contract` · Lifecycle / Overlays and dialogs · [Markdown](https://pi-tui.ratstack.sh/patterns/done-contract.md) · [JSON](https://pi-tui.ratstack.sh/patterns/done-contract.json) · also known as Custom completion

### Intent

Resolve each custom interaction with a typed selection or cancellation.

### Motivation

Nico's custom selectors await a result until their component calls done.

### Applicability

- Use this when built-in dialogs cannot express the required view.

### Structure

```text
ctx.ui.custom -> component
component -> done(result)
done -> dispose + return
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- `Completion callback`: Returns one typed selection or cancellation to the caller.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `custom`, `ExtensionSelectorComponent`

### Consequences

- Selection and cancellation settle one typed interaction.
- A missing or duplicate completion can leave the caller pending or settle incorrectly.

### Implementation

- Call done on every exit path.
- Do not permanently hide a custom-owned overlay instead of resolving it.
- Make completion idempotent.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Resolve custom selectors through done
  - [extensions/session-switch/picker.ts:439-470](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/session-switch/picker.ts#L439-L470) @17cce138
  - [extensions/tool-horizon/boundary-picker.ts:470-500](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/tool-horizon/boundary-picker.ts#L470-L500) @17cce138
  - [extensions/screenshots-picker/index.ts:807-830](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/screenshots-picker/index.ts#L807-L830) @17cce138
  - [extensions/code-actions/ui.ts:25-75](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/code-actions/ui.ts#L25-L75) @17cce138
  - [extensions/sandbox/index.ts:340-370](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/sandbox/index.ts#L340-L370) @17cce138
  - [extensions/tools/index.ts:260-290](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/tools/index.ts#L260-L290) @17cce138
- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Resolve custom selectors through done
  - [code-actions/ui.ts:34-96](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/code-actions/ui.ts#L34-L96) @bca5070b
- [**pi-mcp-adapter**](https://github.com/nicobailon/pi-mcp-adapter): Resolve custom selectors through done
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Resolve custom selectors through done
  - [quote-reply.ts:241-267](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/quote-reply.ts#L241-L267) @859dee67
- [**dot314**](https://github.com/nicobailon/dot314): Open a custom inspection view for touched files
  - [extensions/files-touched.ts:55-90](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/files-touched.ts#L55-L90) @17cce138
- [**dot314**](https://github.com/nicobailon/dot314): Upstream-derived preset picker and status
  - [extensions/preset.ts:1-18](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/preset.ts#L1-L18) @17cce138

### Related

- [Focus Baton](https://pi-tui.ratstack.sh/patterns/focus-baton.md): moves focus without completion

### States

- Focused view (`open`): Show a small read-only inspection list with selection and cancel hints.
- Selection result (`selected`): Close the view and show its typed result in the host.
- Cancelled result (`cancelled`): Close through cancellation and show that no action was selected.

## Result Relay

`result-relay` · Lifecycle / Status and widgets · [Markdown](https://pi-tui.ratstack.sh/patterns/result-relay.md) · [JSON](https://pi-tui.ratstack.sh/patterns/result-relay.json) · also known as Async result widget

### Intent

Replace pending feedback with a persistent non-modal result widget.

### Motivation

Dot314's btw command starts with pending feedback and leaves a result above the editor.

### Applicability

- Use this when background work should leave useful output near the editor.

### Structure

```text
pending -> status
ready -> result widget
dismiss -> clear
```

### Participants

- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- `Result owner`: Replaces pending feedback and clears the dismissed result.

Pi screen APIs: [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `setStatus`, `setWidget`

### Consequences

- Background output can remain useful without a modal.
- The result persists until its owning dismissal or cleanup path clears it.

### Implementation

- Use a stable key for replacement.
- Clear the result when dismissed or cancelled.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Keep asynchronous btw results in a replaceable widget
  - [extensions/btw/index.ts:536-598](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/btw/index.ts#L536-L598) @17cce138

### Related

- [Signal Pair](https://pi-tui.ratstack.sh/patterns/signal-pair.md): pairs compact and detailed signals

### States

- Work pending (`pending`): Show a compact pending status for synthetic work.
- Result ready (`result`): Replace pending feedback with the result widget.
- Dismissed (`dismissed`): Remove the result on dismissal.

## Notice Fuse

`notice-fuse` · Lifecycle / Status and widgets · [Markdown](https://pi-tui.ratstack.sh/patterns/notice-fuse.md) · [JSON](https://pi-tui.ratstack.sh/patterns/notice-fuse.json) · also known as Expiring status

### Intent

Clear a one-off keyed notice after its display interval.

### Motivation

Skill Palette clears a shortcut notice from a timer that could otherwise erase a replacement notice.

### Applicability

- Use this when feedback should not remain in the footer indefinitely.

### Structure

```text
notice -> keyed slot + timer
new notice -> replace owned timer
latest deadline -> clear
```

### Participants

- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- `Notice timer`: Clears only the current notice at its own deadline.

Pi screen APIs: [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `setStatus`

### Consequences

- One-off feedback clears without a persistent footer slot.
- Timers need replacement and stale-context guards.

### Implementation

- An older timer must not erase a newer value.
- Cancel owned timers and reject stale session contexts.

### Known uses: seen in Nico's repos

- [**pi-skill-palette**](https://github.com/nicobailon/pi-skill-palette): Expire one-off status notices
  - [index.ts:907-908](https://github.com/nicobailon/pi-skill-palette/blob/a5c4429b8c2e33ab903d07856497014f3d5ad34e/index.ts#L907-L908) @a5c4429b

### Related

- [Refresh Lease](https://pi-tui.ratstack.sh/patterns/refresh-lease.md): also ties timers to owners

### States

- Notice shown (`notice`): Show a synthetic shortcut notice in a keyed slot.
- New notice (`replacement`): Replace it before the old timer expires.
- Latest notice expires (`expired`): Keep the replacement through the old deadline and clear at its own deadline.

## Draft Return

`draft-return` · Lifecycle / Editors and drafts · [Markdown](https://pi-tui.ratstack.sh/patterns/draft-return.md) · [JSON](https://pi-tui.ratstack.sh/patterns/draft-return.json) · also known as Draft restoration

### Intent

Restore the captured editor draft after a temporary submission.

### Motivation

Boomerang temporarily replaces editor text to submit a reload command while an unsent draft exists.

### Applicability

- Use this when a command needs editor-driven submission without losing the current text.

### Structure

```text
capture draft -> temporary submit
finally -> restore captured text
```

### Participants

- [`ctx.ui.getEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Captures the current main-editor draft.
- [`ctx.ui.setEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Replaces the main-editor text.
- [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Installs the single custom editor factory.
- [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Preserves application controls for editor replacements.
- `Captured draft`: Survives temporary editor-driven submission.

Pi screen APIs: [`ctx.ui.getEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `getEditorText`, `setEditorText`, `setEditorComponent`

### Consequences

- Temporary submission does not discard the captured draft.
- Restoration needs a finally path and the source restores only nonempty text.

### Implementation

- Restore the captured draft in a finally path.
- The source restores only a nonempty captured draft.

### Known uses: seen in Nico's repos

- [**pi-boomerang**](https://github.com/nicobailon/pi-boomerang): Temporarily install an editor and restore draft after submission
  - [index.ts:1318-1337](https://github.com/nicobailon/pi-boomerang/blob/1a5985b2d92cfa84ce1f470d100d02b368711a91/index.ts#L1318-L1337) @1a5985b2

### Related

- [Draft Fence](https://pi-tui.ratstack.sh/patterns/draft-fence.md): separates draft from committed state

### States

- Existing draft (`draft`): Show a synthetic unsent prompt in the editor.
- Temporary submission (`temporary`): Show a temporary command replacing the captured text.
- Draft restored (`restored`): Restore the original nonempty draft after submission or failure.

## Input Lease

`input-lease` · Lifecycle / Keys and focus · [Markdown](https://pi-tui.ratstack.sh/patterns/input-lease.md) · [JSON](https://pi-tui.ratstack.sh/patterns/input-lease.json) · also known as Temporary input listener

### Intent

Intercept raw terminal controls only while their owning operation is active.

### Motivation

Foreground progress controls and fleet widgets must remove terminal listeners after their operation ends.

### Applicability

- Use this when foreground progress or a visible widget needs stop or detach controls.

### Structure

```text
active -> subscribe input
control -> consume / transform
end -> unsubscribe
```

### Participants

- [`ctx.ui.onTerminalInput`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Installs a raw-input handler and returns its unsubscriber.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- [`ExtensionContext.mode`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#account-for-each-mode): Separates TUI from RPC, JSON and print modes.
- `Unsubscriber`: Releases temporary interception when its owner ends.

Pi screen APIs: [`ctx.ui.onTerminalInput`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ExtensionContext.mode`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#account-for-each-mode), `onTerminalInput`, `setWidget`, `matchesKey`

### Consequences

- Temporary stop or detach controls can coexist with the normal editor.
- Raw input interception needs TUI guards and bounded subscription lifetime.

### Implementation

- Subscribe only in TUI mode.
- Always unsubscribe on completion or cancellation.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Bound input interception to foreground progress
  - [extensions/handover/index.ts:700-747](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/handover/index.ts#L700-L747) @17cce138
- [**pi-prompt-template-model**](https://github.com/nicobailon/pi-prompt-template-model): Bound input interception to foreground progress
  - [subagent-step.ts:443-462](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/subagent-step.ts#L443-L462) @6da20591
  - [subagent-step.ts:533-558](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/subagent-step.ts#L533-L558) @6da20591
  - [subagent-step.ts:748-762](https://github.com/nicobailon/pi-prompt-template-model/blob/6da205917e549cbe8e855c1d241c616ae1fb1627/subagent-step.ts#L748-L762) @6da20591
- [**pi-prune**](https://github.com/nicobailon/pi-prune): Bound input interception to foreground progress
  - [index.ts:63-75](https://github.com/nicobailon/pi-prune/blob/0194b7a3eb87db71648fef672effbfb65411a82a/index.ts#L63-L75) @0194b7a3
  - [index.ts:131-132](https://github.com/nicobailon/pi-prune/blob/0194b7a3eb87db71648fef672effbfb65411a82a/index.ts#L131-L132) @0194b7a3
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Bound input interception to foreground progress
  - [src/slash/slash-commands.ts:350-440](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/slash/slash-commands.ts#L350-L440) @6826b054
  - [src/slash/slash-commands.ts:770-835](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/slash/slash-commands.ts#L770-L835) @6826b054
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Install terminal input listeners with explicit teardown
  - [src/tui/fleet-status.ts:575-600](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet-status.ts#L575-L600) @6826b054
  - [src/tui/fleet-status.ts:1080-1130](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/fleet-status.ts#L1080-L1130) @6826b054

### Related

- [Action Compass](https://pi-tui.ratstack.sh/patterns/action-compass.md): resolves configurable component actions

### States

- Temporary controls (`active`): Show a synthetic running task and its cancel or detach hint.
- Control handled (`consumed`): Consume a task-specific control and update its visible state.
- Listener removed (`finished`): Return input to the normal editor after cleanup.

## Refresh Lease

`refresh-lease` · Lifecycle / Rendering and performance · [Markdown](https://pi-tui.ratstack.sh/patterns/refresh-lease.md) · [JSON](https://pi-tui.ratstack.sh/patterns/refresh-lease.json) · also known as Owned refresh

### Intent

Pair asynchronous refresh triggers with component-owned cleanup.

### Motivation

Messenger, Coordination and Autoresearch pair asynchronous redraw triggers with timer cleanup.

### Applicability

- Use this when a widget or overlay changes independently of user input.

### Structure

```text
timer / subscription -> data
changed data -> requestRender
dispose -> cancel / unsubscribe
```

### Participants

- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events): Registers ordered extension event handlers.
- `Resource owner`: Cancels timers and subscriptions on disposal or shutdown.

Pi screen APIs: [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events), `setWidget`, `truncateToWidth`

### Consequences

- A view can react to asynchronous changes without user input.
- Redraw cadence does not prove the underlying data was refreshed.

### Implementation

- Redrawing does not necessarily refresh underlying data.
- Stop timers and unsubscribe listeners on disposal.
- Reject callbacks against replaced session contexts.

### Known uses: seen in Nico's repos

- [**pi-autoresearch**](https://github.com/nicobailon/pi-autoresearch): Pair live refresh with owned timer cleanup
  - [extensions/pi-autoresearch/index.ts:1428-1445](https://github.com/nicobailon/pi-autoresearch/blob/22bd30b19482f2be5936031b2c6b149115779f16/extensions/pi-autoresearch/index.ts#L1428-L1445) @22bd30b1
- [**pi-coordination**](https://github.com/nicobailon/pi-coordination): Pair live refresh with owned timer cleanup
  - [coordinate/dashboard.ts:910-917](https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/dashboard.ts#L910-L917) @7f32f5d9
  - [coordinate/dashboard.ts:1015-1018](https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/dashboard.ts#L1015-L1018) @7f32f5d9
- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Pair live refresh with owned timer cleanup
  - [overlay-coordinator.ts:85-119](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/overlay-coordinator.ts#L85-L119) @09937ed6
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Show bounded background-session status below the editor
  - [background-widget.ts:24-106](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/background-widget.ts#L24-L106) @77df9a81

### Related

- [Tick Heart](https://pi-tui.ratstack.sh/patterns/tick-heart.md): mutates local state on each tick

### States

- Refreshing widget (`mounted`): Show synthetic background durations advancing.
- New snapshot (`updated`): Show a data refresh separately from its redraw.
- Refresh stopped (`disposed`): Clear the widget and show that its timer and listener no longer update the host.

## Process Shell

`process-shell` · Lifecycle / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/process-shell.md) · [JSON](https://pi-tui.ratstack.sh/patterns/process-shell.json) · also known as Terminal process view

### Intent

Bind a temporary terminal process view to one custom interaction.

### Motivation

Nico's interactive shell mounts or attaches a process inside a custom UI with an exit result.

### Applicability

- Use this when a command needs interactive terminal input instead of captured output.

### Structure

```text
process / attachment -> view
exit / cancel -> done(result)
finish -> owned cleanup
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Process owner`: Constructs or attaches the terminal job and owns failure cleanup.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `custom`, `Text`, `matchesKey`

### Consequences

- An interactive process can have a temporary terminal view.
- PTY construction and failure cleanup remain outside Pi's UI API.

### Implementation

- Resolve process exit and cancellation exactly once.
- Clean up handlers and process ownership if initialization fails.
- Pi does not supply the PTY implementation.

### Known uses: seen in Nico's repos

- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Interactive shell overlay lifecycle and terminal sizing
  - [overlay-component.ts:20-205](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L20-L205) @77df9a81
- [**dot314**](https://github.com/nicobailon/dot314): Host an interactive shell inside a custom TUI lifecycle
  - [extensions/interactive-shell.ts:145-180](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/interactive-shell.ts#L145-L180) @17cce138

### Related

- [Output Vault](https://pi-tui.ratstack.sh/patterns/output-vault.md): keeps output beyond the view

### States

- Process attached (`running`): Show synthetic process output in a terminal-like custom view.
- Process exit (`exited`): Resolve an exit result and return to the host.
- Cancelled view (`cancelled`): Resolve cancellation and clean up temporary input ownership.

## Output Vault

`output-vault` · Lifecycle / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/output-vault.md) · [JSON](https://pi-tui.ratstack.sh/patterns/output-vault.json) · also known as Retained job view

### Intent

Keep completed job output available after its foreground view closes.

### Motivation

Interactive Shell's non-streaming dispatch retains completed sessions so output remains queryable after foreground completion.

### Applicability

- Use this when background output must remain inspectable for reattachment.

### Structure

```text
foreground view -> background owner
completed job -> retained output
reattach -> fresh view
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- `Background owner`: Retains completed output independently of the foreground interaction.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `custom`, `setWidget`, `Box`, `DynamicBorder`

### Consequences

- Background output can be inspected after its first view closes.
- Retention differs by streaming mode and needs separate session ownership.

### Implementation

- The source's retention rules differ by streaming mode.
- Closing a view is not the same as releasing the background job.
- Pi does not provide a retained PTY-session store.

### Known uses: seen in Nico's repos

- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Retain dispatch terminals for later completion queries
  - [overlay-component.ts:455-480](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L455-L480) @77df9a81
  - [overlay-component.ts:642-690](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L642-L690) @77df9a81
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Reattach overlay supports bounded selection and deferred refresh
  - [reattach-overlay.ts:18-115](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L18-L115) @77df9a81
  - [reattach-overlay.ts:311-448](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L311-L448) @77df9a81

### Related

- [Process Shell](https://pi-tui.ratstack.sh/patterns/process-shell.md): owns the temporary foreground interaction

### States

- Background ownership (`background`): Show a synthetic job handed off from its foreground view.
- Completed output retained (`completed`): Show a completed job still available in its background widget.
- Inspect retained output (`reattached`): Open a fresh view of its stored synthetic output.

## Deferred Crest

`deferred-crest` · Lifecycle / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/deferred-crest.md) · [JSON](https://pi-tui.ratstack.sh/patterns/deferred-crest.json) · also known as Deferred header

### Intent

Mount optional startup content only after deferred discovery remains eligible.

### Motivation

Powerline Footer discovers optional welcome content asynchronously and rechecks request and session eligibility.

### Applicability

- Use this when a welcome header should not block session startup.

### Structure

```text
startup -> deferred discovery
eligible generation -> setHeader
stale generation -> ignore
```

### Participants

- [`ctx.ui.setHeader`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Installs or restores the startup header.
- [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events): Registers ordered extension event handlers.
- `Session generation`: Rejects stale discovery before mounting optional content.

Pi screen APIs: [`ctx.ui.setHeader`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events), `setHeader`, `Text`

### Consequences

- Startup can proceed before optional header discovery.
- Late discovery must not mount stale session content.

### Implementation

- Recheck eligibility after asynchronous discovery.
- Reject callbacks from superseded session generations.

### Known uses: seen in Nico's repos

- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Deferred welcome header
  - [index.ts:3555-3577](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/index.ts#L3555-L3577) @859dee67

### Related

- [Mode Fence](https://pi-tui.ratstack.sh/patterns/mode-fence.md): guards the available mount surface

### States

- Startup proceeds (`starting`): Show the ordinary host before optional discovery settles.
- Header mounted (`eligible`): Mount a synthetic welcome header after an eligible result.
- Stale discovery ignored (`stale`): Show a new session without a late header from the previous generation.

## Session Memento

`session-memento` · Lifecycle / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/session-memento.md) · [JSON](https://pi-tui.ratstack.sh/patterns/session-memento.json) · also known as Session-saved view

### Intent

Reconstruct deliberate view state from typed custom session entries.

### Motivation

Nico's arcade overlays load typed saved entries, while anycopy validates saved fold IDs before restoring them.

### Applicability

- Use this when a mini-app or tree should resume after its interaction closes.

### Structure

```text
typed snapshot -> appendEntry
session entries -> validate -> view
```

### Participants

- [`pi.appendEntry`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management): Persists typed custom data outside model context.
- [`ctx.sessionManager.getBranch`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management): Reads entries along the active or requested branch.
- [`ctx.sessionManager.getEntries`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management): Reads session-file entries for deliberate reconstruction.
- `Typed snapshot`: Stores deliberate state with explicit compatibility checks.

Pi screen APIs: [`pi.appendEntry`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management), [`ctx.sessionManager.getBranch`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management), [`ctx.sessionManager.getEntries`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management), `SessionManager.inMemory`, `SessionManager.appendCustomEntry`, `SessionManager.getBranch`, `setWidget`

### Consequences

- Deliberate view state can survive closing and resuming.
- Branch selection and snapshot compatibility need explicit checks.

### Implementation

- Choose the active branch for branch-sensitive state.
- Validate restored identifiers and snapshot compatibility.
- Do not persist every animation frame.

### Known uses: seen in Nico's repos

- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Arcade: persist and resume through session entries
  - [arcade/tetris.ts:632-652](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/tetris.ts#L632-L652) @bca5070b
  - [arcade/ping.ts:558-588](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/ping.ts#L558-L588) @bca5070b
  - [arcade/picman.ts:313-328](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/picman.ts#L313-L328) @bca5070b
  - [arcade/spice-invaders.ts:1060-1104](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/spice-invaders.ts#L1060-L1104) @bca5070b
  - [arcade/badlogic-game/badlogic-game.ts:270-297](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/badlogic-game/badlogic-game.ts#L270-L297) @bca5070b
- [**dot314**](https://github.com/nicobailon/dot314): Persist fold state while reusing a session-tree selector
  - [extensions/anycopy/index.ts:971-1035](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/anycopy/index.ts#L971-L1035) @17cce138

### Related

- [Branch Fold](https://pi-tui.ratstack.sh/patterns/branch-fold.md): uses saved fold identifiers

### States

- New view (`initial`): Show a synthetic counter and a compact view preference.
- Saved snapshot (`saved`): Close and append a deliberately typed state snapshot.
- Resumed view (`restored`): Reopen with compatible saved state and the same view preference.

## Mode Fence

`mode-fence` · Lifecycle / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/mode-fence.md) · [JSON](https://pi-tui.ratstack.sh/patterns/mode-fence.json) · also known as Mode-aware UI

### Intent

Keep terminal components separate from dialog-capable and no-UI modes.

### Motivation

The Discord host cannot answer terminal prompts and its old UI adapter lacks parts of the current contract.

### Applicability

- Use this when the same extension runs in TUI, RPC, JSON or print mode.

### Structure

```text
ctx.mode -> terminal mount?
ctx.hasUI -> supported dialog?
no UI -> safe cancellation
```

### Participants

- [`ExtensionContext.mode`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#account-for-each-mode): Separates TUI from RPC, JSON and print modes.
- [`ExtensionContext.hasUI`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#account-for-each-mode): Reports dialog-capable TUI or RPC availability.
- [`ctx.ui.confirm`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Returns a confirmation decision.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- `Cancellation result`: Prevents an unavailable prompt from implying approval.

Pi screen APIs: [`ExtensionContext.mode`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#account-for-each-mode), [`ExtensionContext.hasUI`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#account-for-each-mode), [`ctx.ui.confirm`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `setWidget`, `Text`

### Consequences

- Non-interactive work need not depend on terminal rendering.
- RPC supports dialogs but not custom terminal components.

### Implementation

- hasUI includes RPC and does not imply terminal components.
- Do not copy the incomplete 0.65-era headless adapter.
- Treat false or undefined dialog results as safe cancellation.

### Known uses: seen in Nico's repos

- [**pi-discord**](https://github.com/nicobailon/pi-discord): Refuse headless dialogs safely
  - [daemon/headless-ui.js:4-24](https://github.com/nicobailon/pi-discord/blob/8dce54fe2a42108d0cc992579818fe74cb458c37/daemon/headless-ui.js#L4-L24) @8dce54fe
- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Session-scoped editor component mount
  - [raw-paste/index.ts:95-104](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/raw-paste/index.ts#L95-L104) @bca5070b
- [**dot314**](https://github.com/nicobailon/dot314): Separate compact status from structured detail
  - [extensions/plan-mode.ts:575-590](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/plan-mode.ts#L575-L590) @17cce138
  - [extensions/pi-codex-goal/src/goal-runtime-status.ts:42-63](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-codex-goal/src/goal-runtime-status.ts#L42-L63) @17cce138
  - [extensions/plan-mode.ts:1-18](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/plan-mode.ts#L1-L18) @17cce138
- [**pi-custom-compaction**](https://github.com/nicobailon/pi-custom-compaction): Separate compact status from structured detail
  - [runtime/session-state.ts:49-78](https://github.com/nicobailon/pi-custom-compaction/blob/a0e4700badb1c5c1c2dd12eeb250ff067fa67b7e/runtime/session-state.ts#L49-L78) @a0e4700b
  - [runtime/session-state.ts:105-183](https://github.com/nicobailon/pi-custom-compaction/blob/a0e4700badb1c5c1c2dd12eeb250ff067fa67b7e/runtime/session-state.ts#L105-L183) @a0e4700b
- [**pi-extensions**](https://github.com/nicobailon/pi-extensions): Separate compact status from structured detail
  - [ralph-wiggum/index.ts:190-215](https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/ralph-wiggum/index.ts#L190-L215) @bca5070b
- [**pi-memory-workbench**](https://github.com/nicobailon/pi-memory-workbench): Separate compact status from structured detail
  - [index.ts:72-82](https://github.com/nicobailon/pi-memory-workbench/blob/92b4c9c3ad07841418d77118bf8bd02ad204f7c4/index.ts#L72-L82) @92b4c9c3
- [**pi-messenger**](https://github.com/nicobailon/pi-messenger): Separate compact status from structured detail
  - [index.ts:295-305](https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/index.ts#L295-L305) @09937ed6

### Related

- [Done Contract](https://pi-tui.ratstack.sh/patterns/done-contract.md): settles terminal-only custom interactions

### States

- Terminal mode (`tui`): Show a mounted synthetic component in TUI mode.
- RPC client (`rpc`): Show a synthetic client-observed dialog result with no terminal factory mount.
- No UI (`headless`): Show a fixture transcript recording a skipped mount and a cancelled action in print mode.

## Lazy Peek

`lazy-peek` · Presentation / Lists and pickers · [Markdown](https://pi-tui.ratstack.sh/patterns/lazy-peek.md) · [JSON](https://pi-tui.ratstack.sh/patterns/lazy-peek.json) · also known as Lazy preview picker

### Intent

Load and cache preview detail only for items the user inspects.

### Motivation

Dot314's session switcher avoids loading every full transcript before the user inspects an item.

### Applicability

- Use this when eagerly loading every record would make a picker expensive.

### Structure

```text
highlight -> cache lookup
miss -> load -> cache -> preview
```

### Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- `Preview cache`: Loads inspected detail and limits retained previews.

Pi component APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `wrapTextWithAnsi`

### Consequences

- Only inspected items need loaded preview detail.
- The cache needs a bound and dismissal stays distinct from selection.

### Implementation

- Bound the preview cache.
- Keep dismissal distinct from a confirmed selection.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Reuse cached previews in a session-switch selector
  - [extensions/session-switch/picker.ts:1-45](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/session-switch/picker.ts#L1-L45) @17cce138

### Related

- [Late Paint](https://pi-tui.ratstack.sh/patterns/late-paint.md): caches layout rather than fetched detail

### States

- Preview pending (`loading`): Select a synthetic session while its preview is loading.
- Preview loaded (`loaded`): Show the selected item's detail once available.
- Cached preview (`revisit`): Revisit the previous item and reuse its cached preview.

## Work Caption

`work-caption` · Presentation / Status and widgets · [Markdown](https://pi-tui.ratstack.sh/patterns/work-caption.md) · [JSON](https://pi-tui.ratstack.sh/patterns/work-caption.json) · also known as Working message

### Intent

Set task-specific text in Pi's active working indicator.

### Motivation

Nico's upstream working-message API lets active work replace Pi's generic indicator text.

### Applicability

- Use this when streaming work needs a short progress description.

### Structure

```text
active work -> message override
end -> default message
```

### Participants

- [`ctx.ui.setWorkingMessage`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Overrides or restores the active working label.
- `Active operation`: Provides the task-specific caption and restores its default.

Pi screen APIs: [`ctx.ui.setWorkingMessage`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), `ctx.ui.setWidget`, `ctx.ui.getEditorText`

### Consequences

- The active indicator can describe the current task.
- RPC does not render this override.

### Implementation

- This method is a no-op in RPC mode.
- Restore the default message when the override ends.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Let extensions set the active working message
  - [packages/coding-agent/src/core/extensions/types.ts:75-88](https://github.com/earendil-works/pi/blob/271b49da3c959863afc08ae669f53b55fb424b7f/packages/coding-agent/src/core/extensions/types.ts#L75-L88) @271b49da
- [**dot314**](https://github.com/nicobailon/dot314): Let extensions set the active working message
- [**pi-discord**](https://github.com/nicobailon/pi-discord): Let extensions set the active working message
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Let extensions set the active working message
- [**pi-prompt-template-model**](https://github.com/nicobailon/pi-prompt-template-model): Let extensions set the active working message
- [**pi-prune**](https://github.com/nicobailon/pi-prune): Let extensions set the active working message

### Related

- [Notice Fuse](https://pi-tui.ratstack.sh/patterns/notice-fuse.md): shows an independent temporary notice

### States

- Default indicator (`default`): Show Pi's normal working label.
- Task label (`custom`): Show a synthetic task-specific working message.
- Default restored (`restored`): Clear the override and show the normal label again.

## Render Funnel

`render-funnel` · Presentation / Rendering and performance · [Markdown](https://pi-tui.ratstack.sh/patterns/render-funnel.md) · [JSON](https://pi-tui.ratstack.sh/patterns/render-funnel.json) · also known as Coalesced redraw

### Intent

Collapse bursty redraw scheduling behind one pending timer.

### Motivation

Interactive Shell and Powerline Footer receive bursts of updates that share a pending redraw timer.

### Applicability

- Use this when many output events do not need separate scheduled redraws.

### Structure

```text
event burst -> pending timer
one callback -> requestRender
```

### Participants

- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- `Pending timer`: Combines a burst into one scheduled render request.

Pi screen APIs: [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `ctx.ui.setWidget`

### Consequences

- Several events can share one scheduled redraw request.
- Pending-timer cancellation and flushing need explicit semantics.

### Implementation

- Cancel the pending timer on disposal.
- Pi also coalesces requestRender calls internally.

### Known uses: seen in Nico's repos

- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Coalesce bursty redraw requests
  - [overlay-component.ts:263-273](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L263-L273) @77df9a81
  - [overlay-component.ts:1098-1125](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L1098-L1125) @77df9a81
- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Coalesce bursty redraw requests
  - [render-scheduler.ts:1-28](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/render-scheduler.ts#L1-L28) @859dee67
- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Reattach overlay supports bounded selection and deferred refresh
  - [reattach-overlay.ts:18-115](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L18-L115) @77df9a81
  - [reattach-overlay.ts:311-448](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L311-L448) @77df9a81

### Related

- [Refresh Lease](https://pi-tui.ratstack.sh/patterns/refresh-lease.md): owns the redraw trigger's lifetime

### States

- Burst of updates (`burst`): Show one synthetic event burst updating the latest visible output.
- Single scheduled redraw (`settled`): Display a render counter showing the burst shares one scheduled request.
- Closed before timer (`closed`): Show the host unchanged after a pending redraw is cancelled.

## Late Paint

`late-paint` · Presentation / Rendering and performance · [Markdown](https://pi-tui.ratstack.sh/patterns/late-paint.md) · [JSON](https://pi-tui.ratstack.sh/patterns/late-paint.json) · also known as Width-keyed cache

### Intent

Cache plain layout by width and apply theme styling during rendering.

### Motivation

Intercom's inline messages reuse immutable wrapped text across widths and apply colours while rendering.

### Applicability

- Use this when immutable message content needs repeated wrapping or preview rendering.

### Structure

```text
immutable text + width -> cache
cached layout + theme -> lines
```

### Participants

- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`wrapTextWithAnsi`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Wraps text while preserving styling across lines.
- [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly): Styles text through semantic or concrete colours.
- `Plain layout cache`: Retains immutable wrapping without embedding theme colours.

Pi component APIs: [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`wrapTextWithAnsi`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly), `getThemeByName`

### Consequences

- Cached layout can survive theme changes without retaining old colours.
- Content mutation or width changes require cache invalidation.

### Implementation

- Invalidate layout when its content or width changes.
- Invalidation must not erase application state.
- Do not cache permanently coloured strings.

### Known uses: seen in Nico's repos

- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Cache message layout separately from live theme styling
  - [ui/inline-message.ts:10-125](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/inline-message.ts#L10-L125) @a5fad4df
- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Project workflow state before rendering status widgets
  - [src/tui/render.ts:2520-2585](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/render.ts#L2520-L2585) @6826b054
  - [src/tui/render.ts:3060-3110](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/render.ts#L3060-L3110) @6826b054

### Related

- [Lazy Peek](https://pi-tui.ratstack.sh/patterns/lazy-peek.md): caches loaded data instead of layout

### States

- Cached layout (`cached`): Render an immutable synthetic message using a width-keyed wrapped layout.
- Width changed (`resized`): Change width and rebuild wrapping without clearing the message.
- Theme changed (`theme-change`): Reapply the active theme without retaining old ANSI colours.

## Detail Fold

`detail-fold` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/detail-fold.md) · [JSON](https://pi-tui.ratstack.sh/patterns/detail-fold.json) · also known as Expandable tool result

### Intent

Show compact progress and summaries with bounded expanded tool detail.

### Motivation

Web Access emits phase-specific tool progress while Tool Display caps long expanded previews.

### Applicability

- Use this when structured tool results would overwhelm the transcript.

### Structure

```text
renderCall -> progress
renderResult -> summary / detail
```

### Participants

- [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders the call before or during execution.
- [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders partial or final tool output.
- [`ToolRenderContext`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Shares per-call state, prior components and invalidation.
- `Structured result`: Supplies guarded partial data and bounded expanded detail.

Pi component APIs: [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolRenderContext`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), `ToolExecutionComponent`

### Consequences

- The transcript stays compact while detail remains inspectable.
- Partial results need guards and expanded previews may still need caps.

### Implementation

- Partial results may not contain final detail fields.
- Mark capped previews rather than silently hiding their limit.
- The tool-display snapshot's peer range stops at 0.80.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Render partial, compact and expanded tool results
  - [extensions/todos.ts:1-20](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/todos.ts#L1-L20) @17cce138
- [**pi-interview-tool**](https://github.com/nicobailon/pi-interview-tool): Render partial, compact and expanded tool results
- [**pi-mcp-adapter**](https://github.com/nicobailon/pi-mcp-adapter): Render partial, compact and expanded tool results
  - [index.ts:500-512](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/index.ts#L500-L512) @85db03d8
  - [index.ts:790-804](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/index.ts#L790-L804) @85db03d8
  - [index.ts:1765-1775](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/index.ts#L1765-L1775) @85db03d8
  - [index.ts:2058-2090](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/index.ts#L2058-L2090) @85db03d8
  - [proxy-modes.ts:1130-1140](https://github.com/nicobailon/pi-mcp-adapter/blob/85db03d87cd0f7461b55eab8d25c10bce473b801/proxy-modes.ts#L1130-L1140) @85db03d8
- [**pi-subagent-enhanced**](https://github.com/nicobailon/pi-subagent-enhanced): Render partial, compact and expanded tool results
  - [index.ts:860-930](https://github.com/nicobailon/pi-subagent-enhanced/blob/f895b2a8773341e4b8a2ba197d58bdd43d5cb561/index.ts#L860-L930) @f895b2a8
- [**pi-tool-display**](https://github.com/nicobailon/pi-tool-display): Render partial, compact and expanded tool results
  - [src/tool-overrides.ts:1064-1105](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/tool-overrides.ts#L1064-L1105) @fca8c858
- [**pi-web-access**](https://github.com/nicobailon/pi-web-access): Render partial, compact and expanded tool results
  - [index.ts:1633-1655](https://github.com/nicobailon/pi-web-access/blob/9a0779976ba47350be18f8cfacaffbe2a407113e/index.ts#L1633-L1655) @9a077997

### Related

- [Call Capsule](https://pi-tui.ratstack.sh/patterns/call-capsule.md): retains a shell across result updates

### States

- In flight (`partial`): Show a synthetic phase-specific partial result.
- Summary (`collapsed`): Show one compact completion row.
- Bounded detail (`expanded`): Expand details with an explicit preview-cap notice.

## Call Capsule

`call-capsule` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/call-capsule.md) · [JSON](https://pi-tui.ratstack.sh/patterns/call-capsule.json) · also known as Call-scoped renderer

### Intent

Retain a display shell through call and result renders of one tool execution.

### Motivation

Dot314's renderer-call-state keeps a call display shell available during result rendering.

### Applicability

- Use this when streamed results should update their existing mounted presentation.

### Structure

```text
call context -> display shell
partial / final -> same call state
```

### Participants

- [`ToolRenderContext`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Shares per-call state, prior components and invalidation.
- [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders the call before or during execution.
- [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders partial or final tool output.
- `Display shell`: Belongs to one tool execution across call and result rendering.

Pi component APIs: [`ToolRenderContext`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), `ToolExecutionComponent`

### Consequences

- One tool execution can update its retained presentation.
- Result rendering must tolerate an absent or hidden shell.

### Implementation

- Scope state to the tool call rather than the whole session.
- Handle an absent or hidden previous component.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Reuse call-scoped presentation across tool phases
  - [extensions/repoprompt-mcp/src/index.ts:2903-2932](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/repoprompt-mcp/src/index.ts#L2903-L2932) @17cce138
  - [extensions/repoprompt-cli/index.ts:3134-3164](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/repoprompt-cli/index.ts#L3134-L3164) @17cce138
  - [extensions/repoprompt-mcp/src/index.ts:2900-2935](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/repoprompt-mcp/src/index.ts#L2900-L2935) @17cce138

### Related

- [Renderer Chain](https://pi-tui.ratstack.sh/patterns/renderer-chain.md): selects or wraps the renderer

### States

- Call shell (`call`): Show a synthetic tool call before its result arrives.
- Shell updated (`partial`): Update the same presentation with partial data.
- Missing shell (`no-shell`): Render a final result safely without an earlier mounted shell.

## Hinge Diff

`hinge-diff` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/hinge-diff.md) · [JSON](https://pi-tui.ratstack.sh/patterns/hinge-diff.json) · also known as Adaptive diff

### Intent

Choose compact, unified or split diff presentation from available width.

### Motivation

Tool Display and Dot314 choose edit layouts when narrow terminals cannot support split output.

### Applicability

- Use this when edit output must remain useful in narrow terminals.

### Structure

```text
parsed diff + width -> layout
layout -> compact / unified / split
```

### Participants

- [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders partial or final tool output.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Measures rendered terminal columns rather than string length.
- `Parsed patch`: Supplies the same changes to several width-dependent layouts.

Pi component APIs: [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`visibleWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `ToolExecutionComponent`

### Consequences

- One patch can have compact, unified or split presentation.
- A narrower layout shows less side-by-side context.

### Implementation

- Do not force split panes where their columns do not fit.
- Keep pending preview reads safe and separate from applying an edit.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Choose diff layout from available width
  - [extensions/pi-codex-apply-patch-display/diff-renderer.ts:1210-1238](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/pi-codex-apply-patch-display/diff-renderer.ts#L1210-L1238) @17cce138
- [**pi-tool-display**](https://github.com/nicobailon/pi-tool-display): Choose diff layout from available width
  - [src/diff-renderer.ts:1-35](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/diff-renderer.ts#L1-L35) @fca8c858
  - [src/diff-renderer.ts:2390-2435](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/diff-renderer.ts#L2390-L2435) @fca8c858
  - [src/pending-diff-preview.ts:1-35](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/pending-diff-preview.ts#L1-L35) @fca8c858
  - [src/pending-diff-preview.ts:160-245](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/pending-diff-preview.ts#L160-L245) @fca8c858

### Related

- [Hinge Panel](https://pi-tui.ratstack.sh/patterns/hinge-panel.md): uses the same width-driven rearrangement

### States

- Split diff (`wide`): Show a synthetic before/after diff where both sides fit.
- Unified diff (`narrow`): Switch the same patch to unified lines at narrow widths.
- Compact diff (`compact`): Show a compact summary with an explicit expansion hint.

## Word Spotlight

`word-spotlight` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/word-spotlight.md) · [JSON](https://pi-tui.ratstack.sh/patterns/word-spotlight.json) · also known as Intraline diff

### Intent

Emphasize changed words inside parsed patch lines.

### Motivation

Dot314's RepoPrompt CLI parser highlights small word changes inside patch lines.

### Applicability

- Use this when line-level changes hide a small textual edit.

### Structure

```text
patch lines -> word comparison
comparison -> styled changed spans
```

### Participants

- [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders partial or final tool output.
- [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly): Styles text through semantic or concrete colours.
- [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Clips text to its allotted columns and can pad the result.
- `Word comparison`: Identifies changed spans before terminal styling.

Pi component APIs: [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly), [`truncateToWidth`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `ToolExecutionComponent`

### Consequences

- Small edits remain identifiable within changed lines.
- Incomplete or non-diff blocks need a plain fallback.

### Implementation

- Keep parsing separate from terminal styling.
- Provide a fallback for incomplete or non-diff blocks.

### Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Render patch output with compact and intra-line diff treatments
  - [extensions/repoprompt-cli/index.ts:1020-1085](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/repoprompt-cli/index.ts#L1020-L1085) @17cce138

### Related

- [Hinge Diff](https://pi-tui.ratstack.sh/patterns/hinge-diff.md): chooses the enclosing diff layout

### States

- Changed words (`changed`): Show a synthetic patch with changed-word emphasis.
- Incomplete patch (`incomplete`): Show a plain fallback while the patch is incomplete.
- Non-diff output (`plain`): Render a non-diff block without pretending it is a patch.

## Error Digest

`error-digest` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/error-digest.md) · [JSON](https://pi-tui.ratstack.sh/patterns/error-digest.json) · also known as Diagnostic summary

### Intent

Project structured errors into compact and expanded diagnostic output.

### Motivation

Web Access combines cancellation, query failures and runtime facts into one diagnostic plan.

### Applicability

- Use this when several failures need a readable summary without hiding detail.

### Structure

```text
failures -> pure diagnostic plan
plan -> summary / expanded lines
```

### Participants

- [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders partial or final tool output.
- [`Text`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components): Renders wrapped text content.
- `Diagnostic plan`: Keeps cancellation and query failures separate from rendering.

Pi component APIs: [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`Text`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#compose-built-in-components), `ToolExecutionComponent`

### Consequences

- Errors have a concise summary without losing expanded diagnostic lines.
- Planning must stay separate from component rendering.

### Implementation

- Keep the error plan separate from the TUI renderer.
- Distinguish cancellation from per-query failure.

### Known uses: seen in Nico's repos

- [**pi-web-access**](https://github.com/nicobailon/pi-web-access): Web access: collapsed and expanded search-error diagnostics
  - [render-search-error.ts:1-115](https://github.com/nicobailon/pi-web-access/blob/9a0779976ba47350be18f8cfacaffbe2a407113e/render-search-error.ts#L1-L115) @9a077997
  - [index.ts:1633-1723](https://github.com/nicobailon/pi-web-access/blob/9a0779976ba47350be18f8cfacaffbe2a407113e/index.ts#L1633-L1723) @9a077997

### Related

- [Detail Fold](https://pi-tui.ratstack.sh/patterns/detail-fold.md): supplies the expansion surface

### States

- Error summary (`collapsed`): Show a short summary of synthetic query failures.
- Diagnostic plan (`expanded`): Show all planned diagnostic lines.
- Cancellation (`cancelled`): Show a cancelled operation without labeling it a query failure.

## Renderer Chain

`renderer-chain` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/renderer-chain.md) · [JSON](https://pi-tui.ratstack.sh/patterns/renderer-chain.json) · also known as Renderer decoration

### Intent

Add tool rendering while preserving an existing renderer or fallback.

### Motivation

Tool Display preserves existing renderers unless its explicit override policy requests replacement.

### Applicability

- Use this when rendering extensions must compose rather than overwrite each other.

### Structure

```text
resolver -> next() -> prior renderer
prior / fallback -> component
```

### Participants

- [`pi.registerToolRenderer`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Composes tool-renderer resolution through next.
- [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders the call before or during execution.
- [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders partial or final tool output.
- `Prior renderer`: Provides output to preserve, wrap or fall back from.

Pi component APIs: [`pi.registerToolRenderer`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), `ToolRendererResolver`, `ToolExecutionComponent`, `Container`

### Consequences

- Current resolver composition can retain or wrap an existing renderer.
- The old registerTool interception and teardown strategy must not be copied as the default.

### Implementation

- Use the current resolver and next() rather than copying registerTool interception.
- The source adapter's teardown and override policy are historical implementation details.

### Known uses: seen in Nico's repos

- [**pi-tool-display**](https://github.com/nicobailon/pi-tool-display): Composable renderer adapters
  - [src/tool-overrides.ts:1500-1538](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/tool-overrides.ts#L1500-L1538) @fca8c858
  - [src/tool-overrides.ts:2040-2072](https://github.com/nicobailon/pi-tool-display/blob/fca8c858a0989b63eba18ab935f3d8ed78354c3a/src/tool-overrides.ts#L2040-L2072) @fca8c858

### Related

- [Call Capsule](https://pi-tui.ratstack.sh/patterns/call-capsule.md): retains per-execution renderer state

### States

- No existing renderer (`fallback`): Show a synthetic tool using the supplied fallback renderer.
- Existing renderer (`preserved`): Keep an earlier renderer's output intact.
- Decorated output (`decorated`): Wrap an existing renderer's component with a small extra label.

## Message Fold

`message-fold` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/message-fold.md) · [JSON](https://pi-tui.ratstack.sh/patterns/message-fold.json) · also known as Collapsible message

### Intent

Render custom message content as a preview with expanded detail.

### Motivation

Skill Palette injects context that needs an identifiable preview without filling the transcript.

### Applicability

- Use this when injected context or attachments should stay identifiable but compact.

### Structure

```text
custom message -> text blocks
text -> preview + count / full view
```

### Participants

- [`pi.registerMessageRenderer`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management): Renders a named custom-message type.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`wrapTextWithAnsi`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Wraps text while preserving styling across lines.
- `Text preview`: Retains an identity label and omitted-line count when collapsed.

Pi component APIs: [`pi.registerMessageRenderer`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#state-management), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`wrapTextWithAnsi`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `CustomMessageComponent`, `MessageRenderer`

### Consequences

- Named message content can stay compact until expanded.
- Collapsed output omits lines and the extractor ignores non-text blocks.

### Implementation

- Keep a remaining-line count when the preview is clipped.
- The skill-context extractor omits non-text blocks.

### Known uses: seen in Nico's repos

- [**pi-skill-palette**](https://github.com/nicobailon/pi-skill-palette): Render skill context as a collapsible message preview
  - [index.ts:850-887](https://github.com/nicobailon/pi-skill-palette/blob/a5c4429b8c2e33ab903d07856497014f3d5ad34e/index.ts#L850-L887) @a5c4429b
- [**pi-intercom**](https://github.com/nicobailon/pi-intercom): Cache message layout separately from live theme styling
  - [ui/inline-message.ts:10-125](https://github.com/nicobailon/pi-intercom/blob/a5fad4df2a9fe4909bf4d9b06263c8316976b57d/ui/inline-message.ts#L10-L125) @a5fad4df

### Related

- [Late Paint](https://pi-tui.ratstack.sh/patterns/late-paint.md): reuses wrapped message layout

### States

- Preview (`collapsed`): Show a named synthetic context block and a remaining-line count.
- Full message (`expanded`): Show its full text and synthetic attachment metadata.
- Non-text content (`non-text`): Show only supported text without fabricating content for a non-text block.

## Image Parachute

`image-parachute` · Presentation / Tool and message output · [Markdown](https://pi-tui.ratstack.sh/patterns/image-parachute.md) · [JSON](https://pi-tui.ratstack.sh/patterns/image-parachute.json) · also known as Inline image fallback

### Intent

Render terminal images where supported and text placeholders otherwise.

### Motivation

Nico's inline-image component encounters terminals and modes with different graphics support.

### Applicability

- Use this when custom output or staged previews contain images.

### Structure

```text
image + capabilities + mode
  supported -> image
  otherwise -> placeholder
```

### Participants

- [`Image`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#image): Renders an inline image or unsupported-terminal placeholder.
- [`detectCapabilities`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#image): Reports detected terminal graphics capabilities.
- `Renderer mode`: Restricts image presentation where protocol repainting is unsupported.

Pi component APIs: [`Image`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#image), [`detectCapabilities`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#image), `setCapabilities`, `TuiAltScreen`

### Consequences

- Supported terminals show images while other paths remain readable as text.
- Fallback paths cannot provide the same visual image detail.

### Implementation

- Capabilities depend on terminal protocol and renderer mode.
- Fullscreen iTerm2 uses placeholders rather than repainting inline image placements.
- Do not infer unchanged Ghostty heuristics from declarations alone.

### Known uses: seen in Nico's repos

- [**earendil-works/pi**](https://github.com/earendil-works/pi): Render inline terminal images
  - [packages/tui/src/components/image.ts:1-78](https://github.com/earendil-works/pi/blob/9e9d5c94ed4aad92876a15fab64e78573133695a/packages/tui/src/components/image.ts#L1-L78) @9e9d5c94
  - [packages/tui/src/terminal-image.ts:1-90](https://github.com/earendil-works/pi/blob/9e9d5c94ed4aad92876a15fab64e78573133695a/packages/tui/src/terminal-image.ts#L1-L90) @9e9d5c94
- [**earendil-works/pi**](https://github.com/earendil-works/pi): Detect Ghostty through tmux for inline images
  - [packages/tui/src/terminal-image.ts:36-57](https://github.com/earendil-works/pi/blob/e904b11e7b4fb97105712a951ffb72b2572df69f/packages/tui/src/terminal-image.ts#L36-L57) @e904b11e
- [**dot314**](https://github.com/nicobailon/dot314): Select and preview multiple screenshots in a custom picker
  - [extensions/screenshots-picker/index.ts:800-830](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/screenshots-picker/index.ts#L800-L830) @17cce138

### Related

- [Preview Basket](https://pi-tui.ratstack.sh/patterns/preview-basket.md): uses image-backed staged inspection

### States

- Kitty PNG (`supported`): Show a generated synthetic thumbnail under supported Kitty capabilities.
- iTerm2 PNG (`iterm`): Show the same thumbnail through the inline iTerm2 image protocol.
- Text fallback (`unsupported`): Show a meaningful placeholder when image support is absent.
- Fullscreen iTerm2 (`iterm-fullscreen`): In fullscreen, Pi switches iTerm2 images to the text fallback and restores them when fullscreen ends.

## Colour Sentry

`colour-sentry` · Presentation / Theming · [Markdown](https://pi-tui.ratstack.sh/patterns/colour-sentry.md) · [JSON](https://pi-tui.ratstack.sh/patterns/colour-sentry.json) · also known as Validated theme colours

### Intent

Validate colour overrides before using them in themed terminal output.

### Motivation

Powerline Footer normalizes user colour overrides before emitting terminal output.

### Applicability

- Use this when an extension exposes configurable appearance.

### Structure

```text
override -> validate / parse
valid colour + theme -> styled text
```

### Participants

- [`parseColor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#colors-and-terminal-styles): Converts supported colour values into a Color.
- [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly): Styles text through semantic or concrete colours.
- `Override validator`: Rejects invalid values before they become terminal styles.

Pi component APIs: [`parseColor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#colors-and-terminal-styles), [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly), `Text`

### Consequences

- Validated colours can flow through the current theme helpers.
- Invalid overrides need rejection and reused colour math belongs outside rendering.

### Implementation

- Reject invalid colour values before emitting ANSI output.
- Apply the supplied theme rather than fixed ANSI colours.
- Compute reusable concrete colours outside the render path.

### Known uses: seen in Nico's repos

- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Theme configuration and sanitization
  - [theme.ts:1-50](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/theme.ts#L1-L50) @859dee67

### Related

- [Palette Deck](https://pi-tui.ratstack.sh/patterns/palette-deck.md): supplies coordinated override values

### States

- Active theme (`default`): Show synthetic labels styled by active semantic tokens.
- Valid override (`override`): Apply a validated colour override through theme.style.
- Invalid override (`invalid`): Show an explicit rejected override and unchanged output.
- Don't: fixed ANSI (`dont-fixed-ansi`): Render a fixed ANSI-colour label instead of resolving the active theme. Counter-example; fails hard-coded-colour on purpose.

## Palette Deck

`palette-deck` · Presentation / Theming · [Markdown](https://pi-tui.ratstack.sh/patterns/palette-deck.md) · [JSON](https://pi-tui.ratstack.sh/patterns/palette-deck.json) · also known as Colour presets

### Intent

Centralize coordinated colour choices behind named presets and a default.

### Motivation

Powerline Footer centralizes segment colours in named presets with a default lookup.

### Applicability

- Use this when several footer segments share an appearance configuration.

### Structure

```text
preset name -> palette / default
palette -> coordinated segments
```

### Participants

- [`parseColor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#colors-and-terminal-styles): Converts supported colour values into a Color.
- [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly): Styles text through semantic or concrete colours.
- `Default palette`: Handles unknown names with an explicit fallback.

Pi component APIs: [`parseColor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/tui/README.md#colors-and-terminal-styles), [`theme.style`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#apply-themes-correctly)

### Consequences

- Segments can share one coordinated appearance selection.
- Unknown preset names require an explicit fallback.

### Implementation

- Define a fallback for unknown preset names.
- Resolve preset values through the current colour and theme contracts.

### Known uses: seen in Nico's repos

- [**pi-powerline-footer**](https://github.com/nicobailon/pi-powerline-footer): Preset-driven segment colors
  - [presets.ts:1-45](https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/presets.ts#L1-L45) @859dee67

### Related

- [Colour Sentry](https://pi-tui.ratstack.sh/patterns/colour-sentry.md): validates values before styling

### States

- Default preset (`default`): Show coordinated synthetic segments using the default preset.
- Alternate preset (`alternate`): Choose another named preset for the same segments.
- Unknown name (`unknown`): Use the declared fallback when the preset name is unknown.

## Snapshot Lens

`snapshot-lens` · Presentation / Lifecycle and mounting · [Markdown](https://pi-tui.ratstack.sh/patterns/snapshot-lens.md) · [JSON](https://pi-tui.ratstack.sh/patterns/snapshot-lens.json) · also known as Snapshot status projection

### Intent

Derive compact status and bounded detail from lifecycle snapshots.

### Motivation

Subagents derives workflow widgets from job snapshots, while Dot314 projects lifecycle data into external sidebar slots.

### Applicability

- Use this when several job or workflow states need a coherent display.

### Structure

```text
lifecycle / jobs -> snapshot
snapshot -> status + widget
```

### Participants

- [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Writes or clears one named footer slot.
- [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Mounts, replaces or clears one near-editor widget.
- [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events): Registers ordered extension event handlers.
- `Job snapshot`: Supplies lifecycle facts without being mutated by rendering.

Pi screen APIs: [`ctx.ui.setStatus`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setWidget`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`pi.on`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events), `wrapTextWithAnsi`

### Consequences

- Status and detail can reflect one coherent snapshot.
- Dimension changes and external status effects remain separate concerns.

### Implementation

- Separate projection from mutations and external status effects.
- Invalidate dimension-dependent layout when identity or coverage changes.
- The cmux example is an external sidebar effect rather than a native Pi mount.

### Known uses: seen in Nico's repos

- [**pi-subagents**](https://github.com/nicobailon/pi-subagents): Project workflow state before rendering status widgets
  - [src/tui/render.ts:2520-2585](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/render.ts#L2520-L2585) @6826b054
  - [src/tui/render.ts:3060-3110](https://github.com/nicobailon/pi-subagents/blob/6826b0545216077195ae8ffe468a6434715814b7/src/tui/render.ts#L3060-L3110) @6826b054
- [**dot314**](https://github.com/nicobailon/dot314): Project agent lifecycle into cmux status slots
  - [extensions/cmux/index.ts:60-89](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/cmux/index.ts#L60-L89) @17cce138

### Related

- [Signal Pair](https://pi-tui.ratstack.sh/patterns/signal-pair.md): provides the two display surfaces

### States

- Idle snapshot (`idle`): Project synthetic idle jobs into a compact signal.
- Workflow snapshot (`running`): Show a phase, nested jobs and bounded widget rows.
- Lifecycle ends (`ended`): Update the completed snapshot and clear owned lifecycle status.
