@inobit/pi-reader
v0.3.2
Published
Vim-style reading mode for Pi fullscreen: toggle-once half/page scroll (ctrl-u/d/f/b, gg/G, j/k) with emacs-safe modal editor.
Maintainers
Readme
@inobit/pi-reader
English | 中文
Vim-style reading mode for Pi fullscreen: press the toggle key to enter read-only scrolling with ctrl-u/d/f/b, gg/G, j/k, plus semantic jumps [q/a/t, paragraph {} and search //n/N; your reading position is preserved across mode toggles and manual expand/collapse (prompt-ordinal anchoring), and exit restores emacs editing exactly as you left it.
- Single-key toggle:
alt+o(intercepted viaTUI inputListener, remappable viatoggleKeyinextensions/pi-reader/config.json) →READING. Tool output state is left untouched by default (autoExpandTools: trueopts into auto expand/collapse on toggle) - Position anchoring: mode toggles and manual expand/collapse pin the viewport to the Q/A you were reading — anchored in the
OSC133prompt-ordinal coordinate system (expand/collapse never adds/removes message boundaries, so ordinals are strictly stable); when collapsed content is shorter than one viewport it pins to the last page with the anchored line still on screen - Zero-intrusion editing: fully passthrough in
INSERT, keys intercepted only inREADING;ctrl+u = deleteToLineStartby default with zero regressions - Pixel-perfect Pi scrolling:
half = viewportHeight/2,page = viewportHeight-1(OVERLAP=1), matchingTuiAltScreen - Semantic navigation:
[q/]qquestion,[a/]aanswer,[t/]ttool,{/}paragraph,/search withn/N(vim-style afterEnter) - Robust event routing: keys go through
TUI inputListener— reading mode swallows keys,INSERTpasses through;ctxis refreshed across multi-session events soresumeon old sessions works - Dialog coexistence: when an extension dialog (e.g. a permission ask) pops up while reading, the reader yields every key except its toggle key — dialogs stay fully operable (
arrows/Enter/Esc, chained reason inputs included); the toggle key itself is blocked to prevent the container rebuild that would hang the dialog's promise. If the?help overlay was open under the dialog, help stays logically topmost: it swallows all keys untilEsccloses it, then control hands over to the dialog
Installation
pi install npm:@inobit/pi-readerLocal dev (isolated, --no-extensions excludes installed old version):
pi -ne -e ./packages/pi-reader --tui-mode fullscreenOnly
fullscreenis scrollable; inregularmodescrollByhas no viewport and the extension silently ignores it.
Key Bindings
| Action | Key | Notes |
| --- | --- | --- |
| Toggle reading | alt+o / /reader / /scroll (toggle) | Default alt+o, remappable via toggleKey in config.json (e.g. ctrl+o); effective key is shown in the ? popup |
| Exit | esc / i / ctrl+c | esc/i/ctrl+c in reading mode (ctrl+c does not clear screen), i does not leak into input |
| Help | ? | Only in READING — shows English shortcut reference, esc to close |
| Half page up / down | ctrl+u / ctrl+d | scrollBy(∓half); ctrl+u still deletes to line start in edit mode; count prefix e.g. 3 ctrl+u |
| Page down / up | ctrl+f / ctrl+b | scrollBy(±page); with count |
| Line down / up | j / k + ctrl+n / ctrl+p | scrollBy(±1); with count e.g. 5j |
| Top | g g | Double g within 300ms (including batched gg) → scrollToTop() |
| Bottom | G (shift+g) | scrollToBottom(), follows output |
| Prev / next question | [q / ]q | OSC133;A prompt rows; count prefix e.g. 3]q; flash Question 2/5; visibleBehavior=keep keeps viewport if already visible |
| Prev / next answer | [a / ]a | First non-empty line after prompt; count supported |
| Prev / next tool | [t / ]t | Heuristic ▌/⎿/● etc.; count supported |
| Prev / next paragraph | { / } | Blank-line separated; count e.g. 2} |
| Search | / then n / N | / enters search input (self-contained, flash echoes the query + n/m match count); while typing, every printable key (incl. j/k/n) is part of the query; Enter commits, n next / N prev cycle through matches |
| Count prefix | 1-9 (0 after) | Accumulates up to 4 digits, 800ms timeout, applies to j/k, half/page, [q/a/t, {} |
| Expand / collapse tool output | app.tools.expand (default ctrl+o, remappable in keybindings.json, e.g. alt+o) | Works in both edit and READING mode; inside READING its priority is below toggle/exit/help — don't bind it to the same key as toggleKey |
Configuration
- Reading toggle:
extensions/pi-reader/config.json{ "toggleKey": "alt+o", "autoExpandTools": false, "questionAnchor": "pinTop", "visibleBehavior": "keep", "wrapNavigation": false }autoExpandTools:false(default) keeps tool output state untouched across toggles — position is then naturally lossless;trueopts into auto expand/collapse on toggle (position compensated via the anchor). Others:questionAnchor:pinTop(=1, default) |third(=floor(vh/3)) |center(=floor(vh/2)) |number;visibleBehavior:keep(default, keep viewport if target already visible, flash only) |reanchor;wrapNavigation: wrap at ends.?popup shows the effective toggle key.
Behavior
- Read-only: printable keys are swallowed in reading mode (
INSERTpasses through), the input bar is hidden behind a left-aligned◉ Readingoverlay (borderless, fully covers the original position), original input is preserved and restored on exit - Anchoring: semantic jumps compute
row - offset(offset byquestionAnchor) clamped tomaxTopwithdisableFollow:true; visible targets withkeepstay in place and flashQuestion 2/5instead of scrolling - Indicator:
?in READING shows the English help overlay (Escto close) — a centered bordered box (╭─╮) with aligned key/description columns - Count: digits
1-9accumulate (0only after existing buffer), cleared after800msor after jump/scroll;[/](500ms) is leader sequence;/search runs fully inside the extension (no TUI overlay) soEnter/n/Nnever fight the input focus - Restore: clears the
gg/count/bracket buffers on exit, restores input and (whenautoExpandTools: true) tool collapse state (tool expand/collapse is async and does not block the first frame) - Position anchoring: captures an anchor (nearest prompt ordinal + in-segment offset) synchronously before any height change, then restores via the unified clamp model once layout settles (exact restore → in-segment truncation → pin to last page when content below is shorter than a viewport, anchored line still on screen); the restore monitor calls
requestRenderevery tick (pi-tui renders on demand — zero frames while idle, so the stability criterion would never fire otherwise); toggling while following-end is left to native follow-end semantics
Compatibility & Limitations
- Key protocol: compatible with legacy control sequences and
Kittykeyboard protocol; arrow-key passthrough covers bothCSI(\x1b[) and application-cursor-keysSSU(\x1bO) sequences - Mouse wheel / trackpad, text selection + copy, and
ctrl+shift+fsearch still pass through in fullscreen regularmode has noScrollView— navigation silently no-ops
Development
pnpm --filter @inobit/pi-reader check
pnpm --filter @inobit/pi-reader test # parseReadingKey/halfPage/pageStep/GgSequence + new navigation helpers
pnpm --filter @inobit/pi-reader pack:check
pi -ne -e ./packages/pi-reader --tui-mode fullscreenLicense
MIT
