import * as react from 'react'; import { ReactNode, RefObject } from 'react'; import * as zustand from 'zustand'; import { MusicBeatAnalysis } from '@hyperframes/core/beats'; import * as react_jsx_runtime from 'react/jsx-runtime'; import * as _hyperframes_core_gsap_parser from '@hyperframes/core/gsap-parser'; import { GsapAnimation } from '@hyperframes/core/gsap-parser'; interface PlayerProps { projectId?: string; directUrl?: string; onLoad: () => void; onCompositionLoadingChange?: (loading: boolean) => void; portrait?: boolean; style?: React.CSSProperties; suppressLoadingOverlay?: boolean; } /** * Renders a composition preview using the web component. * * The web component handles iframe scaling, dimension detection, and * ResizeObserver internally. This wrapper bridges its inner iframe to the * forwarded ref so useTimelinePlayer can access it for clip manifest parsing, * timeline probing, and DOM inspection. */ declare const Player: react.ForwardRefExoticComponent>; interface PlayerControlsProps { onTogglePlay: () => void; onSeek: (time: number) => void; disabled?: boolean; isFullscreen?: boolean; onToggleFullscreen?: () => void; } declare const PlayerControls: react.NamedExoticComponent; interface UserBeat { time: number; strength: number; } interface BeatEditState { /** Music src these edits apply to; edits reset when the src changes. */ src: string; /** Beats the user added (audio-file coords). */ added: UserBeat[]; /** Audio-file times of detected beats the user removed. */ removed: number[]; } interface ClipManifestClip { id: string | null; label: string; start: number; duration: number; track: number; kind: "video" | "audio" | "image" | "element" | "composition"; tagName: string | null; compositionId: string | null; parentCompositionId: string | null; compositionSrc: string | null; assetUrl: string | null; } /** Minimal keyframe cache types — mirrors GsapKeyframesData without pulling in Node-only gsap-parser. */ interface KeyframeCacheEntry { format: string; keyframes: Array<{ percentage: number; /** Original tween-relative percentage (server mutations need this, not the clip-relative `percentage`). */ tweenPercentage?: number; /** Which property group the source tween belongs to (position, scale, rotation, visual, etc.). */ propertyGroup?: string; properties: Record; ease?: string; }>; ease?: string; easeEach?: string; } interface TimelineElement { id: string; label?: string; key?: string; tag: string; start: number; duration: number; track: number; domId?: string; /** Stable `data-hf-id` attribute value — used as primary patch target when present */ hfId?: string; /** Best-effort selector used when patching source HTML back from timeline edits */ selector?: string; /** Zero-based occurrence index for non-unique selectors */ selectorIndex?: number; /** Source composition file that owns this element, when known */ sourceFile?: string; src?: string; playbackStart?: number; playbackStartAttr?: "media-start" | "playback-start"; playbackRate?: number; sourceDuration?: number; volume?: number; /** Path from data-composition-src — identifies sub-composition elements */ compositionSrc?: string; /** Whether this row came from authored clip timing or Studio's full-duration layer fallback. */ timingSource?: "authored" | "implicit"; /** Set by data-timeline-locked on the host element — disables move and trim in Studio. */ timelineLocked?: boolean; /** Value of data-timeline-role attribute — used to identify music vs. voiceover. */ timelineRole?: string; /** * Set by useExpandedTimelineElements on an inline-expanded sub-composition * child: the absolute master-timeline start of the sub-comp host the child * lives in. Presence marks the element as expanded; edits subtract it to get * the child's local (sourceFile-relative) time. Works at any nesting depth. */ expandedParentStart?: number; } type ZoomMode = "fit" | "manual"; type TimelineTool = "select" | "razor"; interface PlayerState { isPlaying: boolean; currentTime: number; duration: number; timelineReady: boolean; /** True while a beat dot is being dragged — hides the playhead guideline. */ beatDragging: boolean; elements: TimelineElement[]; selectedElementId: string | null; playbackRate: number; audioMuted: boolean; loopEnabled: boolean; /** Timeline zoom: 'fit' auto-scales to viewport, 'manual' uses manualZoomPercent */ zoomMode: ZoomMode; /** Timeline zoom percent relative to the fit width when in manual mode */ manualZoomPercent: number; /** Work-area in-point (seconds). When set, loop starts here and A jumps here. */ inPoint: number | null; /** Work-area out-point (seconds). When set, loop ends here and E jumps here. */ outPoint: number | null; activeTool: TimelineTool; setActiveTool: (tool: TimelineTool) => void; /** Set of selected keyframe keys in format `${elementId}:${percentage}`. */ selectedKeyframes: Set; toggleSelectedKeyframe: (key: string) => void; clearSelectedKeyframes: () => void; /** Tween-relative percentage of the last-clicked keyframe diamond. Operations * (drag, resize, rotate) target this instead of recomputing from playhead. */ activeKeyframePct: number | null; setActiveKeyframePct: (pct: number | null) => void; /** Motion-path "set destination" mode. Armed from the preview toolbar (replaces * the old double-click-on-canvas UX); while armed, one canvas click places the * new path's destination. `available` is published by MotionPathOverlay so the * toolbar shows the button only when the selected element can take a path. */ motionPathArmed: boolean; setMotionPathArmed: (armed: boolean) => void; motionPathCreateAvailable: boolean; setMotionPathCreateAvailable: (available: boolean) => void; /** Multi-select: additional selected elements beyond selectedElementId. */ selectedElementIds: Set; toggleSelectedElementId: (id: string) => void; clearSelectedElementIds: () => void; /** Keyframe data per element id, populated from parsed GSAP animations. */ keyframeCache: Map; setKeyframeCache: (elementId: string, data: KeyframeCacheEntry | undefined) => void; setIsPlaying: (playing: boolean) => void; setCurrentTime: (time: number) => void; setDuration: (duration: number) => void; setPlaybackRate: (rate: number) => void; setAudioMuted: (muted: boolean) => void; setLoopEnabled: (enabled: boolean) => void; setTimelineReady: (ready: boolean) => void; setBeatDragging: (dragging: boolean) => void; setElements: (elements: TimelineElement[]) => void; setSelectedElementId: (id: string | null) => void; updateElement: (elementId: string, updates: Partial>) => void; setZoomMode: (mode: ZoomMode) => void; setManualZoomPercent: (percent: number) => void; setInPoint: (time: number | null) => void; setOutPoint: (time: number | null) => void; reset: () => void; /** * Request a seek from outside the player loop (e.g. Layers panel). * useTimelinePlayer subscribes and calls adapter.seek() + liveTime.notify(). */ requestedSeekTime: number | null; requestSeek: (time: number) => void; clearSeekRequest: () => void; lintFindingsByElement: Map; setLintFindingsByElement: (map: Map) => void; beatAnalysis: MusicBeatAnalysis | null; setBeatAnalysis: (analysis: MusicBeatAnalysis | null) => void; /** User edits (add/move/delete) layered over the detected beat grid. */ beatEdits: BeatEditState | null; setBeatEdits: (edits: BeatEditState | null) => void; /** Undo/redo stacks for beat edits (in-memory, session-only). */ beatUndo: BeatHistoryEntry[]; beatRedo: BeatHistoryEntry[]; commitBeatEdits: (next: BeatEditState | null, label: string) => void; undoBeatEdits: () => string | null; redoBeatEdits: () => string | null; resetBeatHistory: () => void; beatPersist: (() => void) | null; setBeatPersist: (fn: (() => void) | null) => void; clipManifest: ClipManifestClip[] | null; setClipManifest: (clips: ClipManifestClip[] | null) => void; clipParentMap: Map; setClipParentMap: (map: Map) => void; } interface BeatHistoryEntry { restore: BeatEditState | null; at: number; label: string; } type TimeListener = (time: number) => void; declare const liveTime: { notify: (t: number) => void; subscribe: (cb: TimeListener) => () => boolean; }; declare const usePlayerStore: zustand.UseBoundStore>; interface TimelineTheme { shellBackground: string; shellBorder: string; rulerBorder: string; rowBackground: string; rowBorder: string; gutterBackground: string; gutterBorder: string; textPrimary: string; textSecondary: string; tickText: string; tickMajor: string; tickMinor: string; clipBackground: string; clipBackgroundActive: string; clipBorder: string; clipBorderHover: string; clipBorderActive: string; clipShadow: string; clipShadowHover: string; clipShadowActive: string; clipShadowDragging: string; handleColor: string; panelResizeSeam: string; panelResizeActive: string; clipRadius: string; } type BlockedTimelineEditIntent = "move" | "resize-start" | "resize-end"; /** * Shared callback signatures for timeline editing operations. * Used by NLELayout, Timeline, and any component that passes through * the standard set of timeline mutation handlers. */ interface TimelineDropCallbacks { onFileDrop?: (files: File[], placement?: { start: number; track: number; }) => Promise | void; onAssetDrop?: (assetPath: string, placement: { start: number; track: number; }) => Promise | void; onBlockDrop?: (blockName: string, placement: { start: number; track: number; }) => Promise | void; } interface TimelineEditCallbacks { onMoveElement?: (element: TimelineElement, updates: Pick) => Promise | void; onResizeElement?: (element: TimelineElement, updates: Pick) => Promise | void; onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void; onSplitElement?: (element: TimelineElement, splitTime: number) => Promise | void; onRazorSplit?: (element: TimelineElement, splitTime: number) => Promise | void; onRazorSplitAll?: (splitTime: number) => Promise | void; onDeleteKeyframe?: (elementId: string, percentage: number) => void; onDeleteAllKeyframes?: (elementId: string) => void; onChangeKeyframeEase?: (elementId: string, percentage: number, ease: string) => void; onMoveKeyframe?: (element: TimelineElement, oldPct: number, newPct: number) => void; onToggleKeyframeAtPlayhead?: (element: TimelineElement) => void; } type TimelineEditOverrides = Pick; interface TimelineProps extends TimelineDropCallbacks, TimelineEditOverrides { onSeek?: (time: number) => void; onDrillDown?: (element: TimelineElement) => void; renderClipContent?: (element: TimelineElement, style: { clip: string; label: string; }) => ReactNode; renderClipOverlay?: (element: TimelineElement) => ReactNode; onDeleteElement?: (element: TimelineElement) => Promise | void; onSelectElement?: (element: TimelineElement | null) => void; theme?: Partial; } declare const Timeline: react.NamedExoticComponent; interface VideoThumbnailProps { videoSrc: string; label: string; labelColor: string; duration?: number; } /** * Renders a film-strip of video frames extracted client-side via a hidden *