sia-reactor 0.0.21 → 0.0.22

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.
Files changed (38) hide show
  1. package/README.md +115 -89
  2. package/dist/{TimeTravelOverlay-CJv-S_Km.d.cts → TimeTravelOverlay-DiXUgbUU.d.cts} +9 -8
  3. package/dist/{TimeTravelOverlay-DxqJL0Zk.d.ts → TimeTravelOverlay-eWjAy0yr.d.ts} +9 -8
  4. package/dist/adapters/react.cjs +66 -84
  5. package/dist/adapters/react.d.cts +23 -22
  6. package/dist/adapters/react.d.ts +23 -22
  7. package/dist/adapters/react.js +3 -3
  8. package/dist/adapters/vanilla.cjs +66 -84
  9. package/dist/adapters/vanilla.d.cts +4 -4
  10. package/dist/adapters/vanilla.d.ts +4 -4
  11. package/dist/adapters/vanilla.js +3 -3
  12. package/dist/{chunk-2WBPGSRL.js → chunk-3SKLWTEA.js} +52 -70
  13. package/dist/{chunk-DP74DVRT.js → chunk-BTA6MIQ6.js} +40 -8
  14. package/dist/{chunk-TFLYCXK4.js → chunk-CS3FOV6J.js} +14 -13
  15. package/dist/{index-Oie9hhE8.d.cts → index-BgbbNXTW.d.cts} +306 -207
  16. package/dist/{index-Oie9hhE8.d.ts → index-BgbbNXTW.d.ts} +306 -207
  17. package/dist/index.cjs +54 -75
  18. package/dist/index.d.cts +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +2 -4
  21. package/dist/{plugins.cjs → modules.cjs} +437 -166
  22. package/dist/modules.d.cts +53 -0
  23. package/dist/modules.d.ts +53 -0
  24. package/dist/modules.js +627 -0
  25. package/dist/super.d.ts +610 -281
  26. package/dist/super.global.js +445 -174
  27. package/dist/timeTravel-CraHdbXZ.d.cts +352 -0
  28. package/dist/timeTravel-YUxRHRgh.d.ts +352 -0
  29. package/dist/utils.cjs +41 -7
  30. package/dist/utils.d.cts +1 -1
  31. package/dist/utils.d.ts +1 -1
  32. package/dist/utils.js +3 -1
  33. package/package.json +6 -6
  34. package/dist/plugins.d.cts +0 -112
  35. package/dist/plugins.d.ts +0 -112
  36. package/dist/plugins.js +0 -370
  37. package/dist/timeTravel-B1vedDQc.d.ts +0 -76
  38. package/dist/timeTravel-WpgWmKu-.d.cts +0 -76
@@ -31,7 +31,9 @@ module.exports = __toCommonJS(vanilla_exports);
31
31
  var CTX = {
32
32
  /** Flag indicating whether the application is running in development mode. */
33
33
  isDevEnv: "undefined" !== typeof process ? process.env.NODE_ENV !== "production" : true,
34
- /** active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
34
+ /** Flag indicating whether a cascade is currently ongoing so reactors can allow all writes. */
35
+ isCascading: false,
36
+ /** Active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
35
37
  autotracker: null
36
38
  };
37
39
  var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
@@ -43,7 +45,6 @@ var VERSION = /* @__PURE__ */ Symbol.for("S.I.A_VERSION");
43
45
  var SSVERSION = /* @__PURE__ */ Symbol.for("S.I.A_SNAPSHOT_VERSION");
44
46
  var RTR_BATCH = "undefined" !== typeof window ? ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window) : "undefined" !== typeof process && process.nextTick ? process.nextTick : setTimeout;
45
47
  var RTR_LOG = console.log.bind(console, "[S.I.A Reactor]");
46
- var EVT_WARN = console.warn.bind(console, "[S.I.A Event]");
47
48
  var EVT_OPTS = { LISTENER: ["capture", "depth", "once", "signal", "immediate"], MEDIATOR: ["lazy", "signal", "immediate"] };
