perf(desktop): stop hidden right-pane and background work on every git change

Merged
KI
17h ago
+196-60

Links

  • Issue: #9136 (desktop freezes after 2.0.2). This PR does not close it: the reporter's freeze is not reproduced. See Known Limitations.

Summary

  • A closed right pane no longer re-renders its Changes pane on every git change. Since #9008 the pane stays mounted when closed, but its panes were never told they were hidden.
  • The base-branch picker builds its branch rows only while its popover is open. Before, it built one row per branch on every render.
  • The query cache persists at most every 10 s instead of every 1 s. A missing GitHub login is remembered for 30 s instead of being looked up again on every PR poll.

Why / Context

#9136 reports frequent freezes after 2.0.2, with typing and scrolling dead in the active session. There are no logs, and production builds do not record renderer unresponsive events. We reviewed the 2.0.1 → 2.0.2 renderer diff and found no hard loop. Then we measured the dev app over CDP while a script rewrote a file in the worktree every 250 ms, to look like an agent editing files. Main-thread work that 2.0.2 added or exposed showed up as follows:

Right pane stateScript time per 20 s, beforeAfter
Never opened since load (2.0.1-like)0.29 s—
Changes open1.17 s1.12 s
Changes opened, then closed0.89–1.03 s0.43–0.58 s

With the pane closed, style recalcs per 20 s went from about 100 to 0 and layouts from 38 to 19, which matches the never-opened baseline.

How It Works

  • Hidden right pane: SidebarSlide provides TabVisibilityContext = !hidden. KeptTab combines its own visibility with its parent's, where before it overrode it with true. DiffPane already pauses through useHeldWhileTabHidden and enabled: isTabVisible; until now it was never told.
  • Branch picker: BaseBranchSelector evaluated filtered.map(...) before Radix decided not to mount PopoverContent. With the 12,898 branches in this repo (2,136 listed), that was 185 ms per 15 s of profile while closed. The search state and list moved into a new BranchList, which is only mounted inside the open popover.
  • Query persister: createAsyncStoragePersister runs superjson.stringify over the whole persisted set on any cache event, including events for queries that are never persisted. Git churn kept it running about once a second: 385 ms per 15 s on a 2.87 MB cache. throttleTime: 10_000 brings that to 29 ms per 15 s.
  • GitHub login: viewerGithub.status runs before every PR read. Without a login, every call spawned git credential fill and then gh auth token. A missing login is now remembered for 30 s. A found token is still cached by LocalGitCredentialProvider, as before.

Manual QA Checklist

Done in the dev app over CDP:

  • Changes opened in the right pane, closed, then measured during file churn (numbers above)
  • Reopen after changes made while closed: the new file is listed
  • Branch picker opens and lists branches; search filters (kitenite/add-sec gives one row); Escape closes it
  • Profiled the closed state after the fixes: BaseBranchSelector no longer appears; persister cost went from 385 ms to 29 ms per 15 s
  • Picking a branch changes the base. Not done, to avoid changing the base branch in the dev workspace. The handler is the same onChange + close, now called from BranchList.

Testing

  • bun test src/lib/trpc/routers/viewer-github: new test. It failed before the fix (5 lookups for 5 polls) and passes after (1 lookup, a new one after 30 s via an injected clock).
  • SidebarSlide.test.tsx: new assertions that a kept panel reports hidden while closed and visible when reopened. They fail without the SidebarSlide change (received "true") and pass with it.
  • bun test for the SidebarSlide and DiffPane suites and packages/panes: all pass
  • bun run typecheck in apps/desktop and packages/panes
  • biome check on the changed files, bun run check:i18n (no catalog changes: the English text is unchanged)

Known Limitations

  • The reporter's freeze is not reproduced. The longest task in these runs was about 80 ms. These fixes remove extra main-thread work, which grows with diff size, branch count and cache size. They do not explain a hang of several seconds.
  • Production builds still do not log unresponsive: the handler in main/windows/main.ts is inside if (isDev). Logging it in production is the natural next step, so the next report comes with data.

Risks / Rollout

  • Risk:
    • A crash can lose up to the last 10 s of cached query data (the cache only).
    • After gh auth login, the PR panes can take up to 30 s to switch to the local login.
    • Branch search now resets when the popover closes; before, it was kept until a branch was picked.
  • Rollback: revert the matching commit. Each fix is its own commit.

Summary by cubic

Reduces main-thread work in the desktop renderer during frequent git changes: a hidden right pane no longer re-renders on every git change, the base-branch list is built only while open, cache writes are throttled, and a missing GitHub login is remembered briefly. This relates to #9136 but does not close it — the reported freeze was not reproduced.

Bug Fixes

  • A closed right pane now reports itself hidden, so its Changes pane pauses re-rendering; reopening still lists files changed while closed.
  • A missing GitHub login is remembered for 30s instead of spawning git credential fill and gh auth token on every PR poll; after gh auth login, PR panes can take up to 30s to switch.
  • The missing-login window uses a monotonic clock, so a backward wall-clock correction no longer keeps PR panes on the cloud login until the clock catches up.

Performance

  • The base-branch picker builds one row per branch only while its popover is open; the search now resets when the popover closes.
  • Query cache persistence is throttled from about 1s to 10s; a crash can lose up to the last 10s of cached query data.

Summary by CodeRabbit

  • Improvements
    • Branch selection filters branches as you type and shows a message when no matches are found.
    • Kept workspace panels now correctly reflect whether their parent tab is visible, including when panels are closed and reopened.
    • GitHub connection status checks avoid repeating credential lookups for a short period when no login is available.
    • Workspace query-cache updates are persisted less frequently, reducing repeated background writes.