stable
This commit is contained in:
parent
86226ccc94
commit
077d4ab9ac
71 changed files with 2905 additions and 1063 deletions
91
js/history_system.js
Normal file
91
js/history_system.js
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
"use strict";
|
||||
|
||||
// User-facing undo/redo for direct editing actions.
|
||||
(function (global) {
|
||||
const MAX_HISTORY = 48;
|
||||
|
||||
function cloneSnapshot(snapshot) {
|
||||
if (!snapshot) return null;
|
||||
if (typeof structuredClone === "function") return structuredClone(snapshot);
|
||||
return JSON.parse(JSON.stringify(snapshot));
|
||||
}
|
||||
|
||||
function ensure(worldRef) {
|
||||
if (!worldRef) return null;
|
||||
if (!Array.isArray(worldRef._undoStack)) worldRef._undoStack = [];
|
||||
if (!Array.isArray(worldRef._redoStack)) worldRef._redoStack = [];
|
||||
return worldRef;
|
||||
}
|
||||
|
||||
function snapshotKey(snapshot) {
|
||||
try {
|
||||
const clone = cloneSnapshot(snapshot);
|
||||
if (clone) clone.c = 0;
|
||||
return JSON.stringify(clone);
|
||||
} catch (_) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function capture(worldRef = global.world, label = "action") {
|
||||
const world = ensure(worldRef);
|
||||
if (!world || world._historyRestoring) return false;
|
||||
const snap = global.TarinaiSnapshot?.createSnapshot?.(world);
|
||||
if (!snap) return false;
|
||||
const key = snapshotKey(snap);
|
||||
if (key && key === world._lastUndoSnapshotKey) return false;
|
||||
world._undoStack.push({ snapshot: cloneSnapshot(snap), key, label: String(label || "action"), time: world.time || 0 });
|
||||
if (world._undoStack.length > MAX_HISTORY) world._undoStack.splice(0, world._undoStack.length - MAX_HISTORY);
|
||||
world._redoStack.length = 0;
|
||||
world._lastUndoSnapshotKey = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
function restore(worldRef, entry, source) {
|
||||
if (!worldRef || !entry?.snapshot || !global.TarinaiRestoreCoordinator?.restoreSnapshot) return false;
|
||||
worldRef._historyRestoring = true;
|
||||
try {
|
||||
global.TarinaiRestoreCoordinator.restoreSnapshot(cloneSnapshot(entry.snapshot), worldRef, { source, syncUi: true });
|
||||
worldRef._lastUndoSnapshotKey = snapshotKey(global.TarinaiSnapshot?.createSnapshot?.(worldRef));
|
||||
return true;
|
||||
} finally {
|
||||
worldRef._historyRestoring = false;
|
||||
}
|
||||
}
|
||||
|
||||
function undo(worldRef = global.world) {
|
||||
const world = ensure(worldRef);
|
||||
if (!world || !world._undoStack.length) return { ok: false, reason: "empty" };
|
||||
const current = global.TarinaiSnapshot?.createSnapshot?.(world);
|
||||
const prev = world._undoStack.pop();
|
||||
if (current) world._redoStack.push({ snapshot: cloneSnapshot(current), key: snapshotKey(current), label: "redo", time: world.time || 0 });
|
||||
if (world._redoStack.length > MAX_HISTORY) world._redoStack.splice(0, world._redoStack.length - MAX_HISTORY);
|
||||
const ok = restore(world, prev, "undo");
|
||||
return { ok, label: prev.label || "" };
|
||||
}
|
||||
|
||||
function redo(worldRef = global.world) {
|
||||
const world = ensure(worldRef);
|
||||
if (!world || !world._redoStack.length) return { ok: false, reason: "empty" };
|
||||
const current = global.TarinaiSnapshot?.createSnapshot?.(world);
|
||||
const next = world._redoStack.pop();
|
||||
if (current) world._undoStack.push({ snapshot: cloneSnapshot(current), key: snapshotKey(current), label: "undo", time: world.time || 0 });
|
||||
if (world._undoStack.length > MAX_HISTORY) world._undoStack.splice(0, world._undoStack.length - MAX_HISTORY);
|
||||
const ok = restore(world, next, "redo");
|
||||
return { ok, label: next.label || "" };
|
||||
}
|
||||
|
||||
function clear(worldRef = global.world) {
|
||||
const world = ensure(worldRef);
|
||||
if (!world) return false;
|
||||
world._undoStack.length = 0;
|
||||
world._redoStack.length = 0;
|
||||
world._lastUndoSnapshotKey = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
function canUndo(worldRef = global.world) { return !!worldRef?._undoStack?.length; }
|
||||
function canRedo(worldRef = global.world) { return !!worldRef?._redoStack?.length; }
|
||||
|
||||
global.TarinaiHistory = Object.freeze({ capture, undo, redo, clear, canUndo, canRedo });
|
||||
})(typeof window !== "undefined" ? window : globalThis);
|
||||
Loading…
Add table
Add a link
Reference in a new issue