import { c as SequenceOperation, b as SequenceApplyResult } from '../apply-6wlMOLf8.js';
import { f as SequenceTimeline, g as SequenceClip, e as SequenceTrack } from '../store-B7LLlk9p.js';
import * as react from 'react';
import react__default from 'react';

/**
 * Seams between the timeline editor's engine, media pipeline, and components.
 * Everything here is interface-only so the three layers build independently
 * and products can substitute any layer (e.g. a different frame provider)
 * without forking the editor.
 *
 * Persistence model: the command stack executes optimistically against local
 * timeline state and emits the equivalent `SequenceOperation[]` through
 * `TimelineEditorProps.onApplyOperations`. A rejected promise rolls the
 * command back — the server stays the source of truth, the editor stays
 * responsive.
 */

/** One undoable edit. `execute`/`undo` mutate ONLY local editor state; the
 *  emitted operations are the durable form sent to the product. */
interface TimelineCommand {
    label: string;
    execute(state: EditorTimelineState): EditorTimelineState;
    undo(state: EditorTimelineState): EditorTimelineState;
    /** Durable operations equivalent to `execute` (sent on commit). */
    operations(): SequenceOperation[];
    /** Durable operations equivalent to `undo` (sent when the user undoes a
     *  committed command). */
    inverseOperations(): SequenceOperation[];
}
/** Local editor state — the timeline plus volatile view state the server
 *  never sees. */
interface EditorTimelineState {
    timeline: SequenceTimeline;
    playheadFrame: number;
    selectedClipIds: string[];
    zoom: number;
    scrollLeft: number;
}
/** Manage and track command execution with undo, redo, state subscription, and timeline reset capabilities */
interface CommandStack {
    execute(command: TimelineCommand): void;
    undo(): void;
    redo(): void;
    canUndo(): boolean;
    canRedo(): boolean;
    /** Subscribe to state changes; returns an unsubscribe. */
    subscribe(listener: () => void): () => void;
    getState(): EditorTimelineState;
    /** Replace timeline state from a server refresh WITHOUT clearing history. */
    reset(timeline: SequenceTimeline): void;
}
/** Exponential zoom mapping so the slider feels linear across a 10x+ range.
 *  zoom = pixels per frame. */
interface ZoomMath {
    sliderToZoom(slider: number): number;
    zoomToSlider(zoom: number): number;
    minZoom: number;
    maxZoom: number;
}
/** Define a point in a timeline where snapping occurs based on frame and kind */
interface SnapPoint {
    frame: number;
    kind: 'clip-start' | 'clip-end' | 'playhead' | 'sequence-end';
}
/** Describe the result of snapping a point to a frame including success status and snapped point details */
interface SnapResult {
    frame: number;
    snapped: boolean;
    point: SnapPoint | null;
}
/** rAF-driven playback clock. `performance.now()` deltas drive the playhead;
 *  emits one callback per animation frame while playing. */
interface PlaybackClock {
    play(): void;
    pause(): void;
    seek(frame: number): void;
    isPlaying(): boolean;
    getFrame(): number;
    subscribe(listener: (frame: number) => void): () => void;
    dispose(): void;
}
/** Supplies decoded frames for preview rendering. The baseline implementation
 *  seeks an off-DOM HTMLVideoElement (works everywhere); a WebCodecs
 *  implementation can replace it behind the same seam. */
interface VideoFrameProvider {
    /** Draw the frame at `sourceSeconds` into `ctx` at the given rect. Resolves
     *  when the frame is painted; rejects when the media cannot be decoded. */
    drawFrame(mediaUrl: string, sourceSeconds: number, ctx: CanvasRenderingContext2D, rect: {
        x: number;
        y: number;
        width: number;
        height: number;
    }): Promise<void>;
    /** Pre-warm a media URL so the first `drawFrame` doesn't stall. */
    prefetch(mediaUrl: string): void;
    dispose(): void;
}
/** min/max sample peaks per pixel bucket for waveform rendering. */
interface WaveformData {
    peaks: Float32Array;
    /** Samples represented per peak pair. */
    samplesPerBucket: number;
    durationSeconds: number;
}
/** Represent a segment of transcription with text and start and end times in seconds */
interface TranscriptionSegment {
    text: string;
    startSeconds: number;
    endSeconds: number;
}
/** Whisper-in-a-worker contract. Implementations dynamically import the model
 *  runtime; `available` is false when the optional peer is not installed. */
interface TranscriptionProvider {
    available: boolean;
    transcribe(mediaUrl: string, opts?: {
        language?: string;
        onProgress?: (fraction: number) => void;
    }): Promise<TranscriptionSegment[]>;
}
/** Overridable copy for the editor's product-facing labels. Every field is
 *  optional; the editor ships clear defaults (see `DEFAULT_TIMELINE_LABELS`).
 *  Accessible names (the `*AriaLabel` fields) stay descriptive for screen
 *  readers; the short visible labels carry the on-screen verb. Products rename
 *  per their own vocabulary without forking the component. */
