import { useCallback, useRef, useState } from 'react'; export type PageDraftSnapshot = { title: string; summary: string; content: string; }; const MAX_HISTORY = 50; const HISTORY_DEBOUNCE_MS = 600; export function usePageDraftHistory(initial: PageDraftSnapshot) { const [snapshot, setSnapshot] = useState(initial); const stableRef = useRef(initial); const pastRef = useRef([]); const futureRef = useRef([]); const debounceRef = useRef(null); const [historyRevision, setHistoryRevision] = useState(0); const [canUndo, setCanUndo] = useState(false); const [canRedo, setCanRedo] = useState(false); const bump = () => { setCanUndo(pastRef.current.length > 0); setCanRedo(futureRef.current.length > 0); setHistoryRevision((value) => value + 1); }; const flushDebounced = useCallback(() => { if (debounceRef.current != null) { window.clearTimeout(debounceRef.current); debounceRef.current = null; } }, []); const recordStable = useCallback((next: PageDraftSnapshot) => { if (JSON.stringify(stableRef.current) === JSON.stringify(next)) return; pastRef.current = [...pastRef.current.slice(-(MAX_HISTORY - 1)), stableRef.current]; futureRef.current = []; stableRef.current = next; bump(); }, []); const update = useCallback( (next: PageDraftSnapshot) => { setSnapshot(next); bump(); flushDebounced(); debounceRef.current = window.setTimeout(() => { debounceRef.current = null; recordStable(next); }, HISTORY_DEBOUNCE_MS); }, [flushDebounced, recordStable], ); const replace = useCallback( (next: PageDraftSnapshot, options?: { recordHistory?: boolean }) => { flushDebounced(); if (options?.recordHistory) { pastRef.current = [...pastRef.current.slice(-(MAX_HISTORY - 1)), snapshot]; futureRef.current = []; } stableRef.current = next; setSnapshot(next); bump(); }, [flushDebounced, snapshot], ); const undo = useCallback((): PageDraftSnapshot | null => { flushDebounced(); const previous = pastRef.current[pastRef.current.length - 1]; if (!previous) return null; pastRef.current = pastRef.current.slice(0, -1); futureRef.current = [snapshot, ...futureRef.current]; stableRef.current = previous; setSnapshot(previous); bump(); return previous; }, [flushDebounced, snapshot]); const redo = useCallback((): PageDraftSnapshot | null => { flushDebounced(); const next = futureRef.current[0]; if (!next) return null; futureRef.current = futureRef.current.slice(1); pastRef.current = [...pastRef.current, snapshot]; stableRef.current = next; setSnapshot(next); bump(); return next; }, [flushDebounced, snapshot]); const reset = useCallback( (next: PageDraftSnapshot) => { flushDebounced(); pastRef.current = []; futureRef.current = []; stableRef.current = next; setSnapshot(next); bump(); }, [flushDebounced], ); return { snapshot, update, replace, undo, redo, reset, canUndo, canRedo, historyRevision, }; }