jotai-state-tree 1.4.2 → 1.4.4

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.
@@ -128,6 +128,41 @@ var StateTreeNode = class {
128
128
  this.$parent = parent ?? null;
129
129
  this.$path = parent ? `${parent.$path}/${pathSegment}` : "";
130
130
  this.valueAtom = atom(initialValue);
131
+ this.isAliveAtom = atom(true);
132
+ this.snapshotAtom = atom((get) => {
133
+ const value = get(this.valueAtom);
134
+ if (this.$type._kind === "model") {
135
+ const res = {};
136
+ for (const [key, childNode] of this.children.entries()) {
137
+ res[key] = get(childNode.snapshotAtom);
138
+ }
139
+ if (value && typeof value === "object") {
140
+ for (const key of Object.keys(value)) {
141
+ if (!this.children.has(key)) {
142
+ res[key] = value[key];
143
+ }
144
+ }
145
+ }
146
+ return this.postProcessor ? this.postProcessor(res) : res;
147
+ }
148
+ if (this.$type._kind === "array") {
149
+ const res = [];
150
+ const childrenKeys = Array.from(this.children.keys()).sort((a, b) => Number(a) - Number(b));
151
+ for (const key of childrenKeys) {
152
+ const childNode = this.children.get(key);
153
+ res.push(get(childNode.snapshotAtom));
154
+ }
155
+ return res;
156
+ }
157
+ if (this.$type._kind === "map") {
158
+ const res = {};
159
+ for (const [key, childNode] of this.children.entries()) {
160
+ res[key] = get(childNode.snapshotAtom);
161
+ }
162
+ return res;
163
+ }
164
+ return value;
165
+ });
131
166
  nodeRegistry.set(this.$id, { node: new WeakRef(this), instance: null });
132
167
  nodeFinalizationRegistry.register(this, this.$id, this);
133
168
  }