interface TimelineEditorLabels {
    /** Visible verb on the split-at-playhead tool. */
    splitClip?: string;
    splitClipAriaLabel?: string;
    /** Visible verb on the add-caption-at-playhead tool. */
    addCaption?: string;
    addCaptionAriaLabel?: string;
    /** Visible verb on the export action (rendered only when `onCreateExport`). */
    createExport?: string;
    /** Empty-state heading + the three doors. */
    emptyTitle?: string;
    emptyBody?: string;
    emptyTemplateDoor?: string;
    emptyClipDoor?: string;
    emptyAgentDoor?: string;
    /** Phone gate shown instead of the gesture-heavy editor below `sm`. */
    smallScreenTitle?: string;
    smallScreenBody?: string;
    /** Ghost-lane names shown at rest so time reads as a timeline before any
     *  track exists. */
    ghostVideoLane?: string;
    ghostCaptionLane?: string;
}
/** Define properties and callbacks for editing and applying operations on a sequence timeline */
interface TimelineEditorProps {
    timeline: SequenceTimeline;
    canWrite: boolean;
    /** Persist operations the user produced. Reject to roll the edit back.
     *  Resolve with the per-operation `SequenceApplyResult[]` (what
     *  `applySequenceOperations` returns, index-aligned with `operations`) so
     *  the editor can reconcile its optimistic `local-…` clip ids to the
     *  server-minted ids — without it, undoing a committed place/split/caption
     *  after a timeline refresh emits operations the server cannot resolve.
     *  Resolving void skips reconciliation. */
    onApplyOperations(operations: SequenceOperation[]): Promise<SequenceApplyResult[] | void>;
    /** Selection + playhead surface to the host (e.g. to attach agent context). */
    onSelectionChange?(clips: SequenceClip[]): void;
    onPlayheadChange?(frame: number): void;
    /** Host-rendered side panel (agent chat) and shelf (draggable assets). */
    renderSidePanel?(ctx: {
        selectedClips: SequenceClip[];
        playheadFrame: number;
    }): React.ReactNode;
    renderAssetShelf?(): React.ReactNode;
    /** Frame provider override; omitted → the baseline HTMLVideoElement provider. */
    frameProvider?: VideoFrameProvider;
    /** Overridable product-facing copy; defaults are clear on their own. */
    labels?: TimelineEditorLabels;
    /** Empty-state doors. Supplying a handler renders that door; omitting it
     *  hides it. With none supplied the empty state shows guidance only. */
    onStartFromTemplate?(): void;
    onAddClip?(): void;
    onAskAgent?(): void;
    /** Render the export action (a toolbar "Export…" button). Omitted → no export
     *  affordance. The handler owns the dialog/flow; this surface only invokes it. */
    onCreateExport?(): void;
    /** Off by default. When true AND the consuming product supplies a branded
     *  export pipeline, the export door/button is marked as Tangle-branded. This
     *  surface has no render pipeline of its own to brand, so the flag is a
     *  forward-compatible hint to `onCreateExport`, never a baked watermark. */
    brandedExport?: boolean;
    className?: string;
}
/** Provide default labels and accessibility text for timeline editor UI elements */
declare const DEFAULT_TIMELINE_LABELS: Required<TimelineEditorLabels>;

/**
 * Undo/redo command stack over immutable `EditorTimelineState`.
 *
 * Invariant that makes `reset()` safe: history entries hold COMMANDS — each a
 * pair of state transforms plus the durable operations captured at
 * construction — never state snapshots. Rebasing the timeline from a server
 * refresh therefore cannot stale the history: a later undo re-applies the
 * command's inverse transform to whatever timeline is current. If a rebase
 * removed a clip a historical command targets, that command's transform throws
 * (fail loud) rather than silently editing the wrong clip — the host decides
 * whether to drop history at that point.
 */

/** Oldest entries are dropped past this bound; redo is cleared on execute. */
declare const COMMAND_HISTORY_LIMIT = 200;
/** Create a command stack managing undo and redo operations for a given timeline */
declare function createCommandStack(initial: SequenceTimeline): CommandStack;

/**
 * Concrete `TimelineCommand` factories. Every factory captures the inverse
 * from PRE-state at construction — undo is a value computed once, never a
 * re-derivation from whatever state exists later. Local execute/undo update
 * `EditorTimelineState` immutably; `operations()`/`inverseOperations()` return
 * the durable `SequenceOperation[]` equivalent, built per call from captured
 * primitives so callers can never alias command internals.
 *
 * Id boundary: `place_clip`, `add_caption`, and `split_clip` mint clip ids
 * server-side, so factories that create local clips take a caller-minted id.
 * Every factory also takes an optional `resolveClipId` — a LIVE local→server
 * alias lookup the host feeds from `onApplyOperations` results. Resolution
 * runs at execute/undo/emission time (never at construction), so a command
 * captured against an optimistic `local-…` id keeps working after a server
 * refresh replaced it with the minted id. Residual boundary: clips a durable
 * undo recreates (e.g. the `place_clip` inverse of a delete) mint FRESH
 * server ids that only the next refresh reconciles.
 *
 * Transforms re-resolve their target clip at execute/undo time and throw when
 * it no longer exists (e.g. removed by a `reset()` rebase) — editing a wrong
 * or absent clip silently would corrupt the durable op stream.
 */

