Skip to content

perf(hooks): pause background pollers when page is hidden#602

Open
HUQIANTAO wants to merge 4 commits into
op7418:mainfrom
HUQIANTAO:perf/pause-pollers-hidden
Open

perf(hooks): pause background pollers when page is hidden#602
HUQIANTAO wants to merge 4 commits into
op7418:mainfrom
HUQIANTAO:perf/pause-pollers-hidden

Conversation

@HUQIANTAO
Copy link
Copy Markdown

Problem

Two renderers poll long-lived endpoints every 5 seconds, even when the user has the app in a background tab:

  • useBridgeStatussetInterval(refreshStatus, 5000) while the bridge is running
  • useNotificationPollsetInterval(poll, POLL_INTERVAL) unconditionally on mount

The Electron main process already covers the hidden case via its bgNotifyTimer and only runs when !mainWindow.isVisible(). The renderer's polls are duplicate work: 12 wakeups per minute per hidden tab, each firing a fetch + JSON parse on a thread the user can't see.

This was the same root cause as the previously-shipped PR #600 for git-status polling.

Fix

Gate both intervals on document.visibilityState === 'visible':

  • On visible → start the interval; on hiddenclearInterval and null the ref
  • On visibilitychange → resume runs an immediate catch-up poll so the UI shows fresh data without waiting for the next tick
  • All listeners are removed in the effect cleanup

The visible/hidden listener and the interval share the same pollRef / timerRef, so a tear-down or remount is race-free.

Impact

  • Network: ~12 fewer GET /api/bridge + GET /api/tasks/notify round-trips per minute per hidden window
  • CPU: no more 5-second setInterval waking the renderer event loop on background tabs
  • Battery: meaningful on laptops where the renderer also wakes the network stack
  • Correctness: the immediate catch-up poll on resume means the user-visible state on tab return is at most one tick stale, not five seconds

Hu Qiantao added 4 commits June 3, 2026 01:09
Three flex flex-col containers in ChatListPanel had no gap, so
adjacent SessionListItem hover backgrounds merged into one band
when the cursor swept between rows.

- L546: project group body (新建项目 + 项目列表)
- L618: visibleSessions list inside the project group
- L741: assistant group body (助理 flat list)

Add gap-1.5 (6px) — symmetric with px-3 / h-8 row padding, and the
smallest gap that still reads as separation under bg-sidebar-accent
hover (~6% black overlay on light theme).

SessionListItem.tsx L218 inner-stack gap-0.5 intentionally stays —
outer rhythm and inner rhythm are different problems and should not
collapse onto the same value.

Pure className change: 3 lines, all stock Tailwind utilities, zero
business logic touched.
Two 2xl-style scrolling dialogs had footers with no visual separator
from the last form field above. The Cancel / Save / Test / Connect
buttons read as part of the same visual block as the configuration
rows, defeating the 'decision exit' affordance.

- ModelsSection.tsx (Role mapping dialog, L1960):
  footer welded to last role assignment row.
- PresetConnectDialog.tsx (preset connect/edit, L809):
  footer welded to last advanced field (model mapping grid + extraEnv
  + headers + envOverrides textareas).

Add to both: shrink-0 gap-2 border-t border-border/50 pt-5 mt-3
- shrink-0 keeps the footer pinned when the dialog scrolls
- border-t gives the 1px anchor; pt-5 + mt-3 give the 32px stack

Implementation rule (now in ui-governance.md): footer anchor is
applied at the call site, not in the DialogFooter primitive, so
short dialogs (confirm / add-model) keep their minimal footer
without a stray divider.

Pure className change: 2 lines, all stock Tailwind utilities, zero
business logic touched.
Tokenize the two spacing patterns that motivated this PR so the
next sidebar list or 2xl dialog has a clear analogy anchor instead
of picking 0/2/4/8 from gut feel:

- docs/ui-governance.md: new '间距规范' section
  - sidebar list rhythm: 2px (cell-internal) vs 6px (between-item)
  - dialog footer anchor: border+pt for 2xl scrolling, none for
    short dialogs
  - implementation rule: className at call site, primitive untouched
  - 6px rationale (2px merges with hover bg; 8px compounds on long
    lists; 6px is the smallest reliable separator)

- docs/exec-plans/active/sidebar-list-spacing.md (audit trail):
  problem matrix, fix matrix, decision log, smoke-test cases.

- docs/insights/sidebar-list-2px-vs-6px.md (context):
  why outer-rhythm and inner-rhythm must not collapse onto the
  same value; why ModelsSection AND PresetConnectDialog are
  fixed together (same bug pattern, same fix, same review path).
Pause useBridgeStatus and useNotificationPoll when document.visibilityState
is hidden. The Electron main process already covers the hidden case via
its bg-notify timer, and waking the renderer every 5s just to drain an
empty queue is wasted CPU and network.

On resume we run an immediate catch-up poll so the user sees fresh data
without waiting for the next interval tick.
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 4, 2026

Someone is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@HUQIANTAO HUQIANTAO changed the base branch from worktree-product-refactor-research to main June 5, 2026 05:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant