| 1 | "use client"; |
| 2 | |
| 3 | import { useEffect, useRef } from "react"; |
| 4 | |
| 5 | import { usePresentationHistoryState } from "@/states/presentation-history-state"; |
| 6 | import { usePresentationState } from "@/states/presentation-state"; |
| 7 | |
| 8 | /** |
| 9 | * Simplified history hook - only handles keyboard shortcuts and initialization. |
| 10 | * History pushing is now done directly in setSlides/updateSlide. |
| 11 | */ |
| 12 | export function usePresentationHistory() { |
| 13 | const slides = usePresentationState((s) => s.slides); |
| 14 | const theme = usePresentationState((s) => s.theme); |
| 15 | const customThemeData = usePresentationState((s) => s.customThemeData); |
| 16 | const isGeneratingPresentation = usePresentationState( |
| 17 | (s) => s.isGeneratingPresentation, |
| 18 | ); |
| 19 | |
| 20 | const undo = usePresentationHistoryState((s) => s.undo); |
| 21 | const redo = usePresentationHistoryState((s) => s.redo); |
| 22 | const canUndo = usePresentationHistoryState((s) => s.canUndo); |
| 23 | const canRedo = usePresentationHistoryState((s) => s.canRedo); |
| 24 | const initializeHistory = usePresentationHistoryState( |
| 25 | (s) => s.initializeHistory, |
| 26 | ); |
| 27 | const clearHistory = usePresentationHistoryState((s) => s.clearHistory); |
| 28 | const history = usePresentationHistoryState((s) => s.history); |
| 29 | |
| 30 | useEffect(() => { |
| 31 | console.log("history", history); |
| 32 | }, [history]); |
| 33 | |
| 34 | const hasInitializedRef = useRef(false); |
| 35 | |
| 36 | // Initialize history when slides are first loaded (after generation completes) |
| 37 | useEffect(() => { |
| 38 | // Don't initialize during generation |
| 39 | if (isGeneratingPresentation) { |
| 40 | hasInitializedRef.current = false; |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | // Initialize once when we have slides and haven't initialized yet |
| 45 | if ( |
| 46 | slides.length > 0 && |
| 47 | !hasInitializedRef.current && |
| 48 | history.present === null |
| 49 | ) { |
| 50 | initializeHistory(slides, theme, customThemeData); |
| 51 | hasInitializedRef.current = true; |
| 52 | } |
| 53 | }, [ |
| 54 | slides, |
| 55 | theme, |
| 56 | customThemeData, |
| 57 | isGeneratingPresentation, |
| 58 | initializeHistory, |
| 59 | history.present, |
| 60 | ]); |
| 61 | |
| 62 | // Clear history when navigating away |
| 63 | useEffect(() => { |
| 64 | return () => { |
| 65 | clearHistory(); |
| 66 | hasInitializedRef.current = false; |
| 67 | }; |
| 68 | }, [clearHistory]); |
| 69 | |
| 70 | // Keyboard shortcuts for undo/redo |
| 71 | useEffect(() => { |
| 72 | const handleKeyDown = (event: KeyboardEvent) => { |
| 73 | const isModKey = event.ctrlKey || event.metaKey; |
| 74 | |
| 75 | if (!isModKey) return; |
| 76 | |
| 77 | const key = event.key.toLowerCase(); |
| 78 | |
| 79 | // Undo: Ctrl+Z or Cmd+Z (but not Shift+Z) |
| 80 | if (key === "z" && !event.shiftKey) { |
| 81 | event.preventDefault(); |
| 82 | event.stopPropagation(); |
| 83 | event.stopImmediatePropagation(); |
| 84 | if (canUndo) { |
| 85 | undo(); |
| 86 | } |
| 87 | return; |
| 88 | } |
| 89 | |
| 90 | // Redo: Ctrl+Shift+Z or Cmd+Shift+Z |
| 91 | if (key === "z" && event.shiftKey) { |
| 92 | event.preventDefault(); |
| 93 | event.stopPropagation(); |
| 94 | event.stopImmediatePropagation(); |
| 95 | if (canRedo) { |
| 96 | redo(); |
| 97 | } |
| 98 | return; |
| 99 | } |
| 100 | |
| 101 | // Redo: Ctrl+Y or Cmd+Y (alternative) |
| 102 | if (key === "y") { |
| 103 | event.preventDefault(); |
| 104 | event.stopPropagation(); |
| 105 | event.stopImmediatePropagation(); |
| 106 | if (canRedo) { |
| 107 | redo(); |
| 108 | } |
| 109 | return; |
| 110 | } |
| 111 | }; |
| 112 | |
| 113 | // Use capture phase to intercept before Plate.js |
| 114 | document.addEventListener("keydown", handleKeyDown, true); |
| 115 | |
| 116 | return () => { |
| 117 | document.removeEventListener("keydown", handleKeyDown, true); |
| 118 | }; |
| 119 | }, [undo, redo, canUndo, canRedo]); |
| 120 | } |
| 121 |