sia-reactor 0.0.55 → 0.0.57

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 (55) hide show
  1. package/README.md +10 -5
  2. package/dist/{TimeTravelConsole-BWRXxYp0.d.ts → TimeTravelConsole-Cof4tji7.d.ts} +11 -11
  3. package/dist/{TimeTravelConsole-Dt-W-sLq.d.cts → TimeTravelConsole-CrTYhT1B.d.cts} +11 -11
  4. package/dist/adapters/react.cjs +54 -68
  5. package/dist/adapters/react.cjs.map +1 -1
  6. package/dist/adapters/react.d.cts +3 -3
  7. package/dist/adapters/react.d.ts +3 -3
  8. package/dist/adapters/react.js +11 -23
  9. package/dist/adapters/react.js.map +1 -1
  10. package/dist/adapters/vanilla.cjs +47 -49
  11. package/dist/adapters/vanilla.cjs.map +1 -1
  12. package/dist/adapters/vanilla.d.cts +4 -4
  13. package/dist/adapters/vanilla.d.ts +4 -4
  14. package/dist/adapters/vanilla.js +4 -4
  15. package/dist/{chunk-L7NMQHXQ.js → chunk-5E7VHA7E.js} +1 -1
  16. package/dist/chunk-5E7VHA7E.js.map +1 -0
  17. package/dist/{chunk-VS7CTLQH.js → chunk-BJDQLZ2J.js} +3 -3
  18. package/dist/{chunk-GG7PDKIM.js → chunk-EGNUVKWW.js} +1 -1
  19. package/dist/{chunk-TVXV5YVU.js → chunk-H5UCZPAC.js} +20 -16
  20. package/dist/chunk-H5UCZPAC.js.map +1 -0
  21. package/dist/{chunk-YHGYHYEE.js → chunk-K4LEJA2I.js} +69 -65
  22. package/dist/chunk-K4LEJA2I.js.map +1 -0
  23. package/dist/{consts-CV83gskZ.d.ts → consts-RJWw9ugh.d.cts} +6 -29
  24. package/dist/{consts-CV83gskZ.d.cts → consts-RJWw9ugh.d.ts} +6 -29
  25. package/dist/{index-COpS7d1h.d.ts → index-Cc5CaM9q.d.cts} +42 -30
  26. package/dist/{index-CgFQ_rTz.d.cts → index-CwE3FzCU.d.ts} +42 -30
  27. package/dist/index.cjs +31 -37
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +4 -2
  32. package/dist/index.js.map +1 -1
  33. package/dist/modules.cjs +128 -121
  34. package/dist/modules.cjs.map +1 -1
  35. package/dist/modules.d.cts +13 -7
  36. package/dist/modules.d.ts +13 -7
  37. package/dist/modules.js +72 -64
  38. package/dist/modules.js.map +1 -1
  39. package/dist/styles/time-travel-console.css +4 -4
  40. package/dist/super.d.ts +100 -71
  41. package/dist/super.global.js +147 -131
  42. package/dist/super.global.js.map +1 -1
  43. package/dist/utils.cjs +65 -60
  44. package/dist/utils.cjs.map +1 -1
  45. package/dist/utils.d.cts +35 -4
  46. package/dist/utils.d.ts +35 -4
  47. package/dist/utils.js +7 -3
  48. package/package.json +1 -1
  49. package/dist/chunk-G2WKPDNU.js +0 -3
  50. package/dist/chunk-G2WKPDNU.js.map +0 -1
  51. package/dist/chunk-L7NMQHXQ.js.map +0 -1
  52. package/dist/chunk-TVXV5YVU.js.map +0 -1
  53. package/dist/chunk-YHGYHYEE.js.map +0 -1
  54. /package/dist/{chunk-VS7CTLQH.js.map → chunk-BJDQLZ2J.js.map} +0 -0
  55. /package/dist/{chunk-GG7PDKIM.js.map → chunk-EGNUVKWW.js.map} +0 -0
package/README.md CHANGED
@@ -167,7 +167,7 @@ All methods are available on `Reactor` instances or objects wrapped in `reactive
167
167
 
168
168
  #### **Listeners (Asynchronous/Batched UI Observers)**
169
169
  - **`on(path, callback, options)` <-> `off(path, callback, options)`**: Attach DOM-style event listeners that respect `depth`. Supports `{ capture: true, depth: 1, once: true, init: true }`.
170
- - **`once(path, callback, options)`**: Fires once and self-destructs, others have too: `sonce(...)`, `gonce(...)`, `donce(...)`, `wonce(...)`.
170
+ - **`once(path, callback, options)`**: Fires once and self-destructs, watch has too: `wonce(...)`.
171
171
 
172
172
  #### **Lifecycle & Utilities**
173
173
  - **`tick(path)`**: Forces a synchronous flush of the batch queue for a specific path.
@@ -419,7 +419,7 @@ The `target` and `currentTarget` objects give you absolute surgical awareness of
419
419
  }
420
420
  ```
421
421
 
422
- #### The Power of the `TERMINATOR` (Symbol.for("S.I.A_TERMINATOR"))
422
+ #### The Power of the `TERMINATOR` (Symbol.for("SIA_TERMINATOR"))
423
423
  Because `set` and `delete` mediators execute *before* the memory is written, you have the power to alter reality or stop it entirely using the `TERMINATOR` symbol.
424
424
 
425
425
  ```javascript
@@ -524,7 +524,7 @@ rtr.on("todos", (e: REvent<User, "todos", 1>) => {
524
524
  #### 1. Transactions & Grouping
525
525
  By default, every mutation is recorded as a single frame. For UI gestures like dragging a slider, you want to group hundreds of rapid mutations into a single Undo/Redo step for the `TimeTravelModule`.
526
526
 
527
- Transactions natively support **deep nesting** and preserve your semantic labels. When paired with `createTxPathMerger`, duplicate paths inside these envelopes are compressed in-flight with zero overhead.
527
+ Transactions natively support **deep nesting** and preserve your semantic labels. When paired with `createTxPathMerger`, duplicate paths inside these envelopes are compressed in-flight.
528
528
 
529
529
  ```javascript
