Skip to content

perf(player): keep the playback position out of the player context - #809

Merged
InstaZDLL merged 1 commit into
mainfrom
perf/player-position-store
Oct 5, 2026
Merged

InstaZDLL merged 1 commit into
mainfrom
perf/player-position-store

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Problem

player:position ticks four times a second. positionMs was state in PlayerContext, and the provider's value is an inline object, so every tick re-rendered every usePlayer() consumer (38 of them, LibraryView included) to redraw the seven places that actually show the position.

Fix

  • New src/hooks/usePlayerPosition.ts: a tiny external store (useSyncExternalStore) with usePlayerPosition() for components that render the position, getPlayerPosition() for code that only needs it in a handler, and setPlayerPosition() written by PlayerProvider only (ticks, seeks, resets: the same eight call sites as before). One copy per webview, like the provider itself.
  • positionMs is removed from PlayerContextValue, so the compiler lists every reader: ProgressBar, MiniPlayer, LyricsEditorModal, HeldNote, useKaraokeWordFill and useTrackLyrics now subscribe to the store; AbLoopButton reads it on click and no longer re-renders on ticks at all.
  • The provider no longer re-renders on a tick, so the rest of the tree only re-renders when the player state actually changes.

No behaviour change intended. Docs: an invariant in invariants.md (putting positionMs back in the context brings the 4 Hz re-render back), one line in CLAUDE.md.

Manual check before merge

Progress bar (drag + keyboard seek), synced lyrics + karaoke word fill + held notes, the lyrics editor's capture button, A-B loop capture, the mini-player's seek bar.

Summary by CodeRabbit

  • Améliorations
    • La position de lecture est désormais mise à jour indépendamment des autres informations du lecteur. Les composants qui affichent la progression, les paroles synchronisées ou la boucle A-B continuent de suivre la lecture sans entraîner de mises à jour inutiles dans les autres éléments de l’interface.
    • La lecture, la vitesse et les commandes du lecteur conservent leur fonctionnement habituel.

player:position ticks four times a second, and as context state each tick
re-rendered every usePlayer consumer, the library view included. The
position now lives in a small external store: components that show it call
usePlayerPosition, the A-B loop button reads it on click only.
@InstaZDLL InstaZDLL added scope: frontend React/Vite frontend (src/) scope: docs Docs, README, assets type: perf Performance improvement size: m 50-200 lines labels Oct 5, 2026
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: InstaZDLL/WaveFlow/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 9fce673a-5904-4bc9-a8ee-9490b8189406
📥 Commits

Reviewing files that changed from the base of the PR and between 4add4cb and b762631.

📒 Files selected for processing (12)
  • CLAUDE.md
  • docs/architecture/invariants.md
  • src/components/common/LyricsEditorModal.tsx
  • src/components/player/AbLoopButton.tsx
  • src/components/player/HeldNote.tsx
  • src/components/player/ProgressBar.tsx
  • src/components/views/MiniPlayer.tsx
  • src/contexts/PlayerContext.tsx
  • src/hooks/useKaraokeWordFill.ts
  • src/hooks/usePlayer.ts
  • src/hooks/usePlayerPosition.ts
  • src/hooks/useTrackLyrics.ts
💤 Files with no reviewable changes (1)
  • src/hooks/usePlayer.ts

Included review availability: This review used your included allowance. 8 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 10 reviews per hour.


📝 Walkthrough

Walkthrough

La position de lecture est retirée de PlayerContext et stockée dans un store externe. PlayerProvider met ce store à jour. Les composants et hooks concernés lisent désormais la position via usePlayerPosition() ou getPlayerPosition().

Changes

Position de lecture

Layer / File(s) Summary
Store externe et contrat de lecture
src/hooks/usePlayerPosition.ts, src/hooks/usePlayer.ts
Un store de module expose la lecture, la mise à jour et l’abonnement à la position. positionMs est retiré de PlayerContextValue.
Synchronisation par PlayerProvider
src/contexts/PlayerContext.tsx, docs/architecture/invariants.md
PlayerProvider met à jour le store lors des changements de position, des recherches et des changements de piste. L’invariant documente son rôle d’unique écrivain.
Migration des consommateurs
src/components/common/LyricsEditorModal.tsx, src/components/player/*, src/components/views/MiniPlayer.tsx, src/hooks/useKaraokeWordFill.ts, src/hooks/useTrackLyrics.ts, CLAUDE.md
Les consommateurs concernés obtiennent la position depuis le store externe. Les autres valeurs et commandes de lecture restent accessibles via usePlayer. La documentation précise la séparation du hook.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Refactor

Merge Risk: ⚪ Minimal · up to b7626

The change is mergeable; normal validation should still cover the listed playback interactions.

Security Architecture Review

Security architecture risk: 🔵 Low · up to b7626

The change remains confined to playback-position updates within each window. Existing command paths and event handling are preserved, and no introduced security concern was identified. The main residual uncertainty is how the shared-state lifecycle behaves under unusual event interleavings.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The directly changed state has webview-local scope and propagates to explicit position subscribers. The inspected migration does not expand position handling into a new service, data store, or privileged command path.

Trust Boundaries and Controls

  • observed — The existing player:position event remains the tick input, with updates still suppressed during seeking. Snapshot and user-initiated seek inputs retain their prior paths; only their frontend storage destination changes.

Resilience and Maintainability Implications

  • inferred — The ownership and cleanup checks found no introduced failure-containment regression in the inspected lifecycle. This conclusion is bounded to source inspection, not runtime validation of concurrent event delivery.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Le titre suit le format Conventional Commits et décrit clairement le changement principal : retirer la position de lecture du contexte du lecteur.
Description check ✅ Passed La description explique le problème, la solution et les vérifications manuelles prévues. Elle ne reprend pas les sections « How I tested » et « Checklist » du modèle, et ne confirme pas que les vérifi…
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 9 files. (2 skipped: 2 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands.

@InstaZDLL InstaZDLL self-assigned this Oct 5, 2026
@InstaZDLL
InstaZDLL merged commit 9dc565e into main Oct 5, 2026
16 checks passed
@InstaZDLL
InstaZDLL deleted the perf/player-position-store branch October 5, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: docs Docs, README, assets scope: frontend React/Vite frontend (src/) size: m 50-200 lines type: perf Performance improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant