stack/apps/dashboard
Armaan Jain eba479b7cc
Fix session replay playback freezing the page (#1727)
Fixes the session replay player making the page laggy/unresponsive
during playback. Four compounding main-thread bottlenecks:

1. **Mini-tab re-seek every tick (main freeze cause).** Every 200ms TICK
emitted `sync_mini_tabs`, which calls `replayer.pause(offset)` on every
non-active tab. In rrweb v1 that's a full synchronous seek (rebuild from
last FullSnapshot + fast-forward all events), easily 100ms–1s+ of
blocking work per tab, 5×/sec. Now throttled to
`MINI_TAB_SYNC_INTERVAL_MS` (2s) in the state machine, reset on SEEK so
seeks re-sync promptly, and the executor skips tabs that aren't actually
rendered as mini thumbnails.

2. **Full-page re-render every 200ms.** Every dispatch force-rendered
the whole page component. Added `areStatesRenderEquivalent()` —
dispatches that only change playback bookkeeping fields
(`currentGlobalTimeMsForUi`, etc.) skip the React re-render; mini-tab
visibility is refreshed at the throttled sync cadence instead.

3. **Timeline re-rendered at 60fps.** The transport bar rAF loop used
`setState(currentTime)`, re-rendering the whole bar including up to 2000
marker divs every frame. Current time / progress are now written
directly to DOM refs, and the markers lane is extracted into a memoized
`TimelineMarkersLane`.

4. **Synchronous `addEvent` floods.** Background chunk processing now
yields to the event loop between chunks so feeding large batches into
live replayers doesn't block playback.

Added machine tests for the sync throttle, seek re-sync, and render
equivalence (117 passing).

Link to Devin session:
https://app.devin.ai/sessions/7d53fb70217d4ebaa77b9dbac2ba1f54
Requested by: @Developing-Gamer

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes session replay playback freezing by removing main-thread
bottlenecks in the player and timeline. Playback stays smooth and
responsive; background loads finish faster in hidden tabs.

- **Bug Fixes**
- Throttled mini-tab syncing to `MINI_TAB_SYNC_INTERVAL_MS` (2s);
re-syncs on the next tick after seeks and when buffering resumes
(regardless of page age); skips non-rendered tabs to avoid `rrweb` full
seeks; refreshes mini-tab visibility on the throttled cadence.
- Skipped React re-renders for playback bookkeeping via
`areStatesRenderEquivalent`.
- Stopped 60fps timeline re-renders by writing time/progress to DOM refs
and memoizing the markers lane.
- Yield between event chunks only when the tab is visible; skip yields
in hidden tabs to avoid clamped timers and speed up background loads.
- Added tests for sync throttling, post-seek re-sync (immune to page
age), and render-equivalence.

<sup>Written for commit 4abc80c5f7.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1727?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Performance / UX**
* Session replay playback during rapid timeline changes is smoother,
with fewer unnecessary UI refreshes.
* Timeline progress/time updates are more immediate, and event markers
render more efficiently.
* **Bug Fixes**
* Mini-tab syncing is now throttled while playing and correctly re-syncs
after seeking to keep thumbnails accurate.
* Event/chunk loading now yields between batches to reduce main-thread
blocking.
* **Tests**
* Expanded session replay mini-tab sync tests to cover throttling and
post-seek resynchronization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: armaan <armaan@stack-auth.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-05 21:18:16 -07:00
..
public Add 6/12/26 changelog entry (#1589) 2026-06-16 16:44:03 -07:00
scripts Support local dashboard in remote SSH and GH Codespaces (#1538) 2026-06-04 16:36:17 -07:00
src Fix session replay playback freezing the page (#1727) 2026-07-05 21:18:16 -07:00
.env Gate usage limit banners behind HEXCLAVE_DISABLE_PLAN_LIMITS (#1728) 2026-07-05 21:17:21 -07:00
.env.development Gate usage limit banners behind HEXCLAVE_DISABLE_PLAN_LIMITS (#1728) 2026-07-05 21:17:21 -07:00
.eslintrc.cjs Config sources (#1083) 2026-01-21 18:08:35 -08:00
.gitignore Support local dashboard in remote SSH and GH Codespaces (#1538) 2026-06-04 16:36:17 -07:00
.npmrc Split backend and dashboard (#83) 2024-06-18 15:49:31 +02:00
components.json Split backend and dashboard (#83) 2024-06-18 15:49:31 +02:00
DESIGN-GUIDE.md fix: remove email-outbox dashboard page (#1662) 2026-06-24 11:10:24 -07:00
instrumentation-client.ts feat(hexclave): PR 3 — native @hexclave/* source rename + delete dual-publish wiring (#1482) 2026-05-29 15:21:59 -07:00
LICENSE Split backend and dashboard (#83) 2024-06-18 15:49:31 +02:00
next.config.mjs Remove local emulator support (#1692) 2026-06-30 10:27:01 -07:00
package.json chore: update package versions 2026-07-06 03:17:30 +00:00
postcss.config.js Split backend and dashboard (#83) 2024-06-18 15:49:31 +02:00
tailwind.config.ts feat(hexclave): PR 3 — native @hexclave/* source rename + delete dual-publish wiring (#1482) 2026-05-29 15:21:59 -07:00
tsconfig.json Support local dashboard in remote SSH and GH Codespaces (#1538) 2026-06-04 16:36:17 -07:00
vitest.config.ts In-source unit tests (#429) 2025-02-14 11:47:52 -08:00