@idle-screens/core 0.4.4 → 0.4.5

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
@@ -260,6 +260,28 @@ declare const DEFAULT_CONFIG: IdleScreensConfig;
260
260
  declare function sampleTrack(space: ParamSpace, track: ControlTrack, t: number): Record<string, ParamValue>;
261
261
  /** Default param values (the "resting" program before any track is applied). */
262
262
  declare function defaultParams(space: ParamSpace): Record<string, ParamValue>;
263
+ /**
264
+ * Closed-form ∫₀ᵗ v(τ) dτ of one number param's track curve — the exact curve
265
+ * `sampleTrack` emits for that path, integrated analytically segment by
266
+ * segment (constant spans, plus linear/smooth/step ramps with `dur` and
267
+ * previous-keyframe ramp starts handled identically to sampling).
268
+ *
269
+ * Why it exists: a saver that multiplies a rate by the *sampled* value scales
270
+ * its entire accumulated motion when the param changes — a live steer
271
+ * teleports everything proportionally to elapsed time. Integrating the curve
272
+ * instead makes rate changes glide, while staying a pure function of
273
+ * `(space, track, t)` — no accumulated state, so `renderFrame(t)` stays
274
+ * frame-addressable and scrubbing works.
275
+ *
276
+ * Units: `t` is track time in ms; the result is value·ms (divide by 1000 for
277
+ * value·seconds). Looping tracks integrate as
278
+ * `fullLoops · ∫₀ᵈᵘʳ + ∫₀ʳᵉᵐ`. Non-`number` params integrate their numeric
279
+ * default (or 0). Like `sampleTrack`, no min/max clamping is applied.
280
+ */
281
+ declare function integrateParam(space: ParamSpace, track: ControlTrack, path: string, t: number, bounds?: {
282
+ min?: number;
283
+ max?: number;
284
+ }): number;
263
285
 
264
286
  /**
265
287
  * Detects user inactivity. `idle` flips true once no activity for `timeoutMs`.
@@ -415,8 +437,8 @@ declare class IdleScreenElement extends HostBase {
415
437
  private scheduleHint;
416
438
  private readonly onResize;
417
439
  /** An uncaught error while a non-passthrough saver owns the screen is the
418
- * saver's loop dying. Swap to the crash saver instead of freezing black. */
419
- private static readonly SAVER_FAULT_LOC;
440
+ * saver's loop dying. Swap to the crash saver instead of freezing black.
441
+ * Attribution semantics live in fault-attribution.ts (unit-tested). */
420
442
  private isSaverRelatedRuntimeFault;
421
443
  private isSaverRelatedRejection;
422
444
  private readonly onRuntimeError;
@@ -507,4 +529,4 @@ interface RunIdleWorkerOpts {
507
529
  */
508
530
  declare function runIdleWorker(registry: Record<string, SaverPlugin>, opts?: RunIdleWorkerOpts): void;
509
531
 
510
- 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, resolveConfigMenu, runIdleWorker, sampleTrack };
532
+ 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 };
package/dist/index.js CHANGED
@@ -89,8 +89,26 @@ function lerpValue(type, a, b, p) {
89
89
  }
90
90
  return p >= 1 ? b : a;
91
91
  }
