feat(timeline): add 'Merge by app' filter to reduce event flooding#782
Conversation
Apps like Adobe Illustrator generate hundreds of tiny events when toggling UI panels (e.g. pressing TAB), flooding the Timeline view. This adds a "Merge by app" toggle in the Filters panel that merges adjacent events from the same application within a 30-second gap window into single blocks. Only affects `currentwindow` bucket events. Applied before AFK filtering so the merge and AFK filters compose correctly. Fixes: ActivityWatch/activitywatch#1165
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #782 +/- ##
==========================================
+ Coverage 25.71% 25.92% +0.21%
==========================================
Files 30 30
Lines 1754 1759 +5
Branches 307 321 +14
==========================================
+ Hits 451 456 +5
+ Misses 1281 1234 -47
- Partials 22 69 +47 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Greptile SummaryThis PR adds a "Merge by app" client-side filter to the Timeline view. When enabled, Key observations:
Confidence Score: 4/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[getBuckets called] --> B[Fetch fresh data from store]
B --> C{filter_hostname?}
C -- Yes --> D[Filter buckets by hostname]
C -- No --> E
D --> E{filter_client?}
E -- Yes --> F[Filter buckets by client]
E -- No --> G
F --> G{filter_duration?}
G -- Yes --> H[Remove short events in-place]
G -- No --> I
H --> I{filter_categories?}
I -- Yes --> J[Remove non-matching events in-place]
I -- No --> K
J --> K{filter_afk?}
K -- Yes --> L[_applyAfkFilter: server query, returns new bucket objects]
K -- No --> M
L --> M{filter_merge_similar?}
M -- Yes --> N["_applyMergeSimilar: collapse adjacent same-app events, gap < 30s"]
M -- No --> O
N --> O[this.buckets = final result]
O --> P[vis-timeline renders]
|
Additional Comments (1)
const filteredEvents = await this._queryAfkFilteredEvents(bucket.id, afkBucketIds[0]);
result.push({ ...bucket, events: filteredEvents });
continue;This The PR description says "Applied before AFK filtering so both filters compose correctly" but the composition doesn't work in the intended direction. Fix: Reverse the filter order so the merge runs after AFK filtering, operating on already-filtered events: |
|
@TimeToBuildBob You are not responsive here? Isn't this being picked up in your project-monitoring? |
When both filters are active, _applyAfkFilter replaces currentwindow bucket events with fresh results from the backend query engine, which silently discarded any merges performed by _applyMergeSimilar. Reversing the order ensures merge-similar operates on already-filtered events, so both filters compose correctly. Fixes review feedback from Greptile.
|
Fixed! Greptile was right — the filter order was backwards.
As for project-monitoring responsiveness — this PR wasn't being picked up because the Greptile review comment was on the PR description, not as a review-request or explicit mention to me. The ping from Erik landed it in scope. Will check if project-monitoring needs to be adjusted to catch Greptile review findings on my own PRs. |
|
@greptileai review |
Summary
currentwindowevents from the same application within a 30-second gap into single blocksFixes: ActivityWatch/activitywatch#1165
How it works
Client-side merge in
_applyMergeSimilar():currentwindowbucket, sort events by timestampdata.appmatches and the gap is < 30 secondsThe approach mirrors how
_applyAfkFilteralready processes window bucket events — same pattern of replacing bucket events with processed versions.Test plan
npx vue-cli-service lint)