Fix PTY listener leak, use local restty with selection support (#5)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Neil 2026-03-20 10:14:20 -07:00 committed by GitHub
parent f75609ccc5
commit 0cd33818f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 30 additions and 11 deletions

View File

@ -38,7 +38,7 @@
"lucide-react": "^0.577.0",
"node-pty": "^1.1.0",
"radix-ui": "^1.4.3",
"restty": "^0.1.34",
"restty": "github:nwparker/restty#orca/desktop-word-line-selection",
"shadcn": "^4.0.8",
"simple-git": "^3.33.0",
"tailwind-merge": "^3.5.0",

View File

@ -45,8 +45,8 @@ importers:
specifier: ^1.4.3
version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
restty:
specifier: ^0.1.34
version: 0.1.34(typescript@5.9.3)
specifier: github:nwparker/restty#orca/desktop-word-line-selection
version: https://codeload.github.com/nwparker/restty/tar.gz/fb8a604be99f570fb437b414790ef36fe85c8f17(typescript@5.9.3)
shadcn:
specifier: ^4.0.8
version: 4.0.8(@types/node@25.5.0)(typescript@5.9.3)
@ -3798,8 +3798,9 @@ packages:
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
engines: {node: '>=18'}
restty@0.1.34:
resolution: {integrity: sha512-466w3EXe/yLNfO8Jbha6tLLTij1QRI2MnKbm0UHCmP33Avon4YikIeIIPW1ga1A9310poAcnkoXPeV0b84Yyxw==}
restty@https://codeload.github.com/nwparker/restty/tar.gz/fb8a604be99f570fb437b414790ef36fe85c8f17:
resolution: {tarball: https://codeload.github.com/nwparker/restty/tar.gz/fb8a604be99f570fb437b414790ef36fe85c8f17}
version: 0.1.34
engines: {bun: '>=1.2.0'}
retry@0.12.0:
@ -8146,7 +8147,7 @@ snapshots:
onetime: 7.0.0
signal-exit: 4.1.0
restty@0.1.34(typescript@5.9.3):
restty@https://codeload.github.com/nwparker/restty/tar.gz/fb8a604be99f570fb437b414790ef36fe85c8f17(typescript@5.9.3):
dependencies:
text-shaper: 0.1.18(typescript@5.9.3)
transitivePeerDependencies:

View File

@ -88,6 +88,7 @@ function createIpcPtyTransport(
onBell?: () => void
): PtyTransport {
let connected = false
let destroyed = false
let ptyId: string | null = null
let pendingEscape = false
let inOsc = false
@ -103,6 +104,13 @@ function createIpcPtyTransport(
let unsubData: (() => void) | null = null
let unsubExit: (() => void) | null = null
function cleanupListeners(): void {
unsubData?.()
unsubExit?.()
unsubData = null
unsubExit = null
}
return {
async connect(options) {
storedCallbacks = options.callbacks
@ -113,10 +121,20 @@ function createIpcPtyTransport(
rows: options.rows ?? 24,
cwd
})
// If destroyed while spawn was in flight, kill the new pty and bail
if (destroyed) {
window.api.pty.kill(result.id)
return
}
ptyId = result.id
connected = true
onPtySpawn?.(result.id)
// Clean up any stale listeners before registering new ones
cleanupListeners()
unsubData = window.api.pty.onData((payload) => {
if (payload.id === ptyId) {
storedCallbacks.onData?.(payload.data)
@ -154,10 +172,7 @@ function createIpcPtyTransport(
window.api.pty.kill(ptyId)
connected = false
ptyId = null
unsubData?.()
unsubExit?.()
unsubData = null
unsubExit = null
cleanupListeners()
storedCallbacks.onDisconnect?.()
}
},
@ -179,6 +194,7 @@ function createIpcPtyTransport(
},
destroy() {
destroyed = true
this.disconnect()
}
}
@ -623,7 +639,9 @@ export default function TerminalPane({
renderer: 'auto',
fontSize: currentSettings?.terminalFontSize ?? 14,
fontSizeMode: 'em',
alphaBlending: 'native',
fontHinting: true,
fontHintTarget: 'light',
alphaBlending: 'linear-corrected',
maxScrollbackBytes: currentSettings?.terminalScrollbackBytes ?? 10_000_000,
ptyTransport: createIpcPtyTransport(
cwd,