mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
A group renders as its own row with member rows beneath it, and disclosure
splits into two independent controls: caret shows/hides a group's member
rows (structural), `∿` shows/hides any row's automation-lane rows. Plain
tracks lose their caret (nothing to disclose structurally) and keep only
`∿`. `expandedClipIds` keeps its existing keyframe-lane-state job;
`expandedGroupIds`/`expandedLaneOwnerIds` are new, independent sets.
Groups get a real position in the row/geometry pipeline rather than a
visual-only overlay: `useTimelineTrackDerivations` re-emits a group's member
tracks contiguously under a synthetic fractional anchor key
(firstMember - 0.5, the same fractional-key convention sub-composition
expansion already uses), so `rowGeometry`/keyboard-nav/virtualization treat
a group row as a first-class row without widening their key type away from
number. `TimelineLogicalRow.level` widens `1 | 2` to `1 | 2 | 3` (group /
member-under-group / lane), lanes always `owner.level + 1`.
All of it — grouped row emission, the header, the new expansion state — is
gated behind `isCanaryEnabled("audio-groups")`; disabled, `groups` resolves
empty and every new code path no-ops. `TimelineElement.audioGroup` (+
`audioGroupLabel`, resolved once per document via `resolveAudioGroups` from
B1) is parsed unconditionally, mirroring how `hidden`/`fxChain` already
flow DOM → manifest → TimelineElement — inert without the canary.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
31 lines
1.1 KiB
TypeScript
31 lines
1.1 KiB
TypeScript
import { isMultiDragActive, multiDragDeltaSeconds } from "./timelineMultiDragPreview";
|
|
import type { MultiDragPreviewInput } from "./timelineMultiDragPreview";
|
|
import type { TimelineTimeRange } from "../lib/timelineClipIndex";
|
|
|
|
/** The extra render window a live multi-drag needs: its passengers still draw
|
|
* at their ORIGIN position, offset by the drag delta, outside the normal
|
|
* virtualized range. Empty when nothing is dragging. */
|
|
export function useTimelineMultiDragActorWindows(
|
|
multiDragPreview: MultiDragPreviewInput | null,
|
|
rowsVirtualized: boolean,
|
|
renderTimeRange: TimelineTimeRange,
|
|
) {
|
|
const multiDragDelta =
|
|
multiDragPreview && isMultiDragActive(multiDragPreview)
|
|
? multiDragDeltaSeconds(multiDragPreview)
|
|
: 0;
|
|
const actorWindows =
|
|
rowsVirtualized && multiDragPreview && multiDragDelta !== 0
|
|
? [
|
|
{
|
|
range: {
|
|
start: renderTimeRange.start - multiDragDelta,
|
|
end: renderTimeRange.end - multiDragDelta,
|
|
},
|
|
identities: multiDragPreview.selectedKeys,
|
|
},
|
|
]
|
|
: [];
|
|
return actorWindows;
|
|
}
|