Skip to content

refactor(studio): extract live timeline clock - #2711

Merged
miguel-heygen merged 1 commit into
mainfrom
codex/studio-timeline-f-live-clock-v2
Aug 4, 2026
Merged

refactor(studio): extract live timeline clock#2711
miguel-heygen merged 1 commit into
mainfrom
codex/studio-timeline-f-live-clock-v2

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

What

Extract the live playback-time publisher into a small shared store module without changing playback behavior.

Why

High-frequency timeline consumers need one stable clock contract without coupling to the full player state container or causing React renders on every playback frame.

How

liveTime.ts now owns the listener set plus the notify and subscribe operations. The player store re-exports that contract, so existing consumers keep the same public API while the implementation has one owner.

This is the first PR in the stack and targets main.

Test plan

Exact-head CI completed without failed conclusions, including typecheck, runtime contract, Studio tests, and the timeline viewport gate.

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch 2 times, most recently from 5b22e39 to 816894f Compare July 28, 2026 21:12
@miguel-heygen
miguel-heygen changed the base branch from codex/review-baseline/studio-timeline-family-e-v2 to codex/studio-timeline-e-ease-focus-lifecycle-v2 July 28, 2026 21:13
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-e-ease-focus-lifecycle-v2 branch from 49e772f to 9f951c8 Compare July 28, 2026 22:11
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch from 816894f to 1a4e7c0 Compare July 28, 2026 22:11
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-e-ease-focus-lifecycle-v2 branch from 9f951c8 to b8035c7 Compare July 28, 2026 22:37
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch from 1a4e7c0 to 7b2f56e Compare July 28, 2026 22:37
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-e-ease-focus-lifecycle-v2 branch from b8035c7 to 3f43f78 Compare July 28, 2026 23:04
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch from 7b2f56e to b74d0f7 Compare July 28, 2026 23:05
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-e-ease-focus-lifecycle-v2 branch from 3f43f78 to 142a245 Compare July 28, 2026 23:24
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch from b74d0f7 to 2b50771 Compare July 28, 2026 23:26
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-e-ease-focus-lifecycle-v2 branch from 142a245 to 3f2b313 Compare July 28, 2026 23:54
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch from 2b50771 to d0891eb Compare July 28, 2026 23:54
@miguel-heygen
miguel-heygen changed the base branch from codex/studio-timeline-e-ease-focus-lifecycle-v2 to main July 29, 2026 02:07
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch from d0891eb to 1be5a1f Compare August 4, 2026 17:45
@miguel-heygen
miguel-heygen marked this pull request as ready for review August 4, 2026 20:26

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior-preserving extraction — verified.

Behavior parity. New liveTime.ts is byte-equivalent to the old module-scoped block (rename only: _timeListeners → timeListeners, t → time, cb → listener). Set semantics, notify iteration order, subscribe's unsubscribe closure all identical.

Singleton preserved. liveTime is re-exported from playerStore.ts via export { liveTime } from "./liveTime". Re-export shares module identity — one Set<TimeListener> for the whole app. No accidental fork.

Caller audit @ 1be5a1f (grep across repo, checked at HEAD):

  • Runtime consumers (useTimelinePlayerLoop, useTimelinePlayhead, useTimelineActiveClips, useTimelineRangeSelection, useTimelineSyncCallbacks, useTimelinePlayer, useFrameCapture, usePreviewInteraction, useGestureRecording) all import from ../store/playerStore — reach liveTime through the re-export. ✅
  • Public surface (player/index.ts, studio/src/index.ts) re-exports from ./store/playerStore — external API unchanged. ✅
  • playerStore.test.ts still exercises liveTime via ./playerStore — coverage reaches the extracted module through the same re-export.

Import cycle risk. liveTime.ts has zero imports — cycle-free.

Ambient state. None to lose; module is pure pub/sub with no context/hook coupling.

CI. All required checks SUCCESS on 1be5a1f0 (typecheck, runtime contract, studio timeline viewport gate, windows render, preview parity, producer unit+integration).

— Review by Via

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 1be5a1f0.

Clean behavior-preserving extraction — the liveTime publisher moves from playerStore.ts into a dedicated liveTime.ts module, with the store re-exporting the same public API so existing consumers don't retarget their imports. Verified equivalence: _timeListenerstimeListeners, cblistener, ttime are pure renames; .notify and .subscribe semantics are byte-identical to the previous inline definition.

Nit (body-only): the migrated comment shortens from "Bypasses React state so the RAF loop can update the playhead/time display without triggering re-renders on every frame" to "Lightweight pub-sub avoids React re-renders on every playback frame." The essential why (RAF loop needs a side channel to avoid per-frame renders) is preserved but the concrete consumer ("playhead/time display") is lost. Consider a ponytail: marker per the HF idiom ([[project_hf_ponytail_comment_idiom]]) to lock in the why so a future contributor doesn't wonder why the RAF loop needs to bypass React state.

Everything else in the diff is mechanical rename. No behavioral surface. Stack sets up cleanly for #2712 / #2713.

Ready from my side, leaving as COMMENTED.

Review by Rames D Jusso

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-f-live-clock-v2 branch from 1be5a1f to fc416c8 Compare August 4, 2026 22:41
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Review follow-up folded into exact head fc416c837: the live-clock comment now names the RAF, playhead/time-display consumers, and the reason React rendering is bypassed. Rebased onto current main; local typecheck, static gates, and the full Studio suite are clean.

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta re-verify at fc416c8373e82a11b3865e53ea73480903796ced (prior R1 at 1be5a1f0).

Patch-equivalent rebase, with one addressed nit. PR still touches exactly packages/studio/src/player/store/liveTime.ts (+11) and packages/studio/src/player/store/playerStore.ts (+1/-13). playerStore.ts is byte-identical to the R1-approved head. liveTime.ts has a single semantic change from prior head → the file-lead comment is now:

// ponytail: Playback RAF updates the playhead/time display without per-frame React renders.

That's the direct fix for @james-russo-rames-d-jusso's COMMENTED nit at 1be5a1f0 asking for a ponytail: marker locking in the concrete why (playhead/time display + RAF loop). Now preserved verbatim in the specified idiom. Nit → FIXED.

Prior R1 findings state.

  • Byte-equivalent extraction + rename discipline (_timeListeners → timeListeners, t → time, cb → listener) — unchanged, still holds.
  • Singleton preserved via export { liveTime } from "./liveTime" in playerStore.tsverified at HEAD.
  • Runtime consumers still reach liveTime through the playerStore re-export — no import retargets in this PR.
  • Zero imports in liveTime.ts → no cycle risk — unchanged.
  • Rames's body-only ponytail nit — FIXED (comment updated per HF idiom).

Family F invariants. Preserved — this base PR is a pub-sub extraction and doesn't touch element-identity keying, tie-breaks, deletion fallback chain, same-time keyframe ease, .tsx exports, or useEffect state-syncing.

New-base interactions. None. The 12-commit main advance between 1be5a1f0's base and current base is orthogonal to liveTime.ts / playerStore.ts — no touching commits in the player/store/ subtree.

CI at HEAD. Typecheck / Lint / Fallow / File size / Studio load smoke / Studio timeline viewport gate / Producer unit + integration / Runtime contract / Preview parity / player-perf / windows Render / SDK unit+contract+smoke / CodeQL — all SUCCESS. Tests on windows-latest still IN_PROGRESS at review time (non-blocking, matches prior R1 pattern).

Byte-clean re-stamp.

— Review by Via

@miguel-heygen
miguel-heygen merged commit 19b8a1f into main Aug 4, 2026
45 checks passed
@miguel-heygen
miguel-heygen deleted the codex/studio-timeline-f-live-clock-v2 branch August 4, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants