Repository navigation
Description
What happens
I ran Claude Code with "tui": "fullscreen", which uses the alternate screen buffer (ESC[?1049h on start, ESC[?1049l on exit). When I quit, the pane did not return to the screen it showed before Claude Code started. The final Claude Code frame stayed on screen. Claude Code's exit message and the PowerShell prompt were written over the middle of it, mixed with leftover characters from the lines underneath. The structure is shown below, with the actual content replaced by placeholders:
<line from the final Claude Code frame>
Resume this session with:<remainder of the frame line underneath>
claude --resume <session-id><leftover characters from the frame>
PS C:\...\project> <remainder of the frame line underneath>
<line from the final Claude Code frame>
This is not only a drawing problem. wmux read-screen --lines 2000 on that pane returns 127 lines:
- lines 1–11: output from a shell command run before Claude Code started, cut off partway through a table
- lines 11–127: the Claude Code session, written into the normal buffer as if it were ordinary output
- lines 50–53: the exit message and prompt, written over that text
So the Claude Code frames went to the normal buffer and its scrollback, not to the alternate buffer. By the time Claude Code sent ESC[?1049l, the pane was already on the normal buffer.
This fits xterm.js's handling of DECRST 1049 in the bundled build: activateNormalBuffer() followed by restoreCursor(). When the pane is already on the normal buffer, the buffer switch does nothing and only the cursor restore runs. The cursor jumps to the position saved when the program started, and everything after that is drawn over the stale frame.
Expected: a pane's alternate-buffer state survives a remount. On exit, the pane returns to the normal buffer as it was before the program started.
Steps to reproduce
Any split-tree change that remounts the pane while a program is on the alternate screen. No Claude Code needed:
- Open a terminal pane T. From inside it, run
wmux split --downand note the new pane U. - In T, run:
cls; "MAIN-1"; "MAIN-2"; [Console]::Write("`e[?1049h`e[H"); 1..5|%{"ALT-BEFORE-$_"}; Start-Sleep 12; 1..5|%{"ALT-AFTER-$_"}; Start-Sleep 3; [Console]::Write("`e[?1049l"); "EXITED"
- While it sleeps, run
wmux close-pane <U>from another pane. This remounts T. wmux read-screen --surface <T>right after the close shows onlyMAIN-1andMAIN-2. The pane is back on the normal buffer while the program is still on the alternate one.- After the script exits, the screen reads
EXITED, the prompt, thenALT-AFTER-1..5.EXITEDand the prompt overwroteMAIN-1andMAIN-2at row 0.
With Claude Code, the common trigger is wmux browser open from inside the session in a workspace with no browser pane. That splits Claude Code's own pane. Closing a neighbouring pane, wmux layout agents/grid and dragging a tab between panes do the same.
Cause
snapshotSurfaceBuffer in src/renderer/hooks/useTerminal.ts serializes with excludeAltBuffer: true. The replacement terminal replays only the normal buffer plus mouse modes, and nothing restores ?1049h. The comment expects the TUI's post-remount redraw to cover it, but the TUI redraws without re-sending ?1049h, so the redraw lands in the normal buffer.
Serializing with the alternate buffer included fixes it. SerializeAddon then writes the normal buffer and its cursor, then ESC[?1049h ESC[H and the alternate screen, so the replacement comes back on the alternate buffer and the saved cursor is the shell's real position. I have a branch with that change and a regression test (real xterm + SerializeAddon, remount on the alternate screen) and can open a PR.
wmux version
2.39.0 (Electron 43.0.0; bundled node-pty 1.1.0 with ConPTY/OpenConsole 1.23.251008001)
Windows version
Windows 11 Home 10.0.26200
Anything else
- Shell: PowerShell 7.6.7 (Store install), PSReadLine 2.4.5. The shell only prints its prompt at the cursor, so it doesn't seem to be involved.
- Program: Claude Code 2.1.310 with
"tui": "fullscreen"in~/.claude/settings.json. - Workarounds:
Ctrl+Lorclsafter exiting clears the stale output. Turning off Claude Code's fullscreen TUI avoids the alternate screen altogether. - Related: bug(terminal): terminal buffer clears on any split tree restructure (pane add/remove/drag) #49 (the remount snapshot this changes) and bug(terminal): SGR mouse encoding is lost after terminal remount #164 (mouse modes lost on remount, same mechanism).
- Separate, not fixed here: Reset terminal state (
resetTerminalModes) writesESC[?1049lwhile the program is still running, which leaves the pane in the same state.
Activity
fix(terminal): keep the alternate screen across a pane remount (#345) (…
feat(terminal): keep each surface's xterm alive across split-tree rem…
amirlehmam commentedon Oct 24, 2026
Shipped in v2.40.0. Thanks for the detailed report!
fix(terminal): replay remount state only into the PTY it was taken fr…