Skip to content

refactor(studio): extract timeline render contracts - #2699

Open
miguel-heygen wants to merge 1 commit into
codex/studio-timeline-d-clip-drag-lifecycle-v2from
codex/studio-timeline-d-render-contracts-v2
Open

refactor(studio): extract timeline render contracts#2699
miguel-heygen wants to merge 1 commit into
codex/studio-timeline-d-clip-drag-lifecycle-v2from
codex/studio-timeline-d-render-contracts-v2

Conversation

@miguel-heygen

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

Copy link
Copy Markdown
Collaborator

What

Extract timeline rendering contracts and orchestration helpers needed by viewport virtualization.

Why

The original Timeline component mixed view-model construction, lane contracts, tick generation, selection lifecycle, modifier state, and player synchronization. Virtualization needs those decisions to have explicit owners without introducing parallel geometry or state paths.

How

  • Introduce focused lane, ruler, view-model, selection, modifier, tick, and player-sync modules.
  • Keep Timeline as the orchestrator while preserving existing rendering behavior.
  • Reuse the extracted contracts in later row and clip windows.

This is D4 of the Family D draft review sequence. It targets current main after Family C landed; its Family D patch was replayed without semantic changes. It supersedes legacy PRs #2635 and #2636, which remain open for audit until landing.

Test plan

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (not applicable)

Validated on the exact Family D tip through Timeline and timeline-layout coverage, the full Studio suite, both package typechecks, formatting, lint, file-size checks, and the strict 50,000-element Chromium gate.

2026-07-29 rebase verification

  • Replayed D1-D8 onto current main after Family C landed; git range-diff marks every old/new Family D commit exactly equivalent (=).
  • oxfmt, oxlint, changed-file size gate, Studio typecheck, and Studio Server typecheck passed.
  • Full Studio suite: 288 files passed, 3,168 tests passed, 18 todos. Full Studio Server suite: 29 files and 398 tests passed.
  • Fallow CI audit exited successfully; its five minor observations are local duplication in existing test harness setup, not duplicated production decisions.
  • Chrome 150 browser evidence: 1,000-element control passed 5/5; strict 50,000-element gate passed its 4/5 quorum with 10 mounted rows, 240 mounted clip roots, 995 timeline descendants, approved direct scrolling, and memory returned. Repeated samples showed the same host-timing outlier pattern on the exact pre-rebase control.

No merge was performed; this PR remains a draft.

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 73d1bb5 to ecf76d7 Compare July 27, 2026 20:51
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from 5e02427 to c327ee5 Compare July 28, 2026 20:42
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from ecf76d7 to a69af1c Compare July 28, 2026 20:42
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from c327ee5 to d9d7106 Compare July 28, 2026 22:09
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from a69af1c to 832f7b3 Compare July 28, 2026 22:09
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from d9d7106 to a224fdc Compare July 28, 2026 22:35
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 832f7b3 to 57aa9f0 Compare July 28, 2026 22:35
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from a224fdc to a289615 Compare July 28, 2026 23:02
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 57aa9f0 to 6f4ff16 Compare July 28, 2026 23:03
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from a289615 to da1c1be Compare July 28, 2026 23:22
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 6f4ff16 to c41fac2 Compare July 28, 2026 23:23
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from da1c1be to 2913f87 Compare July 28, 2026 23:53
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from c41fac2 to 42579a9 Compare July 28, 2026 23:54
@miguel-heygen
miguel-heygen changed the base branch from codex/studio-timeline-d-clip-drag-lifecycle-v2 to main July 29, 2026 02:06
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch 2 times, most recently from 4089ed6 to 29b22a9 Compare July 29, 2026 14:46
@miguel-heygen
miguel-heygen changed the base branch from main to codex/studio-timeline-d-clip-drag-lifecycle-v2 July 29, 2026 15:03
@miguel-heygen
miguel-heygen marked this pull request as ready for review July 29, 2026 15:06
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 29b22a9 to 4edbc70 Compare July 29, 2026 23:35
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from b4d9414 to 4ddc5bd Compare July 29, 2026 23:35

@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.

Verdict: APPROVE — grade B+ — rubric CORRECT

Role in stack: D4 refactor — pre-virtualization render-contract extraction. Stacks cleanly onto D3.

Root-cause fit: The extraction gives D5-D8 the seams they need: ticks (useTimelineTicks/timelineRulerGeometry), selection lifecycle (useTimelineSelectionLifecycle), shift modifier, preview-element derivation (getTimelinePreviewElement), duration & keyframe predicates (timelineViewModel), and the player-sync equality check (timelinePlayerSync). Row-height / thumbnail-request / context-menu-target contracts are still baked into the orchestrator — D4 explicitly scopes to ruler/selection/modifier/view-model, deferring is fine so long as D5 exposes row measurement without a re-extraction round.

