feat(studio): timeline track = stacking layer (NLE-style layering)

Re-architect the timeline row model from data-track-index rows to stacking
layers. Rows represent stacking layers per context: explicit-z clips merge onto
one track when they share a z and don't overlap in time; auto-z clips stay one
row each (DOM order); audio is pulled into its own bottom lanes. Rows keyed by a
stable layer id, not data-track-index.

Vertical drag always writes z-index, never track: drop onto a layer joins it
(same z), between layers interpolates a new z, past the ends creates a new
front/back layer. data-track-index is never rewritten; #958 holds. Adds
hasExplicitZIndex capture (computed z != auto).

L1: hasExplicitZIndex on the element model
L2: buildStackingTimelineLayers (layer-based rows)
L3: layer-aware vertical drag (join / interpolate / new-extreme)
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-08 23:46:25 -04:00
parent 03f8089e52
commit e5ed512529
24 changed files with 1078 additions and 426 deletions
@@ -27,6 +27,20 @@ export function getElementZIndex(element: HTMLElement): number {
}
}
// fallow-ignore-next-line complexity
export function hasExplicitZIndex(element: HTMLElement): boolean {
try {
const inline = element.style?.zIndex;
if (inline) return inline !== "auto";
const win = element.ownerDocument?.defaultView;
if (!win) return false;
const value = win.getComputedStyle(element).zIndex;
return value !== "auto" && value !== "";
} catch {
return false;
}
}
export function computeReorderZValues(
existingValues: readonly number[],
fromIndex: number,