@nicknisi/pi-header

Animated header with custom frames for the pi TUI

Packages

Package details

extension

Install @nicknisi/pi-header from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@nicknisi/pi-header
Package
@nicknisi/pi-header
Version
0.1.7
Published
Aug 15, 2026
Downloads
480/mo · 480/wk
Author
nicknisi
License
MIT
Types
extension
Size
583.8 KB
Dependencies
1 dependency · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@nicknisi/pi-header

Replaces pi's built-in TUI header with an animated nicknisi avatar sitting top-left (Claude Code / vim dashboard style), with session info beside it: pi version + model, cwd + git branch, and a random quote typed out character by character. Shown only on fresh sessions; the built-in header is restored the moment the first prompt is sent — like the vim dashboard disappearing when you get to work.

Install

pi install /Users/nicknisi/Developer/pi-extensions/packages/nicknisi-header

What it adds

Commands

Command Description
/nicknisi-header Cycle the header style: blinkwaitingfullcompact. Re-renders live if the dashboard is currently up.
/nicknisi-header-size [small|medium|large] Set the GIF size. No argument cycles through the sizes. Persisted to the config file. Only affects blink/waiting modes; the ASCII art modes are fixed-size.

Modes

  • blink (default) — nicknisi-blink.gif as truecolor half-block frames: a close-up blink loop. Picks quotes from BLINK_QUOTES.
  • waitingnick-waiting.gif: a Sonic-style waiting animation (waits 5s, taps its foot, loops). Picks quotes from WAITING_QUOTES.
  • full — the nicknisi ASCII art from the Neovim dashboard (config/nvim/lua/nisi/assets.lua, ascii.nicknisi), one character per pixel, theme-colored, with randomly-timed blinking eyes.
  • compact — half-block (/) render of the same ASCII art; half the height, muddier.

Events hooked

Event Behavior
session_start If ctx.mode === "tui" and the session has no messages (i.e. not a resume), installs the dashboard via ctx.ui.setHeader(...).
before_agent_start First prompt sent → stops the animation timer and calls ctx.ui.setHeader(undefined) to restore the built-in header.
session_shutdown Stops the animation timer.

UI

A custom header component (Component from @earendil-works/pi-tui) driven by a 50 ms setInterval that advances GIF frames (per-frame delays from the source GIF), runs the eye-blink state machine for ASCII modes, and types out the quote. Calls headerComp.invalidate() + tui.requestRender() only when dirty.

The info column (vertically centered against the art) shows:

pi  v<VERSION> · <provider>/<model>
~/path/to/cwd  ·  <git-branch>
<quote typed char-by-char>▌

Branch is read via git branch --show-current with a 2s timeout; missing branch/repo degrades to just the directory.

Configuration

~/.pi/agent/nicknisi-header.json

Read at extension load, written by /nicknisi-header-size.

{
  "size": "medium"
}
Key Type Default Description
size "small" | "medium" | "large" "medium" GIF render size for blink/waiting modes. Roughly 10 / 13–14 / 15–18 terminal rows.

Write failures are swallowed (non-fatal: size just won't persist). Read failures fall back to {}.

There is no config for the mode itself — mode starts at blink every session and is only changed via /nicknisi-header.

Environment variables

None.

Dependencies

  • @earendil-works/pi-coding-agent (peer) — ExtensionAPI, ExtensionContext, Theme, VERSION, getAgentDir().
  • @earendil-works/pi-tui (peer) — Component type for the custom header.
  • @nicknisi/pi-shared (workspace) — formatDirectory(): collapses $HOME to ~ and strips terminal escape sequences from the path before rendering.
  • No runtime npm deps. Node builtins used: node:child_process (execFileSync for git), node:fs, node:path.

Frame generation (gen-frames.mjs)

The frames-*.ts files are generated data — do not edit by hand. Regenerate from source GIFs with:

node gen-frames.mjs <input.gif> <output.ts> [cols] [pxHeight]

Requires ImageMagick (magick) on PATH. The script coalesces the GIF (compositing delta-optimized frames), resizes to cols x pxHeight pixels, and emits palette-indexed half-block frames:

  • COLORS: [r,g,b][] — index 0 reserved for transparent
  • CELLS: [fgIdx, bgIdx][] — index 0 reserved for a space; each terminal cell is two vertical pixels ( with fg=top/bg=bottom, or for bottom-only)
  • FRAMES: { delay, lines }[] — lines are arrays of "cellIdx" or RLE "cellIdx*run" tokens; per-frame delay in ms

Choose a square-ish pixel grid ≈ correct terminal aspect ratio (tuned for Monaspace Neon with adjust-cell-height=10% in ghostty). index.ts decodes and caches frames lazily with memoized escape sequences.

Caveats

  • Depends on ctx.ui.setHeader() accepting a component factory and on before_agent_start firing before the first turn — both are pi internals that could change across versions. If setHeader semantics change, the restore-on-first-prompt behavior breaks first.
  • Truecolor rendering assumes a terminal with 24-bit color support; the theme-based ASCII modes (full/compact) fall back gracefully to theme colors, but GIF modes will look wrong on 256-color terminals.
  • Aspect ratio of GIF modes is baked in at generation time for Monaspace Neon + ghostty adjust-cell-height=10%; other fonts/cell heights will distort the image.
  • Animation uses a single 50 ms interval while the dashboard is visible; it's stopped on first prompt and on shutdown, but if pi gains a way to send a prompt without before_agent_start firing, the timer could leak for the session.
  • git branch --show-current runs synchronously at dashboard render time with a 2s timeout; on a very slow filesystem this briefly blocks the session_start handler.
  • bgAnsi() converts theme fg escapes to bg escapes via a string replace of "38;""48;" — depends on pi's theme producing standard SGR escapes.
  • All quotes and the ASCII art are hardcoded; changing them requires editing index.ts.