Repository navigation
feat: opt-in setting to pause heartbeats while browser is unfocused - #252
TimeToBuildBob wants to merge 5 commits into
Conversation
|
|
Fixed both Greptile findings in b178771: P1 — Short pauses can disappear Root cause: on refocus, Fix: on focus loss, send a standard-pulsetime heartbeat to close the AW event at exactly the focus-loss time, then clear the cached heartbeat data. On focus gain, P2 — Startup heartbeat ignores pause Root cause: Fix: |
|
@greptileai review |
a1a3902 to
25abb19
Compare
|
@greptileai review |
|
Addressed the remaining P1 ("Polled focus skips gap handling") in 25abb19. When the alarm poll detects a focus loss that the focus-change event missed, it now closes the AW event at the poll time and clears the stored heartbeat data, the same as the event path. Refocus then starts a fresh event instead of merging across the gap. Verified: |
|
@greptileai review |
|
CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click. This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted. |
Adds "Only report while the browser is focused" (default off) in settings. When enabled, heartbeats stop while no browser window has OS focus (windows.onFocusChanged / WINDOW_ID_NONE), and resume immediately on refocus. Alarm ticks also re-poll windows.getLastFocused as a fallback, since focus-change events are unreliable on some Linux window managers. Fixes ActivityWatch#140 Git-Session-Id: eabf
Two bugs in the pause-when-unfocused feature that could cause the unfocused interval to disappear from the recorded timeline: 1. Short pauses erased (P1): on refocus, the immediate heartbeat had the same tab data as the last pre-pause heartbeat. AW's pulsetime merging would extend the pre-pause event across the gap, making the user appear active during the unfocused interval. Fix: on focus loss, send a standard-pulsetime heartbeat to close the event at exactly T_loss, then clear the cached heartbeat data. On refocus, send with pulsetime=0, which forces AW to create a new event at T_gain regardless of how short the gap was. 2. Startup heartbeat ignores pause (P2): the refreshWindowFocus() call at startup ran concurrently with the sendInitialHeartbeat chain rather than before it, so the focus state could be stale when the initial heartbeat fired. Starting while unfocused could record an unwanted heartbeat. Fix: chain refreshWindowFocus() immediately before sendInitialHeartbeat in the startup sequence, and add an explicit shouldPauseForUnfocus() guard inside sendInitialHeartbeat. Git-Session-Id: a1e646d2-a618-5449-a47f-ce4f6402a5b7
When a Linux WM misses a focus-change event, the alarm fallback poll detects the focus-loss transition but previously skipped the gap-closing logic (send-close-heartbeat + clearHeartbeatData). The next alarm tick after refocus would then send a normal-pulsetime heartbeat that could merge the unfocused interval into the activity record. Fix: capture wasFocused before refreshWindowFocus(); on a detected wasFocused → !isWindowFocused transition, apply the same logic as windowFocusChangedListener — close the AW event at the current time and clear stored data — so the eventual refocus heartbeat starts fresh. Git-Session-Id: a9397931-a8f3-58e9-8d53-f8c9b0b513ad
When a focus-change event is missed, the alarm poll knows the browser is unfocused but not when focus was lost. Sending a closing heartbeat stamped at poll time extends the previous AW event through the unfocused interval (heartbeat at 12:00, focus lost at 12:00:05, poll at 12:01 → 55s of away-time counted as browsing). Fix: on poll-detected loss, only clear stored heartbeat data so the event stays at the last known heartbeat. On poll-detected gain, send with pulsetime=0 (same as the event-path refocus) so a short gap is not merged by a normal-pulsetime alarm tick. Git-Session-Id: f2811f35-94c5-5f35-ab15-d55ec4b432cc
Git-Session-Id: 955022f9-0aae-5ac3-8520-7d008562f81b
93d600f to
8e399b3
Compare
|
Rebased onto current master and pushed The heartbeat conflict now preserves both group-only tab updates from master and the opt-in focus gate. The integration tests also caught an ordering regression: awaiting the pause setting before taking the tab snapshot let subsequent tab mutations leak into earlier events. The pause checks now run inside the serialized heartbeat queue, preserving synchronous snapshots and queue order. Verified locally:
Native browser/OS-focus verification remains outstanding; these are deterministic production-module tests with browser/storage/client stubs, not an installed-extension check. CI is running on the new head. |
Summary
Adds an opt-in setting, "Only report while the browser is focused"
(default off, so existing behavior is unchanged), that stops heartbeats
while no browser window has OS focus and resumes immediately when focus
returns.
windows.onFocusChanged;WINDOW_ID_NONEpauses heartbeats,refocus sends an immediate heartbeat so the gap ends right away instead of
waiting for the next alarm tick.
windows.getLastFocused()as a fallback,since focus-change events are unreliable on some Linux window managers
(noted in the issue).
heartbeatAlarmListener,tabActivatedListener, andtabUpdatedListeneron the new check so no heartbeat is sent while pausedand the setting is on.
This repo has no existing unit-test framework (no jest/vitest, no
testscript, CI only builds/lints) — there's nothing to extend here, so
verification is:
npx tsc --noEmitclean,npx vite build --mode productionclean, pre-commit hooks clean. Manual Chrome/Firefoxverification (switching apps for a couple minutes and checking for a gap in
the bucket, as suggested in the issue) still needs a human with a desktop
browser — happy to iterate on review feedback.
Fixes #140.
Test plan
npx tsc --noEmit— cleannpx vite build --mode production— clean