diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index 3bf8a70..262e09a 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -804,13 +804,6 @@ function toggleSidebar() { var collapseBtn = $('sidebar-collapse-btn'); if (collapseBtn) collapseBtn.textContent = isOpen ? '\u2039' : '\u203a'; - - // Refit xterm.js after the sidebar CSS transition completes (0.25s ease). - // 'width' fires on desktop (flex layout); 'transform' fires on mobile (overlay). - sidebar.addEventListener('transitionend', function(e) { - if (e.propertyName !== 'width' && e.propertyName !== 'transform') return; - if (window._refitTerminal) window._refitTerminal(); - }, { once: true }); } /** diff --git a/muxplex/frontend/terminal.js b/muxplex/frontend/terminal.js index fb4a812..e01f397 100644 --- a/muxplex/frontend/terminal.js +++ b/muxplex/frontend/terminal.js @@ -10,6 +10,7 @@ let _currentSession = null; let _vpHandler = null; let _reconnectAttempts = 0; // tracks consecutive failed reconnect attempts for backoff + ttyd respawn let _searchAddon = null; +let _resizeObserver = null; // ─── Module-level encoding helpers ────────────────────────────────────────── // Hoisted here so the clipboard key handler (in openTerminal) can also use them. @@ -357,6 +358,23 @@ function openTerminal(sessionName, remoteId, fontSize) { _term.open(container); + // --- Auto-refit on container resize (sidebar toggle, etc.) --- + // xterm.js FitAddon only resizes on explicit fit() calls. A ResizeObserver + // on the container handles ALL layout changes: sidebar toggle, window resize, + // and any future CSS geometry change. Debounced to coalesce rapid events + // (e.g. during CSS transition animation frames). + if (_resizeObserver) { _resizeObserver.disconnect(); _resizeObserver = null; } + if (typeof ResizeObserver !== 'undefined') { + var _roTimer = null; + _resizeObserver = new ResizeObserver(function() { + clearTimeout(_roTimer); + _roTimer = setTimeout(function() { + if (_fitAddon) try { _fitAddon.fit(); } catch (_) {} + }, 50); + }); + _resizeObserver.observe(container); + } + // --- Clipboard integration --- // Copy: Ctrl+Shift+C intercepts and copies selection to system clipboard // Paste: handled natively by xterm.js (browser paste event → hidden textarea → onData → WebSocket) @@ -504,6 +522,8 @@ function closeTerminal() { _ws = null; } + if (_resizeObserver) { _resizeObserver.disconnect(); _resizeObserver = null; } + if (_term) { _term.dispose(); _term = null; @@ -542,13 +562,6 @@ function setTerminalFontSize(size) { window._setTerminalFontSize = setTerminalFontSize; -// Refit the terminal to its container dimensions. -// Call after layout changes (e.g. sidebar toggle) that alter available space. -window._refitTerminal = function() { - if (!_fitAddon) return; - try { _fitAddon.fit(); } catch (_) {} -}; - // --------------------------------------------------------------------------- // Mobile touch scroll — rAF-batched WheelEvent dispatch // Mobile devices batch touchmove events irregularly; dispatching one WheelEvent diff --git a/pyproject.toml b/pyproject.toml index 53bb426..41ddebc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "muxplex" -version = "0.4.5" +version = "0.4.6" description = "Web-based tmux session dashboard — access all your tmux sessions from any browser" readme = "README.md" license = { text = "MIT" } diff --git a/uv.lock b/uv.lock index a6fc4fd..a92b556 100644 --- a/uv.lock +++ b/uv.lock @@ -332,7 +332,7 @@ wheels = [ [[package]] name = "muxplex" -version = "0.4.5" +version = "0.4.6" source = { editable = "." } dependencies = [ { name = "aiofiles" },