/** Live local→server clip-id lookup (see module header). Must return its
 *  argument when no alias exists. */
type ClipIdResolver = (clipId: string) => string;
/** Define input parameters to move a clip within a timeline including optional track and resolver */
interface MoveClipInput {
    timeline: SequenceTimeline;
    clipId: string;
    startFrame: number;
    /** Omitted → stays on its current track. */
    trackId?: string;
    resolveClipId?: ClipIdResolver;
}
/** Drag-move. The target start clamps through the model's `clampClipStart`
 *  so drags past either edge land at the boundary instead of throwing
 *  mid-gesture; the emitted operation carries the clamped value. */
declare function moveClipCommand(input: MoveClipInput): TimelineCommand;
/** Define input parameters for trimming a clip within a sequence timeline */
interface TrimClipInput {
    timeline: SequenceTimeline;
    clipId: string;
    startFrame: number;
    durationFrames: number;
    /** New source in-point when trimming the head; omitted → unchanged. */
    sourceInFrame?: number;
    resolveClipId?: ClipIdResolver;
}
/** Trim is strict where move is forgiving: the caller (a trim handle) already
 *  knows both edges, so out-of-bounds input is a bug, not a gesture. */
declare function trimClipCommand(input: TrimClipInput): TimelineCommand;
/** Define input parameters required to place a clip within a sequence timeline */
interface PlaceClipInput {
    timeline: SequenceTimeline;
    /** Caller-minted optimistic id for the local clip (see module header). */
    clipId: string;
    trackId: string;
    label: string;
    startFrame: number;
    durationFrames: number;
    /** Omitted → 0 (start of the source). */
    sourceInFrame?: number;
    media?: {
        url: string;
        kind: 'video' | 'image' | 'audio';
    };
    generationId?: string;
    assetId?: string;
    metadata?: Record<string, unknown>;
    resolveClipId?: ClipIdResolver;
}
/** Resolve and validate clip placement parameters to create a timeline command */
declare function placeClipCommand(input: PlaceClipInput): TimelineCommand;
/** Define input parameters required to delete a clip from a sequence timeline */
interface DeleteClipInput {
    timeline: SequenceTimeline;
    clipId: string;
    resolveClipId?: ClipIdResolver;
}
/** Snapshots the full clip at construction so undo restores it exactly. The
 *  durable inverse rebuilds within the closed operation union: caption clips
 *  (caption track + text) invert through `add_caption` — `place_clip` cannot
 *  carry text/language — everything else through `place_clip` with media,
 *  product references, source window, and disabled state intact. Boundary:
 *  the caption path cannot carry `disabled` or `metadata`, so a deleted
 *  disabled caption resurrects ENABLED server-side and caption metadata is
 *  lost — local undo stays exact (same class of loss as the `set_clip_text`
 *  language-clear boundary). */
declare function deleteClipCommand(input: DeleteClipInput): TimelineCommand;
/** Define input parameters for splitting a clip at a specific frame within a timeline */
interface SplitClipInput {
    timeline: SequenceTimeline;
    clipId: string;
    /** Sequence-frame to cut at; must fall strictly inside the clip. */
    atFrame: number;
    /** Caller-minted id for the second (tail) clip. */
    newClipId: string;
    resolveClipId?: ClipIdResolver;
}
/** Source mapping is 1:1 frames (no rate ramps in the model), so the tail's
 *  source in-point is the head's in-point advanced by the head duration. */
declare function splitClipCommand(input: SplitClipInput): TimelineCommand;
/** Define input parameters for adding a caption clip to a sequence timeline */
interface AddCaptionInput {
    timeline: SequenceTimeline;
    /** Caller-minted optimistic id for the local caption clip. */
    clipId: string;
    /** Editor commands are concrete: the caller resolves placement (e.g. via
     *  `chooseCaptionPlacement`) and the target track before constructing. */
    trackId: string;
    text: string;
    language?: string;
    startFrame: number;
    durationFrames: number;
    resolveClipId?: ClipIdResolver;
}
/** Resolve a command to add a caption to a specified caption track within a timeline */
declare function addCaptionCommand(input: AddCaptionInput): TimelineCommand;
/** Define input parameters for setting text and optional language on a specific clip in a timeline */
interface SetClipTextInput {
    timeline: SequenceTimeline;
    clipId: string;
    text: string;
    /** Omitted → language unchanged. */
    language?: string;
    resolveClipId?: ClipIdResolver;
}
/** Requires the clip to already carry text: `set_clip_text` has no "create"
 *  semantics in the union, and an inverse for a text-less clip would have to
 *  invent an empty string. Boundary: when the clip had NO language and this
 *  command sets one, local undo restores `undefined` exactly but the durable
 *  inverse cannot clear language (the op has no clear form) — flagged to the
 *  apply layer. */
