@idle-screens/core 0.2.0 → 0.4.0

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
@@ -84,7 +84,31 @@ interface SaverContext {
84
84
  /** Present for passthrough savers (over the live page). */
85
85
  page?: PageContext;
86
86
  }
87
- /** A mounted, running saver visual. */
87
+ /**
88
+ * One deck of a mounted saver's practical composition stack, bottom-up.
89
+ *
90
+ * This is deliberately NOT the schema's layer concept (declared sprite
91
+ * layers): it describes what a running instance is actually composed of, so
92
+ * hosts and tooling can present and inspect the stack uniformly. A
93
+ * passthrough saver is at minimum `page` + `surface`; a classic canvas saver
94
+ * is just its `surface`; savers may further describe the logical draw passes
95
+ * inside a surface. The model is intentionally small and open — future work
96
+ * (multi-surface savers, per-pass toggles, cross-saver scenes) extends it
97
+ * rather than replacing it.
98
+ */
99
+ interface SaverLayer {
100
+ id: string;
101
+ label: string;
102
+ /** 'page' = the live document a passthrough saver performs on;
103
+ * 'surface' = a rendering surface the instance owns (usually a canvas);
104
+ * 'pass' = a logical draw pass inside the surface above it. */
105
+ kind: 'page' | 'surface' | 'pass';
106
+ /** The element that IS this layer, when one exists. Hosts may toggle its
107
+ * visibility for inspection. A `page` layer has no element — the HOST
108
+ * owns the document and binds it (the saver only borrows it). */
109
+ el?: HTMLElement;
110
+ description?: string;
111
+ }
88
112
  interface SaverInstance {
89
113
  /** Freeze/unfreeze the render loop (paused, reduced-motion, or screensaver end). */
90
114
  setPaused(paused: boolean): void;
@@ -94,6 +118,12 @@ interface SaverInstance {
94
118
  renderFrame?(t: number, seed: number): void;
95
119
  /** Steer parameters over time. Optional. */
96
120
  applyTrack?(track: ControlTrack): void;
121
+ /** Seek logical animation time (ms) for workbench preview. Optional. */
122
+ previewAt?(t: number): void;
123
+ /** Describe the practical composition stack, bottom-up. Optional.
124
+ * (Named `composition`, not `layers`: schema-compiled instances already
125
+ * carry their declared sprite `layers` as data.) */
126
+ composition?(): SaverLayer[];
97
127
  /** Tear down; MUST restore any live-page mutations. */
98
128
  dispose(): void;
99
129
  }
@@ -117,6 +147,17 @@ interface SaverManifest {
117
147
  seed?: number;
118
148
  model?: string;
119
149
  };
150
+ /** Attribution for savers derived from licensed or third-party work — the
151
+ * concept, reference implementation, or embedded assets. Hosts SHOULD show
152
+ * this wherever the saver is showcased; the full ledger is CREDITS.md. */
153
+ attribution?: {
154
+ /** What this saver descends from, e.g. 'After Dark "Messages" — concept by Berkeley Systems'. */
155
+ source: string;
156
+ /** The licensing situation, ours and upstream, e.g. 'MIT port; reference CSS MIT (Bryan Braun)'. */
157
+ license: string;
158
+ /** Reference implementation or rights context. */
159
+ url?: string;
160
+ };
120
161
  thumbnail?: string;
121
162
  /** Saver renders on a provided surface (canvas/OffscreenCanvas) and avoids DOM
122
163
  * APIs, enabling Worker execution when the browser supports OffscreenCanvas. */
@@ -419,4 +460,4 @@ interface RunIdleWorkerOpts {
419
460
  */
420
461
  declare function runIdleWorker(registry: Record<string, SaverPlugin>, opts?: RunIdleWorkerOpts): void;
421
462
 
422
- 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 SaverManifest, type SaverPlugin, type WorkerInbound, type WorkerOutbound, createRng, defaultParams, defineIdleScreen, resolveConfigMenu, runIdleWorker, sampleTrack };
463
+ 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 };
package/dist/index.js CHANGED
@@ -413,6 +413,10 @@ var STYLE = `
413
413
  @keyframes ss-out { from { opacity: 1; } to { opacity: 0; } }
414
414
  .surface { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
415
415
  .surface > canvas { display: block; width: 100%; height: 100%; }
416
+ /* Host-owned fallback markup (<x slot="fallback">), shown only when the active
417
+ saver fails to mount \u2014 the host decides what a broken saver looks like. */
418
+ .fallback { position: absolute; inset: 0; display: none; place-items: center; }
419
+ dialog.frame.show-fallback .fallback { display: grid; }
416
420
  .clock {
417
421
  position: absolute; top: 10px; right: 12px; text-align: right;
418
422
  font-family: 'Orbitron', system-ui, sans-serif; pointer-events: none;
@@ -565,7 +569,12 @@ var IdleScreenElement = class extends HostBase {
565
569
  this.hintEl = document.createElement("div");
566
570
  this.hintEl.className = "hint";
567
571
  this.hintEl.textContent = "press any key";
568
- this.dialog.append(this.surface, this.clockEl, this.hintEl);
572
+ const fallback = document.createElement("div");
573
+ fallback.className = "fallback";
574
+ const slot = document.createElement("slot");
575
+ slot.name = "fallback";
576
+ fallback.append(slot);
577
+ this.dialog.append(this.surface, fallback, this.clockEl, this.hintEl);
569
578
  root.append(style, this.dialog);
570
579
  for (const ev of ["pointerdown", "pointermove", "keydown", "wheel", "touchstart"]) {
571
580
  this.dialog.addEventListener(ev, () => this.onWakeInput(), { passive: true });
@@ -730,13 +739,14 @@ var IdleScreenElement = class extends HostBase {
730
739
  }
731
740
  this.instance = inst;
732
741
  inst.setPaused(eng.reducedMotion.value);
742
+ this.dialog.classList.remove("show-fallback");
733
743
  return;
734
744
  } catch {
735
745
  await this.disposeInstance();
736
746
  if (token !== this.mountToken) return;
737
747
  }
738
748
  }
739
- {
749
+ try {
740
750
  const inst = await plugin.mount({
741
751
  host: this.surface,
742
752
  dpr,
@@ -753,6 +763,12 @@ var IdleScreenElement = class extends HostBase {
753
763
  }
754
764
  this.instance = inst;
755
765
  inst.setPaused(eng.reducedMotion.value);
766
+ this.dialog.classList.remove("show-fallback");
767
+ } catch (err) {
768
+ if (token !== this.mountToken) return;
769
+ console.warn(`[idle-screen] saver "${plugin.manifest.id}" failed to mount:`, err);
770
+ this.surface.replaceChildren();
771
+ this.dialog.classList.add("show-fallback");
756
772
  }
757
773
  }
758
774
  openInWorker(plugin, workerUrl, width, height, seed, dpr, reducedMotion, passthrough) {
@@ -854,6 +870,7 @@ var IdleScreenElement = class extends HostBase {
854
870
  clearTimeout(this.resizeTimer);
855
871
  this.resizeTimer = null;
856
872
  }
873
+ this.dialog.classList.remove("show-fallback");
857
874
  this.hintEl.classList.remove("show");
858
875
  if (this.hintTimer) clearTimeout(this.hintTimer);
859
876
  void this.disposeInstance();
@@ -946,6 +963,7 @@ function runIdleWorker(registry, opts) {
946
963
  let instance = null;
947
964
  let activeCanvas = null;
948
965
  let usingPolyfill = false;
966
+ let mountGen = 0;
949
967
  const post = (msg) => {
950
968
  self.postMessage(msg);
951
969
  };
@@ -959,7 +977,7 @@ function runIdleWorker(registry, opts) {
959
977
  if (typeof requestAnimationFrame === "undefined") {
960
978
  installRafPolyfill();
961
979
  }
962
- const mountPlugin = (plugin, canvas, msg) => {
980
+ const mountPlugin = (plugin, canvas, msg, gen) => {
963
981
  if (msg.forceRafPolyfill) {
964
982
  installRafPolyfill(msg.refreshRate);
965
983
  } else if (usingPolyfill && msg.refreshRate) {
@@ -987,16 +1005,26 @@ function runIdleWorker(registry, opts) {
987
1005
  const result = plugin.mount(ctx);
988
1006
  if (result instanceof Promise) {
989
1007
  result.then((inst) => {
1008
+ if (gen !== mountGen) {
1009
+ inst.dispose();
1010
+ return;
1011
+ }
990
1012
  instance = inst;
991
1013
  post({ type: "mounted" });
992
1014
  }).catch((err) => {
1015
+ if (gen !== mountGen) return;
993
1016
  post({ type: "error", message: String(err) });
994
1017
  });
995
1018
  } else {
1019
+ if (gen !== mountGen) {
1020
+ result.dispose();
1021
+ return;
1022
+ }
996
1023
  instance = result;
997
1024
  post({ type: "mounted" });
998
1025
  }
999
1026
  } catch (err) {
1027
+ if (gen !== mountGen) return;
1000
1028
  post({ type: "error", message: String(err) });
1001
1029
  }
1002
1030
  };
@@ -1009,7 +1037,10 @@ function runIdleWorker(registry, opts) {
1009
1037
  post({ type: "error", message: `unknown saver: ${msg.saverId}` });
1010
1038
  return;
1011
1039
  }
1012
- mountPlugin(plugin, msg.canvas, msg);
1040
+ instance?.dispose();
1041
+ instance = null;
1042
+ const gen = ++mountGen;
1043
+ mountPlugin(plugin, msg.canvas, msg, gen);
1013
1044
  break;
1014
1045
  }
1015
1046
  case "mount-spec": {
@@ -1019,7 +1050,10 @@ function runIdleWorker(registry, opts) {
1019
1050
  }
1020
1051
  try {
1021
1052
  const plugin = opts.compiler(msg.spec);
1022
- mountPlugin(plugin, msg.canvas, msg);
1053
+ instance?.dispose();
1054
+ instance = null;
1055
+ const gen = ++mountGen;
1056
+ mountPlugin(plugin, msg.canvas, msg, gen);
1023
1057
  } catch (err) {
1024
1058
  post({ type: "error", message: String(err) });
1025
1059
  }
@@ -1061,6 +1095,7 @@ function runIdleWorker(registry, opts) {
1061
1095
  break;
1062
1096
  }
1063
1097
  case "dispose":
1098
+ mountGen++;
1064
1099
  instance?.dispose();
1065
1100
  instance = null;
1066
1101
  activeCanvas = null;
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/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';\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 reducedMotion: boolean;\n /** Present for passthrough savers (over the live page). */\n page?: PageContext;\n}\n\n/** A mounted, running saver visual. */\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 /** 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 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 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\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 /** 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 / 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 /** 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","import { effect } from './reactive';\nimport { IdleScreensEngine } from './engine';\nimport { createRng } from './rng';\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 .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 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 this.dialog.append(this.surface, 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 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 }\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 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 {\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 }\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 if (this.resizeTimer) { clearTimeout(this.resizeTimer); this.resizeTimer = null; }\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 this.instance.resize(window.innerWidth, window.innerHeight, newDpr);\n }, 150);\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 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\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 ): 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 instance = inst;\n post({ type: 'mounted' });\n }).catch((err: unknown) => {\n post({ type: 'error', message: String(err) });\n });\n } else {\n instance = result;\n post({ type: 'mounted' });\n }\n } catch (err: unknown) {\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 mountPlugin(plugin, msg.canvas, msg);\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 mountPlugin(plugin, msg.canvas, msg);\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 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/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';\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 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 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\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 /** 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 / 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 /** 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","import { effect } from './reactive';\nimport { IdleScreensEngine } from './engine';\nimport { createRng } from './rng';\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 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 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 }\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 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 this.instance.resize(window.innerWidth, window.innerHeight, newDpr);\n }, 150);\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 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.2.0",
3
+ "version": "0.4.0",
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",