530
530
  import { transaction, startTx, endTx } from "sia-reactor/modules";
@@ -617,10 +617,15 @@ silence(() => {
617
617
  player.state.volume = 100;
618
618
  }); // Useful for internal side-effects that shouldn't ruin the undo/redo history.
619
619
 
620
- // 2. Custom Meta Injection: Injects data seamlessly into the ReactorEvent payload for listeners to read.
620
+ // 2. The Force Wrapper: Executes mutations that bypasses equality checks. same as withMeta({ forced: true }, () => ...)
621
+ force(() => {
622
+ player.state.volume = 100;
623
+ }); // Useful for internal side-effects that must force-sync.
624
+
625
+ // 3. Custom Meta Injection: Injects data seamlessly into the ReactorEvent payload for listeners to read.
621
626
  withMeta({ customSource: "gamepad" }, () => {
622
627
  player.intent.playing = true;
623
- });
628
+ }); // startMeta & endMeta under the hood
624
629
 
625
630
  player.on("intent.playing", (e) => {
626
631
  console.log(e.customSource); // "gamepad"
@@ -1,23 +1,23 @@
1
- import { W as Reactive } from './consts-CV83gskZ.js';
2
- import { g as TimeTravelModule } from './index-COpS7d1h.js';
1
+ import { W as Reactive } from './consts-RJWw9ugh.js';
2
+ import { h as TimeTravelModule } from './index-CwE3FzCU.js';
3
3
 
4
- /** Reactive options for the TimeTravel overlay instance. */
4
+ /** Reactive options for the TimeTravel console instance. */
5
5
  interface TimeTravelConsoleConfig {
6
- /** Header text shown at the top of the overlay panel. */
6
+ /** Header text shown at the top of the console panel. */
7
7
  title: string;
8
8
  /** Accent color used to derive panel theme variables. */
9
9
  color: string;
10
- /** Shows the overlay only in development when true. */
10
+ /** Shows the console only in development when true. */
11
11
  devOnly: boolean;
12
- /** Initial open state applied when the overlay is created. */
12
+ /** Initial open state applied when the console is created. */
13
13
  startOpen: boolean;
14
- /** Container element that owns the overlay layer and dock. */
14
+ /** Container element that owns the console layer and dock. */
15
15
  container?: HTMLElement;
16
16
  }
17
17
  /**
18
- * - Vanilla overlay controller for visual time-travel controls and timeline I/O.
18
+ * - Vanilla console controller for visual time-travel controls and timeline I/O.
19
19
  * - Mounts a docked HUD into the configured container, syncs its UI with module state, and forwards keyboard/button actions to the TimeTravelModule.
20
- * Supports reactive `config` updates (title/color/container/devOnly) and maintains local overlay UI state (`open` and `import` payload text).
20
+ * Supports reactive `config` updates (title/color/container/devOnly) and maintains local console UI state (`open` and `import` payload text).
21
21
  */
22
22
  declare class TimeTravelConsole {
23
23
  static count: number;
@@ -32,9 +32,9 @@ declare class TimeTravelConsole {
32
32
  readonly host: HTMLElement;
33
33
  private clups;
34
34
  private keydown;
35
- /** Creates a docked TimeTravel overlay bound to a module instance.
35
+ /** Creates a docked TimeTravel console overlay bound to a module instance.
36
36
  * @param time TimeTravel module instance that owns timeline operations.
37
- * @param build Optional initial overlay config overrides.
37
+ * @param build Optional initial console config overrides.
38
38
  */
39
39
  constructor(time: TimeTravelModule, build?: Partial<TimeTravelConsoleConfig>);
40
40
  destroy(): void;
@@ -1,23 +1,23 @@
1
- import { W as Reactive } from './consts-CV83gskZ.cjs';
2
- import { g as TimeTravelModule } from './index-CgFQ_rTz.cjs';
1
+ import { W as Reactive } from './consts-RJWw9ugh.cjs';
2
+ import { h as TimeTravelModule } from './index-Cc5CaM9q.cjs';
3
3
 
4
- /** Reactive options for the TimeTravel overlay instance. */
4
+ /** Reactive options for the TimeTravel console instance. */
5
5
  interface TimeTravelConsoleConfig {
6
- /** Header text shown at the top of the overlay panel. */
6
+ /** Header text shown at the top of the console panel. */
7
7
  title: string;
8
8
  /** Accent color used to derive panel theme variables. */
9
9
  color: string;
10
- /** Shows the overlay only in development when true. */
10
+ /** Shows the console only in development when true. */
11
11
  devOnly: boolean;
12
- /** Initial open state applied when the overlay is created. */
12
+ /** Initial open state applied when the console is created. */
13
13
  startOpen: boolean;
14
- /** Container element that owns the overlay layer and dock. */
14
+ /** Container element that owns the console layer and dock. */
15
15
  container?: HTMLElement;
16
16
  }
17
17
  /**
18
- * - Vanilla overlay controller for visual time-travel controls and timeline I/O.
18
+ * - Vanilla console controller for visual time-travel controls and timeline I/O.
19
19
  * - Mounts a docked HUD into the configured container, syncs its UI with module state, and forwards keyboard/button actions to the TimeTravelModule.
20
- * Supports reactive `config` updates (title/color/container/devOnly) and maintains local overlay UI state (`open` and `import` payload text).
20
+ * Supports reactive `config` updates (title/color/container/devOnly) and maintains local console UI state (`open` and `import` payload text).
21
21
  */
22
22
  declare class TimeTravelConsole {
23
23
  static count: number;
@@ -32,9 +32,9 @@ declare class TimeTravelConsole {
32
32
  readonly host: HTMLElement;
33
33
  private clups;
34
34
  private keydown;
35
- /** Creates a docked TimeTravel overlay bound to a module instance.
35
+ /** Creates a docked TimeTravel console overlay bound to a module instance.
36
36
  * @param time TimeTravel module instance that owns timeline operations.
37
- * @param build Optional initial overlay config overrides.
37
+ * @param build Optional initial console config overrides.
38
38
  */
39
39
  constructor(time: TimeTravelModule, build?: Partial<TimeTravelConsoleConfig>);
40
40
  destroy(): void;
@@ -40,15 +40,15 @@ var import_react = require("react");
40
40
  var useISOLayoutEffect = "undefined" !== typeof window ? import_react.useLayoutEffect : import_react.useEffect;
41
41
 
42
42
  // src/ts/core/consts.ts
43
- var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
44
- var INERTIA = /* @__PURE__ */ Symbol.for("S.I.A_INERTIA");
45
- var REJECTABLE = /* @__PURE__ */ Symbol.for("S.I.A_REJECTABLE");
46
- var INDIFFABLE = /* @__PURE__ */ Symbol.for("S.I.A_INDIFFABLE");
47
- var TERMINATOR = /* @__PURE__ */ Symbol.for("S.I.A_TERMINATOR");
48
- var VERSION = /* @__PURE__ */ Symbol.for("S.I.A_VERSION");
49
- var SSVERSION = /* @__PURE__ */ Symbol.for("S.I.A_SNAPSHOT_VERSION");
43
+ var RAW = /* @__PURE__ */ Symbol.for("SIA_RAW");
44
+ var INERTIA = /* @__PURE__ */ Symbol.for("SIA_INERTIA");
45
+ var REJECTABLE = /* @__PURE__ */ Symbol.for("SIA_REJECTABLE");
46
+ var INDIFFABLE = /* @__PURE__ */ Symbol.for("SIA_INDIFFABLE");
47
+ var TERMINATOR = /* @__PURE__ */ Symbol.for("SIA_TERMINATOR");
48
+ var VERSION = /* @__PURE__ */ Symbol.for("SIA_VERSION");
49
+ var SSVERSION = /* @__PURE__ */ Symbol.for("SIA_SNAPSHOT_VERSION");
50
50
  var RTR_BATCH = "undefined" !== typeof window ? ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window) : "undefined" !== typeof process && process.nextTick ? process.nextTick : setTimeout;
51
- var RTR_LOG = console.log.bind(console, "[S.I.A Reactor]");
51
+ var RTR_LOG = console.log.bind(console, "[SIA Reactor]");
52
52
  var EVT_OPTS = { LISTENER: ["capture", "depth", "once", "signal", "init"], MEDIATOR: ["lazy", "signal", "init"] };
53
53
  var NIL = Object.freeze({});
54
54
  var NOOP = () => {
@@ -61,8 +61,6 @@ try {
61
61
  var CTX_BUILD = {
62
62
  /** Flag indicating whether the application is running in development mode. */
63
63
  isDevEnv,
64
- /** Flag indicating whether an operation is bypassing checks so reactors can allow all writes. */
65
- usingForce: false,
66
64
  /** Active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
67
65
  autotracker: null,
68
66
  /** Extensible meta context for payloads and cross-cutting concerns. */
@@ -79,7 +77,24 @@ var CTX_BUILD = {
79
77
  batchingFunction: RTR_BATCH
80
78
  }
81
79
  };
82
- var CTX = globalThis[/* @__PURE__ */ Symbol.for("S.I.A_CTX")] ??= CTX_BUILD;
80
+ var CTX = globalThis[/* @__PURE__ */ Symbol.for("SIA_CTX")] ??= CTX_BUILD;
81
+
82
+ // src/ts/utils/meta.ts
83
+ function resetMeta(key) {
84
+ if (!CTX.meta) return;
85
+ delete CTX.meta[key];
86
+ for (const _ in CTX.meta) return;
87
+ CTX.meta = null;
88
+ }
89
+ function force(task, bool = true) {
90
+ const prev = CTX.meta?.forced;
91
+ (CTX.meta ??= {}).forced = bool;
92
+ try {
93
+ return task();
94
+ } finally {
95
+ prev !== void 0 ? CTX.meta.forced = prev : resetMeta("forced");
96
+ }
97
+ }
83
98
 
84
99
  // src/ts/utils/obj.ts
85
100
  var arrRegex = /^([^\[\]]+)\[(\d+)\]$/;
@@ -106,7 +121,7 @@ function getPath(source, key, separator = ".", keyFn) {
106
121
  if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return currObj[key3];
107
122
  currObj = currObj[key3][Number(iStr)];
108
123
  } else {
109
- if (!isObj(currObj) || !(key2 in currObj)) return currObj[key2];
124
+ if (!isObj(currObj, false) || !(key2 in currObj)) return currObj[key2];
110
125
  currObj = currObj[key2];
111
126
  }
112
127
  }
@@ -143,7 +158,7 @@ function deletePath(target, key, separator = ".", keyFn) {
143
158
  if (i === len - 1) delete currObj[key3][Number(iStr)];
144
159
  else currObj = currObj[key3][Number(iStr)];
145
160
  } else {
146
- if (!isObj(currObj) || !(key2 in currObj)) return;
161
+ if (!isObj(currObj, false) || !(key2 in currObj)) return;
147
162
  if (i === len - 1) delete currObj[key2];
148
163
  else currObj = currObj[key2];
149
164
  }
@@ -160,7 +175,7 @@ function hasPath(source, key, separator = ".", keyFn) {
160
175
  if (i === len - 1) return true;
161
176
  currObj = currObj[key3][Number(iStr)];
162
177
  } else {
163
- if (!isObj(currObj) || !(key2 in currObj)) return false;
178
+ if (!isObj(currObj, false) || !(key2 in currObj)) return false;
164
179
  if (i === len - 1) return true;
165
180
  currObj = currObj[key2];
166
181
  }
@@ -174,15 +189,6 @@ function getDepth(path, depth = !path ? 0 : 1) {
174
189
  function parseEvtOpts(options, opts, boolOpt = opts[0], result = {}) {
175
190
  return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
176
191
  }
177
- function force(task, bool = true) {
178
- const prev = CTX.usingForce;
179
- CTX.usingForce = bool;
180
- try {
181
- return task();
182
- } finally {
183
- CTX.usingForce = prev;
184
- }
185
- }
186
192
  function getTrailRecords(obj, path, reverse = false) {
187
193
  const parts = path.split("."), chain = [["*", obj, obj]];
188
194
  for (let acc = "", currObj = obj, i = 0, len = parts.length; i < len; i++) {
@@ -388,7 +394,7 @@ var Reactor = class {
388
394
  safeValue = value?.[RAW] || value;
389
395
  unchanged = this.config.equalityFunction(safeValue, safeOldValue);
390
396
  }
391
- if (!indiffable && unchanged && !CTX.usingForce && keyStr !== "length") return this.log(`\u{1F504} [\`set\` Trap] Unchanged for "${keyStr}" on [${paths}]`), true;
397
+ if (!indiffable && unchanged && !CTX.meta?.forced && keyStr !== "length") return this.log(`\u{1F504} [\`set\` Trap] Unchanged for "${keyStr}" on [${paths}]`), true;
392
398
  if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
393
399
  if (this.setters) {
394
400
  const wildcords = this.setters.get("*");
@@ -712,10 +718,6 @@ var Reactor = class {
712
718
  get(path, callback, options) {
713
719
  return this.onSync("get", path, callback, options, () => getPath(this.core, path));
714
720
  }
715
- /** Registers a get mediator for a path that only triggers once. */
716
- gonce(path, callback, options) {
717
- return this.get(path, callback, { ...parseEvtOpts(options, EVT_OPTS.MEDIATOR), once: true });
718
- }
719
721
  /**
720
722
  * Removes a get mediator for a path.
721
723
  * @param path Path or wildcard path.
@@ -737,10 +739,6 @@ var Reactor = class {
737
739
  set(path, callback, options) {
738
740
  return this.onSync("set", path, callback, options, () => (force(() => setPath(this.core, path, getPath(this.core, path))), this.tick(path)));
739
741
  }
740
- /** Registers a set mediator for a path that only triggers once. */
741
- sonce(path, callback, options) {
742
- return this.set(path, callback, Object.assign(parseEvtOpts(options, EVT_OPTS.MEDIATOR), { once: true }));
743
- }
744
742
  /**
745
743
  * Removes a set mediator for a path.
746
744
  * @param path Path or wildcard path.
@@ -762,10 +760,6 @@ var Reactor = class {
762
760
  delete(path, callback, options) {
763
761
  return this.onSync("delete", path, callback, options, () => deletePath(this.core, path));
764
762
  }
765
- /** Registers a delete mediator for a path that only triggers once. */
766
- donce(path, callback, options) {
767
- return this.delete(path, callback, Object.assign(parseEvtOpts(options, EVT_OPTS.MEDIATOR), { once: true }));
768
- }
769
763
  /**
770
764
  * Removes a delete mediator for a path.
771
765
  * @param path Path or wildcard path.
@@ -890,7 +884,7 @@ var Reactor = class {
890
884
  };
891
885
 
892
886
  // src/ts/core/mixins.ts
893
- var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "use", "reset", "destroy"];
887
+ var methods = ["tick", "stall", "nostall", "get", "noget", "set", "noset", "delete", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "use", "reset", "destroy"];
894
888
  function reactive(target, build, preferences = NIL) {
895
889
  if ("__Reactor__" in target) return target;
896
890
  const descriptors = {}, rtr = getReactor(target, true, build), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(preferences.prefix || preferences.suffix);
@@ -1057,8 +1051,7 @@ function useReactor(target, options = NIL, build) {
1057
1051
  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);
1058
1052
  optsRef.current = options;
1059
1053
  atrkr.unblock(rtr), queueMicrotask(() => CTX.autotracker === atrkr && (CTX.autotracker = prevTrkr));
1060
- const subscribe = (0, import_react2.useCallback)((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
1061
- const getSnapshot = (0, import_react2.useCallback)(() => versionRef.current, []);
1054
+ const getSnapshot = (0, import_react2.useCallback)(() => versionRef.current, []), subscribe = (0, import_react2.useCallback)((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
1062
1055
  (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1063
1056
  return useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(notifyRef.current, optsRef.current)), [atrkr]), rtr.core;
1064
1057
  }
@@ -1066,8 +1059,7 @@ function useAnyReactor(options = NIL) {
1066
1059
  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);
1067
1060
  optsRef.current = options;
1068
1061
  atrkr.unblock(), queueMicrotask(() => CTX.autotracker === atrkr && (CTX.autotracker = prevTrkr));
1069
- const subscribe = (0, import_react2.useCallback)((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
1070
- const getSnapshot = (0, import_react2.useCallback)(() => versionRef.current, []);
1062
+ const getSnapshot = (0, import_react2.useCallback)(() => versionRef.current, []), subscribe = (0, import_react2.useCallback)((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
1071
1063
  (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1072
1064
  useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(notifyRef.current, optsRef.current)), [atrkr]);
1073
1065
  }
@@ -1075,10 +1067,7 @@ function useReactorSnapshot(target, options, build = { referenceTracking: true,
1075
1067
  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);
1076
1068
  rtr.config.referenceTracking = rtr.config.smartCloning = true;
1077
1069
  optsRef.current = options;
1078
- const subscribe = (0, import_react2.useCallback)((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]);
1079
- const getSnapshot = () => rtr.snapshot();
1080
- const snapshot = (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1081
- const proxy = (0, import_react2.useMemo)(() => atrkr.tracked(snapshot), [atrkr, snapshot]);
1070
+ const getSnapshot = () => rtr.snapshot(), subscribe = (0, import_react2.useCallback)((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]), snapshot = (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot), proxy = (0, import_react2.useMemo)(() => atrkr.tracked(snapshot), [atrkr, snapshot]);
1082
1071
  return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, proxy]), proxy;
1083
1072
  }
1084
1073
 
@@ -1087,35 +1076,29 @@ var import_react3 = require("react");
1087
1076
  function useSelector(target, sel, eq = Object.is, options = NIL, build) {
1088
1077
  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);
1089
1078
  selRef.current = sel, eqRef.current = eq, optsRef.current = options;
1090
- const subscribe = (0, import_react3.useCallback)((notify) => atrkr.callback(notifyRef.current = notify, optsRef.current), [atrkr]);
1091
1079
  const getSnapshot = (0, import_react3.useCallback)(() => {
1092
1080
  const next = withTracker(atrkr, () => selRef.current(rtr.core), rtr);
1093
1081
  return eqRef.current(sliceRef.current, next) ? sliceRef.current : sliceRef.current = next;
1094
- }, [atrkr, rtr]);
1095
- const slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1082
+ }, [atrkr, rtr]), subscribe = (0, import_react3.useCallback)((notify) => atrkr.callback(notifyRef.current = notify, optsRef.current), [atrkr]), slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1096
1083
  return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, slice]), slice;
1097
1084
  }
1098
1085
  function useAnySelector(sel, eq = Object.is, options = NIL) {
1099
1086
  const 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);
1100
1087
  selRef.current = sel, eqRef.current = eq, optsRef.current = options;
1101
- const subscribe = (0, import_react3.useCallback)((notify) => atrkr.callback(notifyRef.current = notify, optsRef.current), [atrkr]);
1102
1088
  const getSnapshot = (0, import_react3.useCallback)(() => {
1103
1089
  const next = withTracker(atrkr, () => selRef.current());
1104
1090
  return eqRef.current(sliceRef.current, next) ? sliceRef.current : sliceRef.current = next;
1105
- }, [atrkr]);
1106
- const slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1091
+ }, [atrkr]), subscribe = (0, import_react3.useCallback)((notify) => atrkr.callback(notifyRef.current = notify, optsRef.current), [atrkr]), slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1107
1092
  return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, slice]), slice;
1108
1093
  }
1109
1094
  function useSelectorSnapshot(target, sel, eq = Object.is, options, build = { referenceTracking: true, smartCloning: true }) {
1110
1095
  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);
1111
1096
  rtr.config.referenceTracking = rtr.config.smartCloning = true;
1112
1097
  optsRef.current = options, selRef.current = sel, eqRef.current = eq;
1113
- const subscribe = (0, import_react3.useCallback)((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]);
1114
1098
  const getSnapshot = (0, import_react3.useCallback)(() => {
1115
1099
  const next = selRef.current(atrkr.tracked(rtr.snapshot()));
1116
1100
  return eqRef.current(sliceRef.current, next) ? sliceRef.current : sliceRef.current = next;
1117
- }, [atrkr]);
1118
- const slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1101
+ }, [atrkr]), subscribe = (0, import_react3.useCallback)((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]), slice = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1119
1102
  return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, slice]), slice;
1120
1103
  }
1121
1104
 
@@ -1124,8 +1107,7 @@ var import_react4 = require("react");
1124
1107
  function usePath(target, path, options = NIL, build) {
1125
1108
  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);
1126
1109
  optsRef.current = options;
1127
- const subscribe = (0, import_react4.useCallback)((notify) => rtr[optsRef.current.sync ? "watch" : "on"](path, () => (versionRef.current++, notify()), optsRef.current), [rtr, path]);
1128
- const getSnapshot = (0, import_react4.useCallback)(() => versionRef.current, []);
1110
+ const getSnapshot = (0, import_react4.useCallback)(() => versionRef.current, []), subscribe = (0, import_react4.useCallback)((notify) => rtr[optsRef.current.sync ? "watch" : "on"](path, () => (versionRef.current++, notify()), optsRef.current), [rtr, path]);
1129
1111
  (0, import_react4.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1130
1112
  return getPath(rtr.core, path);
1131
1113
  }
@@ -1257,8 +1239,9 @@ function effect(callback, options = NIL) {
1257
1239
  // src/ts/adapters/vanilla/TimeTravelConsole.ts
1258
1240
  var keys = {
1259
1241
  blocks: KEYS_BLOCKS,
1260
- // overrides: ["Space", "Home", "End", "Escape", "Delete"], // Playing "God"
1261
- shortcuts: { undo: ["Ctrl+z", "Cmd+z"], redo: ["Ctrl+y", "Ctrl+Shift+z", "Cmd+Shift+z"], genesis: "Home", trackUntrack: "t", ending: "End", playPause: "Space", rewind: "Shift+Space", closeOverlay: "Escape", clrHistory: "Delete", export: "e", import: "i", clear: "c" }
1242
+ whitelist: ["Escape"],
1243
+ // overrides: ["Space", "Home", "End", "Escape", "Delete"], // Playing "God" now
1244
+ shortcuts: { undo: ["Ctrl+z", "Cmd+z"], redo: ["Ctrl+y", "Ctrl+Shift+z", "Cmd+Shift+z"], genesis: "Home", trackUntrack: "t", ending: "End", playPause: "Space", rewind: "Shift+Space", prune: "Insert", reset: "Delete", export: "e", import: "i", clear: "x", copy: "c", toggle: "Ctrl + `" }
1262
1245
  };
1263
1246
  var TimeTravelConsole = class _TimeTravelConsole {
1264
1247
  static count = 0;
@@ -1269,22 +1252,24 @@ var TimeTravelConsole = class _TimeTravelConsole {
1269
1252
  host;
1270
1253
  clups = [];
1271
1254
  keydown;
1272
- /** Creates a docked TimeTravel overlay bound to a module instance.
1255
+ /** Creates a docked TimeTravel console overlay bound to a module instance.
1273
1256
  * @param time TimeTravel module instance that owns timeline operations.
1274
- * @param build Optional initial overlay config overrides.
1257
+ * @param build Optional initial console config overrides.
1275
1258
  */
1276
1259
  constructor(time, build = { devOnly: true }) {
1277
1260
  this.time = time;
1278
1261
  this.config = reactive({ title: `Time Travel Console ${this.index = ++_TimeTravelConsole.count}`, ...build });
1279
1262
  this.state.open = !!this.config.startOpen;
1280
1263
  let wlLive = false, blLive = false;
1281
- const s = this.time.state, host = this.host = createEl("div", { className: "sia-tt-console-host" }), toggle = createEl("button", { className: "sia-tt-console-toggle", type: "button", onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "sia-tt-console", ariaLabel: "time travel overlay" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), clrHistory = createEl("button", { textContent: `Clear History${formatKeyTooltip(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory), onclick: () => (this.time.clear(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyTooltip(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo), onclick: () => this.time.undo(this.state.stride) }), redo = createEl("button", { textContent: `Redo${formatKeyTooltip(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo), onclick: () => this.time.redo(this.state.stride) }), genesis = createEl("button", { textContent: `Genesis${formatKeyTooltip(keys.shortcuts.genesis)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis), onclick: () => this.time.jumpTo(0) }), playPause = createEl("button", { onclick: () => this.time[s.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause) }), rewind = createEl("button", { textContent: `Rewind${formatKeyTooltip(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind), onclick: this.time.rewind }), trackUntrack = createEl("button", { onclick: () => this.time[s.tracking ? "untrack" : "track"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.trackUntrack) }), range = createEl("input", { type: "range", min: "0", title: "time travel frame", ariaLabel: "time travel frame", oninput: () => this.time.jumpTo(Number(range.value)) }), exp = createEl("button", { textContent: `Export${formatKeyTooltip(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export), onclick: () => this.state.import = this.time.export(null, 2) }), imp = createEl("button", { textContent: `Import${formatKeyTooltip(keys.shortcuts.import)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.import), onclick: () => this.state.import.trim().length && this.time.import(this.state.import) }), clr = createEl("button", { textContent: `Clear${formatKeyTooltip(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear), onclick: () => this.state.import = "" }), payload = createEl("textarea", { className: "sia-tt-io", readOnly: true, placeholder: "current payload json", title: "Current History Entry" }), io = createEl("textarea", { className: "sia-tt-io", placeholder: "timeline payload json", title: "Time History", oninput: () => this.state.import = io.value }), foot = createEl("p", { className: "sia-tt-footnote", innerHTML: "<span>Try this in your app? </span>" }), link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" }), box = createEl("div", { className: "sia-tt-status-box" }), status = createEl("div", { className: "sia-tt-status-row" }), filters = createEl("div", { className: "sia-tt-status-row" }), filterBox = createEl("div", { className: "sia-tt-status-box" }), whitelistLabel = createEl("span", { className: "muted", textContent: "Whitelist:" }), blacklistLabel = createEl("span", { className: "muted", textContent: "Blacklist:" }), whitelist = createEl("input", { className: "sia-tt-filter-input sia-tt-io", placeholder: 'a.b, c.d or {"0":["a.b"]}', title: "Whitelist paths", onfocus: () => wlLive = true, onblur: () => (wlLive = false, whitelist.value = formatPaths(this.time.config.whitelist, "*")), oninput: (_, parsed = parsePaths(whitelist.value)) => parsed !== null && (this.time.config.whitelist = parsed) }), blacklist = createEl("input", { className: "sia-tt-filter-input sia-tt-io", placeholder: 'a.b, c.d or {"0":["a.b"]}', title: "Blacklist paths", onfocus: () => blLive = true, onblur: () => (blLive = false, blacklist.value = formatPaths(this.time.config.blacklist, "")), oninput: (_, parsed = parsePaths(blacklist.value, true)) => parsed !== null && (this.time.config.blacklist = parsed) }), speed = createEl("select", { className: "sia-tt-speed", title: "Playback Speed", onchange: () => this.time.config.playbackRate = Number(speed.value) }), stride = createEl("input", { className: "sia-tt-stride sia-tt-button sia-tt-mini-input", type: "number", min: "1", title: "Skip Stride i.e. how many steps count as one unit", oninput: () => this.state.stride = Math.max(1, Number(stride.value) || 1) }), stats = createEl("span", { className: "sia-tt-stats" }), limit = createEl("input", { className: "sia-tt-limit sia-tt-mini-input sia-tt-button", type: "number", min: "1", title: "Maximum number of history entries kept in memory before older entries are discarded.", oninput: () => this.time.config.limit = Math.max(1, Number(limit.value) || 1) }), delay = createEl("input", { className: "sia-tt-delay sia-tt-mini-input sia-tt-button", type: "number", min: "0", step: "50", title: "Maximum playback delay between timeline frames in milliseconds.", oninput: () => this.time.config.maxPlaybackDelay = Math.max(0, Number(delay.value) || 0) }), read = createEl("label", { className: "sia-tt-check", title: "When recording intent mutations, read previous values from the matching state path instead of the intent path. Useful for accurate undo reconstruction of intent-driven flows." }), readBox = createEl("input", { type: "checkbox", checked: !!this.time.config.mirrorReads, onchange: () => this.time.config.mirrorReads = readBox.checked }), write = createEl("label", { className: "sia-tt-check", title: "During playback and teleportation, mirror state writes into matching intent paths to re-enact the recorded history. Useful for accurate reconstruction of the whole session." }), writeBox = createEl("input", { type: "checkbox", checked: !!this.time.config.mirrorWrites, onchange: () => this.time.config.mirrorWrites = writeBox.checked }), filterRow1 = createEl("div", { className: "sia-tt-filter-row" }), filterRow2 = createEl("div", { className: "sia-tt-filter-row" }), row1 = createEl("div", { className: "sia-tt-row" }), row2 = createEl("div", { className: "sia-tt-row" }), row3 = createEl("div", { className: "sia-tt-row" }), row4 = createEl("div", { className: "sia-tt-row" }), row5 = createEl("div", { className: "sia-tt-row sia-tt-config-row" });
1264
+ const s = this.time.state, host = this.host = createEl("div", { className: "sia-tt-console-host" }), toggle = createEl("button", { className: "sia-tt-console-toggle", type: "button", title: `Toggle Overlay${formatKeyTooltip(keys.shortcuts.toggle)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.toggle), onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "sia-tt-console", ariaLabel: "time travel console" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), prune = createEl("button", { textContent: `Prune${formatKeyTooltip(keys.shortcuts.prune)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.prune), onclick: () => this.time.prune() }), reset = createEl("button", { textContent: `Reset${formatKeyTooltip(keys.shortcuts.reset)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.reset), onclick: () => (this.time.reset(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyTooltip(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo), onclick: () => this.time.undo(this.state.stride) }), redo = createEl("button", { textContent: `Redo${formatKeyTooltip(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo), onclick: () => this.time.redo(this.state.stride) }), genesis = createEl("button", { textContent: `Genesis${formatKeyTooltip(keys.shortcuts.genesis)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis), onclick: () => this.time.jumpTo(0) }), playPause = createEl("button", { onclick: () => this.time[s.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause) }), rewind = createEl("button", { textContent: `Rewind${formatKeyTooltip(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind), onclick: this.time.rewind }), trackUntrack = createEl("button", { onclick: () => this.time[s.tracking ? "untrack" : "track"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.trackUntrack) }), range = createEl("input", { type: "range", min: "0", title: "time travel frame", ariaLabel: "time travel frame", oninput: () => this.time.jumpTo(Number(range.value)) }), exp = createEl("button", { textContent: `Export${formatKeyTooltip(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export), onclick: () => this.state.import = this.time.export(null, 2) }), imp = createEl("button", { textContent: `Import${formatKeyTooltip(keys.shortcuts.import)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.import), onclick: () => this.state.import.trim().length && this.time.import(this.state.import) }), clr = createEl("button", { textContent: `Clear${formatKeyTooltip(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear), onclick: () => this.state.import = "" }), copy = createEl("button", { textContent: `Copy${formatKeyTooltip(keys.shortcuts.copy)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.copy), onclick: () => this.state.import.trim().length && navigator.clipboard?.writeText(this.state.import).catch?.(() => {
1265
+ }) }), payload = createEl("textarea", { className: "sia-tt-io", readOnly: true, placeholder: "current payload json", title: "Current History Entry" }), io = createEl("textarea", { className: "sia-tt-io", placeholder: "timeline payload json", title: "Time History", oninput: () => this.state.import = io.value }), foot = createEl("p", { className: "sia-tt-footnote", innerHTML: "<span>Try this in your app? </span>" }), link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" }), box = createEl("div", { className: "sia-tt-status-box" }), status = createEl("div", { className: "sia-tt-status-row" }), filters = createEl("div", { className: "sia-tt-status-row" }), filterBox = createEl("div", { className: "sia-tt-status-box" }), whitelistLabel = createEl("span", { className: "muted", textContent: "Whitelist:" }), blacklistLabel = createEl("span", { className: "muted", textContent: "Blacklist:" }), whitelist = createEl("input", { className: "sia-tt-filter-input sia-tt-io", placeholder: 'a.b, c.d or {"0":["a.b"]}', title: "Whitelist paths", onfocus: () => wlLive = true, onblur: () => (wlLive = false, whitelist.value = formatPaths(this.time.config.whitelist, "*")), oninput: (_, parsed = parsePaths(whitelist.value)) => parsed !== null && (this.time.config.whitelist = parsed) }), blacklist = createEl("input", { className: "sia-tt-filter-input sia-tt-io", placeholder: 'a.b, c.d or {"0":["a.b"]}', title: "Blacklist paths", onfocus: () => blLive = true, onblur: () => (blLive = false, blacklist.value = formatPaths(this.time.config.blacklist, "")), oninput: (_, parsed = parsePaths(blacklist.value, true)) => parsed !== null && (this.time.config.blacklist = parsed) }), speed = createEl("select", { className: "sia-tt-speed", title: "Playback Speed", onchange: () => this.time.state.playbackRate = Number(speed.value) }), stride = createEl("input", { className: "sia-tt-stride sia-tt-button sia-tt-mini-input", type: "number", min: "1", title: "Skip Stride i.e. how many steps count as one unit", oninput: () => this.state.stride = Math.max(1, Number(stride.value) || 1) }), stats = createEl("span", { className: "sia-tt-stats" }), limit = createEl("input", { className: "sia-tt-limit sia-tt-mini-input sia-tt-button", type: "number", min: "1", title: "Maximum number of history entries kept in memory before older entries are discarded.", oninput: () => this.time.config.limit = Math.max(1, Number(limit.value) || 1) }), delay = createEl("input", { className: "sia-tt-delay sia-tt-mini-input sia-tt-button", type: "number", min: "0", step: "50", title: "Maximum playback delay between timeline frames in milliseconds.", oninput: () => this.time.config.maxPlaybackDelay = Math.max(0, Number(delay.value) || 0) }), read = createEl("label", { className: "sia-tt-check", title: "When recording intent mutations, read previous values from the matching state path instead of the intent path. Useful for accurate undo reconstruction of intent-driven flows." }), readBox = createEl("input", { type: "checkbox", checked: !!this.time.config.mirrorReads, onchange: () => this.time.config.mirrorReads = readBox.checked }), write = createEl("label", { className: "sia-tt-check", title: "During playback and teleportation, mirror state writes into matching intent paths to re-enact the recorded history. Useful for accurate reconstruction of the whole session." }), writeBox = createEl("input", { type: "checkbox", checked: !!this.time.config.mirrorWrites, onchange: () => this.time.config.mirrorWrites = writeBox.checked }), filterRow1 = createEl("div", { className: "sia-tt-filter-row" }), filterRow2 = createEl("div", { className: "sia-tt-filter-row" }), row1 = createEl("div", { className: "sia-tt-row" }), row2 = createEl("div", { className: "sia-tt-row" }), row3 = createEl("div", { className: "sia-tt-row" }), row4 = createEl("div", { className: "sia-tt-row" }), row5 = createEl("div", { className: "sia-tt-row sia-tt-config-row" });
1282
1266
  speed.append(...[0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4, 8, 16, 32].map((v) => createEl("option", { value: String(v), textContent: `${v}x` })));
1283
- status.append((box.append(frame), box), clrHistory);
1267
+ status.append((box.append(frame), box), prune, reset);
1284
1268
  filters.append((filterBox.append((filterRow1.append(whitelistLabel, whitelist), filterRow1), (filterRow2.append(blacklistLabel, blacklist), filterRow2)), filterBox));
1285
- panel.append(title, status, (row1.append(playPause, rewind, genesis), row1), (row2.append(undo, redo, trackUntrack, stride), row2), payload, (row3.append(speed, range), row3), filters, (row4.append(exp, imp, clr), row4), io, (row5.append(delay, limit, (read.append(readBox, " Read Mirror"), read), (write.append(writeBox, " Write Mirror"), write)), row5), (foot.prepend(stats), foot.append(link), foot));
1269
+ panel.append(title, status, (row1.append(playPause, rewind, genesis), row1), (row2.append(undo, redo, trackUntrack, stride), row2), payload, (row3.append(speed, range), row3), filters, (row4.append(exp, imp, copy, clr), row4), io, (row5.append(delay, limit, (read.append(readBox, " Read Mirror"), read), (write.append(writeBox, " Write Mirror"), write)), row5), (foot.prepend(stats), foot.append(link), foot));
1286
1270
  this.host.append(toggle, panel);
1287
- this.keydown = (e, can = this.state.open && (this.config.devOnly ? CTX.isDevEnv : true), a = can && keyEventAllowed(e, keys)) => (a && (e.preventDefault(), e.stopImmediatePropagation()), a === "undo" ? this.time.undo(this.state.stride) : a === "redo" ? this.time.redo(this.state.stride) : a === "genesis" ? this.time.jumpTo(0) : a === "trackUntrack" ? this.time[s.tracking ? "untrack" : "track"]() : a === "ending" ? this.time.jumpTo(s.history.length) : 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(null, 2) : a === "import" ? this.state.import.trim().length && this.time.import(this.state.import) : a === "clear" && (this.state.import = ""));
1271
+ this.keydown = (e, can = this.config.devOnly ? CTX.isDevEnv : true, a = can && keyEventAllowed(e, keys), will = a === "toggle" || a && this.state.open) => will && (e.preventDefault(), e.stopImmediatePropagation(), a === "toggle" ? this.state.open = !this.state.open : a === "escape" ? this.state.open = false : a === "undo" ? this.time.undo(this.state.stride) : a === "redo" ? this.time.redo(this.state.stride) : a === "genesis" ? this.time.jumpTo(0) : a === "trackUntrack" ? this.time[s.tracking ? "untrack" : "track"]() : a === "ending" ? this.time.jumpTo(s.history.length) : a === "rewind" ? this.time.rewind() : a === "playPause" ? this.time[s.paused ? "play" : "pause"]() : a === "prune" ? this.time.prune() : a === "reset" ? this.time.reset() : a === "export" ? this.state.import = this.time.export(null, 2) : a === "import" ? this.state.import.trim().length && this.time.import(this.state.import) : a === "clear" ? this.state.import = "" : a === "copy" && this.state.import.trim().length && navigator.clipboard?.writeText(this.state.import).catch?.(() => {
1272
+ }));
1288
1273
  window.addEventListener("keydown", this.keydown, { capture: true });
1289
1274
  const sync = [
1290
1275
  effect(() => this.config.color ? host.style.setProperty("--sia-tt-color", this.config.color) : host.style.removeProperty("--sia-tt-color")),
@@ -1298,7 +1283,7 @@ var TimeTravelConsole = class _TimeTravelConsole {
1298
1283
  effect(() => trackUntrack.textContent = `${s.tracking ? "Untrack" : "Track"}${formatKeyTooltip(keys.shortcuts.trackUntrack)}`),
1299
1284
  effect((sets = 0, txs = 0) => {
1300
1285
  range.max = String(s.history.length);
1301
- range.disabled = clrHistory.disabled = !s.history.length;
1286
+ range.disabled = reset.disabled = !s.history.length;
1302
1287
  for (let i = 0, len = s.history.length; i < len; i++) {
1303
1288
  const frame2 = s.history[i];
1304
1289
  "nodes" in frame2 ? txs++ : frame2.type === "set" && sets++;
@@ -1308,16 +1293,17 @@ var TimeTravelConsole = class _TimeTravelConsole {
1308
1293
  effect(() => {
1309
1294
  frame.textContent = `Frame: ${s.currentFrame} / ${s.history.length}`;
1310
1295
  genesis.disabled = undo.disabled = !this.time.canUndo;
1296
+ redo.disabled = prune.disabled = !this.time.canRedo;
1297
+ playPause.disabled = s.paused && redo.disabled;
1311
1298
  rewind.disabled = !s.paused || !s.currentFrame;
1312
- playPause.disabled = redo.disabled = !this.time.canRedo;
1313
1299
  range.value = String(Math.min(s.currentFrame, s.history.length));
1314
1300
  payload.value = JSON.stringify(s.currentFrame ? s.history[s.currentFrame - 1] : { genesis: true, value: s.initialState }, null, 2);
1315
1301
  }),
1316
1302
  effect(() => {
1317
- clr.disabled = imp.disabled = !this.state.import.trim().length;
1303
+ clr.disabled = copy.disabled = imp.disabled = !this.state.import.trim().length;
1318
1304
  io.value !== this.state.import && (io.value = this.state.import);
1319
1305
  }),
1320
- effect((v = String(this.time.config.playbackRate)) => v !== speed.value && (speed.value = v)),
1306
+ effect((v = String(s.playbackRate)) => v !== speed.value && (speed.value = v)),
1321
1307
  effect((v = String(this.state.stride)) => v !== stride.value && (stride.value = v)),
1322
1308
  effect((v = String(this.time.config.limit)) => v !== limit.value && (limit.value = v)),
1323
1309
  effect((v = String(this.time.config.maxPlaybackDelay)) => v !== delay.value && (delay.value = v)),