fix(terminal): harden clipboard buffer and resize for production

Three fixes from code review:

1. Clear selection after Cmd+C so the terminal resumes receiving
   output immediately instead of staying frozen up to 5 seconds.

2. Add 1MB byte cap on the write buffer to prevent OOM if a fast
   process dumps output while the user has text selected.

3. Use ws.current instead of a stale captured websocket reference
   in the resize effect, preventing throws if the WebSocket
   reconnected during a fullscreen toggle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
suraj-markup 2026-03-02 01:17:16 +05:30
parent 09e2c2b540
commit 3fc51b8882
1 changed files with 22 additions and 8 deletions

View File

@ -197,12 +197,15 @@ export function DirectTerminal({
const writeBuffer: string[] = [];
let selectionActive = false;
let safetyTimer: ReturnType<typeof setTimeout> | null = null;
let bufferBytes = 0;
const MAX_BUFFER_BYTES = 1_048_576; // 1 MB
const flushWriteBuffer = () => {
if (safetyTimer) { clearTimeout(safetyTimer); safetyTimer = null; }
if (writeBuffer.length > 0) {
terminal.write(writeBuffer.join(""));
writeBuffer.length = 0;
bufferBytes = 0;
}
};
@ -235,6 +238,8 @@ export function DirectTerminal({
(e.ctrlKey && e.shiftKey && e.code === "KeyC");
if (isCopy && terminal.hasSelection()) {
navigator.clipboard?.writeText(terminal.getSelection()).catch(() => {});
// Clear selection so the terminal resumes receiving output
terminal.clearSelection();
return false;
}
@ -317,6 +322,12 @@ export function DirectTerminal({
typeof event.data === "string" ? event.data : new TextDecoder().decode(event.data);
if (selectionActive) {
writeBuffer.push(data);
bufferBytes += data.length;
// Flush if buffer exceeds 1 MB to prevent OOM
if (bufferBytes > MAX_BUFFER_BYTES) {
selectionActive = false;
flushWriteBuffer();
}
} else {
terminal.write(data);
}
@ -424,14 +435,17 @@ export function DirectTerminal({
fit.fit();
terminal.refresh(0, terminal.rows - 1);
// Send new size to server
websocket.send(
JSON.stringify({
type: "resize",
cols: terminal.cols,
rows: terminal.rows,
}),
);
// Send new size to server (use ws.current in case WebSocket reconnected)
const currentWs = ws.current;
if (currentWs?.readyState === WebSocket.OPEN) {
currentWs.send(
JSON.stringify({
type: "resize",
cols: terminal.cols,
rows: terminal.rows,
}),
);
}
};
// Start resize polling