declare function setClipTextCommand(input: SetClipTextInput): TimelineCommand;
/** Define input parameters to toggle the disabled state of a clip within a timeline */
interface ToggleClipDisabledInput {
    timeline: SequenceTimeline;
    clipId: string;
    resolveClipId?: ClipIdResolver;
}
/** The target value is captured at construction (not flipped at execute time)
 *  so redo after a rebase applies the same durable op the stack already
 *  emitted. */
declare function toggleClipDisabledCommand(input: ToggleClipDisabledInput): TimelineCommand;

/**
 * Zoom + viewport coordinate math. Zoom is PIXELS PER FRAME; the slider is a
 * normalized [0, 1] control mapped exponentially (zoom = min·(max/min)^slider)
 * so each slider step multiplies the scale by a constant factor — linear
 * slider feel across a 10x+ range.
 */

/** Define configuration settings for minimum and maximum zoom levels */
interface ZoomMathConfig {
    minZoom: number;
    maxZoom: number;
}
/** Create a ZoomMath object that validates config and calculates zoom ratio within bounds */
declare function createZoomMath(config: ZoomMathConfig): ZoomMath;
/** Horizontal viewport: zoom in pixels per frame, scrollLeft in pixels. */
interface ViewportTransform {
    zoom: number;
    scrollLeft: number;
}
/** Frame → viewport-relative pixel x. Output is fractional; round through
 *  `snapPixel` before drawing. */
declare function frameToPixel(frame: number, view: ViewportTransform): number;
/** Viewport-relative pixel x → integer frame. Frames are integer positions,
 *  so the result rounds to the nearest frame and floors at 0 (a pointer left
 *  of frame 0 resolves to 0). */
declare function pixelToFrame(pixel: number, view: ViewportTransform): number;
/** Snap a CSS-pixel value to the device pixel grid so 1px timeline rules
 *  render crisp on fractional-DPR displays. */
declare function snapPixel(value: number, devicePixelRatio: number): number;

/**
 * Drag snapping. Snap points come from the timeline's structure (clip edges,
 * playhead, sequence end); the snap THRESHOLD is measured in screen pixels at
 * the current zoom — what feels "close" is a screen distance, not a frame
 * count — and converts to frames as thresholdPx / zoom.
 */

/** Snap point with its owning clip when it came from one, so a drag can
 *  exclude the dragged clip's own edges. Structurally a `SnapPoint`. */
interface TimelineSnapPoint extends SnapPoint {
    clipId?: string;
}
/** Disabled clips still occupy timeline space visually, so their edges remain
 *  snap targets. */
declare function collectSnapPoints(timeline: SequenceTimeline, playheadFrame: number): TimelineSnapPoint[];
/** Define options to configure snapping behavior including zoom, threshold, and exclusion criteria */
interface ApplySnapOptions {
    /** Pixels per frame — converts the pixel threshold into frames. */
    zoom: number;
    /** Screen-distance threshold; 10px matches the editor's hit-slop. */
    thresholdPx?: number;
    /** Return true to remove a point from consideration (e.g. the dragged
     *  clip's own edges via `TimelineSnapPoint.clipId`). */
    exclude?: (point: SnapPoint) => boolean;
}
/** Nearest candidate wins; ties keep the first candidate in `points` order
 *  (sorted by frame from `collectSnapPoints`, so the lower frame). */
declare function applySnap(frame: number, points: SnapPoint[], opts: ApplySnapOptions): SnapResult;

/**
 * rAF playback clock. The playhead is derived from a (start time, start
 * frame) anchor and `performance.now()` deltas — never from per-tick
 * increments — so dropped animation frames cannot accumulate drift.
 *
 * Browser-only at PLAY time, import-safe everywhere: `requestAnimationFrame`
 * and `performance` are resolved off `globalThis` when playback starts, never
 * at module load or construction, so server bundles that import the editor
 * engine do not crash.
 */

/** Define configuration settings for playback clock including frames per second and total duration frames */
interface PlaybackClockConfig {
    fps: number;
    durationFrames: number;
}
/** Create a playback clock that manages frame timing and playback state based on configuration */
declare function createPlaybackClock(config: PlaybackClockConfig): PlaybackClock;

/**
 * Baseline frame pipeline behind the `VideoFrameProvider` seam: an off-DOM
 * HTMLVideoElement pool for video URLs and an HTMLImageElement pool for
 * stills, merged by a per-URL kind dispatcher so the editor canvas never
 * cares which kind a clip's media is. A WebCodecs implementation can replace
 * this wholesale behind the same seam.
 *
 * Server-safe at import time, browser-only at call time: nothing touches
 * `document` or `fetch` until a provider method runs.
 */

/** Define the default maximum number of media elements allowed in a collection */
declare const DEFAULT_MAX_MEDIA_ELEMENTS = 4;
/** Half a frame at 30fps. Seeks closer than this repaint the decoder's
 *  current frame instead of forcing a redundant seek. */
declare const SEEK_TOLERANCE_SECONDS: number;
/** A seek that hasn't fired `seeked` after this long is a decode failure —
 *  the draw REJECTS rather than painting whatever frame happens to be up. */
declare const SEEK_TIMEOUT_MS = 5000;
/** Define a rectangular frame with position and size properties x, y, width, and height */
interface FrameRect {
    x: number;
    y: number;
    width: number;
    height: number;
}
/** Object-fit 'contain' placement: preserve aspect ratio, fit entirely inside
 *  `dest`, center the residual space. Callers own clearing the letterbox
 *  margins — this paints only the fitted region. */
declare function containFitRect(source: {
    width: number;
    height: number;
}, dest: FrameRect): FrameRect;
/** Determine if seeking is required based on the difference between current and target times */
declare function needsSeek(currentTimeSeconds: number, targetSeconds: number): boolean;
/** Manage a leased element from a pool and release it to enable LRU eviction */
interface PooledElementLease<T> {
    element: T;
    /** Unpins the element; idle elements become LRU-evictable. Idempotent. */
    release(): void;
}
/** Manage a pool of media elements to acquire, check, count, and dispose resources efficiently */
interface MediaElementPool<T> {
    acquire(url: string): PooledElementLease<T>;
    has(url: string): boolean;
    size(): number;
    dispose(): void;
}
/** LRU pool of media elements keyed by URL. Entries pinned by an outstanding
 *  lease are never evicted — a draw in flight must keep its element — so the
 *  pool can temporarily exceed `maxElements` under concurrent draws and
 *  shrinks back as leases release. */
declare function createMediaElementPool<T>(opts: {
    maxElements: number;
    create(url: string): T;
    destroy(element: T, url: string): void;
}): MediaElementPool<T>;
/** Extension-based kind classification; 'unknown' defers to a HEAD
 *  content-type probe at draw time. */
declare function classifyMediaUrl(url: string): 'video' | 'image' | 'unknown';
/** Stills behind the same `VideoFrameProvider` seam — `sourceSeconds` is
 *  validated for contract parity but does not affect the painted pixels. */
declare function createImageFrameProvider(opts?: {
    maxElements?: number;
}): VideoFrameProvider;
/** The baseline provider `TimelineEditorProps.frameProvider` defaults to.
 *  Video and image pools each hold up to `maxElements` entries. */
declare function createVideoElementFrameProvider(opts?: {
    maxElements?: number;
}): VideoFrameProvider;

/**
 * Waveform rendering for audio clips: bucketed max-abs peaks computed once
 * per (media, zoom bucket count) and painted as mirrored bars around the
 * track lane's midline. `computeWaveform` is pure so peak math is testable
 * without Web Audio; `loadWaveform` is the browser edge that decodes real
 * media into it.
 */

/** Structural slice of Web Audio's AudioBuffer so peak math runs on synthetic
 *  fixtures in tests and on real decoded buffers in the browser. */
interface AudioBufferLike {
    numberOfChannels: number;
    length: number;
    sampleRate: number;
    duration: number;
    getChannelData(channel: number): Float32Array;
}
/** One max-abs peak per bucket, taken across all channels. `peaks[b]` is the
 *  loudest absolute sample in bucket `b`; rendering mirrors it around the
 *  midline, which is what the contract's "peak pair" denotes. Buckets past
 *  the end of short audio hold 0. */
declare function computeWaveform(buffer: AudioBufferLike, bucketCount: number): WaveformData;
/** Fetch + decode `mediaUrl` and bucket it. Pass `ctx` to reuse a shared
 *  AudioContext; otherwise one is created and closed around the decode. */
declare function loadWaveform(mediaUrl: string, bucketCount: number, ctx?: AudioContext): Promise<WaveformData>;
/** Paint mirrored peak bars centered on the rect's midline. Silent buckets
 *  still paint a 1px hairline so the lane reads as audio, not as empty;
 *  peaks beyond ±1.0 (hot masters) clip to the full lane height. */
declare function drawWaveform(ctx: CanvasRenderingContext2D, data: WaveformData, rect: {
    x: number;
    y: number;
    width: number;
    height: number;
}, color: string): void;

/**
 * Whisper transcription behind the `TranscriptionProvider` seam, powered by
 * the OPTIONAL peer `@huggingface/transformers`. The peer is loaded with a
 * dynamic variable-specifier import inside `transcribe()` so bundlers leave
 * it external and apps that never transcribe never pay for it. `available`
 * is the sync UI affordance signal; `transcribe()` re-verifies with the real
 * import and is the source of truth.
 */

/** Provide the default Whisper model identifier for ONNX community large v3 turbo */
declare const DEFAULT_WHISPER_MODEL = "onnx-community/whisper-large-v3-turbo";
interface WhisperChunk {
    text: string;
    timestamp: [number, number | null];
}
/** Define the structure for transcribed text output with optional segmented chunks */
interface WhisperOutput {
    text: string;
    chunks?: WhisperChunk[];
}
/** Mean-mixdown to mono. Single-channel buffers return the live channel data
 *  without copying — callers must treat the result as read-only. */
