fix: undo/redo rendering pipeline for canvas

Brush and eraser mouseUp handlers were bumping _historyVersion a second
time (after addObject already bumped it in mouseDown).  This created a
ghost history entry whose objects array held the same reference as the
current state.  On undo, zundo restored that identical reference, so
zustand's Object.is selector equality saw no change and skipped the
canvas re-render -- the stroke stayed visible even though the history
panel moved backward.

Remove the redundant _historyVersion increment from both mouseUp
handlers.  The addObject call in mouseDown already records the correct
pre-stroke snapshot.  mouseUp now only patches lastAction for the
history label without creating a new temporal entry.
This commit is contained in:
SnapOtter
2026-05-07 20:36:12 +08:00
parent fa3085aff9
commit 30b14151a4
2 changed files with 12 additions and 8 deletions
@@ -82,10 +82,12 @@ export function useBrushTool() {
const handleMouseUp = useCallback(() => {
if (strokeRef.current) {
useEditorStore.setState({
lastAction: "Brush Stroke",
_historyVersion: useEditorStore.getState()._historyVersion + 1,
});
// Only update the label -- addObject() in handleMouseDown already
// incremented _historyVersion and recorded the pre-stroke snapshot.
// Bumping the version again would create a second history entry whose
// objects array still contains the line, so the first undo would
// restore the same objects reference and the canvas would not repaint.
useEditorStore.setState({ lastAction: "Brush Stroke" });
}
strokeRef.current = null;
}, []);
@@ -82,10 +82,12 @@ export function useEraserTool() {
const handleMouseUp = useCallback(() => {
if (strokeRef.current) {
useEditorStore.setState({
lastAction: "Eraser Stroke",
_historyVersion: useEditorStore.getState()._historyVersion + 1,
});
// Only update the label -- addObject() in handleMouseDown already
// incremented _historyVersion and recorded the pre-stroke snapshot.
// Bumping the version again would create a second history entry whose
// objects array still contains the line, so the first undo would
// restore the same objects reference and the canvas would not repaint.
useEditorStore.setState({ lastAction: "Eraser Stroke" });
}
strokeRef.current = null;
}, []);