sia-reactor 0.0.20 → 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.
@@ -2,10 +2,10 @@ import {
2
2
  Autotracker,
3
3
  TimeTravelOverlay,
4
4
  withTracker
5
- } from "../chunk-RFQ2JJSV.js";
5
+ } from "../chunk-TFLYCXK4.js";
6
6
  import {
7
7
  getReactor
8
- } from "../chunk-VPR5SP3E.js";
8
+ } from "../chunk-2WBPGSRL.js";
9
9
  import "../chunk-5A44QFT6.js";
10
10
  import "../chunk-P37ADJMM.js";
11
11
  import {
@@ -16,7 +16,7 @@ import {
16
16
  } from "../chunk-DP74DVRT.js";
17
17
 
18
18
  // src/ts/adapters/react/hooks/useReactor.ts
19
- import { useRef, useCallback, useSyncExternalStore } from "react";
19
+ import { useRef, useCallback, useSyncExternalStore, useMemo } from "react";
20
20
 
21
21
  // src/ts/adapters/react/utils.ts
22
22
  import { useLayoutEffect, useEffect } from "react";
@@ -27,7 +27,7 @@ function useReactor(target, options = NIL, build) {
27
27
  const versionRef = useRef(0), tgtRef = useRef(), rtrRef = useRef(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, atrkrRef = useRef(), prevTrkr = CTX.autotracker, atrkr = CTX.autotracker = atrkrRef.current ||= new Autotracker(), optsRef = useRef(options), notifyRef = useRef(NOOP);
28
28
  optsRef.current = options;
29
29
  atrkr.unblock(rtr), queueMicrotask(() => CTX.autotracker === atrkr && (CTX.autotracker = prevTrkr));
30
- const subscribe = useCallback((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), []);
30
+ const subscribe = useCallback((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
31
31
  const getSnapshot = useCallback(() => versionRef.current, []);
32
32
  useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
33
33
  return useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(notifyRef.current, optsRef.current)), [atrkr]), rtr.core;
@@ -36,11 +36,21 @@ function useAnyReactor(options = NIL) {
36
36
  const versionRef = useRef(0), atrkrRef = useRef(), prevTrkr = CTX.autotracker, atrkr = CTX.autotracker = atrkrRef.current ||= new Autotracker(), optsRef = useRef(options), notifyRef = useRef(NOOP);
37
37
  optsRef.current = options;
38
38
  atrkr.unblock(), queueMicrotask(() => CTX.autotracker === atrkr && (CTX.autotracker = prevTrkr));
39
- const subscribe = useCallback((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), []);
39
+ const subscribe = useCallback((notify) => atrkr.callback(notifyRef.current = () => (versionRef.current++, notify())), [atrkr]);
40
40
  const getSnapshot = useCallback(() => versionRef.current, []);
41
41
  useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
42
42
  useISOLayoutEffect(() => (CTX.autotracker = prevTrkr, atrkr.callback(notifyRef.current, optsRef.current)), [atrkr]);
43
43
  }
44
+ function useReactorSnapshot(target, options, build = { referenceTracking: true, smartCloning: true }) {
45
+ const tgtRef = useRef(), rtrRef = useRef(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, atrkrRef = useRef(), atrkrRtrRef = useRef(), atrkr = atrkrRtrRef.current !== rtr || !atrkrRef.current ? (atrkrRtrRef.current = rtr, atrkrRef.current = new Autotracker(rtr)) : atrkrRef.current, notifyRef = useRef(NOOP), optsRef = useRef(options);
46
+ rtr.config.referenceTracking = rtr.config.smartCloning = true;
47
+ optsRef.current = options;
48
+ const subscribe = useCallback((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]);
49
+ const getSnapshot = () => rtr.snapshot();
50
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
51
+ const proxy = useMemo(() => atrkr.tracked(snapshot), [atrkr, snapshot]);
52
+ return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, proxy]), proxy;
53
+ }
44
54
 
45
55
  // src/ts/adapters/react/hooks/useSelector.ts
46
56
  import { useSyncExternalStore as useSyncExternalStore2, useRef as useRef2, useCallback as useCallback2 } from "react";
@@ -66,6 +76,18 @@ function useAnySelector(sel, eq = Object.is, options = NIL) {
66
76
  const slice = useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
67
77
  return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, slice]), slice;
68
78
  }
79
+ function useSelectorSnapshot(target, sel, eq = Object.is, options, build = { referenceTracking: true, smartCloning: true }) {
80
+ const tgtRef = useRef2(), rtrRef = useRef2(), rtr = tgtRef.current !== target || !rtrRef.current ? (tgtRef.current = target, rtrRef.current = getReactor(target, true, build)) : rtrRef.current, atrkrRef = useRef2(), atrkrRtrRef = useRef2(), atrkr = atrkrRtrRef.current !== rtr || !atrkrRef.current ? (atrkrRtrRef.current = rtr, atrkrRef.current = new Autotracker(rtr)) : atrkrRef.current, notifyRef = useRef2(NOOP), sliceRef = useRef2(), selRef = useRef2(sel), eqRef = useRef2(eq), optsRef = useRef2(options);
81
+ rtr.config.referenceTracking = rtr.config.smartCloning = true;
82
+ optsRef.current = options, selRef.current = sel, eqRef.current = eq;
83
+ const subscribe = useCallback2((notify) => (atrkr.callback(notifyRef.current = notify, optsRef.current), () => atrkr.cleanup()), [atrkr]);
84
+ const getSnapshot = useCallback2(() => {
85
+ const next = selRef.current(atrkr.tracked(rtr.snapshot()));
86
+ return eqRef.current(sliceRef.current, next) ? sliceRef.current : sliceRef.current = next;
87
+ }, [atrkr]);
88
+ const slice = useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
89
+ return useISOLayoutEffect(() => atrkr.callback(notifyRef.current, optsRef.current), [atrkr, slice]), slice;
90
+ }
69
91
 
70
92
  // src/ts/adapters/react/hooks/usePath.ts
71
93
  import { useRef as useRef3, useSyncExternalStore as useSyncExternalStore3, useCallback as useCallback3 } from "react";
@@ -79,14 +101,14 @@ function usePath(target, path, options = NIL, build) {
79
101
  }
80
102
 
81
103
  // src/ts/adapters/react/TimeTravelOverlay.tsx
82
- import { useEffect as useEffect2, useRef as useRef4 } from "react";
104
+ import { useRef as useRef4 } from "react";
83
105
  function TimeTravelOverlay2(props) {
84
- const vRef = useRef4(null), { time, title, color = "", devOnly = true, startOpen = false, container = document.body } = props;
85
- useEffect2(() => {
106
+ const vRef = useRef4(null), { time, title, color, devOnly, startOpen, container } = props;
107
+ useISOLayoutEffect(() => {
86
108
  vRef.current = new TimeTravelOverlay(time, props);
87
109
  return () => void (vRef.current?.destroy(), vRef.current = null);
88
110
  }, [time]);
89
- 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 ?? document.body, vRef.current.state.open = startOpen)), [title, color, devOnly, container, startOpen]);
111
+ 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]);
90
112
  return null;
91
113
  }
92
114
  export {
@@ -96,5 +118,7 @@ export {
96
118
  useISOLayoutEffect,
97
119
  usePath,
98
120
  useReactor,
99
- useSelector
121
+ useReactorSnapshot,
122
+ useSelector,
123
+ useSelectorSnapshot
100
124
  };
@@ -468,27 +468,27 @@ var Reactor = class {
468
468
  listeners;
469
469
  /**
470
470
  * Creates a new Reactor instance.
471
- * @param object Initial state object.
471
+ * @param target Initial state target.
472
472
  * @param build Reactor bootstrap/build configuration.
473
473
  * @example
474
474
  * const rtr = new Reactor({ count: 0 });
475
475
  */
476
- constructor(object = {}, build) {
476
+ constructor(target = {}, build) {
477
477
  this[INERTIA] = true;
478
478
  this.config = { crossRealms: false, smartCloning: false, eventBubbling: true, lineageTracing: false, preserveContext: false, equalityFunction: Object.is, batchingFunction: RTR_BATCH, ...build };
479
- this.core = this.proxied(object);
479
+ this.core = this.proxied(target);
480
480
  if (build) this.canLog = !!build.debug;
481
481
  }
482
- proxied(obj, rejectable = false, indiffable = false, parent, key, path) {
483
- if (!obj || "object" !== typeof obj) return obj;
484
- obj = obj[RAW] || obj;
485
- if (this.config.referenceTracking && parent && key && !this.link(obj, parent, key, false)) return obj;
486
- const cached = this.proxyCache.get(obj);
482
+ proxied(target, rejectable = false, indiffable = false, parent, key, path) {
483
+ if (!target || "object" !== typeof target) return target;
484
+ target = target[RAW] || target;
485
+ if (this.config.referenceTracking && parent && key && !this.link(target, parent, key, false)) return target;
486
+ const cached = this.proxyCache.get(target);
487
487
  if (cached) return cached;
488
- if (obj[INERTIA] || !canHandle(obj, this.config, false)) return obj;
489
- rejectable ||= obj[REJECTABLE];
490
- indiffable ||= obj[INDIFFABLE];
491
- const proxy = new Proxy(obj, {
488
+ if (target[INERTIA] || !canHandle(target, this.config, false)) return target;
489
+ rejectable ||= target[REJECTABLE];
490
+ indiffable ||= target[INDIFFABLE];
491
+ const proxy = new Proxy(target, {
492
492
  // Robust Proxy handler
493
493
  get: (object, key2, receiver) => {
494
494
  if (key2 === RAW) return this.log(`\u{1F440} [Reactor \`get\` Trap] Peeked at ${object}`), object;
@@ -501,10 +501,10 @@ var Reactor = class {
501
501
  for (let i = 0, len = this.config.lineageTracing ? paths.length : 1; i < len; i++) {
502
502
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.getters.get(currPath);
503
503
  if (!cords && !wildcords) continue;
504
- const target = { path: currPath, value, key: keyStr, hadKey: true, object: receiver }, payload = { type: "get", target, currentTarget: target, root: this.core, rejectable };
504
+ const target2 = { path: currPath, value, key: keyStr, hadKey: true, object: receiver }, payload = { type: "get", target: target2, currentTarget: target2, root: this.core, rejectable };
505
505
  if (cords) value = this.mediate(currPath, payload, "get", cords);
506
506
  if (!wildcords) continue;
507
- target.value = value;
507
+ target2.value = value;
508
508
  value = this.mediate("*", payload, "get", wildcords);
509
509
  }
510
510
  }
@@ -526,13 +526,13 @@ var Reactor = class {
526
526
  for (let i = 0; i < loopLen; i++) {
527
527
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.setters.get(currPath);
528
528
  if (!cords && !wildcords) continue;
529
- const target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "set", target, currentTarget: target, root: this.core, terminated, rejectable };
529
+ const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable };
530
530
  if (cords) {
531
531
  const result2 = this.mediate(currPath, payload, "set", cords);
532
532
  if (!(terminated ||= payload.terminated)) value = result2;
533
533
  }
534
534
  if (!wildcords) continue;
535
- target.value = value;
535
+ target2.value = value;
536
536
  const result = this.mediate("*", payload, "set", wildcords);
537
537
  if (!(terminated ||= payload.terminated)) value = result;
538
538
  }
@@ -543,8 +543,8 @@ var Reactor = class {
543
543
  if (this.config.referenceTracking && !unchanged) this.config.smartCloning && this.stamp(object), this.unlink(safeOldValue, object, keyStr), this.link(safeValue, object, keyStr);
544
544
  if (this.watchers || this.listeners)
545
545
  for (let i = 0; i < loopLen; i++) {
546
- const currPath = this.config.lineageTracing ? paths[i] : fullPath, target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
547
- this.notify(currPath, { type: "set", target, currentTarget: target, root: this.core, terminated, rejectable });
546
+ const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
547
+ this.notify(currPath, { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable });
548
548
  }
549
549
  return true;
550
550
  },
@@ -558,7 +558,7 @@ var Reactor = class {
558
558
  for (let i = 0; i < loopLen; i++) {
559
559
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.deleters.get(currPath);
560
560
  if (!cords && !wildcords) continue;
561
- const target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "delete", target, currentTarget: target, root: this.core, rejectable };
561
+ const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable };
562
562
  if (cords) {
563
563
  const result2 = this.mediate(currPath, payload, "delete", cords);
564
564
  if (!(terminated ||= payload.terminated)) value = result2;
@@ -574,8 +574,8 @@ var Reactor = class {
574
574
  if (this.config.referenceTracking) this.config.smartCloning && this.stamp(object), this.unlink(oldValue?.[RAW] || oldValue, object, keyStr);
575
575
  if (this.watchers || this.listeners)
576
576
  for (let i = 0; i < loopLen; i++) {
577
- const currPath = this.config.lineageTracing ? paths[i] : fullPath, target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
578
- this.notify(currPath, { type: "delete", target, currentTarget: target, root: this.core, rejectable });
577
+ const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
578
+ this.notify(currPath, { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable });
579
579
  }
580
580
  return true;
581
581
  },
@@ -601,7 +601,7 @@ var Reactor = class {
601
601
  return ownKeys;
602
602
  }
603
603
  });
604
- return this.proxyCache.set(obj, proxy), proxy;
604
+ return this.proxyCache.set(target, proxy), proxy;
605
605
  }
606
606
  trace(target, path, paths = [], seen = /* @__PURE__ */ new WeakSet()) {
607
607
  if (Object.is(target, this.core[RAW] || this.core)) return paths.push(path), paths;
@@ -754,15 +754,14 @@ var Reactor = class {
754
754
  if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
755
755
  return cord.sclup = !sig || sig.aborted ? NOOP : () => sig.removeEventListener("abort", cord.clup), cord.clup;
756
756
  }
757
- cloned(obj, raw, seen = /* @__PURE__ */ new WeakMap()) {
758
- if (!obj || "object" !== typeof obj) return obj;
759
- obj = obj[RAW] || obj;
760
- const cloned = seen.get(obj);
757
+ cloned(target, raw, seen = /* @__PURE__ */ new WeakMap()) {
758
+ if (!target || "object" !== typeof target) return target;
759
+ const obj = target[RAW] || target, cloned = seen.get(obj);
761
760
  if (cloned) return cloned;
762
761
  if (!canHandle(obj, this.config, false)) return obj;
763
762
  const version = obj[VERSION] || 0, cached = !raw && this.config.smartCloning && (this.snapCache ??= /* @__PURE__ */ new WeakMap()).get(obj);
764
763
  if (cached && obj[SSVERSION] === version) return cached;
765
- const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj[RAW] || obj;
764
+ const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
766
765
  seen.set(obj, clone);
767
766
  const keys2 = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
768
767
  for (let i = 0, len = keys2.length; i < len; i++) clone[keys2[i]] = this.cloned(obj[keys2[i]], raw, seen);
@@ -946,19 +945,8 @@ var Reactor = class {
946
945
  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;
947
946
  return false;
948
947
  }
949
- /**
950
- * Creates a snapshot; possibly clone of state (or a state branch).
951
- * You could alternatively use or serialize your proxied state "as is" except the environment demands no proxies or new references.
952
- * @param raw Use raw (deep unproxied & uncloned) version of branch.
953
- * @param branch Branch to clone.
954
- * @returns Snapshot deep or smart (structurally shared) clone.
955
- * @example
956
- * const snap = rtr.snapshot();
957
- * @example
958
- * const snap = rtr.snapshot(false, rtr.core.history.past);
959
- */
960
- snapshot(raw = !this.config.smartCloning, branch = this.core) {
961
- return this.cloned(branch, raw);
948
+ snapshot(raw = !this.config.smartCloning, branch) {
949
+ return this.cloned(arguments.length < 2 ? this.core : branch, raw);
962
950
  }
963
951
  /**
964
952
  * Cascades object updates into direct child paths.
@@ -1118,41 +1106,16 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
1118
1106
  */
1119
1107
  constructor(time, build = {}) {
1120
1108
  this.time = time;
1121
- this.config = reactive({ container: document.body, color: "", startOpen: false, devOnly: true, title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
1109
+ this.config = reactive({ title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
1122
1110
  this.state.open = !!this.config.startOpen;
1123
- const host = createEl("div", { className: "tt-overlay-host" });
1124
- const toggle = createEl("button", { className: "tt-overlay-toggle", type: "button", onclick: () => this.state.open = !this.state.open });
1125
- const panel = createEl("aside", { className: "tt-overlay", ariaLabel: "time travel overlay" });
1126
- const title = createEl("div", { className: "title" });
1127
- const frame = createEl("span", { className: "muted" });
1128
- const history = createEl("span", { className: "muted" });
1129
- const paused = createEl("span", { className: "muted" });
1130
- const clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") });
1131
- const undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo });
1132
- const redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo });
1133
- const genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis, false), onclick: () => this.time.jumpTo(0) });
1134
- const playPause = createEl("button", { onclick: () => this.time[this.time.state.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause, false) });
1135
- const rewind = createEl("button", { textContent: `Rewind${formatKeyForDisplay(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind, false), onclick: this.time.rewind });
1136
- const 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)) });
1137
- const exp = createEl("button", { textContent: `Export${formatKeyForDisplay(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export, false), onclick: () => this.state.import = this.time.export() });
1138
- const 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) });
1139
- const clr = createEl("button", { textContent: `Clear${formatKeyForDisplay(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear, false), onclick: () => this.state.import = "" });
1140
- const io = createEl("textarea", { className: "tt-io", placeholder: "timeline payload json", oninput: () => this.state.import = io.value });
1141
- const foot = createEl("p", { className: "tt-footnote", textContent: "Want this in your app? " });
1142
- const link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" });
1143
- const box = createEl("div", { className: "tt-status-box" });
1144
- const status = createEl("div", { className: "tt-status-row" });
1145
- const row1 = createEl("div", { className: "tt-row" });
1146
- const row2 = createEl("div", { className: "tt-row" });
1147
- const row3 = createEl("div", { className: "tt-row" });
1148
- status.append((box.append(frame, history, paused), box), clrHistory);
1149
- panel.append(title, status, (row1.append(undo, redo, genesis), row1), (row2.append(playPause, rewind), row2), range, (row3.append(exp, imp, clr), row3), io, (foot.appendChild(link), foot));
1111
+ const s = this.time.state, host = createEl("div", { className: "tt-overlay-host" }), toggle = createEl("button", { className: "tt-overlay-toggle", type: "button", onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "tt-overlay", ariaLabel: "time travel overlay" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo }), redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo }), genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis, false), onclick: () => this.time.jumpTo(0) }), playPause = createEl("button", { onclick: () => this.time[s.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause, false) }), rewind = createEl("button", { textContent: `Rewind${formatKeyForDisplay(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind, false), onclick: this.time.rewind }), range = createEl("input", { type: "range", min: "0", max: "0", value: "0", title: "time travel frame", ariaLabel: "time travel frame", oninput: () => this.time.jumpTo(Number(range.value)) }), exp = createEl("button", { textContent: `Export${formatKeyForDisplay(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export, false), onclick: () => this.state.import = this.time.export(null, 2) }), imp = createEl("button", { textContent: `Import${formatKeyForDisplay(keys.shortcuts.import)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.import, false), onclick: () => this.state.import.trim().length && this.time.import(this.state.import) }), clr = createEl("button", { textContent: `Clear${formatKeyForDisplay(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear, false), onclick: () => this.state.import = "" }), payload = createEl("textarea", { className: "tt-io", readOnly: true, placeholder: "current payload json", title: "current payload" }), io = createEl("textarea", { className: "tt-io", placeholder: "timeline payload json", oninput: () => this.state.import = io.value }), foot = createEl("p", { className: "tt-footnote", textContent: "Want this in your app? " }), link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" }), box = createEl("div", { className: "tt-status-box" }), status = createEl("div", { className: "tt-status-row" }), row1 = createEl("div", { className: "tt-row" }), row2 = createEl("div", { className: "tt-row" }), row3 = createEl("div", { className: "tt-row" });
1112
+ status.append((box.append(frame), box), clrHistory);
1113
+ 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));
1150
1114
  host.append(toggle, panel);
1151
- this.els = { host, toggle, panel, title, frame, history, paused, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, io };
1115
+ this.els = { host, toggle, panel, title, frame, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, payload, io };
1152
1116
  this.keyup = (e) => {
1153
- if (!this.state.open) return;
1154
- const a = keyEventAllowed(e, keys);
1155
- 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[this.time.state.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 = "");
1117
+ const a = this.state.open && keyEventAllowed(e, keys);
1118
+ a === "undo" ? this.time.undo() : a === "redo" ? this.time.redo() : a === "genesis" ? this.time.jumpTo(0) : a === "prevFrame" ? this.time.step(1, false) : a === "nextFrame" ? this.time.step(1, true) : a === "skipBwd" ? this.time.step(5, false) : a === "skipFwd" ? this.time.step(5, true) : a === "rewind" ? this.time.rewind() : a === "playPause" ? this.time[s.paused ? "play" : "pause"]() : a === "clrHistory" ? this.time.clear() : a === "closeOverlay" ? this.state.open = false : a === "export" ? this.state.import = this.time.export() : a === "import" ? this.state.import.trim().length && this.time.import(this.state.import) : a === "clear" && (this.state.import = "");
1156
1119
  };
1157
1120
  window.addEventListener("keydown", this.keyup);
1158
1121
  const sync = [
@@ -1162,26 +1125,19 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
1162
1125
  const dock = getDock(this.config.container);
1163
1126
  if (host.parentNode !== dock) dock.appendChild(host);
1164
1127
  }),
1165
- effect(() => (toggle.textContent = `${this.state.open ? "Hide" : "Show"} ${this.config.title ?? ""}`, panel.hidden = !this.state.open)),
1128
+ effect(() => toggle.textContent = `${(panel.hidden = !this.state.open) ? "Show" : "Hide"} ${title.textContent = this.config.title ?? ""}`),
1129
+ effect(() => playPause.textContent = `${s.paused ? "Play" : "Pause"}${formatKeyForDisplay(keys.shortcuts.playPause)}`),
1166
1130
  effect(() => {
1167
- title.textContent = this.config.title ?? "";
1168
- frame.textContent = `Frame: ${this.time.state.currentFrame} / ${this.time.state.history.length}`;
1169
- history.textContent = `History: ${this.time.state.history.length}`;
1170
- paused.textContent = `Paused: ${this.time.state.paused ? "Yes" : "No"}`;
1171
- playPause.textContent = `${this.time.state.paused ? "Play" : "Pause"}${formatKeyForDisplay(keys.shortcuts.playPause)}`;
1131
+ frame.textContent = `Frame: ${s.currentFrame} / ${s.history.length}`;
1132
+ range.disabled = clrHistory.disabled = !s.history.length;
1133
+ genesis.disabled = rewind.disabled = undo.disabled = !s.currentFrame;
1134
+ playPause.disabled = redo.disabled = s.currentFrame >= s.history.length;
1135
+ range.max = String(s.history.length);
1136
+ range.value = String(Math.min(s.currentFrame, s.history.length));
1137
+ payload.value = JSON.stringify(s.currentFrame ? s.history[s.currentFrame - 1] : { type: "genesis", value: s.initialState }, null, 2);
1172
1138
  }),
1173
1139
  effect(() => {
1174
- clrHistory.disabled = !this.time.state.history.length;
1175
- undo.disabled = this.time.state.currentFrame <= 0;
1176
- redo.disabled = this.time.state.currentFrame >= this.time.state.history.length;
1177
- genesis.disabled = this.time.state.currentFrame <= 0;
1178
- playPause.disabled = this.time.state.currentFrame === this.time.state.history.length;
1179
- rewind.disabled = !this.time.state.currentFrame;
1180
- range.max = String(this.time.state.history.length);
1181
- range.value = String(Math.min(this.time.state.currentFrame, this.time.state.history.length));
1182
- range.disabled = !this.time.state.history.length;
1183
- imp.disabled = !this.state.import.trim().length;
1184
- clr.disabled = !this.state.import.trim().length;
1140
+ clr.disabled = imp.disabled = !this.state.import.trim().length;
1185
1141
  io.value !== this.state.import && (io.value = this.state.import);
1186
1142
  })
1187
1143
  ];
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions } from '../index-DCG3sacH.cjs';
2
- export { A as Autotracker, w as withTracker } from '../index-DCG3sacH.cjs';
3
- export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-BYSnHBXx.cjs';
4
- import '../timeTravel-L8CEhHIo.cjs';
1
+ import { E as EffectOptions } from '../index-Oie9hhE8.cjs';
2
+ export { A as Autotracker, w as withTracker } from '../index-Oie9hhE8.cjs';
3
+ export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-CJv-S_Km.cjs';
4
+ import '../timeTravel-WpgWmKu-.cjs';
5
5
 
6
6
  /**
7
7
  * Runs a reactive side effect in vanilla JavaScript.
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions } from '../index-DCG3sacH.js';
2
- export { A as Autotracker, w as withTracker } from '../index-DCG3sacH.js';
3
- export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-DoNrZwvX.js';
4
- import '../timeTravel-Bv_u5M1D.js';
1
+ import { E as EffectOptions } from '../index-Oie9hhE8.js';
2
+ export { A as Autotracker, w as withTracker } from '../index-Oie9hhE8.js';
3
+ export { T as TimeTravelConfig, a as TimeTravelOverlay } from '../TimeTravelOverlay-DxqJL0Zk.js';
4
+ import '../timeTravel-B1vedDQc.js';
5
5
 
6
6
  /**
7
7
  * Runs a reactive side effect in vanilla JavaScript.
@@ -3,8 +3,8 @@ import {
3
3
  TimeTravelOverlay,
4
4
  effect,
5
5
  withTracker
6
- } from "../chunk-RFQ2JJSV.js";
7
- import "../chunk-VPR5SP3E.js";
6
+ } from "../chunk-TFLYCXK4.js";
7
+ import "../chunk-2WBPGSRL.js";
8
8
  import "../chunk-5A44QFT6.js";
9
9
  import "../chunk-P37ADJMM.js";
10
10
  import "../chunk-DP74DVRT.js";
@@ -175,27 +175,27 @@ var Reactor = class {
175
175
  listeners;
176
176
  /**
177
177
  * Creates a new Reactor instance.
178
- * @param object Initial state object.
178
+ * @param target Initial state target.
179
179
  * @param build Reactor bootstrap/build configuration.
180
180
  * @example
181
181
  * const rtr = new Reactor({ count: 0 });
182
182
  */
183
- constructor(object = {}, build) {
183
+ constructor(target = {}, build) {
184
184
  this[INERTIA] = true;
185
185
  this.config = { crossRealms: false, smartCloning: false, eventBubbling: true, lineageTracing: false, preserveContext: false, equalityFunction: Object.is, batchingFunction: RTR_BATCH, ...build };
186
- this.core = this.proxied(object);
186
+ this.core = this.proxied(target);
187
187
  if (build) this.canLog = !!build.debug;
188
188
  }
189
- proxied(obj, rejectable = false, indiffable = false, parent, key, path) {
190
- if (!obj || "object" !== typeof obj) return obj;
191
- obj = obj[RAW] || obj;
192
- if (this.config.referenceTracking && parent && key && !this.link(obj, parent, key, false)) return obj;
193
- const cached = this.proxyCache.get(obj);
189
+ proxied(target, rejectable = false, indiffable = false, parent, key, path) {
190
+ if (!target || "object" !== typeof target) return target;
191
+ target = target[RAW] || target;
192
+ if (this.config.referenceTracking && parent && key && !this.link(target, parent, key, false)) return target;
193
+ const cached = this.proxyCache.get(target);
194
194
  if (cached) return cached;
195
- if (obj[INERTIA] || !canHandle(obj, this.config, false)) return obj;
196
- rejectable ||= obj[REJECTABLE];
197
- indiffable ||= obj[INDIFFABLE];
198
- const proxy = new Proxy(obj, {
195
+ if (target[INERTIA] || !canHandle(target, this.config, false)) return target;
196
+ rejectable ||= target[REJECTABLE];
197
+ indiffable ||= target[INDIFFABLE];
198
+ const proxy = new Proxy(target, {
199
199
  // Robust Proxy handler
200
200
  get: (object, key2, receiver) => {
201
201
  if (key2 === RAW) return this.log(`\u{1F440} [Reactor \`get\` Trap] Peeked at ${object}`), object;
@@ -208,10 +208,10 @@ var Reactor = class {
208
208
  for (let i = 0, len = this.config.lineageTracing ? paths.length : 1; i < len; i++) {
209
209
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.getters.get(currPath);
210
210
  if (!cords && !wildcords) continue;
211
- const target = { path: currPath, value, key: keyStr, hadKey: true, object: receiver }, payload = { type: "get", target, currentTarget: target, root: this.core, rejectable };
211
+ const target2 = { path: currPath, value, key: keyStr, hadKey: true, object: receiver }, payload = { type: "get", target: target2, currentTarget: target2, root: this.core, rejectable };
212
212
  if (cords) value = this.mediate(currPath, payload, "get", cords);
213
213
  if (!wildcords) continue;
214
- target.value = value;
214
+ target2.value = value;
215
215
  value = this.mediate("*", payload, "get", wildcords);
216
216
  }
217
217
  }
@@ -233,13 +233,13 @@ var Reactor = class {
233
233
  for (let i = 0; i < loopLen; i++) {
234
234
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.setters.get(currPath);
235
235
  if (!cords && !wildcords) continue;
236
- const target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "set", target, currentTarget: target, root: this.core, terminated, rejectable };
236
+ const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable };
237
237
  if (cords) {
238
238
  const result2 = this.mediate(currPath, payload, "set", cords);
239
239
  if (!(terminated ||= payload.terminated)) value = result2;
240
240
  }
241
241
  if (!wildcords) continue;
242
- target.value = value;
242
+ target2.value = value;
243
243
  const result = this.mediate("*", payload, "set", wildcords);
244
244
  if (!(terminated ||= payload.terminated)) value = result;
245
245
  }
@@ -250,8 +250,8 @@ var Reactor = class {
250
250
  if (this.config.referenceTracking && !unchanged) this.config.smartCloning && this.stamp(object), this.unlink(safeOldValue, object, keyStr), this.link(safeValue, object, keyStr);
251
251
  if (this.watchers || this.listeners)
252
252
  for (let i = 0; i < loopLen; i++) {
253
- const currPath = this.config.lineageTracing ? paths[i] : fullPath, target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
254
- this.notify(currPath, { type: "set", target, currentTarget: target, root: this.core, terminated, rejectable });
253
+ const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
254
+ this.notify(currPath, { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable });
255
255
  }
256
256
  return true;
257
257
  },
@@ -265,7 +265,7 @@ var Reactor = class {
265
265
  for (let i = 0; i < loopLen; i++) {
266
266
  const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.deleters.get(currPath);
267
267
  if (!cords && !wildcords) continue;
268
- const target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "delete", target, currentTarget: target, root: this.core, rejectable };
268
+ const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable };
269
269
  if (cords) {
270
270
  const result2 = this.mediate(currPath, payload, "delete", cords);
271
271
  if (!(terminated ||= payload.terminated)) value = result2;
@@ -281,8 +281,8 @@ var Reactor = class {
281
281
  if (this.config.referenceTracking) this.config.smartCloning && this.stamp(object), this.unlink(oldValue?.[RAW] || oldValue, object, keyStr);
282
282
  if (this.watchers || this.listeners)
283
283
  for (let i = 0; i < loopLen; i++) {
284
- const currPath = this.config.lineageTracing ? paths[i] : fullPath, target = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
285
- this.notify(currPath, { type: "delete", target, currentTarget: target, root: this.core, rejectable });
284
+ const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
285
+ this.notify(currPath, { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable });
286
286
  }
287
287
  return true;
288
288
  },
@@ -308,7 +308,7 @@ var Reactor = class {
308
308
  return ownKeys;
309
309
  }
310
310
  });
311
- return this.proxyCache.set(obj, proxy), proxy;
311
+ return this.proxyCache.set(target, proxy), proxy;
312
312
  }
313
313
  trace(target, path, paths = [], seen = /* @__PURE__ */ new WeakSet()) {
314
314
  if (Object.is(target, this.core[RAW] || this.core)) return paths.push(path), paths;
@@ -461,15 +461,14 @@ var Reactor = class {
461
461
  if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
462
462
  return cord.sclup = !sig || sig.aborted ? NOOP : () => sig.removeEventListener("abort", cord.clup), cord.clup;
463
463
  }
464
- cloned(obj, raw, seen = /* @__PURE__ */ new WeakMap()) {
465
- if (!obj || "object" !== typeof obj) return obj;
466
- obj = obj[RAW] || obj;
467
- const cloned = seen.get(obj);
464
+ cloned(target, raw, seen = /* @__PURE__ */ new WeakMap()) {
465
+ if (!target || "object" !== typeof target) return target;
466
+ const obj = target[RAW] || target, cloned = seen.get(obj);
468
467
  if (cloned) return cloned;
469
468
  if (!canHandle(obj, this.config, false)) return obj;
470
469
  const version = obj[VERSION] || 0, cached = !raw && this.config.smartCloning && (this.snapCache ??= /* @__PURE__ */ new WeakMap()).get(obj);
471
470
  if (cached && obj[SSVERSION] === version) return cached;
472
- const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj[RAW] || obj;
471
+ const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
473
472
  seen.set(obj, clone);
474
473
  const keys = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
475
474
  for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = this.cloned(obj[keys[i]], raw, seen);
@@ -653,19 +652,8 @@ var Reactor = class {
653
652
  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;
654
653
  return false;
655
654
  }
656
- /**
657
- * Creates a snapshot; possibly clone of state (or a state branch).
658
- * You could alternatively use or serialize your proxied state "as is" except the environment demands no proxies or new references.
659
- * @param raw Use raw (deep unproxied & uncloned) version of branch.
660
- * @param branch Branch to clone.
661
- * @returns Snapshot deep or smart (structurally shared) clone.
662
- * @example
663
- * const snap = rtr.snapshot();
664
- * @example
665
- * const snap = rtr.snapshot(false, rtr.core.history.past);
666
- */
667
- snapshot(raw = !this.config.smartCloning, branch = this.core) {
668
- return this.cloned(branch, raw);
655
+ snapshot(raw = !this.config.smartCloning, branch) {
656
+ return this.cloned(arguments.length < 2 ? this.core : branch, raw);
669
657
  }
670
658
  /**
671
659
  * Cascades object updates into direct child paths.