92
+ function numericValue(v) {
93
+ if (typeof v === "number") return Number.isFinite(v) ? v : null;
94
+ if (typeof v === "string") {
95
+ const n = Number(v);
96
+ return v.trim() !== "" && Number.isFinite(n) ? n : null;
97
+ }
98
+ return null;
99
+ }
92
100
  function evalPath(dflt, type, defaultEase, deltas, t) {
93
101
  if (deltas.length === 0) return dflt;
102
+ if (type === "number") {
103
+ const cleaned = [];
104
+ for (const k of deltas) {
105
+ const n = numericValue(k.value);
106
+ if (n === null) continue;
107
+ cleaned.push(n === k.value ? k : { ...k, value: n });
108
+ }
109
+ deltas = cleaned;
110
+ if (deltas.length === 0) return dflt;
111
+ }
94
112
  let prevVal = dflt;
95
113
  let prevT = -Infinity;
96
114
  for (const k of deltas) {
@@ -125,6 +143,74 @@ function defaultParams(space) {
125
143
  for (const [path, def] of Object.entries(space)) out[path] = def.default;
126
144
  return out;
127
145
  }
146
+ function easeIntegral(e, u) {
147
+ const x = u <= 0 ? 0 : u >= 1 ? 1 : u;
148
+ switch (e) {
149
+ case "step":
150
+ return 0;
151
+ case "smooth":
152
+ return x * x * x - x * x * x * x / 2;
153
+ // ∫ 3x² − 2x³
154
+ case "linear":
155
+ default:
156
+ return x * x / 2;
157
+ }
158
+ }
159
+ function integrateDeltas(dflt, defaultEase, deltas, t) {
160
+ let acc = 0;
161
+ let cursor = 0;
162
+ let prevVal = dflt;
163
+ let prevT = -Infinity;
164
+ for (const k of deltas) {
165
+ const rampStart = k.dur != null ? k.t - k.dur : prevT === -Infinity ? k.t : prevT;
166
+ const D0 = k.t - rampStart;
167
+ const end = Math.min(t, k.t);
168
+ const constEnd = Math.min(Math.max(rampStart, cursor), end);
169
+ if (constEnd > cursor) acc += prevVal * (constEnd - cursor);
170
+ if (D0 > 0 && end > constEnd) {
171
+ const ease = k.ease ?? defaultEase;
172
+ const ua = (constEnd - rampStart) / D0;
173
+ const ub = (end - rampStart) / D0;
174
+ acc += prevVal * (end - constEnd) + (k.value - prevVal) * D0 * (easeIntegral(ease, ub) - easeIntegral(ease, ua));
175
+ }
176
+ if (t <= k.t) return acc;
177
+ cursor = k.t;
178
+ prevVal = k.value;
179
+ prevT = k.t;
180
+ }
181
+ return acc + prevVal * (t - cursor);
182
+ }
183
+ function integrateParam(space, track, path, t, bounds) {
184
+ const def = space[path];
185
+ if (!def) return 0;
186
+ const clamp = (n) => {
187
+ if (bounds?.min !== void 0 && n < bounds.min) return bounds.min;
188
+ if (bounds?.max !== void 0 && n > bounds.max) return bounds.max;
189
+ return n;
190
+ };
191
+ const dflt = clamp(numericValue(def.default) ?? 0);
192
+ if (def.type !== "number" || t <= 0) return dflt * Math.max(0, t);
193
+ const deltas = [];
194
+ for (const d of track.deltas) {
195
+ if (d.path !== path) continue;
196
+ const raw = numericValue(d.value);
197
+ if (raw === null) continue;
198
+ const value = clamp(raw);
199
+ const entry = { t: d.t, value };
200
+ if (d.ease !== void 0) entry.ease = d.ease;
201
+ if (d.dur !== void 0) entry.dur = d.dur;
202
+ deltas.push(entry);
203
+ }
204
+ deltas.sort((a, b) => a.t - b.t);
205
+ const defaultEase = def.ease ?? "linear";
206
+ if (track.loop && track.duration && track.duration > 0) {
207
+ const loops = Math.floor(t / track.duration);
208
+ const rem = t - loops * track.duration;
209
+ const oneLoop = integrateDeltas(dflt, defaultEase, deltas, track.duration);
210
+ return loops * oneLoop + integrateDeltas(dflt, defaultEase, deltas, rem);
211
+ }
212
+ return integrateDeltas(dflt, defaultEase, deltas, t);
213
+ }
128
214
 
129
215
  // src/idle-detector.ts
130
216
  var IdleDetector = class _IdleDetector {
@@ -437,6 +523,28 @@ function renderFaultScreen(host, info) {
437
523
  return root;
438
524
  }
439
525
 
526
+ // src/fault-attribution.ts
527
+ var SAVER_FAULT_LOC = /idle-screens|saver-|@idle-screens|three|metaquarium|schema|core|capabilities/i;
528
+ function sameOrigin(url, origin) {
529
+ try {
530
+ return new URL(url, origin === "null" ? void 0 : origin).origin === origin;
531
+ } catch {
532
+ return false;
533
+ }
534
+ }
535
+ function isSaverFaultFilename(filename, origin) {
536
+ if (typeof filename !== "string" || filename.length === 0) return true;
537
+ if (SAVER_FAULT_LOC.test(filename)) return true;
538
+ return sameOrigin(filename, origin);
539
+ }
540
+ function isSaverFaultRejectionHint(hint, origin) {
541
+ if (!hint) return true;
542
+ if (SAVER_FAULT_LOC.test(hint)) return true;
543
+ const urls = hint.match(/https?:\/\/[^\s)]+/gi);
544
+ if (!urls) return true;
545
+ return urls.some((u) => sameOrigin(u, origin));
546
+ }
547
+
440
548
  // src/idle-screen.element.ts
441
549
  var STYLE = `
442
550
  :host { all: initial; }
@@ -525,7 +633,7 @@ function supportsModuleWorker() {
525
633
  }
526
634
  return moduleWorkerDetected;
527
635
  }
528
- var IdleScreenElement = class _IdleScreenElement extends HostBase {
636
+ var IdleScreenElement = class extends HostBase {
529
637
  _engine = null;
530
638
  ownsEngine = false;
531
639
  _externalEngine = null;
@@ -987,20 +1095,14 @@ var IdleScreenElement = class _IdleScreenElement extends HostBase {
987
1095
  }, 150);
988
1096
  };
989
1097
  /** An uncaught error while a non-passthrough saver owns the screen is the
990
- * saver's loop dying. Swap to the crash saver instead of freezing black. */
991
- static SAVER_FAULT_LOC = /idle-screens|saver-|@idle-screens|three|metaquarium|schema|core|capabilities/i;
1098
+ * saver's loop dying. Swap to the crash saver instead of freezing black.
1099
+ * Attribution semantics live in fault-attribution.ts (unit-tested). */
992
1100
  isSaverRelatedRuntimeFault(ev) {
993
- const filename = ev.filename;
994
- if (typeof filename === "string" && filename.length > 0) {
995
- return _IdleScreenElement.SAVER_FAULT_LOC.test(filename);
996
- }
997
- return true;
1101
+ return isSaverFaultFilename(ev.filename, location.origin);
998
1102
  }
999
1103
  isSaverRelatedRejection(ev) {
1000
1104
  const hint = String(ev.reason?.stack ?? ev.reason ?? "");
1001
- if (!hint) return true;
1002
- if (_IdleScreenElement.SAVER_FAULT_LOC.test(hint)) return true;
1003
- return !/https?:\/\//i.test(hint);
1105
+ return isSaverFaultRejectionHint(hint, location.origin);
1004
1106
  }
1005
1107
  onRuntimeError = (ev) => {
1006
1108
  if (!this.dialog?.open || !this.instance) return;
@@ -1264,6 +1366,7 @@ export {
1264
1366
  defaultParams,
1265
1367
  defineIdleScreen,
1266
1368
  effect,
1369
+ integrateParam,
1267
1370
  resolveConfigMenu,
1268
1371
  runIdleWorker,
1269
1372
  sampleTrack,
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/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 /** 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\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 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","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","import { effect } from './reactive';\nimport { IdleScreensEngine } from './engine';\nimport { createRng } from './rng';\nimport { renderFaultScreen } from './fault-screen';\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 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 const proxy: SaverInstance = {\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 dispose: () => {\n disposed = true;\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 };\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 private static readonly SAVER_FAULT_LOC =\n /idle-screens|saver-|@idle-screens|three|metaquarium|schema|core|capabilities/i;\n\n private isSaverRelatedRuntimeFault(ev: ErrorEvent): boolean {\n const filename = ev.filename;\n if (typeof filename === 'string' && filename.length > 0) {\n return IdleScreenElement.SAVER_FAULT_LOC.test(filename);\n }\n return true;\n }\n\n private isSaverRelatedRejection(ev: PromiseRejectionEvent): boolean {\n const hint = String((ev.reason as Error)?.stack ?? ev.reason ?? '');\n if (!hint) return true;\n if (IdleScreenElement.SAVER_FAULT_LOC.test(hint)) return true;\n // No script URL in the hint → treat as saver-owned (inline / synthetic).\n // A same-origin page URL without saver markers is host noise — skip.\n return !/https?:\\/\\//i.test(hint);\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: 'dispose' };\n\n/** Worker → main thread messages. */\nexport type WorkerOutbound =\n | { type: 'mounted' }\n | { type: 'sampled'; hasContent: boolean }\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 '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 /** 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 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 const proxy: SaverInstance = {\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 dispose: () => {\n disposed = true;\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 };\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: 'dispose' };\n\n/** Worker → main thread messages. */\nexport type WorkerOutbound =\n | { type: 'mounted' }\n | { type: 'sampled'; hasContent: boolean }\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 '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.4",
3
+ "version": "0.4.5",
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",