Skip to content

feat(studio): timeline lane-zone model#2199

Open
ukimsanov wants to merge 1 commit into
studio-dnd/pr07-stacking-syncfrom
studio-dnd/pr08-timeline-zones
Open

feat(studio): timeline lane-zone model#2199
ukimsanov wants to merge 1 commit into
studio-dnd/pr07-stacking-syncfrom
studio-dnd/pr08-timeline-zones

Conversation

@ukimsanov

@ukimsanov ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

What

new pure module timelineZones — visual/audio track-zone classification (classifyZone) and normalizeToZones, which re-packs lanes into zone-consistent rows; tests cover the stacking/zones interaction.

Why

completes the z-model started in the stacking-sync PR.

How

new files; consumes isAudioTimelineElement (leaf-helpers PR) and computeStackingPatches (stacking-sync PR); type-only playerStore imports.

Test plan

bunx vitest run timelineZones.test.ts; tsc --noEmit; fallow audit clean.


Stack position 8/25 — studio NLE overhaul (CapCut-parity timeline + canvas). Graphite manages bases; merge from #2192 upward. Temporary TEMP(studio-dnd) fallow entries (unwired-component windows) are all removed by #2213 (app-shell swap), whose tree is byte-identical to the fully-verified integration branch.

ukimsanov commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@ukimsanov ukimsanov force-pushed the studio-dnd/pr07-stacking-sync branch from a3c734a to e90bfcc Compare July 11, 2026 00:55
@ukimsanov ukimsanov force-pushed the studio-dnd/pr08-timeline-zones branch 2 times, most recently from 95e9ca4 to d0aa2e0 Compare July 11, 2026 01:15
@ukimsanov ukimsanov force-pushed the studio-dnd/pr07-stacking-sync branch from e90bfcc to af7dd22 Compare July 11, 2026 01:15

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed as part of the 25-PR NLE overhaul stack. Full stack review on #2205 (the keystone). No blockers on this PR. — Miga

@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: LGTM (green) — the keystone of the two-way agreement

The most consequential math in this batch, and the tests reflect it: a whole section is devoted to the qa-clean regression that motivated the rewrite (whole-track ordering by MAX z carried a low-z clip above a covering higher-z clip), a section to reload stability, and a section to the round-trip with #2198's computeStackingPatches. Round-trip convergence is not asserted merely by construction — it is exercised end-to-end.

The pointed queries

  • Correctness of normalizeToZones — no zone identity violations. packages/studio/src/player/components/timelineZones.ts:164–:198 splits elements into visual and audio arrays (:170–:174 by classifyZone), packs each independently, and never reassigns across zones. A clip in zone X remains in zone X after re-packing — the identity property. #2199's tests at :76–:87 (drops audio below the visual lanes even when sharing a track index, groups multiple audio tracks at the bottom) verify this end-to-end.
  • Empty and single-lane edges. :165 short-circuits an empty input (return elements — same reference, no reallocation). Single-element inputs pack into lane 0 (verified implicitly by all one-clip contexts). No off-by-one at zero.
  • Stacking invariants preserved. The per-clip pack at :100–:117 sorts by zOf(b) - zOf(a) || b.domIndex - a.domIndex || key — deterministic, stable, and (critically) using the INPUT domIndex, not the mutated lane, as the tie-break. The comment at :86–:88 flags this correction from the historical oscillation bug (tie-breaking on the mutated output caused non-termination). Test at :187–:195 (tie-breaks equal-z overlapping clips on the STABLE id, not the mutated lane) pins the invariant.
  • Fixed point / idempotence. :99–:107, :197–:206, :252–:256, :308–:315 all pass. The claim normalizeToZones(normalizeToZones(x)) === normalizeToZones(x) is asserted structurally (lane per id equal after two passes) across multiple shapes.
  • Round-trip with #2198 — the cross-PR invariant. :332–:405 is the money test. Two scenarios: (1) authored order a(z=1)/b(z=5), user drags a above b, computeStackingPatches emits a→6, re-normalize returns a to lane 0 — convergent. (2) qa-clean drag video below ralu → cascade patches → re-normalize places video below ralu. Both scenarios also verify computeStackingPatches(renormalized) === [] — the fixed-point signature. This is the invariant Batch-A pivots on and it is nailed.
  • Reload stability. :208–:219, :258–:262 verify that fresh objects with the same z produce identical lanes across two independent normalizations. Discovery re-runs are deterministic.
  • Half-open overlap. :25–:27 mirrors #2198 with the same EPS = 1e-6 — consistent within the stacking half of this batch.

Nits (non-blocking)

  • The zone-identity property (visual stays visual, audio stays audio) is guaranteed by construction (:170–:174) but never asserted directly. A one-line invariant test on a mixed input would document what the split ensures — nice-to-have, not needed.
  • The EPS = 1e-6 epsilon convention is now in two files (timelineStackingSync.ts and here). Consider a shared TIMELINE_EPSILON constant at some point to avoid drift if one is ever tuned — cosmetic.

R1 by Via

@ukimsanov ukimsanov force-pushed the studio-dnd/pr07-stacking-sync branch from af7dd22 to bf91abd Compare July 11, 2026 02:56
@ukimsanov ukimsanov force-pushed the studio-dnd/pr08-timeline-zones branch from d0aa2e0 to b3f0ed0 Compare July 11, 2026 02:56
@ukimsanov ukimsanov force-pushed the studio-dnd/pr07-stacking-sync branch from bf91abd to 0691107 Compare July 11, 2026 08:18
@ukimsanov ukimsanov force-pushed the studio-dnd/pr08-timeline-zones branch from b3f0ed0 to 41f7e1d Compare July 11, 2026 08:18
@ukimsanov ukimsanov marked this pull request as ready for review July 11, 2026 09:06
@ukimsanov ukimsanov force-pushed the studio-dnd/pr07-stacking-sync branch from 0691107 to 9e58745 Compare July 11, 2026 10:49
@ukimsanov ukimsanov force-pushed the studio-dnd/pr08-timeline-zones branch from 41f7e1d to 0c56eb0 Compare July 11, 2026 10:49
What: new pure module timelineZones — visual/audio track-zone
classification (classifyZone) and normalizeToZones, which re-packs lanes
into zone-consistent rows; tests cover the stacking/zones interaction.

Why: completes the z-model started in the stacking-sync PR.

How: new files; consumes isAudioTimelineElement (leaf-helpers PR) and
computeStackingPatches (stacking-sync PR); type-only playerStore imports.

Test plan: bunx vitest run timelineZones.test.ts; tsc --noEmit; fallow
audit clean.
@ukimsanov ukimsanov force-pushed the studio-dnd/pr08-timeline-zones branch from 0c56eb0 to 69f9285 Compare July 11, 2026 12:51
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