8000 fix(terminal): replay remount state only into the PTY it was taken from by jpstratman · Pull Request #360 · amirlehmam/wmux · GitHub
[go: up one dir, main page]

Skip to content

fix(terminal): replay remount state only into the PTY it was taken from - #360

Merged
amirlehmam merged 1 commit into
amirlehmam:masterfrom
jpstratman:fix/remount-snapshot-only-for-live-surfaces
Oct 25, 2026
Merged

amirlehmam merged 1 commit into
amirlehmam:masterfrom
jpstratman:fix/remount-snapshot-only-for-live-surfaces

Conversation

@jpstratman
Copy link
Copy Markdown
Contributor

Refs #345. Follow-up to #348. Since #346 a surface's xterm lives as long as the surface, so the remount snapshot was mostly taken when a pane was really closed. It stayed cached and was replayed into the next terminal with the same surface id, which Load session reuses. Because #348 made the snapshot carry the alternate screen, closing a pane that ran vim or Claude Code fullscreen and then loading a session that held it put the new shell's prompt on the alternate buffer, kept its output out of scrollback, and left mouse tracking on.

What changes

  • A closed surface is no longer serialized, and its cached snapshot is dropped, so a close no longer serializes the whole scrollback during unmount.
  • The snapshot and the saved mouse modes are replayed only when the PTY lookup says the PTY was reused (pty.has, or create's reused). A fresh PTY gets neither. The preload type for pty.create now includes reused, which main already returned.

Design choices

  • The replay stays. The root ErrorBoundary's retry rebuilds every terminal on its live PTY in production, and StrictMode and Fast Refresh do it in dev.
  • The replay now waits for the async PTY lookup, by which time the pane has usually been fitted, so the write callback re-fits to the pane's real size instead of only when a held sync was refused.

Manual verification

In a wmux-dev.ps1 -Preview instance:

  • Run a mouse-tracking TUI, Save session, close the pane, then Load session. The restored pane is a fresh shell on the normal buffer with no mouse tracking, and the wheel scrolls scrollback. On master the shell came back on the alternate buffer with drag tracking on.
  • With the TUI running, trigger the root ErrorBoundary and click retry. The terminal comes back on the alternate screen at the same size, the wheel still reaches the TUI as SGR reports, and q restores the shell screen.

< 8000 /div>
Since amirlehmam#346 a surface's xterm lives as long as the surface, so the remount
snapshot is mostly taken when a pane is really closed. That snapshot stayed
cached and was replayed into the next terminal with the same surface id, and
Load session keeps saved ids. Since amirlehmam#348 the snapshot carries the alternate
screen, so closing a pane that ran vim or Claude Code fullscreen and then
loading a session that held it put the new shell's prompt on the alternate
buffer, with its output kept out of scrollback.

The cache is now RemountSnapshotCache (utils/remount-snapshot.ts):

- A terminal whose surface has left the store is not serialized, and any
  earlier entry for it is dropped. A close no longer serializes the whole
  scrollback during unmount.
- The replay waits for the PTY lookup and runs only when the PTY was
  reused (pty.has, or create's `reused` for StrictMode's second mount). A
  fresh PTY drops the entry.

The per-surface mouse modes had the same flaw: a closed TUI's modes were
replayed into a fresh shell on its id, which then tracked the mouse in SGR,
and the wheel handler forwarded scrolling to the shell as escape codes. They
are now replayed on the same reuse decision (takeMouseModeReplay), and a
fresh PTY forgets the surface's modes and wheel remainder.

The machinery stays for terminals rebuilt on a live PTY: the root
ErrorBoundary's retry remounts every terminal in production, and StrictMode
and Fast Refresh do it in dev. Because the hold now starts after the async
lookup, the write callback re-fits whatever the pane size is instead of only
when a sync was refused.

Refs amirlehmam#345
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0