From 92d2090593590f583f665b14cef77d67ea8ef060 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 20 Jul 2026 22:13:57 -0700 Subject: [PATCH] perf(checks-panel): gate the terminal-cwd poll on window visibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Checks panel polls window.api.pty.getCwd every 4s to follow the active terminal's cwd. It was gated only on panel visibility (isPanelVisible), not window visibility — so a hidden or minimized window kept spawning lsof on macOS every 4s (the 4s cadence exceeds getCwd's 1.5s per-pid cache TTL, so every tick is a guaranteed miss and a fresh subprocess). Swap the raw window.setInterval for installWindowVisibilityInterval — the shared helper every sibling timer already uses — which pauses while the window is hidden and runs an immediate refresh on becoming visible so a cd made while hidden is picked up on return. A visible-but-unfocused window (second monitor) keeps polling, matching prior behavior. Co-authored-by: Orca --- ...use-checks-panel-terminal-worktree.test.ts | 30 +++++++++++++++++++ .../use-checks-panel-terminal-worktree.ts | 17 ++++++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.test.ts b/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.test.ts index 6088c511be8..7d194d2bf00 100644 --- a/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.test.ts +++ b/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.test.ts @@ -129,6 +129,36 @@ describe('useChecksPanelTerminalWorktree', () => { expect(latest?.worktree).toBe(parentWorktree) }) + it('does not spawn getCwd while the window is hidden, then refreshes on becoming visible', async () => { + getCwdMock.mockResolvedValue(`${CHILD_PATH}/src`) + const setVisibility = (state: 'visible' | 'hidden'): void => { + Object.defineProperty(document, 'visibilityState', { + configurable: true, + get: () => state + }) + } + setVisibility('hidden') + try { + await renderHook(parentWorktree) + await flushMicrotasks() + // Hidden window: no lsof-backed cwd probe at all. + expect(getCwdMock).not.toHaveBeenCalled() + expect(latest?.worktree).toBe(parentWorktree) + + setVisibility('visible') + await act(async () => { + document.dispatchEvent(new Event('visibilitychange')) + }) + await flushMicrotasks() + // Becoming visible runs an immediate refresh (a `cd` made while hidden + // is picked up promptly on return). + expect(getCwdMock).toHaveBeenCalledWith('pty-1') + expect(latest?.worktree).toBe(childWorktree) + } finally { + setVisibility('visible') + } + }) + it('keeps the default worktree when the cwd is outside every worktree', async () => { getCwdMock.mockResolvedValue('/tmp/scratch') diff --git a/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.ts b/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.ts index 67b9416f488..eb902500547 100644 --- a/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.ts +++ b/src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.ts @@ -1,6 +1,7 @@ import { useEffect, useMemo, useState } from 'react' import { useShallow } from 'zustand/react/shallow' import { useAppStore } from '@/store' +import { installWindowVisibilityInterval } from '@/lib/window-visibility-interval' import { useAllWorktrees, useRepoMap } from '@/store/selectors' import { isRemoteRuntimePtyId } from '@/runtime/runtime-terminal-inspection' import { parseAppSshPtyId } from '../../../../shared/ssh-pty-id' @@ -31,7 +32,8 @@ type ChecksPanelTerminalWorktree = { * the *remote* shell's OSC 7 path, which must not resolve a local worktree. * getCwd reads the local shell pid, so it is correct for that case. Remote * runtime PTYs are skipped (their cwd lives on the relay host). Polling is - * gated on panel visibility, so a hidden panel does no background work and the + * gated on both panel visibility and window visibility, so a hidden panel or a + * hidden/minimized window does no background work (no `lsof` spawns) and the * caller's fallback worktree is used. * * Resolution is scoped to locally-executing worktrees: the cwd comes from a @@ -123,11 +125,18 @@ export function useChecksPanelTerminalWorktree(args: { } } - void refresh() - const interval = window.setInterval(refresh, TERMINAL_CWD_POLL_MS) + // Why: getCwd shells out to `lsof` on macOS every tick (poll cadence > + // the 1.5s per-pid cache TTL, so each tick is a guaranteed miss). Gate on + // window visibility so a hidden/minimized window spawns no subprocesses; + // the helper runs an immediate refresh on becoming visible so a `cd` made + // while hidden is picked up promptly on return. + const stopInterval = installWindowVisibilityInterval({ + run: () => void refresh(), + intervalMs: TERMINAL_CWD_POLL_MS + }) return () => { disposed = true - window.clearInterval(interval) + stopInterval() } }, [activeTerminalPtyId, shouldPollCwd])