# Spinner catalog

Spinners come from the [`unicode-animations`](https://www.npmjs.com/package/unicode-animations)
npm package. On every state entry, a random spinner is picked from that
state's pool. Below is the per-state mapping; for the full frame data
of each spinner, see the package's docs/demo. The kaomoji shown
alongside the spinner is re-rolled every 30 s while a state
lasts (see `KAOMOJI_REROLL_MS` in `index.ts`); the spinner itself only
changes on state entry.

- npm: https://www.npmjs.com/package/unicode-animations
- Local demo: run `npx unicode-animations --web` (auto-generates a
  `spinners/` folder with `demo.html` in the cwd)

## Per-state pools

| State | Pool | Notes |
|---|---|---|
| `thinking` / `toolRunning` (shared) | `rain`, `breathe`, `checkerboard`, `sparkle`, `braille`, `snake` | 6 options (shared by both states) |
| `searching` | `diagswipe`, `scan`, `cascade` | 3 options — all visually distinct scanning motions |
| `editing` | `columns`, `fillsweep` | 2 options — careful vs. filling |
| `error` | `pulse` | 1 option — sole spinner for error state |
| `idle` / `startup` / `complete` | — | silent states, no spinner |

To preview any spinner in isolation, run `npx unicode-animations` — the
postinstall shows a grid of all available spinners animating in your
terminal.

## How to add or swap a spinner

Edit the matching entry in `SPINNER_POOL_FOR_STATE` in `index.ts`. For
example, to give `error` more variety:

```ts
error: ["pulse", "snake", "checkerboard"],
```

Any name that's a key in `unicode-animations`'s `spinners` object will
work. The TypeScript `SpinnerName` type is `keyof typeof spinners`, so
the compiler will catch typos. Note: `thinking` and `toolRunning` share
`THINKING_TOOL_SPINNER_POOL`; edit that constant to change the shared
pool (both states use it).

## Minimum state TTL (3 s)

Once a state is entered, it persists for at least 3 s. This prevents
flicker (e.g. an error being immediately clobbered by the next
`turn_start`). `error` has priority and always wins, even within the
3 s window. The TTL applies to *state changes* — within a state, the
spinner can still cycle freely between frames at its own interval.

See `index.ts` for `STATE_TTL_MS` and `STATES_WITH_TTL` if you want to
tune this.

## Notes on VS Code rendering

The unicode-animations spinners are all multi-codepoint braille patterns
(some are 2-4 cells wide). They render correctly in terminals that
support grapheme clustering (most modern terminals, including VS Code's
integrated terminal on recent versions). If you see alignment issues,
your terminal is treating each codepoint as a separate cell — try a
different terminal or fall back to a single-codepoint spinner (you'd
need to define your own since the package is all multi-codepoint braille).
