export interface EffectFrame { /** The 2D context. Or null when `context: null` (WebGL/Three effects own * their renderer and read `canvas` instead). */ ctx: CanvasRenderingContext2D | null; canvas: HTMLCanvasElement; /** Canvas size in CSS pixels (already DPR-scaled on the 2D context). */ width: number; height: number; dpr: number; /** Live `prefers-reduced-motion` state: damp or freeze your motion. */ reducedMotion: boolean; } export interface EffectOptions { /** Runs once, after the first layout size is known. Build your state here; * return a function to dispose it (GPU buffers, renderers) on destroy. */ setup?: (fx: EffectFrame) => void | (() => void); /** One simulation step. `dt` is seconds since last frame, clamped to 50ms. */ frame: (fx: EffectFrame, dt: number) => void; /** Called after a resize (fx fields already updated). */ resize?: (fx: EffectFrame) => void; /** Max device-pixel-ratio (default 2. Beyond that it's just heat). */ dprCap?: number; /** `'2d'` (default) grabs and DPR-scales a 2D context. `null` grabs * nothing: for WebGL/Three: create your own renderer on `fx.canvas` * (the harness still sizes the backing store and runs the lifecycle). */ context?: '2d' | null; /** Pause automatically when offscreen, zero-area or tab hidden (default true). */ autoPause?: boolean; } export interface EffectHandle { pause: () => void; resume: () => void; destroy: () => void; } export declare function mountEffect(canvas: HTMLCanvasElement, { setup, frame, resize, dprCap, autoPause, context }: EffectOptions): EffectHandle;