# pi-sticky

> **Fully vibe-coded.** Might break. Works for me.

Keeps the chat input and footer pinned to the bottom of the terminal while you scroll through chat history.

Works alongside your other extensions — it does not replace the editor or footer, it pins whatever is already registered.

## What it does

Uses a terminal scroll-region technique (vendored from `pi-powerline-footer`) to split the terminal into two zones:

- **Scrollable zone** — chat history
- **Pinned zone** — status indicator, editor (e.g. from pi-vim), widgets, footer (e.g. from pi-status)

Mouse scroll and `super+up` / `super+down` (or Page Up / Page Down) move through chat history without the input area disappearing.

## Installation

```
pi install https://github.com/burneikis/pi-sticky
```

Then add to your `~/.pi/agent/settings.json`:

```json
{
  "extensions": [
    "~/.pi/agent/extensions/pi-sticky",
    "~/.pi/agent/extensions/pi-vim",
    "~/.pi/agent/extensions/pi-status"
  ]
}
```

### Load order is required

**pi-sticky must be listed first.** It wraps `ctx.ui.setEditorComponent` and `ctx.ui.setFooter` before other extensions call them. If pi-vim or pi-status loads first, their registrations will not be intercepted and the sticky behavior will not work.

## Compatibility

| Extension | Works? | Notes |
|---|---|---|
| pi-vim | Yes | Editor is pinned as-is, vim modes fully preserved |
| pi-status | Yes | Footer is pinned at the very bottom |
| pi-powerline-footer | No | Both extensions patch the same terminal primitives; use one or the other |
| Any `setWidget` extension | Yes | Widgets above/below editor are pinned alongside the editor |

## Configuration

No configuration required. The extension works with sensible defaults:

- Mouse scroll enabled
- Keyboard scroll: `super+up` / `super+down`, `PageUp` / `PageDown`, `ctrl+shift+up` / `ctrl+shift+down`
- Mouse text selection and copy-to-clipboard on release

## Credits

The `fixed-editor/` directory is vendored from [pi-powerline-footer](https://github.com/nicobailon/pi-powerline-footer) by Nico Bailon (MIT licensed).

## How it works (technical)

All extensions registered in the same session share a single `ctx` object (the extension runner creates it once and passes it to every `session_start` handler). pi-sticky uses this to intercept `ctx.ui.setEditorComponent` and `ctx.ui.setFooter` in its own `session_start` before other extensions run theirs.

The interception wraps each factory to capture:
- The `tui` reference (the TUI instance, from the factory's first argument)
- The editor renderable (the object returned by the editor factory)

Compositor installation is deferred via `setTimeout(0)` so it runs after **all** extensions' `session_start` handlers complete. This ensures the footer (registered by pi-status) is already in `tui.children` when the compositor searches for it positionally.

The pinned layout from top to bottom:

```
statusContainer     (loading / thinking animation)
widgetContainerAbove (extensions using setWidget "aboveEditor")
editorContainer     (the chat input)
widgetContainerBelow (extensions using setWidget "belowEditor")
footer              (pi-status or built-in footer)
```

These elements are hidden from the scrollable TUI render and re-rendered explicitly in the pinned cluster on every frame.
