Skip to content

Commit 20f4337

Browse files
authored
fix(app): terminal disconnect and resync (anomalyco#14004)
1 parent fb79dd7 commit 20f4337

3 files changed

Lines changed: 91 additions & 12 deletions

File tree

packages/app/src/components/terminal.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ export const Terminal = (props: TerminalProps) => {
346346
}
347347
ghostty = g
348348
term = t
349-
output = terminalWriter((data) => t.write(data))
349+
output = terminalWriter((data, done) => t.write(data, done))
350350

351351
t.attachCustomKeyEventHandler((event) => {
352352
const key = event.key.toLowerCase()
@@ -520,9 +520,19 @@ export const Terminal = (props: TerminalProps) => {
520520
disposed = true
521521
if (fitFrame !== undefined) cancelAnimationFrame(fitFrame)
522522
if (sizeTimer !== undefined) clearTimeout(sizeTimer)
523-
output?.flush()
524-
persistTerminal({ term, addon: serializeAddon, cursor, pty: local.pty, onCleanup: props.onCleanup })
525-
cleanup()
523+
if (ws && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) ws.close()
524+
525+
const finalize = () => {
526+
persistTerminal({ term, addon: serializeAddon, cursor, pty: local.pty, onCleanup: props.onCleanup })
527+
cleanup()
528+
}
529+
530+
if (!output) {
531+
finalize()
532+
return
533+
}
534+
535+
output.flush(finalize)
526536
})
527537

528538
return (

packages/app/src/utils/terminal-writer.test.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ describe("terminalWriter", () => {
66
const calls: string[] = []
77
const scheduled: VoidFunction[] = []
88
const writer = terminalWriter(
9-
(data) => calls.push(data),
9+
(data, done) => {
10+
calls.push(data)
11+
done?.()
12+
},
1013
(flush) => scheduled.push(flush),
1114
)
1215

@@ -24,10 +27,38 @@ describe("terminalWriter", () => {
2427
test("flush is a no-op when empty", () => {
2528
const calls: string[] = []
2629
const writer = terminalWriter(
27-
(data) => calls.push(data),
30+
(data, done) => {
31+
calls.push(data)
32+
done?.()
33+
},
2834
(flush) => flush(),
2935
)
3036
writer.flush()
3137
expect(calls).toEqual([])
3238
})
39+
40+
test("flush waits for pending write completion", () => {
41+
const calls: string[] = []
42+
let done: VoidFunction | undefined
43+
const writer = terminalWriter(
44+
(data, finish) => {
45+
calls.push(data)
46+
done = finish
47+
},
48+
(flush) => flush(),
49+
)
50+
51+
writer.push("a")
52+
53+
let settled = false
54+
writer.flush(() => {
55+
settled = true
56+
})
57+
58+
expect(calls).toEqual(["a"])
59+
expect(settled).toBe(false)
60+
61+
done?.()
62+
expect(settled).toBe(true)
63+
})
3364
})

packages/app/src/utils/terminal-writer.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,64 @@
11
export function terminalWriter(
2-
write: (data: string) => void,
2+
write: (data: string, done?: VoidFunction) => void,
33
schedule: (flush: VoidFunction) => void = queueMicrotask,
44
) {
55
let chunks: string[] | undefined
6+
let waits: VoidFunction[] | undefined
67
let scheduled = false
8+
let writing = false
79

8-
const flush = () => {
10+
const settle = () => {
11+
if (scheduled || writing || chunks?.length) return
12+
const list = waits
13+
if (!list?.length) return
14+
waits = undefined
15+
for (const fn of list) {
16+
fn()
17+
}
18+
}
19+
20+
const run = () => {
21+
if (writing) return
922
scheduled = false
1023
const items = chunks
11-
if (!items?.length) return
24+
if (!items?.length) {
25+
settle()
26+
return
27+
}
1228
chunks = undefined
13-
write(items.join(""))
29+
writing = true
30+
write(items.join(""), () => {
31+
writing = false
32+
if (chunks?.length) {
33+
if (scheduled) return
34+
scheduled = true
35+
schedule(run)
36+
return
37+
}
38+
settle()
39+
})
1440
}
1541

1642
const push = (data: string) => {
1743
if (!data) return
1844
if (chunks) chunks.push(data)
1945
else chunks = [data]
2046

21-
if (scheduled) return
47+
if (scheduled || writing) return
2248
scheduled = true
23-
schedule(flush)
49+
schedule(run)
50+
}
51+
52+
const flush = (done?: VoidFunction) => {
53+
if (!scheduled && !writing && !chunks?.length) {
54+
done?.()
55+
return
56+
}
57+
if (done) {
58+
if (waits) waits.push(done)
59+
else waits = [done]
60+
}
61+
run()
2462
}
2563

2664
return { push, flush }

0 commit comments

Comments
 (0)