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 <hyperframes-player> 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<PlayerProps & react.RefAttributes<HTMLIFrameElement>>;

interface PlayerControlsProps {
    onTogglePlay: () => void;
    onSeek: (time: number) => void;
    disabled?: boolean;
    isFullscreen?: boolean;
    onToggleFullscreen?: () => void;
}
declare const PlayerControls: react.NamedExoticComponent<PlayerControlsProps>;

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<string, number | string>;
        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<string>;
    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<string>;
    toggleSelectedElementId: (id: string) => void;
    clearSelectedElementIds: () => void;
    /** Keyframe data per element id, populated from parsed GSAP animations. */
    keyframeCache: Map<string, KeyframeCacheEntry>;
    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<Pick<TimelineElement, "start" | "duration" | "track" | "playbackStart">>) => 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<string, {
        count: number;
        messages: string[];
    }>;
    setLintFindingsByElement: (map: Map<string, {
        count: number;
        messages: string[];
    }>) => 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<string, string>;
    setClipParentMap: (map: Map<string, string>) => 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<zustand.StoreApi<PlayerState>>;

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> | void;
    onAssetDrop?: (assetPath: string, placement: {
        start: number;
        track: number;
    }) => Promise<void> | void;
    onBlockDrop?: (blockName: string, placement: {
        start: number;
        track: number;
    }) => Promise<void> | void;
}
interface TimelineEditCallbacks {
    onMoveElement?: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
    onResizeElement?: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "duration" | "playbackStart">) => Promise<void> | void;
    onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
    onSplitElement?: (element: TimelineElement, splitTime: number) => Promise<void> | void;
    onRazorSplit?: (element: TimelineElement, splitTime: number) => Promise<void> | void;
    onRazorSplitAll?: (splitTime: number) => Promise<void> | 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<TimelineEditCallbacks, "onMoveElement" | "onResizeElement" | "onBlockedEditAttempt" | "onSplitElement">;

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> | void;
    onSelectElement?: (element: TimelineElement | null) => void;
    theme?: Partial<TimelineTheme>;
}
declare const Timeline: react.NamedExoticComponent<TimelineProps>;

interface VideoThumbnailProps {
    videoSrc: string;
    label: string;
    labelColor: string;
    duration?: number;
}
/**
 * Renders a film-strip of video frames extracted client-side via a hidden
 * <video> + <canvas>. Each frame is a fixed-width tile; frames repeat to
 * fill the clip width — matching ClipThumbnail's visual pattern.
 */
declare const VideoThumbnail: react.NamedExoticComponent<VideoThumbnailProps>;

interface CompositionThumbnailProps {
    previewUrl: string;
    label: string;
    labelColor: string;
    selector?: string;
    selectorIndex?: number;
    seekTime?: number;
    duration?: number;
    width?: number;
    height?: number;
}
declare const CompositionThumbnail: react.NamedExoticComponent<CompositionThumbnailProps>;

/**
 * Runtime iframe integration utilities.
 *
 * Handles the boundary between the studio host page and the preview iframe:
 * - Viewport normalisation on load
 * - Auto-healing missing data-composition-id attributes
 * - Unmuting media via postMessage
 * - Resolving the underlying <iframe> from any wrapper element
 * - Scanning the DOM for composition hosts the manifest missed
 *   (element-reference starts that the CDN runtime fails to resolve)
 */

/**
 * Resolve the underlying iframe from any host element. Supports:
 * - Direct `<iframe>` element (most common — studio's own `Player.tsx`)
 * - Custom elements (e.g. `<hyperframes-player>`) whose shadow DOM contains an iframe
 * - Wrapper elements whose light DOM contains a descendant iframe
 *
 * Exported so web-component consumers can pre-resolve the iframe before
 * assigning it to `iframeRef` returned by `useTimelinePlayer`. Returns `null`
 * when the element has no associated iframe yet.
 *
 * @example
 * ```tsx
 * const { iframeRef } = useTimelinePlayer();
 * const playerElRef = useRef<HyperframesPlayer>(null);
 *
 * useEffect(() => {
 *   iframeRef.current = resolveIframe(playerElRef.current);
 * }, [iframeRef]);
 * ```
 */
declare function resolveIframe(el: Element | null): HTMLIFrameElement | null;

declare function useTimelinePlayer(): {
    iframeRef: react.RefObject<HTMLIFrameElement | null>;
    play: () => void;
    pause: () => void;
    togglePlay: () => void;
    seek: (time: number, options?: {
        keepPlaying?: boolean;
    }) => boolean;
    onIframeLoad: () => void;
    refreshPlayer: () => void;
    saveSeekPosition: () => void;
    resetPlayer: () => void;
};

