Skip to content

In-app scrollback with history/live mode #150

@bananabot9000

Description

@bananabot9000

Summary

With the CLI rendering in the alternate buffer (#149), there is no terminal-managed scrollback during a session. The user cannot scroll up to see previous responses until they exit. This issue adds in-app scrollback with a history/live mode toggle.

Feature

Maintain an in-memory history buffer of all rendered output. Provide a scrollable viewport above the zone that lets the user browse previous responses during a session. Two modes: live mode (default, follows new output) and history mode (viewport pinned, new output continues in background).

Functionality

History buffer

  • All completed output (assistant responses, tool results, system messages) appended to an in-memory array of rendered lines
  • The buffer grows for the duration of the session
  • The history flush to main buffer (Alternate buffer rendering with history flush #149) and the in-memory buffer are fed from the same source

Live mode (default)

  • Viewport follows the latest output -- new content scrolls into view automatically
  • This is the current behaviour: user watches Claude respond in real-time

History mode

  • Entered automatically when the user scrolls up (Page Up, or equivalent keybind)
  • Viewport is pinned -- new output appends to the buffer but does not move the viewport
  • The user is reading previous output undisturbed while Claude continues responding
  • Visual indicator that history mode is active (e.g. scroll position in status bar, or a mode label)

Returning to live mode

  • Esc exits history mode and scrolls to the bottom (back to live mode)
  • Scrolling to the very bottom of the buffer also returns to live mode naturally
  • New output after returning to live mode resumes auto-scrolling

Scroll keybindings

  • Page Up / Page Down -- scroll by a screenful
  • Shift+Up / Shift+Down (or equivalent) -- scroll line by line
  • Mouse wheel if supported by the terminal/protocol (Kitty keyboard protocol provides scroll events)
  • Keybinds should be simple and discoverable

Position indicator

  • When in history mode, show the scroll position somewhere visible (status bar is the natural location)
  • Could be line numbers ([42/128]), percentage, or a visual scroll track on the right edge
  • When in live mode, no indicator needed (or a subtle "live" label)

Editor overflow

  • If the multi-line editor (input area) grows taller than the zone, the editor itself needs internal scrolling
  • Position indicator should also reflect editor scroll state when the editor is the active scroll target
  • This may already be partially handled by the existing Viewport -- assess during implementation

Depends on


Guidance

Existing infrastructure

  • Viewport class (src/Viewport.ts) already implements a stateful scroll window over an unbounded buffer with cursor chasing. This is the core primitive needed -- the history buffer feeds into the same pipeline
  • layout() produces an unbounded row buffer; Viewport.resolve() slices it to screen height. History lines become additional input to this pipeline
  • The zone (editor, status bar) occupies fixed rows at the bottom. The scrollable history viewport is everything above the zone

Two scroll targets

There are two independently scrollable areas:

  1. History viewport (above the zone) -- previous responses, tool output
  2. Editor (within the zone) -- when multi-line input exceeds the zone allocation

These should be distinct scroll contexts. The active one depends on focus/mode. History mode scrolls the history viewport. Normal input scrolls the editor if it overflows.

Mode state

A simple boolean or enum on the renderer/terminal: scrollMode: 'live' | 'history'. In live mode, the viewport's scroll offset tracks the bottom of the buffer. In history mode, it's pinned to whatever position the user scrolled to. Mode transitions are triggered by keybinds and scroll-to-bottom detection.

Rendering in history mode

When pinned, new output doesn't trigger a viewport scroll -- it just appends to the buffer. The zone at the bottom still updates (status bar shows Claude is thinking, editor is responsive). Only the history area above the zone is frozen from the user's perspective.

Memory considerations

The history buffer grows unboundedly for the session. For typical CLI sessions (hundreds of responses), this is negligible. For extremely long sessions, a ring buffer or max-lines cap could be added later. Not a concern for v1.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions