sia-reactor 0.0.19 → 0.0.21

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 +102 -58
  2. package/dist/TimeTravelOverlay-CJv-S_Km.d.cts +41 -0
  3. package/dist/TimeTravelOverlay-DxqJL0Zk.d.ts +41 -0
  4. package/dist/adapters/react.cjs +308 -92
  5. package/dist/adapters/react.d.cts +68 -10
  6. package/dist/adapters/react.d.ts +68 -10
  7. package/dist/adapters/react.js +80 -29
  8. package/dist/adapters/vanilla.cjs +957 -10
  9. package/dist/adapters/vanilla.d.cts +4 -2
  10. package/dist/adapters/vanilla.d.ts +4 -2
  11. package/dist/adapters/vanilla.js +8 -12
  12. package/dist/{chunk-Q2AKMIHN.js → chunk-2WBPGSRL.js} +106 -48
  13. package/dist/chunk-5A44QFT6.js +93 -0
  14. package/dist/{chunk-4MJUBEI7.js → chunk-DP74DVRT.js} +4 -2
  15. package/dist/{chunk-UQIMMJTY.js → chunk-P37ADJMM.js} +3 -3
  16. package/dist/chunk-TFLYCXK4.js +251 -0
  17. package/dist/{index-C2hyIh0K.d.cts → index-Oie9hhE8.d.cts} +14 -10
  18. package/dist/{index-C2hyIh0K.d.ts → index-Oie9hhE8.d.ts} +14 -10
  19. package/dist/index.cjs +45 -52
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +4 -6
  23. package/dist/plugins.cjs +72 -73
  24. package/dist/plugins.d.cts +4 -75
  25. package/dist/plugins.d.ts +4 -75
  26. package/dist/plugins.js +27 -22
  27. package/dist/styles/time-travel-overlay.css +189 -0
  28. package/dist/super.d.ts +79 -26
  29. package/dist/super.global.js +200 -105
  30. package/dist/timeTravel-B1vedDQc.d.ts +76 -0
  31. package/dist/timeTravel-WpgWmKu-.d.cts +76 -0
  32. package/dist/utils.cjs +34 -7
  33. package/dist/utils.d.cts +9 -2
  34. package/dist/utils.d.ts +9 -2
  35. package/dist/utils.js +17 -65
  36. package/package.json +3 -2
  37. package/dist/chunk-FGUCKMLH.js +0 -154
  38. package/dist/chunk-KIQP7G7W.js +0 -80
@@ -17,27 +17,32 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/adapters/react.ts
20
+ // src/ts/adapters/react.ts
21
21
  var react_exports = {};
22
22
  __export(react_exports, {
23
+ TimeTravelOverlay: () => TimeTravelOverlay2,
23
24
  useAnyReactor: () => useAnyReactor,
24
25
  useAnySelector: () => useAnySelector,
25
26
  useISOLayoutEffect: () => useISOLayoutEffect,
26
27
  usePath: () => usePath,
27
28
  useReactor: () => useReactor,
28
- useSelector: () => useSelector
29
+ useReactorSnapshot: () => useReactorSnapshot,
30
+ useSelector: () => useSelector,
31
+ useSelectorSnapshot: () => useSelectorSnapshot
29
32
  });
30
33
  module.exports = __toCommonJS(react_exports);
31
34
 
32
- // src/adapters/react/utils.ts
35
+ // src/ts/adapters/react/hooks/useReactor.ts
36
+ var import_react2 = require("react");
37
+
38
+ // src/ts/adapters/react/utils.ts
33
39
  var import_react = require("react");
34
40
  var useISOLayoutEffect = "undefined" !== typeof window ? import_react.useLayoutEffect : import_react.useEffect;
35
41
 
36
- // src/adapters/react/hooks/useReactor.ts
37
- var import_react2 = require("react");
38
-
39
- // src/core/consts.ts
42
+ // src/ts/core/consts.ts
40
43
  var CTX = {
44
+ /** Flag indicating whether the application is running in development mode. */
45
+ isDevEnv: "undefined" !== typeof process ? process.env.NODE_ENV !== "production" : true,
41
46
  /** active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
42
47
  autotracker: null
43
48
  };
@@ -56,7 +61,7 @@ var NIL = Object.freeze({});
56
61
  var NOOP = () => {
57
62
  };
58
63
 
59
- // src/utils/obj.ts
64
+ // src/ts/utils/obj.ts
60
65
  var arrRx = /^([^\[\]]+)\[(\d+)\]$/;
61
66
  function isObj(obj, arraycheck = true) {
62
67
  return "object" === typeof obj && obj !== null && (arraycheck ? !Array.isArray(obj) : true);
@@ -73,10 +78,10 @@ function canHandle(obj, config = NIL, typecheck = true) {
73
78
  function getAny(source, key, separator = ".", keyFunc) {
74
79
  if (key === "*") return source;
75
80
  if (!key.includes(separator)) return source[keyFunc ? keyFunc(key) : key];
76
- const keys = key.split(separator);
81
+ const keys2 = key.split(separator);
77
82
  let currObj = source;
78
- for (let i = 0, len = keys.length; i < len; i++) {
79
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
83
+ for (let i = 0, len = keys2.length; i < len; i++) {
84
+ const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRx);
80
85
  if (match) {
81
86
  const [, key3, iStr] = match;
82
87
  if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return void 0;
@@ -91,9 +96,9 @@ function getAny(source, key, separator = ".", keyFunc) {
91
96
  function setAny(target, key, value, separator = ".", keyFunc) {
92
97
  if (key === "*") return Object.assign(target, value);
93
98
  if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
94
- const keys = key.split(separator);
95
- for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
96
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
99
+ const keys2 = key.split(separator);
100
+ for (let currObj = target, i = 0, len = keys2.length; i < len; i++) {
101
+ const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRx);
97
102
  if (match) {
98
103
  const [, key3, iStr] = match;
99
104
  if (!Array.isArray(currObj[key3])) currObj[key3] = [];
@@ -107,14 +112,14 @@ function setAny(target, key, value, separator = ".", keyFunc) {
107
112
  }
108
113
  function deleteAny(target, key, separator = ".", keyFunc) {
109
114
  if (key === "*") {
110
- const keys2 = Object.keys(target);
111
- for (let i = 0, len = keys2.length; i < len; i++) delete target[keys2[i]];
115
+ const keys3 = Object.keys(target);
116
+ for (let i = 0, len = keys3.length; i < len; i++) delete target[keys3[i]];
112
117
  return;
113
118
  }
114
119
  if (!key.includes(separator)) return void delete target[keyFunc ? keyFunc(key) : key];
115
- const keys = key.split(separator);
116
- for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
117
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
120
+ const keys2 = key.split(separator);
121
+ for (let currObj = target, i = 0, len = keys2.length; i < len; i++) {
122
+ const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRx);
118
123
  if (match) {
119
124
  const [, key3, iStr] = match;
120
125
  if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return;
@@ -130,9 +135,9 @@ function deleteAny(target, key, separator = ".", keyFunc) {
130
135
  function inAny(source, key, separator = ".", keyFunc) {
131
136
  if (key === "*") return true;
132
137
  if (!key.includes(separator)) return key in source;
133
- const keys = key.split(separator);
134
- for (let currObj = source, i = 0, len = keys.length; i < len; i++) {
135
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
138
+ const keys2 = key.split(separator);
139
+ for (let currObj = source, i = 0, len = keys2.length; i < len; i++) {
140
+ const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRx);
136
141
  if (match) {
137
142
  const [, key3, iStr] = match;
138
143
  if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return false;
@@ -161,18 +166,18 @@ function getTrailRecords(obj, path, reverse = false) {
161
166
  function nuke(target) {
162
167
  let proto = target;
163
168
  while (proto && proto !== Object.prototype) {
164
- const keys = Object.getOwnPropertyNames(proto);
165
- for (let i = 0, len = keys.length; i < len; i++) {
166
- if (keys[i] === "constructor") continue;
167
- const desc = Object.getOwnPropertyDescriptor(proto, keys[i]);
169
+ const keys2 = Object.getOwnPropertyNames(proto);
170
+ for (let i = 0, len = keys2.length; i < len; i++) {
171
+ if (keys2[i] === "constructor") continue;
172
+ const desc = Object.getOwnPropertyDescriptor(proto, keys2[i]);
168
173
  if (desc && ("function" === typeof desc.value || desc.get || desc.set)) continue;
169
- proto[keys[i]] = null;
174
+ proto[keys2[i]] = null;
170
175
  }
171
176
  proto = Object.getPrototypeOf(proto);
172
177
  }
173
178
  }
174
179
 
175
- // src/core/event.ts
180
+ // src/ts/core/event.ts
176
181
  var ReactorEvent = class _ReactorEvent {
177
182
  /** No active propagation phase. */
178
183
  static NONE = 0;
@@ -295,7 +300,7 @@ var ReactorEvent = class _ReactorEvent {
295
300
  }
296
301
  };
297
302
 
