Download hooks/useCanvasHistory.ts from im1111/xpaintdev: direct link, hf CLI and curl.
- Browser
- Download file 4.73 kB
-
https://huggingface.co/spaces/im1111/xpaintdev/resolve/main/hooks/useCanvasHistory.ts
- Command line
-
hf download hf://spaces/im1111/xpaintdev/hooks/useCanvasHistory.ts
-
curl -L -o useCanvasHistory.ts https://huggingface.co/spaces/im1111/xpaintdev/resolve/main/hooks/useCanvasHistory.ts
4.73 kB
| import { useState, useEffect, useCallback } from 'react'; | |
| import { saveCanvasState as dbSaveCanvasState, loadCanvasState as dbLoadCanvasState, LoadedCanvasState } from '../services/dbService'; | |
| import { MAX_HISTORY_STEPS, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT } from '../constants'; | |
| import { getBlankCanvasDataURL } from '../utils/canvasUtils'; | |
| export interface CanvasHistoryHook { | |
| historyStack: string[]; | |
| currentHistoryIndex: number; | |
| canvasWidth: number; | |
| canvasHeight: number; | |
| isLoading: boolean; | |
| currentDataURL: string | null; | |
| handleDrawEnd: (dataURL: string, newWidth?: number, newHeight?: number) => void; | |
| handleUndo: () => void; | |
| handleRedo: () => void; | |
| updateCanvasState: (dataURL: string, width: number, height: number) => void; | |
| } | |
| export const useCanvasHistory = (): CanvasHistoryHook => { | |
| const [historyStack, setHistoryStack] = useState<string[]>([]); | |
| const [currentHistoryIndex, setCurrentHistoryIndex] = useState<number>(-1); | |
| const [canvasWidth, setCanvasWidth] = useState<number>(DEFAULT_CANVAS_WIDTH); | |
| const [canvasHeight, setCanvasHeight] = useState<number>(DEFAULT_CANVAS_HEIGHT); | |
| const [isLoading, setIsLoading] = useState<boolean>(true); | |
| const currentDataURL = historyStack.length > 0 && currentHistoryIndex >= 0 ? historyStack[currentHistoryIndex] : null; | |
| const loadInitialCanvas = useCallback(async () => { | |
| setIsLoading(true); | |
| const savedState: LoadedCanvasState | null = await dbLoadCanvasState(); | |
| if (savedState && savedState.dataURL) { // Ensure dataURL exists | |
| setCanvasWidth(savedState.width); | |
| setCanvasHeight(savedState.height); | |
| setHistoryStack([savedState.dataURL]); | |
| setCurrentHistoryIndex(0); | |
| } else { | |
| const initialWidth = DEFAULT_CANVAS_WIDTH; | |
| const initialHeight = DEFAULT_CANVAS_HEIGHT; | |
| setCanvasWidth(initialWidth); | |
| setCanvasHeight(initialHeight); | |
| const blankCanvas = getBlankCanvasDataURL(initialWidth, initialHeight); | |
| setHistoryStack([blankCanvas]); | |
| setCurrentHistoryIndex(0); | |
| await dbSaveCanvasState(blankCanvas, initialWidth, initialHeight); | |
| } | |
| setIsLoading(false); | |
| }, []); | |
| useEffect(() => { | |
| loadInitialCanvas(); | |
| }, [loadInitialCanvas]); | |
| const handleDrawEnd = useCallback((dataURL: string, newWidth?: number, newHeight?: number) => { | |
| const effectiveWidth = newWidth ?? canvasWidth; | |
| const effectiveHeight = newHeight ?? canvasHeight; | |
| if (newWidth && newWidth !== canvasWidth) setCanvasWidth(newWidth); | |
| if (newHeight && newHeight !== canvasHeight) setCanvasHeight(newHeight); | |
| setHistoryStack(prevStack => { | |
| // If currentHistoryIndex is not at the end, it means we've undone some steps. | |
| // New drawing should overwrite the "redo" history. | |
| const newStackBase = prevStack.slice(0, currentHistoryIndex + 1); | |
| let newStack = [...newStackBase, dataURL]; | |
| if (newStack.length > MAX_HISTORY_STEPS) { | |
| newStack = newStack.slice(newStack.length - MAX_HISTORY_STEPS); | |
| } | |
| // Update currentHistoryIndex to point to the new state (end of the new stack) | |
| setCurrentHistoryIndex(newStack.length - 1); | |
| return newStack; | |
| }); | |
| dbSaveCanvasState(dataURL, effectiveWidth, effectiveHeight); | |
| }, [currentHistoryIndex, canvasWidth, canvasHeight]); // Added canvasWidth, canvasHeight as they are used for effective dimensions. | |
| const updateCanvasState = useCallback((dataURL: string, width: number, height: number) => { | |
| // This function is for direct updates, like loading an image or AI result | |
| handleDrawEnd(dataURL, width, height); | |
| }, [handleDrawEnd]); | |
| const handleUndo = () => { | |
| if (currentHistoryIndex > 0) { | |
| setCurrentHistoryIndex(prevIndex => prevIndex - 1); | |
| // Autosave current state when undoing to ensure persistence of the "undone to" state. | |
| // This is implicit as currentDataURL will update, and if the app were to reload, | |
| // it should load the state at currentHistoryIndex. | |
| // The currentDataURL used by CanvasComponent will be historyStack[newIndex]. | |
| // dbSaveCanvasState(historyStack[currentHistoryIndex - 1], canvasWidth, canvasHeight); // This might be too aggressive, rely on drawEnd | |
| } | |
| }; | |
| const handleRedo = () => { | |
| if (currentHistoryIndex < historyStack.length - 1) { | |
| setCurrentHistoryIndex(prevIndex => prevIndex + 1); | |
| // dbSaveCanvasState(historyStack[currentHistoryIndex + 1], canvasWidth, canvasHeight); // Same as undo, might be too aggressive | |
| } | |
| }; | |
| return { | |
| historyStack, | |
| currentHistoryIndex, | |
| canvasWidth, | |
| canvasHeight, | |
| isLoading, | |
| currentDataURL, | |
| handleDrawEnd, | |
| handleUndo, | |
| handleRedo, | |
| updateCanvasState, | |
| }; | |
| }; | |