fix: use ResizeObserver for terminal refit after sidebar toggle

The previous transitionend approach (v0.4.5) had a { once: true } bug:
the sidebar transitions two CSS properties (width and min-width), and if
min-width fired first, the listener self-destructed before the width event
arrived — so _refitTerminal() was never called.

Replace the fragile event-based approach with a ResizeObserver on
#terminal-container. This automatically calls _fitAddon.fit() (debounced
50ms) whenever the container dimensions change, handling sidebar toggle,
browser resize, and any future layout change. Remove the now-unnecessary
window._refitTerminal global and the broken transitionend handler.

Bumps version to 0.4.6.
This commit is contained in:
Brian Krabach
2026-05-02 07:45:11 -07:00
parent 538d11165c
commit abe6a97241
4 changed files with 22 additions and 16 deletions
-7
View File
@@ -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 });
}
/**
+20 -7
View File
@@ -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