298
- // src/core/reactor.ts
303
+ // src/ts/core/reactor.ts
299
304
  var Reactor = class {
300
305
  log = NOOP;
301
306
  core;
@@ -323,27 +328,27 @@ var Reactor = class {
323
328
  listeners;
324
329
  /**
325
330
  * Creates a new Reactor instance.
326
- * @param object Initial state object.
331
+ * @param target Initial state target.
327
332
  * @param build Reactor bootstrap/build configuration.
328
333
  * @example
329
334
  * const rtr = new Reactor({ count: 0 });
330
335
  */
331
- constructor(object = {}, build) {
336
+ constructor(target = {}, build) {
332
337
  this[INERTIA] = true;
333
338
  this.config = { crossRealms: false, smartCloning: false, eventBubbling: true, lineageTracing: false, preserveContext: false, equalityFunction: Object.is, batchingFunction: RTR_BATCH, ...build };
334
- this.core = this.proxied(object);
339
+ this.core = this.proxied(target);
335
340
  if (build) this.canLog = !!build.debug;
336
341
  }
337
- proxied(obj, rejectable = false, indiffable = false, parent, key, path) {
338
- if (!obj || "object" !== typeof obj) return obj;
339
- obj = obj[RAW] || obj;
340
- if (this.config.referenceTracking && parent && key && !this.link(obj, parent, key, false)) return obj;
341
- const cached = this.proxyCache.get(obj);
342
+ proxied(target, rejectable = false, indiffable = false, parent, key, path) {
343
+ if (!target || "object" !== typeof target) return target;
344
+ target = target[RAW] || target;
345
+ if (this.config.referenceTracking && parent && key && !this.link(target, parent, key, false)) return target;
346
+ const cached = this.proxyCache.get(target);
342
347
  if (cached) return cached;
343
- if (obj[INERTIA] || !canHandle(obj, this.config, false)) return obj;
344
- rejectable ||= obj[REJECTABLE];
345
- indiffable ||= obj[INDIFFABLE];
346
- const proxy = new Proxy(obj, {
348
+ if (target[INERTIA] || !canHandle(target, this.config, false)) return target;
349
+ rejectable ||= target[REJECTABLE];
350
+ indiffable ||= target[INDIFFABLE];
351
+ const proxy = new Proxy(target, {
347
352
  // Robust Proxy handler
348
353
  get: (object, key2, receiver) => {
349
354
  if (key2 === RAW) return this.log(`\u{1F440} [Reactor \`get\` Trap] Peeked at ${object}`), object;
@@ -356,10 +361,10 @@ var Reactor = class {
356
361
  for (let i = 0, len = this.config.lineageTracing ? paths.length : 1; i < len; i++) {
357
362
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.getters.get(currPath);
358
363
  if (!cords && !wildcords) continue;
359
- const target = { path: currPath, value, key: keyStr, keyExisted: true, object: receiver }, payload = { type: "get", target, currentTarget: target, root: this.core, rejectable };
364
+ const target2 = { path: currPath, value, key: keyStr, hadKey: true, object: receiver }, payload = { type: "get", target: target2, currentTarget: target2, root: this.core, rejectable };
360
365
  if (cords) value = this.mediate(currPath, payload, "get", cords);
361
366
  if (!wildcords) continue;
362
- target.value = value;
367
+ target2.value = value;
363
368
  value = this.mediate("*", payload, "get", wildcords);
364
369
  }
365
370
  }
@@ -367,7 +372,7 @@ var Reactor = class {
367
372
  },
368
373
  set: (object, key2, value, receiver) => {
369
374
  let unchanged, safeValue, safeOldValue, terminated = false;
370
- const keyStr = String(key2), fullPath = path ? path + "." + keyStr : keyStr, paths = this.config.lineageTracing ? this.trace(object, keyStr) : fullPath, loopLen = this.config.lineageTracing ? paths.length : 1, oldValue = !this.config.preserveContext ? object[key2] : Reflect.get(object, key2, receiver), keyExisted = !this.config.preserveContext ? key2 in object : Reflect.has(object, key2);
375
+ const keyStr = String(key2), fullPath = path ? path + "." + keyStr : keyStr, paths = this.config.lineageTracing ? this.trace(object, keyStr) : fullPath, loopLen = this.config.lineageTracing ? paths.length : 1, oldValue = !this.config.preserveContext ? object[key2] : Reflect.get(object, key2, receiver), hadKey = !this.config.preserveContext ? key2 in object : Reflect.has(object, key2);
371
376
  this.log(`\u270F\uFE0F [Reactor \`set\` Trap] Initiated for "${keyStr}" on "${paths}"`), CTX.autotracker?.track(fullPath, this, true);
372
377
  if (this.config.referenceTracking || !indiffable) {
373
378
  safeOldValue = oldValue?.[RAW] || oldValue;
@@ -381,13 +386,13 @@ var Reactor = class {
381
386
  for (let i = 0; i < loopLen; i++) {
382
387
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.setters.get(currPath);
383
388
  if (!cords && !wildcords) continue;
384
- const target = { path: currPath, value, oldValue, key: keyStr, keyExisted, object: receiver }, payload = { type: "set", target, currentTarget: target, root: this.core, terminated, rejectable };
389
+ const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable };
385
390
  if (cords) {
386
391
  const result2 = this.mediate(currPath, payload, "set", cords);
387
392
  if (!(terminated ||= payload.terminated)) value = result2;
388
393
  }
389
394
  if (!wildcords) continue;
390
- target.value = value;
395
+ target2.value = value;
391
396
  const result = this.mediate("*", payload, "set", wildcords);
392
397
  if (!(terminated ||= payload.terminated)) value = result;
393
398
  }
@@ -398,14 +403,14 @@ var Reactor = class {
398
403
  if (this.config.referenceTracking && !unchanged) this.config.smartCloning && this.stamp(object), this.unlink(safeOldValue, object, keyStr), this.link(safeValue, object, keyStr);
399
404
  if (this.watchers || this.listeners)
400
405
  for (let i = 0; i < loopLen; i++) {
401
- const currPath = this.config.lineageTracing ? paths[i] : fullPath, target = { path: currPath, value, oldValue, key: keyStr, keyExisted, object: receiver };
402
- this.notify(currPath, { type: "set", target, currentTarget: target, root: this.core, terminated, rejectable });
406
+ const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
407
+ this.notify(currPath, { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable });
403
408
  }
404
409
  return true;
405
410
  },
406
411
  deleteProperty: (object, key2) => {
407
412
  let value, receiver = this.proxyCache.get(object), terminated = false;
408
- const keyStr = String(key2), fullPath = path ? path + "." + keyStr : keyStr, paths = this.config.lineageTracing ? this.trace(object, keyStr) : fullPath, loopLen = this.config.lineageTracing ? paths.length : 1, oldValue = !this.config.preserveContext ? object[key2] : Reflect.get(object, key2, receiver), keyExisted = !this.config.preserveContext ? key2 in object : Reflect.has(object, key2);
413
+ const keyStr = String(key2), fullPath = path ? path + "." + keyStr : keyStr, paths = this.config.lineageTracing ? this.trace(object, keyStr) : fullPath, loopLen = this.config.lineageTracing ? paths.length : 1, oldValue = !this.config.preserveContext ? object[key2] : Reflect.get(object, key2, receiver), hadKey = !this.config.preserveContext ? key2 in object : Reflect.has(object, key2);
409
414
  this.log(`\u{1F5D1}\uFE0F [Reactor \`deleteProperty\` Trap] Initiated for "${keyStr}" on "${paths}"`), CTX.autotracker?.track(fullPath, this, true);
410
415
  if (this.config.deleteProperty) terminated = (value = this.config.deleteProperty(object, key2, oldValue, receiver, paths)) === TERMINATOR;
411
416
  if (this.deleters) {
@@ -413,7 +418,7 @@ var Reactor = class {
413
418
  for (let i = 0; i < loopLen; i++) {
414
419
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.deleters.get(currPath);
415
420
  if (!cords && !wildcords) continue;
416
- const target = { path: currPath, value, oldValue, key: keyStr, keyExisted, object: receiver }, payload = { type: "delete", target, currentTarget: target, root: this.core, rejectable };
421
+ const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable };
417
422
  if (cords) {
418
423
  const result2 = this.mediate(currPath, payload, "delete", cords);
419
424
  if (!(terminated ||= payload.terminated)) value = result2;
@@ -429,8 +434,8 @@ var Reactor = class {
429
434
  if (this.config.referenceTracking) this.config.smartCloning && this.stamp(object), this.unlink(oldValue?.[RAW] || oldValue, object, keyStr);
430
435
  if (this.watchers || this.listeners)
431
436
  for (let i = 0; i < loopLen; i++) {
432
- const currPath = this.config.lineageTracing ? paths[i] : fullPath, target = { path: currPath, value, oldValue, key: keyStr, keyExisted, object: receiver };
433
- this.notify(currPath, { type: "delete", target, currentTarget: target, root: this.core, rejectable });
437
+ const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
438
+ this.notify(currPath, { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable });
434
439
  }
435
440
  return true;
436
441
  },
@@ -456,7 +461,7 @@ var Reactor = class {
456
461
  return ownKeys;
457
462
  }
458
463
  });
459
- return this.proxyCache.set(obj, proxy), proxy;
464
+ return this.proxyCache.set(target, proxy), proxy;
460
465
  }
461
466
  trace(target, path, paths = [], seen = /* @__PURE__ */ new WeakSet()) {
462
467
  if (Object.is(target, this.core[RAW] || this.core)) return paths.push(path), paths;
@@ -548,7 +553,7 @@ var Reactor = class {
548
553
  fire([path, object, value], e, isCapture, cords = this.listeners.get(path)) {
549
554
  if (!cords) return;
550
555
  e.type = path !== e.target.path ? "update" : e.staticType;
551
- e.currentTarget = { path, value, oldValue: e.type !== "update" ? e.target.oldValue : void 0, key: e.type !== "update" ? path : path.slice(path.lastIndexOf(".") + 1) || "", keyExisted: e.type !== "update" ? e.target.keyExisted : true, object };
556
+ e.currentTarget = { path, value, oldValue: e.type !== "update" ? e.target.oldValue : void 0, key: e.type !== "update" ? path : path.slice(path.lastIndexOf(".") + 1) || "", hadKey: e.type !== "update" ? e.target.hadKey : true, object };
552
557
  for (let i = 0, len = cords.length, tDepth; i < len; i++) {
553
558
  if (e.immediatePropagationStopped) break;
554
559
  if (cords[i].capture !== isCapture) continue;
@@ -603,24 +608,23 @@ var Reactor = class {
603
608
  }
604
609
  getContext(path) {
605
610
  const lastDot = path.lastIndexOf("."), value = getAny(this.core, path), object = lastDot === -1 ? this.core : getAny(this.core, path.slice(0, lastDot));
606
- return { path, value, key: path.slice(lastDot + 1) || "", keyExisted: true, object };
611
+ return { path, value, key: path.slice(lastDot + 1) || "", hadKey: true, object };
607
612
  }
608
613
  bindSignal(cord, sig) {
609
614
  if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
610
615
  return cord.sclup = !sig || sig.aborted ? NOOP : () => sig.removeEventListener("abort", cord.clup), cord.clup;
611
616
  }
612
- cloned(obj, raw, seen = /* @__PURE__ */ new WeakMap()) {
613
- if (!obj || "object" !== typeof obj) return obj;
614
- obj = obj[RAW] || obj;
615
- const cloned = seen.get(obj);
617
+ cloned(target, raw, seen = /* @__PURE__ */ new WeakMap()) {
618
+ if (!target || "object" !== typeof target) return target;
619
+ const obj = target[RAW] || target, cloned = seen.get(obj);
616
620
  if (cloned) return cloned;
617
621
  if (!canHandle(obj, this.config, false)) return obj;
618
622
  const version = obj[VERSION] || 0, cached = !raw && this.config.smartCloning && (this.snapCache ??= /* @__PURE__ */ new WeakMap()).get(obj);
619
623
  if (cached && obj[SSVERSION] === version) return cached;
620
- const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj[RAW] || obj;
624
+ const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
621
625
  seen.set(obj, clone);
622
- const keys = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
623
- for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = this.cloned(obj[keys[i]], raw, seen);
626
+ const keys2 = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
627
+ for (let i = 0, len = keys2.length; i < len; i++) clone[keys2[i]] = this.cloned(obj[keys2[i]], raw, seen);
624
628
  if (!raw && this.config.smartCloning) this.snapCache.set(obj, clone), obj[SSVERSION] = version;
625
629
  return clone;
626
630
  }
@@ -801,19 +805,8 @@ var Reactor = class {
801
805
  for (let i = 0, len = cords.length; i < len; i++) if (Object.is(cords[i].cb, callback) && cords[i].capture === capture) return cords[i].sclup(), cords.splice((len--, i--), 1), !cords.length && this.listeners.delete(path), true;
802
806
  return false;
803
807
  }
804
- /**
805
- * Creates a snapshot; possibly clone of state (or a state branch).
806
- * You could alternatively use or serialize your proxied state "as is" except the environment demands no proxies or new references.
807
- * @param raw Use raw (deep unproxied & uncloned) version of branch.
808
- * @param branch Branch to clone.
809
- * @returns Snapshot deep or smart (structurally shared) clone.
810
- * @example
811
- * const snap = rtr.snapshot();
812
- * @example
813
- * const snap = rtr.snapshot(false, rtr.core.history.past);
814
- */
815
- snapshot(raw = !this.config.smartCloning, branch = this.core) {
816
- return this.cloned(branch, raw);
808
+ snapshot(raw = !this.config.smartCloning, branch) {
809
+ return this.cloned(arguments.length < 2 ? this.core : branch, raw);
817
810
  }
818
811
  /**
819
812
  * Cascades object updates into direct child paths.
@@ -826,9 +819,9 @@ var Reactor = class {
826
819
  */
827
820
  cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objectSafe = true) {
828
821
  if (type !== "set" && type !== "delete" || !canHandle(news, this.config) || (objectSafe ? !canHandle(olds, this.config) : false)) return;
829
- const obj = objectSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
822
+ const obj = objectSafe ? mergeObjs(olds, news) : news, keys2 = Object.keys(obj);
830
823
  this.isCascading = true;
831
- for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path === "*" ? keys[i] : path + "." + keys[i], obj[keys[i]]);
824
+ for (let i = 0, len = keys2.length; i < len; i++) setAny(this.core, path === "*" ? keys2[i] : path + "." + keys2[i], obj[keys2[i]]);
832
825
  this.isCascading = false;
833
826
  }
834
827
  /**
@@ -864,7 +857,25 @@ var Reactor = class {
864
857
  }
865
858
  };
866
859
 
867
- // src/adapters/autotracker.ts
860
+ // src/ts/core/mixins.ts
861
+ 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"];
862
+ function reactive(target, build, preferences = NIL) {
863
+ if ("__Reactor__" in target) return target;
864
+ const descriptors = {}, rtr = getReactor(target, true, build), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(preferences.prefix || preferences.suffix);
865
+ for (let i = 0, len = methods.length; i < len; i++) {
866
+ let key = methods[i];
867
+ if (hasAffix) (preferences.whitelist?.includes(key) ?? true) && (key = `${preferences.prefix || ""}${key}${preferences.suffix || ""}`);
868
+ else if (preferences.whitelist?.includes(key)) continue;
869
+ descriptors[key] = { value: rtr[key].bind(rtr), ...locks };
870
+ }
871
+ descriptors["__Reactor__"] = { value: rtr, ...locks };
872
+ return Object.defineProperties(rtr.core, descriptors), rtr.core;
873
+ }
874
+ function getReactor(target, create = false, build) {
875
+ return (target instanceof Reactor ? target : target.__Reactor__) || (create ? new Reactor(target, build) : void 0);
876
+ }
877
+
878
+ // src/ts/adapters/autotracker.ts
868
879
  var Autotracker = class {
869
880
  proxy;
870
881
  deps = /* @__PURE__ */ new Map();
@@ -1006,24 +1017,40 @@ function withTracker(tracker, run, rtr) {
1006
1017
  }
1007
1018
  }
1008
1019
 
1009
- // src/adapters/react/hooks/useReactor.ts
1010
- function useReactor(target, options = NIL) {
1011
- const [, reRender] = (0, import_react2.useReducer)((s) => s + 1, 0), tgtRef = (0, import_react2.useRef)(), rtrRef = (0, import_react2.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = target instanceof Reactor ? target : target.__Reactor__ || new Reactor(target)) : rtrRef.current, atrkrRef = (0, import_react2.useRef)(), prevTrkr = CTX.autotracker, atrkr = CTX.autotracker = atrkrRef.current ||= new Autotracker(), optsRef = (0, import_react2.useRef)(options);
1020
+ // src/ts/adapters/react/hooks/useReactor.ts
1021
+ function useReactor(target, options = NIL, build) {
1022
+ const versionRef = (0, import_react2.useRef)(0), tgtRef = (0, import_react2.useRef)(), rtrRef = (0, import_react2.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, atrkrRef = (0, import_react2.useRef)(), prevTrkr = CTX.autotracker, atrkr = CTX.autotracker = atrkrRef.current ||= new Autotracker(), optsRef = (0, import_react2.useRef)(options), notifyRef = (0, import_react2.useRef)(NOOP);
1012
1023
  optsRef.current = options;
1013
1024
  atrkr.unblock(rtr), queueMicrotask(() => CTX.autotracker === atrkr && (CTX.autotracker = prevTrkr));
1014
- return useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(reRender, optsRef.current)), [atrkr]), rtr.core;
1025
+ const subscribe = (0, import_react2.useCallback)((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
1026
+ const getSnapshot = (0, import_react2.useCallback)(() => versionRef.current, []);
1027
+ (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1028
+ return useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(notifyRef.current, optsRef.current)), [atrkr]), rtr.core;
1015
1029
  }
1016
1030
  function useAnyReactor(options = NIL) {
1017
- const [, reRender] = (0, import_react2.useReducer)((s) => s + 1, 0), atrkrRef = (0, import_react2.useRef)(), prevTrkr = CTX.autotracker, atrkr = CTX.autotracker = atrkrRef.current ||= new Autotracker(), optsRef = (0, import_react2.useRef)(options);
1031
+ const versionRef = (0, import_react2.useRef)(0), atrkrRef = (0, import_react2.useRef)(), prevTrkr = CTX.autotracker, atrkr = CTX.autotracker = atrkrRef.current ||= new Autotracker(), optsRef = (0, import_react2.useRef)(options), notifyRef = (0, import_react2.useRef)(NOOP);
1018
1032
  optsRef.current = options;
1019
1033
  atrkr.unblock(), queueMicrotask(() => CTX.autotracker === atrkr && (CTX.autotracker = prevTrkr));
1020
- useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(reRender, optsRef.current)), [atrkr]);
1034
+ const subscribe = (0, import_react2.useCallback)((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
1035
+ const getSnapshot = (0, import_react2.useCallback)(() => versionRef.current, []);
1036
+ (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1037
+ useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(notifyRef.current, optsRef.current)), [atrkr]);
1038
+ }
1039
+ function useReactorSnapshot(target, options, build = { referenceTracking: true, smartCloning: true }) {
1040
+ const tgtRef = (0, import_react2.useRef)(), rtrRef = (0, import_react2.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, atrkrRef = (0, import_react2.useRef)(), atrkrRtrRef = (0, import_react2.useRef)(), atrkr = atrkrRtrRef.current !== rtr || !atrkrRef.current ? (atrkrRtrRef.current = rtr, atrkrRef.current = new Autotracker(rtr)) : atrkrRef.current, notifyRef = (0, import_react2.useRef)(NOOP), optsRef = (0, import_react2.useRef)(options);
1041
+ rtr.config.referenceTracking = rtr.config.smartCloning = true;
1042
+ optsRef.current = options;
1043
+ const subscribe = (0, import_react2.useCallback)((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]);
1044
+ const getSnapshot = () => rtr.snapshot();
1045
+ const snapshot = (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1046
+ const proxy = (0, import_react2.useMemo)(() => atrkr.tracked(snapshot), [atrkr, snapshot]);
1047
+ return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, proxy]), proxy;
1021
1048
  }
1022
1049
 
1023
- // src/adapters/react/hooks/useSelector.ts
1050
+ // src/ts/adapters/react/hooks/useSelector.ts
1024
1051
  var import_react3 = require("react");
1025
- function useSelector(target, sel, eq = Object.is, options = NIL) {
1026
- const rtrRef = (0, import_react3.useRef)(), tgtRef = (0, import_react3.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = target instanceof Reactor ? target : target.__Reactor__ || new Reactor(target)) : rtrRef.current, atrkrRef = (0, import_react3.useRef)(), atrkr = atrkrRef.current ||= new Autotracker(), notifyRef = (0, import_react3.useRef)(NOOP), sliceRef = (0, import_react3.useRef)(), selRef = (0, import_react3.useRef)(sel), eqRef = (0, import_react3.useRef)(eq), optsRef = (0, import_react3.useRef)(options);
1052
+ function useSelector(target, sel, eq = Object.is, options = NIL, build) {
1053
+ const rtrRef = (0, import_react3.useRef)(), tgtRef = (0, import_react3.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, atrkrRef = (0, import_react3.useRef)(), atrkr = atrkrRef.current ||= new Autotracker(), notifyRef = (0, import_react3.useRef)(NOOP), sliceRef = (0, import_react3.useRef)(), selRef = (0, import_react3.useRef)(sel), eqRef = (0, import_react3.useRef)(eq), optsRef = (0, import_react3.useRef)(options);
1027
1054
  selRef.current = sel, eqRef.current = eq, optsRef.current = options;
1028
1055
  const subscribe = (0, import_react3.useCallback)((notify) => atrkr.callback(notifyRef.current = notify, optsRef.current), [atrkr]);
1029
1056
  const getSnapshot = (0, import_react3.useCallback)(() => {
@@ -1044,20 +1071,209 @@ function useAnySelector(sel, eq = Object.is, options = NIL) {
1044
1071
  const slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1045
1072
  return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, slice]), slice;
1046
1073
  }
1074
+ function useSelectorSnapshot(target, sel, eq = Object.is, options, build = { referenceTracking: true, smartCloning: true }) {
1075
+ const tgtRef = (0, import_react3.useRef)(), rtrRef = (0, import_react3.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, atrkrRef = (0, import_react3.useRef)(), atrkrRtrRef = (0, import_react3.useRef)(), atrkr = atrkrRtrRef.current !== rtr || !atrkrRef.current ? (atrkrRtrRef.current = rtr, atrkrRef.current = new Autotracker(rtr)) : atrkrRef.current, notifyRef = (0, import_react3.useRef)(NOOP), sliceRef = (0, import_react3.useRef)(), selRef = (0, import_react3.useRef)(sel), eqRef = (0, import_react3.useRef)(eq), optsRef = (0, import_react3.useRef)(options);
1076
+ rtr.config.referenceTracking = rtr.config.smartCloning = true;
1077
+ optsRef.current = options, selRef.current = sel, eqRef.current = eq;
1078
+ const subscribe = (0, import_react3.useCallback)((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]);
1079
+ const getSnapshot = (0, import_react3.useCallback)(() => {
1080
+ const next = selRef.current(atrkr.tracked(rtr.snapshot()));
1081
+ return eqRef.current(sliceRef.current, next) ? sliceRef.current : sliceRef.current = next;
1082
+ }, [atrkr]);
1083
+ const slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1084
+ return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, slice]), slice;
1085
+ }
1047
1086
 
1048
- // src/adapters/react/hooks/usePath.ts
1087
+ // src/ts/adapters/react/hooks/usePath.ts
1049
1088
  var import_react4 = require("react");
1050
- function usePath(target, path, options = NIL) {
1051
- const [, reRender] = (0, import_react4.useReducer)((s) => s + 1, 0), tgtRef = (0, import_react4.useRef)(), rtrRef = (0, import_react4.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = target instanceof Reactor ? target : target.__Reactor__ || new Reactor(target)) : rtrRef.current, optsRef = (0, import_react4.useRef)(options);
1089
+ function usePath(target, path, options = NIL, build) {
1090
+ const versionRef = (0, import_react4.useRef)(0), tgtRef = (0, import_react4.useRef)(), rtrRef = (0, import_react4.useRef)(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, optsRef = (0, import_react4.useRef)(options);
1052
1091
  optsRef.current = options;
1053
- return useISOLayoutEffect(() => void rtr[optsRef.current.sync ? "watch" : "on"](path, reRender, optsRef.current), [rtr, path]), getAny(rtr.core, path);
1092
+ const subscribe = (0, import_react4.useCallback)((notify) => rtr[optsRef.current.sync ? "watch" : "on"](path, () => (versionRef.current++, notify()), optsRef.current), [rtr, path]);
1093
+ const getSnapshot = (0, import_react4.useCallback)(() => versionRef.current, []);
1094
+ (0, import_react4.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1095
+ return getAny(rtr.core, path);
1096
+ }
1097
+
1098
+ // src/ts/adapters/react/TimeTravelOverlay.tsx
1099
+ var import_react5 = require("react");
1100
+
1101
+ // src/ts/utils/keys.ts
1102
+ function stringifyKeyEvent(e) {
1103
+ const parts = [];
1104
+ if (e.ctrlKey) parts.push("ctrl");
1105
+ if (e.altKey) parts.push("alt");
1106
+ if (e.shiftKey) parts.push("shift");
1107
+ if (e.metaKey) parts.push("meta");
1108
+ parts.push(e.key?.toLowerCase() ?? "");
1109
+ return parts.join("+");
1110
+ }
1111
+ function cleanKeyCombo(combo) {
1112
+ const clean = (combo2) => {
1113
+ const m = ["ctrl", "alt", "shift", "meta"], alias = { cmd: "meta", space: " " };
1114
+ if (combo2 === " " || combo2 === "+") return combo2;
1115
+ combo2 = combo2.replace(/\+\s*\+$/, "+plus");
1116
+ const p = combo2.toLowerCase().split("+").filter((k) => k !== "").map((k) => alias[k] || (k === "plus" ? "+" : k.trim() || " "));
1117
+ return [...p.filter((k) => m.includes(k)).sort((a, b) => m.indexOf(a) - m.indexOf(b)), ...p.filter((k) => !m.includes(k)) || ""].join("+");
1118
+ };
1119
+ return Array.isArray(combo) ? combo.map(clean) : clean(combo);
1120
+ }
1121
+ function matchKeys(required, actual, strict = false) {
1122
+ actual = cleanKeyCombo(actual);
1123
+ const match = (required2, actual2) => {
1124
+ required2 = cleanKeyCombo(required2);
1125
+ if (strict) return required2 === actual2;
1126
+ const reqKeys = required2.split("+"), actKeys = actual2.split("+");
1127
+ return reqKeys.every((k) => actKeys.includes(k));
1128
+ };
1129
+ return Array.isArray(required) ? required.some((req) => match(req, actual)) : match(required, actual);
1130
+ }
1131
+ function getTermsForKey(combo, settings) {
1132
+ const terms = { override: false, block: false, whitelisted: false, action: null }, { overrides = [], shortcuts = {}, blocks = [], strictMatches: s = false, whitelist = [] } = settings || {};
1133
+ combo = cleanKeyCombo(combo);
1134
+ if (matchKeys(overrides, combo, s)) terms.override = true;
1135
+ if (matchKeys(blocks, combo, s)) terms.block = true;
1136
+ if (matchKeys(whitelist, combo)) terms.whitelisted = true;
1137
+ terms.action = Object.keys(shortcuts).find((key) => matchKeys(shortcuts[key], combo, s)) || null;
1138
+ return terms;
1139
+ }
1140
+ function keyEventAllowed(e, settings) {
1141
+ if (settings.disabled || (e.key === " " || e.key === "Enter") && (e.target?.ownerDocument || document).activeElement?.tagName === "BUTTON" || (e.target?.ownerDocument || document).activeElement?.matches("input,textarea,[contenteditable='true']")) return false;
1142
+ const combo = stringifyKeyEvent(e), { override, block, action, whitelisted } = getTermsForKey(combo, settings);
1143
+ if (block) return false;
1144
+ if (override) e.preventDefault();
1145
+ if (action) return action;
1146
+ if (whitelisted) return e.key.toLowerCase();
1147
+ return false;
1148
+ }
1149
+ var formatKeyForDisplay = (combo) => ` ${(Array.isArray(combo) ? combo : [combo]).map((c) => `(${cleanKeyCombo(c).replace(" ", "space")})`).join(" or ")}`;
1150
+ function parseForARIAKS(s, formatted = true) {
1151
+ const m = { ctrl: "Control", cmd: "Meta", space: "Space", plus: "+" };
1152
+ return (formatted && !Array.isArray(s) ? s : formatKeyForDisplay(s)).toLowerCase().replace(/[()]/g, "").replace(/\bor\b/g, " ").replace(/\w+/g, (k) => m[k] || k).replace(/\s+/g, " ").trim();
1153
+ }
1154
+
1155
+ // src/ts/utils/dom.ts
1156
+ function createEl(tag, props, dataset, styles, el = tag ? document?.createElement(tag) : null) {
1157
+ return assignEl(el, props, dataset, styles), el;
1158
+ }
1159
+ function assignEl(el, props, dataset, styles) {
1160
+ if (!el) return;
1161
+ if (props) {
1162
+ for (const k of Object.keys(props)) if (props[k] !== void 0) el[k] = props[k];
1163
+ }
1164
+ if (dataset) {
1165
+ for (const k of Object.keys(dataset)) if (dataset[k] !== void 0) el.dataset[k] = String(dataset[k]);
1166
+ }
1167
+ if (styles) {
1168
+ for (const k of Object.keys(styles)) if (styles[k] !== void 0) el.style[k] = styles[k];
1169
+ }
1170
+ }
1171
+
1172
+ // src/ts/adapters/vanilla/effect.ts
1173
+ function effect(callback, options) {
1174
+ const atrkr = new Autotracker();
1175
+ let destroyed = false;
1176
+ (function execute() {
1177
+ if (!destroyed) withTracker(atrkr, () => callback()), atrkr.callback(execute, options);
1178
+ })();
1179
+ return () => (destroyed = true, atrkr.destroy());
1180
+ }
1181
+
1182
+ // src/ts/adapters/vanilla/TimeTravelOverlay.ts
1183
+ var keys = {
1184
+ 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"],
1185
+ 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" }
1186
+ };
1187
+ var TimeTravelOverlay = class _TimeTravelOverlay {
1188
+ static count = 0;
1189
+ index = _TimeTravelOverlay.count;
1190
+ config;
1191
+ state = reactive({ open: false, import: "" });
1192
+ time;
1193
+ els;
1194
+ clups = [];
1195
+ keyup;
1196
+ /** Creates a docked TimeTravel overlay bound to a plugin instance.
1197
+ * @param time TimeTravel plugin instance that owns timeline operations.
1198
+ * @param build Optional initial overlay config overrides.
1199
+ */
1200
+ constructor(time, build = {}) {
1201
+ this.time = time;
1202
+ this.config = reactive({ title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
1203
+ this.state.open = !!this.config.startOpen;
1204
+ 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" });
1205
+ status.append((box.append(frame), box), clrHistory);
1206
+ 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));
1207
+ host.append(toggle, panel);
1208
+ this.els = { host, toggle, panel, title, frame, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, payload, io };
1209
+ this.keyup = (e) => {
1210
+ const a = this.state.open && keyEventAllowed(e, keys);
1211
+ 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 = "");
1212
+ };
1213
+ window.addEventListener("keydown", this.keyup);
1214
+ const sync = [
1215
+ effect(() => this.config.color ? host.style.setProperty("--sia-tt-color", this.config.color) : host.style.removeProperty("--sia-tt-color")),
1216
+ effect(() => {
1217
+ if (this.config.devOnly && !CTX.isDevEnv) return void host.remove();
1218
+ const dock = getDock(this.config.container);
1219
+ if (host.parentNode !== dock) dock.appendChild(host);
1220
+ }),
1221
+ effect(() => toggle.textContent = `${(panel.hidden = !this.state.open) ? "Show" : "Hide"} ${title.textContent = this.config.title ?? ""}`),
1222
+ effect(() => playPause.textContent = `${s.paused ? "Play" : "Pause"}${formatKeyForDisplay(keys.shortcuts.playPause)}`),
1223
+ effect(() => {
1224
+ frame.textContent = `Frame: ${s.currentFrame} / ${s.history.length}`;
1225
+ range.disabled = clrHistory.disabled = !s.history.length;
1226
+ genesis.disabled = rewind.disabled = undo.disabled = !s.currentFrame;
1227
+ playPause.disabled = redo.disabled = s.currentFrame >= s.history.length;
1228
+ range.max = String(s.history.length);
1229
+ range.value = String(Math.min(s.currentFrame, s.history.length));
1230
+ payload.value = JSON.stringify(s.currentFrame ? s.history[s.currentFrame - 1] : { type: "genesis", value: s.initialState }, null, 2);
1231
+ }),
1232
+ effect(() => {
1233
+ clr.disabled = imp.disabled = !this.state.import.trim().length;
1234
+ io.value !== this.state.import && (io.value = this.state.import);
1235
+ })
1236
+ ];
1237
+ this.clups.push(...sync);
1238
+ }
1239
+ destroy() {
1240
+ this.clups.forEach((fn) => fn());
1241
+ this.keyup && window.removeEventListener("keydown", this.keyup);
1242
+ this.els.host.remove();
1243
+ nuke(this), --_TimeTravelOverlay.count;
1244
+ }
1245
+ };
1246
+ function getDirChild(parent, className) {
1247
+ for (const child of parent.children) if (child instanceof HTMLElement && child.classList.contains(className)) return child;
1248
+ }
1249
+ function getDock(container) {
1250
+ const host = container && container !== document.documentElement ? container : document.body;
1251
+ if (host !== document.body && getComputedStyle(host).position === "static") host.style.position = "relative";
1252
+ const layer = getDirChild(host, "tt-overlay-layer") || createEl("div", { className: "tt-overlay-layer" }, void 0, { position: host === document.body ? "fixed" : "absolute" });
1253
+ if (layer.parentElement !== host) host.appendChild(layer);
1254
+ const dock = getDirChild(layer, "tt-overlay-dock") || createEl("div", { className: "tt-overlay-dock" });
1255
+ return dock.parentElement !== layer && layer.appendChild(dock), dock;
1256
+ }
1257
+
1258
+ // src/ts/adapters/react/TimeTravelOverlay.tsx
1259
+ function TimeTravelOverlay2(props) {
1260
+ const vRef = (0, import_react5.useRef)(null), { time, title, color, devOnly, startOpen, container } = props;
1261
+ useISOLayoutEffect(() => {
1262
+ vRef.current = new TimeTravelOverlay(time, props);
1263
+ return () => void (vRef.current?.destroy(), vRef.current = null);
1264
+ }, [time]);
1265
+ useISOLayoutEffect(() => void (vRef.current && (title !== void 0 && (vRef.current.config.title = title), vRef.current.config.color = color, vRef.current.config.devOnly = devOnly, vRef.current.config.container = container, vRef.current.state.open = startOpen)), [title, color, devOnly, container, startOpen]);
1266
+ return null;
1054
1267
  }
1055
1268
  // Annotate the CommonJS export names for ESM import in node:
1056
1269
  0 && (module.exports = {
1270
+ TimeTravelOverlay,
1057
1271
  useAnyReactor,
1058
1272
  useAnySelector,
1059
1273
  useISOLayoutEffect,
1060
1274
  usePath,
1061
1275
  useReactor,
1062
- useSelector
1276
+ useReactorSnapshot,
1277
+ useSelector,
1278
+ useSelectorSnapshot
1063
1279
  });