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.
- package/README.md +6 -8
- package/dist/{TimeTravelOverlay-BYSnHBXx.d.cts → TimeTravelOverlay-CJv-S_Km.d.cts} +2 -2
- package/dist/{TimeTravelOverlay-DoNrZwvX.d.ts → TimeTravelOverlay-DxqJL0Zk.d.ts} +2 -2
- package/dist/adapters/react.cjs +78 -96
- package/dist/adapters/react.d.cts +47 -6
- package/dist/adapters/react.d.ts +47 -6
- package/dist/adapters/react.js +34 -10
- package/dist/adapters/vanilla.cjs +45 -89
- package/dist/adapters/vanilla.d.cts +4 -4
- package/dist/adapters/vanilla.d.ts +4 -4
- package/dist/adapters/vanilla.js +2 -2
- package/dist/{chunk-VPR5SP3E.js → chunk-2WBPGSRL.js} +28 -40
- package/dist/{chunk-RFQ2JJSV.js → chunk-TFLYCXK4.js} +18 -50
- package/dist/{index-DCG3sacH.d.cts → index-Oie9hhE8.d.cts} +6 -5
- package/dist/{index-DCG3sacH.d.ts → index-Oie9hhE8.d.ts} +6 -5
- package/dist/index.cjs +28 -40
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins.cjs +46 -51
- package/dist/plugins.d.cts +3 -3
- package/dist/plugins.d.ts +3 -3
- package/dist/plugins.js +19 -12
- package/dist/styles/time-travel-overlay.css +1 -1
- package/dist/super.d.ts +15 -13
- package/dist/super.global.js +63 -100
- package/dist/{timeTravel-Bv_u5M1D.d.ts → timeTravel-B1vedDQc.d.ts} +10 -9
- package/dist/{timeTravel-L8CEhHIo.d.cts → timeTravel-WpgWmKu-.d.cts} +10 -9
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/package.json +1 -1
package/dist/plugins.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
reactive
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2WBPGSRL.js";
|
|
4
4
|
import {
|
|
5
5
|
clamp,
|
|
6
6
|
guardAllMethods,
|
|
@@ -246,9 +246,10 @@ var PERSIST_PLUGIN_BUILD = { disabled: false, key: "REACTOR_STORE", throttle: 25
|
|
|
246
246
|
// src/ts/plugins/timeTravel.ts
|
|
247
247
|
var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
248
248
|
static plugName = "timeTravel";
|
|
249
|
+
lastTimestamp = 0;
|
|
249
250
|
playbackTimeoutId = -1;
|
|
250
251
|
constructor(config, rtr) {
|
|
251
|
-
super({ ...TIME_TRAVEL_PLUGIN_BUILD, ...config }, rtr, {
|
|
252
|
+
super({ ...TIME_TRAVEL_PLUGIN_BUILD, ...config }, rtr, { initialState: null, history: [], currentFrame: 0, paused: true });
|
|
252
253
|
}
|
|
253
254
|
// ===========================================================================
|
|
254
255
|
// THE FOUNDATION & WIRETAP (Passive Recording)
|
|
@@ -256,6 +257,7 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
256
257
|
wire() {
|
|
257
258
|
this.rtr.config.referenceTracking = this.rtr.config.smartCloning = this.rtr.config.eventTimeStamps = true;
|
|
258
259
|
if (!this.state.history.length || this.state.initialState == null) this.state.initialState = this.rtr.snapshot();
|
|
260
|
+
this.lastTimestamp = performance.now();
|
|
259
261
|
this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
|
|
260
262
|
this.config.on("paths", this.handlePathsState, { signal: this.signal, immediate: true });
|
|
261
263
|
!this.state.paused && this.play();
|
|
@@ -269,8 +271,9 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
269
271
|
if (!this.state.paused) return;
|
|
270
272
|
if (this.state.currentFrame < this.state.history.length) this.state.history = this.state.history.slice(0, this.state.currentFrame);
|
|
271
273
|
if (this.state.history.length >= this.config.maxHistoryLength) this.state.history = this.state.history.slice(1);
|
|
272
|
-
this.state.history.push({
|
|
274
|
+
this.state.history.push({ path: e.target.path, value: this.rtr.snapshot(false, e.target.value), oldValue: this.rtr.snapshot(false, e.target.oldValue), type: e.staticType, rejected: e.rejected, timedelta: e.timestamp - this.lastTimestamp, hadKey: e.target.hadKey });
|
|
273
275
|
this.state.currentFrame = this.state.history.length;
|
|
276
|
+
this.lastTimestamp = e.timestamp;
|
|
274
277
|
}
|
|
275
278
|
/** Clears timeline history and resets playhead/genesis to the current reactor state. */
|
|
276
279
|
clear() {
|
|
@@ -278,6 +281,7 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
278
281
|
this.playbackTimeoutId = -1;
|
|
279
282
|
this.state.history.length = this.state.currentFrame = 0;
|
|
280
283
|
this.state.initialState = this.rtr.snapshot();
|
|
284
|
+
this.lastTimestamp = performance.now();
|
|
281
285
|
}
|
|
282
286
|
// ===========================================================================
|
|
283
287
|
// THE TIME MACHINE (Manual Controls)
|
|
@@ -313,9 +317,9 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
313
317
|
async automove(forward = true) {
|
|
314
318
|
this.state.paused = false;
|
|
315
319
|
while ((forward ? this.state.currentFrame < this.state.history.length : this.state.currentFrame > 0) && !this.state.paused) {
|
|
316
|
-
const currIndex = forward ? this.state.currentFrame : this.state.currentFrame - 1, e = this.state.history[
|
|
320
|
+
const currIndex = forward ? this.state.currentFrame : this.state.currentFrame - 1, e = this.state.history[forward ? currIndex + 1 : currIndex - 1];
|
|
317
321
|
this.jumpTo(this.state.currentFrame + (forward ? 1 : -1), true);
|
|
318
|
-
if (
|
|
322
|
+
if (e?.timedelta > 0) await new Promise((res) => this.playbackTimeoutId = setTimeout(() => res(0), Math.min(e.timedelta, this.config.maxPlaybackDelay), this.signal));
|
|
319
323
|
}
|
|
320
324
|
this.state.paused = true;
|
|
321
325
|
}
|
|
@@ -329,21 +333,24 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
329
333
|
// TELEMETRY & I/O (Session Import/Export)
|
|
330
334
|
// ===========================================================================
|
|
331
335
|
/** Exports the current session as a JSON string. */
|
|
332
|
-
export() {
|
|
333
|
-
|
|
336
|
+
export(replacer, space) {
|
|
337
|
+
try {
|
|
338
|
+
return JSON.stringify(this.state, replacer, space);
|
|
339
|
+
} catch (e) {
|
|
340
|
+
return this.rtr.log(`[Reactor ${this.name} Plug] Failed to export session`), "";
|
|
341
|
+
}
|
|
334
342
|
}
|
|
335
343
|
/** Imports a session from a JSON string, allowing you to replay or analyze past states. */
|
|
336
344
|
import(json) {
|
|
337
345
|
try {
|
|
338
346
|
setAny(this.state, "*", JSON.parse(json));
|
|
347
|
+
this.lastTimestamp = performance.now();
|
|
339
348
|
const resume = !this.state.paused, target = this.state.currentFrame;
|
|
340
349
|
this.state.paused = false;
|
|
341
|
-
setAny(this.rtr.core, "*", deepClone(this.state.initialState, this.rtr.config));
|
|
342
|
-
this.
|
|
343
|
-
this.state.currentFrame = 0;
|
|
344
|
-
this.jumpTo(target), resume && this.play();
|
|
350
|
+
setAny(this.rtr.core, "*", deepClone(this.state.initialState, this.rtr.config)), this.rtr.tick();
|
|
351
|
+
this.state.currentFrame = 0, this.jumpTo(target), resume && this.play();
|
|
345
352
|
} catch (e) {
|
|
346
|
-
this.rtr.log(`[Reactor ${this.name} Plug] Failed to load session
|
|
353
|
+
this.rtr.log(`[Reactor ${this.name} Plug] Failed to load session`);
|
|
347
354
|
}
|
|
348
355
|
}
|
|
349
356
|
};
|
package/dist/super.d.ts
CHANGED
|
@@ -769,13 +769,13 @@ declare class Reactor<T extends object> {
|
|
|
769
769
|
protected listeners?: Map<WildPaths<T>, Array<ListenerRecord<T>>>;
|
|
770
770
|
/**
|
|
771
771
|
* Creates a new Reactor instance.
|
|
772
|
-
* @param
|
|
772
|
+
* @param target Initial state target.
|
|
773
773
|
* @param build Reactor bootstrap/build configuration.
|
|
774
774
|
* @example
|
|
775
775
|
* const rtr = new Reactor({ count: 0 });
|
|
776
776
|
*/
|
|
777
|
-
constructor(
|
|
778
|
-
proxied<O extends object>(
|
|
777
|
+
constructor(target?: T, build?: ReactorBuild<T>);
|
|
778
|
+
proxied<O extends object>(target: O, rejectable?: boolean, indiffable?: boolean, parent?: object, key?: string, path?: string): O;
|
|
779
779
|
trace(target: object, path: string, paths?: string[], seen?: WeakSet<object>): Paths<T>[];
|
|
780
780
|
protected link(target: any, parent: object, key: string, typecheck?: boolean, es?: (object | string)[]): boolean;
|
|
781
781
|
protected unlink(target: any, parent: object, key: string): void;
|
|
@@ -815,7 +815,7 @@ declare class Reactor<T extends object> {
|
|
|
815
815
|
getDepth(path: string, depth?: number): number;
|
|
816
816
|
protected getContext<P extends WildPaths<T>>(path: P): Target<T, P>;
|
|
817
817
|
protected bindSignal<Cb>(cord: GetterRecord<T> | SetterRecord<T> | DeleterRecord<T> | WatcherRecord<T> | ListenerRecord<T>, sig?: AbortSignal): Cb;
|
|
818
|
-
protected cloned(
|
|
818
|
+
protected cloned<O>(target: O, raw: boolean, seen?: WeakMap<WeakKey, any>): O;
|
|
819
819
|
protected syncAdd<P extends WildPaths<T>>(key: "get" | "set" | "delete" | "watch", path: P, cb: any, opts: SyncOptions | undefined, onImmediate?: (immediate: boolean | "auto") => void): () => boolean | undefined;
|
|
820
820
|
protected syncDrop<P extends WildPaths<T>>(store: Map<WildPaths<T>, any[]> | undefined, path: P, cb: any): boolean | undefined;
|
|
821
821
|
/**
|
|
@@ -931,7 +931,8 @@ declare class Reactor<T extends object> {
|
|
|
931
931
|
* @example
|
|
932
932
|
* const snap = rtr.snapshot(false, rtr.core.history.past);
|
|
933
933
|
*/
|
|
934
|
-
snapshot(raw?: boolean
|
|
934
|
+
snapshot(raw?: boolean): T;
|
|
935
|
+
snapshot<B>(raw?: boolean, branch?: B): B;
|
|
935
936
|
/**
|
|
936
937
|
* Cascades object updates into direct child paths.
|
|
937
938
|
* @param payload Event or payload source.
|
|
@@ -1390,20 +1391,20 @@ declare const PERSIST_PLUGIN_BUILD: Partial<PersistConfig<any>>;
|
|
|
1390
1391
|
|
|
1391
1392
|
/** The DNA of a specific moment in time, Records the 'Desire' (Intent) or the 'Fact' (State). */
|
|
1392
1393
|
interface HistoryEntry {
|
|
1393
|
-
/** Was it a 'set' or a 'delete' surgery? */
|
|
1394
|
-
type: REvent<any, any>["staticType"];
|
|
1395
1394
|
/** The surgical address in the Reactor */
|
|
1396
1395
|
path: string;
|
|
1397
1396
|
/** The data payload at that moment */
|
|
1398
1397
|
value: any;
|
|
1399
1398
|
/** The "Undo" antidote (Previous value), if applicable */
|
|
1400
1399
|
oldValue: any;
|
|
1401
|
-
/**
|
|
1402
|
-
|
|
1400
|
+
/** Was it a 'set' or a 'delete' surgery? */
|
|
1401
|
+
type: REvent<any, any>["staticType"];
|
|
1403
1402
|
/** Did the Power Line disapprove?; why? */
|
|
1404
1403
|
rejected: string;
|
|
1405
1404
|
/** For chronological re-enactment */
|
|
1406
|
-
|
|
1405
|
+
timedelta: number;
|
|
1406
|
+
/** Did the key for the value exist on its parent object? */
|
|
1407
|
+
hadKey: boolean;
|
|
1407
1408
|
}
|
|
1408
1409
|
interface TimeTravelConfig$1<T extends object = any> {
|
|
1409
1410
|
/** Specific paths only, no "*"; instead don't pass anything */
|
|
@@ -1414,10 +1415,10 @@ interface TimeTravelConfig$1<T extends object = any> {
|
|
|
1414
1415
|
maxPlaybackDelay: number;
|
|
1415
1416
|
}
|
|
1416
1417
|
interface TimeTravelState {
|
|
1417
|
-
/** The "Timeline" of mutations (Chronological Log) */
|
|
1418
|
-
history: HistoryEntry[];
|
|
1419
1418
|
/** The "Genesis" snapshot (Raw Data) */
|
|
1420
1419
|
initialState: any;
|
|
1420
|
+
/** The "Timeline" of mutations (Chronological Log) */
|
|
1421
|
+
history: HistoryEntry[];
|
|
1421
1422
|
/** The manual playhead (Index in the Timeline) */
|
|
1422
1423
|
currentFrame: number;
|
|
1423
1424
|
/** Whether playback is currently paused (Automatic Replay) */
|
|
@@ -1429,6 +1430,7 @@ interface TimeTravelState {
|
|
|
1429
1430
|
*/
|
|
1430
1431
|
declare class TimeTravelPlugin<T extends object = any> extends BaseReactorPlugin<T, TimeTravelConfig$1<T>, TimeTravelState> {
|
|
1431
1432
|
static readonly plugName = "timeTravel";
|
|
1433
|
+
protected lastTimestamp: number;
|
|
1432
1434
|
protected playbackTimeoutId: number;
|
|
1433
1435
|
constructor(config?: Partial<TimeTravelConfig$1<T>>, rtr?: Reactor<T>);
|
|
1434
1436
|
wire(): void;
|
|
@@ -1454,7 +1456,7 @@ declare class TimeTravelPlugin<T extends object = any> extends BaseReactorPlugin
|
|
|
1454
1456
|
/** Pauses the live VCR playback. */
|
|
1455
1457
|
pause: () => void;
|
|
1456
1458
|
/** Exports the current session as a JSON string. */
|
|
1457
|
-
export(): string;
|
|
1459
|
+
export(replacer?: ((this: any, key: string, value: any) => any) | (number | string)[] | null, space?: string | number): string;
|
|
1458
1460
|
/** Imports a session from a JSON string, allowing you to replay or analyze past states. */
|
|
1459
1461
|
import(json: string): void;
|
|
1460
1462
|
}
|
package/dist/super.global.js
CHANGED
|
@@ -363,27 +363,27 @@ var sia = (() => {
|
|
|
363
363
|
listeners;
|
|
364
364
|
/**
|
|
365
365
|
* Creates a new Reactor instance.
|
|
366
|
-
* @param
|
|
366
|
+
* @param target Initial state target.
|
|
367
367
|
* @param build Reactor bootstrap/build configuration.
|
|
368
368
|
* @example
|
|
369
369
|
* const rtr = new Reactor({ count: 0 });
|
|
370
370
|
*/
|
|
371
|
-
constructor(
|
|
371
|
+
constructor(target = {}, build) {
|
|
372
372
|
this[INERTIA] = true;
|
|
373
373
|
this.config = { crossRealms: false, smartCloning: false, eventBubbling: true, lineageTracing: false, preserveContext: false, equalityFunction: Object.is, batchingFunction: RTR_BATCH, ...build };
|
|
374
|
-
this.core = this.proxied(
|
|
374
|
+
this.core = this.proxied(target);
|
|
375
375
|
if (build) this.canLog = !!build.debug;
|
|
376
376
|
}
|
|
377
|
-
proxied(
|
|
378
|
-
if (!
|
|
379
|
-
|
|
380
|
-
if (this.config.referenceTracking && parent && key && !this.link(
|
|
381
|
-
const cached = this.proxyCache.get(
|
|
377
|
+
proxied(target, rejectable = false, indiffable = false, parent, key, path) {
|
|
378
|
+
if (!target || "object" !== typeof target) return target;
|
|
379
|
+
target = target[RAW] || target;
|
|
380
|
+
if (this.config.referenceTracking && parent && key && !this.link(target, parent, key, false)) return target;
|
|
381
|
+
const cached = this.proxyCache.get(target);
|
|
382
382
|
if (cached) return cached;
|
|
383
|
-
if (
|
|
384
|
-
rejectable ||=
|
|
385
|
-
indiffable ||=
|
|
386
|
-
const proxy = new Proxy(
|
|
383
|
+
if (target[INERTIA] || !canHandle(target, this.config, false)) return target;
|
|
384
|
+
rejectable ||= target[REJECTABLE];
|
|
385
|
+
indiffable ||= target[INDIFFABLE];
|
|
386
|
+
const proxy = new Proxy(target, {
|
|
387
387
|
// Robust Proxy handler
|
|
388
388
|
get: (object, key2, receiver) => {
|
|
389
389
|
if (key2 === RAW) return this.log(`\u{1F440} [Reactor \`get\` Trap] Peeked at ${object}`), object;
|
|
@@ -396,10 +396,10 @@ var sia = (() => {
|
|
|
396
396
|
for (let i = 0, len = this.config.lineageTracing ? paths.length : 1; i < len; i++) {
|
|
397
397
|
const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.getters.get(currPath);
|
|
398
398
|
if (!cords && !wildcords) continue;
|
|
399
|
-
const
|
|
399
|
+
const target2 = { path: currPath, value, key: keyStr, hadKey: true, object: receiver }, payload = { type: "get", target: target2, currentTarget: target2, root: this.core, rejectable };
|
|
400
400
|
if (cords) value = this.mediate(currPath, payload, "get", cords);
|
|
401
401
|
if (!wildcords) continue;
|
|
402
|
-
|
|
402
|
+
target2.value = value;
|
|
403
403
|
value = this.mediate("*", payload, "get", wildcords);
|
|
404
404
|
}
|
|
405
405
|
}
|
|
@@ -421,13 +421,13 @@ var sia = (() => {
|
|
|
421
421
|
for (let i = 0; i < loopLen; i++) {
|
|
422
422
|
const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.setters.get(currPath);
|
|
423
423
|
if (!cords && !wildcords) continue;
|
|
424
|
-
const
|
|
424
|
+
const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable };
|
|
425
425
|
if (cords) {
|
|
426
426
|
const result2 = this.mediate(currPath, payload, "set", cords);
|
|
427
427
|
if (!(terminated ||= payload.terminated)) value = result2;
|
|
428
428
|
}
|
|
429
429
|
if (!wildcords) continue;
|
|
430
|
-
|
|
430
|
+
target2.value = value;
|
|
431
431
|
const result = this.mediate("*", payload, "set", wildcords);
|
|
432
432
|
if (!(terminated ||= payload.terminated)) value = result;
|
|
433
433
|
}
|
|
@@ -438,8 +438,8 @@ var sia = (() => {
|
|
|
438
438
|
if (this.config.referenceTracking && !unchanged) this.config.smartCloning && this.stamp(object), this.unlink(safeOldValue, object, keyStr), this.link(safeValue, object, keyStr);
|
|
439
439
|
if (this.watchers || this.listeners)
|
|
440
440
|
for (let i = 0; i < loopLen; i++) {
|
|
441
|
-
const currPath = this.config.lineageTracing ? paths[i] : fullPath,
|
|
442
|
-
this.notify(currPath, { type: "set", target, currentTarget:
|
|
441
|
+
const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
|
|
442
|
+
this.notify(currPath, { type: "set", target: target2, currentTarget: target2, root: this.core, terminated, rejectable });
|
|
443
443
|
}
|
|
444
444
|
return true;
|
|
445
445
|
},
|
|
@@ -453,7 +453,7 @@ var sia = (() => {
|
|
|
453
453
|
for (let i = 0; i < loopLen; i++) {
|
|
454
454
|
const currPath = this.config.lineageTracing ? paths[i] : fullPath, cords = this.deleters.get(currPath);
|
|
455
455
|
if (!cords && !wildcords) continue;
|
|
456
|
-
const
|
|
456
|
+
const target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver }, payload = { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable };
|
|
457
457
|
if (cords) {
|
|
458
458
|
const result2 = this.mediate(currPath, payload, "delete", cords);
|
|
459
459
|
if (!(terminated ||= payload.terminated)) value = result2;
|
|
@@ -469,8 +469,8 @@ var sia = (() => {
|
|
|
469
469
|
if (this.config.referenceTracking) this.config.smartCloning && this.stamp(object), this.unlink(oldValue?.[RAW] || oldValue, object, keyStr);
|
|
470
470
|
if (this.watchers || this.listeners)
|
|
471
471
|
for (let i = 0; i < loopLen; i++) {
|
|
472
|
-
const currPath = this.config.lineageTracing ? paths[i] : fullPath,
|
|
473
|
-
this.notify(currPath, { type: "delete", target, currentTarget:
|
|
472
|
+
const currPath = this.config.lineageTracing ? paths[i] : fullPath, target2 = { path: currPath, value, oldValue, key: keyStr, hadKey, object: receiver };
|
|
473
|
+
this.notify(currPath, { type: "delete", target: target2, currentTarget: target2, root: this.core, rejectable });
|
|
474
474
|
}
|
|
475
475
|
return true;
|
|
476
476
|
},
|
|
@@ -496,7 +496,7 @@ var sia = (() => {
|
|
|
496
496
|
return ownKeys;
|
|
497
497
|
}
|
|
498
498
|
});
|
|
499
|
-
return this.proxyCache.set(
|
|
499
|
+
return this.proxyCache.set(target, proxy), proxy;
|
|
500
500
|
}
|
|
501
501
|
trace(target, path, paths = [], seen = /* @__PURE__ */ new WeakSet()) {
|
|
502
502
|
if (Object.is(target, this.core[RAW] || this.core)) return paths.push(path), paths;
|
|
@@ -649,15 +649,14 @@ var sia = (() => {
|
|
|
649
649
|
if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
|
|
650
650
|
return cord.sclup = !sig || sig.aborted ? NOOP : () => sig.removeEventListener("abort", cord.clup), cord.clup;
|
|
651
651
|
}
|
|
652
|
-
cloned(
|
|
653
|
-
if (!
|
|
654
|
-
obj =
|
|
655
|
-
const cloned = seen.get(obj);
|
|
652
|
+
cloned(target, raw, seen = /* @__PURE__ */ new WeakMap()) {
|
|
653
|
+
if (!target || "object" !== typeof target) return target;
|
|
654
|
+
const obj = target[RAW] || target, cloned = seen.get(obj);
|
|
656
655
|
if (cloned) return cloned;
|
|
657
656
|
if (!canHandle(obj, this.config, false)) return obj;
|
|
658
657
|
const version = obj[VERSION] || 0, cached = !raw && this.config.smartCloning && (this.snapCache ??= /* @__PURE__ */ new WeakMap()).get(obj);
|
|
659
658
|
if (cached && obj[SSVERSION] === version) return cached;
|
|
660
|
-
const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj
|
|
659
|
+
const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
|
|
661
660
|
seen.set(obj, clone);
|
|
662
661
|
const keys2 = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
|
|
663
662
|
for (let i = 0, len = keys2.length; i < len; i++) clone[keys2[i]] = this.cloned(obj[keys2[i]], raw, seen);
|
|
@@ -841,19 +840,8 @@ var sia = (() => {
|
|
|
841
840
|
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;
|
|
842
841
|
return false;
|
|
843
842
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
* You could alternatively use or serialize your proxied state "as is" except the environment demands no proxies or new references.
|
|
847
|
-
* @param raw Use raw (deep unproxied & uncloned) version of branch.
|
|
848
|
-
* @param branch Branch to clone.
|
|
849
|
-
* @returns Snapshot deep or smart (structurally shared) clone.
|
|
850
|
-
* @example
|
|
851
|
-
* const snap = rtr.snapshot();
|
|
852
|
-
* @example
|
|
853
|
-
* const snap = rtr.snapshot(false, rtr.core.history.past);
|
|
854
|
-
*/
|
|
855
|
-
snapshot(raw = !this.config.smartCloning, branch = this.core) {
|
|
856
|
-
return this.cloned(branch, raw);
|
|
843
|
+
snapshot(raw = !this.config.smartCloning, branch) {
|
|
844
|
+
return this.cloned(arguments.length < 2 ? this.core : branch, raw);
|
|
857
845
|
}
|
|
858
846
|
/**
|
|
859
847
|
* Cascades object updates into direct child paths.
|
|
@@ -1379,9 +1367,10 @@ var sia = (() => {
|
|
|
1379
1367
|
// src/ts/plugins/timeTravel.ts
|
|
1380
1368
|
var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
1381
1369
|
static plugName = "timeTravel";
|
|
1370
|
+
lastTimestamp = 0;
|
|
1382
1371
|
playbackTimeoutId = -1;
|
|
1383
1372
|
constructor(config, rtr) {
|
|
1384
|
-
super({ ...TIME_TRAVEL_PLUGIN_BUILD, ...config }, rtr, {
|
|
1373
|
+
super({ ...TIME_TRAVEL_PLUGIN_BUILD, ...config }, rtr, { initialState: null, history: [], currentFrame: 0, paused: true });
|
|
1385
1374
|
}
|
|
1386
1375
|
// ===========================================================================
|
|
1387
1376
|
// THE FOUNDATION & WIRETAP (Passive Recording)
|
|
@@ -1389,6 +1378,7 @@ var sia = (() => {
|
|
|
1389
1378
|
wire() {
|
|
1390
1379
|
this.rtr.config.referenceTracking = this.rtr.config.smartCloning = this.rtr.config.eventTimeStamps = true;
|
|
1391
1380
|
if (!this.state.history.length || this.state.initialState == null) this.state.initialState = this.rtr.snapshot();
|
|
1381
|
+
this.lastTimestamp = performance.now();
|
|
1392
1382
|
this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
|
|
1393
1383
|
this.config.on("paths", this.handlePathsState, { signal: this.signal, immediate: true });
|
|
1394
1384
|
!this.state.paused && this.play();
|
|
@@ -1402,8 +1392,9 @@ var sia = (() => {
|
|
|
1402
1392
|
if (!this.state.paused) return;
|
|
1403
1393
|
if (this.state.currentFrame < this.state.history.length) this.state.history = this.state.history.slice(0, this.state.currentFrame);
|
|
1404
1394
|
if (this.state.history.length >= this.config.maxHistoryLength) this.state.history = this.state.history.slice(1);
|
|
1405
|
-
this.state.history.push({
|
|
1395
|
+
this.state.history.push({ path: e.target.path, value: this.rtr.snapshot(false, e.target.value), oldValue: this.rtr.snapshot(false, e.target.oldValue), type: e.staticType, rejected: e.rejected, timedelta: e.timestamp - this.lastTimestamp, hadKey: e.target.hadKey });
|
|
1406
1396
|
this.state.currentFrame = this.state.history.length;
|
|
1397
|
+
this.lastTimestamp = e.timestamp;
|
|
1407
1398
|
}
|
|
1408
1399
|
/** Clears timeline history and resets playhead/genesis to the current reactor state. */
|
|
1409
1400
|
clear() {
|
|
@@ -1411,6 +1402,7 @@ var sia = (() => {
|
|
|
1411
1402
|
this.playbackTimeoutId = -1;
|
|
1412
1403
|
this.state.history.length = this.state.currentFrame = 0;
|
|
1413
1404
|
this.state.initialState = this.rtr.snapshot();
|
|
1405
|
+
this.lastTimestamp = performance.now();
|
|
1414
1406
|
}
|
|
1415
1407
|
// ===========================================================================
|
|
1416
1408
|
// THE TIME MACHINE (Manual Controls)
|
|
@@ -1446,9 +1438,9 @@ var sia = (() => {
|
|
|
1446
1438
|
async automove(forward = true) {
|
|
1447
1439
|
this.state.paused = false;
|
|
1448
1440
|
while ((forward ? this.state.currentFrame < this.state.history.length : this.state.currentFrame > 0) && !this.state.paused) {
|
|
1449
|
-
const currIndex = forward ? this.state.currentFrame : this.state.currentFrame - 1, e = this.state.history[
|
|
1441
|
+
const currIndex = forward ? this.state.currentFrame : this.state.currentFrame - 1, e = this.state.history[forward ? currIndex + 1 : currIndex - 1];
|
|
1450
1442
|
this.jumpTo(this.state.currentFrame + (forward ? 1 : -1), true);
|
|
1451
|
-
if (
|
|
1443
|
+
if (e?.timedelta > 0) await new Promise((res) => this.playbackTimeoutId = setTimeout2(() => res(0), Math.min(e.timedelta, this.config.maxPlaybackDelay), this.signal));
|
|
1452
1444
|
}
|
|
1453
1445
|
this.state.paused = true;
|
|
1454
1446
|
}
|
|
@@ -1462,21 +1454,24 @@ var sia = (() => {
|
|
|
1462
1454
|
// TELEMETRY & I/O (Session Import/Export)
|
|
1463
1455
|
// ===========================================================================
|
|
1464
1456
|
/** Exports the current session as a JSON string. */
|
|
1465
|
-
export() {
|
|
1466
|
-
|
|
1457
|
+
export(replacer, space) {
|
|
1458
|
+
try {
|
|
1459
|
+
return JSON.stringify(this.state, replacer, space);
|
|
1460
|
+
} catch (e) {
|
|
1461
|
+
return this.rtr.log(`[Reactor ${this.name} Plug] Failed to export session`), "";
|
|
1462
|
+
}
|
|
1467
1463
|
}
|
|
1468
1464
|
/** Imports a session from a JSON string, allowing you to replay or analyze past states. */
|
|
1469
1465
|
import(json) {
|
|
1470
1466
|
try {
|
|
1471
1467
|
setAny(this.state, "*", JSON.parse(json));
|
|
1468
|
+
this.lastTimestamp = performance.now();
|
|
1472
1469
|
const resume = !this.state.paused, target = this.state.currentFrame;
|
|
1473
1470
|
this.state.paused = false;
|
|
1474
|
-
setAny(this.rtr.core, "*", deepClone(this.state.initialState, this.rtr.config));
|
|
1475
|
-
this.
|
|
1476
|
-
this.state.currentFrame = 0;
|
|
1477
|
-
this.jumpTo(target), resume && this.play();
|
|
1471
|
+
setAny(this.rtr.core, "*", deepClone(this.state.initialState, this.rtr.config)), this.rtr.tick();
|
|
1472
|
+
this.state.currentFrame = 0, this.jumpTo(target), resume && this.play();
|
|
1478
1473
|
} catch (e) {
|
|
1479
|
-
this.rtr.log(`[Reactor ${this.name} Plug] Failed to load session
|
|
1474
|
+
this.rtr.log(`[Reactor ${this.name} Plug] Failed to load session`);
|
|
1480
1475
|
}
|
|
1481
1476
|
}
|
|
1482
1477
|
};
|
|
@@ -1663,41 +1658,16 @@ var sia = (() => {
|
|
|
1663
1658
|
*/
|
|
1664
1659
|
constructor(time, build = {}) {
|
|
1665
1660
|
this.time = time;
|
|
1666
|
-
this.config = reactive({
|
|
1661
|
+
this.config = reactive({ title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
|
|
1667
1662
|
this.state.open = !!this.config.startOpen;
|
|
1668
|
-
const host = createEl("div", { className: "tt-overlay-host" });
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
const title = createEl("div", { className: "title" });
|
|
1672
|
-
const frame = createEl("span", { className: "muted" });
|
|
1673
|
-
const history = createEl("span", { className: "muted" });
|
|
1674
|
-
const paused = createEl("span", { className: "muted" });
|
|
1675
|
-
const clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") });
|
|
1676
|
-
const undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo });
|
|
1677
|
-
const redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo });
|
|
1678
|
-
const genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis, false), onclick: () => this.time.jumpTo(0) });
|
|
1679
|
-
const playPause = createEl("button", { onclick: () => this.time[this.time.state.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause, false) });
|
|
1680
|
-
const rewind = createEl("button", { textContent: `Rewind${formatKeyForDisplay(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind, false), onclick: this.time.rewind });
|
|
1681
|
-
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)) });
|
|
1682
|
-
const exp = createEl("button", { textContent: `Export${formatKeyForDisplay(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export, false), onclick: () => this.state.import = this.time.export() });
|
|
1683
|
-
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) });
|
|
1684
|
-
const clr = createEl("button", { textContent: `Clear${formatKeyForDisplay(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear, false), onclick: () => this.state.import = "" });
|
|
1685
|
-
const io = createEl("textarea", { className: "tt-io", placeholder: "timeline payload json", oninput: () => this.state.import = io.value });
|
|
1686
|
-
const foot = createEl("p", { className: "tt-footnote", textContent: "Want this in your app? " });
|
|
1687
|
-
const link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" });
|
|
1688
|
-
const box = createEl("div", { className: "tt-status-box" });
|
|
1689
|
-
const status = createEl("div", { className: "tt-status-row" });
|
|
1690
|
-
const row1 = createEl("div", { className: "tt-row" });
|
|
1691
|
-
const row2 = createEl("div", { className: "tt-row" });
|
|
1692
|
-
const row3 = createEl("div", { className: "tt-row" });
|
|
1693
|
-
status.append((box.append(frame, history, paused), box), clrHistory);
|
|
1694
|
-
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));
|
|
1663
|
+
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" });
|
|
1664
|
+
status.append((box.append(frame), box), clrHistory);
|
|
1665
|
+
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));
|
|
1695
1666
|
host.append(toggle, panel);
|
|
1696
|
-
this.els = { host, toggle, panel, title, frame,
|
|
1667
|
+
this.els = { host, toggle, panel, title, frame, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, payload, io };
|
|
1697
1668
|
this.keyup = (e) => {
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
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 = "");
|
|
1669
|
+
const a = this.state.open && keyEventAllowed(e, keys);
|
|
1670
|
+
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 = "");
|
|
1701
1671
|
};
|
|
1702
1672
|
window.addEventListener("keydown", this.keyup);
|
|
1703
1673
|
const sync = [
|
|
@@ -1707,26 +1677,19 @@ var sia = (() => {
|
|
|
1707
1677
|
const dock = getDock(this.config.container);
|
|
1708
1678
|
if (host.parentNode !== dock) dock.appendChild(host);
|
|
1709
1679
|
}),
|
|
1710
|
-
effect(() =>
|
|
1680
|
+
effect(() => toggle.textContent = `${(panel.hidden = !this.state.open) ? "Show" : "Hide"} ${title.textContent = this.config.title ?? ""}`),
|
|
1681
|
+
effect(() => playPause.textContent = `${s.paused ? "Play" : "Pause"}${formatKeyForDisplay(keys.shortcuts.playPause)}`),
|
|
1711
1682
|
effect(() => {
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1683
|
+
frame.textContent = `Frame: ${s.currentFrame} / ${s.history.length}`;
|
|
1684
|
+
range.disabled = clrHistory.disabled = !s.history.length;
|
|
1685
|
+
genesis.disabled = rewind.disabled = undo.disabled = !s.currentFrame;
|
|
1686
|
+
playPause.disabled = redo.disabled = s.currentFrame >= s.history.length;
|
|
1687
|
+
range.max = String(s.history.length);
|
|
1688
|
+
range.value = String(Math.min(s.currentFrame, s.history.length));
|
|
1689
|
+
payload.value = JSON.stringify(s.currentFrame ? s.history[s.currentFrame - 1] : { type: "genesis", value: s.initialState }, null, 2);
|
|
1717
1690
|
}),
|
|
1718
1691
|
effect(() => {
|
|
1719
|
-
|
|
1720
|
-
undo.disabled = this.time.state.currentFrame <= 0;
|
|
1721
|
-
redo.disabled = this.time.state.currentFrame >= this.time.state.history.length;
|
|
1722
|
-
genesis.disabled = this.time.state.currentFrame <= 0;
|
|
1723
|
-
playPause.disabled = this.time.state.currentFrame === this.time.state.history.length;
|
|
1724
|
-
rewind.disabled = !this.time.state.currentFrame;
|
|
1725
|
-
range.max = String(this.time.state.history.length);
|
|
1726
|
-
range.value = String(Math.min(this.time.state.currentFrame, this.time.state.history.length));
|
|
1727
|
-
range.disabled = !this.time.state.history.length;
|
|
1728
|
-
imp.disabled = !this.state.import.trim().length;
|
|
1729
|
-
clr.disabled = !this.state.import.trim().length;
|
|
1692
|
+
clr.disabled = imp.disabled = !this.state.import.trim().length;
|
|
1730
1693
|
io.value !== this.state.import && (io.value = this.state.import);
|
|
1731
1694
|
})
|
|
1732
1695
|
];
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { a as REvent, P as Paths, B as BaseReactorPlugin, b as Reactor } from './index-
|
|
1
|
+
import { a as REvent, P as Paths, B as BaseReactorPlugin, b as Reactor } from './index-Oie9hhE8.js';
|
|
2
2
|
|
|
3
3
|
/** The DNA of a specific moment in time, Records the 'Desire' (Intent) or the 'Fact' (State). */
|
|
4
4
|
interface HistoryEntry {
|
|
5
|
-
/** Was it a 'set' or a 'delete' surgery? */
|
|
6
|
-
type: REvent<any, any>["staticType"];
|
|
7
5
|
/** The surgical address in the Reactor */
|
|
8
6
|
path: string;
|
|
9
7
|
/** The data payload at that moment */
|
|
10
8
|
value: any;
|
|
11
9
|
/** The "Undo" antidote (Previous value), if applicable */
|
|
12
10
|
oldValue: any;
|
|
13
|
-
/**
|
|
14
|
-
|
|
11
|
+
/** Was it a 'set' or a 'delete' surgery? */
|
|
12
|
+
type: REvent<any, any>["staticType"];
|
|
15
13
|
/** Did the Power Line disapprove?; why? */
|
|
16
14
|
rejected: string;
|
|
17
15
|
/** For chronological re-enactment */
|
|
18
|
-
|
|
16
|
+
timedelta: number;
|
|
17
|
+
/** Did the key for the value exist on its parent object? */
|
|
18
|
+
hadKey: boolean;
|
|
19
19
|
}
|
|
20
20
|
interface TimeTravelConfig<T extends object = any> {
|
|
21
21
|
/** Specific paths only, no "*"; instead don't pass anything */
|
|
@@ -26,10 +26,10 @@ interface TimeTravelConfig<T extends object = any> {
|
|
|
26
26
|
maxPlaybackDelay: number;
|
|
27
27
|
}
|
|
28
28
|
interface TimeTravelState {
|
|
29
|
-
/** The "Timeline" of mutations (Chronological Log) */
|
|
30
|
-
history: HistoryEntry[];
|
|
31
29
|
/** The "Genesis" snapshot (Raw Data) */
|
|
32
30
|
initialState: any;
|
|
31
|
+
/** The "Timeline" of mutations (Chronological Log) */
|
|
32
|
+
history: HistoryEntry[];
|
|
33
33
|
/** The manual playhead (Index in the Timeline) */
|
|
34
34
|
currentFrame: number;
|
|
35
35
|
/** Whether playback is currently paused (Automatic Replay) */
|
|
@@ -41,6 +41,7 @@ interface TimeTravelState {
|
|
|
41
41
|
*/
|
|
42
42
|
declare class TimeTravelPlugin<T extends object = any> extends BaseReactorPlugin<T, TimeTravelConfig<T>, TimeTravelState> {
|
|
43
43
|
static readonly plugName = "timeTravel";
|
|
44
|
+
protected lastTimestamp: number;
|
|
44
45
|
protected playbackTimeoutId: number;
|
|
45
46
|
constructor(config?: Partial<TimeTravelConfig<T>>, rtr?: Reactor<T>);
|
|
46
47
|
wire(): void;
|
|
@@ -66,7 +67,7 @@ declare class TimeTravelPlugin<T extends object = any> extends BaseReactorPlugin
|
|
|
66
67
|
/** Pauses the live VCR playback. */
|
|
67
68
|
pause: () => void;
|
|
68
69
|
/** Exports the current session as a JSON string. */
|
|
69
|
-
export(): string;
|
|
70
|
+
export(replacer?: ((this: any, key: string, value: any) => any) | (number | string)[] | null, space?: string | number): string;
|
|
70
71
|
/** Imports a session from a JSON string, allowing you to replay or analyze past states. */
|
|
71
72
|
import(json: string): void;
|
|
72
73
|
}
|