import type { TierResult } from "@pmndrs/detect-gpu"; import type { Gpu } from "vgpu"; import type { DowngradeReason, QualityPreference, QualityTier } from "./types.js"; /** * Ask every canvas on the page for a tier. * * One setting rather than a prop on each canvas, because the thing it * describes is the reader's device, which every canvas shares. A canvas that * names its own `quality.tier` keeps it; every other one follows this, and * follows it live — a settings menu calls this once and the whole page swaps. * Defaults to `auto`. */ export declare function setQualityTier(tier: QualityPreference): void; /** The tier the page is asked for. */ export declare function qualityTier(): QualityPreference; /** Hear every change to the page's tier. Returns the unsubscribe. */ export declare function onQualityTier(listener: (tier: QualityPreference) => void): () => void; export interface QualityConfig { /** * Where detect-gpu's benchmark tables are served from: a directory holding * its `dist/benchmarks/*.json`. Defaults to detect-gpu's own CDN copy on * unpkg. Point it at a same-origin copy to keep the page free of third-party * requests; a fetch that fails keeps High. */ readonly benchmarksUrl?: string; } /** Settings for Auto's device check. Call before the first canvas mounts. */ export declare function configureQuality(next: QualityConfig): void; /** The tier a preference starts on. Auto begins High and earns Low. */ export declare function initialTier(preference: QualityPreference): QualityTier; /** The device pixel ratio clamp for a tier. Low renders one texel per CSS pixel at most. */ export declare function tierDpr(tier: QualityTier, dpr: readonly [number, number]): readonly [number, number]; /** The `quality` uniform for a tier. */ export declare function tierUniform(tier: QualityTier): number; export interface FrameHealthSample { /** Raw frame interval in milliseconds, including frames skipped on purpose. */ readonly deltaMs: number; /** The loop had work to do, the page was visible, and someone was still on High in Auto. */ readonly active: boolean; } export interface FrameHealthMonitor { /** Feed one sample per frame. Returns true once the presented rate has fallen under target. */ record(sample: FrameHealthSample): boolean; /** Forget the history, for example right after a resize. */ reset(): void; } /** * Pure live-health policy, ported from the example. It measures presented * frames over *active* animation time, so a 60 Hz display is judged against * 60 and a 120 Hz display against 90. Hidden tabs and idle gaps (over 250 ms) * reset the window instead of counting as drops. Once tripped it stays tripped * until `reset()`. */ export declare function createFrameHealthMonitor(): FrameHealthMonitor; interface BatteryManagerLike extends EventTarget { readonly charging: boolean; readonly level: number; } /** * Whether the adapter alone asks for Low, before anything is fetched. * * A software adapter cannot draw a raymarch at any size, and the browser says * so up front. Everything else waits for the benchmark. */ export declare function adapterRequestsLow(gpu: Gpu): boolean; /** * Whether detect-gpu's verdict on the GPU asks for Low. * * The example's rule, less one clause. It sends every mobile device to Low * as well; that rule put an iPhone 14 on Low before it had drawn a frame, when * its GPU benchmarks at the top tier. Phones are judged by their GPU here like * everything else. Blocklisted GPUs and benchmark tiers 0 and 1 — under 30 * frames a second in detect-gpu's tables — ask for Low. A GPU the tables do * not know, or a table that could not be fetched, keeps High and leaves the * verdict to the frame rate. */ export declare function gpuTierRequestsLow(result: Pick): boolean; /** Inclusive threshold, ignored while the device is plugged in. */ export declare function batteryRequestsLow(battery: Pick): boolean; /** * The page's one set of signals, owned by the Stage beside the frame loop. * * Armed when the first Auto renderer reaches High, and one frame plus a * macrotask later than that, so the first presented frame is never delayed by * a battery query. Disarmed when the last watcher leaves. The verdict outlives * the watchers: a page that dropped stays dropped for every renderer created * after, until the reader pins a tier by hand. */ export declare class QualityMonitor { private readonly gpu; private readonly watchers; private readonly health; private battery; private armed; private arming; private armingFrame; private verdict; constructor(gpu: Gpu); /** Why the page dropped to Low, or nothing if it has not. */ get downgraded(): DowngradeReason | undefined; /** Whether any renderer still wants to hear a verdict. */ get watching(): boolean; /** * Hear the page's first downgrade. A renderer on High in Auto calls this; * one already on Low, or pinned, has nothing to hear. */ watch(listener: (reason: DowngradeReason) => void): () => void; /** One sample per frame of the shared loop. */ recordFrame(deltaMs: number, drew: boolean): void; /** Forget the frame history: a resize or a tier swap costs frames that are not a verdict. */ resetHealth(): void; private arm; private start; private readonly onBattery; private disarm; private requestLow; } export {}; //# sourceMappingURL=quality.d.ts.map