[BUG] Fullscreen: sticky prompt header no longer appears when scrolling up (regression 2.1.246 → 2.1.248)

Status Open
Reported on v2.1.248
Maintainer reply None cached
Activity 0 comments · opened Aug 30, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In the fullscreen renderer ("tui": "fullscreen"), scrolling the transcript up used to pin the user prompt of the turn currently at the top of the viewport as a one-line header (❯ <prompt text>, user-message background), updating as you scroll past earlier turns.

Since 2.1.248 this header never appears: at any scroll position the top row is the blank padding row and the transcript content starts directly below it. Both while a turn is streaming and after it finished; also with tool-heavy turns. 2.1.246 still shows the header in the identical setup (same terminal, same tmux, same prompts, same injected scroll events).

What Should Happen?

Top row shows the pinned prompt of the turn at the viewport top — e.g. ❯ Count from 1 to 60, … — and switches to the other prompts as you scroll past them. This is what 2.1.246 does.

Error Messages/Logs

No error output — the header is silently absent. In-app scrolling itself works.

Steps to Reproduce

  1. "tui": "fullscreen" in settings.json; start claude in a terminal with mouse reporting (reproduced inside tmux 3.6b with TERM=xterm-256color, and in a libghostty-based terminal via tmux).
  2. Send three prompts so the transcript is longer than the screen, e.g.
  • Count from 1 to 60, one number per line, nothing else.
  • Reply with only the word OK.
  • Count from 100 to 160, one number per line, nothing else.
  1. Scroll up with the mouse wheel until the top of the viewport is inside the first turn's answer (e.g. numbers 30–40 visible at the top).

Deterministic variant (no real mouse needed): run the session in a detached tmux session and inject SGR wheel-up events, e.g. tmux send-keys -t <session> -l $'\e[<64;60;18M' 60–80 times, then tmux capture-pane -p.

Tested: 2.1.248, 2.1.250, 2.1.251 → no header. 2.1.246 → header shown. (2.1.247 not available to me.)

Claude Model

Not sure / Multiple models

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.246

Claude Code Version

2.1.251

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Reading the bundled code: in 2.1.246 the sticky-prompt hook (the one that calls setStickyPrompt from the ScrollChrome context, next to the VirtualMessageList: itemKeys/messages length desync diagnostic) reads scrollRef.current?.isSticky(), getScrollTop() and getPendingDelta() fresh on every render.

From 2.1.248 the same hook takes a scrollViewport handle and those three reads are wrapped in compiler-memoized slots keyed only on the identity of scrollViewport.handle. The handle identity does not change while scrolling, so the values stay at their first-render state (isSticky === true, scrollTop === 0); the firstVisible > 0 && !isSticky condition never becomes true and the header is never set. The useSyncExternalStore subscription still re-renders the hook on scroll, but the memoized reads do not refresh.

Everything else on the path is intact: hideSticky is only passed as isTeammate (false for the main transcript), the header component renders when given text, and the in-app scrolling works.

Possibly related to the recent "erratic fullscreen scrolling when positioned at an earlier message" fix, which touches the same scroll-position code.

macOS 15.6 (arm64), native installer builds; terminal: libghostty-based app + tmux 3.6b.

View original on GitHub ↗