import { RevealState } from './reveal'; export type CyclePhase = 'idle' | 'reveal' | 'visible' | 'hide'; export interface CycleEvent { phase: CyclePhase; src: string | null; } export interface CycleOptions { reveal: RevealState; /** Image pool. Random pick per cycle, never same as previous. */ images: string[]; /** Random seconds in `idle` phase (shader-only). */ delayRange: [number, number]; /** * Time the image stays visible after the reveal completes. * - `number` — fixed ms. * - `[min, max]` — random ms per cycle, re-rolled each reveal. */ holdMs: number | [number, number]; /** ms cross-fade back to shader. */ fadeOutMs: number; /** Random initial delay seed so multiple cycles don't sync. */ initialDelayMs?: number; /** Cycle event hook. */ onPhase?: (event: CycleEvent) => void; /** * Optional callback invoked just before each pick that returns a list of * src strings the cycle MUST avoid this round (in addition to its own * "no immediate repeat" rule). Use this to coordinate multiple cycles so * they never display the same image at the same time. If every candidate * is excluded the cycle falls back to the standard random pick. */ excludeSrcs?: () => string[] | Set | null | undefined; } export interface Cycle { start: () => void; stop: () => void; /** Replace the `excludeSrcs` callback (or clear it by passing `null`). */ setExcludeSrcs: (fn: CycleOptions['excludeSrcs'] | null) => void; /** * Fire a single reveal pass *now*, regardless of whether the cycle is * currently auto-running. No-op if a reveal is already in progress * (`reveal`/`visible`/`hide` phases). * * `hold: 'auto'` (default) — runs reveal -> hold (`holdMs`) -> hide * automatically. When auto-running this re-enters the auto-loop afterwards; * otherwise the cycle returns to a stopped idle state. * * `hold: 'manual'` — runs reveal then stays in the `visible` phase * indefinitely. Call `triggerHide()` to fade out. */ triggerOnce: (opts?: { hold?: 'auto' | 'manual'; }) => void; /** * Manually trigger the hide fade if currently in `reveal` or `visible` * phase. No-op otherwise. Useful for "Hide image" buttons that complement * a `triggerOnce({ hold: 'manual' })` call. */ triggerHide: () => void; /** * Dissolve the currently revealed image into a sustained "regenerating" * pixel churn: the image breaks into the effect's cell grid, cells pop * in/out with the preset's flicker clock, and the shader plays through the * gaps (see `RevealState.startBoil`). The cycle returns to a stopped * `idle` state so the consumer can end the churn with `triggerOnce()` * (reveals the next image over it) or `stop()`. No-op unless currently in * `reveal`/`visible`. * * `autoRevealAfterMs` — when set, the churn ends by itself: after that many * ms the cycle fires `triggerOnce({ hold: 'manual' })`, dissolving the next * image in over the churn. Any manual `triggerOnce()` / `stop()` before the * timer fires cancels it. */ triggerBoil: (opts?: { autoRevealAfterMs?: number; }) => void; /** Current phase of the cycle. Useful for syncing UI button state. */ getPhase: () => CyclePhase; setPaused: (paused: boolean) => void; setImages: (images: string[]) => void; setOptions: (opts: Partial>) => void; isRunning: () => boolean; dispose: () => void; } export declare function createCycle(opts: CycleOptions): Cycle;