Files
omniai-web/src/features/canvas/useCanvasHistory.ts
T
2026-06-02 12:38:01 +08:00

66 lines
2.0 KiB
TypeScript

import { useCallback, useRef } from "react";
export interface CanvasHistorySnapshot {
textNodes: unknown[];
imageNodes: unknown[];
videoNodes: unknown[];
manualLinks: unknown[];
nodePackages: unknown[];
}
interface CanvasHistoryState {
past: CanvasHistorySnapshot[];
future: CanvasHistorySnapshot[];
}
const MAX_HISTORY = 50;
export function useCanvasHistory() {
const historyRef = useRef<CanvasHistoryState>({ past: [], future: [] });
const lastPushRef = useRef<number>(0);
const pushSnapshot = useCallback((snapshot: CanvasHistorySnapshot) => {
const now = Date.now();
// Debounce: skip if pushed within 300ms (e.g. rapid drag moves)
if (now - lastPushRef.current < 300) return;
lastPushRef.current = now;
const history = historyRef.current;
history.past = [...history.past.slice(-(MAX_HISTORY - 1)), snapshot];
history.future = [];
}, []);
const undo = useCallback(
(currentSnapshot: CanvasHistorySnapshot): CanvasHistorySnapshot | null => {
const history = historyRef.current;
if (!history.past.length) return null;
const previous = history.past[history.past.length - 1];
history.past = history.past.slice(0, -1);
history.future = [...history.future, currentSnapshot];
return previous;
},
[]
);
const redo = useCallback(
(currentSnapshot: CanvasHistorySnapshot): CanvasHistorySnapshot | null => {
const history = historyRef.current;
if (!history.future.length) return null;
const next = history.future[history.future.length - 1];
history.future = history.future.slice(0, -1);
history.past = [...history.past, currentSnapshot];
return next;
},
[]
);
const canUndo = useCallback(() => historyRef.current.past.length > 0, []);
const canRedo = useCallback(() => historyRef.current.future.length > 0, []);
const clear = useCallback(() => {
historyRef.current = { past: [], future: [] };
}, []);
return { pushSnapshot, undo, redo, canUndo, canRedo, clear };
}