diff options
| author | Adam <[email protected]> | 2026-02-05 11:28:22 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-02-05 14:21:13 -0600 |
| commit | 287540551427198a02811e85d90547a17ac6164e (patch) | |
| tree | 1c0cc702641780d9bdbe50d77a5eab666087116c | |
| parent | 8c0300c021dd70e363ee0ffa966deb4b974803c1 (diff) | |
| download | opencode-287540551427198a02811e85d90547a17ac6164e.tar.gz opencode-287540551427198a02811e85d90547a17ac6164e.zip | |
fix(app): more terminal stability fixes
| -rw-r--r-- | bun.lock | 7 | ||||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | packages/app/package.json | 2 | ||||
| -rw-r--r-- | packages/app/src/components/terminal.tsx | 46 | ||||
| -rw-r--r-- | packages/app/src/context/terminal.tsx | 1 |
5 files changed, 43 insertions, 17 deletions
@@ -44,7 +44,7 @@ "@thisbeyond/solid-dnd": "0.7.5", "diff": "catalog:", "fuzzysort": "catalog:", - "ghostty-web": "0.3.0", + "ghostty-web": "0.4.0", "luxon": "catalog:", "marked": "catalog:", "marked-shiki": "catalog:", @@ -496,9 +496,6 @@ "web-tree-sitter", "tree-sitter-bash", ], - "patchedDependencies": { - "[email protected]": "patches/[email protected]", - }, "overrides": { "@types/bun": "catalog:", "@types/node": "catalog:", @@ -2605,7 +2602,7 @@ "get-tsconfig": ["[email protected]", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], - "ghostty-web": ["[email protected]", "", {}, "sha512-SAdSHWYF20GMZUB0n8kh1N6Z4ljMnuUqT8iTB2n5FAPswEV10MejEpLlhW/769GL5+BQa1NYwEg9y/XCckV5+A=="], + "ghostty-web": ["[email protected]", "", {}, "sha512-0puDBik2qapbD/QQBW9o5ZHfXnZBqZWx/ctBiVtKZ6ZLds4NYb+wZuw1cRLXZk9zYovIQ908z3rvFhexAvc5Hg=="], "gifwrap": ["[email protected]", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="], diff --git a/package.json b/package.json index b052e2bbf..65cd0dea8 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,5 @@ "@types/bun": "catalog:", "@types/node": "catalog:" }, - "patchedDependencies": { - "[email protected]": "patches/[email protected]" - } + "patchedDependencies": {} } diff --git a/packages/app/package.json b/packages/app/package.json index 81e589e38..12688ac07 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -54,7 +54,7 @@ "@thisbeyond/solid-dnd": "0.7.5", "diff": "catalog:", "fuzzysort": "catalog:", - "ghostty-web": "0.3.0", + "ghostty-web": "0.4.0", "luxon": "catalog:", "marked": "catalog:", "marked-shiki": "catalog:", diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx index e87670f59..4d44d5f7e 100644 --- a/packages/app/src/components/terminal.tsx +++ b/packages/app/src/components/terminal.tsx @@ -70,6 +70,7 @@ export const Terminal = (props: TerminalProps) => { let handleTextareaBlur: () => void let disposed = false const cleanups: VoidFunction[] = [] + let tail = local.pty.tail ?? "" const cleanup = () => { if (!cleanups.length) return @@ -256,6 +257,7 @@ export const Terminal = (props: TerminalProps) => { serializeAddon = serializer t.open(container) + container.addEventListener("pointerdown", handlePointerDown) cleanups.push(() => container.removeEventListener("pointerdown", handlePointerDown)) @@ -276,15 +278,11 @@ export const Terminal = (props: TerminalProps) => { focusTerminal() + fit.fit() + if (local.pty.buffer) { - if (local.pty.rows && local.pty.cols) { - t.resize(local.pty.cols, local.pty.rows) - } t.write(local.pty.buffer, () => { - if (local.pty.scrollY) { - t.scrollToLine(local.pty.scrollY) - } - fitAddon.fit() + if (local.pty.scrollY) t.scrollToLine(local.pty.scrollY) }) } @@ -322,6 +320,19 @@ 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?.() sdk.client.pty @@ -338,7 +349,25 @@ export const Terminal = (props: TerminalProps) => { cleanups.push(() => socket.removeEventListener("open", handleOpen)) const handleMessage = (event: MessageEvent) => { - t.write(event.data) + const data = typeof event.data === "string" ? event.data : "" + if (!data) return + + const next = (() => { + if (!sync) return data + const n = overlap(data) + if (!n) { + sync = false + return data + } + const trimmed = data.slice(n) + if (trimmed) sync = false + return trimmed + })() + + if (!next) return + + t.write(next) + tail = next.length >= limit ? next.slice(-limit) : (tail + next).slice(-limit) } socket.addEventListener("message", handleMessage) cleanups.push(() => socket.removeEventListener("message", handleMessage)) @@ -392,6 +421,7 @@ export const Terminal = (props: TerminalProps) => { props.onCleanup({ ...local.pty, buffer, + tail, rows: t.rows, cols: t.cols, scrollY: t.getViewportY(), diff --git a/packages/app/src/context/terminal.tsx b/packages/app/src/context/terminal.tsx index e01b8bc4d..0c383a78d 100644 --- a/packages/app/src/context/terminal.tsx +++ b/packages/app/src/context/terminal.tsx @@ -13,6 +13,7 @@ export type LocalPTY = { cols?: number buffer?: string scrollY?: number + tail?: string } const WORKSPACE_KEY = "__workspace__" |
