bedee3ba8d
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
66 lines
2.0 KiB
TypeScript
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 };
|
|
}
|