fix: smooth terminal streaming

This commit is contained in:
Mohamed Boudra
2026-04-21 20:47:17 +07:00
parent 39db3ea5ff
commit 6a03873d81
9 changed files with 1126 additions and 19 deletions
@@ -8,6 +8,7 @@ import type { ITheme } from "@xterm/xterm";
import type { TerminalState } from "@server/shared/messages";
import type { PendingTerminalModifiers } from "../utils/terminal-keys";
import { TerminalEmulatorRuntime } from "../terminal/runtime/terminal-emulator-runtime";
import { openExternalUrl } from "../utils/open-external-url";
import { focusWithRetries } from "../utils/web-focus";
import {
computeScrollOffsetFromDragDelta,
@@ -325,6 +326,7 @@ export default function TerminalEmulator({
onResize,
onTerminalKey,
onPendingModifiersConsumed,
onOpenExternalUrl: openExternalUrl,
},
});
runtime.setPendingModifiers({ pendingModifiers });
@@ -350,6 +352,7 @@ export default function TerminalEmulator({
onResize,
onTerminalKey,
onPendingModifiersConsumed,
onOpenExternalUrl: openExternalUrl,
},
});
}, [onInput, onPendingModifiersConsumed, onResize, onTerminalKey]);
@@ -8,7 +8,6 @@ import { WebglAddon } from "@xterm/addon-webgl";
import { LigaturesAddon } from "@xterm/addon-ligatures/lib/addon-ligatures.mjs";
import { Terminal, type ITheme } from "@xterm/xterm";
import type { TerminalState } from "@server/shared/messages";
import { openExternalUrl } from "@/utils/open-external-url";
import {
type PendingTerminalModifiers,
isTerminalModifierDomKey,
@@ -37,6 +36,7 @@ export type TerminalEmulatorRuntimeCallbacks = {
meta: boolean;
}) => Promise<void> | void;
onPendingModifiersConsumed?: () => Promise<void> | void;
onOpenExternalUrl?: (url: string) => Promise<void> | void;
};
type TerminalEmulatorRuntimeDisposables = {
@@ -80,6 +80,7 @@ const isMac =
const DEFAULT_TOUCH_SCROLL_LINE_HEIGHT_PX = 18;
const FIT_TIMEOUT_DELAYS_MS = [0, 16, 48, 120, 250, 500, 1_000, 2_000];
const OUTPUT_OPERATION_TIMEOUT_MS = 5_000;
const RESET_TERMINAL_ANSI = "\u001bc";
const DEFAULT_TERMINAL_FONT_FAMILY = [
// Prefer common developer fonts, with Nerd Font variants for prompt/TUI glyphs.
@@ -177,7 +178,7 @@ export class TerminalEmulatorRuntime {
terminal.loadAddon(
new WebLinksAddon((event, uri) => {
event.preventDefault();
void openExternalUrl(uri);
void this.callbacks.onOpenExternalUrl?.(uri);
}),
);
terminal.loadAddon(new SearchAddon({ highlightLimit: 20_000 }));
@@ -264,6 +265,7 @@ export class TerminalEmulatorRuntime {
}
this.lastSize = { rows: nextRows, cols: nextCols };
this.refreshVisibleRows();
this.callbacks.onResize?.({
rows: nextRows,
cols: nextCols,
@@ -509,7 +511,7 @@ export class TerminalEmulatorRuntime {
}
this.outputOperations.push({
type: "snapshot",
text: renderTerminalSnapshotToAnsi(input.state),
text: `${RESET_TERMINAL_ANSI}${renderTerminalSnapshotToAnsi(input.state)}`,
rows: input.state.rows,
cols: input.state.cols,
suppressInput: true,
@@ -535,17 +537,26 @@ export class TerminalEmulatorRuntime {
return;
}
try {
terminal.refresh(0, Math.max(0, terminal.rows - 1));
} catch {
// ignore
}
this.refreshVisibleRows();
}
focus(): void {
this.terminal?.focus();
}
private refreshVisibleRows(): void {
const terminal = this.terminal;
if (!terminal || terminal.rows <= 0) {
return;
}
try {
terminal.refresh(0, terminal.rows - 1);
} catch {
// ignore
}
}
unmount(): void {
this.clearInFlightOutputTimeout();
const inFlightOperation = this.inFlightOutputOperation;
@@ -616,7 +627,6 @@ export class TerminalEmulatorRuntime {
) {
terminal.resize(operation.cols, operation.rows);
}
terminal.reset();
} catch {
finalizeOperation(operation);
return;