diff --git a/pyproject.toml b/pyproject.toml index 95cb5f8..cf017ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual-webterm" -version = "0.3.29" +version = "0.3.30" description = "Serve terminal sessions over the web" authors = ["Will McGugan "] license = "MIT" diff --git a/src/textual_webterm/static/js/terminal.js b/src/textual_webterm/static/js/terminal.js index 9d7db88..af520de 100644 --- a/src/textual_webterm/static/js/terminal.js +++ b/src/textual_webterm/static/js/terminal.js @@ -14665,13 +14665,20 @@ class WebTerminal { this.send(["resize", { width: fallback.cols, height: fallback.rows }]); return; } - if (dims && this.isValidSize(dims.cols, dims.rows) && this.isTerminalReady()) { - this.terminal.resize(dims.cols, dims.rows); - this.resizeState.lastValidSize = dims; - this.send(["resize", { width: dims.cols, height: dims.rows }]); - } else { - const reason = !dims ? "proposeDimensions failed" : !this.isValidSize(dims.cols, dims.rows) ? `invalid dimensions: ${dims.cols}x${dims.rows}` : "terminal not ready"; - console.warn(`Initial fit ${reason}, using fallback`); + try { + if (dims && this.isValidSize(dims.cols, dims.rows) && this.isTerminalReady()) { + this.terminal.resize(dims.cols, dims.rows); + this.resizeState.lastValidSize = dims; + this.send(["resize", { width: dims.cols, height: dims.rows }]); + } else { + const reason = !dims ? "proposeDimensions failed" : !this.isValidSize(dims.cols, dims.rows) ? `invalid dimensions: ${dims.cols}x${dims.rows}` : "terminal not ready"; + console.warn(`Initial fit ${reason}, using fallback`); + this.terminal.resize(fallback.cols, fallback.rows); + this.resizeState.lastValidSize = fallback; + this.send(["resize", { width: fallback.cols, height: fallback.rows }]); + } + } catch (e) { + console.warn(`Initial fit failed with exception: ${e.message}, using fallback`); this.terminal.resize(fallback.cols, fallback.rows); this.resizeState.lastValidSize = fallback; this.send(["resize", { width: fallback.cols, height: fallback.rows }]); @@ -14684,6 +14691,22 @@ class WebTerminal { } else { init(); } + const fallbackTimeout = setTimeout(() => { + if (!this.resizeState.lastValidSize) { + console.warn("Initial fit timed out, applying fallback dimensions"); + const fallback = { cols: 80, rows: 24 }; + try { + this.terminal.resize(fallback.cols, fallback.rows); + this.resizeState.lastValidSize = fallback; + this.send(["resize", { width: fallback.cols, height: fallback.rows }]); + } catch (e) { + console.error("Fallback resize failed:", e); + } + } + }, 2000); + this.socket?.addEventListener("open", () => { + clearTimeout(fallbackTimeout); + }); this.terminal.focus(); }); this.socket.addEventListener("close", () => { diff --git a/src/textual_webterm/static/js/terminal.ts b/src/textual_webterm/static/js/terminal.ts index 9158a8e..453d8e3 100644 --- a/src/textual_webterm/static/js/terminal.ts +++ b/src/textual_webterm/static/js/terminal.ts @@ -373,15 +373,24 @@ class WebTerminal { } // Validate dimensions and terminal readiness before applying - if (dims && this.isValidSize(dims.cols, dims.rows) && this.isTerminalReady()) { - this.terminal.resize(dims.cols, dims.rows); - this.resizeState.lastValidSize = dims; - this.send(["resize", { width: dims.cols, height: dims.rows }]); - } else { - const reason = !dims ? "proposeDimensions failed" : - !this.isValidSize(dims.cols, dims.rows) ? `invalid dimensions: ${dims.cols}x${dims.rows}` : - "terminal not ready"; - console.warn(`Initial fit ${reason}, using fallback`); + // Use a defensive approach with multiple fallback strategies + try { + if (dims && this.isValidSize(dims.cols, dims.rows) && this.isTerminalReady()) { + this.terminal.resize(dims.cols, dims.rows); + this.resizeState.lastValidSize = dims; + this.send(["resize", { width: dims.cols, height: dims.rows }]); + } else { + const reason = !dims ? "proposeDimensions failed" : + !this.isValidSize(dims.cols, dims.rows) ? `invalid dimensions: ${dims.cols}x${dims.rows}` : + "terminal not ready"; + console.warn(`Initial fit ${reason}, using fallback`); + this.terminal.resize(fallback.cols, fallback.rows); + this.resizeState.lastValidSize = fallback; + this.send(["resize", { width: fallback.cols, height: fallback.rows }]); + } + } catch (e) { + console.warn(`Initial fit failed with exception: ${e.message}, using fallback`); + // If anything goes wrong, use the fallback dimensions this.terminal.resize(fallback.cols, fallback.rows); this.resizeState.lastValidSize = fallback; this.send(["resize", { width: fallback.cols, height: fallback.rows }]); @@ -396,6 +405,26 @@ class WebTerminal { } else { init(); } + + // Add a timeout-based fallback in case the initial fit never succeeds + const fallbackTimeout = setTimeout(() => { + if (!this.resizeState.lastValidSize) { + console.warn("Initial fit timed out, applying fallback dimensions"); + const fallback = { cols: 80, rows: 24 }; + try { + this.terminal.resize(fallback.cols, fallback.rows); + this.resizeState.lastValidSize = fallback; + this.send(["resize", { width: fallback.cols, height: fallback.rows }]); + } catch (e) { + console.error("Fallback resize failed:", e); + } + } + }, 2000); + + // Clean up timeout when WebSocket connects + this.socket?.addEventListener('open', () => { + clearTimeout(fallbackTimeout); + }); // Focus terminal this.terminal.focus();