Files
hyperframes/packages/studio/src/player/components/timelineLaneProps.ts
T
Miguel ÁngelandCodex 96861cbafc perf(studio-server): coordinate cancelable thumbnail generation (#2720)
* perf(studio): schedule adaptive timeline thumbnails

* perf(studio): bound thumbnail decoding resources

* perf(studio): virtualize timeline thumbnail media

* perf(studio): prioritize timeline thumbnail work

* perf(studio-server): coordinate cancelable thumbnail generation

---------

Co-authored-by: Codex <codex@local>
2026-08-05 20:41:35 -07:00

101 lines
4.3 KiB
TypeScript

import { type ReactNode } from "react";
import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
import type { MusicBeatAnalysis } from "@hyperframes/core/beats";
import type { TimelineElement, KeyframeCacheEntry } from "../store/playerStore";
import type { TimelineKeyframeTarget } from "./timelineKeyframeIdentity";
import type { TimelineTheme } from "./timelineTheme";
import type { TrackVisualStyle } from "./timelineIcons";
import type { DraggedClipState, ResizingClipState, BlockedClipState } from "./useTimelineClipDrag";
import type { TimelineClipIndex, TimelineTimeRange } from "../lib/timelineClipIndex";
import type { TimelineRowGeometry } from "./timelineLayout";
import type { TimelineVirtualRow } from "./useTimelineVirtualRows";
import type { TimelineLogicalRow } from "./timelineKeyboardNavigation";
import type { TimelineClipRenderContext } from "./TimelineTypes";
/**
* Props shared by the scroll container ({@link import("./TimelineCanvas")}) and
* the lane renderer ({@link import("./TimelineLanes")}). TimelineCanvas passes
* these straight through via spread, so they are declared once here and both
* prop types compose from this base — no duplicated prop list. Kept in its own
* module so neither side has to import the other just to name the contract.
*/
export interface TimelineLaneBaseProps {
pps: number;
contentOrigin: number;
contentGutter: number;
trackContentWidth: number;
theme: TimelineTheme;
displayTrackOrder: number[];
rowHeights: readonly number[];
rowGeometry: TimelineRowGeometry;
virtualRows: readonly TimelineVirtualRow[];
logicalRows: readonly TimelineLogicalRow[];
focusedTargetId: string | null;
rowsVirtualized: boolean;
clipIndex: TimelineClipIndex;
renderTimeRange: TimelineTimeRange;
visibleTimeRange: TimelineTimeRange;
pinnedClipIdentities: ReadonlySet<string>;
trackOrder: number[];
tracks: [number, TimelineElement[]][];
trackStyles: Map<number, TrackVisualStyle>;
laneCounts: ReadonlyMap<string, number>;
selectedElementId: string | null;
selectedElementIds: Set<string>;
hoveredClip: string | null;
draggedClip: DraggedClipState | null;
blockedClipRef: React.RefObject<BlockedClipState | null>;
suppressClickRef: React.RefObject<boolean>;
scrollRef: React.RefObject<HTMLDivElement | null>;
renderClipContent?: (
element: TimelineElement,
style: { clip: string; label: string },
context: TimelineClipRenderContext,
) => ReactNode;
renderClipOverlay?: (element: TimelineElement) => ReactNode;
onDrillDown?: (element: TimelineElement) => void;
onSelectElement?: (element: TimelineElement | null) => void;
setHoveredClip: (key: string | null) => void;
setShowPopover: (v: boolean) => void;
setRangeSelection: (v: null) => void;
setResizingClip: (v: ResizingClipState | null) => void;
setDraggedClip: (v: DraggedClipState | null) => void;
setSelectedElementId: (id: string | null) => void;
shiftClickClipRef: React.RefObject<{
element: TimelineElement;
anchorX: number;
anchorY: number;
} | null>;
getPreviewElement: (element: TimelineElement) => TimelineElement;
getTrackStyle: (tag: string) => TrackVisualStyle;
keyframeCache?: Map<string, KeyframeCacheEntry>;
gsapAnimations: Map<string, GsapAnimation[]>;
selectedKeyframes: Set<string>;
currentTime: number;
onSeek?: (time: number) => void;
onSelectSegment?: (elementId: string, target: TimelineKeyframeTarget) => void;
onClickKeyframe?: (element: TimelineElement, target: TimelineKeyframeTarget) => void;
onShiftClickKeyframe?: (elementId: string, target: TimelineKeyframeTarget) => void;
onContextMenuKeyframe?: (
e: React.MouseEvent,
elementId: string,
target: TimelineKeyframeTarget,
) => void;
onMoveKeyframe?: (
elementId: string,
keyframe: TimelineKeyframeTarget,
toClipPercentage: number,
propertyGroup?: string,
tweenPercentage?: number,
animationId?: string,
) => Promise<boolean>;
onContextMenuClip?: (e: React.MouseEvent, element: TimelineElement) => void;
/**
* Right-click on EMPTY lane space (not on a clip — those preventDefault
* before this fires — not the gutter/ruler, not below the lanes). `time` is
* the timeline time (seconds) under the pointer on that lane.
*/
onContextMenuLane?: (e: React.MouseEvent, track: number, time: number) => void;
beatAnalysis?: MusicBeatAnalysis | null;
}