Stabilize initial terminal sizing

This commit is contained in:
GitHub Copilot
2026-01-27 23:27:02 +00:00
parent 5e84d00a30
commit cb6ea35c94
3 changed files with 51 additions and 36 deletions
+34 -19
View File
@@ -59,6 +59,7 @@ class WebTerminal {
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private reconnectDelay = 1000;
private resizeEventsEnabled = false;
constructor(container: HTMLElement, wsUrl: string, config: TerminalConfig = {}) {
this.element = container;
@@ -114,6 +115,9 @@ class WebTerminal {
// Handle resize
this.terminal.onResize(({ cols, rows }) => {
if (!this.resizeEventsEnabled) {
return;
}
this.send(["resize", { width: cols, height: rows }]);
});
@@ -135,11 +139,7 @@ class WebTerminal {
if (!("fonts" in document)) {
return;
}
const fontSet = document.fonts;
if (fontSet.status === "loaded") {
return;
}
fontSet.ready
document.fonts.ready
.then(() => this.scheduleFit())
.catch(() => {
// Ignore font readiness errors; resize observer will handle future resizes.
@@ -180,25 +180,40 @@ class WebTerminal {
this.element.classList.remove("-disconnected");
// Send initial size.
// Important: avoid sending a bogus early resize before fonts/layout settle,
// otherwise the PTY will hard-wrap output at the wrong column count.
this.fit();
const sendResize = () => {
const dims = this.fitAddon.proposeDimensions();
if (dims) {
// Important: the PTY hard-wraps output based on its initial cols/rows.
// If we send a resize before fonts/layout settle, the initial cols can be
// too small and the shell will wrap permanently.
this.resizeEventsEnabled = false;
const init = () => {
const fallback = { cols: 132, rows: 45 };
const maxAttempts = 120;
const attemptFitAndResize = (attempt: number) => {
const dims = this.fitAddon.proposeDimensions();
if (!dims) {
if (attempt < maxAttempts) {
window.requestAnimationFrame(() => attemptFitAndResize(attempt + 1));
return;
}
this.terminal.resize(fallback.cols, fallback.rows);
this.resizeEventsEnabled = true;
this.send(["resize", { width: fallback.cols, height: fallback.rows }]);
return;
}
this.terminal.resize(dims.cols, dims.rows);
this.resizeEventsEnabled = true;
this.send(["resize", { width: dims.cols, height: dims.rows }]);
}
};
window.requestAnimationFrame(() => attemptFitAndResize(0));
};
if ("fonts" in document) {
document.fonts.ready
.then(() => {
this.scheduleFit();
sendResize();
})
.catch(() => sendResize());
document.fonts.ready.then(init).catch(init);
} else {
sendResize();
init();
}
// Focus terminal