declare function mixdownToMono(buffer: AudioBufferLike): Float32Array;
/** Map whisper chunk output to contract segments. A `null` end timestamp is
 *  whisper's "ran past the end of the audio" sentinel on the final chunk and
 *  resolves to the audio duration. */
declare function mapWhisperOutput(output: WhisperOutput | WhisperOutput[], durationSeconds: number, mediaUrl: string): TranscriptionSegment[];
/** Create a Whisper-based transcription provider with optional model configuration */
declare function createWhisperTranscriptionProvider(opts?: {
    model?: string;
}): TranscriptionProvider;

declare const SEQUENCE_MEDIA_DRAG_TYPE = "application/x-sequence-media";
declare function TimelineEditor(props: TimelineEditorProps): react.JSX.Element;

interface PreviewCanvasProps {
    timeline: SequenceTimeline;
    clock: PlaybackClock;
    frameProvider: VideoFrameProvider;
    className?: string;
}
declare function PreviewCanvas({ timeline, clock, frameProvider, className }: PreviewCanvasProps): react.JSX.Element;

/**
 * Adaptive timecode ruler. Tick density follows zoom through
 * `selectTickStepSeconds` (major ticks never closer than ~80px, minor ticks
 * at a fifth of the major step when they'd sit at least 8px apart). Click or
 * drag scrubs: the pointer is captured, every move quantizes to a whole frame,
 * and the frame is committed through `onScrub` (the editor routes it to
 * `PlaybackClock.seek`).
 */
interface TimelineRulerProps {
    fps: number;
    durationFrames: number;
    /** Pixels per frame. */
    zoom: number;
    onScrub(frame: number): void;
}
declare function TimelineRuler({ fps, durationFrames, zoom, onScrub }: TimelineRulerProps): react.JSX.Element;

interface ClipMoveCommit {
    clipId: string;
    startFrame: number;
    trackId: string;
}
interface ClipTrimCommit {
    clipId: string;
    startFrame: number;
    durationFrames: number;
    sourceInFrame: number;
}
interface TimelineClipChipProps {
    clip: SequenceClip;
    track: SequenceTrack;
    fps: number;
    /** Pixels per frame. */
    zoom: number;
    sequenceDurationFrames: number;
    selected: boolean;
    canWrite: boolean;
    /** Roving-tabindex: exactly one chip per editor carries tabIndex 0 so the
     *  clip set is a single Tab stop; arrows move focus across the others. */
    tabbable: boolean;
    frameProvider: VideoFrameProvider;
    /** Snap a candidate move (both clip edges considered); editor closes over
     *  the engine's snap points. */
    snapMove(candidate: {
        startFrame: number;
        durationFrames: number;
        clipId: string;
    }): {
        startFrame: number;
        point: SnapPoint | null;
    };
    /** Snap a single trim edge. */
    snapEdge(candidate: {
        frame: number;
        clipId: string;
    }): {
        frame: number;
        point: SnapPoint | null;
    };
    onSnapPointChange(point: SnapPoint | null): void;
    onSelect(clipId: string, additive: boolean): void;
    /** Keyboard delete of the focused clip (one command on the stack), routed
     *  through the same locked-track guard as the transport Delete key. */
    onRequestDelete(clipId: string): void;
    /** Move keyboard focus to the previous/next clip in DOM order (roving
     *  tabindex); the editor owns the ordered chip set. */
    onFocusStep(clipId: string, direction: -1 | 1): void;
    onCommitMove(input: ClipMoveCommit): void;
    onCommitTrim(input: ClipTrimCommit): void;
    onCommitText(input: {
        clipId: string;
        text: string;
    }): void;
}
declare function TimelineClipChip(props: TimelineClipChipProps): react.JSX.Element;

interface TimelineTrackRowProps {
    track: SequenceTrack;
    clips: SequenceClip[];
    fps: number;
    zoom: number;
    sequenceDurationFrames: number;
    selectedClipIds: ReadonlySet<string>;
    /** The single clip that carries tabIndex 0 (roving tabindex); null seeds the
     *  first chip in the editor as the lone Tab stop. */
    tabbableClipId: string | null;
    canWrite: boolean;
    frameProvider: VideoFrameProvider;
    snapMove(candidate: {
        startFrame: number;
        durationFrames: number;
        clipId: string;
    }): {
        startFrame: number;
        point: SnapPoint | null;
    };
    snapEdge(candidate: {
        frame: number;
        clipId: string;
    }): {
        frame: number;
        point: SnapPoint | null;
    };
    onSnapPointChange(point: SnapPoint | null): void;
    onSelectClip(clipId: string, additive: boolean): void;
    onRequestDeleteClip(clipId: string): void;
    onFocusStepClip(clipId: string, direction: -1 | 1): void;
    onCommitMove(input: ClipMoveCommit): void;
    onCommitTrim(input: ClipTrimCommit): void;
    onCommitText(input: {
        clipId: string;
        text: string;
    }): void;
    onLaneSeek(frame: number): void;
}
declare function TimelineTrackRow(props: TimelineTrackRowProps): react.JSX.Element;