@@ -319,6 +354,10 @@ var StateTreeNode = class {
319
354
  this.children.clear();
320
355
  this.unregisterIdentifier();
321
356
  this.$isAlive = false;
357
+ try {
358
+ globalStore.set(this.isAliveAtom, false);
359
+ } catch (e) {
360
+ }
322
361
  notifyLifecycleChange(this, false);
323
362
  nodeRegistry.delete(this.$id);
324
363
  nodeFinalizationRegistry.unregister(this);
@@ -495,6 +534,10 @@ function clearAllRegistries() {
495
534
  const node = entry.node.deref();
496
535
  if (node) {
497
536
  node.$isAlive = false;
537
+ try {
538
+ globalStore.set(node.isAliveAtom, false);
539
+ } catch (e) {
540
+ }
498
541
  }
499
542
  }
500
543
  nodeRegistry.clear();
@@ -953,29 +996,32 @@ function unfreeze(target) {
953
996
  }
954
997
 
955
998
  // src/undo.ts
956
- var undoManagersRegistry = /* @__PURE__ */ new WeakMap();
957
- var timeTravelManagersRegistry = /* @__PURE__ */ new WeakMap();
958
- var UndoManager = class {
999
+ import { atom as atom2 } from "jotai";
1000
+ var historyTrackersRegistry = /* @__PURE__ */ new WeakMap();
1001
+ var HistoryTracker = class {
959
1002
  constructor(target, options = {}) {
960
- this.historyEntries = [];
961
- this.currentIndex = -1;
962
- this.isUndoing = false;
963
- this.isRedoing = false;
1003
+ // Transient state
1004
+ this.autoRecord = false;
1005
+ this.isApplyingHistory = false;
964
1006
  this.skipRecording = false;
965
1007
  this.grouping = false;
966
1008
  this.actionGrouping = false;
967
1009
  this.currentGroup = [];
968
1010
  this.currentGroupInverse = [];
1011
+ this.lastChangeTime = 0;
969
1012
  this.disposer = null;
970
1013
  this.actionDisposer = null;
971
- this.lastChangeTime = 0;
972
1014
  this.target = target;
973
- this.options = {
974
- maxHistoryLength: options.maxHistoryLength ?? 100,
975
- groupByTime: options.groupByTime ?? false,
976
- groupingWindow: options.groupingWindow ?? 200
977
- };
978
- undoManagersRegistry.set(target, this);
1015
+ this.maxHistoryLength = options.maxHistoryLength ?? options.maxSnapshots ?? 100;
1016
+ this.groupByTime = options.groupByTime ?? false;
1017
+ this.groupingWindow = options.groupingWindow ?? 200;
1018
+ this.autoRecord = options.autoRecord ?? false;
1019
+ const initialSnapshot = getSnapshot(target);
1020
+ this.historyAtom = atom2({
1021
+ entries: [],
1022
+ currentIndex: -1,
1023
+ initialSnapshot
1024
+ });
979
1025
  this.disposer = onPatch(target, (patch, reversePatch) => {
980
1026
  this.recordPatch(patch, reversePatch);
981
1027
  });
@@ -988,32 +1034,18 @@ var UndoManager = class {
988
1034
  }
989
1035
  });
990
1036
  }
991
- get canUndo() {
992
- return this.currentIndex >= 0;
993
- }
994
- get canRedo() {
995
- return this.currentIndex < this.historyEntries.length - 1;
996
- }
997
- get undoLevels() {
998
- return this.currentIndex + 1;
999
- }
1000
- get redoLevels() {
1001
- return this.historyEntries.length - this.currentIndex - 1;
1002
- }
1003
- get history() {
1004
- return [...this.historyEntries];
1005
- }
1006
- get historyIndex() {
1007
- return this.currentIndex;
1008
- }
1009
1037
  recordPatch(patch, reversePatch) {
1010
- if (this.isUndoing || this.isRedoing || this.skipRecording) {
1038
+ if (!this.autoRecord) {
1039
+ return;
1040
+ }
1041
+ if (this.isApplyingHistory || this.skipRecording) {
1011
1042
  return;
1012
1043
  }
1013
1044
  const node = getStateTreeNode(this.target);
1014
1045
  if (node.getRoot().$isApplyingHistory) {
1015
1046
  return;
1016
1047
  }
1048
+ const store = getGlobalStore();
1017
1049
  const now = Date.now();
1018
1050
  if (isActionRunning() && !this.grouping) {
1019
1051
  this.grouping = true;
@@ -1031,86 +1063,172 @@ var UndoManager = class {
1031
1063
  this.currentGroupInverse.push({ ...patch });
1032
1064
  return;
1033
1065
  }
1034
- if (this.options.groupByTime && this.historyEntries.length > 0 && now - this.lastChangeTime < this.options.groupingWindow && this.currentIndex === this.historyEntries.length - 1) {
1035
- const lastEntry = this.historyEntries[this.currentIndex];
1036
- lastEntry.patches.push(reversePatch);
1037
- lastEntry.inversePatches.push({ ...patch });
1038
- lastEntry.timestamp = now;
1039
- } else {
1040
- if (this.currentIndex < this.historyEntries.length - 1) {
1041
- this.historyEntries.splice(this.currentIndex + 1);
1042
- }
1043
- this.historyEntries.push({
1044
- patches: [reversePatch],
1045
- inversePatches: [{ ...patch }],
1046
- timestamp: now
1047
- });
1048
- this.currentIndex++;
1049
- if (this.historyEntries.length > this.options.maxHistoryLength) {
1050
- const excess = this.historyEntries.length - this.options.maxHistoryLength;
1051
- this.historyEntries.splice(0, excess);
1052
- this.currentIndex -= excess;
1066
+ store.set(this.historyAtom, (prev) => {
1067
+ let entries = prev.currentIndex < prev.entries.length - 1 ? prev.entries.slice(0, prev.currentIndex + 1) : [...prev.entries];
1068
+ if (this.groupByTime && entries.length > 0 && now - this.lastChangeTime < this.groupingWindow && prev.currentIndex === prev.entries.length - 1) {
1069
+ const lastEntry = { ...entries[entries.length - 1] };
1070
+ lastEntry.patches = [...lastEntry.patches, reversePatch];
1071
+ lastEntry.inversePatches = [...lastEntry.inversePatches, { ...patch }];
1072
+ lastEntry.timestamp = now;
1073
+ lastEntry.snapshot = getSnapshot(this.target);
1074
+ entries[entries.length - 1] = lastEntry;
1075
+ this.lastChangeTime = now;
1076
+ return {
1077
+ ...prev,
1078
+ entries
1079
+ };
1080
+ } else {
1081
+ const newEntry = {
1082
+ patches: [reversePatch],
1083
+ inversePatches: [{ ...patch }],
1084
+ timestamp: now,
1085
+ snapshot: getSnapshot(this.target)
1086
+ };
1087
+ entries.push(newEntry);
1088
+ let newIndex = entries.length - 1;
1089
+ if (entries.length > this.maxHistoryLength) {
1090
+ const excess = entries.length - this.maxHistoryLength;
1091
+ entries = entries.slice(excess);
1092
+ newIndex -= excess;
1093
+ }
1094
+ this.lastChangeTime = now;
1095
+ return {
1096
+ ...prev,
1097
+ entries,
1098
+ currentIndex: newIndex
1099
+ };
1053
1100
  }
1054
- }
1055
- this.lastChangeTime = now;
1101
+ });
1056
1102
  }
1057
1103
  undo() {
1058
- if (!this.canUndo) {
1104
+ const store = getGlobalStore();
1105
+ const state = store.get(this.historyAtom);
1106
+ if (state.currentIndex < 0) {
1059
1107
  return;
1060
1108
  }
1061
1109
  const node = getStateTreeNode(this.target);
1062
1110
  const rootNode = node.getRoot();
1063
1111
  const wasApplying = rootNode.$isApplyingHistory;
1064
1112
  rootNode.$isApplyingHistory = true;
1065
- this.isUndoing = true;
1113
+ this.isApplyingHistory = true;
1066
1114
  try {
1067
- const entry = this.historyEntries[this.currentIndex];
1115
+ const entry = state.entries[state.currentIndex];
1068
1116
  for (let i = entry.patches.length - 1; i >= 0; i--) {
1069
1117
  applyPatch(this.target, entry.patches[i]);
1070
1118
  }
1071
- this.currentIndex--;
1072
- const tt = timeTravelManagersRegistry.get(this.target);
1073
- if (tt) {
1074
- const N = tt.snapshots.length;
1075
- const M = this.historyEntries.length;
1076
- const ttIndex = this.currentIndex + 1 + (N - 1 - M);
1077
- tt.index = Math.max(0, Math.min(N - 1, ttIndex));
1078
- }
1119
+ store.set(this.historyAtom, (prev) => ({
1120
+ ...prev,
1121
+ currentIndex: prev.currentIndex - 1
1122
+ }));
1079
1123
  } finally {
1080
- this.isUndoing = false;
1124
+ this.isApplyingHistory = false;
1081
1125
  rootNode.$isApplyingHistory = wasApplying;
1082
1126
  }
1083
1127
  }
1084
1128
  redo() {
1085
- if (!this.canRedo) {
1129
+ const store = getGlobalStore();
1130
+ const state = store.get(this.historyAtom);
1131
+ if (state.currentIndex >= state.entries.length - 1) {
1086
1132
  return;
1087
1133
  }
1088
1134
  const node = getStateTreeNode(this.target);
1089
1135
  const rootNode = node.getRoot();
1090
1136
  const wasApplying = rootNode.$isApplyingHistory;
1091
1137
  rootNode.$isApplyingHistory = true;
1092
- this.isRedoing = true;
1138
+ this.isApplyingHistory = true;
1093
1139
  try {
1094
- this.currentIndex++;
1095
- const entry = this.historyEntries[this.currentIndex];
1140
+ const nextIndex = state.currentIndex + 1;
1141
+ const entry = state.entries[nextIndex];
1096
1142
  for (const patch of entry.inversePatches) {
1097
1143
  applyPatch(this.target, patch);
1098
1144
  }
1099
- const tt = timeTravelManagersRegistry.get(this.target);
1100
- if (tt) {
1101
- const N = tt.snapshots.length;
1102
- const M = this.historyEntries.length;
1103
- const ttIndex = this.currentIndex + 1 + (N - 1 - M);
1104
- tt.index = Math.max(0, Math.min(N - 1, ttIndex));
1105
- }
1145
+ store.set(this.historyAtom, (prev) => ({
1146
+ ...prev,
1147
+ currentIndex: nextIndex
1148
+ }));
1106
1149
  } finally {
1107
- this.isRedoing = false;
1150
+ this.isApplyingHistory = false;
1108
1151
  rootNode.$isApplyingHistory = wasApplying;
1109
1152
  }
1110
1153
  }
1154
+ goTo(index) {
1155
+ const store = getGlobalStore();
1156
+ const state = store.get(this.historyAtom);
1157
+ const maxIdx = state.entries.length;
1158
+ if (index < 0 || index > maxIdx) {
1159
+ return;
1160
+ }
1161
+ const node = getStateTreeNode(this.target);
1162
+ const rootNode = node.getRoot();
1163
+ const wasApplying = rootNode.$isApplyingHistory;
1164
+ rootNode.$isApplyingHistory = true;
1165
+ this.isApplyingHistory = true;
1166
+ try {
1167
+ const targetSnapshot = index === 0 ? state.initialSnapshot : state.entries[index - 1].snapshot;
1168
+ applySnapshot(this.target, targetSnapshot);
1169
+ store.set(this.historyAtom, (prev) => ({
1170
+ ...prev,
1171
+ currentIndex: index - 1
1172
+ }));
1173
+ } finally {
1174
+ this.isApplyingHistory = false;
1175
+ rootNode.$isApplyingHistory = wasApplying;
1176
+ }
1177
+ }
1178
+ goBack() {
1179
+ const store = getGlobalStore();
1180
+ const state = store.get(this.historyAtom);
1181
+ const currentSnapshotIndex = state.currentIndex + 1;
1182
+ if (currentSnapshotIndex > 0) {
1183
+ this.goTo(currentSnapshotIndex - 1);
1184
+ }
1185
+ }
1186
+ goForward() {
1187
+ const store = getGlobalStore();
1188
+ const state = store.get(this.historyAtom);
1189
+ const currentSnapshotIndex = state.currentIndex + 1;
1190
+ if (currentSnapshotIndex < state.entries.length) {
1191
+ this.goTo(currentSnapshotIndex + 1);
1192
+ }
1193
+ }
1194
+ record() {
1195
+ const store = getGlobalStore();
1196
+ const state = store.get(this.historyAtom);
1197
+ let entries = state.currentIndex < state.entries.length - 1 ? state.entries.slice(0, state.currentIndex + 1) : [...state.entries];
1198
+ const newEntry = {
1199
+ patches: [],
1200
+ inversePatches: [],
1201
+ timestamp: Date.now(),
1202
+ snapshot: getSnapshot(this.target)
1203
+ };
1204
+ entries.push(newEntry);
1205
+ let newIndex = entries.length - 1;
1206
+ if (entries.length > this.maxHistoryLength) {
1207
+ const excess = entries.length - this.maxHistoryLength;
1208
+ entries = entries.slice(excess);
1209
+ newIndex -= excess;
1210
+ }
1211
+ store.set(this.historyAtom, {
1212
+ ...state,
1213
+ entries,
1214
+ currentIndex: newIndex
1215
+ });
1216
+ }
1217
+ getSnapshot(index) {
1218
+ const store = getGlobalStore();
1219
+ const state = store.get(this.historyAtom);
1220
+ if (index < 0 || index > state.entries.length) {
1221
+ throw new Error(`[jotai-state-tree] Invalid snapshot index: ${index}`);
1222
+ }
1223
+ return index === 0 ? state.initialSnapshot : state.entries[index - 1].snapshot;
1224
+ }
1111
1225
  clear() {
1112
- this.historyEntries = [];
1113
- this.currentIndex = -1;
1226
+ const store = getGlobalStore();
1227
+ store.set(this.historyAtom, {
1228
+ entries: [],
1229
+ currentIndex: -1,
1230
+ initialSnapshot: getSnapshot(this.target)
1231
+ });
1114
1232
  this.currentGroup = [];
1115
1233
  this.currentGroupInverse = [];
1116
1234
  this.grouping = false;
@@ -1129,20 +1247,28 @@ var UndoManager = class {
1129
1247
  this.grouping = false;
1130
1248
  this.actionGrouping = false;
1131
1249
  if (this.currentGroup.length > 0) {
1132
- if (this.currentIndex < this.historyEntries.length - 1) {
1133
- this.historyEntries.splice(this.currentIndex + 1);
1134
- }
1135
- this.historyEntries.push({
1136
- patches: this.currentGroup,
1137
- inversePatches: this.currentGroupInverse,
1138
- timestamp: Date.now()
1250
+ const store = getGlobalStore();
1251
+ store.set(this.historyAtom, (prev) => {
1252
+ let entries = prev.currentIndex < prev.entries.length - 1 ? prev.entries.slice(0, prev.currentIndex + 1) : [...prev.entries];
1253
+ const newEntry = {
1254
+ patches: [...this.currentGroup],
1255
+ inversePatches: [...this.currentGroupInverse],
1256
+ timestamp: Date.now(),
1257
+ snapshot: getSnapshot(this.target)
1258
+ };
1259
+ entries.push(newEntry);
1260
+ let newIndex = entries.length - 1;
1261
+ if (entries.length > this.maxHistoryLength) {
1262
+ const excess = entries.length - this.maxHistoryLength;
1263
+ entries = entries.slice(excess);
1264
+ newIndex -= excess;
1265
+ }
1266
+ return {
1267
+ ...prev,
1268
+ entries,
1269
+ currentIndex: newIndex
1270
+ };
1139
1271
  });
1140
- this.currentIndex++;
1141
- if (this.historyEntries.length > this.options.maxHistoryLength) {
1142
- const excess = this.historyEntries.length - this.options.maxHistoryLength;
1143
- this.historyEntries.splice(0, excess);
1144
- this.currentIndex -= excess;
1145
- }
1146
1272
  }
1147
1273
  this.currentGroup = [];
1148
1274
  this.currentGroupInverse = [];
@@ -1156,7 +1282,7 @@ var UndoManager = class {
1156
1282
  }
1157
1283
  }
1158
1284
  dispose() {
1159
- undoManagersRegistry.delete(this.target);
1285
+ historyTrackersRegistry.delete(this.target);
1160
1286
  if (this.disposer) {
1161
1287
  this.disposer();
1162
1288
  this.disposer = null;
@@ -1165,143 +1291,113 @@ var UndoManager = class {
1165
1291
  this.actionDisposer();
1166
1292
  this.actionDisposer = null;
1167
1293
  }
1168
- this.clear();
1169
1294
  }
1170
1295
  };
1171
- function createUndoManager(target, options) {
1172
- return new UndoManager(target, options);
1173
- }
1174
- var TimeTravelManager = class {
1175
- constructor(target, options = {}) {
1176
- this.snapshots = [];
1177
- this.index = -1;
1178
- this.isApplying = false;
1179
- this.disposer = null;
1180
- this.actionDisposer = null;
1181
- this.pendingRecord = false;
1182
- this.actionGrouping = false;
1183
- this.target = target;
1184
- this.maxSnapshots = options.maxSnapshots ?? 50;
1185
- this.autoRecord = options.autoRecord ?? false;
1186
- timeTravelManagersRegistry.set(target, this);
1187
- this.record();
1188
- if (this.autoRecord) {
1189
- this.disposer = onPatch(target, () => {
1190
- if (this.isApplying) return;
1191
- const node = getStateTreeNode(this.target);
1192
- if (node.getRoot().$isApplyingHistory) {
1193
- return;
1194
- }
1195
- if (isActionRunning()) {
1196
- this.pendingRecord = true;
1197
- if (!this.actionGrouping) {
1198
- this.actionGrouping = true;
1199
- Promise.resolve().then(() => {
1200
- if (this.actionGrouping) {
1201
- this.commitPendingRecord();
1202
- }
1203
- });
1204
- }
1205
- } else {
1206
- this.record();
1207
- }
1208
- });
1209
- this.actionDisposer = onAction(target, () => {
1210
- const current = getCurrentAction();
1211
- if (current && !current.parent) {
1212
- if (this.actionGrouping) {
1213
- this.commitPendingRecord();
1214
- }
1215
- }
1216
- });
1217
- }
1218
- }
1219
- commitPendingRecord() {
1220
- this.actionGrouping = false;
1221
- if (this.pendingRecord) {
1222
- this.pendingRecord = false;
1223
- this.record();
1224
- }
1225
- }
1226
- get currentIndex() {
1227
- return this.index;
1228
- }
1229
- get snapshotCount() {
1230
- return this.snapshots.length;
1231
- }
1232
- get canGoBack() {
1233
- return this.index > 0;
1234
- }
1235
- get canGoForward() {
1236
- return this.index < this.snapshots.length - 1;
1237
- }
1238
- record() {
1239
- if (this.index < this.snapshots.length - 1) {
1240
- this.snapshots.splice(this.index + 1);
1241
- }
1242
- this.snapshots.push(getSnapshot(this.target));
1243
- this.index++;
1244
- if (this.snapshots.length > this.maxSnapshots) {
1245
- const excess = this.snapshots.length - this.maxSnapshots;
1246
- this.snapshots.splice(0, excess);
1247
- this.index -= excess;
1248
- }
1249
- }
1250
- goBack() {
1251
- if (!this.canGoBack) return;
1252
- this.goTo(this.index - 1);
1296
+ function getOrCreateHistoryTracker(target, options = {}) {
1297
+ let tracker = historyTrackersRegistry.get(target);
1298
+ if (!tracker) {
1299
+ tracker = new HistoryTracker(target, options);
1300
+ historyTrackersRegistry.set(target, tracker);
1253
1301
  }
1254
- goForward() {
1255
- if (!this.canGoForward) return;
1256
- this.goTo(this.index + 1);
1257
- }
1258
- goTo(index) {
1259
- if (index < 0 || index >= this.snapshots.length) return;
1260
- const node = getStateTreeNode(this.target);
1261
- const rootNode = node.getRoot();
1262
- const wasApplying = rootNode.$isApplyingHistory;
1263
- rootNode.$isApplyingHistory = true;
1264
- this.isApplying = true;
1265
- try {
1266
- this.index = index;
1267
- applySnapshot(this.target, this.snapshots[index]);
1268
- const undo = undoManagersRegistry.get(this.target);
1269
- if (undo) {
1270
- const N = this.snapshots.length;
1271
- const M = undo.historyEntries.length;
1272
- const undoIndex = this.index - 1 - (N - 1 - M);
1273
- undo.currentIndex = Math.max(-1, Math.min(M - 1, undoIndex));
1274
- }
1275
- } finally {
1276
- this.isApplying = false;
1277
- rootNode.$isApplyingHistory = wasApplying;
1278
- }
1279
- }
1280
- getSnapshot(index) {
1281
- if (index < 0 || index >= this.snapshots.length) {
1282
- throw new Error(`[jotai-state-tree] Invalid snapshot index: ${index}`);
1302
+ return tracker;
1303
+ }
1304
+ function createUndoManager(target, options) {
1305
+ const tracker = getOrCreateHistoryTracker(target, options);
1306
+ tracker.autoRecord = true;
1307
+ const store = getGlobalStore();
1308
+ return {
1309
+ get canUndo() {
1310
+ const state = store.get(tracker.historyAtom);
1311
+ return state.currentIndex >= 0;
1312
+ },
1313
+ get canRedo() {
1314
+ const state = store.get(tracker.historyAtom);
1315
+ return state.currentIndex < state.entries.length - 1;
1316
+ },
1317
+ get undoLevels() {
1318
+ const state = store.get(tracker.historyAtom);
1319
+ return state.currentIndex + 1;
1320
+ },
1321
+ get redoLevels() {
1322
+ const state = store.get(tracker.historyAtom);
1323
+ return state.entries.length - state.currentIndex - 1;
1324
+ },
1325
+ get history() {
1326
+ const state = store.get(tracker.historyAtom);
1327
+ return state.entries;
1328
+ },
1329
+ get historyIndex() {
1330
+ const state = store.get(tracker.historyAtom);
1331
+ return state.currentIndex;
1332
+ },
1333
+ undo() {
1334
+ tracker.undo();
1335
+ },
1336
+ redo() {
1337
+ tracker.redo();
1338
+ },
1339
+ clear() {
1340
+ tracker.clear();
1341
+ },
1342
+ startGroup() {
1343
+ tracker.startGroup();
1344
+ },
1345
+ endGroup() {
1346
+ tracker.endGroup();
1347
+ },
1348
+ withoutUndo(fn) {
1349
+ return tracker.withoutUndo(fn);
1350
+ },
1351
+ dispose() {
1352
+ tracker.dispose();
1283
1353
  }
1284
- return this.snapshots[index];
1285
- }
1286
- clear() {
1287
- this.snapshots = [];
1288
- this.index = -1;
1289
- this.record();
1354
+ };
1355
+ }
1356
+ function createTimeTravelManager(target, options) {
1357
+ const tracker = getOrCreateHistoryTracker(target, options);
1358
+ if (options?.autoRecord) {
1359
+ tracker.autoRecord = true;
1290
1360
  }
1291
- dispose() {
1292
- timeTravelManagersRegistry.delete(this.target);
1293
- if (this.disposer) {
1294
- this.disposer();
1295
- this.disposer = null;
1296
- }
1297
- if (this.actionDisposer) {
1298
- this.actionDisposer();
1299
- this.actionDisposer = null;
1361
+ const store = getGlobalStore();
1362
+ return {
1363
+ get currentIndex() {
1364
+ const state = store.get(tracker.historyAtom);
1365
+ return state.currentIndex + 1;
1366
+ },
1367
+ get snapshotCount() {
1368
+ const state = store.get(tracker.historyAtom);
1369
+ return state.entries.length + 1;
1370
+ },
1371
+ get canGoBack() {
1372
+ const state = store.get(tracker.historyAtom);
1373
+ return state.currentIndex + 1 > 0;
1374
+ },
1375
+ get canGoForward() {
1376
+ const state = store.get(tracker.historyAtom);
1377
+ return state.currentIndex + 1 < state.entries.length + 1 - 1;
1378
+ },
1379
+ record() {
1380
+ tracker.record();
1381
+ },
1382
+ goBack() {
1383
+ tracker.goBack();
1384
+ },
1385
+ goForward() {
1386
+ tracker.goForward();
1387
+ },
1388
+ goTo(index) {
1389
+ tracker.goTo(index);
1390
+ },
1391
+ getSnapshot(index) {
1392
+ return tracker.getSnapshot(index);
1393
+ },
1394
+ clear() {
1395
+ tracker.clear();
1396
+ },
1397
+ dispose() {
1398
+ tracker.dispose();
1300
1399
  }
1301
- }
1302
- };
1303
- function createTimeTravelManager(target, options) {
1304
- return new TimeTravelManager(target, options);
1400
+ };
1305
1401
  }
1306
1402
  var ActionRecorder = class {
1307
1403
  constructor(target) {
@@ -1443,6 +1539,7 @@ export {
1443
1539
  freeze,
1444
1540
  isFrozen,
1445
1541
  unfreeze,
1542
+ getOrCreateHistoryTracker,
1446
1543
  createUndoManager,
1447
1544
  createTimeTravelManager,
1448
1545
  createActionRecorder
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-DL1pyOkT.mjs';
2
- export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-DL1pyOkT.mjs';
1
+ import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-BSq-Pomp.mjs';
2
+ export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-BSq-Pomp.mjs';
3
3
  import 'jotai/vanilla/internals';
4
4
  import 'jotai';
5
5