Claims verified (file:line at HEAD 4edbc701):

  • Timeline.tsx +21/-71: selectedElement/selectedElementRef/shiftHeld-effect/tick-useMemo/getPreviewElement bodies removed; only local consumers, no dangling refs.
  • timelineLayout.ts -118/+2: tick logic moved out; re-exports formatTimelineTickLabel/generateTicks from ruler module — no import-path breakage.
  • New files are .ts (hooks/helpers only, no JSX) — Fast Refresh compliant.
  • Named exports only, import type on pure-type imports.
  • No any, no @ts-ignore, no disabled tests, no new TODOs. Pre-existing eslint-disable react-hooks/exhaustive-deps moved verbatim.
  • Bit-identical output: hasKeyframedTimelineClips, getEffectiveTimelineDuration, getTimelinePreviewElement, tick math, timelineElementsChanged line-for-line equivalent (identifier renames only).

Adversarial findings:

  • Nit — element-identity duplication: the element.key ?? element.id fallback is inlined in getTimelinePreviewElement and useTimelineSelectionLifecycle. D5-D8 will need the same fallback for keying — worth a one-liner timelineElementIdentity(el) helper in timelineViewModel.ts now.
  • Nit — narrowed union: useTimelineTicks accepts timeDisplayMode: "time" | "frame" inline instead of importing the store type; drifts silently if playerStore extends the mode.
  • Nit — doc erosion: timelineRulerGeometry.ts drops the "Nice NLE steps" / "frame-labels-must-be-whole-frames" explanatory comments — the load-bearing why for these two functions.
  • Nit — indirection: useTimelineTicks imports generateTicks from ./timelineLayout (which re-exports), not from source ./timelineRulerGeometry.

CI state: Required checks green.

Suggested next step: Land as-is. Address the element-identity helper in D5 where row/clip windows will re-introduce the key ?? id fallback, and consider a follow-up to restore the tick-generation docstrings.

Review by Via

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from 4ddc5bd to 6958e61 Compare July 30, 2026 00:23
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 4edbc70 to 2b89498 Compare July 30, 2026 00:23
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Addressed at 2b894982b.

  • New render-contract code reuses the existing getTimelineElementIdentity owner.
  • TimelineTimeDisplayMode now has one exported type owner.
  • Restored the ruler’s explanatory comments about interval selection, whole-frame steps, subdivisions, drift avoidance, and the safety cap.
  • Removed the timelineLayout tick re-export path; ruler consumers import the ruler geometry directly.

@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Reviewed the exact #2699 delta (6958e61..2b89498), not the rest of the stack.\n\nThe refactor is clean: ruler geometry, view-model derivation, selection lifecycle, Shift tracking, tick generation, and player-sync decisions move behind narrow modules without changing their algorithms. The current head also resolves the prior review drift: it reuses getTimelineElementIdentity, shares TimelineTimeDisplayMode, restores the semantic comments, and imports ruler geometry directly instead of through the Timeline re-export. Window listeners in the new Shift hook are paired with cleanup, and I found no new import cycle or production side effect.\n\nCoverage remains primarily behavioral through the existing Timeline/player tests rather than unit tests for every extracted pure helper; that is acceptable for this mechanical delta. Exact-head lightweight checks are green/skipped as expected, with Graphite mergeability pending on the downstack chain. It must wait for #2697s blocker, but this delta itself is approvable.\n\nGitHub will not let the shared author identity submit a formal approval, so recording the verdict as a PR comment.\n\nVerdict: APPROVE\nReasoning: The render-contract extraction preserves behavior, improves ownership boundaries, and introduces no lifecycle or dependency regression.\n\n— Magi

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 2b89498 to f5cd679 Compare July 30, 2026 01:31
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch 2 times, most recently from 9b9e15e to 8c1dd61 Compare July 30, 2026 03:15
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from f5cd679 to 60e3c4f Compare July 30, 2026 03:15
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from 8c1dd61 to dd56d5b Compare July 30, 2026 03:47
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 60e3c4f to 3f74418 Compare July 30, 2026 03:47
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-render-contracts-v2 branch from 3f74418 to f8cd5e9 Compare July 30, 2026 04:05
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-drag-lifecycle-v2 branch from dd56d5b to 6b92049 Compare July 30, 2026 04:05
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.

2 participants