declare function formatTime(time: number): string;

interface NLELayoutProps {
    projectId: string;
    portrait?: boolean;
    /** Slot for overlays rendered on top of the preview (cursors, highlights, etc.) */
    previewOverlay?: ReactNode;
    /** Slot rendered above the timeline tracks (toolbar with split, delete, zoom) */
    timelineToolbar?: ReactNode;
    /** Slot rendered below the timeline tracks */
    timelineFooter?: ReactNode;
    /** Increment to force the preview to reload (e.g., after file writes) */
    refreshKey?: number;
    /** Navigate to a specific composition path (e.g., "compositions/intro.html") */
    activeCompositionPath?: string | null;
    /** Callback to expose the iframe ref (for element picker, etc.) */
    onIframeRef?: (iframe: HTMLIFrameElement | null) => void;
    /** Callback when the viewed composition changes (drill-down/back) */
    onCompositionChange?: (compositionPath: string | null) => void;
    /** Custom clip content renderer for timeline (thumbnails, waveforms, etc.) */
    renderClipContent?: (element: TimelineElement, style: {
        clip: string;
        label: string;
    }) => ReactNode;
    onFileDrop?: (files: File[], placement?: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
    onDeleteElement?: (element: TimelineElement) => Promise<void> | void;
    onAssetDrop?: (assetPath: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
    onBlockDrop?: (blockName: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
    onPreviewBlockDrop?: (blockName: string, position: {
        left: number;
        top: number;
    }) => Promise<void> | void;
    onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
    onSelectTimelineElement?: (element: TimelineElement | null) => void;
    /** Exposes the compIdToSrc map for parent components (e.g., useRenderClipContent) */
    onCompIdToSrcChange?: (map: Map<string, string>) => void;
    /** Whether the timeline panel is visible (default: true) */
    timelineVisible?: boolean;
    /** Callback to toggle timeline visibility */
    onToggleTimeline?: () => void;
    /** Notifies parent when composition loading state changes */
    onCompositionLoadingChange?: (loading: boolean) => void;
}
declare const NLELayout: react.NamedExoticComponent<NLELayoutProps>;

interface NLEPreviewProps {
    projectId: string;
    iframeRef: RefObject<HTMLIFrameElement | null>;
    onIframeLoad: () => void;
    onCompositionLoadingChange?: (loading: boolean) => void;
    portrait?: boolean;
    directUrl?: string;
    suppressLoadingOverlay?: boolean;
    onStageRef?: (ref: React.RefObject<HTMLDivElement | null>) => void;
}
declare const NLEPreview: react.NamedExoticComponent<NLEPreviewProps>;

interface CompositionLevel {
    /** Unique id — "master" or composition file path */
    id: string;
    /** Display label — "Master" or filename without extension */
    label: string;
    /** Preview URL for this composition level */
    previewUrl: string;
}
interface CompositionBreadcrumbProps {
    stack: CompositionLevel[];
    onNavigate: (index: number) => void;
}
declare function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcrumbProps): react_jsx_runtime.JSX.Element | null;

interface SourceEditorProps {
    content: string;
    filePath?: string;
    language?: string;
    onChange?: (content: string) => void;
    readOnly?: boolean;
    revealOffset?: number | null;
}
declare const SourceEditor: react.NamedExoticComponent<SourceEditorProps>;

/**
 * Source Patcher — Maps visual property edits back to source HTML files.
 * Handles inline style updates, attribute changes, and text content.
 */
interface PatchOperation {
    type: "inline-style" | "attribute" | "text-content" | "html-attribute";
    property: string;
    value: string | null;
}
interface PatchTarget {
    id?: string | null;
    hfId?: string;
    selector?: string;
    selectorIndex?: number;
}
/**
 * Find which source file contains an element by its ID.
 */
declare function resolveSourceFile(elementId: string | null, selector: string, files: Record<string, string>): string | null;
/**
 * Apply a patch operation to an HTML source file.
 */
declare function applyPatch(html: string, elementId: string, op: PatchOperation): string;

interface DomEditCapabilities {
    canSelect: boolean;
    canEditStyles: boolean;
    /** Directly editable authored left/top style fields. Canvas drag uses manual edits instead. */
    canMove: boolean;
    /** Directly editable authored width/height style fields. Canvas resize uses manual edits instead. */
    canResize: boolean;
    canApplyManualOffset: boolean;
    canApplyManualSize: boolean;
    canApplyManualRotation: boolean;
    reasonIfDisabled?: string;
}
interface DomEditTextField {
    key: string;
    label: string;
    value: string;
    tagName: string;
    attributes: Array<{
        name: string;
        value: string;
    }>;
    inlineStyles: Record<string, string>;
    computedStyles: Record<string, string>;
    source: "self" | "child" | "text-node";
}
interface DomEditSelection extends PatchTarget {
    element: HTMLElement;
    label: string;
    tagName: string;
    sourceFile: string;
    compositionPath: string;
    compositionSrc?: string;
    isCompositionHost: boolean;
    isInsideLockedComposition: boolean;
    boundingBox: {
        x: number;
        y: number;
        width: number;
        height: number;
    };
    textContent: string | null;
    dataAttributes: Record<string, string>;
    inlineStyles: Record<string, string>;
    computedStyles: Record<string, string>;
    textFields: DomEditTextField[];
    capabilities: DomEditCapabilities;
    gsapAnimations?: GsapAnimation[];
}

interface ImportedFontAsset {
    family: string;
    path: string;
    url: string;
}

interface PropertyPanelProps {
    projectId: string;
    projectDir: string | null;
    assets: string[];
    element: DomEditSelection | null;
    multiSelectCount?: number;
    copiedAgentPrompt: boolean;
    onClearSelection: () => void;
    onSetStyle: (prop: string, value: string) => void | Promise<void>;
    onSetAttribute: (attr: string, value: string) => void | Promise<void>;
    onSetAttributeLive: (attr: string, value: string | null) => void | Promise<void>;
    onSetHtmlAttribute: (attr: string, value: string | null) => void | Promise<void>;
    onSetManualOffset: (element: DomEditSelection, next: {
        x: number;
        y: number;
    }) => void;
    onSetManualSize: (element: DomEditSelection, next: {
        width: number;
        height: number;
    }) => void;
    onSetManualRotation: (element: DomEditSelection, next: {
        angle: number;
    }) => void;
    onSetText: (value: string, fieldKey?: string) => void;
    onSetTextFieldStyle: (fieldKey: string, property: string, value: string) => void;
    onAddTextField: (afterFieldKey?: string) => string | Promise<string | null> | null;
    onRemoveTextField: (fieldKey: string) => void;
    onAskAgent: () => void;
    onImportAssets?: (files: FileList, dir?: string) => Promise<string[]>;
    fontAssets?: ImportedFontAsset[];
    onImportFonts?: (files: FileList | File[]) => Promise<ImportedFontAsset[]>;
    previewIframeRef?: React.RefObject<HTMLIFrameElement | null>;
    gsapAnimations?: _hyperframes_core_gsap_parser.GsapAnimation[];
    gsapMultipleTimelines?: boolean;
    gsapUnsupportedTimelinePattern?: boolean;
    onUpdateGsapProperty?: (animId: string, prop: string, value: number | string) => void;
    onUpdateGsapMeta?: (animId: string, updates: {
        duration?: number;
        ease?: string;
        position?: number;
    }) => void;
    onDeleteGsapAnimation?: (animId: string) => void;
    onAddGsapProperty?: (animId: string, prop: string) => void;
    onRemoveGsapProperty?: (animId: string, prop: string) => void;
    onUpdateGsapFromProperty?: (animId: string, prop: string, value: number | string) => void;
    onAddGsapFromProperty?: (animId: string, prop: string) => void;
    onRemoveGsapFromProperty?: (animId: string, prop: string) => void;
    onAddGsapAnimation?: (method: "to" | "from" | "set" | "fromTo") => void;
    onSetArcPath?: (animId: string, config: {
        enabled: boolean;
        autoRotate?: boolean | number;
        segments?: _hyperframes_core_gsap_parser.ArcPathSegment[];
    }) => void;
    onUpdateArcSegment?: (animId: string, segmentIndex: number, update: Partial<_hyperframes_core_gsap_parser.ArcPathSegment>) => void;
    /** Unroll computed (helper/loop) tweens into literal tweens for direct editing. */
    onUnroll?: (animationId: string) => void;
    onAddKeyframe?: (animationId: string, percentage: number, property: string, value: number | string) => void;
    onRemoveKeyframe?: (animationId: string, percentage: number) => void;
    onUpdateKeyframeEase?: (animationId: string, percentage: number, ease: string) => void;
    onSetAllKeyframeEases?: (animationId: string, ease: string) => void;
    onConvertToKeyframes?: (animationId: string, duration?: number) => void;
    onCommitAnimatedProperty?: (selection: DomEditSelection, property: string, value: number | string) => Promise<void>;
    /** Batched variant: commit several props into ONE keyframe (e.g. the 3D cube's
     * rotationX/Y/Z) so multi-axis edits don't race into adjacent duplicates. */
    onCommitAnimatedProperties?: (selection: DomEditSelection, props: Record<string, number | string>) => Promise<void>;
    onSeekToTime?: (time: number) => void;
    recordingState?: "idle" | "recording" | "preview";
    recordingDuration?: number;
    onToggleRecording?: () => void;
}
interface LocalFontData {
    family: string;
    fullName?: string;
    postscriptName?: string;
    style?: string;
    blob?: () => Promise<Blob>;
}
declare global {
    interface Window {
        queryLocalFonts?: () => Promise<LocalFontData[]>;
    }
}

declare const PropertyPanel: react.NamedExoticComponent<PropertyPanelProps>;

interface FileTreeProps {
    files: string[];
    activeFile: string | null;
    onSelectFile: (path: string) => void;
    onCreateFile?: (path: string) => void;
    onCreateFolder?: (path: string) => void;
    onDeleteFile?: (path: string) => void;
    onRenameFile?: (oldPath: string, newPath: string) => void;
    onDuplicateFile?: (path: string) => void;
    onMoveFile?: (oldPath: string, newPath: string) => void;
    onImportFiles?: (files: FileList, dir?: string) => void;
    lintFindingsByFile?: Map<string, {
        count: number;
        messages: string[];
    }>;
}
declare const FileTree: react.NamedExoticComponent<FileTreeProps>;

declare function StudioApp(): react_jsx_runtime.JSX.Element;

interface PickedElement {
    id: string | null;
    tagName: string;
    selector: string;
    label: string;
    boundingBox: {
        x: number;
        y: number;
        width: number;
        height: number;
    };
    textContent: string | null;
    src: string | null;
    dataAttributes: Record<string, string>;
    computedStyles: Record<string, string>;
}
interface UseElementPickerReturn {
    isPickMode: boolean;
    pickedElement: PickedElement | null;
    enablePick: () => void;
    disablePick: () => void;
    clearPick: () => void;
    /** Update a CSS property on the picked element live + persist to source */
    setStyle: (prop: string, value: string) => void;
    /** Update a data attribute on the picked element + persist to source */
    setDataAttr: (attr: string, value: string) => void;
    /** Update the text content of the picked element + persist to source */
    setTextContent: (text: string) => void;
    /** Override the active iframe (for zoomed canvas view). Pass null to restore primary. */
    setActiveIframe: (el: HTMLIFrameElement | null) => void;
    /** Ref that always points to the active iframe (focused canvas frame or preview panel) */
    activeIframeRef: React.RefObject<HTMLIFrameElement | null>;
}
interface PickerOptions {
    /** Workspace files for source patching */
    workspaceFiles?: Record<string, string>;
    /** Callback to sync patched files to the project */
    onSyncFiles?: (files: Record<string, string>) => void;
}
/**
 * Hook for element picking via the HyperFrame runtime's picker API.
 * Communicates with the iframe via postMessage.
 */
declare function useElementPicker(iframeRef: React.RefObject<HTMLIFrameElement | null>, options?: PickerOptions): UseElementPickerReturn;

/**
 * HTML Editor — Utility functions for parsing and manipulating HyperFrame HTML source.
 */
/**
 * Parse a CSS inline style string into a key-value map.
 * e.g. "opacity: 0.5; transform: matrix(1,0,0,1,0,0)" →
 *      { opacity: "0.5", transform: "matrix(1,0,0,1,0,0)" }
 */
declare function parseStyleString(style: string): Record<string, string>;
/**
 * Merge `newStyles` into an opening tag string's `style` attribute.
 * - New values win over existing ones.
 * - If no `style` attribute is present, one is added before the closing `>`.
 */
declare function mergeStyleIntoTag(tag: string, newStyles: string): string;
/**
 * Find the full element block (opening tag through closing tag) in the source.
 * Uses quote-aware scanning to handle attributes containing >.
 * Uses depth counting to handle nested same-name tags.
 */
declare function findElementBlock(html: string, elementId: string): {
    start: number;
    end: number;
    openTag: string;
    tagName: string;
    indent: string;
    innerContent: string;
    isSelfClosing: boolean;
} | null;

export { CompositionBreadcrumb, type CompositionLevel, CompositionThumbnail, FileTree, NLELayout, NLEPreview, type PatchOperation, type PickedElement, Player, PlayerControls, PropertyPanel, SourceEditor, StudioApp, Timeline, type TimelineElement, VideoThumbnail, applyPatch, findElementBlock, formatTime, liveTime, mergeStyleIntoTag, parseStyleString, resolveIframe, resolveSourceFile, useElementPicker, usePlayerStore, useTimelinePlayer };
