@idle-screens/core 0.4.7 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -571,4 +571,5 @@ interface RunIdleWorkerOpts {
571
571
  */
572
572
  declare function runIdleWorker(registry: Record<string, SaverPlugin>, opts?: RunIdleWorkerOpts): void;
573
573
 
574
- export { type ConfigMenuConfig, type ControlTrack, DEFAULT_CONFIG, DEFAULT_MENU_HOTKEY, type Ease, type EngineState, IdleDetector, IdleScreenElement, type IdleScreensConfig, IdleScreensEngine, type PageContext, type ParamDef, type ParamDelta, type ParamSpace, type ParamType, type ParamValue, type Rng, type RunIdleWorkerOpts, type SaverContext, type SaverInstance, type SaverLayer, type SaverManifest, type SaverPlugin, type WorkerInbound, type WorkerOutbound, createRng, defaultParams, defineIdleScreen, integrateParam, resolveConfigMenu, runIdleWorker, sampleTrack };
574
+ export { DEFAULT_CONFIG, DEFAULT_MENU_HOTKEY, IdleDetector, IdleScreenElement, IdleScreensEngine, createRng, defaultParams, defineIdleScreen, integrateParam, resolveConfigMenu, runIdleWorker, sampleTrack };
575
+ export type { ConfigMenuConfig, ControlTrack, Ease, EngineState, IdleScreensConfig, PageContext, ParamDef, ParamDelta, ParamSpace, ParamType, ParamValue, Rng, RunIdleWorkerOpts, SaverContext, SaverInstance, SaverLayer, SaverManifest, SaverPlugin, WorkerInbound, WorkerOutbound };
package/dist/index.js CHANGED
@@ -1122,6 +1122,7 @@ var IdleScreenElement = class extends HostBase {
1122
1122
  }
1123
1123
  close() {
1124
1124
  if (!this.dialog?.open) return;
1125
+ ++this.mountToken;
1125
1126
  window.removeEventListener("resize", this.onResize);
1126
1127
  window.removeEventListener("error", this.onRuntimeError);
1127
1128
  window.removeEventListener("unhandledrejection", this.onRuntimeRejection);
@@ -1286,6 +1287,7 @@ var IdleScreenElement = class extends HostBase {
1286
1287
  this._engine?.wake();
1287
1288
  }
1288
1289
  teardown() {
1290
+ ++this.mountToken;
1289
1291
  window.removeEventListener("resize", this.onResize);
1290
1292
  window.removeEventListener("error", this.onRuntimeError);
1291
1293
  window.removeEventListener("unhandledrejection", this.onRuntimeRejection);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/rng.ts","../src/reactive.ts","../src/control-track.ts","../src/idle-detector.ts","../src/engine.ts","../src/fault-screen.ts","../src/fault-attribution.ts","../src/idle-screen.element.ts","../src/worker-protocol.ts"],"sourcesContent":["import type { Rng } from './rng';\n\n/** How a parameter interpolates between control-track deltas. */\nexport type Ease = 'step' | 'linear' | 'smooth';\n\nexport type ParamType = 'number' | 'color' | 'bool' | 'enum' | 'string';\nexport type ParamValue = number | string | boolean;\n\n/** One typed, ranged, interpolatable knob: an agent's steering surface. */\nexport interface ParamDef {\n type: ParamType;\n default: ParamValue;\n min?: number;\n max?: number;\n options?: string[];\n /** Default interpolation when a delta targets this knob. */\n ease?: Ease;\n}\n\nexport type ParamSpace = Record<string, ParamDef>;\n\n/** A sparse, timestamped, interpolatable parameter change. Agents stream THESE\n * (over AG-UI state-patches / MCP resource updates), never frames. */\nexport interface ParamDelta {\n /** ms from track start. */\n t: number;\n /** key in the saver's paramSpace. */\n path: string;\n value: ParamValue;\n ease?: Ease;\n /** ramp duration ms from the previous value (0 = instant). */\n dur?: number;\n}\n\n/** A recorded or live steering performance for a seeded program. */\nexport interface ControlTrack {\n program: string;\n seed: number;\n duration?: number;\n loop?: boolean;\n deltas: ParamDelta[];\n}\n\n/** Page-context seam: passthrough savers read/eat the live page through this,\n * rather than querying `document` directly. */\nexport interface PageContext {\n /** Sample dominant colors of the page (for palette-reactive savers). */\n palette(): string[];\n /** Visible page blocks a passthrough saver may transform (\"victims\"). */\n victims(selector: string): HTMLElement[];\n}\n\n/** What the host hands a saver when it mounts. */\nexport interface SaverContext {\n /** A full-viewport container the saver renders into. Canvas savers create a\n * canvas inside it; DOM savers (toasters, DVD, messages) append elements. The\n * host is cleared by the runtime on dispose. */\n host: HTMLElement;\n /** Pre-created rendering surface. When present, canvas savers should use this\n * instead of creating their own canvas. In a Worker this is an OffscreenCanvas. */\n surface?: HTMLCanvasElement | OffscreenCanvas;\n /** Device pixel ratio — use instead of reading window.devicePixelRatio. */\n dpr: number;\n width: number;\n height: number;\n /** Seeded PRNG. Use this, never Math.random(), for determinism. */\n rng: Rng;\n seed: number;\n /** Initial paramSpace overrides for this mount (e.g. a channel's published\n * `{id, params}` scene). Savers treat these as the new defaults; live\n * steering via applyTrack still layers on top. Optional. */\n params?: Record<string, ParamValue>;\n reducedMotion: boolean;\n /** Present for passthrough savers (over the live page). */\n page?: PageContext;\n}\n\n/**\n * One deck of a mounted saver's practical composition stack, bottom-up.\n *\n * This is deliberately NOT the schema's layer concept (declared sprite\n * layers): it describes what a running instance is actually composed of, so\n * hosts and tooling can present and inspect the stack uniformly. A\n * passthrough saver is at minimum `page` + `surface`; a classic canvas saver\n * is just its `surface`; savers may further describe the logical draw passes\n * inside a surface. The model is intentionally small and open — future work\n * (multi-surface savers, per-pass toggles, cross-saver scenes) extends it\n * rather than replacing it.\n */\nexport interface SaverLayer {\n id: string;\n label: string;\n /** 'page' = the live document a passthrough saver performs on;\n * 'surface' = a rendering surface the instance owns (usually a canvas);\n * 'pass' = a logical draw pass inside the surface above it. */\n kind: 'page' | 'surface' | 'pass';\n /** The element that IS this layer, when one exists. Hosts may toggle its\n * visibility for inspection. A `page` layer has no element — the HOST\n * owns the document and binds it (the saver only borrows it). */\n el?: HTMLElement;\n description?: string;\n}\n\nexport interface SaverInstance {\n /** Freeze/unfreeze the render loop (paused, reduced-motion, or screensaver end). */\n setPaused(paused: boolean): void;\n resize(width: number, height: number, dpr?: number): void;\n /** Deterministic, frame-addressable render: draw the state at logical time `t`\n * (ms) for `seed`. Pure w.r.t. (program, seed, applied track, t). Optional. */\n renderFrame?(t: number, seed: number): void;\n /** Snapshot the CURRENT frame as an ImageBitmap, rendering one on demand if\n * the loop is throttled (hidden tab). Exists because page JS cannot read\n * this frame from outside: a worker-transferred canvas is opaque to the\n * main thread, and a non-preserveDrawingBuffer WebGL canvas reads black\n * outside the render tick. Resolve null for \"nothing to show yet\".\n * Optional. */\n capture?(): Promise<ImageBitmap | null>;\n /** Describe the CURRENT state in words and numbers — what a pixel capture\n * cannot say: which objects exist, where they are, what behaviour is in\n * flight. Plain JSON, small (a few KB), synchronous — read from the last\n * rendered frame, never a fresh simulation. The analytic perception a\n * non-vision agent gets for a saver that has no declarative spec.\n * Return null for \"nothing yet\". Optional. */\n inspect?(): Record<string, unknown> | null;\n /** Steer parameters over time. Optional. */\n applyTrack?(track: ControlTrack): void;\n /** Seek logical animation time (ms) for workbench preview. Optional. */\n previewAt?(t: number): void;\n /** Describe the practical composition stack, bottom-up. Optional.\n * (Named `composition`, not `layers`: schema-compiled instances already\n * carry their declared sprite `layers` as data.) */\n composition?(): SaverLayer[];\n /** Tear down; MUST restore any live-page mutations. */\n dispose(): void;\n}\n\n/** Self-describing metadata, readable WITHOUT executing the saver. */\nexport interface SaverManifest {\n id: string;\n label: string;\n /** One-line summary for catalogs / agent tooling (e.g. MCP `listSavers`). */\n description?: string;\n passthrough?: boolean;\n minBackend?: 'css' | 'canvas2d' | 'webgl2' | 'webgpu';\n costTier?: 'idle' | 'low' | 'medium' | 'high';\n motionIntensity?: 'calm' | 'moderate' | 'energetic';\n reducedMotionFallback?: 'static' | 'slow' | 'hide';\n palette?: string[];\n paramSpace?: ParamSpace;\n a11y?: { flashSafe?: boolean; notes?: string };\n provenance?: { prompt?: string; seed?: number; model?: string };\n /** Attribution for savers derived from licensed or third-party work — the\n * concept, reference implementation, or embedded assets. Hosts SHOULD show\n * this wherever the saver is showcased; the full ledger is CREDITS.md. */\n attribution?: {\n /** What this saver descends from, e.g. 'After Dark \"Messages\" — concept by Berkeley Systems'. */\n source: string;\n /** The licensing situation, ours and upstream, e.g. 'MIT port; reference CSS MIT (Bryan Braun)'. */\n license: string;\n /** Reference implementation or rights context. */\n url?: string;\n };\n thumbnail?: string;\n /** Saver renders on a provided surface (canvas/OffscreenCanvas) and avoids DOM\n * APIs, enabling Worker execution when the browser supports OffscreenCanvas. */\n workerReady?: boolean;\n /**\n * How the saver relates to time — a semantic claim about the program, not\n * an API flag (renderFrame's presence is the API).\n *\n * - 'closed-form': every frame is a pure function of (t, seed, params) —\n * compiled plans, phase integrals, triangle waves, CSS keyframes. With\n * `renderFrame` this means scrubbing, seek-back determinism, and\n * reproducible perception readings; without it (DOM/CSS savers) the\n * motion is still periodic and pure, just not pixel-addressable by us.\n * - 'simulated': state genuinely evolves by integration (fluid dynamics,\n * reaction-diffusion) — frames depend on history, sampling is the only\n * honest observation, and motion metrics between runs are meaningless.\n *\n * Tooling should prefer this over hardcoded per-saver capability lists.\n */\n timeModel?: 'closed-form' | 'simulated';\n}\n\n/** A registered saver: manifest + a (possibly lazy/async) mount factory. */\nexport interface SaverPlugin {\n manifest: SaverManifest;\n mount(ctx: SaverContext): SaverInstance | Promise<SaverInstance>;\n /** Original schema spec (JSON-serializable). When present, the element sends\n * a `mount-spec` message to the Worker instead of `mount`, so the Worker can\n * compile and run the spec without pre-registration in its registry. */\n spec?: unknown;\n}\n\n/** Engine configuration. */\nexport interface IdleScreensConfig {\n /** Idle time before sleeping, in ms. */\n timeoutMs: number;\n /** Also sleep when the window loses focus. */\n sleepOnBlur: boolean;\n /** Do not run on localhost (debug hooks bypass this). */\n disableOnLocalhost: boolean;\n /** Which registered saver to show. */\n defaultPluginId: string;\n /** Saver to mount when the ACTIVE saver faults at runtime (throws in its\n * loop, loses its GL context and errors, etc.). The screen must stay a\n * screen: on fault the element swaps to this saver (e.g. 'bsod'), and if\n * it too fails, to a built-in DOM fault screen. Optional. */\n crashSaverId?: string;\n /** How to pick across sleeps. */\n selection: 'fixed' | 'random' | 'rotate';\n /** Show the clock chrome (non-passthrough savers only). */\n showClock: boolean;\n /** Exact document title while asleep. */\n sleepTitle?: string;\n /** Master seed for plugin selection + the default saver seed. */\n seed: number;\n /** Return true for a URL where the saver must never sleep. */\n suppress?: (url: string) => boolean;\n /** Pluggable persistence (defaults to localStorage when available). */\n storage?: { get(key: string): string | null; set(key: string, value: string): void };\n /** URL of the idle-worker script. When set, savers with `workerReady: true`\n * render in a Web Worker via OffscreenCanvas. Omit to disable Worker rendering. */\n workerUrl?: string;\n /** Built-in, keyboard-opened config menu (saver picker). `true`/omitted =\n * enabled with ⌘K / Ctrl+K; `false` = no hotkey and no built-in menu (the\n * `configMenuOpen` signal + open/close API still work, so a host may render\n * its own). Pass an object to reconfigure. */\n configMenu?: boolean | ConfigMenuConfig;\n}\n\n/** Options for the built-in, keyboard-opened config menu. */\nexport interface ConfigMenuConfig {\n /** Master switch. Default true. */\n enabled?: boolean;\n /** Decides whether a keydown opens/toggles the menu. Default: ⌘K / Ctrl+K. */\n hotkey?: (e: KeyboardEvent) => boolean;\n /** Show the saver picker control. Default true. */\n showPicker?: boolean;\n /** Preview the picked saver immediately (sleep to show it). Default true. */\n previewOnPick?: boolean;\n /** Heading text. Default 'Screen Saver'. */\n title?: string;\n}\n\n/** Default menu hotkey: ⌘K (mac) / Ctrl+K, no other modifiers. */\nexport const DEFAULT_MENU_HOTKEY = (e: KeyboardEvent): boolean =>\n (e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey && e.key.toLowerCase() === 'k';\n\n/** Normalize `configMenu` to a filled config, or null when disabled. */\nexport function resolveConfigMenu(\n c: boolean | ConfigMenuConfig | undefined,\n): Required<ConfigMenuConfig> | null {\n if (c === false) return null;\n const o: ConfigMenuConfig = c === true || c === undefined ? {} : c;\n if (o.enabled === false) return null;\n return {\n enabled: true,\n hotkey: o.hotkey ?? DEFAULT_MENU_HOTKEY,\n showPicker: o.showPicker ?? true,\n previewOnPick: o.previewOnPick ?? true,\n title: o.title ?? 'Screen Saver',\n };\n}\n\nexport const DEFAULT_CONFIG: IdleScreensConfig = {\n timeoutMs: 60_000,\n sleepOnBlur: true,\n disableOnLocalhost: false,\n defaultPluginId: '',\n selection: 'fixed',\n showClock: true,\n seed: 1,\n configMenu: true,\n};\n","/**\n * A tiny, fast, seedable PRNG (splitmix32). Deterministic: the same seed always\n * yields the same sequence, on any machine. This is what makes \"same program +\n * seed + control-track = identical frames\" hold. Savers take an `Rng` from their\n * `SaverContext` instead of calling `Math.random()`.\n */\nexport interface Rng {\n /** Next float in [0, 1). */\n next(): number;\n /** Float in [min, max). */\n range(min: number, max: number): number;\n /** Integer in [min, max] inclusive. */\n int(min: number, max: number): number;\n /** Random element of a non-empty array. */\n pick<T>(arr: readonly T[]): T;\n /** A fresh independent stream (e.g. per subsystem), derived from this seed. */\n fork(salt: number): Rng;\n}\n\nexport function createRng(seed: number): Rng {\n let a = seed >>> 0;\n const next = (): number => {\n a |= 0;\n a = (a + 0x9e3779b9) | 0;\n let t = a ^ (a >>> 16);\n t = Math.imul(t, 0x21f0aaad);\n t = t ^ (t >>> 15);\n t = Math.imul(t, 0x735a2d97);\n return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296;\n };\n const rng: Rng = {\n next,\n range: (min, max) => min + (max - min) * next(),\n int: (min, max) => min + Math.floor(next() * (max - min + 1)),\n pick: (arr) => arr[Math.floor(next() * arr.length)] as (typeof arr)[number],\n fork: (salt) => createRng((seed ^ Math.imul(salt + 1, 0x9e3779b9)) >>> 0),\n };\n return rng;\n}\n","/**\n * Framework-agnostic reactivity. We re-export `@preact/signals-core` so the rest\n * of the engine uses the same `signal` / `computed` / `effect` primitives it did\n * as Angular signals, with near-identical semantics.\n */\nexport {\n signal,\n computed,\n effect,\n batch,\n untracked,\n type Signal,\n type ReadonlySignal,\n} from '@preact/signals-core';\n","import type { ControlTrack, Ease, ParamDelta, ParamSpace, ParamValue } from './types';\n\nfunction easeFn(e: Ease, p: number): number {\n const x = p <= 0 ? 0 : p >= 1 ? 1 : p;\n switch (e) {\n case 'step':\n return x >= 1 ? 1 : 0;\n case 'smooth':\n return x * x * (3 - 2 * x);\n case 'linear':\n default:\n return x;\n }\n}\n\nfunction lerp(a: number, b: number, p: number): number {\n return a + (b - a) * p;\n}\n\nfunction parseHex(c: string): [number, number, number] | null {\n const m = /^#?([0-9a-f]{6})$/i.exec(c.trim());\n if (!m) return null;\n const n = parseInt(m[1]!, 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\nfunction toHex(rgb: [number, number, number]): string {\n const h = (v: number): string =>\n Math.round(Math.max(0, Math.min(255, v)))\n .toString(16)\n .padStart(2, '0');\n return `#${h(rgb[0])}${h(rgb[1])}${h(rgb[2])}`;\n}\n\nfunction lerpValue(type: string, a: ParamValue, b: ParamValue, p: number): ParamValue {\n if (type === 'number' && typeof a === 'number' && typeof b === 'number') return lerp(a, b, p);\n if (type === 'color' && typeof a === 'string' && typeof b === 'string') {\n const ca = parseHex(a);\n const cb = parseHex(b);\n if (ca && cb) return toHex([lerp(ca[0], cb[0], p), lerp(ca[1], cb[1], p), lerp(ca[2], cb[2], p)]);\n }\n return p >= 1 ? b : a; // bool / enum / string / step\n}\n\n/** A number-param keyframe value as both sampling and integration see it:\n * finite numbers pass, finite numeric strings coerce (MCP harnesses with\n * untyped `value` params stringify numbers), anything else is null — the\n * keyframe is treated as absent. Shared by evalPath and integrateParam so\n * the sampled curve and its integral can never disagree about a value. */\nfunction numericValue(v: ParamValue): number | null {\n if (typeof v === 'number') return Number.isFinite(v) ? v : null;\n if (typeof v === 'string') {\n const n = Number(v);\n return v.trim() !== '' && Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\nfunction evalPath(\n dflt: ParamValue,\n type: string,\n defaultEase: Ease,\n deltas: ParamDelta[],\n t: number,\n): ParamValue {\n if (deltas.length === 0) return dflt;\n // Number params: stringified keyframes coerce, junk keyframes are ignored\n // (as if absent) — identical treatment to integrateParam, so the distance\n // warp can never diverge from the curve the saver samples.\n if (type === 'number') {\n const cleaned: ParamDelta[] = [];\n for (const k of deltas) {\n const n = numericValue(k.value);\n if (n === null) continue;\n cleaned.push(n === k.value ? k : { ...k, value: n });\n }\n deltas = cleaned;\n if (deltas.length === 0) return dflt;\n }\n let prevVal = dflt;\n let prevT = -Infinity;\n for (const k of deltas) {\n if (t < k.t) {\n const rampStart = k.dur != null ? k.t - k.dur : prevT === -Infinity ? k.t : prevT;\n if (t <= rampStart) return prevVal;\n const p = easeFn(k.ease ?? defaultEase, (t - rampStart) / (k.t - rampStart || 1));\n return lerpValue(type, prevVal, k.value, p);\n }\n prevVal = k.value;\n prevT = k.t;\n }\n return prevVal; // after the last keyframe\n}\n\n/**\n * Sample a control track: given the saver's param space (defaults + types) and a\n * track, return the interpolated parameter values at logical time `t` (ms). Pure\n * and deterministic: identical `(space, track, t)` yields identical output.\n */\nexport function sampleTrack(\n space: ParamSpace,\n track: ControlTrack,\n t: number,\n): Record<string, ParamValue> {\n const byPath = new Map<string, ParamDelta[]>();\n for (const d of track.deltas) {\n const arr = byPath.get(d.path) ?? [];\n arr.push(d);\n byPath.set(d.path, arr);\n }\n const wrapped =\n track.loop && track.duration && track.duration > 0 ? t % track.duration : t;\n\n const out: Record<string, ParamValue> = {};\n for (const [path, def] of Object.entries(space)) {\n const deltas = (byPath.get(path) ?? []).slice().sort((a, b) => a.t - b.t);\n out[path] = evalPath(def.default, def.type, def.ease ?? 'linear', deltas, wrapped);\n }\n return out;\n}\n\n/** Default param values (the \"resting\" program before any track is applied). */\nexport function defaultParams(space: ParamSpace): Record<string, ParamValue> {\n const out: Record<string, ParamValue> = {};\n for (const [path, def] of Object.entries(space)) out[path] = def.default;\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// integrateParam — the closed-form integral of one tracked number param.\n// ---------------------------------------------------------------------------\n\n/** ∫₀ᵘ ease dx, for the eases easeFn defines. `step` holds 0 until u = 1 (the\n * value jumps at the keyframe), so its running integral is 0 on [0, 1). */\nfunction easeIntegral(e: Ease, u: number): number {\n const x = u <= 0 ? 0 : u >= 1 ? 1 : u;\n switch (e) {\n case 'step':\n return 0;\n case 'smooth':\n return x * x * x - (x * x * x * x) / 2; // ∫ 3x² − 2x³\n case 'linear':\n default:\n return (x * x) / 2;\n }\n}\n\nfunction integrateDeltas(\n dflt: number,\n defaultEase: Ease,\n deltas: Array<{ t: number; value: number; ease?: Ease; dur?: number }>,\n t: number,\n): number {\n let acc = 0;\n let cursor = 0; // integrated up to here\n let prevVal = dflt;\n let prevT = -Infinity;\n for (const k of deltas) {\n // Identical rampStart rule to evalPath — including a `dur` that reaches\n // back past the previous keyframe, where sampling starts mid-ease. The\n // eased span is therefore integrated in the ramp's ORIGINAL coordinates\n // (u measured from rampStart over D0), never renormalized to the span we\n // happen to integrate.\n const rampStart = k.dur != null ? k.t - k.dur : prevT === -Infinity ? k.t : prevT;\n const D0 = k.t - rampStart;\n const end = Math.min(t, k.t);\n // Constant stretch at prevVal, up to wherever the ramp (or t) begins.\n const constEnd = Math.min(Math.max(rampStart, cursor), end);\n if (constEnd > cursor) acc += prevVal * (constEnd - cursor);\n // Eased stretch: v(τ) = prevVal + (k.value − prevVal)·E((τ−rampStart)/D0).\n if (D0 > 0 && end > constEnd) {\n const ease = k.ease ?? defaultEase;\n const ua = (constEnd - rampStart) / D0;\n const ub = (end - rampStart) / D0;\n acc +=\n prevVal * (end - constEnd) +\n (k.value - prevVal) * D0 * (easeIntegral(ease, ub) - easeIntegral(ease, ua));\n }\n if (t <= k.t) return acc;\n cursor = k.t;\n prevVal = k.value;\n prevT = k.t;\n }\n return acc + prevVal * (t - cursor);\n}\n\n/**\n * Closed-form ∫₀ᵗ v(τ) dτ of one number param's track curve — the exact curve\n * `sampleTrack` emits for that path, integrated analytically segment by\n * segment (constant spans, plus linear/smooth/step ramps with `dur` and\n * previous-keyframe ramp starts handled identically to sampling).\n *\n * Why it exists: a saver that multiplies a rate by the *sampled* value scales\n * its entire accumulated motion when the param changes — a live steer\n * teleports everything proportionally to elapsed time. Integrating the curve\n * instead makes rate changes glide, while staying a pure function of\n * `(space, track, t)` — no accumulated state, so `renderFrame(t)` stays\n * frame-addressable and scrubbing works.\n *\n * Units: `t` is track time in ms; the result is value·ms (divide by 1000 for\n * value·seconds). Looping tracks integrate as\n * `fullLoops · ∫₀ᵈᵘʳ + ∫₀ʳᵉᵐ`. Non-`number` params integrate their numeric\n * default (or 0). Like `sampleTrack`, no min/max clamping is applied.\n */\nexport function integrateParam(\n space: ParamSpace,\n track: ControlTrack,\n path: string,\n t: number,\n bounds?: { min?: number; max?: number },\n): number {\n const def = space[path];\n if (!def) return 0;\n // Endpoint clamping bounds the WHOLE curve because every ease is a\n // monotone interpolation between keyframe values: if both endpoints are\n // in range, every eased point between them is too. (A ramp aimed at an\n // out-of-range value is compressed to the clamped endpoint rather than\n // clipped mid-flight — in range, deterministic, and the curve an\n // intake-validated track would have produced.)\n const clamp = (n: number): number => {\n if (bounds?.min !== undefined && n < bounds.min) return bounds.min;\n if (bounds?.max !== undefined && n > bounds.max) return bounds.max;\n return n;\n };\n const dflt = clamp(numericValue(def.default) ?? 0);\n if (def.type !== 'number' || t <= 0) return dflt * Math.max(0, t);\n const deltas: Array<{ t: number; value: number; ease?: Ease; dur?: number }> = [];\n for (const d of track.deltas) {\n if (d.path !== path) continue;\n const raw = numericValue(d.value);\n if (raw === null) continue;\n const value = clamp(raw);\n const entry: { t: number; value: number; ease?: Ease; dur?: number } = { t: d.t, value };\n if (d.ease !== undefined) entry.ease = d.ease;\n if (d.dur !== undefined) entry.dur = d.dur;\n deltas.push(entry);\n }\n deltas.sort((a, b) => a.t - b.t);\n const defaultEase = def.ease ?? 'linear';\n if (track.loop && track.duration && track.duration > 0) {\n const loops = Math.floor(t / track.duration);\n const rem = t - loops * track.duration;\n const oneLoop = integrateDeltas(dflt, defaultEase, deltas, track.duration);\n return loops * oneLoop + integrateDeltas(dflt, defaultEase, deltas, rem);\n }\n return integrateDeltas(dflt, defaultEase, deltas, t);\n}\n","import { signal, type ReadonlySignal } from './reactive';\n\n/**\n * Detects user inactivity. `idle` flips true once no activity for `timeoutMs`.\n * Framework-agnostic port of the Angular IdleService: 6 activity events + a\n * single `setTimeout` (not a poll) whose reschedule is throttled to >=500ms so\n * frequent pointermove events do not thrash timers.\n */\nexport class IdleDetector {\n private readonly _idle = signal(false);\n readonly idle: ReadonlySignal<boolean> = this._idle;\n\n private timer: ReturnType<typeof setTimeout> | null = null;\n private lastReset = 0;\n private running = false;\n\n private static readonly EVENTS = [\n 'pointerdown',\n 'pointermove',\n 'keydown',\n 'wheel',\n 'touchstart',\n 'scroll',\n ] as const;\n\n constructor(private readonly timeoutMs: number) {}\n\n private readonly onActivity = (): void => this.markActive();\n\n start(): void {\n if (this.running || typeof window === 'undefined') return;\n this.running = true;\n for (const e of IdleDetector.EVENTS) {\n window.addEventListener(e, this.onActivity, { passive: true });\n }\n this.schedule();\n }\n\n stop(): void {\n if (!this.running) return;\n this.running = false;\n for (const e of IdleDetector.EVENTS) {\n window.removeEventListener(e, this.onActivity);\n }\n this.clear();\n }\n\n /** Force an activity pulse (resets the countdown, wakes from idle). */\n markActive(): void {\n if (this._idle.value) this._idle.value = false;\n const now = typeof performance !== 'undefined' ? performance.now() : Date.now();\n if (now - this.lastReset < 500) return; // throttle reschedules\n this.lastReset = now;\n this.schedule();\n }\n\n private schedule(): void {\n this.clear();\n this.timer = setTimeout(() => {\n this._idle.value = true;\n }, this.timeoutMs);\n }\n\n private clear(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n}\n","import { computed, effect, signal, untracked, type ReadonlySignal } from './reactive';\nimport { IdleDetector } from './idle-detector';\nimport { createRng, type Rng } from './rng';\nimport {\n DEFAULT_CONFIG,\n resolveConfigMenu,\n type ConfigMenuConfig,\n type IdleScreensConfig,\n type SaverPlugin,\n} from './types';\n\nexport type EngineState = 'awake' | 'sleeping';\n\nconst hasWindow = typeof window !== 'undefined';\nconst hasDoc = typeof document !== 'undefined';\n\nfunction defaultStorage(): IdleScreensConfig['storage'] {\n return {\n get: (k) => {\n try {\n return hasWindow ? window.localStorage.getItem(k) : null;\n } catch {\n return null;\n }\n },\n set: (k, v) => {\n try {\n if (hasWindow) window.localStorage.setItem(k, v);\n } catch {\n /* private mode / disabled storage */\n }\n },\n };\n}\n\n/**\n * The framework-agnostic idle-screens state machine (port of the Angular\n * ScreensaverService). Owns: awake/sleeping state, plugin selection, the sleep\n * clock, reduced-motion + visibility, idle/blur triggers, and the\n * `window.__idleScreens` debug hook. It does NOT render; the `<idle-screen>`\n * element observes its signals.\n */\nexport class IdleScreensEngine {\n readonly config: IdleScreensConfig;\n private readonly plugins: SaverPlugin[];\n private readonly storage: NonNullable<IdleScreensConfig['storage']>;\n private readonly idle: IdleDetector;\n private readonly selectionRng: Rng;\n\n private readonly _state = signal<EngineState>('awake');\n readonly state: ReadonlySignal<EngineState> = this._state;\n readonly isSleeping = computed(() => this._state.value === 'sleeping');\n\n private readonly _activeId = signal('');\n readonly activePlugin = computed(\n () =>\n this.plugins.find((p) => p.manifest.id === this._activeId.value) ??\n this.plugins[0] ??\n null,\n );\n readonly activeIsPassthrough = computed(\n () => this.activePlugin.value?.manifest.passthrough ?? false,\n );\n\n private readonly _now = signal(new Date(0));\n readonly now: ReadonlySignal<Date> = this._now;\n private readonly _hidden = signal(false);\n readonly hidden: ReadonlySignal<boolean> = this._hidden;\n private readonly _reducedMotion = signal(false);\n readonly reducedMotion: ReadonlySignal<boolean> = this._reducedMotion;\n\n /** Resolved built-in config-menu options, or null when disabled. */\n readonly configMenu: Required<ConfigMenuConfig> | null;\n private readonly _configMenuOpen = signal(false);\n /** Whether the config menu is open. Drives the built-in `<idle-screen>` menu;\n * a host may also observe this to render its own. */\n readonly configMenuOpen: ReadonlySignal<boolean> = this._configMenuOpen;\n\n private clockTimer: ReturnType<typeof setInterval> | null = null;\n private started = false;\n private disposers: Array<() => void> = [];\n\n constructor(config: Partial<IdleScreensConfig>, plugins: SaverPlugin[]) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.plugins = plugins;\n this.configMenu = resolveConfigMenu(this.config.configMenu);\n this.storage = this.config.storage ?? defaultStorage()!;\n this.idle = new IdleDetector(this.config.timeoutMs);\n this.selectionRng = createRng(this.config.seed >>> 0);\n\n const stored = this.storage.get('idleScreens.plugin');\n const initialId =\n (stored && plugins.some((p) => p.manifest.id === stored) && stored) ||\n this.config.defaultPluginId ||\n plugins[0]?.manifest.id ||\n '';\n this._activeId.value = initialId;\n }\n\n get pluginList(): Array<{ id: string; label: string }> {\n return this.plugins.map((p) => ({ id: p.manifest.id, label: p.manifest.label }));\n }\n\n /** Look up a registered plugin (e.g. the configured crash saver). */\n pluginById(id: string): SaverPlugin | undefined {\n return this.plugins.find((p) => p.manifest.id === id);\n }\n\n /** Begin idle detection + listeners + debug hook. */\n init(): void {\n if (this.started || !hasWindow) return;\n this.started = true;\n\n if (hasDoc) {\n this._hidden.value = document.hidden;\n const onVis = (): void => {\n this._hidden.value = document.hidden;\n };\n document.addEventListener('visibilitychange', onVis);\n this.disposers.push(() => document.removeEventListener('visibilitychange', onVis));\n }\n\n const mq = hasWindow ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;\n if (mq) {\n this._reducedMotion.value = mq.matches;\n const onMq = (): void => {\n this._reducedMotion.value = mq.matches;\n };\n mq.addEventListener('change', onMq);\n this.disposers.push(() => mq.removeEventListener('change', onMq));\n }\n\n // Sleep when idle fires (unless suppressed). `sleep()` reads `state` in its\n // guard; wrap it in `untracked` so this effect depends ONLY on the idle\n // signal — otherwise flipping `state` to awake in wake() (while idle is still\n // latched true) would re-trigger this effect and immediately re-sleep.\n this.idle.start();\n this.disposers.push(\n effect(() => {\n if (this.idle.idle.value) untracked(() => this.sleep());\n }),\n );\n\n if (this.config.sleepOnBlur) {\n const onBlur = (): void => this.sleep();\n window.addEventListener('blur', onBlur);\n this.disposers.push(() => window.removeEventListener('blur', onBlur));\n }\n\n // Optional built-in config-menu hotkey (⌘K / Ctrl+K by default).\n if (this.configMenu) {\n const hotkey = this.configMenu.hotkey;\n const onKey = (e: KeyboardEvent): void => {\n if (hotkey(e)) {\n e.preventDefault();\n this.toggleConfigMenu();\n }\n };\n window.addEventListener('keydown', onKey);\n this.disposers.push(() => window.removeEventListener('keydown', onKey));\n }\n\n // Drive the clock only while asleep + visible.\n this.disposers.push(\n effect(() => {\n const ticking = this._state.value === 'sleeping' && !this._hidden.value;\n if (ticking && this.clockTimer === null) {\n this._now.value = new Date();\n this.clockTimer = setInterval(() => {\n this._now.value = new Date();\n }, 1000);\n } else if (!ticking && this.clockTimer !== null) {\n clearInterval(this.clockTimer);\n this.clockTimer = null;\n }\n }),\n );\n\n this.installDebugHook();\n }\n\n destroy(): void {\n this.idle.stop();\n if (this.clockTimer !== null) {\n clearInterval(this.clockTimer);\n this.clockTimer = null;\n }\n for (const d of this.disposers) d();\n this.disposers = [];\n this.started = false;\n }\n\n private suppressed(): boolean {\n if (this.config.disableOnLocalhost && hasWindow && /^(localhost|127\\.|\\[::1\\])/.test(location.hostname)) {\n return true;\n }\n const url = hasWindow ? location.pathname + location.search : '';\n return this.config.suppress ? this.config.suppress(url) : false;\n }\n\n sleep(): void {\n if (this._state.value === 'sleeping' || this.suppressed()) return;\n this.pickPlugin();\n this._state.value = 'sleeping';\n }\n\n /** Sleep ignoring suppression rules (debug / manual). */\n forceSleep(): void {\n if (this._state.value === 'sleeping') return;\n this.pickPlugin();\n this._state.value = 'sleeping';\n }\n\n wake(): void {\n if (this._state.value === 'awake') return;\n this._state.value = 'awake';\n this.idle.markActive();\n }\n\n toggle(): void {\n if (this.isSleeping.value) this.wake();\n else this.forceSleep();\n }\n\n openConfigMenu(): void {\n this._configMenuOpen.value = true;\n }\n closeConfigMenu(): void {\n this._configMenuOpen.value = false;\n }\n toggleConfigMenu(): void {\n this._configMenuOpen.value = !this._configMenuOpen.value;\n }\n\n setPlugin(id: string): void {\n if (!this.plugins.some((p) => p.manifest.id === id)) return;\n this._activeId.value = id;\n this.storage.set('idleScreens.plugin', id);\n }\n\n private pickPlugin(): void {\n if (this.plugins.length === 0) return;\n switch (this.config.selection) {\n case 'random': {\n const i = this.selectionRng.int(0, this.plugins.length - 1);\n this._activeId.value = this.plugins[i]!.manifest.id;\n break;\n }\n case 'rotate': {\n const cur = this.plugins.findIndex((p) => p.manifest.id === this._activeId.value);\n const next = this.plugins[(cur + 1) % this.plugins.length]!;\n this._activeId.value = next.manifest.id;\n break;\n }\n case 'fixed':\n default:\n // Keep the current (persisted / menu-chosen) plugin. `defaultPluginId`\n // is only the INITIAL pick (see constructor); once the user chooses a\n // saver via setPlugin/the menu it sticks across sleeps.\n break;\n }\n }\n\n private installDebugHook(): void {\n if (!hasWindow) return;\n (window as unknown as { __idleScreens?: unknown }).__idleScreens = {\n sleep: () => this.forceSleep(),\n wake: () => this.wake(),\n toggle: () => this.toggle(),\n setPlugin: (id: string) => this.setPlugin(id),\n openMenu: () => this.openConfigMenu(),\n closeMenu: () => this.closeConfigMenu(),\n toggleMenu: () => this.toggleConfigMenu(),\n state: () => this._state.value,\n menuOpen: () => this._configMenuOpen.value,\n active: () => this.activePlugin.value?.manifest.id ?? null,\n plugins: this.pluginList,\n };\n }\n}\n","/**\n * The floor of the graceful-degradation ladder: a zero-dependency DOM fault\n * screen the element shows when the active saver faults at runtime AND the\n * configured crash saver (config.crashSaverId, e.g. 'bsod') is unavailable\n * or faults too. The screen must stay a screen — dark, calm, flash-safe,\n * and honest about what happened. Any key still wakes.\n */\n\nexport interface FaultInfo {\n /** Id of the saver that faulted. */\n saverId: string;\n /** One error line, already stringified. Truncated for display. */\n message: string;\n}\n\nexport function renderFaultScreen(host: HTMLElement, info: FaultInfo): HTMLElement {\n const root = document.createElement('div');\n root.className = 'is-fault-screen';\n root.setAttribute('role', 'alert');\n root.setAttribute('aria-live', 'assertive');\n root.style.cssText = [\n 'position:absolute',\n 'inset:0',\n 'display:grid',\n 'place-items:center',\n 'background:#040610',\n 'color:#8fb4d8',\n 'font-family:ui-monospace,SFMono-Regular,Menlo,monospace',\n 'font-size:14px',\n 'line-height:1.7',\n 'text-align:left',\n ].join(';');\n\n const box = document.createElement('div');\n const msg = info.message.length > 160 ? `${info.message.slice(0, 157)}…` : info.message;\n const line = (text: string, style = ''): HTMLElement => {\n const el = document.createElement('div');\n el.textContent = text;\n if (style) el.style.cssText = style;\n return el;\n };\n box.append(\n line('IDLE_SCREENS :: SAVER_FAULT', 'color:#d8e8f8;letter-spacing:0.12em;margin-bottom:1em'),\n line(`The saver \"${info.saverId}\" stopped and was halted.`),\n line(msg, 'color:#5a7a9a;margin-top:0.5em;max-width:60ch;overflow-wrap:break-word'),\n line('The screen stays asleep. Press any key to wake.', 'color:#5a7a9a;margin-top:2em'),\n );\n root.append(box);\n host.append(root);\n return root;\n}\n","/**\n * Attribution for runtime faults while a saver owns the screen: is this\n * uncaught error / unhandled rejection plausibly the saver's, or foreign\n * noise (browser extension, third-party script) the crash ladder must ignore?\n *\n * The deliberate bias is toward engaging the ladder. While a non-passthrough\n * saver holds the fullscreen dialog, the saver is the only thing on screen —\n * a false positive swaps one working screen for the crash screen (still a\n * screen, one swap per sleep), while a false negative freezes a dead saver\n * black. Only sources that clearly cannot be the saver's code are filtered:\n * in production the saver compiles into an unmarked bundle chunk\n * (assets/index-<hash>.js), so origin — not the script's name — is the\n * trustworthy signal.\n */\n\n/** Script locations that name saver/engine code outright (dev servers, CDNs). */\nexport const SAVER_FAULT_LOC =\n /idle-screens|saver-|@idle-screens|three|metaquarium|schema|core|capabilities/i;\n\n/** Same-origin scripts are fault-eligible: app code and saver code are\n * indistinguishable once bundled. Opaque origins (\"null\", file://) compare\n * equal to themselves, which keeps file:// hosts (native wrappers) working. */\nfunction sameOrigin(url: string, origin: string): boolean {\n try {\n return new URL(url, origin === 'null' ? undefined : origin).origin === origin;\n } catch {\n return false;\n }\n}\n\n/**\n * ErrorEvent path. `filename` is the reporting script's URL — empty for\n * inline/synthetic throws (treated as ours), a chrome-extension:// or\n * third-party URL for foreign noise (skipped unless it names saver code).\n */\nexport function isSaverFaultFilename(filename: unknown, origin: string): boolean {\n if (typeof filename !== 'string' || filename.length === 0) return true;\n if (SAVER_FAULT_LOC.test(filename)) return true;\n return sameOrigin(filename, origin);\n}\n\n/**\n * PromiseRejectionEvent path. All we have is the reason's stack (or its\n * string form): no URL at all is inline/synthetic (ours); any same-origin\n * frame claims it; a hint made solely of foreign script URLs is noise.\n */\nexport function isSaverFaultRejectionHint(hint: string, origin: string): boolean {\n if (!hint) return true;\n if (SAVER_FAULT_LOC.test(hint)) return true;\n const urls = hint.match(/https?:\\/\\/[^\\s)]+/gi);\n if (!urls) return true;\n return urls.some((u) => sameOrigin(u, origin));\n}\n","import { effect } from './reactive';\nimport { IdleScreensEngine } from './engine';\nimport { createRng } from './rng';\nimport { renderFaultScreen } from './fault-screen';\nimport { isSaverFaultFilename, isSaverFaultRejectionHint } from './fault-attribution';\nimport type { IdleScreensConfig, PageContext, SaverInstance, SaverPlugin } from './types';\nimport type { WorkerInbound, WorkerOutbound } from './worker-protocol';\n\nconst STYLE = `\n :host { all: initial; }\n dialog.frame {\n position: fixed; inset: 0; width: 100%; height: 100%;\n max-width: none; max-height: none; margin: 0; border: 0; padding: 0;\n background: #0a0a0f; overflow: hidden; cursor: none; color: #fff;\n }\n dialog.frame[open] { animation: ss-in 0.5s ease; }\n dialog.frame.leaving { animation: ss-out 0.32s ease forwards; }\n dialog.frame.reduced { animation: none; }\n dialog.frame::backdrop { background: #0a0a0f; }\n dialog.frame.passthrough, dialog.frame.passthrough::backdrop { background: transparent; }\n @keyframes ss-in { from { opacity: 0; } to { opacity: 1; } }\n @keyframes ss-out { from { opacity: 1; } to { opacity: 0; } }\n .surface { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }\n .surface > canvas { display: block; width: 100%; height: 100%; }\n /* Host-owned fallback markup (<x slot=\"fallback\">), shown only when the active\n saver fails to mount — the host decides what a broken saver looks like. */\n .fallback { position: absolute; inset: 0; display: none; place-items: center; }\n dialog.frame.show-fallback .fallback { display: grid; }\n .clock {\n position: absolute; top: 10px; right: 12px; text-align: right;\n font-family: 'Orbitron', system-ui, sans-serif; pointer-events: none;\n }\n .clock .day { font-size: 34px; opacity: 0.9; }\n .clock .time { font-size: 54px; }\n .hint {\n position: fixed; bottom: 26px; left: 50%; transform: translateX(-50%);\n color: rgba(230,230,230,0.6); font: 12px ui-monospace, monospace;\n letter-spacing: 0.25em; text-transform: uppercase; opacity: 0;\n transition: opacity 1s ease; pointer-events: none;\n }\n .hint.show { opacity: 1; }\n\n /* Built-in config menu: a normal (interactive) centered dialog, distinct from\n the cursor-hidden saver frame. */\n dialog.menu {\n position: fixed; inset: 0; margin: auto; border: 0; padding: 0;\n width: min(420px, calc(100vw - 32px)); max-width: none;\n background: transparent; color: #f2f2f5;\n }\n dialog.menu::backdrop { background: rgba(6,6,10,0.5); backdrop-filter: blur(2px); }\n .menu-card {\n background: #14141b; border: 1px solid rgba(255,255,255,0.12);\n border-radius: 14px; padding: 20px; box-shadow: 0 24px 60px rgba(0,0,0,0.5);\n font-family: system-ui, sans-serif;\n }\n .menu-title { font-size: 18px; font-weight: 600; margin: 0 0 14px; }\n .menu-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }\n .menu-row > span { font-size: 14px; opacity: 0.85; }\n .menu-select {\n flex: 1; max-width: 60%; padding: 7px 10px; border-radius: 8px;\n background: #0d0d12; color: #f2f2f5; border: 1px solid rgba(255,255,255,0.16);\n font-size: 14px;\n }\n .menu-actions { display: flex; justify-content: flex-end; }\n .menu-close {\n padding: 7px 16px; border-radius: 8px; cursor: pointer; font-size: 14px;\n background: #2a2a37; color: #f2f2f5; border: 1px solid rgba(255,255,255,0.14);\n }\n .menu-close:hover { background: #34344a; }\n`;\n\n/** Node-safe base: during SSR/prerender there is no `HTMLElement`, and importing\n * this module (for the engine/types) must not crash. The element is only ever\n * constructed/registered in the browser. */\nconst HostBase: typeof HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n// Firefox shipped transferControlToOffscreen (~105) before module workers (~114).\n// The getter-probe detects {type:'module'} support without loading a real script.\nlet moduleWorkerDetected: boolean | undefined;\nfunction supportsModuleWorker(): boolean {\n if (moduleWorkerDetected !== undefined) return moduleWorkerDetected;\n moduleWorkerDetected = false;\n try {\n let probed = false;\n const url = URL.createObjectURL(new Blob([], { type: 'text/javascript' }));\n try {\n const w = new Worker(url, {\n get type() { probed = true; return 'module' as WorkerType; },\n } as WorkerOptions);\n w.terminate();\n } finally {\n URL.revokeObjectURL(url);\n }\n moduleWorkerDetected = probed;\n } catch {\n // ignore — browser doesn't support module workers\n }\n return moduleWorkerDetected;\n}\n\n/**\n * `<idle-screen>` — the framework-agnostic overlay (port of the Angular\n * ScreensaverComponent). Owns the top-layer `<dialog>`, mounts the active saver\n * into a canvas, shows the clock + \"press any key\" hint, fades in/out, wakes on\n * input with a 450ms arm-guard, and goes transparent for passthrough savers.\n *\n * Configure by setting the `.config` and `.plugins` properties (Angular can do\n * this via `[config]` / `[plugins]` with CUSTOM_ELEMENTS_SCHEMA).\n */\nexport class IdleScreenElement extends HostBase {\n private _engine: IdleScreensEngine | null = null;\n private ownsEngine = false;\n private _externalEngine: IdleScreensEngine | null = null;\n private _config: Partial<IdleScreensConfig> | null = null;\n private _plugins: SaverPlugin[] | null = null;\n\n private dialog!: HTMLDialogElement;\n private surface!: HTMLDivElement;\n private clockEl!: HTMLDivElement;\n private hintEl!: HTMLDivElement;\n private menuDialog: HTMLDialogElement | null = null;\n private menuSelect: HTMLSelectElement | null = null;\n private menuBuilt = false;\n\n private instance: SaverInstance | null = null;\n /** Runtime-fault state: one crash swap per sleep; reset on each mount. */\n private crashed = false;\n private activeSaverId = '';\n private worker: Worker | null = null;\n private cachedWorker: { worker: Worker; url: string } | null = null;\n private currentWorkerUrl: string | null = null;\n private cleanupWorkerHandlers: (() => void) | null = null;\n /** @internal Force the setTimeout-based rAF polyfill in Workers (for testing). */\n forceRafPolyfill = false;\n private mountToken = 0;\n private armed = false;\n private armTimer: ReturnType<typeof setTimeout> | null = null;\n private hintTimer: ReturnType<typeof setTimeout> | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private resizeTimer: ReturnType<typeof setTimeout> | null = null;\n private disposers: Array<() => void> = [];\n\n set config(c: Partial<IdleScreensConfig>) {\n this._config = c;\n this.maybeStart();\n }\n set plugins(p: SaverPlugin[]) {\n this._plugins = p;\n this.maybeStart();\n }\n /** Use an externally-owned, already-initialized engine (e.g. an Angular service\n * that also drives a config menu) instead of creating one from config+plugins. */\n set engine(e: IdleScreensEngine) {\n this._externalEngine = e;\n this.maybeStart();\n }\n /** The live engine (for host code / tests). */\n get idleEngine(): IdleScreensEngine | null {\n return this._engine;\n }\n /** Whether the currently mounted saver is running in a Web Worker. */\n get isWorker(): boolean {\n return this.worker !== null;\n }\n\n /** Request a pixel sample from the Worker to verify rendering.\n * Returns false if not in Worker mode or if the canvas has no content. */\n async sampleWorkerPixels(): Promise<boolean> {\n if (!this.worker) return false;\n const w = this.worker;\n return new Promise<boolean>((resolve) => {\n const handler = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type === 'sampled') {\n w.removeEventListener('message', handler);\n resolve(e.data.hasContent);\n }\n };\n w.addEventListener('message', handler);\n w.postMessage({ type: 'sample' } satisfies WorkerInbound);\n });\n }\n\n /**\n * The mounted saver's own account of its frame (`SaverInstance.inspect`) —\n * a convenience that forwards to whatever is mounted. Synchronous and\n * last-known: a main-thread saver answers live; a Worker-hosted saver\n * answers the most recent `inspectAsync()` (null until one has been made).\n * Null when nothing is mounted or the saver does not describe itself.\n */\n inspect(): Record<string, unknown> | null {\n try {\n return this.instance?.inspect?.() ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * `inspect()` with the round trip a Worker-hosted saver needs (2 s timeout,\n * null on timeout). Main-thread savers answer immediately with the same\n * value `inspect()` would.\n */\n async inspectAsync(): Promise<Record<string, unknown> | null> {\n const inst = this.instance as (SaverInstance & { inspectAsync?: () => Promise<Record<string, unknown> | null> }) | null;\n if (!inst) return null;\n try {\n if (typeof inst.inspectAsync === 'function') return await inst.inspectAsync();\n return inst.inspect?.() ?? null;\n } catch {\n return null;\n }\n }\n\n connectedCallback(): void {\n if (!this.shadowRoot) this.buildDom();\n this.maybeStart();\n }\n\n disconnectedCallback(): void {\n this.teardown();\n }\n\n private buildDom(): void {\n const root = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = STYLE;\n this.dialog = document.createElement('dialog');\n this.dialog.className = 'frame';\n this.surface = document.createElement('div');\n this.surface.className = 'surface';\n this.clockEl = document.createElement('div');\n this.clockEl.className = 'clock';\n this.hintEl = document.createElement('div');\n this.hintEl.className = 'hint';\n this.hintEl.textContent = 'press any key';\n // Host-owned fallback (AVAL-style): light-DOM children with slot=\"fallback\"\n // render here when a saver fails to mount, instead of a black screen.\n const fallback = document.createElement('div');\n fallback.className = 'fallback';\n const slot = document.createElement('slot');\n slot.name = 'fallback';\n fallback.append(slot);\n this.dialog.append(this.surface, fallback, this.clockEl, this.hintEl);\n root.append(style, this.dialog);\n\n for (const ev of ['pointerdown', 'pointermove', 'keydown', 'wheel', 'touchstart'] as const) {\n this.dialog.addEventListener(ev, () => this.onWakeInput(), { passive: true });\n }\n this.dialog.addEventListener('cancel', (e) => {\n e.preventDefault();\n this._engine?.wake();\n });\n }\n\n private maybeStart(): void {\n if (this._engine || !this.shadowRoot || typeof window === 'undefined') return;\n if (this._externalEngine) {\n this._engine = this._externalEngine; // caller owns lifecycle + init()\n this.ownsEngine = false;\n } else if (this._config && this._plugins) {\n this._engine = new IdleScreensEngine(this._config, this._plugins);\n this._engine.init();\n this.ownsEngine = true;\n } else {\n return;\n }\n const eng = this._engine;\n\n this.disposers.push(\n effect(() => {\n // Read reactive deps up front.\n const sleeping = eng.isSleeping.value;\n const active = eng.activePlugin.value;\n const passthrough = eng.activeIsPassthrough.value;\n if (sleeping && active) this.open(active, passthrough);\n else this.close();\n }),\n );\n\n // Clock chrome.\n this.disposers.push(\n effect(() => {\n const show = this._engine!.config.showClock && !eng.activeIsPassthrough.value;\n const now = eng.now.value;\n this.clockEl.style.display = show && eng.isSleeping.value ? 'block' : 'none';\n if (show) {\n const day = now.toLocaleDateString(undefined, {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n });\n const time = now.toLocaleTimeString();\n this.clockEl.innerHTML = `<div class=\"day\">${day}</div><div class=\"time\">${time}</div>`;\n }\n }),\n );\n\n // Optional built-in config menu (saver picker), opened via engine hotkey/API.\n if (eng.configMenu) {\n this.buildMenu(eng.configMenu);\n this.disposers.push(\n effect(() => {\n const open = eng.configMenuOpen.value;\n const d = this.menuDialog;\n if (!d) return;\n if (open && !d.open) {\n this.syncMenuSelection();\n d.showModal();\n } else if (!open && d.open) {\n d.close();\n }\n }),\n );\n // Keep the picker in sync if the active saver changes elsewhere.\n this.disposers.push(\n effect(() => {\n const id = eng.activePlugin.value?.manifest.id ?? '';\n if (this.menuSelect && this.menuSelect.value !== id) this.menuSelect.value = id;\n }),\n );\n }\n }\n\n private buildMenu(cfg: NonNullable<IdleScreensEngine['configMenu']>): void {\n if (this.menuBuilt || !this.shadowRoot) return;\n const eng = this._engine!;\n const d = document.createElement('dialog');\n d.className = 'menu';\n const card = document.createElement('div');\n card.className = 'menu-card';\n\n const title = document.createElement('div');\n title.className = 'menu-title';\n title.textContent = cfg.title;\n card.append(title);\n\n if (cfg.showPicker) {\n const row = document.createElement('label');\n row.className = 'menu-row';\n const label = document.createElement('span');\n label.textContent = 'Saver';\n const select = document.createElement('select');\n select.className = 'menu-select';\n for (const p of eng.pluginList) {\n const opt = document.createElement('option');\n opt.value = p.id;\n opt.textContent = p.label;\n select.append(opt);\n }\n select.addEventListener('change', () => {\n eng.setPlugin(select.value);\n if (cfg.previewOnPick) {\n // Close the menu first so the saver isn't stacked behind this modal,\n // then show the chosen saver right away.\n eng.closeConfigMenu();\n eng.forceSleep();\n }\n });\n row.append(label, select);\n card.append(row);\n this.menuSelect = select;\n }\n\n const actions = document.createElement('div');\n actions.className = 'menu-actions';\n const close = document.createElement('button');\n close.className = 'menu-close';\n close.type = 'button';\n close.textContent = 'Close';\n close.addEventListener('click', () => eng.closeConfigMenu());\n actions.append(close);\n card.append(actions);\n\n d.append(card);\n // Escape and backdrop click both close (without waking the saver).\n d.addEventListener('cancel', (e) => {\n e.preventDefault();\n eng.closeConfigMenu();\n });\n d.addEventListener('click', (e) => {\n if (e.target === d) eng.closeConfigMenu();\n });\n this.shadowRoot.append(d);\n this.menuDialog = d;\n this.menuBuilt = true;\n }\n\n private syncMenuSelection(): void {\n if (this.menuSelect) {\n this.menuSelect.value = this._engine?.activePlugin.value?.manifest.id ?? '';\n }\n }\n\n private pageContext(): PageContext {\n return {\n palette: () => [],\n victims: (selector: string) =>\n typeof document !== 'undefined'\n ? Array.from(document.querySelectorAll<HTMLElement>(selector))\n : [],\n };\n }\n\n private async open(plugin: SaverPlugin, passthrough: boolean, skipWorker = false): Promise<void> {\n const eng = this._engine!;\n this.dialog.classList.toggle('passthrough', passthrough);\n this.dialog.classList.toggle('reduced', eng.reducedMotion.value);\n this.crashed = false;\n this.activeSaverId = plugin.manifest.id;\n if (!this.dialog.open) {\n this.dialog.classList.remove('leaving');\n this.dialog.showModal();\n this.arm();\n this.scheduleHint();\n window.addEventListener('resize', this.onResize);\n // Runtime crash guard: an uncaught error while a NON-passthrough saver\n // owns the screen is, in practice, the saver's render loop dying (a GL\n // crash, a bad frame). Passthrough savers share the page with the\n // site's own scripts, so attribution there is unsafe — skip them.\n window.addEventListener('error', this.onRuntimeError);\n window.addEventListener('unhandledrejection', this.onRuntimeRejection);\n }\n // (Re)mount the active saver.\n const token = ++this.mountToken;\n await this.disposeInstance();\n const w = window.innerWidth;\n const h = window.innerHeight;\n const seed = eng.config.seed >>> 0;\n const dpr = typeof devicePixelRatio === 'number' ? devicePixelRatio : 1;\n\n const workerEligible = !skipWorker\n && plugin.manifest.workerReady\n && eng.config.workerUrl\n && typeof HTMLCanvasElement.prototype.transferControlToOffscreen === 'function'\n && supportsModuleWorker();\n\n if (workerEligible) {\n try {\n const inst = await this.openInWorker(plugin, eng.config.workerUrl!, w, h, seed, dpr, eng.reducedMotion.value, passthrough);\n if (token !== this.mountToken) {\n inst.dispose();\n return;\n }\n this.instance = inst;\n inst.setPaused(eng.reducedMotion.value);\n this.dialog.classList.remove('show-fallback');\n return;\n } catch {\n // Worker failed — fall through to main-thread mount\n await this.disposeInstance();\n if (token !== this.mountToken) return;\n }\n }\n try {\n const inst = await plugin.mount({\n host: this.surface,\n dpr,\n width: w,\n height: h,\n rng: createRng(seed),\n seed,\n reducedMotion: eng.reducedMotion.value,\n page: plugin.manifest.passthrough ? this.pageContext() : undefined,\n });\n if (token !== this.mountToken) {\n inst.dispose();\n return;\n }\n this.instance = inst;\n inst.setPaused(eng.reducedMotion.value);\n this.dialog.classList.remove('show-fallback');\n } catch (err) {\n // Saver failed to mount — show the host's fallback slot instead of a\n // black screen. The host owns what \"broken\" looks like.\n if (token !== this.mountToken) return;\n console.warn(`[idle-screen] saver \"${plugin.manifest.id}\" failed to mount:`, err);\n this.surface.replaceChildren();\n this.dialog.classList.add('show-fallback');\n }\n }\n\n private openInWorker(\n plugin: SaverPlugin,\n workerUrl: string,\n width: number,\n height: number,\n seed: number,\n dpr: number,\n reducedMotion: boolean,\n passthrough: boolean,\n ): Promise<SaverInstance> {\n const canvas = document.createElement('canvas');\n canvas.style.cssText = 'display:block;width:100%;height:100%';\n this.surface.appendChild(canvas);\n\n const offscreen = canvas.transferControlToOffscreen();\n\n // Reuse cached worker if URL matches, otherwise create new\n let worker: Worker;\n if (this.cachedWorker && this.cachedWorker.url === workerUrl) {\n worker = this.cachedWorker.worker;\n this.cachedWorker = null;\n } else {\n this.terminateCachedWorker();\n worker = new Worker(workerUrl, { type: 'module' });\n }\n this.worker = worker;\n this.currentWorkerUrl = workerUrl;\n\n const mountMsg: WorkerInbound = plugin.spec\n ? {\n type: 'mount-spec',\n canvas: offscreen,\n spec: plugin.spec,\n width, height, seed, dpr, reducedMotion,\n ...(this.forceRafPolyfill ? { forceRafPolyfill: true } : {}),\n }\n : {\n type: 'mount',\n canvas: offscreen,\n saverId: plugin.manifest.id,\n width, height, seed, dpr, reducedMotion,\n ...(this.forceRafPolyfill ? { forceRafPolyfill: true } : {}),\n };\n worker.postMessage(mountMsg, [offscreen]);\n\n let disposed = false;\n\n // Capture round-trips are correlated by id: nothing stops a second call\n // before the first answers, and an uncorrelated FIFO would hand caller A\n // caller B's frame. Answers ride the same message listener the proxy\n // installs below; a 2s timeout keeps a dead worker from parking a caller\n // forever (resolving null matches the contract: \"nothing to show yet\").\n let captureSeq = 0;\n const pendingCaptures = new Map<number, (bitmap: ImageBitmap | null) => void>();\n const onCaptured = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type !== 'captured') return;\n const settle = pendingCaptures.get(e.data.id);\n if (settle) {\n pendingCaptures.delete(e.data.id);\n settle(e.data.bitmap);\n }\n };\n worker.addEventListener('message', onCaptured);\n let inspectSeq = 0;\n const pendingInspects = new Map<number, (state: Record<string, unknown> | null) => void>();\n const onInspected = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type !== 'inspected') return;\n const settle = pendingInspects.get(e.data.id);\n if (settle) {\n pendingInspects.delete(e.data.id);\n settle(e.data.state);\n }\n };\n worker.addEventListener('message', onInspected);\n\n let lastInspect: Record<string, unknown> | null = null;\n const proxy: SaverInstance & { inspectAsync(): Promise<Record<string, unknown> | null> } = {\n setPaused: (p) => worker.postMessage({ type: 'pause', paused: p } satisfies WorkerInbound),\n resize: (w, h, newDpr) => worker.postMessage({ type: 'resize', width: w, height: h, dpr: newDpr ?? dpr } satisfies WorkerInbound),\n applyTrack: (track) => worker.postMessage({ type: 'track', track } satisfies WorkerInbound),\n capture: () =>\n new Promise<ImageBitmap | null>((resolve) => {\n if (disposed) {\n resolve(null);\n return;\n }\n const id = ++captureSeq;\n const timer = setTimeout(() => {\n pendingCaptures.delete(id);\n resolve(null);\n }, 2000);\n pendingCaptures.set(id, (bitmap) => {\n clearTimeout(timer);\n resolve(bitmap);\n });\n worker.postMessage({ type: 'capture', id } satisfies WorkerInbound);\n }),\n // The proxy's inspect is async under the hood (a worker round trip);\n // the SaverInstance contract is synchronous, so expose the promise\n // form as `inspectAsync` and answer the last state synchronously.\n inspect: () => lastInspect,\n inspectAsync: () =>\n new Promise<Record<string, unknown> | null>((resolve) => {\n if (disposed) {\n resolve(null);\n return;\n }\n const id = ++inspectSeq;\n const timer = setTimeout(() => {\n pendingInspects.delete(id);\n resolve(null);\n }, 2000);\n pendingInspects.set(id, (state) => {\n clearTimeout(timer);\n lastInspect = state;\n resolve(state);\n });\n worker.postMessage({ type: 'inspect', id } satisfies WorkerInbound);\n }),\n dispose: () => {\n disposed = true;\n worker.removeEventListener('message', onCaptured);\n worker.removeEventListener('message', onInspected);\n for (const settle of pendingCaptures.values()) settle(null);\n pendingCaptures.clear();\n for (const settle of pendingInspects.values()) settle(null);\n pendingInspects.clear();\n this.cleanupWorkerHandlers?.();\n this.cleanupWorkerHandlers = null;\n worker.postMessage({ type: 'dispose' } satisfies WorkerInbound);\n worker.terminate();\n this.worker = null;\n canvas.remove();\n },\n };\n\n return new Promise<SaverInstance>((resolve, reject) => {\n const onMessage = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type === 'mounted') {\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n\n // Post-mount crash recovery: if the Worker errors mid-animation,\n // fall back to main-thread rendering.\n const onCrash = (ev: Event): void => {\n if (disposed) return;\n disposed = true;\n const msg = ev instanceof ErrorEvent ? ev.message : 'Worker messageerror';\n console.warn(`[idle-screen] Worker crashed mid-animation: ${msg}`);\n this.cleanupWorkerHandlers?.();\n this.cleanupWorkerHandlers = null;\n worker.terminate();\n this.worker = null;\n this.instance = null;\n this.surface?.replaceChildren();\n void this.open(plugin, passthrough, true);\n };\n worker.addEventListener('error', onCrash);\n worker.addEventListener('messageerror', onCrash);\n this.cleanupWorkerHandlers = () => {\n worker.removeEventListener('error', onCrash);\n worker.removeEventListener('messageerror', onCrash);\n // disposeInstance() caches the Worker for reuse WITHOUT calling\n // proxy.dispose(), so the capture/inspect listeners and any\n // in-flight requests must be cleared here too — or every remount\n // stacks another listener on the cached Worker and a pending\n // inspect settles from a later mount's answer.\n worker.removeEventListener('message', onCaptured);\n worker.removeEventListener('message', onInspected);\n for (const settle of pendingCaptures.values()) settle(null);\n pendingCaptures.clear();\n for (const settle of pendingInspects.values()) settle(null);\n pendingInspects.clear();\n };\n\n resolve(proxy);\n } else if (e.data.type === 'error') {\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n reject(new Error(e.data.message));\n }\n };\n const onError = (e: ErrorEvent): void => {\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n reject(new Error(e.message));\n };\n worker.addEventListener('message', onMessage);\n worker.addEventListener('error', onError);\n });\n }\n\n private close(): void {\n if (!this.dialog?.open) return;\n window.removeEventListener('resize', this.onResize);\n window.removeEventListener('error', this.onRuntimeError);\n window.removeEventListener('unhandledrejection', this.onRuntimeRejection);\n this.crashed = false;\n if (this.resizeTimer) { clearTimeout(this.resizeTimer); this.resizeTimer = null; }\n this.dialog.classList.remove('show-fallback');\n this.hintEl.classList.remove('show');\n if (this.hintTimer) clearTimeout(this.hintTimer);\n void this.disposeInstance();\n const reduced = this._engine?.reducedMotion.value;\n if (reduced) {\n this.dialog.close();\n return;\n }\n this.dialog.classList.add('leaving');\n if (this.closeTimer) clearTimeout(this.closeTimer);\n this.closeTimer = setTimeout(() => {\n if (this.dialog.open) this.dialog.close();\n this.dialog.classList.remove('leaving');\n }, 320);\n }\n\n private async disposeInstance(): Promise<void> {\n this.cleanupWorkerHandlers?.();\n this.cleanupWorkerHandlers = null;\n\n if (this.instance) {\n if (this.worker) {\n // Worker instance: send dispose but cache the worker for reuse\n this.worker.postMessage({ type: 'dispose' } satisfies WorkerInbound);\n this.cachedWorker = { worker: this.worker, url: this.currentWorkerUrl! };\n this.worker = null;\n } else {\n this.instance.dispose();\n }\n this.instance = null;\n } else if (this.worker) {\n this.worker.terminate();\n this.worker = null;\n }\n this.surface?.replaceChildren();\n }\n\n private terminateCachedWorker(): void {\n if (this.cachedWorker) {\n this.cachedWorker.worker.terminate();\n this.cachedWorker = null;\n }\n }\n\n private arm(): void {\n this.armed = false;\n if (this.armTimer) clearTimeout(this.armTimer);\n this.armTimer = setTimeout(() => {\n this.armed = true;\n }, 450);\n }\n\n private scheduleHint(): void {\n if (this.hintTimer) clearTimeout(this.hintTimer);\n this.hintEl.classList.remove('show');\n this.hintTimer = setTimeout(() => this.hintEl.classList.add('show'), 3000);\n }\n\n private readonly onResize = (): void => {\n if (this.resizeTimer !== null) clearTimeout(this.resizeTimer);\n this.resizeTimer = setTimeout(() => {\n this.resizeTimer = null;\n if (!this.instance || !this.dialog?.open) return;\n const newDpr = typeof devicePixelRatio === 'number' ? devicePixelRatio : 1;\n try {\n this.instance.resize(window.innerWidth, window.innerHeight, newDpr);\n } catch (err) {\n this.engageCrashScreen(err);\n }\n }, 150);\n };\n\n /** An uncaught error while a non-passthrough saver owns the screen is the\n * saver's loop dying. Swap to the crash saver instead of freezing black.\n * Attribution semantics live in fault-attribution.ts (unit-tested). */\n private isSaverRelatedRuntimeFault(ev: ErrorEvent): boolean {\n return isSaverFaultFilename(ev.filename, location.origin);\n }\n\n private isSaverRelatedRejection(ev: PromiseRejectionEvent): boolean {\n const hint = String((ev.reason as Error)?.stack ?? ev.reason ?? '');\n return isSaverFaultRejectionHint(hint, location.origin);\n }\n\n private readonly onRuntimeError = (ev: ErrorEvent): void => {\n if (!this.dialog?.open || !this.instance) return;\n if (this._engine?.pluginById(this.activeSaverId)?.manifest.passthrough) return; // page noise, not ours\n if (!this.isSaverRelatedRuntimeFault(ev)) return;\n this.engageCrashScreen(ev.error ?? ev.message);\n };\n\n private readonly onRuntimeRejection = (ev: PromiseRejectionEvent): void => {\n if (!this.dialog?.open || !this.instance) return;\n if (this._engine?.pluginById(this.activeSaverId)?.manifest.passthrough) return;\n if (!this.isSaverRelatedRejection(ev)) return;\n this.engageCrashScreen(ev.reason);\n };\n\n /**\n * The graceful-degradation ladder for runtime saver faults. The screen must\n * stay a screen: dispose the faulted instance, then mount the configured\n * crash saver (config.crashSaverId — the BSOD, fittingly), and if that is\n * missing, IS the faulted saver, or faults too, render the built-in DOM\n * fault screen. One swap per sleep; any key still wakes.\n */\n private engageCrashScreen(err: unknown): void {\n if (!this.dialog?.open) return;\n const faultedId = this.activeSaverId;\n const message = err instanceof Error ? err.message : String(err ?? 'unknown fault');\n\n if (this.crashed) {\n console.warn(`[idle-screen] saver \"${faultedId}\" faulted again at runtime:`, err);\n ++this.mountToken;\n try {\n this.instance?.dispose();\n } catch {\n /* the faulted instance may not even dispose cleanly */\n }\n this.instance = null;\n this.surface.replaceChildren();\n renderFaultScreen(this.surface, { saverId: faultedId, message });\n return;\n }\n\n this.crashed = true;\n console.warn(`[idle-screen] saver \"${faultedId}\" faulted at runtime:`, err);\n ++this.mountToken; // invalidate any in-flight mount\n try {\n this.instance?.dispose();\n } catch {\n /* the faulted instance may not even dispose cleanly */\n }\n this.instance = null;\n this.surface.replaceChildren();\n\n const eng = this._engine;\n const crashId = eng?.config.crashSaverId;\n const crashPlugin = crashId && crashId !== faultedId ? eng?.pluginById(crashId) : undefined;\n if (crashPlugin && eng) {\n const token = this.mountToken;\n void Promise.resolve()\n .then(() =>\n crashPlugin.mount({\n host: this.surface,\n dpr: typeof devicePixelRatio === 'number' ? devicePixelRatio : 1,\n width: window.innerWidth,\n height: window.innerHeight,\n rng: createRng(eng.config.seed >>> 0),\n seed: eng.config.seed >>> 0,\n reducedMotion: eng.reducedMotion.value,\n page: crashPlugin.manifest.passthrough ? this.pageContext() : undefined,\n }),\n )\n .then((inst) => {\n if (token !== this.mountToken || !this.dialog?.open) {\n inst.dispose();\n return;\n }\n this.instance = inst;\n inst.setPaused(eng.reducedMotion.value);\n })\n .catch(() => {\n if (token !== this.mountToken || !this.dialog?.open) return;\n this.surface.replaceChildren();\n renderFaultScreen(this.surface, { saverId: faultedId, message });\n });\n } else {\n renderFaultScreen(this.surface, { saverId: faultedId, message });\n }\n }\n\n private onWakeInput(): void {\n if (!this.armed) return; // grace period after sleeping\n this._engine?.wake();\n }\n\n private teardown(): void {\n window.removeEventListener('resize', this.onResize);\n window.removeEventListener('error', this.onRuntimeError);\n window.removeEventListener('unhandledrejection', this.onRuntimeRejection);\n if (this.resizeTimer) { clearTimeout(this.resizeTimer); this.resizeTimer = null; }\n void this.disposeInstance();\n this.terminateCachedWorker();\n if (this.armTimer) clearTimeout(this.armTimer);\n if (this.hintTimer) clearTimeout(this.hintTimer);\n if (this.closeTimer) clearTimeout(this.closeTimer);\n for (const d of this.disposers) d();\n this.disposers = [];\n if (this.ownsEngine) this._engine?.destroy(); // external engines are the caller's to destroy\n this._engine = null;\n }\n}\n\n/** Register the <idle-screen> custom element (idempotent). */\nexport function defineIdleScreen(tag = 'idle-screen'): void {\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\n customElements.define(tag, IdleScreenElement);\n }\n}\n","import { createRng } from './rng';\nimport type { ControlTrack, SaverContext, SaverInstance, SaverPlugin } from './types';\n\n/** Main thread → Worker messages. */\nexport type WorkerInbound =\n | {\n type: 'mount';\n canvas: OffscreenCanvas;\n saverId: string;\n width: number;\n height: number;\n seed: number;\n dpr: number;\n reducedMotion: boolean;\n /** Display refresh rate (Hz) for the rAF polyfill on browsers that lack\n * Worker requestAnimationFrame (Safari). Defaults to 60. */\n refreshRate?: number;\n /** Force the setTimeout-based rAF polyfill even when native rAF exists.\n * Used by tests to verify the polyfill drives frames. */\n forceRafPolyfill?: boolean;\n }\n | {\n type: 'mount-spec';\n canvas: OffscreenCanvas;\n spec: unknown;\n width: number;\n height: number;\n seed: number;\n dpr: number;\n reducedMotion: boolean;\n refreshRate?: number;\n forceRafPolyfill?: boolean;\n }\n | { type: 'resize'; width: number; height: number; dpr: number }\n | { type: 'pause'; paused: boolean }\n | { type: 'track'; track: ControlTrack }\n | { type: 'sample' }\n | { type: 'capture'; id: number }\n | { type: 'inspect'; id: number }\n | { type: 'dispose' };\n\n/** Worker → main thread messages. */\nexport type WorkerOutbound =\n | { type: 'mounted' }\n | { type: 'sampled'; hasContent: boolean }\n | { type: 'captured'; id: number; bitmap: ImageBitmap | null }\n | { type: 'inspected'; id: number; state: Record<string, unknown> | null }\n | { type: 'error'; message: string };\n\n/** Options for {@link runIdleWorker}. */\nexport interface RunIdleWorkerOpts {\n /** Spec compiler for `mount-spec` messages. Pass `compileSaver` from\n * `@idle-screens/schema` to enable schema-based savers in this Worker. */\n compiler?: (spec: unknown) => SaverPlugin;\n}\n\n/**\n * Run inside a dedicated Worker. Accepts a registry of worker-ready saver plugins\n * and listens for lifecycle messages from the main thread.\n *\n * Usage (in the Worker entry file):\n * ```ts\n * import { runIdleWorker } from '@idle-screens/core';\n * import { warp } from './warp';\n * runIdleWorker({ warp });\n * ```\n */\nexport function runIdleWorker(\n registry: Record<string, SaverPlugin>,\n opts?: RunIdleWorkerOpts,\n): void {\n let instance: SaverInstance | null = null;\n let activeCanvas: OffscreenCanvas | null = null;\n let usingPolyfill = false;\n let mountGen = 0;\n\n const post = (msg: WorkerOutbound): void => {\n self.postMessage(msg);\n };\n\n const installRafPolyfill = (refreshRate?: number): void => {\n const g = globalThis as Record<string, unknown>;\n const interval = refreshRate ? Math.round(1000 / refreshRate) : 16;\n g.requestAnimationFrame = (cb: FrameRequestCallback) =>\n setTimeout(() => cb(performance.now()), interval) as unknown as number;\n g.cancelAnimationFrame = (id: number) => clearTimeout(id);\n usingPolyfill = true;\n };\n\n if (typeof requestAnimationFrame === 'undefined') {\n installRafPolyfill();\n }\n\n const mountPlugin = (\n plugin: SaverPlugin,\n canvas: OffscreenCanvas,\n msg: {\n width: number; height: number; seed: number;\n dpr: number; reducedMotion: boolean;\n refreshRate?: number; forceRafPolyfill?: boolean;\n },\n gen: number,\n ): void => {\n if (msg.forceRafPolyfill) {\n installRafPolyfill(msg.refreshRate);\n } else if (usingPolyfill && msg.refreshRate) {\n installRafPolyfill(msg.refreshRate);\n }\n\n activeCanvas = canvas;\n const hostStub = new Proxy({} as HTMLElement, {\n get(_target, prop) {\n throw new Error(\n `Saver accessed host.${String(prop)} in a Worker — ` +\n `Workers have no DOM. Use ctx.surface instead of creating your own canvas.`,\n );\n },\n });\n const ctx: SaverContext = {\n host: hostStub,\n surface: canvas,\n dpr: msg.dpr,\n width: msg.width,\n height: msg.height,\n rng: createRng(msg.seed),\n seed: msg.seed,\n reducedMotion: msg.reducedMotion,\n };\n try {\n const result = plugin.mount(ctx);\n if (result instanceof Promise) {\n result.then((inst) => {\n if (gen !== mountGen) {\n inst.dispose();\n return;\n }\n instance = inst;\n post({ type: 'mounted' });\n }).catch((err: unknown) => {\n if (gen !== mountGen) return;\n post({ type: 'error', message: String(err) });\n });\n } else {\n if (gen !== mountGen) {\n result.dispose();\n return;\n }\n instance = result;\n post({ type: 'mounted' });\n }\n } catch (err: unknown) {\n if (gen !== mountGen) return;\n post({ type: 'error', message: String(err) });\n }\n };\n\n self.addEventListener('message', (raw: MessageEvent<WorkerInbound>) => {\n const msg = raw.data;\n switch (msg.type) {\n case 'mount': {\n const plugin = registry[msg.saverId];\n if (!plugin) {\n post({ type: 'error', message: `unknown saver: ${msg.saverId}` });\n return;\n }\n instance?.dispose();\n instance = null;\n const gen = ++mountGen;\n mountPlugin(plugin, msg.canvas, msg, gen);\n break;\n }\n case 'mount-spec': {\n if (!opts?.compiler) {\n post({ type: 'error', message: 'no compiler provided for mount-spec' });\n return;\n }\n try {\n const plugin = opts.compiler(msg.spec);\n instance?.dispose();\n instance = null;\n const gen = ++mountGen;\n mountPlugin(plugin, msg.canvas, msg, gen);\n } catch (err: unknown) {\n post({ type: 'error', message: String(err) });\n }\n break;\n }\n case 'resize':\n instance?.resize(msg.width, msg.height, msg.dpr);\n break;\n case 'pause':\n instance?.setPaused(msg.paused);\n break;\n case 'track':\n instance?.applyTrack?.(msg.track);\n break;\n case 'sample': {\n if (!activeCanvas) {\n post({ type: 'sampled', hasContent: false });\n return;\n }\n const c2d = activeCanvas.getContext('2d');\n if (!c2d) {\n post({ type: 'sampled', hasContent: false });\n return;\n }\n const cw = activeCanvas.width;\n const ch = activeCanvas.height;\n let hasContent = false;\n const COLS = 10;\n const ROWS = 10;\n for (let r = 0; r < ROWS && !hasContent; r++) {\n const y = Math.floor((ch * (r + 1)) / (ROWS + 1));\n for (let c = 0; c < COLS && !hasContent; c++) {\n const x = Math.floor((cw * (c + 1)) / (COLS + 1));\n const data = c2d.getImageData(x, y, 1, 1).data;\n if (data[0]! > 0 || data[1]! > 0 || data[2]! > 0 || data[3]! > 0) hasContent = true;\n }\n }\n post({ type: 'sampled', hasContent });\n break;\n }\n case 'inspect': {\n // State dump for perception (the worker owns the instance, so this\n // is the only read path). Synchronous by contract; a throwing saver\n // answers null rather than killing the worker.\n let state: Record<string, unknown> | null = null;\n try {\n state = instance?.inspect?.() ?? null;\n } catch {\n state = null;\n }\n post({ type: 'inspected', id: msg.id, state });\n break;\n }\n case 'capture': {\n // On-demand frame snapshot (the transferred canvas is unreadable from\n // the main thread, so this is the ONLY read path for worker savers).\n // Prefer the instance's own capture — it can render a fresh frame in\n // the same task, which matters for WebGL where the presented buffer\n // reads black. Plain-2d savers fall back to a direct bitmap of the\n // OffscreenCanvas, whose buffer persists between frames.\n const id = msg.id;\n const reply = (bitmap: ImageBitmap | null): void => {\n if (bitmap) {\n // Options form: valid on both the Window typing tsc sees here and\n // the DedicatedWorkerGlobalScope this actually runs in.\n self.postMessage({ type: 'captured', id, bitmap } satisfies WorkerOutbound, { transfer: [bitmap] });\n } else {\n post({ type: 'captured', id, bitmap: null });\n }\n };\n const own = instance?.capture?.();\n if (own) {\n own.then(reply, () => reply(null));\n } else if (activeCanvas) {\n createImageBitmap(activeCanvas).then(reply, () => reply(null));\n } else {\n reply(null);\n }\n break;\n }\n case 'dispose':\n mountGen++;\n instance?.dispose();\n instance = null;\n activeCanvas = null;\n break;\n }\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/types.ts","../src/rng.ts","../src/reactive.ts","../src/control-track.ts","../src/idle-detector.ts","../src/engine.ts","../src/fault-screen.ts","../src/fault-attribution.ts","../src/idle-screen.element.ts","../src/worker-protocol.ts"],"sourcesContent":["import type { Rng } from './rng';\n\n/** How a parameter interpolates between control-track deltas. */\nexport type Ease = 'step' | 'linear' | 'smooth';\n\nexport type ParamType = 'number' | 'color' | 'bool' | 'enum' | 'string';\nexport type ParamValue = number | string | boolean;\n\n/** One typed, ranged, interpolatable knob: an agent's steering surface. */\nexport interface ParamDef {\n type: ParamType;\n default: ParamValue;\n min?: number;\n max?: number;\n options?: string[];\n /** Default interpolation when a delta targets this knob. */\n ease?: Ease;\n}\n\nexport type ParamSpace = Record<string, ParamDef>;\n\n/** A sparse, timestamped, interpolatable parameter change. Agents stream THESE\n * (over AG-UI state-patches / MCP resource updates), never frames. */\nexport interface ParamDelta {\n /** ms from track start. */\n t: number;\n /** key in the saver's paramSpace. */\n path: string;\n value: ParamValue;\n ease?: Ease;\n /** ramp duration ms from the previous value (0 = instant). */\n dur?: number;\n}\n\n/** A recorded or live steering performance for a seeded program. */\nexport interface ControlTrack {\n program: string;\n seed: number;\n duration?: number;\n loop?: boolean;\n deltas: ParamDelta[];\n}\n\n/** Page-context seam: passthrough savers read/eat the live page through this,\n * rather than querying `document` directly. */\nexport interface PageContext {\n /** Sample dominant colors of the page (for palette-reactive savers). */\n palette(): string[];\n /** Visible page blocks a passthrough saver may transform (\"victims\"). */\n victims(selector: string): HTMLElement[];\n}\n\n/** What the host hands a saver when it mounts. */\nexport interface SaverContext {\n /** A full-viewport container the saver renders into. Canvas savers create a\n * canvas inside it; DOM savers (toasters, DVD, messages) append elements. The\n * host is cleared by the runtime on dispose. */\n host: HTMLElement;\n /** Pre-created rendering surface. When present, canvas savers should use this\n * instead of creating their own canvas. In a Worker this is an OffscreenCanvas. */\n surface?: HTMLCanvasElement | OffscreenCanvas;\n /** Device pixel ratio — use instead of reading window.devicePixelRatio. */\n dpr: number;\n width: number;\n height: number;\n /** Seeded PRNG. Use this, never Math.random(), for determinism. */\n rng: Rng;\n seed: number;\n /** Initial paramSpace overrides for this mount (e.g. a channel's published\n * `{id, params}` scene). Savers treat these as the new defaults; live\n * steering via applyTrack still layers on top. Optional. */\n params?: Record<string, ParamValue>;\n reducedMotion: boolean;\n /** Present for passthrough savers (over the live page). */\n page?: PageContext;\n}\n\n/**\n * One deck of a mounted saver's practical composition stack, bottom-up.\n *\n * This is deliberately NOT the schema's layer concept (declared sprite\n * layers): it describes what a running instance is actually composed of, so\n * hosts and tooling can present and inspect the stack uniformly. A\n * passthrough saver is at minimum `page` + `surface`; a classic canvas saver\n * is just its `surface`; savers may further describe the logical draw passes\n * inside a surface. The model is intentionally small and open — future work\n * (multi-surface savers, per-pass toggles, cross-saver scenes) extends it\n * rather than replacing it.\n */\nexport interface SaverLayer {\n id: string;\n label: string;\n /** 'page' = the live document a passthrough saver performs on;\n * 'surface' = a rendering surface the instance owns (usually a canvas);\n * 'pass' = a logical draw pass inside the surface above it. */\n kind: 'page' | 'surface' | 'pass';\n /** The element that IS this layer, when one exists. Hosts may toggle its\n * visibility for inspection. A `page` layer has no element — the HOST\n * owns the document and binds it (the saver only borrows it). */\n el?: HTMLElement;\n description?: string;\n}\n\nexport interface SaverInstance {\n /** Freeze/unfreeze the render loop (paused, reduced-motion, or screensaver end). */\n setPaused(paused: boolean): void;\n resize(width: number, height: number, dpr?: number): void;\n /** Deterministic, frame-addressable render: draw the state at logical time `t`\n * (ms) for `seed`. Pure w.r.t. (program, seed, applied track, t). Optional. */\n renderFrame?(t: number, seed: number): void;\n /** Snapshot the CURRENT frame as an ImageBitmap, rendering one on demand if\n * the loop is throttled (hidden tab). Exists because page JS cannot read\n * this frame from outside: a worker-transferred canvas is opaque to the\n * main thread, and a non-preserveDrawingBuffer WebGL canvas reads black\n * outside the render tick. Resolve null for \"nothing to show yet\".\n * Optional. */\n capture?(): Promise<ImageBitmap | null>;\n /** Describe the CURRENT state in words and numbers — what a pixel capture\n * cannot say: which objects exist, where they are, what behaviour is in\n * flight. Plain JSON, small (a few KB), synchronous — read from the last\n * rendered frame, never a fresh simulation. The analytic perception a\n * non-vision agent gets for a saver that has no declarative spec.\n * Return null for \"nothing yet\". Optional. */\n inspect?(): Record<string, unknown> | null;\n /** Steer parameters over time. Optional. */\n applyTrack?(track: ControlTrack): void;\n /** Seek logical animation time (ms) for workbench preview. Optional. */\n previewAt?(t: number): void;\n /** Describe the practical composition stack, bottom-up. Optional.\n * (Named `composition`, not `layers`: schema-compiled instances already\n * carry their declared sprite `layers` as data.) */\n composition?(): SaverLayer[];\n /** Tear down; MUST restore any live-page mutations. */\n dispose(): void;\n}\n\n/** Self-describing metadata, readable WITHOUT executing the saver. */\nexport interface SaverManifest {\n id: string;\n label: string;\n /** One-line summary for catalogs / agent tooling (e.g. MCP `listSavers`). */\n description?: string;\n passthrough?: boolean;\n minBackend?: 'css' | 'canvas2d' | 'webgl2' | 'webgpu';\n costTier?: 'idle' | 'low' | 'medium' | 'high';\n motionIntensity?: 'calm' | 'moderate' | 'energetic';\n reducedMotionFallback?: 'static' | 'slow' | 'hide';\n palette?: string[];\n paramSpace?: ParamSpace;\n a11y?: { flashSafe?: boolean; notes?: string };\n provenance?: { prompt?: string; seed?: number; model?: string };\n /** Attribution for savers derived from licensed or third-party work — the\n * concept, reference implementation, or embedded assets. Hosts SHOULD show\n * this wherever the saver is showcased; the full ledger is CREDITS.md. */\n attribution?: {\n /** What this saver descends from, e.g. 'After Dark \"Messages\" — concept by Berkeley Systems'. */\n source: string;\n /** The licensing situation, ours and upstream, e.g. 'MIT port; reference CSS MIT (Bryan Braun)'. */\n license: string;\n /** Reference implementation or rights context. */\n url?: string;\n };\n thumbnail?: string;\n /** Saver renders on a provided surface (canvas/OffscreenCanvas) and avoids DOM\n * APIs, enabling Worker execution when the browser supports OffscreenCanvas. */\n workerReady?: boolean;\n /**\n * How the saver relates to time — a semantic claim about the program, not\n * an API flag (renderFrame's presence is the API).\n *\n * - 'closed-form': every frame is a pure function of (t, seed, params) —\n * compiled plans, phase integrals, triangle waves, CSS keyframes. With\n * `renderFrame` this means scrubbing, seek-back determinism, and\n * reproducible perception readings; without it (DOM/CSS savers) the\n * motion is still periodic and pure, just not pixel-addressable by us.\n * - 'simulated': state genuinely evolves by integration (fluid dynamics,\n * reaction-diffusion) — frames depend on history, sampling is the only\n * honest observation, and motion metrics between runs are meaningless.\n *\n * Tooling should prefer this over hardcoded per-saver capability lists.\n */\n timeModel?: 'closed-form' | 'simulated';\n}\n\n/** A registered saver: manifest + a (possibly lazy/async) mount factory. */\nexport interface SaverPlugin {\n manifest: SaverManifest;\n mount(ctx: SaverContext): SaverInstance | Promise<SaverInstance>;\n /** Original schema spec (JSON-serializable). When present, the element sends\n * a `mount-spec` message to the Worker instead of `mount`, so the Worker can\n * compile and run the spec without pre-registration in its registry. */\n spec?: unknown;\n}\n\n/** Engine configuration. */\nexport interface IdleScreensConfig {\n /** Idle time before sleeping, in ms. */\n timeoutMs: number;\n /** Also sleep when the window loses focus. */\n sleepOnBlur: boolean;\n /** Do not run on localhost (debug hooks bypass this). */\n disableOnLocalhost: boolean;\n /** Which registered saver to show. */\n defaultPluginId: string;\n /** Saver to mount when the ACTIVE saver faults at runtime (throws in its\n * loop, loses its GL context and errors, etc.). The screen must stay a\n * screen: on fault the element swaps to this saver (e.g. 'bsod'), and if\n * it too fails, to a built-in DOM fault screen. Optional. */\n crashSaverId?: string;\n /** How to pick across sleeps. */\n selection: 'fixed' | 'random' | 'rotate';\n /** Show the clock chrome (non-passthrough savers only). */\n showClock: boolean;\n /** Exact document title while asleep. */\n sleepTitle?: string;\n /** Master seed for plugin selection + the default saver seed. */\n seed: number;\n /** Return true for a URL where the saver must never sleep. */\n suppress?: (url: string) => boolean;\n /** Pluggable persistence (defaults to localStorage when available). */\n storage?: { get(key: string): string | null; set(key: string, value: string): void };\n /** URL of the idle-worker script. When set, savers with `workerReady: true`\n * render in a Web Worker via OffscreenCanvas. Omit to disable Worker rendering. */\n workerUrl?: string;\n /** Built-in, keyboard-opened config menu (saver picker). `true`/omitted =\n * enabled with ⌘K / Ctrl+K; `false` = no hotkey and no built-in menu (the\n * `configMenuOpen` signal + open/close API still work, so a host may render\n * its own). Pass an object to reconfigure. */\n configMenu?: boolean | ConfigMenuConfig;\n}\n\n/** Options for the built-in, keyboard-opened config menu. */\nexport interface ConfigMenuConfig {\n /** Master switch. Default true. */\n enabled?: boolean;\n /** Decides whether a keydown opens/toggles the menu. Default: ⌘K / Ctrl+K. */\n hotkey?: (e: KeyboardEvent) => boolean;\n /** Show the saver picker control. Default true. */\n showPicker?: boolean;\n /** Preview the picked saver immediately (sleep to show it). Default true. */\n previewOnPick?: boolean;\n /** Heading text. Default 'Screen Saver'. */\n title?: string;\n}\n\n/** Default menu hotkey: ⌘K (mac) / Ctrl+K, no other modifiers. */\nexport const DEFAULT_MENU_HOTKEY = (e: KeyboardEvent): boolean =>\n (e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey && e.key.toLowerCase() === 'k';\n\n/** Normalize `configMenu` to a filled config, or null when disabled. */\nexport function resolveConfigMenu(\n c: boolean | ConfigMenuConfig | undefined,\n): Required<ConfigMenuConfig> | null {\n if (c === false) return null;\n const o: ConfigMenuConfig = c === true || c === undefined ? {} : c;\n if (o.enabled === false) return null;\n return {\n enabled: true,\n hotkey: o.hotkey ?? DEFAULT_MENU_HOTKEY,\n showPicker: o.showPicker ?? true,\n previewOnPick: o.previewOnPick ?? true,\n title: o.title ?? 'Screen Saver',\n };\n}\n\nexport const DEFAULT_CONFIG: IdleScreensConfig = {\n timeoutMs: 60_000,\n sleepOnBlur: true,\n disableOnLocalhost: false,\n defaultPluginId: '',\n selection: 'fixed',\n showClock: true,\n seed: 1,\n configMenu: true,\n};\n","/**\n * A tiny, fast, seedable PRNG (splitmix32). Deterministic: the same seed always\n * yields the same sequence, on any machine. This is what makes \"same program +\n * seed + control-track = identical frames\" hold. Savers take an `Rng` from their\n * `SaverContext` instead of calling `Math.random()`.\n */\nexport interface Rng {\n /** Next float in [0, 1). */\n next(): number;\n /** Float in [min, max). */\n range(min: number, max: number): number;\n /** Integer in [min, max] inclusive. */\n int(min: number, max: number): number;\n /** Random element of a non-empty array. */\n pick<T>(arr: readonly T[]): T;\n /** A fresh independent stream (e.g. per subsystem), derived from this seed. */\n fork(salt: number): Rng;\n}\n\nexport function createRng(seed: number): Rng {\n let a = seed >>> 0;\n const next = (): number => {\n a |= 0;\n a = (a + 0x9e3779b9) | 0;\n let t = a ^ (a >>> 16);\n t = Math.imul(t, 0x21f0aaad);\n t = t ^ (t >>> 15);\n t = Math.imul(t, 0x735a2d97);\n return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296;\n };\n const rng: Rng = {\n next,\n range: (min, max) => min + (max - min) * next(),\n int: (min, max) => min + Math.floor(next() * (max - min + 1)),\n pick: (arr) => arr[Math.floor(next() * arr.length)] as (typeof arr)[number],\n fork: (salt) => createRng((seed ^ Math.imul(salt + 1, 0x9e3779b9)) >>> 0),\n };\n return rng;\n}\n","/**\n * Framework-agnostic reactivity. We re-export `@preact/signals-core` so the rest\n * of the engine uses the same `signal` / `computed` / `effect` primitives it did\n * as Angular signals, with near-identical semantics.\n */\nexport {\n signal,\n computed,\n effect,\n batch,\n untracked,\n type Signal,\n type ReadonlySignal,\n} from '@preact/signals-core';\n","import type { ControlTrack, Ease, ParamDelta, ParamSpace, ParamValue } from './types';\n\nfunction easeFn(e: Ease, p: number): number {\n const x = p <= 0 ? 0 : p >= 1 ? 1 : p;\n switch (e) {\n case 'step':\n return x >= 1 ? 1 : 0;\n case 'smooth':\n return x * x * (3 - 2 * x);\n case 'linear':\n default:\n return x;\n }\n}\n\nfunction lerp(a: number, b: number, p: number): number {\n return a + (b - a) * p;\n}\n\nfunction parseHex(c: string): [number, number, number] | null {\n const m = /^#?([0-9a-f]{6})$/i.exec(c.trim());\n if (!m) return null;\n const n = parseInt(m[1]!, 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\nfunction toHex(rgb: [number, number, number]): string {\n const h = (v: number): string =>\n Math.round(Math.max(0, Math.min(255, v)))\n .toString(16)\n .padStart(2, '0');\n return `#${h(rgb[0])}${h(rgb[1])}${h(rgb[2])}`;\n}\n\nfunction lerpValue(type: string, a: ParamValue, b: ParamValue, p: number): ParamValue {\n if (type === 'number' && typeof a === 'number' && typeof b === 'number') return lerp(a, b, p);\n if (type === 'color' && typeof a === 'string' && typeof b === 'string') {\n const ca = parseHex(a);\n const cb = parseHex(b);\n if (ca && cb) return toHex([lerp(ca[0], cb[0], p), lerp(ca[1], cb[1], p), lerp(ca[2], cb[2], p)]);\n }\n return p >= 1 ? b : a; // bool / enum / string / step\n}\n\n/** A number-param keyframe value as both sampling and integration see it:\n * finite numbers pass, finite numeric strings coerce (MCP harnesses with\n * untyped `value` params stringify numbers), anything else is null — the\n * keyframe is treated as absent. Shared by evalPath and integrateParam so\n * the sampled curve and its integral can never disagree about a value. */\nfunction numericValue(v: ParamValue): number | null {\n if (typeof v === 'number') return Number.isFinite(v) ? v : null;\n if (typeof v === 'string') {\n const n = Number(v);\n return v.trim() !== '' && Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\nfunction evalPath(\n dflt: ParamValue,\n type: string,\n defaultEase: Ease,\n deltas: ParamDelta[],\n t: number,\n): ParamValue {\n if (deltas.length === 0) return dflt;\n // Number params: stringified keyframes coerce, junk keyframes are ignored\n // (as if absent) — identical treatment to integrateParam, so the distance\n // warp can never diverge from the curve the saver samples.\n if (type === 'number') {\n const cleaned: ParamDelta[] = [];\n for (const k of deltas) {\n const n = numericValue(k.value);\n if (n === null) continue;\n cleaned.push(n === k.value ? k : { ...k, value: n });\n }\n deltas = cleaned;\n if (deltas.length === 0) return dflt;\n }\n let prevVal = dflt;\n let prevT = -Infinity;\n for (const k of deltas) {\n if (t < k.t) {\n const rampStart = k.dur != null ? k.t - k.dur : prevT === -Infinity ? k.t : prevT;\n if (t <= rampStart) return prevVal;\n const p = easeFn(k.ease ?? defaultEase, (t - rampStart) / (k.t - rampStart || 1));\n return lerpValue(type, prevVal, k.value, p);\n }\n prevVal = k.value;\n prevT = k.t;\n }\n return prevVal; // after the last keyframe\n}\n\n/**\n * Sample a control track: given the saver's param space (defaults + types) and a\n * track, return the interpolated parameter values at logical time `t` (ms). Pure\n * and deterministic: identical `(space, track, t)` yields identical output.\n */\nexport function sampleTrack(\n space: ParamSpace,\n track: ControlTrack,\n t: number,\n): Record<string, ParamValue> {\n const byPath = new Map<string, ParamDelta[]>();\n for (const d of track.deltas) {\n const arr = byPath.get(d.path) ?? [];\n arr.push(d);\n byPath.set(d.path, arr);\n }\n const wrapped =\n track.loop && track.duration && track.duration > 0 ? t % track.duration : t;\n\n const out: Record<string, ParamValue> = {};\n for (const [path, def] of Object.entries(space)) {\n const deltas = (byPath.get(path) ?? []).slice().sort((a, b) => a.t - b.t);\n out[path] = evalPath(def.default, def.type, def.ease ?? 'linear', deltas, wrapped);\n }\n return out;\n}\n\n/** Default param values (the \"resting\" program before any track is applied). */\nexport function defaultParams(space: ParamSpace): Record<string, ParamValue> {\n const out: Record<string, ParamValue> = {};\n for (const [path, def] of Object.entries(space)) out[path] = def.default;\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// integrateParam — the closed-form integral of one tracked number param.\n// ---------------------------------------------------------------------------\n\n/** ∫₀ᵘ ease dx, for the eases easeFn defines. `step` holds 0 until u = 1 (the\n * value jumps at the keyframe), so its running integral is 0 on [0, 1). */\nfunction easeIntegral(e: Ease, u: number): number {\n const x = u <= 0 ? 0 : u >= 1 ? 1 : u;\n switch (e) {\n case 'step':\n return 0;\n case 'smooth':\n return x * x * x - (x * x * x * x) / 2; // ∫ 3x² − 2x³\n case 'linear':\n default:\n return (x * x) / 2;\n }\n}\n\nfunction integrateDeltas(\n dflt: number,\n defaultEase: Ease,\n deltas: Array<{ t: number; value: number; ease?: Ease; dur?: number }>,\n t: number,\n): number {\n let acc = 0;\n let cursor = 0; // integrated up to here\n let prevVal = dflt;\n let prevT = -Infinity;\n for (const k of deltas) {\n // Identical rampStart rule to evalPath — including a `dur` that reaches\n // back past the previous keyframe, where sampling starts mid-ease. The\n // eased span is therefore integrated in the ramp's ORIGINAL coordinates\n // (u measured from rampStart over D0), never renormalized to the span we\n // happen to integrate.\n const rampStart = k.dur != null ? k.t - k.dur : prevT === -Infinity ? k.t : prevT;\n const D0 = k.t - rampStart;\n const end = Math.min(t, k.t);\n // Constant stretch at prevVal, up to wherever the ramp (or t) begins.\n const constEnd = Math.min(Math.max(rampStart, cursor), end);\n if (constEnd > cursor) acc += prevVal * (constEnd - cursor);\n // Eased stretch: v(τ) = prevVal + (k.value − prevVal)·E((τ−rampStart)/D0).\n if (D0 > 0 && end > constEnd) {\n const ease = k.ease ?? defaultEase;\n const ua = (constEnd - rampStart) / D0;\n const ub = (end - rampStart) / D0;\n acc +=\n prevVal * (end - constEnd) +\n (k.value - prevVal) * D0 * (easeIntegral(ease, ub) - easeIntegral(ease, ua));\n }\n if (t <= k.t) return acc;\n cursor = k.t;\n prevVal = k.value;\n prevT = k.t;\n }\n return acc + prevVal * (t - cursor);\n}\n\n/**\n * Closed-form ∫₀ᵗ v(τ) dτ of one number param's track curve — the exact curve\n * `sampleTrack` emits for that path, integrated analytically segment by\n * segment (constant spans, plus linear/smooth/step ramps with `dur` and\n * previous-keyframe ramp starts handled identically to sampling).\n *\n * Why it exists: a saver that multiplies a rate by the *sampled* value scales\n * its entire accumulated motion when the param changes — a live steer\n * teleports everything proportionally to elapsed time. Integrating the curve\n * instead makes rate changes glide, while staying a pure function of\n * `(space, track, t)` — no accumulated state, so `renderFrame(t)` stays\n * frame-addressable and scrubbing works.\n *\n * Units: `t` is track time in ms; the result is value·ms (divide by 1000 for\n * value·seconds). Looping tracks integrate as\n * `fullLoops · ∫₀ᵈᵘʳ + ∫₀ʳᵉᵐ`. Non-`number` params integrate their numeric\n * default (or 0). Like `sampleTrack`, no min/max clamping is applied.\n */\nexport function integrateParam(\n space: ParamSpace,\n track: ControlTrack,\n path: string,\n t: number,\n bounds?: { min?: number; max?: number },\n): number {\n const def = space[path];\n if (!def) return 0;\n // Endpoint clamping bounds the WHOLE curve because every ease is a\n // monotone interpolation between keyframe values: if both endpoints are\n // in range, every eased point between them is too. (A ramp aimed at an\n // out-of-range value is compressed to the clamped endpoint rather than\n // clipped mid-flight — in range, deterministic, and the curve an\n // intake-validated track would have produced.)\n const clamp = (n: number): number => {\n if (bounds?.min !== undefined && n < bounds.min) return bounds.min;\n if (bounds?.max !== undefined && n > bounds.max) return bounds.max;\n return n;\n };\n const dflt = clamp(numericValue(def.default) ?? 0);\n if (def.type !== 'number' || t <= 0) return dflt * Math.max(0, t);\n const deltas: Array<{ t: number; value: number; ease?: Ease; dur?: number }> = [];\n for (const d of track.deltas) {\n if (d.path !== path) continue;\n const raw = numericValue(d.value);\n if (raw === null) continue;\n const value = clamp(raw);\n const entry: { t: number; value: number; ease?: Ease; dur?: number } = { t: d.t, value };\n if (d.ease !== undefined) entry.ease = d.ease;\n if (d.dur !== undefined) entry.dur = d.dur;\n deltas.push(entry);\n }\n deltas.sort((a, b) => a.t - b.t);\n const defaultEase = def.ease ?? 'linear';\n if (track.loop && track.duration && track.duration > 0) {\n const loops = Math.floor(t / track.duration);\n const rem = t - loops * track.duration;\n const oneLoop = integrateDeltas(dflt, defaultEase, deltas, track.duration);\n return loops * oneLoop + integrateDeltas(dflt, defaultEase, deltas, rem);\n }\n return integrateDeltas(dflt, defaultEase, deltas, t);\n}\n","import { signal, type ReadonlySignal } from './reactive';\n\n/**\n * Detects user inactivity. `idle` flips true once no activity for `timeoutMs`.\n * Framework-agnostic port of the Angular IdleService: 6 activity events + a\n * single `setTimeout` (not a poll) whose reschedule is throttled to >=500ms so\n * frequent pointermove events do not thrash timers.\n */\nexport class IdleDetector {\n private readonly _idle = signal(false);\n readonly idle: ReadonlySignal<boolean> = this._idle;\n\n private timer: ReturnType<typeof setTimeout> | null = null;\n private lastReset = 0;\n private running = false;\n\n private static readonly EVENTS = [\n 'pointerdown',\n 'pointermove',\n 'keydown',\n 'wheel',\n 'touchstart',\n 'scroll',\n ] as const;\n\n constructor(private readonly timeoutMs: number) {}\n\n private readonly onActivity = (): void => this.markActive();\n\n start(): void {\n if (this.running || typeof window === 'undefined') return;\n this.running = true;\n for (const e of IdleDetector.EVENTS) {\n window.addEventListener(e, this.onActivity, { passive: true });\n }\n this.schedule();\n }\n\n stop(): void {\n if (!this.running) return;\n this.running = false;\n for (const e of IdleDetector.EVENTS) {\n window.removeEventListener(e, this.onActivity);\n }\n this.clear();\n }\n\n /** Force an activity pulse (resets the countdown, wakes from idle). */\n markActive(): void {\n if (this._idle.value) this._idle.value = false;\n const now = typeof performance !== 'undefined' ? performance.now() : Date.now();\n if (now - this.lastReset < 500) return; // throttle reschedules\n this.lastReset = now;\n this.schedule();\n }\n\n private schedule(): void {\n this.clear();\n this.timer = setTimeout(() => {\n this._idle.value = true;\n }, this.timeoutMs);\n }\n\n private clear(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n}\n","import { computed, effect, signal, untracked, type ReadonlySignal } from './reactive';\nimport { IdleDetector } from './idle-detector';\nimport { createRng, type Rng } from './rng';\nimport {\n DEFAULT_CONFIG,\n resolveConfigMenu,\n type ConfigMenuConfig,\n type IdleScreensConfig,\n type SaverPlugin,\n} from './types';\n\nexport type EngineState = 'awake' | 'sleeping';\n\nconst hasWindow = typeof window !== 'undefined';\nconst hasDoc = typeof document !== 'undefined';\n\nfunction defaultStorage(): IdleScreensConfig['storage'] {\n return {\n get: (k) => {\n try {\n return hasWindow ? window.localStorage.getItem(k) : null;\n } catch {\n return null;\n }\n },\n set: (k, v) => {\n try {\n if (hasWindow) window.localStorage.setItem(k, v);\n } catch {\n /* private mode / disabled storage */\n }\n },\n };\n}\n\n/**\n * The framework-agnostic idle-screens state machine (port of the Angular\n * ScreensaverService). Owns: awake/sleeping state, plugin selection, the sleep\n * clock, reduced-motion + visibility, idle/blur triggers, and the\n * `window.__idleScreens` debug hook. It does NOT render; the `<idle-screen>`\n * element observes its signals.\n */\nexport class IdleScreensEngine {\n readonly config: IdleScreensConfig;\n private readonly plugins: SaverPlugin[];\n private readonly storage: NonNullable<IdleScreensConfig['storage']>;\n private readonly idle: IdleDetector;\n private readonly selectionRng: Rng;\n\n private readonly _state = signal<EngineState>('awake');\n readonly state: ReadonlySignal<EngineState> = this._state;\n readonly isSleeping = computed(() => this._state.value === 'sleeping');\n\n private readonly _activeId = signal('');\n readonly activePlugin = computed(\n () =>\n this.plugins.find((p) => p.manifest.id === this._activeId.value) ??\n this.plugins[0] ??\n null,\n );\n readonly activeIsPassthrough = computed(\n () => this.activePlugin.value?.manifest.passthrough ?? false,\n );\n\n private readonly _now = signal(new Date(0));\n readonly now: ReadonlySignal<Date> = this._now;\n private readonly _hidden = signal(false);\n readonly hidden: ReadonlySignal<boolean> = this._hidden;\n private readonly _reducedMotion = signal(false);\n readonly reducedMotion: ReadonlySignal<boolean> = this._reducedMotion;\n\n /** Resolved built-in config-menu options, or null when disabled. */\n readonly configMenu: Required<ConfigMenuConfig> | null;\n private readonly _configMenuOpen = signal(false);\n /** Whether the config menu is open. Drives the built-in `<idle-screen>` menu;\n * a host may also observe this to render its own. */\n readonly configMenuOpen: ReadonlySignal<boolean> = this._configMenuOpen;\n\n private clockTimer: ReturnType<typeof setInterval> | null = null;\n private started = false;\n private disposers: Array<() => void> = [];\n\n constructor(config: Partial<IdleScreensConfig>, plugins: SaverPlugin[]) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.plugins = plugins;\n this.configMenu = resolveConfigMenu(this.config.configMenu);\n this.storage = this.config.storage ?? defaultStorage()!;\n this.idle = new IdleDetector(this.config.timeoutMs);\n this.selectionRng = createRng(this.config.seed >>> 0);\n\n const stored = this.storage.get('idleScreens.plugin');\n const initialId =\n (stored && plugins.some((p) => p.manifest.id === stored) && stored) ||\n this.config.defaultPluginId ||\n plugins[0]?.manifest.id ||\n '';\n this._activeId.value = initialId;\n }\n\n get pluginList(): Array<{ id: string; label: string }> {\n return this.plugins.map((p) => ({ id: p.manifest.id, label: p.manifest.label }));\n }\n\n /** Look up a registered plugin (e.g. the configured crash saver). */\n pluginById(id: string): SaverPlugin | undefined {\n return this.plugins.find((p) => p.manifest.id === id);\n }\n\n /** Begin idle detection + listeners + debug hook. */\n init(): void {\n if (this.started || !hasWindow) return;\n this.started = true;\n\n if (hasDoc) {\n this._hidden.value = document.hidden;\n const onVis = (): void => {\n this._hidden.value = document.hidden;\n };\n document.addEventListener('visibilitychange', onVis);\n this.disposers.push(() => document.removeEventListener('visibilitychange', onVis));\n }\n\n const mq = hasWindow ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;\n if (mq) {\n this._reducedMotion.value = mq.matches;\n const onMq = (): void => {\n this._reducedMotion.value = mq.matches;\n };\n mq.addEventListener('change', onMq);\n this.disposers.push(() => mq.removeEventListener('change', onMq));\n }\n\n // Sleep when idle fires (unless suppressed). `sleep()` reads `state` in its\n // guard; wrap it in `untracked` so this effect depends ONLY on the idle\n // signal — otherwise flipping `state` to awake in wake() (while idle is still\n // latched true) would re-trigger this effect and immediately re-sleep.\n this.idle.start();\n this.disposers.push(\n effect(() => {\n if (this.idle.idle.value) untracked(() => this.sleep());\n }),\n );\n\n if (this.config.sleepOnBlur) {\n const onBlur = (): void => this.sleep();\n window.addEventListener('blur', onBlur);\n this.disposers.push(() => window.removeEventListener('blur', onBlur));\n }\n\n // Optional built-in config-menu hotkey (⌘K / Ctrl+K by default).\n if (this.configMenu) {\n const hotkey = this.configMenu.hotkey;\n const onKey = (e: KeyboardEvent): void => {\n if (hotkey(e)) {\n e.preventDefault();\n this.toggleConfigMenu();\n }\n };\n window.addEventListener('keydown', onKey);\n this.disposers.push(() => window.removeEventListener('keydown', onKey));\n }\n\n // Drive the clock only while asleep + visible.\n this.disposers.push(\n effect(() => {\n const ticking = this._state.value === 'sleeping' && !this._hidden.value;\n if (ticking && this.clockTimer === null) {\n this._now.value = new Date();\n this.clockTimer = setInterval(() => {\n this._now.value = new Date();\n }, 1000);\n } else if (!ticking && this.clockTimer !== null) {\n clearInterval(this.clockTimer);\n this.clockTimer = null;\n }\n }),\n );\n\n this.installDebugHook();\n }\n\n destroy(): void {\n this.idle.stop();\n if (this.clockTimer !== null) {\n clearInterval(this.clockTimer);\n this.clockTimer = null;\n }\n for (const d of this.disposers) d();\n this.disposers = [];\n this.started = false;\n }\n\n private suppressed(): boolean {\n if (this.config.disableOnLocalhost && hasWindow && /^(localhost|127\\.|\\[::1\\])/.test(location.hostname)) {\n return true;\n }\n const url = hasWindow ? location.pathname + location.search : '';\n return this.config.suppress ? this.config.suppress(url) : false;\n }\n\n sleep(): void {\n if (this._state.value === 'sleeping' || this.suppressed()) return;\n this.pickPlugin();\n this._state.value = 'sleeping';\n }\n\n /** Sleep ignoring suppression rules (debug / manual). */\n forceSleep(): void {\n if (this._state.value === 'sleeping') return;\n this.pickPlugin();\n this._state.value = 'sleeping';\n }\n\n wake(): void {\n if (this._state.value === 'awake') return;\n this._state.value = 'awake';\n this.idle.markActive();\n }\n\n toggle(): void {\n if (this.isSleeping.value) this.wake();\n else this.forceSleep();\n }\n\n openConfigMenu(): void {\n this._configMenuOpen.value = true;\n }\n closeConfigMenu(): void {\n this._configMenuOpen.value = false;\n }\n toggleConfigMenu(): void {\n this._configMenuOpen.value = !this._configMenuOpen.value;\n }\n\n setPlugin(id: string): void {\n if (!this.plugins.some((p) => p.manifest.id === id)) return;\n this._activeId.value = id;\n this.storage.set('idleScreens.plugin', id);\n }\n\n private pickPlugin(): void {\n if (this.plugins.length === 0) return;\n switch (this.config.selection) {\n case 'random': {\n const i = this.selectionRng.int(0, this.plugins.length - 1);\n this._activeId.value = this.plugins[i]!.manifest.id;\n break;\n }\n case 'rotate': {\n const cur = this.plugins.findIndex((p) => p.manifest.id === this._activeId.value);\n const next = this.plugins[(cur + 1) % this.plugins.length]!;\n this._activeId.value = next.manifest.id;\n break;\n }\n case 'fixed':\n default:\n // Keep the current (persisted / menu-chosen) plugin. `defaultPluginId`\n // is only the INITIAL pick (see constructor); once the user chooses a\n // saver via setPlugin/the menu it sticks across sleeps.\n break;\n }\n }\n\n private installDebugHook(): void {\n if (!hasWindow) return;\n (window as unknown as { __idleScreens?: unknown }).__idleScreens = {\n sleep: () => this.forceSleep(),\n wake: () => this.wake(),\n toggle: () => this.toggle(),\n setPlugin: (id: string) => this.setPlugin(id),\n openMenu: () => this.openConfigMenu(),\n closeMenu: () => this.closeConfigMenu(),\n toggleMenu: () => this.toggleConfigMenu(),\n state: () => this._state.value,\n menuOpen: () => this._configMenuOpen.value,\n active: () => this.activePlugin.value?.manifest.id ?? null,\n plugins: this.pluginList,\n };\n }\n}\n","/**\n * The floor of the graceful-degradation ladder: a zero-dependency DOM fault\n * screen the element shows when the active saver faults at runtime AND the\n * configured crash saver (config.crashSaverId, e.g. 'bsod') is unavailable\n * or faults too. The screen must stay a screen — dark, calm, flash-safe,\n * and honest about what happened. Any key still wakes.\n */\n\nexport interface FaultInfo {\n /** Id of the saver that faulted. */\n saverId: string;\n /** One error line, already stringified. Truncated for display. */\n message: string;\n}\n\nexport function renderFaultScreen(host: HTMLElement, info: FaultInfo): HTMLElement {\n const root = document.createElement('div');\n root.className = 'is-fault-screen';\n root.setAttribute('role', 'alert');\n root.setAttribute('aria-live', 'assertive');\n root.style.cssText = [\n 'position:absolute',\n 'inset:0',\n 'display:grid',\n 'place-items:center',\n 'background:#040610',\n 'color:#8fb4d8',\n 'font-family:ui-monospace,SFMono-Regular,Menlo,monospace',\n 'font-size:14px',\n 'line-height:1.7',\n 'text-align:left',\n ].join(';');\n\n const box = document.createElement('div');\n const msg = info.message.length > 160 ? `${info.message.slice(0, 157)}…` : info.message;\n const line = (text: string, style = ''): HTMLElement => {\n const el = document.createElement('div');\n el.textContent = text;\n if (style) el.style.cssText = style;\n return el;\n };\n box.append(\n line('IDLE_SCREENS :: SAVER_FAULT', 'color:#d8e8f8;letter-spacing:0.12em;margin-bottom:1em'),\n line(`The saver \"${info.saverId}\" stopped and was halted.`),\n line(msg, 'color:#5a7a9a;margin-top:0.5em;max-width:60ch;overflow-wrap:break-word'),\n line('The screen stays asleep. Press any key to wake.', 'color:#5a7a9a;margin-top:2em'),\n );\n root.append(box);\n host.append(root);\n return root;\n}\n","/**\n * Attribution for runtime faults while a saver owns the screen: is this\n * uncaught error / unhandled rejection plausibly the saver's, or foreign\n * noise (browser extension, third-party script) the crash ladder must ignore?\n *\n * The deliberate bias is toward engaging the ladder. While a non-passthrough\n * saver holds the fullscreen dialog, the saver is the only thing on screen —\n * a false positive swaps one working screen for the crash screen (still a\n * screen, one swap per sleep), while a false negative freezes a dead saver\n * black. Only sources that clearly cannot be the saver's code are filtered:\n * in production the saver compiles into an unmarked bundle chunk\n * (assets/index-<hash>.js), so origin — not the script's name — is the\n * trustworthy signal.\n */\n\n/** Script locations that name saver/engine code outright (dev servers, CDNs). */\nexport const SAVER_FAULT_LOC =\n /idle-screens|saver-|@idle-screens|three|metaquarium|schema|core|capabilities/i;\n\n/** Same-origin scripts are fault-eligible: app code and saver code are\n * indistinguishable once bundled. Opaque origins (\"null\", file://) compare\n * equal to themselves, which keeps file:// hosts (native wrappers) working. */\nfunction sameOrigin(url: string, origin: string): boolean {\n try {\n return new URL(url, origin === 'null' ? undefined : origin).origin === origin;\n } catch {\n return false;\n }\n}\n\n/**\n * ErrorEvent path. `filename` is the reporting script's URL — empty for\n * inline/synthetic throws (treated as ours), a chrome-extension:// or\n * third-party URL for foreign noise (skipped unless it names saver code).\n */\nexport function isSaverFaultFilename(filename: unknown, origin: string): boolean {\n if (typeof filename !== 'string' || filename.length === 0) return true;\n if (SAVER_FAULT_LOC.test(filename)) return true;\n return sameOrigin(filename, origin);\n}\n\n/**\n * PromiseRejectionEvent path. All we have is the reason's stack (or its\n * string form): no URL at all is inline/synthetic (ours); any same-origin\n * frame claims it; a hint made solely of foreign script URLs is noise.\n */\nexport function isSaverFaultRejectionHint(hint: string, origin: string): boolean {\n if (!hint) return true;\n if (SAVER_FAULT_LOC.test(hint)) return true;\n const urls = hint.match(/https?:\\/\\/[^\\s)]+/gi);\n if (!urls) return true;\n return urls.some((u) => sameOrigin(u, origin));\n}\n","import { effect } from './reactive';\nimport { IdleScreensEngine } from './engine';\nimport { createRng } from './rng';\nimport { renderFaultScreen } from './fault-screen';\nimport { isSaverFaultFilename, isSaverFaultRejectionHint } from './fault-attribution';\nimport type { IdleScreensConfig, PageContext, SaverInstance, SaverPlugin } from './types';\nimport type { WorkerInbound, WorkerOutbound } from './worker-protocol';\n\nconst STYLE = `\n :host { all: initial; }\n dialog.frame {\n position: fixed; inset: 0; width: 100%; height: 100%;\n max-width: none; max-height: none; margin: 0; border: 0; padding: 0;\n background: #0a0a0f; overflow: hidden; cursor: none; color: #fff;\n }\n dialog.frame[open] { animation: ss-in 0.5s ease; }\n dialog.frame.leaving { animation: ss-out 0.32s ease forwards; }\n dialog.frame.reduced { animation: none; }\n dialog.frame::backdrop { background: #0a0a0f; }\n dialog.frame.passthrough, dialog.frame.passthrough::backdrop { background: transparent; }\n @keyframes ss-in { from { opacity: 0; } to { opacity: 1; } }\n @keyframes ss-out { from { opacity: 1; } to { opacity: 0; } }\n .surface { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }\n .surface > canvas { display: block; width: 100%; height: 100%; }\n /* Host-owned fallback markup (<x slot=\"fallback\">), shown only when the active\n saver fails to mount — the host decides what a broken saver looks like. */\n .fallback { position: absolute; inset: 0; display: none; place-items: center; }\n dialog.frame.show-fallback .fallback { display: grid; }\n .clock {\n position: absolute; top: 10px; right: 12px; text-align: right;\n font-family: 'Orbitron', system-ui, sans-serif; pointer-events: none;\n }\n .clock .day { font-size: 34px; opacity: 0.9; }\n .clock .time { font-size: 54px; }\n .hint {\n position: fixed; bottom: 26px; left: 50%; transform: translateX(-50%);\n color: rgba(230,230,230,0.6); font: 12px ui-monospace, monospace;\n letter-spacing: 0.25em; text-transform: uppercase; opacity: 0;\n transition: opacity 1s ease; pointer-events: none;\n }\n .hint.show { opacity: 1; }\n\n /* Built-in config menu: a normal (interactive) centered dialog, distinct from\n the cursor-hidden saver frame. */\n dialog.menu {\n position: fixed; inset: 0; margin: auto; border: 0; padding: 0;\n width: min(420px, calc(100vw - 32px)); max-width: none;\n background: transparent; color: #f2f2f5;\n }\n dialog.menu::backdrop { background: rgba(6,6,10,0.5); backdrop-filter: blur(2px); }\n .menu-card {\n background: #14141b; border: 1px solid rgba(255,255,255,0.12);\n border-radius: 14px; padding: 20px; box-shadow: 0 24px 60px rgba(0,0,0,0.5);\n font-family: system-ui, sans-serif;\n }\n .menu-title { font-size: 18px; font-weight: 600; margin: 0 0 14px; }\n .menu-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }\n .menu-row > span { font-size: 14px; opacity: 0.85; }\n .menu-select {\n flex: 1; max-width: 60%; padding: 7px 10px; border-radius: 8px;\n background: #0d0d12; color: #f2f2f5; border: 1px solid rgba(255,255,255,0.16);\n font-size: 14px;\n }\n .menu-actions { display: flex; justify-content: flex-end; }\n .menu-close {\n padding: 7px 16px; border-radius: 8px; cursor: pointer; font-size: 14px;\n background: #2a2a37; color: #f2f2f5; border: 1px solid rgba(255,255,255,0.14);\n }\n .menu-close:hover { background: #34344a; }\n`;\n\n/** Node-safe base: during SSR/prerender there is no `HTMLElement`, and importing\n * this module (for the engine/types) must not crash. The element is only ever\n * constructed/registered in the browser. */\nconst HostBase: typeof HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n// Firefox shipped transferControlToOffscreen (~105) before module workers (~114).\n// The getter-probe detects {type:'module'} support without loading a real script.\nlet moduleWorkerDetected: boolean | undefined;\nfunction supportsModuleWorker(): boolean {\n if (moduleWorkerDetected !== undefined) return moduleWorkerDetected;\n moduleWorkerDetected = false;\n try {\n let probed = false;\n const url = URL.createObjectURL(new Blob([], { type: 'text/javascript' }));\n try {\n const w = new Worker(url, {\n get type() { probed = true; return 'module' as WorkerType; },\n } as WorkerOptions);\n w.terminate();\n } finally {\n URL.revokeObjectURL(url);\n }\n moduleWorkerDetected = probed;\n } catch {\n // ignore — browser doesn't support module workers\n }\n return moduleWorkerDetected;\n}\n\n/**\n * `<idle-screen>` — the framework-agnostic overlay (port of the Angular\n * ScreensaverComponent). Owns the top-layer `<dialog>`, mounts the active saver\n * into a canvas, shows the clock + \"press any key\" hint, fades in/out, wakes on\n * input with a 450ms arm-guard, and goes transparent for passthrough savers.\n *\n * Configure by setting the `.config` and `.plugins` properties (Angular can do\n * this via `[config]` / `[plugins]` with CUSTOM_ELEMENTS_SCHEMA).\n */\nexport class IdleScreenElement extends HostBase {\n private _engine: IdleScreensEngine | null = null;\n private ownsEngine = false;\n private _externalEngine: IdleScreensEngine | null = null;\n private _config: Partial<IdleScreensConfig> | null = null;\n private _plugins: SaverPlugin[] | null = null;\n\n private dialog!: HTMLDialogElement;\n private surface!: HTMLDivElement;\n private clockEl!: HTMLDivElement;\n private hintEl!: HTMLDivElement;\n private menuDialog: HTMLDialogElement | null = null;\n private menuSelect: HTMLSelectElement | null = null;\n private menuBuilt = false;\n\n private instance: SaverInstance | null = null;\n /** Runtime-fault state: one crash swap per sleep; reset on each mount. */\n private crashed = false;\n private activeSaverId = '';\n private worker: Worker | null = null;\n private cachedWorker: { worker: Worker; url: string } | null = null;\n private currentWorkerUrl: string | null = null;\n private cleanupWorkerHandlers: (() => void) | null = null;\n /** @internal Force the setTimeout-based rAF polyfill in Workers (for testing). */\n forceRafPolyfill = false;\n private mountToken = 0;\n private armed = false;\n private armTimer: ReturnType<typeof setTimeout> | null = null;\n private hintTimer: ReturnType<typeof setTimeout> | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private resizeTimer: ReturnType<typeof setTimeout> | null = null;\n private disposers: Array<() => void> = [];\n\n set config(c: Partial<IdleScreensConfig>) {\n this._config = c;\n this.maybeStart();\n }\n set plugins(p: SaverPlugin[]) {\n this._plugins = p;\n this.maybeStart();\n }\n /** Use an externally-owned, already-initialized engine (e.g. an Angular service\n * that also drives a config menu) instead of creating one from config+plugins. */\n set engine(e: IdleScreensEngine) {\n this._externalEngine = e;\n this.maybeStart();\n }\n /** The live engine (for host code / tests). */\n get idleEngine(): IdleScreensEngine | null {\n return this._engine;\n }\n /** Whether the currently mounted saver is running in a Web Worker. */\n get isWorker(): boolean {\n return this.worker !== null;\n }\n\n /** Request a pixel sample from the Worker to verify rendering.\n * Returns false if not in Worker mode or if the canvas has no content. */\n async sampleWorkerPixels(): Promise<boolean> {\n if (!this.worker) return false;\n const w = this.worker;\n return new Promise<boolean>((resolve) => {\n const handler = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type === 'sampled') {\n w.removeEventListener('message', handler);\n resolve(e.data.hasContent);\n }\n };\n w.addEventListener('message', handler);\n w.postMessage({ type: 'sample' } satisfies WorkerInbound);\n });\n }\n\n /**\n * The mounted saver's own account of its frame (`SaverInstance.inspect`) —\n * a convenience that forwards to whatever is mounted. Synchronous and\n * last-known: a main-thread saver answers live; a Worker-hosted saver\n * answers the most recent `inspectAsync()` (null until one has been made).\n * Null when nothing is mounted or the saver does not describe itself.\n */\n inspect(): Record<string, unknown> | null {\n try {\n return this.instance?.inspect?.() ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * `inspect()` with the round trip a Worker-hosted saver needs (2 s timeout,\n * null on timeout). Main-thread savers answer immediately with the same\n * value `inspect()` would.\n */\n async inspectAsync(): Promise<Record<string, unknown> | null> {\n const inst = this.instance as (SaverInstance & { inspectAsync?: () => Promise<Record<string, unknown> | null> }) | null;\n if (!inst) return null;\n try {\n if (typeof inst.inspectAsync === 'function') return await inst.inspectAsync();\n return inst.inspect?.() ?? null;\n } catch {\n return null;\n }\n }\n\n connectedCallback(): void {\n if (!this.shadowRoot) this.buildDom();\n this.maybeStart();\n }\n\n disconnectedCallback(): void {\n this.teardown();\n }\n\n private buildDom(): void {\n const root = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = STYLE;\n this.dialog = document.createElement('dialog');\n this.dialog.className = 'frame';\n this.surface = document.createElement('div');\n this.surface.className = 'surface';\n this.clockEl = document.createElement('div');\n this.clockEl.className = 'clock';\n this.hintEl = document.createElement('div');\n this.hintEl.className = 'hint';\n this.hintEl.textContent = 'press any key';\n // Host-owned fallback (AVAL-style): light-DOM children with slot=\"fallback\"\n // render here when a saver fails to mount, instead of a black screen.\n const fallback = document.createElement('div');\n fallback.className = 'fallback';\n const slot = document.createElement('slot');\n slot.name = 'fallback';\n fallback.append(slot);\n this.dialog.append(this.surface, fallback, this.clockEl, this.hintEl);\n root.append(style, this.dialog);\n\n for (const ev of ['pointerdown', 'pointermove', 'keydown', 'wheel', 'touchstart'] as const) {\n this.dialog.addEventListener(ev, () => this.onWakeInput(), { passive: true });\n }\n this.dialog.addEventListener('cancel', (e) => {\n e.preventDefault();\n this._engine?.wake();\n });\n }\n\n private maybeStart(): void {\n if (this._engine || !this.shadowRoot || typeof window === 'undefined') return;\n if (this._externalEngine) {\n this._engine = this._externalEngine; // caller owns lifecycle + init()\n this.ownsEngine = false;\n } else if (this._config && this._plugins) {\n this._engine = new IdleScreensEngine(this._config, this._plugins);\n this._engine.init();\n this.ownsEngine = true;\n } else {\n return;\n }\n const eng = this._engine;\n\n this.disposers.push(\n effect(() => {\n // Read reactive deps up front.\n const sleeping = eng.isSleeping.value;\n const active = eng.activePlugin.value;\n const passthrough = eng.activeIsPassthrough.value;\n if (sleeping && active) this.open(active, passthrough);\n else this.close();\n }),\n );\n\n // Clock chrome.\n this.disposers.push(\n effect(() => {\n const show = this._engine!.config.showClock && !eng.activeIsPassthrough.value;\n const now = eng.now.value;\n this.clockEl.style.display = show && eng.isSleeping.value ? 'block' : 'none';\n if (show) {\n const day = now.toLocaleDateString(undefined, {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n });\n const time = now.toLocaleTimeString();\n this.clockEl.innerHTML = `<div class=\"day\">${day}</div><div class=\"time\">${time}</div>`;\n }\n }),\n );\n\n // Optional built-in config menu (saver picker), opened via engine hotkey/API.\n if (eng.configMenu) {\n this.buildMenu(eng.configMenu);\n this.disposers.push(\n effect(() => {\n const open = eng.configMenuOpen.value;\n const d = this.menuDialog;\n if (!d) return;\n if (open && !d.open) {\n this.syncMenuSelection();\n d.showModal();\n } else if (!open && d.open) {\n d.close();\n }\n }),\n );\n // Keep the picker in sync if the active saver changes elsewhere.\n this.disposers.push(\n effect(() => {\n const id = eng.activePlugin.value?.manifest.id ?? '';\n if (this.menuSelect && this.menuSelect.value !== id) this.menuSelect.value = id;\n }),\n );\n }\n }\n\n private buildMenu(cfg: NonNullable<IdleScreensEngine['configMenu']>): void {\n if (this.menuBuilt || !this.shadowRoot) return;\n const eng = this._engine!;\n const d = document.createElement('dialog');\n d.className = 'menu';\n const card = document.createElement('div');\n card.className = 'menu-card';\n\n const title = document.createElement('div');\n title.className = 'menu-title';\n title.textContent = cfg.title;\n card.append(title);\n\n if (cfg.showPicker) {\n const row = document.createElement('label');\n row.className = 'menu-row';\n const label = document.createElement('span');\n label.textContent = 'Saver';\n const select = document.createElement('select');\n select.className = 'menu-select';\n for (const p of eng.pluginList) {\n const opt = document.createElement('option');\n opt.value = p.id;\n opt.textContent = p.label;\n select.append(opt);\n }\n select.addEventListener('change', () => {\n eng.setPlugin(select.value);\n if (cfg.previewOnPick) {\n // Close the menu first so the saver isn't stacked behind this modal,\n // then show the chosen saver right away.\n eng.closeConfigMenu();\n eng.forceSleep();\n }\n });\n row.append(label, select);\n card.append(row);\n this.menuSelect = select;\n }\n\n const actions = document.createElement('div');\n actions.className = 'menu-actions';\n const close = document.createElement('button');\n close.className = 'menu-close';\n close.type = 'button';\n close.textContent = 'Close';\n close.addEventListener('click', () => eng.closeConfigMenu());\n actions.append(close);\n card.append(actions);\n\n d.append(card);\n // Escape and backdrop click both close (without waking the saver).\n d.addEventListener('cancel', (e) => {\n e.preventDefault();\n eng.closeConfigMenu();\n });\n d.addEventListener('click', (e) => {\n if (e.target === d) eng.closeConfigMenu();\n });\n this.shadowRoot.append(d);\n this.menuDialog = d;\n this.menuBuilt = true;\n }\n\n private syncMenuSelection(): void {\n if (this.menuSelect) {\n this.menuSelect.value = this._engine?.activePlugin.value?.manifest.id ?? '';\n }\n }\n\n private pageContext(): PageContext {\n return {\n palette: () => [],\n victims: (selector: string) =>\n typeof document !== 'undefined'\n ? Array.from(document.querySelectorAll<HTMLElement>(selector))\n : [],\n };\n }\n\n private async open(plugin: SaverPlugin, passthrough: boolean, skipWorker = false): Promise<void> {\n const eng = this._engine!;\n this.dialog.classList.toggle('passthrough', passthrough);\n this.dialog.classList.toggle('reduced', eng.reducedMotion.value);\n this.crashed = false;\n this.activeSaverId = plugin.manifest.id;\n if (!this.dialog.open) {\n this.dialog.classList.remove('leaving');\n this.dialog.showModal();\n this.arm();\n this.scheduleHint();\n window.addEventListener('resize', this.onResize);\n // Runtime crash guard: an uncaught error while a NON-passthrough saver\n // owns the screen is, in practice, the saver's render loop dying (a GL\n // crash, a bad frame). Passthrough savers share the page with the\n // site's own scripts, so attribution there is unsafe — skip them.\n window.addEventListener('error', this.onRuntimeError);\n window.addEventListener('unhandledrejection', this.onRuntimeRejection);\n }\n // (Re)mount the active saver.\n const token = ++this.mountToken;\n await this.disposeInstance();\n const w = window.innerWidth;\n const h = window.innerHeight;\n const seed = eng.config.seed >>> 0;\n const dpr = typeof devicePixelRatio === 'number' ? devicePixelRatio : 1;\n\n const workerEligible = !skipWorker\n && plugin.manifest.workerReady\n && eng.config.workerUrl\n && typeof HTMLCanvasElement.prototype.transferControlToOffscreen === 'function'\n && supportsModuleWorker();\n\n if (workerEligible) {\n try {\n const inst = await this.openInWorker(plugin, eng.config.workerUrl!, w, h, seed, dpr, eng.reducedMotion.value, passthrough);\n if (token !== this.mountToken) {\n inst.dispose();\n return;\n }\n this.instance = inst;\n inst.setPaused(eng.reducedMotion.value);\n this.dialog.classList.remove('show-fallback');\n return;\n } catch {\n // Worker failed — fall through to main-thread mount\n await this.disposeInstance();\n if (token !== this.mountToken) return;\n }\n }\n try {\n const inst = await plugin.mount({\n host: this.surface,\n dpr,\n width: w,\n height: h,\n rng: createRng(seed),\n seed,\n reducedMotion: eng.reducedMotion.value,\n page: plugin.manifest.passthrough ? this.pageContext() : undefined,\n });\n if (token !== this.mountToken) {\n inst.dispose();\n return;\n }\n this.instance = inst;\n inst.setPaused(eng.reducedMotion.value);\n this.dialog.classList.remove('show-fallback');\n } catch (err) {\n // Saver failed to mount — show the host's fallback slot instead of a\n // black screen. The host owns what \"broken\" looks like.\n if (token !== this.mountToken) return;\n console.warn(`[idle-screen] saver \"${plugin.manifest.id}\" failed to mount:`, err);\n this.surface.replaceChildren();\n this.dialog.classList.add('show-fallback');\n }\n }\n\n private openInWorker(\n plugin: SaverPlugin,\n workerUrl: string,\n width: number,\n height: number,\n seed: number,\n dpr: number,\n reducedMotion: boolean,\n passthrough: boolean,\n ): Promise<SaverInstance> {\n const canvas = document.createElement('canvas');\n canvas.style.cssText = 'display:block;width:100%;height:100%';\n this.surface.appendChild(canvas);\n\n const offscreen = canvas.transferControlToOffscreen();\n\n // Reuse cached worker if URL matches, otherwise create new\n let worker: Worker;\n if (this.cachedWorker && this.cachedWorker.url === workerUrl) {\n worker = this.cachedWorker.worker;\n this.cachedWorker = null;\n } else {\n this.terminateCachedWorker();\n worker = new Worker(workerUrl, { type: 'module' });\n }\n this.worker = worker;\n this.currentWorkerUrl = workerUrl;\n\n const mountMsg: WorkerInbound = plugin.spec\n ? {\n type: 'mount-spec',\n canvas: offscreen,\n spec: plugin.spec,\n width, height, seed, dpr, reducedMotion,\n ...(this.forceRafPolyfill ? { forceRafPolyfill: true } : {}),\n }\n : {\n type: 'mount',\n canvas: offscreen,\n saverId: plugin.manifest.id,\n width, height, seed, dpr, reducedMotion,\n ...(this.forceRafPolyfill ? { forceRafPolyfill: true } : {}),\n };\n worker.postMessage(mountMsg, [offscreen]);\n\n let disposed = false;\n\n // Capture round-trips are correlated by id: nothing stops a second call\n // before the first answers, and an uncorrelated FIFO would hand caller A\n // caller B's frame. Answers ride the same message listener the proxy\n // installs below; a 2s timeout keeps a dead worker from parking a caller\n // forever (resolving null matches the contract: \"nothing to show yet\").\n let captureSeq = 0;\n const pendingCaptures = new Map<number, (bitmap: ImageBitmap | null) => void>();\n const onCaptured = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type !== 'captured') return;\n const settle = pendingCaptures.get(e.data.id);\n if (settle) {\n pendingCaptures.delete(e.data.id);\n settle(e.data.bitmap);\n }\n };\n worker.addEventListener('message', onCaptured);\n let inspectSeq = 0;\n const pendingInspects = new Map<number, (state: Record<string, unknown> | null) => void>();\n const onInspected = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type !== 'inspected') return;\n const settle = pendingInspects.get(e.data.id);\n if (settle) {\n pendingInspects.delete(e.data.id);\n settle(e.data.state);\n }\n };\n worker.addEventListener('message', onInspected);\n\n let lastInspect: Record<string, unknown> | null = null;\n const proxy: SaverInstance & { inspectAsync(): Promise<Record<string, unknown> | null> } = {\n setPaused: (p) => worker.postMessage({ type: 'pause', paused: p } satisfies WorkerInbound),\n resize: (w, h, newDpr) => worker.postMessage({ type: 'resize', width: w, height: h, dpr: newDpr ?? dpr } satisfies WorkerInbound),\n applyTrack: (track) => worker.postMessage({ type: 'track', track } satisfies WorkerInbound),\n capture: () =>\n new Promise<ImageBitmap | null>((resolve) => {\n if (disposed) {\n resolve(null);\n return;\n }\n const id = ++captureSeq;\n const timer = setTimeout(() => {\n pendingCaptures.delete(id);\n resolve(null);\n }, 2000);\n pendingCaptures.set(id, (bitmap) => {\n clearTimeout(timer);\n resolve(bitmap);\n });\n worker.postMessage({ type: 'capture', id } satisfies WorkerInbound);\n }),\n // The proxy's inspect is async under the hood (a worker round trip);\n // the SaverInstance contract is synchronous, so expose the promise\n // form as `inspectAsync` and answer the last state synchronously.\n inspect: () => lastInspect,\n inspectAsync: () =>\n new Promise<Record<string, unknown> | null>((resolve) => {\n if (disposed) {\n resolve(null);\n return;\n }\n const id = ++inspectSeq;\n const timer = setTimeout(() => {\n pendingInspects.delete(id);\n resolve(null);\n }, 2000);\n pendingInspects.set(id, (state) => {\n clearTimeout(timer);\n lastInspect = state;\n resolve(state);\n });\n worker.postMessage({ type: 'inspect', id } satisfies WorkerInbound);\n }),\n dispose: () => {\n disposed = true;\n worker.removeEventListener('message', onCaptured);\n worker.removeEventListener('message', onInspected);\n for (const settle of pendingCaptures.values()) settle(null);\n pendingCaptures.clear();\n for (const settle of pendingInspects.values()) settle(null);\n pendingInspects.clear();\n this.cleanupWorkerHandlers?.();\n this.cleanupWorkerHandlers = null;\n worker.postMessage({ type: 'dispose' } satisfies WorkerInbound);\n worker.terminate();\n this.worker = null;\n canvas.remove();\n },\n };\n\n return new Promise<SaverInstance>((resolve, reject) => {\n const onMessage = (e: MessageEvent<WorkerOutbound>): void => {\n if (e.data.type === 'mounted') {\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n\n // Post-mount crash recovery: if the Worker errors mid-animation,\n // fall back to main-thread rendering.\n const onCrash = (ev: Event): void => {\n if (disposed) return;\n disposed = true;\n const msg = ev instanceof ErrorEvent ? ev.message : 'Worker messageerror';\n console.warn(`[idle-screen] Worker crashed mid-animation: ${msg}`);\n this.cleanupWorkerHandlers?.();\n this.cleanupWorkerHandlers = null;\n worker.terminate();\n this.worker = null;\n this.instance = null;\n this.surface?.replaceChildren();\n void this.open(plugin, passthrough, true);\n };\n worker.addEventListener('error', onCrash);\n worker.addEventListener('messageerror', onCrash);\n this.cleanupWorkerHandlers = () => {\n worker.removeEventListener('error', onCrash);\n worker.removeEventListener('messageerror', onCrash);\n // disposeInstance() caches the Worker for reuse WITHOUT calling\n // proxy.dispose(), so the capture/inspect listeners and any\n // in-flight requests must be cleared here too — or every remount\n // stacks another listener on the cached Worker and a pending\n // inspect settles from a later mount's answer.\n worker.removeEventListener('message', onCaptured);\n worker.removeEventListener('message', onInspected);\n for (const settle of pendingCaptures.values()) settle(null);\n pendingCaptures.clear();\n for (const settle of pendingInspects.values()) settle(null);\n pendingInspects.clear();\n };\n\n resolve(proxy);\n } else if (e.data.type === 'error') {\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n reject(new Error(e.data.message));\n }\n };\n const onError = (e: ErrorEvent): void => {\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n reject(new Error(e.message));\n };\n worker.addEventListener('message', onMessage);\n worker.addEventListener('error', onError);\n });\n }\n\n private close(): void {\n if (!this.dialog?.open) return;\n ++this.mountToken; // an async mount still in flight must not install into a closed dialog\n window.removeEventListener('resize', this.onResize);\n window.removeEventListener('error', this.onRuntimeError);\n window.removeEventListener('unhandledrejection', this.onRuntimeRejection);\n this.crashed = false;\n if (this.resizeTimer) { clearTimeout(this.resizeTimer); this.resizeTimer = null; }\n this.dialog.classList.remove('show-fallback');\n this.hintEl.classList.remove('show');\n if (this.hintTimer) clearTimeout(this.hintTimer);\n void this.disposeInstance();\n const reduced = this._engine?.reducedMotion.value;\n if (reduced) {\n this.dialog.close();\n return;\n }\n this.dialog.classList.add('leaving');\n if (this.closeTimer) clearTimeout(this.closeTimer);\n this.closeTimer = setTimeout(() => {\n if (this.dialog.open) this.dialog.close();\n this.dialog.classList.remove('leaving');\n }, 320);\n }\n\n private async disposeInstance(): Promise<void> {\n this.cleanupWorkerHandlers?.();\n this.cleanupWorkerHandlers = null;\n\n if (this.instance) {\n if (this.worker) {\n // Worker instance: send dispose but cache the worker for reuse\n this.worker.postMessage({ type: 'dispose' } satisfies WorkerInbound);\n this.cachedWorker = { worker: this.worker, url: this.currentWorkerUrl! };\n this.worker = null;\n } else {\n this.instance.dispose();\n }\n this.instance = null;\n } else if (this.worker) {\n this.worker.terminate();\n this.worker = null;\n }\n this.surface?.replaceChildren();\n }\n\n private terminateCachedWorker(): void {\n if (this.cachedWorker) {\n this.cachedWorker.worker.terminate();\n this.cachedWorker = null;\n }\n }\n\n private arm(): void {\n this.armed = false;\n if (this.armTimer) clearTimeout(this.armTimer);\n this.armTimer = setTimeout(() => {\n this.armed = true;\n }, 450);\n }\n\n private scheduleHint(): void {\n if (this.hintTimer) clearTimeout(this.hintTimer);\n this.hintEl.classList.remove('show');\n this.hintTimer = setTimeout(() => this.hintEl.classList.add('show'), 3000);\n }\n\n private readonly onResize = (): void => {\n if (this.resizeTimer !== null) clearTimeout(this.resizeTimer);\n this.resizeTimer = setTimeout(() => {\n this.resizeTimer = null;\n if (!this.instance || !this.dialog?.open) return;\n const newDpr = typeof devicePixelRatio === 'number' ? devicePixelRatio : 1;\n try {\n this.instance.resize(window.innerWidth, window.innerHeight, newDpr);\n } catch (err) {\n this.engageCrashScreen(err);\n }\n }, 150);\n };\n\n /** An uncaught error while a non-passthrough saver owns the screen is the\n * saver's loop dying. Swap to the crash saver instead of freezing black.\n * Attribution semantics live in fault-attribution.ts (unit-tested). */\n private isSaverRelatedRuntimeFault(ev: ErrorEvent): boolean {\n return isSaverFaultFilename(ev.filename, location.origin);\n }\n\n private isSaverRelatedRejection(ev: PromiseRejectionEvent): boolean {\n const hint = String((ev.reason as Error)?.stack ?? ev.reason ?? '');\n return isSaverFaultRejectionHint(hint, location.origin);\n }\n\n private readonly onRuntimeError = (ev: ErrorEvent): void => {\n if (!this.dialog?.open || !this.instance) return;\n if (this._engine?.pluginById(this.activeSaverId)?.manifest.passthrough) return; // page noise, not ours\n if (!this.isSaverRelatedRuntimeFault(ev)) return;\n this.engageCrashScreen(ev.error ?? ev.message);\n };\n\n private readonly onRuntimeRejection = (ev: PromiseRejectionEvent): void => {\n if (!this.dialog?.open || !this.instance) return;\n if (this._engine?.pluginById(this.activeSaverId)?.manifest.passthrough) return;\n if (!this.isSaverRelatedRejection(ev)) return;\n this.engageCrashScreen(ev.reason);\n };\n\n /**\n * The graceful-degradation ladder for runtime saver faults. The screen must\n * stay a screen: dispose the faulted instance, then mount the configured\n * crash saver (config.crashSaverId — the BSOD, fittingly), and if that is\n * missing, IS the faulted saver, or faults too, render the built-in DOM\n * fault screen. One swap per sleep; any key still wakes.\n */\n private engageCrashScreen(err: unknown): void {\n if (!this.dialog?.open) return;\n const faultedId = this.activeSaverId;\n const message = err instanceof Error ? err.message : String(err ?? 'unknown fault');\n\n if (this.crashed) {\n console.warn(`[idle-screen] saver \"${faultedId}\" faulted again at runtime:`, err);\n ++this.mountToken;\n try {\n this.instance?.dispose();\n } catch {\n /* the faulted instance may not even dispose cleanly */\n }\n this.instance = null;\n this.surface.replaceChildren();\n renderFaultScreen(this.surface, { saverId: faultedId, message });\n return;\n }\n\n this.crashed = true;\n console.warn(`[idle-screen] saver \"${faultedId}\" faulted at runtime:`, err);\n ++this.mountToken; // invalidate any in-flight mount\n try {\n this.instance?.dispose();\n } catch {\n /* the faulted instance may not even dispose cleanly */\n }\n this.instance = null;\n this.surface.replaceChildren();\n\n const eng = this._engine;\n const crashId = eng?.config.crashSaverId;\n const crashPlugin = crashId && crashId !== faultedId ? eng?.pluginById(crashId) : undefined;\n if (crashPlugin && eng) {\n const token = this.mountToken;\n void Promise.resolve()\n .then(() =>\n crashPlugin.mount({\n host: this.surface,\n dpr: typeof devicePixelRatio === 'number' ? devicePixelRatio : 1,\n width: window.innerWidth,\n height: window.innerHeight,\n rng: createRng(eng.config.seed >>> 0),\n seed: eng.config.seed >>> 0,\n reducedMotion: eng.reducedMotion.value,\n page: crashPlugin.manifest.passthrough ? this.pageContext() : undefined,\n }),\n )\n .then((inst) => {\n if (token !== this.mountToken || !this.dialog?.open) {\n inst.dispose();\n return;\n }\n this.instance = inst;\n inst.setPaused(eng.reducedMotion.value);\n })\n .catch(() => {\n if (token !== this.mountToken || !this.dialog?.open) return;\n this.surface.replaceChildren();\n renderFaultScreen(this.surface, { saverId: faultedId, message });\n });\n } else {\n renderFaultScreen(this.surface, { saverId: faultedId, message });\n }\n }\n\n private onWakeInput(): void {\n if (!this.armed) return; // grace period after sleeping\n this._engine?.wake();\n }\n\n private teardown(): void {\n ++this.mountToken; // nor into a disconnected element\n window.removeEventListener('resize', this.onResize);\n window.removeEventListener('error', this.onRuntimeError);\n window.removeEventListener('unhandledrejection', this.onRuntimeRejection);\n if (this.resizeTimer) { clearTimeout(this.resizeTimer); this.resizeTimer = null; }\n void this.disposeInstance();\n this.terminateCachedWorker();\n if (this.armTimer) clearTimeout(this.armTimer);\n if (this.hintTimer) clearTimeout(this.hintTimer);\n if (this.closeTimer) clearTimeout(this.closeTimer);\n for (const d of this.disposers) d();\n this.disposers = [];\n if (this.ownsEngine) this._engine?.destroy(); // external engines are the caller's to destroy\n this._engine = null;\n }\n}\n\n/** Register the <idle-screen> custom element (idempotent). */\nexport function defineIdleScreen(tag = 'idle-screen'): void {\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\n customElements.define(tag, IdleScreenElement);\n }\n}\n","import { createRng } from './rng';\nimport type { ControlTrack, SaverContext, SaverInstance, SaverPlugin } from './types';\n\n/** Main thread → Worker messages. */\nexport type WorkerInbound =\n | {\n type: 'mount';\n canvas: OffscreenCanvas;\n saverId: string;\n width: number;\n height: number;\n seed: number;\n dpr: number;\n reducedMotion: boolean;\n /** Display refresh rate (Hz) for the rAF polyfill on browsers that lack\n * Worker requestAnimationFrame (Safari). Defaults to 60. */\n refreshRate?: number;\n /** Force the setTimeout-based rAF polyfill even when native rAF exists.\n * Used by tests to verify the polyfill drives frames. */\n forceRafPolyfill?: boolean;\n }\n | {\n type: 'mount-spec';\n canvas: OffscreenCanvas;\n spec: unknown;\n width: number;\n height: number;\n seed: number;\n dpr: number;\n reducedMotion: boolean;\n refreshRate?: number;\n forceRafPolyfill?: boolean;\n }\n | { type: 'resize'; width: number; height: number; dpr: number }\n | { type: 'pause'; paused: boolean }\n | { type: 'track'; track: ControlTrack }\n | { type: 'sample' }\n | { type: 'capture'; id: number }\n | { type: 'inspect'; id: number }\n | { type: 'dispose' };\n\n/** Worker → main thread messages. */\nexport type WorkerOutbound =\n | { type: 'mounted' }\n | { type: 'sampled'; hasContent: boolean }\n | { type: 'captured'; id: number; bitmap: ImageBitmap | null }\n | { type: 'inspected'; id: number; state: Record<string, unknown> | null }\n | { type: 'error'; message: string };\n\n/** Options for {@link runIdleWorker}. */\nexport interface RunIdleWorkerOpts {\n /** Spec compiler for `mount-spec` messages. Pass `compileSaver` from\n * `@idle-screens/schema` to enable schema-based savers in this Worker. */\n compiler?: (spec: unknown) => SaverPlugin;\n}\n\n/**\n * Run inside a dedicated Worker. Accepts a registry of worker-ready saver plugins\n * and listens for lifecycle messages from the main thread.\n *\n * Usage (in the Worker entry file):\n * ```ts\n * import { runIdleWorker } from '@idle-screens/core';\n * import { warp } from './warp';\n * runIdleWorker({ warp });\n * ```\n */\nexport function runIdleWorker(\n registry: Record<string, SaverPlugin>,\n opts?: RunIdleWorkerOpts,\n): void {\n let instance: SaverInstance | null = null;\n let activeCanvas: OffscreenCanvas | null = null;\n let usingPolyfill = false;\n let mountGen = 0;\n\n const post = (msg: WorkerOutbound): void => {\n self.postMessage(msg);\n };\n\n const installRafPolyfill = (refreshRate?: number): void => {\n const g = globalThis as Record<string, unknown>;\n const interval = refreshRate ? Math.round(1000 / refreshRate) : 16;\n g.requestAnimationFrame = (cb: FrameRequestCallback) =>\n setTimeout(() => cb(performance.now()), interval) as unknown as number;\n g.cancelAnimationFrame = (id: number) => clearTimeout(id);\n usingPolyfill = true;\n };\n\n if (typeof requestAnimationFrame === 'undefined') {\n installRafPolyfill();\n }\n\n const mountPlugin = (\n plugin: SaverPlugin,\n canvas: OffscreenCanvas,\n msg: {\n width: number; height: number; seed: number;\n dpr: number; reducedMotion: boolean;\n refreshRate?: number; forceRafPolyfill?: boolean;\n },\n gen: number,\n ): void => {\n if (msg.forceRafPolyfill) {\n installRafPolyfill(msg.refreshRate);\n } else if (usingPolyfill && msg.refreshRate) {\n installRafPolyfill(msg.refreshRate);\n }\n\n activeCanvas = canvas;\n const hostStub = new Proxy({} as HTMLElement, {\n get(_target, prop) {\n throw new Error(\n `Saver accessed host.${String(prop)} in a Worker — ` +\n `Workers have no DOM. Use ctx.surface instead of creating your own canvas.`,\n );\n },\n });\n const ctx: SaverContext = {\n host: hostStub,\n surface: canvas,\n dpr: msg.dpr,\n width: msg.width,\n height: msg.height,\n rng: createRng(msg.seed),\n seed: msg.seed,\n reducedMotion: msg.reducedMotion,\n };\n try {\n const result = plugin.mount(ctx);\n if (result instanceof Promise) {\n result.then((inst) => {\n if (gen !== mountGen) {\n inst.dispose();\n return;\n }\n instance = inst;\n post({ type: 'mounted' });\n }).catch((err: unknown) => {\n if (gen !== mountGen) return;\n post({ type: 'error', message: String(err) });\n });\n } else {\n if (gen !== mountGen) {\n result.dispose();\n return;\n }\n instance = result;\n post({ type: 'mounted' });\n }\n } catch (err: unknown) {\n if (gen !== mountGen) return;\n post({ type: 'error', message: String(err) });\n }\n };\n\n self.addEventListener('message', (raw: MessageEvent<WorkerInbound>) => {\n const msg = raw.data;\n switch (msg.type) {\n case 'mount': {\n const plugin = registry[msg.saverId];\n if (!plugin) {\n post({ type: 'error', message: `unknown saver: ${msg.saverId}` });\n return;\n }\n instance?.dispose();\n instance = null;\n const gen = ++mountGen;\n mountPlugin(plugin, msg.canvas, msg, gen);\n break;\n }\n case 'mount-spec': {\n if (!opts?.compiler) {\n post({ type: 'error', message: 'no compiler provided for mount-spec' });\n return;\n }\n try {\n const plugin = opts.compiler(msg.spec);\n instance?.dispose();\n instance = null;\n const gen = ++mountGen;\n mountPlugin(plugin, msg.canvas, msg, gen);\n } catch (err: unknown) {\n post({ type: 'error', message: String(err) });\n }\n break;\n }\n case 'resize':\n instance?.resize(msg.width, msg.height, msg.dpr);\n break;\n case 'pause':\n instance?.setPaused(msg.paused);\n break;\n case 'track':\n instance?.applyTrack?.(msg.track);\n break;\n case 'sample': {\n if (!activeCanvas) {\n post({ type: 'sampled', hasContent: false });\n return;\n }\n const c2d = activeCanvas.getContext('2d');\n if (!c2d) {\n post({ type: 'sampled', hasContent: false });\n return;\n }\n const cw = activeCanvas.width;\n const ch = activeCanvas.height;\n let hasContent = false;\n const COLS = 10;\n const ROWS = 10;\n for (let r = 0; r < ROWS && !hasContent; r++) {\n const y = Math.floor((ch * (r + 1)) / (ROWS + 1));\n for (let c = 0; c < COLS && !hasContent; c++) {\n const x = Math.floor((cw * (c + 1)) / (COLS + 1));\n const data = c2d.getImageData(x, y, 1, 1).data;\n if (data[0]! > 0 || data[1]! > 0 || data[2]! > 0 || data[3]! > 0) hasContent = true;\n }\n }\n post({ type: 'sampled', hasContent });\n break;\n }\n case 'inspect': {\n // State dump for perception (the worker owns the instance, so this\n // is the only read path). Synchronous by contract; a throwing saver\n // answers null rather than killing the worker.\n let state: Record<string, unknown> | null = null;\n try {\n state = instance?.inspect?.() ?? null;\n } catch {\n state = null;\n }\n post({ type: 'inspected', id: msg.id, state });\n break;\n }\n case 'capture': {\n // On-demand frame snapshot (the transferred canvas is unreadable from\n // the main thread, so this is the ONLY read path for worker savers).\n // Prefer the instance's own capture — it can render a fresh frame in\n // the same task, which matters for WebGL where the presented buffer\n // reads black. Plain-2d savers fall back to a direct bitmap of the\n // OffscreenCanvas, whose buffer persists between frames.\n const id = msg.id;\n const reply = (bitmap: ImageBitmap | null): void => {\n if (bitmap) {\n // Options form: valid on both the Window typing tsc sees here and\n // the DedicatedWorkerGlobalScope this actually runs in.\n self.postMessage({ type: 'captured', id, bitmap } satisfies WorkerOutbound, { transfer: [bitmap] });\n } else {\n post({ type: 'captured', id, bitmap: null });\n }\n };\n const own = instance?.capture?.();\n if (own) {\n own.then(reply, () => reply(null));\n } else if (activeCanvas) {\n createImageBitmap(activeCanvas).then(reply, () => reply(null));\n } else {\n reply(null);\n }\n break;\n }\n case 'dispose':\n mountGen++;\n instance?.dispose();\n instance = null;\n activeCanvas = null;\n break;\n }\n });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idle-screens/core",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Engine, <idle-screen> custom element, plugin registry, seeded RNG, control-track, and types for idle-screens.",
5
5
  "license": "MIT",
6
6
  "author": "Shawn Partridge",
@@ -39,7 +39,7 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@preact/signals-core": "^1.14.4"
42
+ "@preact/signals-core": "^1.14.5"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsup",