import { TemplateResult as TemplateResult$1 } from "../node_modules/lit-html/development/lit-html.js"; import { FrameController, FrameRenderable, FrameState } from "../preview/FrameController.js"; import { QualityUpgradeScheduler } from "../preview/QualityUpgradeScheduler.js"; import { TemporalMixinInterface } from "./EFTemporal.js"; import { EFMedia } from "./EFMedia.js"; import { ContainerInfo } from "./ContainerInfo.js"; import { ElementPositionInfo } from "./ElementPositionInfo.js"; import { RenderToVideoOptions, RenderWindow } from "../preview/renderTimegroupToVideo.types.js"; import * as _$lit from "lit"; import { LitElement, PropertyValues } from "lit"; //#region src/elements/EFTimegroup.d.ts type FrameTaskCallback = (info: { ownCurrentTimeMs: number; currentTimeMs: number; durationMs: number; percentComplete: number; element: EFTimegroup; }) => void | Promise; /** * Result of createRenderClone() - contains the clone, its container, and cleanup function. */ interface RenderCloneResult { /** The cloned timegroup, fully functional with its own time state */ clone: EFTimegroup; /** The offscreen container holding the clone */ container: HTMLElement; /** Call this to remove the clone from DOM and clean up */ cleanup: () => void; } /** * Initializer function type for setting up JavaScript behavior on timegroup instances. * This function is called on both the prime timeline and each render clone. * * CONSTRAINTS: * - MUST be synchronous (no async/await, no Promise return) * - MUST complete in <2000ms (error) or <100ms (warning) * - Should only register callbacks and set up behavior, not do expensive work * - GPU operations (WebGL context creation, shader compilation) may take up to ~1s */ type TimegroupInitializer = (timegroup: EFTimegroup) => void; /** * The four timegroup modes define how duration is calculated: * - "fit": Inherits duration from parent timegroup * - "fixed": Uses explicit duration attribute * - "sequence": Sum of child durations minus overlaps * - "contain": Maximum of child durations */ type TimeMode = "fit" | "fixed" | "sequence" | "contain"; /** * Per-phase timing data returned by seekForRender(). * All values are in milliseconds. */ interface SeekForRenderTiming { updateComplete1Ms: number; updateComplete2Ms: number; updateComplete3Ms: number; textSegmentsMs: number; renderFrameMs: number; renderFrameQueryMs: number; renderFramePrepareMs: number; renderFrameDrawMs: number; renderFrameAnimsMs: number; frameTasksMs: number; totalMs: number; } declare const EFTimegroup_base: (new (...args: any[]) => TemporalMixinInterface) & typeof LitElement; declare class EFTimegroup extends EFTimegroup_base implements FrameRenderable { #private; static get observedAttributes(): string[]; static styles: _$lit.CSSResult; /** @internal */ _timeGroupContext: this; /** @internal */ efContext: this; shouldAutoReady(): boolean; get mode(): TimeMode; set mode(value: TimeMode); get overlapMs(): number; set overlapMs(value: number); /** * Initializer function for setting up JavaScript behavior on this timegroup. * This function is called ONCE per instance - on the prime timeline when first connected, * and on each render clone when created. * * Use this to register frame callbacks, set up event listeners, or initialize state. * The same initializer code runs on both prime and clones, eliminating duplication. * * CONSTRAINTS: * - MUST be synchronous (no async/await, no Promise return) * - MUST complete in <100ms (error thrown) or <10ms (warning logged) * - Should only register callbacks and set up behavior, not do expensive work * * TIMING: * - If set before element connects to DOM: runs automatically after connectedCallback * - If set after element is connected: runs immediately * - Clones automatically copy and run the initializer when created * * @example * ```javascript * const tg = document.querySelector('ef-timegroup'); * tg.initializer = (instance) => { * // Runs once on prime timeline, once on each clone * instance.addFrameTask((info) => { * // Update content based on time * }); * }; * ``` * @public */ get initializer(): TimegroupInitializer | undefined; set initializer(fn: TimegroupInitializer | undefined); /** @public */ fps: number; /** * When true, automatically seeks to frame 0 after media durations are loaded. * Only applies to root timegroups (timegroups that are not nested inside another timegroup). * This ensures the first frame is rendered immediately on initialization. */ autoInit: boolean; /** * When true, automatically wraps this root timegroup with an ef-workbench element. * The workbench provides development UI including hierarchy panel, timeline, and playback controls. * Only applies to root timegroups. * @public */ workbench: boolean; attributeChangedCallback(name: string, old: string | null, value: string | null): void; /** @public */ fit: "none" | "contain" | "cover"; /** @internal */ isRestoringFromLocalStorage(): boolean; /** @internal - Used by PlaybackController to set restoration state */ setRestoringFromLocalStorage(value: boolean): void; /** * Get the frame controller for centralized rendering coordination. * @public */ get frameController(): FrameController; /** * Get the quality upgrade scheduler for background segment fetching. * @public */ get qualityUpgradeScheduler(): QualityUpgradeScheduler; /** * Query timegroup's readiness state for a given time. * Timegroups are always ready (no async preparation needed). * @public */ getFrameState(_timeMs: number): FrameState; /** * Async preparation phase (no-op for timegroups). * Timegroups don't need preparation - they just coordinate child rendering. * @public */ prepareFrame(_timeMs: number, _signal: AbortSignal): Promise; /** * Synchronous render phase - executes custom frame callbacks. * Called by FrameController after all preparation is complete. * Kicks off async frame callbacks without blocking (they run in background). * @public */ renderFrame(_timeMs: number): void; /** * Get the effective FPS for this timegroup. * During rendering, uses the render options FPS if available. * Otherwise uses the configured fps property. * @public */ get effectiveFps(): number; /** * The current time of this timegroup within itself, in milliseconds. * * For child timegroups with an explicit `fps` attribute, the derived time is * quantized to the nearest frame boundary at that fps — producing the visual * "hold" effect of a lower frame rate segment. Without an explicit `fps` * attribute the inherited EFTemporal implementation is used unchanged. * @public */ get ownCurrentTimeMs(): number; /** * Get the current content epoch (used by thumbnail cache). * The epoch increments whenever visual content changes. * @public */ get contentEpoch(): number; /** * Increment content epoch (called when visual content changes). * This invalidates cached thumbnails by changing their cache keys. * @public */ incrementContentEpoch(): void; /** * Request a frame re-render at the current time. * * Use this when the source-to-timeline mapping has changed (e.g., sourcein/sourceout) * but currentTimeMs hasn't. The FrameController only re-renders when currentTimeMs * or durationMs change, so this provides a way for child elements to request a * re-render when their internal state changes the visual output. * @public */ requestFrameRender(): void; /** * Whether this timegroup is in headless rendering mode — either a render clone * (off-screen thumbnail / export, marked with `data-no-playback-controller`) * or the live FRAMEGEN render context (Electron cloud rendering, where * `FRAMEGEN_BRIDGE` is injected by the main process before the page loads). * * In both cases seekForRender() is the exclusive render driver and autonomous * re-renders must be suppressed. FRAMEGEN_BRIDGE is present from the very first * element connection, which is why it must be checked here rather than waiting * for initialize() to set the attribute. */ get isRenderClone(): boolean; /** * Eagerly warm the scrub BufferedSeekingInput cache for all ef-video descendants * whose source overlaps the given timeline range. Called by EFTimeline when the * visible viewport changes so that subsequent scrub seeks are served from cache. */ warmScrubForVisibleRange(fromMs: number, toMs: number, signal?: AbortSignal): void; /** @public */ set currentTime(time: number); /** @public */ get currentTime(): number; /** @public */ set currentTimeMs(ms: number); /** @public */ get currentTimeMs(): number; /** * The time the user last requested via seek/scrub. * Preview systems should use this instead of currentTimeMs to avoid * seeing intermediate times during batch operations (thumbnails, export). * @public */ get userTimeMs(): number; /** * Seek to a specific time and wait for all frames to be ready. * This is the recommended way to seek in tests and programmatic control. * * Combines seeking (Purpose 3) with frame rendering (Purpose 4) to ensure * all visible elements are ready after the seek completes. * * Updates both the source time AND userTimeMs (what the preview displays). * * @param timeMs - Time in milliseconds to seek to * @returns Promise that resolves when the seek is complete and all visible children are ready * @public */ seek(timeMs: number): Promise; /** * Optimized seek for render loops. * Unlike `seek()`, this: * - Skips waitForMediaDurations (already loaded at render setup) * - Skips localStorage persistence * - Uses FrameController for centralized element coordination * * Still waits for all content to be ready (Lit updates, element preparation, rendering). * * @param timeMs - Time in milliseconds to seek to * @internal */ seekForRender(timeMs: number): Promise; /** * Determines if this is a root timegroup (no parent timegroups) * @public */ get isRootTimegroup(): boolean; /** * Property-based frame task callback for React integration. * When set, automatically registers the callback as a frame task. * Setting a new value automatically cleans up the previous callback. * Set to null or undefined to remove the callback. * * @example * // React usage: * { * // Per-frame updates * }} /> * * @public */ get onFrame(): FrameTaskCallback | null; set onFrame(callback: FrameTaskCallback | null | undefined); /** * Register a custom frame task callback that will be executed during frame rendering. * The callback receives timing information and can be async or sync. * Multiple callbacks can be registered and will execute in parallel. * * @param callback - Function to execute on each frame * @returns A cleanup function that removes the callback when called * @public */ addFrameTask(callback: FrameTaskCallback): () => void; /** * Remove a previously registered custom frame task callback. * * @param callback - The callback function to remove * @public */ removeFrameTask(callback: FrameTaskCallback): void; /** @internal */ saveTimeToLocalStorage(time: number): void; render(): TemplateResult$1<1>; /** @internal */ loadTimeFromLocalStorage(): number | undefined; connectedCallback(): void; /** * Called when this timegroup becomes a root (no parent timegroup). * Sets up the playback listener after PlaybackController is created. * @internal */ didBecomeRoot(): void; protected updated(changedProperties: PropertyValues): void; disconnectedCallback(): void; /** * Render the timegroup to an MP4 video file and trigger download. * Captures each frame at the specified fps, encodes using WebCodecs via * MediaBunny, and downloads the resulting video. * * Uses dynamic import to only load render utilities in browser context. * * @param options - Rendering options (fps, codec, bitrate, filename, etc.) * @returns Promise that resolves when video is downloaded * @public */ renderToVideo(options?: RenderToVideoOptions): Promise; /** * Create an independent clone of this timegroup for rendering. * The clone is a fully functional ef-timegroup with its own animations * and time state, isolated from the original (Prime-timeline). * * OPTIONAL: An initializer can be set via `timegroup.initializer = (tg) => { ... }` * to re-run JavaScript setup (frame callbacks, React components) on each clone. * * This enables: * - Rendering without affecting user's preview position * - Concurrent renders with different clones * - Re-running JavaScript setup on each clone (if initializer is provided) * * @returns Promise resolving to clone, container, and cleanup function * @throws Error if initializer is async or takes too long * @public * @param options.renderWindow - When provided, only media elements whose * temporal range overlaps [fromMs, toMs) receive a full MediaEngine. * All others get a lightweight index-only duration probe, cutting memory * for large compositions from O(N clips) to O(active clips in window). */ createRenderClone(options?: { renderWindow?: RenderWindow; }): Promise; /** @internal */ get storageKey(): string; /** @internal */ get intrinsicDurationMs(): number | undefined; /** @internal */ get hasOwnDuration(): boolean; /** @public */ get durationMs(): number; /** @internal */ waitForMediaDurations(signal?: AbortSignal, renderWindow?: RenderWindow): Promise; /** * Lightweight duration probe: calls getDurationOnly() on every descendant * media element instead of the full getMediaEngine(). One index-JSON fetch * per clip, no SegmentTransport / TimingModel / CachedFetcher allocation. * * Use this in getRenderInfo contexts where only durationMs is needed — * not for rendering (use waitForMediaDurations / createRenderClone for that). * @internal */ waitForMediaDurationsLight(signal?: AbortSignal): Promise; /** @internal */ get childTemporals(): TemporalMixinInterface[]; /** * Returns true if the timegroup should be wrapped with a workbench. * * A timegroup should be wrapped with a workbench if: * - It's being rendered (EF_RENDERING), OR * - The workbench property is set to true * * If the timegroup is already wrapped in a context provider like ef-preview, * it should NOT be wrapped in a workbench. * @internal */ shouldWrapWithWorkbench(): boolean; /** @internal */ wrapWithWorkbench(): void; /** * Returns media elements for playback audio rendering * For standalone media, returns [this]; for timegroups, returns all descendants * Used by PlaybackController for audio-driven playback * @internal */ getMediaElements(): EFMedia[]; /** * Render audio buffer for playback * Called by PlaybackController during live playback * Delegates to shared renderTemporalAudio utility for consistent behavior * @internal */ renderAudio(fromMs: number, toMs: number, signal?: AbortSignal): Promise; seekTask: { run(): void | Promise; taskComplete: Promise; }; /** * Get container information for this timegroup. * Timegroups are always containers and can contain children. * Display mode is determined from computed styles. * * @public */ getContainerInfo(): ContainerInfo; /** * Get position information for this timegroup. * Returns computed bounds, transform, and rotation. * * @public */ getPositionInfo(): ElementPositionInfo | null; } declare global { interface HTMLElementTagNameMap { "ef-timegroup": EFTimegroup & Element; } } //#endregion export { EFTimegroup }; //# sourceMappingURL=EFTimegroup.d.ts.map