diff --git a/frontend/src/js/app.js b/frontend/src/js/app.js index 26c5b49..81dcb02 100644 --- a/frontend/src/js/app.js +++ b/frontend/src/js/app.js @@ -134,13 +134,24 @@ class App { window.runtime.EventsOn('session:state', (payload) => { this.tabs.updateSessionState(payload.sessionID, payload.state, payload.error); this.updateStatusBar(); - // Re-fit the terminal once connected so it picks up the final container size. - // Use multiple delays because the panel layout may still be settling. + // Once connected, ensure the backend PTY has the correct dimensions. + // The initial fit happens before the SSH handshake completes, so the + // onResize callback fires when the backend isn't ready. By the time + // the connection is established, xterm already has the right cols/rows + // and fitAddon.fit() won't trigger a new resize event. We must + // explicitly send the current terminal size to the backend. if (payload.state === 'connected') { + const entry = this.terminals.terminals.get(payload.sessionID); + if (entry) { + const { cols, rows } = entry.term; + try { + window.go.api.Service.ResizeSession(payload.sessionID, cols, rows); + } catch (_) { /* noop */ } + } + // Also re-fit to catch any layout changes that occurred while connecting. const fit = () => this.terminals.fitSession(payload.sessionID); - setTimeout(fit, 30); - setTimeout(fit, 150); - setTimeout(fit, 500); + setTimeout(fit, 50); + setTimeout(fit, 300); } }); diff --git a/frontend/src/js/tabs.js b/frontend/src/js/tabs.js index 29d4a7e..ff7630c 100644 --- a/frontend/src/js/tabs.js +++ b/frontend/src/js/tabs.js @@ -47,8 +47,10 @@ class TabManager { panel.style.display = 'none'; this.panelContainer.appendChild(panel); - this.setActive(sessionInfo.id); + // Hide the empty-state BEFORE activating the panel so the + // panel-container gets the full workspace height immediately. this._updateEmptyState(); + this.setActive(sessionInfo.id); } /** diff --git a/frontend/src/js/terminal.js b/frontend/src/js/terminal.js index f859de1..6630eec 100644 --- a/frontend/src/js/terminal.js +++ b/frontend/src/js/terminal.js @@ -67,23 +67,15 @@ class TerminalManager { const container = document.getElementById(containerId); term.open(container); - // Fit the terminal once the container has real dimensions. - // The panel may have just transitioned from display:none, so we - // poll until the container reports a non-trivial size. + // Force a synchronous layout reflow so the container reports its + // true dimensions, then fit the terminal to fill the space. + // eslint-disable-next-line no-unused-expressions + container.offsetHeight; const doFit = () => { try { fitAddon.fit(); } catch (_) { /* noop */ } }; - let fitAttempts = 0; - const tryFit = () => { - fitAttempts++; - const rect = container.getBoundingClientRect(); - if (rect.width > 10 && rect.height > 10) { - doFit(); - } else if (fitAttempts < 20) { - // Container still has no size — retry - setTimeout(tryFit, 50); - return; - } - }; - requestAnimationFrame(tryFit); + doFit(); + // Belt-and-braces: retry once after a paint in case the initial + // reflow dimensions were still stale. + requestAnimationFrame(doFit); // Forward user keystrokes to the backend term.onData((data) => {