diff --git a/muxplex/frontend/terminal.js b/muxplex/frontend/terminal.js index 3fc4109..ba50478 100644 --- a/muxplex/frontend/terminal.js +++ b/muxplex/frontend/terminal.js @@ -661,20 +661,32 @@ function switchTerminal(sessionName, remoteId, fontSize) { } } - // 3. If target is in cache — instant switch, no network + // 3. If target is in cache — check WebSocket liveness before the fast path if (_terminalCache.has(key)) { var entry = _terminalCache.get(key); - if (entry.container) { - entry.container.style.display = ''; + var wsIsLive = entry.ws && + (entry.ws.readyState === WebSocket.OPEN || + entry.ws.readyState === WebSocket.CONNECTING); + if (wsIsLive) { + // Fast path: live connection — swap containers, no network round-trip + if (entry.container) { + entry.container.style.display = ''; + } + _syncModuleState(entry); + _activeSessionKey = key; + _touchCacheOrder(key); + if (_fitAddon) { + try { _fitAddon.fit(); } catch (_) {} + } + if (_term) _term.focus(); + return; } - _syncModuleState(entry); - _activeSessionKey = key; - _touchCacheOrder(key); - if (_fitAddon) { - try { _fitAddon.fit(); } catch (_) {} - } - if (_term) _term.focus(); - return; + // WebSocket died while this session was in the background. + // The close handler's stale-guard (ws !== _ws) suppresses reconnects for + // non-active sessions, so the entry is left with a dead socket. + // Destroy the stale entry and fall through to create a fresh terminal. + // openSession() already POST'd /connect so the backend ttyd is ready. + destroyCachedEntry(key); } // 4. Not in cache — create new entry @@ -698,7 +710,12 @@ function switchTerminal(sessionName, remoteId, fontSize) { subContainer.style.height = '100%'; parentContainer.appendChild(subContainer); - // Reset module-level state for fresh creation + // Reset module-level state for fresh creation. + // Null _term/_fitAddon BEFORE createTerminal() so it does NOT dispose the + // previously-active session's terminal — that terminal lives on in its own + // cache entry and must stay alive for instant switching later. + _term = null; + _fitAddon = null; _currentSession = null; _reconnectAttempts = 0; _reconnectTimer = null;