import { TemporalMixinInterface } from "../../elements/EFTemporal.js"; import { SelectionContext } from "../../canvas/selection/selectionContext.js"; import { TimelineState } from "./timelineStateContext.js"; import * as _$lit from "lit"; import { LitElement, PropertyValues, TemplateResult } from "lit"; //#region src/gui/timeline/EFTimeline.d.ts declare const EFTimeline_base: typeof LitElement; /** * EFTimeline - Unified timeline component * * Core invariant: pixelsPerMs determines all positioning. * Everything else (ruler, tracks, playhead) derives from this single value. */ declare class EFTimeline extends EFTimeline_base { #private; static styles: _$lit.CSSResult[]; /** * Target element ID or "selection" to derive from canvas selection. * * - Empty string (default): Automatically derives target from canvas selection. * The timeline shows the root temporal element containing the currently selected element. * - "selection": Explicitly use selection-derived targeting (same as empty string). * - Element ID: Use the specified element as the target (must be a temporal element). * * When deriving from selection, the timeline automatically updates when selection changes. */ target: string; /** * The core zoom value - pixels per millisecond. * All positioning derives from this single value. */ pixelsPerMs: number; /** Maximum zoom as a multiplier of DEFAULT_PIXELS_PER_MS (0.1 px/ms). * 90 × 0.1 = 9.0 px/ms ≈ 300 px/frame at 30fps — suitable for frame-level editing. */ maxZoom: number; enableTrim: boolean; showControls: boolean; showRuler: boolean; showHierarchy: boolean; showPlayhead: boolean; showPlaybackControls: boolean; showZoomControls: boolean; showTimeDisplay: boolean; /** * Target temporal element ID for playback control. * Use this to specify which temporal element the timeline controls. * If not set and target is a canvas, derives from canvas selection. * * Examples: * - `control-target="timegroup-1"` - control specific timegroup * - Empty: derive from canvas active selection */ controlTarget: string; /** * CSS selectors for elements to hide in the timeline. * Comma-separated list of selectors (e.g., "ef-waveform, .helper"). */ hide: string; /** * CSS selectors for elements to show in the timeline. * When set, only matching elements are shown. Comma-separated list. */ show: string; get hideSelectors(): string[] | undefined; get showSelectors(): string[] | undefined; /** * Target element for canvas-wide state (selection, highlight). * This should be set to the canvas element. */ private targetElement; private _highlightedElement; private currentTimeMs; private isPlaying; private isLooping; private viewportScrollLeft; private _timelineState; private _editingContext; private targetController?; private tracksScrollRef; private containerRef; private playheadRef; private playheadHandleRef; private frameHighlightRef; private playheadLayerRef; private animationFrameId?; private selectionChangeHandler?; private scrollHandler?; private keydownHandler?; private isDraggingPlayhead; private targetObserver?; private canvasActiveRootTemporalChangeHandler?; private resizeObserver?; private cachedViewportWidth; private saveZoomScrollDebounceTimer; private scrubPrefetchDebounceTimer; private scrubPrefetchAbortController; private lastContextUpdateTime; private static readonly CONTEXT_UPDATE_INTERVAL_MS; selectionContext?: SelectionContext; get providedPlaying(): boolean; get providedLoop(): boolean; get providedCurrentTime(): number; get providedDuration(): number; get providedTargetTemporal(): TemporalMixinInterface | null; /** Get timeline state (for external access) */ get timelineState(): TimelineState; /** Update timeline state when any constituent value changes */ private updateTimelineState; /** * Get the target canvas element. * The canvas is the source of truth for selection and highlight state. */ private getCanvas; private getCanvasSelectionContext; /** * Get the currently highlighted element. * Local state is the source of truth; canvas is kept in sync as a side effect. */ getHighlightedElement(): HTMLElement | null; /** * Set the highlighted element. * Always updates local state (triggers re-render); also syncs to canvas when present. */ setHighlightedElement(element: HTMLElement | null): void; get targetTemporal(): TemporalMixinInterface | null; get durationMs(): number; /** Content width in pixels (derived from duration and pixelsPerMs) */ get contentWidthPx(): number; /** Current zoom as percentage (for display) */ get zoomPercent(): number; /** Derive fps from target temporal (defaults to 30) */ get fps(): number; /** Whether frame markers should be visible at current zoom */ get showFrameMarkers(): boolean; /** * Get the root timegroup ID for localStorage key generation. * Returns null if no root timegroup is found or it has no ID. */ private getRootTimegroupId; /** * Get localStorage key for timeline state (zoom and scroll). */ private getTimelineStorageKey; /** * Save timeline zoom and scroll to localStorage. */ private saveTimelineState; /** * Restore timeline zoom and scroll from localStorage. */ private restoreTimelineState; /** * Debounced save of timeline state to avoid excessive localStorage writes. */ private debouncedSaveTimelineState; /** * Debounced warm of the scrub cache for the currently visible timeline range. * Triggered by zoom or scroll changes so scrub seeks within the viewport are * served from the BufferedSeekingInput cache rather than the network. */ private debouncedWarmScrubForVisibleRange; connectedCallback(): void; disconnectedCallback(): void; /** * Setup MutationObserver to watch target element for ANY changes. * Re-registers when target changes. */ private setupTargetObserver; protected willUpdate(changedProperties: PropertyValues): void; protected firstUpdated(): void; private setupResizeObserver; protected updated(changedProperties: PropertyValues): void; private setupSelectionListener; private removeSelectionListener; /** * Single source-of-truth for applying a scroll position change. * Keeps ruler-outer, viewportScrollLeft, playhead, and clip-path all in sync * immediately — without relying on the async scroll event. */ private applyScrollLeft; private setupScrollListener; private removeScrollListener; private setupKeyboardListener; private removeKeyboardListener; /** Margin from edge before auto-scroll kicks in during playback */ private static readonly PLAYHEAD_MARGIN; private lastPlayheadPx; private isFollowingPlayhead; private startTimeUpdate; /** * Update playhead position directly via DOM manipulation. * This bypasses the Lit render cycle for smooth 60fps playhead movement. */ private updatePlayheadPositionDirect; /** * Smooth playhead following - scrolls to keep playhead at a fixed screen position. * This eliminates jitter by scrolling exactly as much as the playhead moves. * * PERFORMANCE NOTE: We DO update viewportScrollLeft state here, but the context * cascade is prevented in updateTimelineState() during playback. This means: * - State stays in sync (for non-context consumers) * - But context consumers don't re-render during auto-scroll * - Components inside the scroll container scroll natively */ private followPlayhead; private stopTimeUpdate; /** * Subscribe to playback controller events for playing/loop state. * This avoids race conditions from polling when targetTemporal changes. */ private subscribeToPlaybackController; /** * Unsubscribe from playback controller events. */ private unsubscribeFromPlaybackController; private handlePlay; private handlePause; private handleToggleLoop; /** * The pixelsPerMs value that fits the entire timeline into the viewport without scrolling. * This is the minimum zoom level — you cannot zoom out further than this. */ private get fitPixelsPerMs(); /** Minimum allowed pixelsPerMs — always equals fitPixelsPerMs so the full timeline is always reachable. */ private get effectiveMinPixelsPerMs(); private handleZoomIn; private handleZoomOut; private handleFitToViewport; /** * Handle wheel events for gestural zoom. * Cmd/Ctrl + wheel zooms toward the cursor position. * Without modifier, native scroll behavior is preserved. */ private handleWheel; /** * Seek to a specific time, optionally quantizing to frame boundaries. * @param timeMs The raw time to seek to * @param snapToFrame Whether to quantize to the nearest frame boundary (default: true when frame markers visible) */ private handleSeek; /** * Handle keyboard navigation for frame-by-frame or second-by-second movement. * - Arrow Left/Right: move by one frame * - Shift+Arrow Left/Right: move by one second */ private handleKeyDown; private handleRulerPointerDown; private handlePlayheadPointerDown; /** Hierarchy panel width - must match CSS --timeline-hierarchy-width */ private static readonly HIERARCHY_WIDTH; private handleTracksPointerDown; /** Edge scroll zone width in pixels */ private static readonly EDGE_SCROLL_ZONE; /** Base scroll speed in pixels per frame */ private static readonly EDGE_SCROLL_SPEED; private startPlayheadDrag; private formatTime; private renderPlaybackControls; private renderTimeDisplay; private renderZoomControls; /** * Render the frame highlight placeholder. * Position and visibility are controlled exclusively by updatePlayheadPositionDirect * via the frameHighlightRef so that Lit re-renders never clobber the 60fps rAF * position with the throttled this.currentTimeMs value. */ private renderFrameHighlight; private renderControls; private handleTrimChange; /** * Handle row hover events - update canvas highlighted element. */ private handleRowHover; /** * Handle row selection events - update selection context. */ private handleRowSelect; /** * Render timeline rows using flattened hierarchy. * Each row is a unified component with both label and track. */ private renderRows; render(): TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { "ef-timeline": EFTimeline; } } //#endregion export { EFTimeline }; //# sourceMappingURL=EFTimeline.d.ts.map