import { DeformFn, MetalFxInstance, ShaderRGB } from '../renderer/core'; import { Tween } from '../tween'; import { OutlineBuf } from '../renderer/outline'; import { Sprite, Tinted } from './bake'; import { GlowOptions, PerimSample } from './geometry'; export type { GlowOptions } from './geometry'; export interface GlowHandles { /** Carries the `.metal-fx-glow-svg` class (theme opacity / blend / filter * from the stylesheet). Inside it `env` carries the appear/disappear * opacity, and the canvas sits in that, overscanned by `margin`. The * envelope lives on a plain div, not the canvas: Safari can show stale or * empty canvas content when the canvas itself is the element whose * opacity is animated in its own compositing layer. */ wrap: HTMLDivElement; env: HTMLDivElement; canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D; /** Ring mode clips: everything outside the ring (drawn at 50 %) and the * band itself (100 %), as evenodd paths in canvas CSS px. Rebuilt only * when the outline changes (deform). */ surroundPath: Path2D | null; bandPath: Path2D | null; /** Point mode: glyph alpha per device pixel of `canvas`, from the mask image. */ maskAlpha: Uint8ClampedArray | null; maskReady: boolean; /** CSS-px margin the canvas extends beyond the host box on every side. */ margin: number; dpr: number; halo: Sprite; extra: Sprite; haloTint: Tinted; extraTint: Tinted; /** Outline buffers for the band mask; `maskSum` is a cheap checksum of the * last rendered outline so unchanged composites don't repaint it. */ mO: OutlineBuf; mI: OutlineBuf; maskSum: number; maskDeformed: boolean; /** Current deform, mirrored from the instance so the hotspot rides the dent. */ deform: DeformFn | null; width: number; height: number; cornerRadius: number; kind: 'pill' | 'circle'; /** Master scale used at bake time so per-frame position math * (GLOW.inset / GLOW.extraOutward) stays consistent with the sprites. */ scale: number; perim: PerimSample[]; /** True when perim is a point set (custom mask) rather than a ring arc. */ pointMode: boolean; currentIdx: number; appearedAt: number; glowOpacity: number; /** Appear/disappear envelope, 0..1. The tween runs on `envClock`, a clock * that advances by at most `ENV_MAX_STEP_MS` per tick: a dropped frame * stretches the fade instead of skipping most of it (which read as a pop). */ relocTween: Tween | null; relocNextIdx: number; relocMul: number; envClock: number; /** Cursor mode: the hotspot faces `inst.cursorLight` instead of hunting * luminance. `cursorArc` is the smoothed hotspot arc position. */ cursorMode: boolean; cursorArc: number; cursorTargetArc: number; lastTickMs: number; wanderS: number; wanderTargetS: number; wanderFrames: number; tintFrom: ShaderRGB; tintTarget: ShaderRGB; tintTween: Tween | null; tintHoldUntil: number; /** Last drawn state, for the skip test. */ dX: number; dY: number; dAng: number; dEX: number; dEY: number; dHOp: number; dEOp: number; dHaloTint: string; dExtraTint: string; dirty: boolean; /** Last envelope written to `canvas.style.opacity`. The appear/disappear * fade is a compositor-only opacity change — no canvas redraw. */ dEnv: number; } export declare function injectGlow(container: HTMLElement, opts: GlowOptions): GlowHandles; /** * Keep the glow's band mask (and hotspot) on the deformed outline. Call after * every composite while `deform` is set; pass null once to restore the rigid * mask. Cheap: a checksum of ~40 points, and a small path fill when changed. */ export declare function updateGlowMask(h: GlowHandles, deform: DeformFn | null): void; /** * One glow tick. Returns true while an envelope is animating (relocation * fade, tint crossfade, cursor tracking) — the loop then calls again every * animation frame so the fade is smooth instead of stepping at 15 fps. */ export declare function updateGlow(h: GlowHandles, inst: MetalFxInstance, nowMs: number, strengthMul: number, theme?: 'dark' | 'light'): boolean; /** * Carry the visible state of a glow across a rebuild (a real resize), so the * halo keeps its hotspot, brightness and fade instead of restarting from * invisible. Only state — never geometry, sprites or masks. */ export declare function carryGlowState(prev: GlowHandles, next: GlowHandles): void; export declare function resizeGlow(handles: GlowHandles, container: HTMLElement, opts: GlowOptions): GlowHandles;