/**
 * Playhead overlay for the track area: a full-height line with a triangular
 * cap. Positioned in timeline pixels (frame * zoom) inside the scrolled
 * content, so it moves with horizontal scroll for free. Pointer-transparent —
 * scrubbing belongs to the ruler.
 */
interface TimelinePlayheadProps {
    frame: number;
    zoom: number;
}
declare function TimelinePlayhead({ frame, zoom }: TimelinePlayheadProps): react.JSX.Element;

/**
 * Lazy boundary around the Tangle knot from `../../brand`.
 *
 * `/brand` re-exports the mark from `@tangle-network/sandbox-ui`, an OPT-IN peer
 * (see brand/index.tsx). Static-importing it would pull the peer into every
 * module that renders the timeline — breaking substrate-free environments (and
 * products that haven't installed the peer) at module-eval time. Code-splitting
 * it (the repo's `lazy.tsx` convention) keeps the peer out of the editor's
 * static graph: the knot streams in only when an empty/phone state actually
 * renders, and its absence degrades to empty space rather than a crash.
 */
interface BrandMarkProps {
    size: number;
    className?: string;
}
declare function BrandMark({ size, className }: BrandMarkProps): react.JSX.Element;

interface TimelineEmptyStateProps {
    labels?: TimelineEditorLabels;
    brandedExport?: boolean;
    onStartFromTemplate?(): void;
    onAddClip?(): void;
    onAskAgent?(): void;
}
declare function TimelineEmptyState(props: TimelineEmptyStateProps): react.JSX.Element;

/**
 * Labeled placeholder lanes drawn behind the empty state so the surface reads
 * as a timeline before any track exists: a "Video" lane and a "Captions" lane
 * with the same sticky header column the real `TimelineTrackRow` uses, sized to
 * the timeline width so the ruler's timecodes line up over real lanes. Purely
 * decorative — no clips, no gestures.
 */
interface TimelineGhostLanesProps {
    /** Timeline pixel width (durationFrames * zoom), so ghost lanes match the ruler. */
    laneWidth: number;
    videoLabel: string;
    captionLabel: string;
}
declare function TimelineGhostLanes({ laneWidth, videoLabel, captionLabel }: TimelineGhostLanesProps): react.JSX.Element;

interface TimelineSmallScreenGateProps {
    labels?: TimelineEditorLabels;
}
declare function TimelineSmallScreenGate({ labels }: TimelineSmallScreenGateProps): react.JSX.Element;

interface SnapIndicatorLineProps {
    point: SnapPoint | null;
    zoom: number;
}
declare function SnapIndicatorLine({ point, zoom }: SnapIndicatorLineProps): react.JSX.Element | null;

interface ZoomControlProps {
    zoomMath: ZoomMath;
    zoom: number;
    onZoomChange(zoom: number): void;
    /** Fit-to-sequence pixels-per-frame; clicking the readout snaps back to it. */
    fitZoom?: number;
}
declare function ZoomControl({ zoomMath, zoom, onZoomChange, fitZoom }: ZoomControlProps): react.JSX.Element;

/**
 * Fuses several `TimelineCommand`s into one undo step so a single gesture
 * (multi-select delete, drop that creates a track and places a clip) never
 * fragments the history. Execute runs in order; undo runs in strict reverse so
 * intermediate states reconstruct exactly.
 */

declare function compositeCommand(label: string, commands: TimelineCommand[]): TimelineCommand;

/**
 * Pure pointer-gesture math for the timeline editor. Every drag/trim/scrub
 * gesture quantizes through these functions so the interactive behavior is
 * unit-testable without a DOM. All inputs/outputs are integer frames except
 * pixel deltas and zoom (px per frame), which are the only float-valued edge.
 */

/** Quantize a horizontal pointer delta to whole frames at the current zoom. */
declare function framesFromPixelDelta(deltaX: number, zoom: number): number;
interface MoveDragInput {
    originStartFrame: number;
    durationFrames: number;
    deltaFrames: number;
    sequenceDurationFrames: number;
}
/** New start frame for a move drag, clamped so the clip stays fully inside
 *  the sequence. */
declare function moveDragStartFrame(input: MoveDragInput): number;
interface TrimStartDragInput {
    originStartFrame: number;
    originDurationFrames: number;
    originSourceInFrame: number;
    deltaFrames: number;
}
interface TrimStartDragResult {
    startFrame: number;
    durationFrames: number;
    sourceInFrame: number;
}
/**
 * Head trim: the clip END is invariant; start slides between two hard walls —
 * it cannot reveal media before source frame 0 (sourceInFrame >= 0) and cannot
 * pass within MIN_SEQUENCE_CLIP_FRAMES of the end. sourceInFrame shifts by
 * exactly the start delta so the visible content stays anchored.
 */
