import * as react_jsx_runtime from 'react/jsx-runtime'; import { EasingFunction, MotionValue } from 'framer-motion'; import * as react from 'react'; import { ElementType, CSSProperties, ReactNode } from 'react'; type SceneConfig = { id: string; label: string; phaseOrder: readonly string[]; phaseDurations: Readonly>; phaseLabels?: Readonly>; }; type PhaseRange = { phase: string; start: number; end: number; duration: number; }; declare function computeRanges(config: SceneConfig): PhaseRange[]; declare function totalDurationFor(ranges: PhaseRange[]): number; declare function rangeOf(ranges: PhaseRange[], phase: string): PhaseRange; declare function phaseAtTime(ranges: PhaseRange[], totalDuration: number, time: number): string; /** Calm, decisive easing — no bounce. Loupe's house default. */ declare const HOUSE_CURVE_FN: EasingFunction; /** Soft settle with mild overshoot — for cards landing into place. */ declare const SETTLE_CURVE_FN: EasingFunction; /** * TimelineProvider — owns the time axis for a single scene. * * Each scene wraps itself in a ``. The provider * computes phase ranges, runs the rAF loop, and (if a LoupeRegistry is mounted * above) registers itself so the floating panel can drive it. */ type TimelineState = { sceneId: string; sceneLabel: string; ranges: PhaseRange[]; totalDuration: number; phaseOrder: readonly string[]; time: MotionValue; speed: number; setSpeed: (s: number) => void; paused: boolean; setPaused: (p: boolean) => void; seek: (ms: number) => void; restart: () => void; }; declare function TimelineProvider({ config, children, }: { config: SceneConfig; children: React.ReactNode; }): react_jsx_runtime.JSX.Element; declare function useTimeline(): TimelineState; /** Subscribe to the raw time MotionValue of the active scene. */ declare function useTimelineTime(): MotionValue; /** Look up a range from the active scene's config. Throws if phase unknown. */ declare function useRangeOf(phase: string): PhaseRange; type UseTimelineValueOptions = { startMs?: number; endMs?: number; /** Shortcut: span the full duration of this phase. */ phase?: string; /** Offset within the phase before the transition begins. */ offset?: number; /** How long the transition runs (overrides endMs). */ duration?: number; ease?: EasingFunction; }; /** * Derive a MotionValue animating `from → to` between two timeline points. * Holds at `from` before the window and at `to` after it. */ declare function useTimelineValue(from: number, to: number, options?: UseTimelineValueOptions): MotionValue; /** Current phase name, derived from time on every tick. */ declare function usePhaseFromTime(): string; /** * Counter that increments each time the timeline ENTERS `targetPhase`. * Use as a `playKey` on one-shot effects (Lottie, Web Animations) so they * re-trigger on every loop pass and on scrub-past-then-forward. */ declare function usePhaseEnterKey(targetPhase: string): number; /** * LoupeRegistry — app-root context that knows about every Loupe-enabled * scene currently mounted on the page. A single floating panel drives the * active scene picked from the dropdown. */ type RegisteredTimeline = { time: MotionValue; ranges: PhaseRange[]; totalDuration: number; phaseOrder: readonly string[]; phaseLabels?: Readonly>; speed: number; setSpeed: (s: number) => void; paused: boolean; setPaused: (p: boolean) => void; seek: (ms: number) => void; restart: () => void; }; type RegisteredAnnotations = { state: unknown; }; type RegisteredScene = { id: string; label: string; rootRef: React.RefObject; timeline: RegisteredTimeline; annotations?: RegisteredAnnotations; }; /** * Shape callers pass to `useRegisterSceneWithLoupe`. Matches what * a TimelineProvider-style component already computes — a MotionValue * + scene config + controls. Keep this decoupled from our internal * `TimelineState` so third-party timelines don't need to mirror our * exact types. */ type ExternalScene = { id: string; label: string; phaseOrder: readonly string[]; phaseLabels?: Readonly>; ranges: PhaseRange[]; totalDuration: number; time: MotionValue; speed: number; setSpeed: (s: number) => void; paused: boolean; setPaused: (p: boolean) => void; seek: (ms: number) => void; restart: () => void; }; type LoupeRegistryState = { scenes: RegisteredScene[]; activeSceneId: string | null; /** * Set the active scene by id, or pass `null` to clear selection * (collapses the floating panel to a draggable pill). Marks the * user's choice as sticky so later registrations don't auto-swap * it back. */ setActiveSceneId: (id: string | null) => void; registerScene: (scene: RegisteredScene) => void; unregisterScene: (id: string) => void; attachAnnotations: (sceneId: string, ann: RegisteredAnnotations) => void; flashTick: number; flash: (id: string) => void; }; declare function LoupeRegistryProvider({ children, }: { children: React.ReactNode; }): react_jsx_runtime.JSX.Element; declare function useLoupeRegistry(): LoupeRegistryState; declare function useOptionalLoupeRegistry(): LoupeRegistryState | null; /** * Register a scene with the Loupe panel's registry from outside * Loupe's built-in ``. Useful when you've got * your OWN TimelineProvider implementation (e.g. a local copy of * the timeline primitives) and just want its state to show up in * the Loupe panel's scene dropdown. * * Call this inside your provider with the scene's timeline state * + the root-element ref you want the panel to flash / scroll to. * No-op if no `` is mounted above — safe to * leave in place even when Loupe isn't active. * * @example * ```tsx * function MyTimelineProvider({ config, children }) { * const time = motionValue(0); * // ...all the usual state... * const value = { id: config.id, label: config.label, time, ... }; * const rootRef = useRef(null); * useRegisterSceneWithLoupe(value, rootRef); * return
{children}
; * } * ``` */ declare function useRegisterSceneWithLoupe(scene: ExternalScene, rootRef: React.RefObject): void; declare function useSceneRootRef(): React.RefObject; /** * Like `useSceneRootRef` but returns `null` when no provider is mounted. * Used by `` to detect whether Loupe is active without throwing * in production code that ships the component but skips the provider. */ declare function useOptionalSceneRootRef(): React.RefObject | null; /** * — the recommended wrapper for a Loupe-instrumented scene. * * Bakes in two invariants consumers used to re-derive by hand: * 1. Registers itself with the scene-ref context so registry features * (flash, scroll-to, picker fallback) can locate the scene. * 2. Pointer-events: auto whenever Loupe is mounted; pointer-events: * none otherwise. `document.elementFromPoint` sees through any * ancestor with `pointer-events: none`, so a hardcoded `none` on * the scene root makes the picker pick through to the page * underneath. We restore production click-through automatically * when no `` is mounted above. */ interface SceneRootProps { as?: ElementType; className?: string; style?: CSSProperties; children?: ReactNode; [key: string]: unknown; } declare const SceneRoot: react.ForwardRefExoticComponent & react.RefAttributes>; declare const SCENE_ROOT_ATTR = "data-loupe-scene-root"; declare function LoupePanel(): react_jsx_runtime.JSX.Element; /** * A single piece of timestamped feedback on a DOM element (or a drawn region). * * Captured fields are chosen so that pasting into an AI agent gives it enough * context to jump straight to the right file + right frame of animation. */ type Annotation = { /** Stable id, generated at creation. */ id: string; /** ISO timestamp of when the annotation was created. */ createdAt: string; /** Scene id this annotation belongs to (matches a TimelineProvider config id). */ sceneId: string; /** Human label for the scene (denormalized for export readability). */ sceneLabel: string; /** Active phase at annotation time. Generic string per scene's vocabulary. */ phase: string; /** ms elapsed inside the phase when the annotation was made. */ phaseElapsedMs: number; /** Phase's total duration (ms). */ phaseDurationMs: number; /** Absolute ms on the scene's loop timeline. */ globalTimeMs: number; /** Scene's total loop duration (ms). */ totalDurationMs: number; /** Rounded percent `globalTimeMs / totalDurationMs`. */ totalPercent: number; /** Short, human-readable CSS path to the element. Not uniquely-guaranteed. */ selector?: string; /** Resolved React component name (from Fiber), if any. */ componentName?: string; /** Source file path + line, from React Fiber `_debugSource`, if any. */ sourceLocation?: string; /** Raw DOM tag of the picked element. */ tagName?: string; region?: { x: number; y: number; w: number; h: number; }; note: string; color: string; }; type AnnotationDraft = { kind: 'element'; element: Element; snapshot: Omit; } | { kind: 'region'; region: { x: number; y: number; w: number; h: number; }; snapshot: Omit; }; /** * AnnotationsProvider — lives at app root (above all TimelineProviders). * * Reads `activeSceneId` from the LoupeRegistry and presents that scene's * annotation list. Scene-aware ops (pickElement, pickRegion, focusAnnotation) * read time/pause/seek from the active scene's registered TimelineState. */ type PickerMode = 'off' | 'element' | 'region'; type AnnotationsState = { annotations: Annotation[]; visible: boolean; pickerMode: PickerMode; draft: AnnotationDraft | null; setVisible: (v: boolean) => void; setPickerMode: (m: PickerMode) => void; /** * Accepts any DOM `Element`, NOT `HTMLElement`. SVG nodes (paper paths, * circles, cross strokes inside an animated ``) inherit from * `Element` only — tightening this to `HTMLElement` silently drops every * SVG pick and was the original bug behind "I can't select the paper." * Regression covered in `AnnotationsProvider.test.tsx`. */ pickElement: (el: Element) => void; pickRegion: (region: { x: number; y: number; w: number; h: number; }) => void; commitDraft: (note: string) => void; cancelDraft: () => void; updateAnnotation: (id: string, patch: Partial) => void; deleteAnnotation: (id: string) => void; clearAll: () => void; focusAnnotation: (id: string) => Annotation | undefined; }; declare function AnnotationsProvider({ children }: { children: React.ReactNode; }): react_jsx_runtime.JSX.Element; declare function useAnnotations(): AnnotationsState; declare function AnnotationOverlay(): react_jsx_runtime.JSX.Element | null; /** * AnnotationPins — numbered pin at each saved annotation. Element pins * follow their selector via rAF tracking; region pins anchor to the region's * top-right corner. Click → focus + edit. Right-click → delete. */ declare function AnnotationPins(): react_jsx_runtime.JSX.Element | null; /** * Serialize a list of annotations into a markdown block that can be pasted * into an AI agent (or any chat) and convey: * - WHICH scene the user was reviewing (groups multiple scenes) * - WHERE in the DOM the user was pointing * - WHEN in the animation they were pointing * - WHAT they wrote */ declare function annotationsToMarkdown(list: Annotation[]): string; export { type Annotation, type AnnotationDraft, AnnotationOverlay, AnnotationPins, AnnotationsProvider, type ExternalScene, HOUSE_CURVE_FN, LoupePanel, LoupeRegistryProvider, type PhaseRange, type PickerMode, type RegisteredScene, type RegisteredTimeline, SCENE_ROOT_ATTR, SETTLE_CURVE_FN, type SceneConfig, SceneRoot, TimelineProvider, type TimelineState, annotationsToMarkdown, computeRanges, phaseAtTime, rangeOf, totalDurationFor, useAnnotations, useLoupeRegistry, useOptionalLoupeRegistry, useOptionalSceneRootRef, usePhaseEnterKey, usePhaseFromTime, useRangeOf, useRegisterSceneWithLoupe, useSceneRootRef, useTimeline, useTimelineTime, useTimelineValue };