mirror of
https://github.com/stablyai/orca
synced 2026-04-21 14:17:16 +00:00
fix(terminal): prevent WebGL context leak and atomic generation bump (#806)
This commit is contained in:
parent
3918e4cfcf
commit
e8b8b5f96e
2 changed files with 31 additions and 20 deletions
|
|
@ -34,7 +34,14 @@ export function useTerminalPaneGlobalEffects({
|
|||
isVisibleRef,
|
||||
toggleExpandPane
|
||||
}: UseTerminalPaneGlobalEffectsArgs): void {
|
||||
const wasVisibleRef = useRef(false)
|
||||
// Why: starts as `true` so the first render with isVisible=false triggers
|
||||
// suspendRendering(). Without this, background worktrees that mount hidden
|
||||
// (isVisible=false from the start) never suspend their WebGL contexts —
|
||||
// openTerminal() unconditionally creates a WebGL addon, but this effect
|
||||
// only suspends on true→false transitions. The leaked contexts exhaust
|
||||
// Chromium's ~8-context budget, causing "webglcontextlost" on visible
|
||||
// terminals and making them unresponsive.
|
||||
const wasVisibleRef = useRef(true)
|
||||
|
||||
// Why: tracks any in-progress chunked pending-write flush so the cleanup
|
||||
// function can cancel it if the pane deactivates mid-flush.
|
||||
|
|
|
|||
|
|
@ -529,6 +529,27 @@ export const createWorktreeSlice: StateCreator<AppState, [], [], WorktreeSlice>
|
|||
// side-effects limited to unread clearing; true activity signals such as
|
||||
// PTY lifecycle and explicit edits still flow through bumpWorktreeActivity.
|
||||
const metaUpdates: Partial<WorktreeMeta> = shouldClearUnread ? { isUnread: false } : {}
|
||||
|
||||
// Why: the generation bump for dead-PTY tabs MUST happen in the same
|
||||
// set() as the activation. Two separate set() calls let React/Zustand
|
||||
// render the old (dead-transport) TerminalPane as visible for one frame
|
||||
// before the generation bump unmounts it — that intermediate render
|
||||
// resumes the pane with a transport stuck at connected=false/ptyId=null,
|
||||
// and user input is silently dropped.
|
||||
const tabs = s.tabsByWorktree[worktreeId ?? ''] ?? []
|
||||
const allDead = worktreeId && tabs.length > 0 && tabs.every((tab) => !tab.ptyId)
|
||||
const tabsByWorktreeUpdate = allDead
|
||||
? {
|
||||
tabsByWorktree: {
|
||||
...s.tabsByWorktree,
|
||||
[worktreeId!]: tabs.map((tab) => ({
|
||||
...tab,
|
||||
generation: (tab.generation ?? 0) + 1
|
||||
}))
|
||||
}
|
||||
}
|
||||
: {}
|
||||
|
||||
return {
|
||||
activeWorktreeId: worktreeId,
|
||||
activeFileId,
|
||||
|
|
@ -538,28 +559,11 @@ export const createWorktreeSlice: StateCreator<AppState, [], [], WorktreeSlice>
|
|||
activeTabId,
|
||||
...(shouldClearUnread
|
||||
? { worktreesByRepo: applyWorktreeUpdates(s.worktreesByRepo, worktreeId, metaUpdates) }
|
||||
: {})
|
||||
: {}),
|
||||
...tabsByWorktreeUpdate
|
||||
}
|
||||
})
|
||||
|
||||
// If the worktree has tabs but all PTYs are dead (e.g. after shutdown),
|
||||
// bump generation so TerminalPanes remount with fresh PTY connections.
|
||||
if (worktreeId) {
|
||||
const tabs = get().tabsByWorktree[worktreeId] ?? []
|
||||
const allDead = tabs.length > 0 && tabs.every((tab) => !tab.ptyId)
|
||||
if (allDead) {
|
||||
set((s) => ({
|
||||
tabsByWorktree: {
|
||||
...s.tabsByWorktree,
|
||||
[worktreeId]: (s.tabsByWorktree[worktreeId] ?? []).map((tab) => ({
|
||||
...tab,
|
||||
generation: (tab.generation ?? 0) + 1
|
||||
}))
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// Why: force-refreshing GitHub data on every switch burned API rate limit
|
||||
// quota and added 200-800ms latency. Only refresh when cache is actually
|
||||
// stale (>5 min old). Users can still force-refresh via the sidebar button.
|
||||
|
|
|
|||
Loading…
Reference in a new issue