declare function trimStartDrag(input: TrimStartDragInput): TrimStartDragResult;
interface TrimEndDragInput {
    originStartFrame: number;
    originDurationFrames: number;
    sourceInFrame: number;
    deltaFrames: number;
    sequenceDurationFrames: number;
    /** Natural source length in frames when known; bounds how far the tail can
     *  extend. Omit for stills and media of unknown length. */
    sourceDurationFrames?: number;
}
/** Tail trim: start is invariant; duration is bounded below by the minimum
 *  clip length and above by both the sequence end and the remaining source
 *  material past the in-point. */
declare function trimEndDrag(input: TrimEndDragInput): {
    durationFrames: number;
};
/**
 * Smallest ruler step whose major ticks sit at least `minSpacingPx` apart at
 * the current zoom; past the table it grows in whole minutes so labels never
 * collide at extreme zoom-out.
 */
declare function selectTickStepSeconds(input: {
    zoom: number;
    fps: number;
    minSpacingPx?: number;
}): number;
interface LetterboxRect {
    x: number;
    y: number;
    width: number;
    height: number;
}
/** Contain-fit a media aspect inside a container, centered with letterbox or
 *  pillarbox bars. */
declare function letterboxRect(input: {
    containerWidth: number;
    containerHeight: number;
    mediaWidth: number;
    mediaHeight: number;
}): LetterboxRect;
/** Caption type scales with the rendered frame, floored so captions stay
 *  legible on small previews. */
declare function captionFontPx(canvasCssHeight: number): number;
/** Pixel geometry for a clip chip; width floors at 2px so 1-frame clips stay
 *  grabbable. */
declare function clipChipGeometry(input: {
    startFrame: number;
    durationFrames: number;
    zoom: number;
}): {
    left: number;
    width: number;
};
/**
 * A move drag snaps whichever clip edge lands closest to a snap point: the
 * start edge directly, or the end edge re-expressed as a start. An unsnapped
 * candidate passes through unchanged.
 */
declare function chooseMoveSnap(input: {
    candidateStartFrame: number;
    durationFrames: number;
    startSnap: SnapResult;
    endSnap: SnapResult;
}): {
    startFrame: number;
    point: SnapPoint | null;
};

declare const SequenceTimelineEditorLazy: react__default.LazyExoticComponent<typeof TimelineEditor>;

export { type AddCaptionInput, type ApplySnapOptions, type AudioBufferLike, BrandMark, type BrandMarkProps, COMMAND_HISTORY_LIMIT, type ClipIdResolver, type ClipMoveCommit, type ClipTrimCommit, type CommandStack, DEFAULT_MAX_MEDIA_ELEMENTS, DEFAULT_TIMELINE_LABELS, DEFAULT_WHISPER_MODEL, type DeleteClipInput, type EditorTimelineState, type FrameRect, type LetterboxRect, type MediaElementPool, type MoveClipInput, type MoveDragInput, type PlaceClipInput, type PlaybackClock, type PlaybackClockConfig, type PooledElementLease, PreviewCanvas, type PreviewCanvasProps, SEEK_TIMEOUT_MS, SEEK_TOLERANCE_SECONDS, SEQUENCE_MEDIA_DRAG_TYPE, SequenceTimelineEditorLazy, type SetClipTextInput, SnapIndicatorLine, type SnapIndicatorLineProps, type SnapPoint, type SnapResult, type SplitClipInput, TimelineClipChip, type TimelineClipChipProps, type TimelineCommand, TimelineEditor, type TimelineEditorLabels, type TimelineEditorProps, TimelineEmptyState, type TimelineEmptyStateProps, TimelineGhostLanes, type TimelineGhostLanesProps, TimelinePlayhead, type TimelinePlayheadProps, TimelineRuler, type TimelineRulerProps, TimelineSmallScreenGate, type TimelineSmallScreenGateProps, type TimelineSnapPoint, TimelineTrackRow, type TimelineTrackRowProps, type ToggleClipDisabledInput, type TranscriptionProvider, type TranscriptionSegment, type TrimClipInput, type TrimEndDragInput, type TrimStartDragInput, type TrimStartDragResult, type VideoFrameProvider, type ViewportTransform, type WaveformData, type WhisperOutput, ZoomControl, type ZoomControlProps, type ZoomMath, type ZoomMathConfig, addCaptionCommand, applySnap, captionFontPx, chooseMoveSnap, classifyMediaUrl, clipChipGeometry, collectSnapPoints, compositeCommand, computeWaveform, containFitRect, createCommandStack, createImageFrameProvider, createMediaElementPool, createPlaybackClock, createVideoElementFrameProvider, createWhisperTranscriptionProvider, createZoomMath, deleteClipCommand, drawWaveform, frameToPixel, framesFromPixelDelta, letterboxRect, loadWaveform, mapWhisperOutput, mixdownToMono, moveClipCommand, moveDragStartFrame, needsSeek, pixelToFrame, placeClipCommand, selectTickStepSeconds, setClipTextCommand, snapPixel, splitClipCommand, toggleClipDisabledCommand, trimClipCommand, trimEndDrag, trimStartDrag };
