/** Where the clock stands: never started, running, held by a pause, or finished. */ export type TimerPhase = 'idle' | 'running' | 'paused' | 'complete'; /** The clock as a consumer renders it, computed fresh on every read. */ export interface TimerState { /** Where the clock stands right now, as one of the four phases. */ phase: TimerPhase; /** Milliseconds the clock has actually run, never counting paused time. */ elapsed: number; /** Milliseconds left before completion, or `Infinity` when counting up. */ remaining: number; /** The countdown length in force, `Infinity` for an open-ended count-up. */ duration: number; } /** How one clock is configured; every field has a working default. */ export interface TimerOptions { /** * Countdown length in milliseconds; leave it out to count up with no end. * * @default Infinity */ duration?: number | undefined; /** * Milliseconds between `onTick` calls while running; `0` reports no ticks. * * @default 0 */ tickMs?: number | undefined; /** * Whether a hidden or unloading page pauses the clock and a returning page resumes it. * * @default true */ pauseWhenHidden?: boolean | undefined; /** * Called on every tick and once more at completion, for rendering the clock only. * * Ticks are far too frequent to announce: write them to a progress bar or a * `