-
Notifications
You must be signed in to change notification settings - Fork 669
Fix terminal state loss when switching workspaces #2717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix terminal state loss when switching workspaces #2717
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
WalkthroughAdds a per-window tab-view cache ( Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (1)
emain/emain-window.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-15T03:18:52.647Z
Learnt from: sawka
Repo: wavetermdev/waveterm PR: 2433
File: emain/emain-window.ts:811-828
Timestamp: 2025-10-15T03:18:52.647Z
Learning: In emain/emain-window.ts, within the relaunchBrowserWindows function, ClientService.GetClientData() is guaranteed to return a valid client object and never null/undefined. The backend ensures a client is initialized before startup, so no null-guard is needed when accessing clientData.windowids.
Applied to files:
emain/emain-window.ts
🔇 Additional comments (5)
emain/emain-window.ts (5)
142-142: LGTM!The cache field is well-documented and properly initialized alongside the existing
allLoadedTabViewsmap. Using aMap<string, WaveTabView>provides efficient O(1) lookup by tab ID.Also applies to: 222-222
357-361: LGTM!Proper cleanup of cached views on window teardown. Destroying each cached view before clearing the map prevents memory leaks.
586-610: Good defensive handling for rapid workspace switching.The edge case handling at lines 594-598 correctly destroys any existing cached view before caching the new one, preventing memory leaks during rapid workspace switches. The decision to keep views as children of
contentView(positioned off-screen) rather than removing them is sound for preserving WebView/xterm.js state.
615-623: LGTM!The cache-first lookup correctly reuses existing tab views, and setting
tabInitialized = trueensures the view bypasses full reinitialization, preserving terminal state. Removing from cache on reuse prevents double-ownership.
679-685: The function is correctly designed for active tabs only, not cached ones.
getWaveWindowByTabIdonly checksallLoadedTabViewsby design. It's only called viagetWaveWindowByWebContentsId, which retrieves tabs fromwcIdToWaveTabMap—a map containing only active tabs with running web content. Active tabs always exist inallLoadedTabViewsand never inallTabViewsCache(cached tabs lack active webContents). While similar operations likeremoveTabViewdefensively check both maps, the current behavior is intentional and safe for this lookup's usage pattern.
Fixes issue where TUI applications (vim, htop, opencode, etc.) would lose terminal state when switching between workspaces. This caused inability to scroll and display corruption. Root cause: Workspace switching was destroying all tab views including terminals, then recreating them from cache. This destroyed xterm.js instances and lost their state. Solution: Cache tab views across workspace switches instead of destroying them. Tab views are positioned off-screen but kept alive, preserving: - Terminal buffer state (normal and alternate screen modes) - Scrollback history and scrolling capability - Running processes and their output - Cursor position and all terminal modes Memory management: Cached views kept alive until tab closed or window closed. Note: This PR includes the StreamCancelFn type fix from wavetermdev#2716 to ensure the branch builds correctly.
562732e to
978443c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
emain/emain-window.ts (1)
357-361: MissingremoveChildViewfor cached views before destroy.Cached views remain as children of
contentView(just positioned off-screen). For consistency with the loop above (lines 351-356) and to avoid relying on implicit cleanup during window destruction, consider removing them fromcontentViewbefore destroying.🔎 Proposed fix
// Also destroy any cached views for (const tabView of this.allTabViewsCache.values()) { + if (!this.isDestroyed()) { + this.contentView.removeChildView(tabView); + } tabView?.destroy(); } this.allTabViewsCache.clear();
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (1)
emain/emain-window.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-15T03:18:52.647Z
Learnt from: sawka
Repo: wavetermdev/waveterm PR: 2433
File: emain/emain-window.ts:811-828
Timestamp: 2025-10-15T03:18:52.647Z
Learning: In emain/emain-window.ts, within the relaunchBrowserWindows function, ClientService.GetClientData() is guaranteed to return a valid client object and never null/undefined. The backend ensures a client is initialized before startup, so no null-guard is needed when accessing clientData.windowids.
Applied to files:
emain/emain-window.ts
🧬 Code graph analysis (1)
emain/emain-window.ts (1)
emain/emain-tabview.ts (2)
WaveTabView(106-225)getOrCreateWebViewForTab(295-349)
🔇 Additional comments (5)
emain/emain-window.ts (5)
142-142: LGTM!The cache declaration is well-typed and the comment clearly documents its purpose for preserving tab views across workspace switches.
222-222: LGTM!Consistent initialization alongside
allLoadedTabViews.
586-610: Good handling of workspace switch with proper edge case protection.The implementation correctly:
- Preserves terminal state by caching instead of destroying
- Handles rapid workspace switching by destroying stale cached views
- Documents the lifecycle clearly
The design choice to keep cached views as children of
contentView(just off-screen) is valid and simpler than removing/re-adding.
615-625: LGTM!Cache-first lookup pattern is correctly implemented. Cached views are properly removed from the cache and will be re-added to
allLoadedTabViewsviasetTabViewIntoWindow.
655-673: Good fix - addresses the previous review comment.The
removeChildViewcall is now correctly placed after the if-else block (lines 668-671), ensuring cached views are removed fromcontentViewbefore being destroyed. This properly handles the case where cached views remain as children ofcontentView(just positioned off-screen).
|
Closing temporarily for additional debugging and testing. Will reopen after issues are resolved. |
Summary
Fixes issue where TUI applications (vim, htop, opencode, etc.) would lose terminal state when switching between workspaces, causing inability to scroll and display corruption.
Problem
When switching workspaces:
removeAllChildViews()This made it impossible to scroll in terminals after switching back to a workspace with running TUI applications.
Root Cause
The
switchworkspaceoperation calledremoveAllChildViews()which destroyed all tab views including terminals. When recreated, the xterm.js terminal instances lost their state even though the backend shell processes continued running.Tab switching vs Workspace switching:
Solution
Cache tab views across workspace switches instead of destroying them:
allTabViewsCacheand position off-screenThis preserves:
Changes
Memory Management
Testing
Tested with opencode (TUI app using @OpenTui):
Additional Changes
Includes
StreamCancelFntype fix from #2716 to ensure this branch builds correctly. This fix corrects the type definition inwshrpctypes.gofromfunc()tofunc(context.Context) error.Behavior Match
Now matches macOS Terminal, iTerm2, and other terminal emulators where terminal state is preserved across workspace/tab switches.