Stabilize terminal mount across pane moves and splits#667
Merged
mehmetozguldev merged 3 commits intoMay 7, 2026
Conversation
Fixes athasdev#648 Mount each xterm exactly once per session at app root via TerminalHost and re-parent its DOM (raw appendChild) into whichever TerminalSlot is currently displaying it. Editor-pane splits, terminal-tab splits, tab moves, and other layout changes only swap a portal target — no React unmount, no PTY churn. Removes the snapshot save/restore dance entirely. Snapshot replay was masking the underlying remount bug and corrupting alt-buffer state when TUIs (Claude Code, vim, htop, etc.) were active during a layout change. Slots route mousedown to onActivate via a native listener, since portal rendering breaks React event bubbling and the host pane would otherwise never see the click. Also restores the initiating terminal as active after the in-tab split, so the split layout (companion rendered inside the initiator's iter) stays visible instead of swapping to the new companion.
Contributor
Author
|
@mehmetozguldev Have a look please → tested, took a LONG time to fix |
mehmetozguldev
approved these changes
May 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #648.
The previous fix (#896dcbf8) added snapshot save/restore to mask the symptom, but the actual root cause is that
<XtermTerminal>was being unmounted and remounted whenever a buffer moved between panes (editor pane split, tab move, etc.). Snapshot replay then corrupted alt-buffer state for any TUI in flight (Claude Code, vim, htop, lazygit), produced duplicate banners, narrow reflow, and other rendering artifacts depending on timing.This PR replaces the masking layer with a stable mount.
Architecture
<TerminalHost />mounted at app root holds every live xterm instance for the lifetime of its session.<TerminalSlot sessionId>placeholder div instead of<XtermTerminal>directly. Slots register themselves interminal-slots-store.TerminalHostkeeps a stable wrapper<div>per session and re-parents it (raw DOMappendChild) into whichever slot is currently registered. When no slot exists for a brief window during a pane move, the wrapper is parked in an offscreen container so xterm stays mounted.Other fixes folded in
mousedownlistener on the slot routes activation to the host pane. Portal rendering breaks React event bubbling (events bubble through React tree, not DOM tree), so the existinghandlePaneMouseDownCaptureinPaneContainerwas never seeing clicks on the portaled xterm — pane stayed inactive,isActivestayed false, terminal looked muted (opacity-60) and the tab stayed gray.handleSplitView), explicitly restore the initiating terminal as active.createTerminalswapsactiveTerminalIdto the new companion, but the companion is rendered only inside the initiator's iter interminal-container.tsx's map — so the split layout was hidden behind the new fresh pane until the user switched tabs.Removed
saveSessionSnapshot/getSessionSnapshot/clearSessionSnapshotinterminal-store.TerminalViewSnapshot+viewSnapshot+serializedContentonTerminal.terminal-store-snapshot.test.ts— the behavior it covered no longer exists.terminal.tsxanduse-terminal-connection.ts.Test plan
seq 1 100, split editor pane horizontally with a markdown file → terminal pane retains scrollback, no duplicate output, no narrow reflow.bun run typecheckclean.bun run lintclean (no new warnings).