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