Skip to content

Switch CLI rendering to alternate screen buffer#151

Merged
shellicar merged 6 commits intomainfrom
feature/149-alternate-buffer-rendering
Mar 29, 2026
Merged

Switch CLI rendering to alternate screen buffer#151
shellicar merged 6 commits intomainfrom
feature/149-alternate-buffer-rendering

Conversation

@shellicar
Copy link
Copy Markdown
Owner

Summary

  • Rendering moves to alternate screen buffer for full screen ownership
  • Renderer simplified to stateless redraw with no position tracking
  • Completed responses flush to main buffer for persistent scrollback

Related Issues

Closes #149

Co-Authored-By: Claude noreply@anthropic.com

Replaces main-buffer rendering with the alternate screen buffer. The
alternate buffer gives the CLI full screen ownership: no scrollback to
corrupt, absolute positioning works without caveats, and resize is
clear and redraw. This replaces the position tracking, resize
heuristics, and scrollback corruption workarounds from prior fixes.

Changes:
- Screen: add enterAltBuffer/exitAltBuffer to interface and StdoutScreen
- MockScreen: dual-buffer model with save/restore on switch, remove
  readonly from cells, add assertInAltBuffer/assertInMainBuffer/getMainRow
- Renderer: remove lastVisibleCursorRow, lastFrame, pendingResize,
  zoneHeight, notifyResize, writeHistoryLine, writeHistory. Simplified
  stateless render() always starts with cursorAt(1,1)
- Terminal: remove notifyResize, add enterAltBuffer/exitAltBuffer,
  historyBuffer and inAltBuffer fields. writeHistory accumulates in
  historyBuffer when in alt buffer, writes directly to screen otherwise
- ClaudeCli: enter alt buffer after startup messages, before first
  redraw. Exit alt buffer as first operation in cleanup. Add SIGTERM
  and uncaughtException handlers that exit alt buffer before process
  exit. Remove notifyResize call from resize handler.
- Tests: replace main-buffer Renderer tests with alt-buffer equivalents,
  add MockScreen buffer-switch tests, update terminal-integration tests
Completed responses accumulate in historyBuffer during alt buffer
rendering. flushHistory() exits the alt buffer, writes accumulated
lines to main buffer scrollback, then re-enters alt buffer and
redraws. Called in sendQuery() after showSkills() so every completed
response is permanently visible in scrollback.
@shellicar shellicar added this to the 1.0 milestone Mar 29, 2026
@shellicar shellicar added the enhancement New feature or request label Mar 29, 2026
@shellicar shellicar self-assigned this Mar 29, 2026
Copy link
Copy Markdown
Collaborator

@bananabot9000 bananabot9000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean implementation. The Phase 0 design in the session log is thorough and the code follows it precisely.

What I like:

  • Renderer is now stateless. No lastVisibleCursorRow, no pendingResize, no zoneHeight, no lastFrame. render() is a pure function of its input plus cursorAt(1,1). That's a massive simplification
  • Screen interface extended cleanly -- enterAltBuffer()/exitAltBuffer() keeps escape sequences centralised. MockScreen's dual-buffer model with save/restore is the right testing abstraction
  • flushHistory() uses screen.exitAltBuffer/enterAltBuffer directly (bypassing Terminal.inAltBuffer) so the logical state stays consistent. Subtle but correct
  • Cleanup handler ordering: exitAltBuffer() FIRST in cleanup(), before kitty keyboard protocol restore. If the process dies mid-cleanup, the user at least sees their main buffer
  • writeHistory() routing based on inAltBuffer -- main buffer writes go direct, alt buffer writes accumulate. Clean dual-path
  • Tests replaced, not just removed. The new MockScreen dual-buffer and History flush describe blocks cover the new contracts. assertInAltBuffer()/assertInMainBuffer() assertions are a nice touch

Observations (not blocking):

  • flushHistory() calls this.screen.exitAltBuffer() / enterAltBuffer() but doesn't touch this.inAltBuffer. The flag stays true throughout the flush, which is the correct choice (the flush is transparent to the rest of Terminal). Worth a comment if it's not already documented
  • The SIGTERM handler exits with code 0. If the process was asked to terminate, 0 is fine. Some prefer 128+SIGNUM (143) for signal exits but it doesn't matter in practice
  • cursorUp and cursorTo consts removed entirely. The only positioning primitive is cursorAt. Clean

191 tests pass per the session log. Ship it.

@shellicar shellicar merged commit c31d56e into main Mar 29, 2026
4 checks passed
@shellicar shellicar deleted the feature/149-alternate-buffer-rendering branch March 29, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Alternate buffer rendering with history flush

2 participants