summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src/components')
-rw-r--r--packages/app/src/components/terminal.tsx58
1 files changed, 33 insertions, 25 deletions
diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx
index 085a79613..bd7ab2447 100644
--- a/packages/app/src/components/terminal.tsx
+++ b/packages/app/src/components/terminal.tsx
@@ -320,8 +320,6 @@ export const Terminal = (props: TerminalProps) => {
const mod = loaded.mod
const g = loaded.ghostty
- const once = { value: false }
-
const restore = typeof local.pty.buffer === "string" ? local.pty.buffer : ""
const restoreSize =
restore &&
@@ -416,20 +414,28 @@ export const Terminal = (props: TerminalProps) => {
cleanups.push(() => window.removeEventListener("resize", handleResize))
}
- if (restore && restoreSize) {
- t.write(restore, () => {
- fit.fit()
- scheduleSize(t.cols, t.rows)
- if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY)
- startResize()
+ const write = (data: string) =>
+ new Promise<void>((resolve) => {
+ if (!output) {
+ resolve()
+ return
+ }
+ output.push(data)
+ output.flush(resolve)
})
+
+ if (restore && restoreSize) {
+ await write(restore)
+ fit.fit()
+ scheduleSize(t.cols, t.rows)
+ if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY)
+ startResize()
} else {
fit.fit()
scheduleSize(t.cols, t.rows)
if (restore) {
- t.write(restore, () => {
- if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY)
- })
+ await write(restore)
+ if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY)
}
startResize()
}
@@ -438,38 +444,32 @@ export const Terminal = (props: TerminalProps) => {
// console.log("Scroll position:", ydisp)
// })
+ const once = { value: false }
+ let closing = false
+
const url = new URL(sdk.url + `/pty/${local.pty.id}/connect`)
url.searchParams.set("directory", sdk.directory)
url.searchParams.set("cursor", String(start !== undefined ? start : local.pty.buffer ? -1 : 0))
url.protocol = url.protocol === "https:" ? "wss:" : "ws:"
url.username = server.current?.http.username ?? ""
url.password = server.current?.http.password ?? ""
+
const socket = new WebSocket(url)
socket.binaryType = "arraybuffer"
ws = socket
- cleanups.push(() => {
- if (socket.readyState !== WebSocket.CLOSED && socket.readyState !== WebSocket.CLOSING) socket.close()
- })
- if (disposed) {
- cleanup()
- return
- }
const handleOpen = () => {
local.onConnect?.()
scheduleSize(t.cols, t.rows)
}
socket.addEventListener("open", handleOpen)
- cleanups.push(() => socket.removeEventListener("open", handleOpen))
-
if (socket.readyState === WebSocket.OPEN) handleOpen()
const decoder = new TextDecoder()
-
const handleMessage = (event: MessageEvent) => {
if (disposed) return
+ if (closing) return
if (event.data instanceof ArrayBuffer) {
- // WebSocket control frame: 0x00 + UTF-8 JSON (currently { cursor }).
const bytes = new Uint8Array(event.data)
if (bytes[0] !== 0) return
const json = decoder.decode(bytes.subarray(1))
@@ -491,20 +491,20 @@ export const Terminal = (props: TerminalProps) => {
cursor += data.length
}
socket.addEventListener("message", handleMessage)
- cleanups.push(() => socket.removeEventListener("message", handleMessage))
const handleError = (error: Event) => {
if (disposed) return
+ if (closing) return
if (once.value) return
once.value = true
console.error("WebSocket error:", error)
local.onConnectError?.(error)
}
socket.addEventListener("error", handleError)
- cleanups.push(() => socket.removeEventListener("error", handleError))
const handleClose = (event: CloseEvent) => {
if (disposed) return
+ if (closing) return
// Normal closure (code 1000) means PTY process exited - server event handles cleanup
// For other codes (network issues, server restart), trigger error handler
if (event.code !== 1000) {
@@ -514,7 +514,15 @@ export const Terminal = (props: TerminalProps) => {
}
}
socket.addEventListener("close", handleClose)
- cleanups.push(() => socket.removeEventListener("close", handleClose))
+
+ cleanups.push(() => {
+ closing = true
+ socket.removeEventListener("open", handleOpen)
+ socket.removeEventListener("message", handleMessage)
+ socket.removeEventListener("error", handleError)
+ socket.removeEventListener("close", handleClose)
+ if (socket.readyState !== WebSocket.CLOSED && socket.readyState !== WebSocket.CLOSING) socket.close(1000)
+ })
}
void run().catch((err) => {