48
49
  var NIL = Object.freeze({});
49
50
  var NOOP = () => {
@@ -142,10 +143,6 @@ function inAny(source, key, separator = ".", keyFunc) {
142
143
  function parseEvtOpts(options, opts, boolOpt = opts[0], result = {}) {
143
144
  return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
144
145
  }
145
- function mergeObjs(o1 = {}, o2 = {}) {
146
- const merged = { ...o1 || {}, ...o2 || {} };
147
- return Object.keys(merged).forEach((k) => isObj(o1?.[k]) && isObj(o2?.[k]) && (merged[k] = mergeObjs(o1[k], o2[k]))), merged;
148
- }
149
146
  function getTrailRecords(obj, path, reverse = false) {
150
147
  const parts = path.split("."), chain = [["*", obj, obj]];
151
148
  for (let acc = "", currObj = obj, i = 0, len = parts.length; i < len; i++) chain.push([acc += (i ? "." : "") + parts[i], currObj, currObj = currObj?.[parts[i]]]);
@@ -269,9 +266,9 @@ var Autotracker = class {
269
266
  * const stop = atrkr.callback(() => console.log("changed")); // re-run after when ".user.name" changes
270
267
  * @example Packaged Customization
271
268
  * const atrkr = new Autotracker(); // no reactor passed
272
- * withTracker(atrkr, () => state.user.name); // import `withTracker` first
269
+ * withTracker(atrkr, () => state.user.name); // import `withTracker` too
273
270
  * const stop = atrkr.callback(() => console.log("sync"), { sync: true }); // re-run immediately when ".user.name" changes, works on any path used from any reactor state
274
- * @example Extensive customization
271
+ * @example Extensive Customization
275
272
  * atrkr.unblock();
276
273
  * const prev = CTX.autotracker;
277
274
  * CTX.autotracker = atrkr; // import CTX first
@@ -340,35 +337,34 @@ var ReactorEvent = class _ReactorEvent {
340
337
  staticType;
341
338
  /** Original event target context. */
342
339
  target;
343
- /** Root reactive object for this event wave. */
340
+ /** Root reactive object for this event instance wave. */
344
341
  root;
345
- /** Original target path for this event wave. */
342
+ /** Original target path for this event instance wave. */
346
343
  path;
347
344
  /** Current value at the event target path. */
348
345
  value;
349
346
  /** Previous value at the event target path. */
350
347
  oldValue;
351
- /** Whether resolve/reject intent semantics are allowed for this event. */
348
+ /** Whether resolve/reject intent semantics are allowed for this event instance. */
352
349
  rejectable;
353
- /** Whether this event wave can bubble back up to ancestors or just capture down. */
350
+ /** Whether this event instance wave can bubble back up to ancestors or just capture down. */
354
351
  bubbles;
355
352
  /**
356
- * `DOMHighResTimeStamp` for this event payload for native event parity and accuracy.
353
+ * `DOMHighResTimeStamp` for this event instance payload for native event parity and accuracy.
357
354
  * Enable `eventTimeStamps` option, then use this over custom timestamps in listeners for accuracy.
358
355
  * */
359
356
  timestamp;
360
- _warn = NOOP;
357
+ /** The `Reactor` instance that dispatched this event instance. */
358
+ reactor;
361
359
  _resolved = "";
362
360
  _rejected = "";
363
361
  _propagationStopped = false;
364
362
  _immediatePropagationStopped = false;
365
363
  /**
366
364
  * @param payload Source payload for this event instance.
367
- * @param bubbles Whether bubbling is enabled for this wave.
368
- * @param canWarn Whether warning output is enabled.
369
- * @param canStamp Whether timestamping is enabled.
365
+ * @param reactor The `Reactor` instance creating this event instance.
370
366
  */
371
- constructor(payload, bubbles = false, canStamp = false, canWarn = true) {
367
+ constructor(payload, reactor) {
372
368
  this.staticType = this.type = payload.type;
373
369
  this.target = payload.target;
374
370
  this.currentTarget = payload.currentTarget;
@@ -377,9 +373,9 @@ var ReactorEvent = class _ReactorEvent {
377
373
  this.value = payload.target.value;
378
374
  this.oldValue = payload.target.oldValue;
379
375
  this.rejectable = payload.rejectable;
380
- this.bubbles = bubbles;
381
- if (canStamp) this.timestamp = performance.now();
382
- if (canWarn) this._warn = EVT_WARN;
376
+ this.bubbles = !!reactor.config.eventBubbling;
377
+ if (reactor.config.eventTimeStamps) this.timestamp = performance.now();
378
+ this.reactor = reactor;
383
379
  }
384
380
  /** Whether propagation has been stopped. */
385
381
  get propagationStopped() {
@@ -409,9 +405,9 @@ var ReactorEvent = class _ReactorEvent {
409
405
  * @example e.resolve("API Load successful"); // message
410
406
  */
411
407
  resolve(message) {
412
- if (!this.rejectable) return this._warn(`[ReactorEvent] Ignored resolve() call on a non-rejectable ${this.staticType} at "${this.path}"`);
413
- if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this._warn(`[ReactorEvent] Resolving an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
414
- if (this.rejectable) this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`;
408
+ if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`resolve()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
409
+ if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.reactor.log(`[ReactorEvent] Resolving an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
410
+ if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`}`);
415
411
  }
416
412
  /** Rejection reason for rejectable events. */
417
413
  get rejected() {
@@ -424,9 +420,9 @@ var ReactorEvent = class _ReactorEvent {
424
420
  * @example e.resolve("User is not logged in"); // reason
425
421
  */
426
422
  reject(reason) {
427
- if (!this.rejectable) return this._warn(`[ReactorEvent] Ignored reject() call on a non-rejectable ${this.staticType} at "${this.path}"`);
428
- if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this._warn(`[ReactorEvent] Rejecting an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
429
- if (this.rejectable) this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`;
423
+ if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`reject()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
424
+ if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.reactor.log(`[ReactorEvent] Rejecting an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
425
+ if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`}`);
430
426
  }
431
427
  /**
432
428
  * Returns event path values from target to root.
@@ -435,24 +431,22 @@ var ReactorEvent = class _ReactorEvent {
435
431
  composedPath() {
436
432
  return getTrailRecords(this.root, this.path, true).map((r) => r[2]);
437
433
  }
438
- get canWarn() {
439
- return this._warn !== NOOP;
440
- }
441
434
  };
442
435
 
443
436
  // src/ts/core/reactor.ts
444
437
  var Reactor = class {
438
+ /** Logger function for this reactor instance, override if desired, `this.canLog = false` resets. */
445
439
  log = NOOP;
440
+ /** The core state object for this reactor instance. */
446
441
  core;
447
442
  // `?:`s | pay the ~800 byte price upfront for what u might never use
448
- plugins;
443
+ /** The modules being used by this reactor. */
444
+ modules;
445
+ /** Configuration options for this reactor instance. */
449
446
  config;
447
+ /** Whether this reactor instance is currently batching updates, a window view into the engine timing */
450
448
  isBatching = false;
451
449
  // Async Batching
452
- isCascading = false;
453
- // Setter Cascading
454
- isLogging = false;
455
- // keeping track so API getter doesn't slow down internal iterations in any way
456
450
  queue;
457
451
  // Tasks to run after flush
458
452
  batch;
@@ -519,7 +513,7 @@ var Reactor = class {
519
513
  safeValue = value?.[RAW] || value;
520
514
  unchanged = this.config.equalityFunction(safeValue, safeOldValue);
521
515
  }
522
- if (!indiffable && unchanged && !this.isCascading) return this.log(`\u{1F504} [Reactor \`set\` Trap] Unchanged for "${keyStr}" on "${paths}"`), true;
516
+ if (!indiffable && unchanged && !CTX.isCascading) return this.log(`\u{1F504} [Reactor \`set\` Trap] Unchanged for "${keyStr}" on "${paths}"`), true;
523
517
  if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
524
518
  if (this.setters) {
525
519
  const wildcords = this.setters.get("*");
@@ -673,7 +667,7 @@ var Reactor = class {
673
667
  if (this.queue?.size) for (const task of this.queue) task(), this.queue.delete(task);
674
668
  }
675
669
  wave(path, payload) {
676
- const e = new ReactorEvent(payload, this.config.eventBubbling, this.config.eventTimeStamps, this.isLogging), chain = getTrailRecords(this.core, path);
670
+ const e = new ReactorEvent(payload, this), chain = getTrailRecords(this.core, path);
677
671
  e.eventPhase = ReactorEvent.CAPTURING_PHASE;
678
672
  for (let i = 0; i <= chain.length - 2; i++) {
679
673
  if (e.propagationStopped) break;
@@ -747,8 +741,8 @@ var Reactor = class {
747
741
  return depth;
748
742
  }
749
743
  getContext(path) {
750
- const lastDot = path.lastIndexOf("."), value = getAny(this.core, path), object = lastDot === -1 ? this.core : getAny(this.core, path.slice(0, lastDot));
751
- return { path, value, key: path.slice(lastDot + 1) || "", hadKey: true, object };
744
+ const last = path.lastIndexOf("."), value = getAny(this.core, path), object = last === -1 ? this.core : getAny(this.core, path.slice(0, last));
745
+ return { path, value, key: path.slice(last + 1) || "", hadKey: true, object };
752
746
  }
753
747
  bindSignal(cord, sig) {
754
748
  if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
@@ -764,7 +758,11 @@ var Reactor = class {
764
758
  const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
765
759
  seen.set(obj, clone);
766
760
  const keys2 = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
767
- for (let i = 0, len = keys2.length; i < len; i++) clone[keys2[i]] = this.cloned(obj[keys2[i]], raw, seen);
761
+ for (let i = 0, len = keys2.length; i < len; i++)
762
+ try {
763
+ clone[keys2[i]] = this.cloned(obj[keys2[i]], raw, seen);
764
+ } catch {
765
+ }
768
766
  if (!raw && this.config.smartCloning) this.snapCache.set(obj, clone), obj[SSVERSION] = version;
769
767
  return clone;
770
768
  }
@@ -852,7 +850,7 @@ var Reactor = class {
852
850
  * rtr.delete("cache.temp", () => TERMINATOR);
853
851
  */
854
852
  delete(path, callback, options) {
855
- return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path, void 0));
853
+ return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path));
856
854
  }
857
855
  /** Registers a delete mediator for a path that only triggers once. */
858
856
  donce(path, callback, options) {
@@ -869,7 +867,7 @@ var Reactor = class {
869
867
  }
870
868
  /**
871
869
  * Registers a watcher for a path.
872
- * Watch callbacks run synchronously with the operation.
870
+ * Watch callbacks run synchronously with the operation, use leaf paths for reliability as it sees exact sets; no bubbling here.
873
871
  * @param path Path or wildcard path.
874
872
  * @param callback Watch callback.
875
873
  * @param options Sync options.
@@ -918,7 +916,7 @@ var Reactor = class {
918
916
  cord = { cb: callback, capture, depth, once, clup: () => this.off(path, callback, options), lDepth: depth !== void 0 ? this.getDepth(path) : depth };
919
917
  if (immediate && (immediate !== "auto" || inAny(this.core, path))) {
920
918
  const target = this.getContext(path);
921
- callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this.config.eventBubbling, this.isLogging));
919
+ callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this));
922
920
  }
923
921
  (cords ?? (this.listeners.set(path, cords = []), cords)).push(cord);
924
922
  return this.bindSignal(cord, signal);
@@ -949,56 +947,39 @@ var Reactor = class {
949
947
  return this.cloned(arguments.length < 2 ? this.core : branch, raw);
950
948
  }
951
949
  /**
952
- * Cascades object updates into direct child paths.
953
- * @param payload Event or payload source.
954
- * @param objectSafe Merge old/new object values before cascading, don't set for arrays; merger doesn't play nice
955
- * @example
956
- * rtr.on("user", (event) => rtr.cascade(event));
957
- * @example
958
- * rtr.watch("user", (_value, payload) => rtr.cascade(payload));
959
- */
960
- cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objectSafe = true) {
961
- if (type !== "set" && type !== "delete" || !canHandle(news, this.config) || (objectSafe ? !canHandle(olds, this.config) : false)) return;
962
- const obj = objectSafe ? mergeObjs(olds, news) : news, keys2 = Object.keys(obj);
963
- this.isCascading = true;
964
- for (let i = 0, len = keys2.length; i < len; i++) setAny(this.core, path === "*" ? keys2[i] : path + "." + keys2[i], obj[keys2[i]]);
965
- this.isCascading = false;
966
- }
967
- /**
968
- * Installs a plugin instance.
969
- * @param plugin Plugin instance.
970
- * @returns Current reactor for fluent chaining.
950
+ * Installs a module instance.
951
+ * @param target Module instance.
952
+ * @param id Optional identification tag for this instance in the module.
953
+ * @returns Current `Reactor` instance for fluent chaining.
971
954
  */
972
- plugIn(plugin) {
973
- const name = plugin.constructor.plugName;
974
- this.plugins?.get(name)?.destroy();
975
- return (this.plugins ??= /* @__PURE__ */ new Map()).set(name, (plugin.setup(this), plugin)), this;
955
+ use(target, id) {
956
+ return (this.modules ??= /* @__PURE__ */ new Set()).add(target.setup(this, id)), this;
976
957
  }
977
- /** Resets the reactor to its initial state. */
958
+ /** Resets this reactor instance to its initial state. */
978
959
  reset() {
979
960
  this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear();
980
961
  this.batch?.clear(), this.queue?.clear(), this.isBatching = false;
981
962
  }
982
963
  destroy() {
983
- if (this.plugins) for (const plug of this.plugins.values()) plug.destroy();
964
+ if (this.modules) for (const mdle of this.modules) mdle.destroy();
984
965
  this.reset(), nuke(this);
985
966
  }
986
967
  get canLog() {
987
- return this.isLogging = this.log !== NOOP;
968
+ return this.log !== NOOP;
988
969
  }
989
970
  set canLog(value) {
990
- this.log = (this.isLogging = value) ? RTR_LOG : NOOP;
971
+ this.log = value ? RTR_LOG : NOOP;
991
972
  }
992
- get canTraceLineage() {
993
- return this.config.referenceTracking && !!this.config.lineageTracing;
973
+ get canLineageTrace() {
974
+ return this.config.lineageTracing && this.config.referenceTracking;
994
975
  }
995
976
  get canSmartClone() {
996
- return this.config.referenceTracking && !!this.config.smartCloning;
977
+ return this.config.smartCloning && this.config.referenceTracking;
997
978
  }
998
979
  };
999
980
 
1000
981
  // src/ts/core/mixins.ts
1001
- var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "cascade", "plugIn", "reset", "destroy"];
982
+ var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "use", "reset", "destroy"];
1002
983
  function reactive(target, build, preferences = NIL) {
1003
984
  if ("__Reactor__" in target) return target;
1004
985
  const descriptors = {}, rtr = getReactor(target, true, build), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(preferences.prefix || preferences.suffix);
@@ -1006,7 +987,7 @@ function reactive(target, build, preferences = NIL) {
1006
987
  let key = methods[i];
1007
988
  if (hasAffix) (preferences.whitelist?.includes(key) ?? true) && (key = `${preferences.prefix || ""}${key}${preferences.suffix || ""}`);
1008
989
  else if (preferences.whitelist?.includes(key)) continue;
1009
- descriptors[key] = { value: rtr[key].bind(rtr), ...locks };
990
+ descriptors[key] = { value: rtr[methods[i]].bind(rtr), ...locks };
1010
991
  }
1011
992
  descriptors["__Reactor__"] = { value: rtr, ...locks };
1012
993
  return Object.defineProperties(rtr.core, descriptors), rtr.core;
@@ -1088,8 +1069,8 @@ function assignEl(el, props, dataset, styles) {
1088
1069
 
1089
1070
  // src/ts/adapters/vanilla/TimeTravelOverlay.ts
1090
1071
  var keys = {
1091
- overrides: ["Ctrl+z", "Cmd+z", "Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z", "Ctrl+g", "Cmd+g", ",", ".", "ArrowLeft", "ArrowRight", "Space", "Alt+Space", "Escape", "Delete", "e", "i", "c"],
1092
- shortcuts: { undo: ["Ctrl+z", "Cmd+z"], redo: ["Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z"], genesis: ["Ctrl+g", "Cmd+g"], prevFrame: ",", nextFrame: ".", skipBwd: "ArrowLeft", skipFwd: "ArrowRight", playPause: "Space", rewind: "Alt+Space", closeOverlay: "Escape", clrHistory: "Delete", export: "e", import: "i", clear: "c" }
1072
+ overrides: ["Ctrl+z", "Cmd+z", "Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z", "Home", "End", ",", ".", "ArrowLeft", "ArrowRight", "Space", "Alt+Space", "Escape", "Delete", "e", "i", "c"],
1073
+ shortcuts: { undo: ["Ctrl+z", "Cmd+z"], redo: ["Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z"], genesis: "Home", ending: "End", prevFrame: ",", nextFrame: ".", skipBwd: "ArrowLeft", skipFwd: "ArrowRight", playPause: "Space", rewind: "Alt+Space", closeOverlay: "Escape", clrHistory: "Delete", export: "e", import: "i", clear: "c" }
1093
1074
  };
1094
1075
  var TimeTravelOverlay = class _TimeTravelOverlay {
1095
1076
  static count = 0;
@@ -1100,22 +1081,22 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
1100
1081
  els;
1101
1082
  clups = [];
1102
1083
  keyup;
1103
- /** Creates a docked TimeTravel overlay bound to a plugin instance.
1104
- * @param time TimeTravel plugin instance that owns timeline operations.
1084
+ /** Creates a docked TimeTravel overlay bound to a module instance.
1085
+ * @param time TimeTravel module instance that owns timeline operations.
1105
1086
  * @param build Optional initial overlay config overrides.
1106
1087
  */
1107
1088
  constructor(time, build = {}) {
1108
1089
  this.time = time;
1109
1090
  this.config = reactive({ title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
1110
1091
  this.state.open = !!this.config.startOpen;
1111
- const s = this.time.state, host = createEl("div", { className: "tt-overlay-host" }), toggle = createEl("button", { className: "tt-overlay-toggle", type: "button", onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "tt-overlay", ariaLabel: "time travel overlay" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo }), redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo }), genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis, false), onclick: () => this.time.jumpTo(0) }), playPause = createEl("button", { onclick: () => this.time[s.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause, false) }), rewind = createEl("button", { textContent: `Rewind${formatKeyForDisplay(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind, false), onclick: this.time.rewind }), range = createEl("input", { type: "range", min: "0", max: "0", value: "0", title: "time travel frame", ariaLabel: "time travel frame", oninput: () => this.time.jumpTo(Number(range.value)) }), exp = createEl("button", { textContent: `Export${formatKeyForDisplay(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export, false), onclick: () => this.state.import = this.time.export(null, 2) }), imp = createEl("button", { textContent: `Import${formatKeyForDisplay(keys.shortcuts.import)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.import, false), onclick: () => this.state.import.trim().length && this.time.import(this.state.import) }), clr = createEl("button", { textContent: `Clear${formatKeyForDisplay(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear, false), onclick: () => this.state.import = "" }), payload = createEl("textarea", { className: "tt-io", readOnly: true, placeholder: "current payload json", title: "current payload" }), io = createEl("textarea", { className: "tt-io", placeholder: "timeline payload json", oninput: () => this.state.import = io.value }), foot = createEl("p", { className: "tt-footnote", textContent: "Want this in your app? " }), link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" }), box = createEl("div", { className: "tt-status-box" }), status = createEl("div", { className: "tt-status-row" }), row1 = createEl("div", { className: "tt-row" }), row2 = createEl("div", { className: "tt-row" }), row3 = createEl("div", { className: "tt-row" });
1092
+ const s = this.time.state, host = createEl("div", { className: "tt-overlay-host" }), toggle = createEl("button", { className: "tt-overlay-toggle", type: "button", onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "tt-overlay", ariaLabel: "time travel overlay" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo }), redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo }), genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis, false), onclick: () => this.time.jumpTo(0) }), playPause = createEl("button", { onclick: () => this.time[s.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause, false) }), rewind = createEl("button", { textContent: `Rewind${formatKeyForDisplay(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind, false), onclick: this.time.rewind }), range = createEl("input", { type: "range", min: "0", max: "0", value: "0", title: "time travel frame", ariaLabel: "time travel frame", oninput: () => this.time.jumpTo(Number(range.value)) }), exp = createEl("button", { textContent: `Export${formatKeyForDisplay(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export, false), onclick: () => this.state.import = this.time.export(null, 2) }), imp = createEl("button", { textContent: `Import${formatKeyForDisplay(keys.shortcuts.import)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.import, false), onclick: () => this.state.import.trim().length && this.time.import(this.state.import) }), clr = createEl("button", { textContent: `Clear${formatKeyForDisplay(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear, false), onclick: () => this.state.import = "" }), payload = createEl("textarea", { className: "tt-io", readOnly: true, placeholder: "current payload json", title: "current payload" }), io = createEl("textarea", { className: "tt-io", placeholder: "timeline payload json", oninput: () => this.state.import = io.value }), foot = createEl("p", { className: "tt-footnote", textContent: "Want this in your app? " }), link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" }), box = createEl("div", { className: "tt-status-box" }), status = createEl("div", { className: "tt-status-row" }), row1 = createEl("div", { className: "tt-row" }), row2 = createEl("div", { className: "tt-row" }), row3 = createEl("div", { className: "tt-row" });
1112
1093
  status.append((box.append(frame), box), clrHistory);
1113
1094
  panel.append(title, status, (row1.append(undo, redo, genesis), row1), (row2.append(playPause, rewind), row2), payload, range, (row3.append(exp, imp, clr), row3), io, (foot.appendChild(link), foot));
1114
1095
  host.append(toggle, panel);
1115
1096
  this.els = { host, toggle, panel, title, frame, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, payload, io };
1116
1097
  this.keyup = (e) => {
1117
- const a = this.state.open && keyEventAllowed(e, keys);
1118
- a === "undo" ? this.time.undo() : a === "redo" ? this.time.redo() : a === "genesis" ? this.time.jumpTo(0) : a === "prevFrame" ? this.time.step(1, false) : a === "nextFrame" ? this.time.step(1, true) : a === "skipBwd" ? this.time.step(5, false) : a === "skipFwd" ? this.time.step(5, true) : a === "rewind" ? this.time.rewind() : a === "playPause" ? this.time[s.paused ? "play" : "pause"]() : a === "clrHistory" ? this.time.clear() : a === "closeOverlay" ? this.state.open = false : a === "export" ? this.state.import = this.time.export() : a === "import" ? this.state.import.trim().length && this.time.import(this.state.import) : a === "clear" && (this.state.import = "");
1098
+ const a = this.state.open && (this.config.devOnly ? CTX.isDevEnv : true) && keyEventAllowed(e, keys);
1099
+ a === "undo" ? this.time.undo() : a === "redo" ? this.time.redo() : a === "genesis" ? this.time.jumpTo(0) : a === "ending" ? this.time.jumpTo(s.history.length) : a === "prevFrame" ? this.time.step(1, false) : a === "nextFrame" ? this.time.step(1, true) : a === "skipBwd" ? this.time.step(5, false) : a === "skipFwd" ? this.time.step(5, true) : a === "rewind" ? this.time.rewind() : a === "playPause" ? this.time[s.paused ? "play" : "pause"]() : a === "clrHistory" ? this.time.clear() : a === "closeOverlay" ? this.state.open = false : a === "export" ? this.state.import = this.time.export() : a === "import" ? this.state.import.trim().length && this.time.import(this.state.import) : a === "clear" && (this.state.import = "");
1119
1100
  };
1120
1101
  window.addEventListener("keydown", this.keyup);
1121
1102
  const sync = [
@@ -1130,7 +1111,8 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
1130
1111
  effect(() => {
1131
1112
  frame.textContent = `Frame: ${s.currentFrame} / ${s.history.length}`;
1132
1113
  range.disabled = clrHistory.disabled = !s.history.length;
1133
- genesis.disabled = rewind.disabled = undo.disabled = !s.currentFrame;
1114
+ genesis.disabled = undo.disabled = !s.currentFrame;
1115
+ rewind.disabled = !s.paused || !s.currentFrame;
1134
1116
  playPause.disabled = redo.disabled = s.currentFrame >= s.history.length;
1135
1117
  range.max = String(s.history.length);
1136
1118
  range.value = String(Math.min(s.currentFrame, s.history.length));
@@ -1144,7 +1126,7 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
1144
1126
  this.clups.push(...sync);
1145
1127
  }
1146
1128
  destroy() {
1147
- this.clups.forEach((fn) => fn());
1129
+ for (const clup of this.clups) clup();
1148
1130
  this.keyup && window.removeEventListener("keydown", this.keyup);
1149
1131
  this.els.host.remove();
1150
1132
  nuke(this), --_TimeTravelOverlay.count;
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions } from '../index-Oie9hhE8.cjs';
2
- export { A as Autotracker, w as withTracker } from '../index-Oie9hhE8.cjs';
3
- export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-CJv-S_Km.cjs';
4
- import '../timeTravel-WpgWmKu-.cjs';
1
+ import { E as EffectOptions } from '../index-BgbbNXTW.cjs';
2
+ export { A as Autotracker, w as withTracker } from '../index-BgbbNXTW.cjs';
3
+ export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-DiXUgbUU.cjs';
4
+ import '../timeTravel-CraHdbXZ.cjs';
5
5
 
6
6
  /**
7
7
  * Runs a reactive side effect in vanilla JavaScript.
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions } from '../index-Oie9hhE8.js';
2
- export { A as Autotracker, w as withTracker } from '../index-Oie9hhE8.js';
3
- export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-DxqJL0Zk.js';
4
- import '../timeTravel-B1vedDQc.js';
1
+ import { E as EffectOptions } from '../index-BgbbNXTW.js';
2
+ export { A as Autotracker, w as withTracker } from '../index-BgbbNXTW.js';
3
+ export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-eWjAy0yr.js';
4
+ import '../timeTravel-YUxRHRgh.js';
5
5
 
6
6
  /**
7
7
  * Runs a reactive side effect in vanilla JavaScript.
@@ -3,11 +3,11 @@ import {
3
3
  TimeTravelOverlay,
4
4
  effect,
5
5
  withTracker
6
- } from "../chunk-TFLYCXK4.js";
7
- import "../chunk-2WBPGSRL.js";
6
+ } from "../chunk-CS3FOV6J.js";
7
+ import "../chunk-3SKLWTEA.js";
8
8
  import "../chunk-5A44QFT6.js";
9
9
  import "../chunk-P37ADJMM.js";
10
- import "../chunk-DP74DVRT.js";
10
+ import "../chunk-BTA6MIQ6.js";
11
11
  export {
12
12
  Autotracker,
13
13
  TimeTravelOverlay,