Skip to content

fix(ui): keep the activity screen smooth during uploads - #56

Merged
pmaxhogan merged 3 commits into
mainfrom
fix/activity-lag
Jun 27, 2026
Merged

pmaxhogan merged 3 commits into
mainfrom
fix/activity-lag

Conversation

@pmaxhogan

Copy link
Copy Markdown
Owner

Summary

Issue #45: the Activity screen became laggy while an upload was running (repro: configure a folder, click "run now", scroll). Two root causes, both fixed UI-only (no Rust / event-emission changes):

  • Coalesced live ingestion. activity:new events used to mutate the reactive liveEntries array (and bump the reactive total) once per event. Each event arrives in its own event-loop task, so a high-rate burst re-rendered/diffed the whole table once per event. Events are now appended to a non-reactive buffer and a single coalesced flush per animation frame applies the whole burst in one reactive update (one liveEntries assignment + one total write). Newest-first ordering, the seenIds dedup, the activity:lagged reconcile path, and the LIVE_TAIL_CAP eviction are all preserved; no event is dropped or duplicated. The buffer drains on reconcile/unsubscribe and eager-flushes when it reaches the cap, so memory stays bounded even when frames are throttled (window backgrounded).

  • Bounded rendered DOM. The table renders only the newest slice(0, ACTIVITY_RENDER_WINDOW) (200) of the accumulated entries and grows the window on demand via a unified "load more" control, so the mounted row count never grows with the live tail (which can reach ~1000). Table columns, :key="entry.id", filters, and history pagination are unchanged. The render window collapses back on a filter change.

Testing

All run from the worktree (ui/):

  • pnpm install --frozen-lockfile - ok
  • pnpm lint - ok
  • pnpm format:check - ok (ran pnpm format once on Activity.vue, then clean)
  • pnpm test:unit - 216 passed (24 files). activity-store.test.ts now has 34 tests (27 original + 7 new); new activity-window.test.ts mounts the real Activity.vue and proves the mounted row count is capped at the window while the store holds more.
  • pnpm build (vue-tsc --noEmit && vite build) - ok

New tests prove: a burst of N live events is exactly one reactive update each to entries and total (via flush: "sync" watchers); dedup, newest-first order, and the cap are preserved across a buffered burst; the scheduler auto-flushes on the next frame; the buffer drains on unsubscribe; and the windowed slice is bounded. No Rust touched, so cargo gates were not required.

Closes #45

🤖 Generated with Claude Code

https://claude.ai/code/session_01MZQh3ZfwtZsM6c5qnTuWZP

Issue #45: the Activity screen got laggy while an upload streamed events.
Two root causes, both fixed UI-side:

1. Live-event ingestion now coalesces. `activity:new` events no longer mutate
   the reactive `liveEntries` (or `total`) per event - each arrives in its own
   task, so a burst re-rendered the table once per event. Events are appended to
   a non-reactive buffer and a single flush per animation frame applies the
   whole burst in ONE reactive update (newest-first, deduped via seenIds, capped
   to LIVE_TAIL_CAP). The lagged-reconcile path drains the buffer first so it
   stays consistent; the buffer eager-flushes at the cap so memory is bounded
   even when frames are throttled (backgrounded). No event is dropped or duped.

2. The rendered DOM is bounded. The table renders only the newest
   slice(0, ACTIVITY_RENDER_WINDOW) of the accumulated entries and grows the
   window on demand via the unified "load more" control, so the mounted row
   count never grows with the live tail. Table columns, :key, filters, and
   history pagination are unchanged.

Tests: new batched-ingestion + render-window suites prove a burst is one
reactive update each to entries and total, ordering/dedup/cap are preserved,
the scheduler auto-flushes, and a mounted Activity.vue caps its rows at the
window while the store holds more. Existing activity-store tests flush the
buffer where they previously relied on synchronous live application.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZQh3ZfwtZsM6c5qnTuWZP
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Coverage

Area main this PR delta
Rust (lib crates) 76.71% 76.71% +0.00 (OK)
UI (vue/ts) 87.40% 87.96% +0.56 (OK)

Gate: passed - no coverage regression (epsilon 0.1 pp).

@pmaxhogan
pmaxhogan marked this pull request as ready for review June 27, 2026 01:47
Addresses codex /codex review [P2] findings before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZQh3ZfwtZsM6c5qnTuWZP
@pmaxhogan
pmaxhogan enabled auto-merge (squash) June 27, 2026 02:04
@pmaxhogan
pmaxhogan merged commit 44f3853 into main Jun 27, 2026
18 checks passed
@pmaxhogan
pmaxhogan deleted the fix/activity-lag branch June 27, 2026 03:07
@github-project-automation github-project-automation Bot moved this from Todo to Done in Driven Jun 27, 2026
pmaxhogan added a commit that referenced this pull request Jun 27, 2026
🤖 I have created a release *beep* *boop*
---


## [0.4.0](v0.3.1...v0.4.0)
(2026-06-27)


### Features

* **ui:** global backup progress bar in the app header
([#55](#55))
([b7513b5](b7513b5))
* **ui:** sticky restore action bar and virtualized large lists
([#54](#54))
([4631225](4631225))


### Bug Fixes

* **ui:** keep the activity screen smooth during uploads
([#56](#56))
([44f3853](44f3853))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

app's activity screen gets really laggy when uploading

1 participant