From 6324d1c351618c5f7146a3d31d6b4f78b16f256c Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 6 Feb 2026 09:54:38 -0600 Subject: [PATCH] fix(app): more terminal replay issues --- packages/app/src/components/terminal.tsx | 45 ++++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx index 2ee2e074e..64adc797c 100644 --- a/packages/app/src/components/terminal.tsx +++ b/packages/app/src/components/terminal.tsx @@ -287,6 +287,27 @@ export const Terminal = (props: TerminalProps) => { handleResize = () => fit.fit() window.addEventListener("resize", handleResize) cleanups.push(() => window.removeEventListener("resize", handleResize)) + const limit = 16_384 + const min = 32 + const windowMs = 750 + const seed = tail.length > limit ? tail.slice(-limit) : tail + let sync = seed.length >= min + let syncUntil = 0 + const stopSync = () => { + sync = false + syncUntil = 0 + } + + const overlap = (data: string) => { + if (!seed) return 0 + const max = Math.min(seed.length, data.length) + if (max < min) return 0 + for (let i = max; i >= min; i--) { + if (seed.slice(-i) === data.slice(0, i)) return i + } + return 0 + } + const onResize = t.onResize(async (size) => { if (socket.readyState === WebSocket.OPEN) { await sdk.client.pty @@ -302,6 +323,7 @@ export const Terminal = (props: TerminalProps) => { }) cleanups.push(() => disposeIfDisposable(onResize)) const onData = t.onData((data) => { + if (data) stopSync() if (socket.readyState === WebSocket.OPEN) { socket.send(data) } @@ -317,21 +339,9 @@ export const Terminal = (props: TerminalProps) => { // console.log("Scroll position:", ydisp) // }) - const limit = 16_384 - const seed = tail - let sync = !!seed - - const overlap = (data: string) => { - if (!seed) return 0 - const max = Math.min(seed.length, data.length) - for (let i = max; i > 0; i--) { - if (seed.slice(-i) === data.slice(0, i)) return i - } - return 0 - } - const handleOpen = () => { local.onConnect?.() + if (sync) syncUntil = Date.now() + windowMs sdk.client.pty .update({ ptyID: local.pty.id, @@ -346,18 +356,23 @@ export const Terminal = (props: TerminalProps) => { cleanups.push(() => socket.removeEventListener("open", handleOpen)) const handleMessage = (event: MessageEvent) => { + if (disposed) return const data = typeof event.data === "string" ? event.data : "" if (!data) return const next = (() => { if (!sync) return data + if (syncUntil && Date.now() > syncUntil) { + stopSync() + return data + } const n = overlap(data) if (!n) { - sync = false + stopSync() return data } const trimmed = data.slice(n) - if (trimmed) sync = false + if (trimmed) stopSync() return trimmed })()