---
slug: "pi-neon-editor"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/CikeSeven/pi-neon-editor@main/README.md"
repo: "https://github.com/CikeSeven/pi-neon-editor"
source_file: "README.md"
branch: "main"
---
# pi-neon-editor

Animated flowing neon border and optional keyword glow for the [pi](https://github.com/earendil-works/pi) input editor.

This package replaces the default editor component with a `CustomEditor` subclass that re-renders the editor border as an animated truecolor gradient. It also adds an optional keyword shine while you type.

## Installation

From npm:

```bash
pi install npm:pi-neon-editor
```

From git:

```bash
pi install git:github.com/CikeSeven/pi-neon-editor
```

Or try it without installing:

```bash
pi -e npm:pi-neon-editor
```

Manual (development): clone or copy this repository into pi's global extension directory and `/reload`:

```text
~/.pi/agent/extensions/neon-editor/
```

## Features

- Flowing rainbow border with a moving glow highlight
- Border presets: `neon`, `ocean`, `sunset`, `matrix`, `ember`, `violet` — each defines gradient colors plus an `accent` color used by every glow/highlight effect (no hard-coded white)
- Render modes: `flow`, `pulse`, `static`, `swing`
- Adjustable animation speed and glow strength
- Adjustable border size: thickness (1-4 rows), inner padding, six line glyphs
- Full frame mode: gradient side borders with rounded corners (`/neon frame on`)
- End caps ornaments for the horizontal borders (`/neon caps block|diamond|angle`)
- Custom background panel: preset tint, solid color, or a dark flowing gradient
- Optional keyword glow, e.g. make `ultrathink` shine while typing
- Reactive effects linked to your workflow: typing ripples, send flash, done pulse
- Persistent user config at `~/.pi/agent/neon-editor.json`
- Restores the previous editor component when disabled

## Usage

Reload pi or restart it:

```text
/reload
```

Then control it with:

```text
/neon                    Open the interactive settings menu (TUI mode)
/neon status             Show current status as a notification
/neon on                 Enable neon editor
/neon off                Disable and restore the previous editor
/neon preset ocean       Switch palette preset
/neon mode pulse         Switch render mode: flow | pulse | static | swing
/neon working surge      Working indicator style: comet | surge
/neon speed 120          Set frame interval in ms, range 40-300
/neon glow 70            Set glow strength, range 0-100
/neon thickness 2        Border height in rows, range 1-4
/neon pad 1              Blank lines between border and text, range 0-3
/neon glyph heavy        Border line glyph: light | heavy | double | dashed | dotted | mixed
/neon frame on           Full frame: gradient side borders + rounded corners
/neon margin 3           Frame inner margin (columns), range 1-4
/neon caps block         End caps on the horizontal borders: none | block | diamond | angle
/neon bg gradient        Background: none | tint | solid | gradient
/neon bgboost 20         Background brightness, range 5-60 (tint/gradient)
/neon bgbase 250,250,252 Background mix base: r,g,b | auto (auto-detects the terminal background)
/neon fx send off        Toggle a reactive effect: typing | send | done | working
/neon typingpause 800    Freeze the border while typing (IME anti-flicker), range 0-5000, 0 = off
/neon keyword ultrathink Highlight a keyword while typing
/neon keyword            Clear keyword highlight
/neon reset              Reset config to defaults
```

## Interactive menu

Running `/neon` with no arguments opens a settings menu built from pi's
dialog components. Every entry shows its current value; press Enter to
change it and Esc to close the menu:

```text
neon-editor · enter to edit, esc to close
  Turn off
  Preset — neon          → picker: neon / ocean / sunset / matrix / ember / violet
  Mode — flow            → picker: flow / pulse / static / swing
  Speed — 70ms           → number input (40-300)
  Glow — 70              → number input (0-100)
  Thickness — 1          → picker: 1-4
  Pad — 0                → picker: 0-3
  Glyph — light          → picker: light / heavy / double
  Keyword — -            → text input (empty clears)
  Effects — typing+send+done → toggle each reactive effect
  Working style — comet    → picker: comet / surge
  Reset to defaults
```

Changes apply immediately and are persisted to the config file. All
subcommands (`/neon preset ocean` etc.) remain available for scripting and
quick edits; `/neon status` prints a one-line summary.

## Presets

Each preset bundles two parameters:

| Preset | Gradient `colors` | `accent` (glow spot + all reactive effects) |
| --- | --- | --- |
| `neon` | rainbow | bright pink `[255,179,222]` |
| `ocean` | blues | bright cyan `[224,247,250]` |
| `sunset` | reds/oranges | bright peach `[255,214,165]` |
| `matrix` | greens | bright green `[190,255,190]` |
| `ember` | fiery oranges | bright amber `[255,236,179]` |
| `violet` | purples | bright lavender `[234,204,255]` |

The flow/pulse/static/swing glow spot, the typing ripple, the send flash,
the done pulse, and the working comet all brighten **toward the preset's
`accent`**, never toward a fixed white. Switching presets recolors every
effect consistently. To tune a preset, edit `PRESETS` in `index.ts`.

## Frame and caps

`/neon frame on` turns the box into a full rectangle: the top/bottom borders
gain corners and every content row gets gradient side borders that flow with
the animation. `/neon margin <1-4>` (default 2) controls the inner horizontal
padding so text keeps a comfortable distance from the sides; the padding is
removed again when the frame is turned off.
Corner/vertical glyphs follow the current line glyph:

| Glyph | Horizontal | Vertical | Corners |
| --- | --- | --- | --- |
| `light` | ─ | │ | ╭ ╮ ╰ ╯ |
| `heavy` | ━ | ┃ | ┏ ┓ ┗ ┛ |
| `double` | ═ | ║ | ╔ ╗ ╚ ╝ |
| `dashed` | ┄ | ┆ | ╭ ╮ ╰ ╯ |
| `dotted` | ┈ | ┊ | ╭ ╮ ╰ ╯ |
| `mixed` | ─═ alternating | │║ alternating | ┏ ┓ ┗ ┛ |

`/neon caps <style>` decorates the two ends of the horizontal borders when
the frame is **off** (frame corners take precedence when it is on):

- `block` — `◢…◣` on top, `◥…◤` on the bottom
- `diamond` — `◆…◆` on both
- `angle` — `⟨…⟩` on both
- `none` — plain ends (default)

## Background

`/neon bg <mode>` paints a background panel inside the box (borders included;
the autocomplete dropdown is left alone):

- `none` — default terminal background
- `tint` — a soft tint of the current preset's accent color
- `gradient` — a muted copy of the border gradient flowing across the box;
  typing ripples and send/done flashes shimmer through the backdrop
- `solid` — a fixed custom color from `"bgColor": [r, g, b]` in the config
  file (falls back to `tint` when unset)

`/neon bgboost <5-60>` scales the strength of `tint`/`gradient` (default 15%).
The painter preserves the editor's own styling: cursor and selection keep
their inverse/highlight rendering on top of the background.

### Background base color

`tint`/`gradient` colors are mixed from a base color towards the preset
colors. By default the base is **auto-detected from the terminal's real
background** (an OSC 11 query at session start), so the panel blends into
both dark and light terminal themes — on a light terminal the panel is a
light tint, not a black slab. Terminals that don't answer the query fall
back to a black base (the pre-1.4 behavior).

Pin an explicit base with `/neon bgbase <r,g,b>` (or `"bgBase": [r, g, b]`
in the config file); `/neon bgbase auto` restores auto-detection.

**Terminal background images / transparency:** OSC 11 reports only the color
scheme's solid background color — never the image — and SGR backgrounds have
no alpha channel, so any painted `bg` mode covers the wallpaper with an
opaque panel. Use `bg none` to keep background images and acrylic
transparency visible inside the box.

## Custom presets

You can define your own presets in the config file — no code changes
needed. Add a `presets` object to `~/.pi/agent/neon-editor.json`:

```json
{
  "preset": "sakura",
  "presets": {
    "sakura": {
      "colors": [[255, 183, 197], [255, 133, 162], [219, 68, 116], [147, 20, 68]],
      "accent": [255, 224, 232]
    },
    "toxic": {
      "colors": [[57, 255, 20], [0, 143, 17]]
    }
  }
}
```

Rules:

- `colors`: one or more `[r, g, b]` triples (0-255). The border gradient
  interpolates through them in order.
- `accent`: optional `[r, g, b]`. Used by the glow spot and every reactive
  effect (typing ripple, send flash, done pulse, working comet). If omitted,
  the brightest entry of `colors` is used.
- Names must match `^[a-z0-9][a-z0-9_-]{0,23}$` (case-insensitive) and are
  lowercased. Invalid entries are silently ignored.
- A custom preset with the same name as a built-in **overrides** it.
- Custom presets show up in `/neon preset <name>`, the interactive menu's
  preset picker, and survive config saves. `/reload` (or a `/neon` command
  that rewrites the config) picks up edits.

## Render modes

| Mode | Look |
| --- | --- |
| `flow` | The gradient streams along the border in one direction, with a glow highlight sweeping through. |
| `pulse` | The gradient stays put while the whole border breathes in brightness. |
| `static` | Frozen gradient with a fixed glow highlight at the center. No motion. |
| `swing` | The glow highlight oscillates left-right between the two ends (triangle wave), and the gradient phase ping-pongs with it. |

## IME composition flicker (Windows Terminal)

Both borders animate every frame, and pi's line differ repaints the whole
contiguous range between the first and last changed line — so the editor's
text rows are rewritten on every animation frame even when their content is
unchanged. Terminals that draw the IME composition string inline over the
grid (notably Windows Terminal) repaint that composition on each rewrite,
which shows up as the composition letters flickering while you type with an
IME (e.g. Chinese pinyin input).

`/neon typingpause <ms>` mitigates this: after any keystroke, the animation
timer stops requesting repaints until you have been idle for `<ms>`
milliseconds. Typed text still updates instantly (pi repaints real input
edits itself); only the border animation freezes while you are composing,
then resumes when you pause. Recommended: `800`. Set `0` (default) to keep
the animation always running. Note the trade-off: the typing ripple effect
needs per-keystroke repaints, so it stays hidden while the guard is active.

## Reactive effects

The border reacts to what is happening in the session. Each effect can be
toggled independently with `/neon fx <typing|send|done> <on|off>` or from
the interactive menu (`Effects — ...` entry):

| Effect | Trigger | What it looks like |
| --- | --- | --- |
| `typing` | Every keystroke in the editor (via the editor's `handleInput`) | A bright core flashes on the border at the cursor's column, then a wavefront ring expands outward along the border, fading over ~18 frames (~1.3s at the default 70ms interval). |
| `send` | pi's `input` event (you submit a prompt) | The whole border flashes bright once, fading over ~12 frames. |
| `done` | pi's `agent_end` event (the agent finishes generating) | The border pulses three times, decaying over ~36 frames — a subtle "I'm done" signal. |
| `working` | Between pi's `agent_start` and `agent_end` events | An indicator runs inside the border while the agent is generating; style is configurable (see below). Stops the moment the agent finishes. |

**Working indicator styles** (`/neon working <comet|surge>`, default `comet`):

- `comet` — a bright accent-colored comet with a fading trail ping-pongs
  fast between the two ends of the border (9 columns per frame).
- `surge` — no accent highlight; the raw preset gradient itself sloshes
  left-right at the same pace. Pure palette motion, no extra color.

Effects only fire while neon-editor is enabled and rendering in TUI mode.
They are layered on top of the current render mode (`flow`/`pulse`/`static`/`swing`)
via `Math.max` blending, so they never fight the base animation.

## Border size

Terminals cannot make a single line physically taller, so "border size" is
controlled by three independent settings:

```text
┌─ thickness ─┐  ┌─ pad ─┐

───────────────  ┐
───────────────  ┘ thickness 2: N border rows, directly adjacent
                 ┐
 (blank line)    ┘ pad 1: blank rows between border and text
 your text here
 (blank line)    ┐
                 ┘ pad applied again above the bottom border
───────────────  ┐
───────────────  ┘ bottom border rows
```

| Setting | Command | Range | Default | What it controls |
| --- | --- | --- | --- | --- |
| `thickness` | `/neon thickness <1-4>` | 1-4 rows | 1 | Border height. The rows are rendered **directly adjacent** to each other, forming one solid thick border. There is no gap between them. |
| `padY` | `/neon pad <0-3>` | 0-3 rows | 0 | Inner padding: blank rows inserted **between the border and the text** (like CSS padding). Applied to both top and bottom. `0` means the text touches the border rows directly. |
| `glyph` | `/neon glyph <...>` | 6 styles | light | Line glyph of the border characters: `light` = `─`, `heavy` = `━`, `double` = `═`, `dashed` = `┄`, `dotted` = `┈`, `mixed` = alternating `─═`. Purely visual; takes no extra rows. |

Example — a visibly chunky, roomy frame:

```text
/neon thickness 2
/neon pad 1
/neon glyph heavy
```

Back to a slim single-line frame:

```text
/neon thickness 1
/neon pad 0
/neon glyph light
```

## Config

Runtime settings are stored in:

```text
~/.pi/agent/neon-editor.json
```

Example:

```json
{
  "enabled": true,
  "preset": "neon",
  "mode": "flow",
  "intervalMs": 70,
  "glow": 70,
  "keyword": "",
  "thickness": 1,
  "padY": 0,
  "glyph": "light",
  "frame": false,
  "caps": "none",
  "margin": 2,
  "bg": "none",
  "bgColor": null,
  "bgBase": null,
  "bgStrength": 15,
  "fx": { "typing": true, "send": true, "done": true, "working": true },
  "workingStyle": "comet",
  "typingPauseMs": 0,
  "presets": {}
}
```

## Install as a pi package

If you move this directory out of `~/.pi/agent/extensions`, install it explicitly:

```bash
pi install /absolute/path/to/neon-editor
```

Do not install the same directory while it still lives under `~/.pi/agent/extensions`, or pi may load it twice.

## Development

Quick syntax/load check:

```bash
pi -e ~/.pi/agent/extensions/neon-editor --version
```

Run pi with a temporary PTY to inspect the animated border:

```bash
script -qec "pi -e ~/.pi/agent/extensions/neon-editor" /tmp/neon-editor.log
```

## Notes

- The glow is simulated with ANSI truecolor brightness; terminals cannot render real blur/bloom.
- Animation works by calling `tui.requestRender()` on a timer. Lower `speed` values look smoother but redraw more often.
- If another extension also replaces the editor component, load order matters. `/neon off` restores the editor factory that was active before neon-editor was enabled.

## License

MIT — see [LICENSE](https://github.com/CikeSeven/pi-neon-editor/tree/HEAD/LICENSE).
