/**
 * Headless Project Engine for CLI operations
 * No React/Zustand dependency - pure TypeScript
 */
import type { TimelineState, ProjectMeta, Track, Clip, MediaSource, Effect, Transition, Id, TimeSeconds, AspectRatio, MediaType } from "@vibeframe/core/timeline";
/** Generate unique ID */
export declare const generateId: () => Id;
/**
 * Persisted timeline shape — what gets written to disk.
 *
 * UI runtime fields (currentTime, isPlaying, zoom, scrollX, selectedClipIds,
 * selectedTrackId) are stripped on save and re-injected with defaults on
 * load. They have no business in a CLI-generated project file: every CLI
 * command starts a fresh process, so playhead / zoom / selection have no
 * continuity to preserve.
 */
export type PersistedTimelineState = Omit<TimelineState, "currentTime" | "isPlaying" | "zoom" | "scrollX" | "selectedClipIds" | "selectedTrackId">;
/** Project file format */
export interface ProjectFile {
    version: string;
    state: PersistedTimelineState;
}
/**
 * Headless Project Engine
 * Manages timeline state without React/Zustand
 */
export declare class Project {
    private state;
    private filePath;
    constructor(name?: string);
    /** Get current state (immutable copy) */
    getState(): TimelineState;
    /** Get project metadata */
    getMeta(): ProjectMeta;
    /** Get file path */
    getFilePath(): string | null;
    setName(name: string): void;
    setAspectRatio(ratio: AspectRatio): void;
    setFrameRate(fps: number): void;
    addSource(source: Omit<MediaSource, "id">): MediaSource;
    removeSource(id: Id): boolean;
    getSource(id: Id): MediaSource | undefined;
    getSources(): MediaSource[];
    addTrack(track: Omit<Track, "id">): Track;
    removeTrack(id: Id): boolean;
    updateTrack(id: Id, updates: Partial<Omit<Track, "id">>): boolean;
    getTrack(id: Id): Track | undefined;
    getTracks(): Track[];
    getTracksByType(type: MediaType): Track[];
    addClip(clip: Omit<Clip, "id" | "effects">): Clip;
    removeClip(id: Id): boolean;
    updateClip(id: Id, updates: Partial<Omit<Clip, "id">>): boolean;
    moveClip(id: Id, trackId: Id, startTime: TimeSeconds): boolean;
    trimClipStart(id: Id, newStartTime: TimeSeconds): boolean;
    trimClipEnd(id: Id, newDuration: TimeSeconds): boolean;
    getClip(id: Id): Clip | undefined;
    getClips(): Clip[];
    getClipsByTrack(trackId: Id): Clip[];
    /**
     * Split a clip at a specific time, creating two clips
     * @param id Clip ID to split
     * @param splitTime Time relative to clip start (not timeline time)
     * @returns [firstClip, secondClip] or null if failed
     */
    splitClip(id: Id, splitTime: TimeSeconds): [Clip, Clip] | null;
    /**
     * Duplicate a clip
     * @param id Clip ID to duplicate
     * @param offsetTime Optional time offset for the duplicate (default: place after original)
     * @returns The duplicated clip or null if failed
     */
    duplicateClip(id: Id, offsetTime?: TimeSeconds): Clip | null;
    addEffect(clipId: Id, effect: Omit<Effect, "id">): Effect | null;
    removeEffect(clipId: Id, effectId: Id): boolean;
    updateEffect(clipId: Id, effectId: Id, updates: Partial<Omit<Effect, "id">>): boolean;
    addTransition(transition: Omit<Transition, "id">): Transition;
    removeTransition(id: Id): boolean;
    getTransitions(): Transition[];
    private calculateDuration;
    getDuration(): TimeSeconds;
    toJSON(): ProjectFile;
    static fromJSON(data: ProjectFile): Project;
    setFilePath(path: string): void;
    getSummary(): {
        name: string;
        duration: TimeSeconds;
        aspectRatio: AspectRatio;
        frameRate: number;
        trackCount: number;
        clipCount: number;
        sourceCount: number;
    };
}
