mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: resolve 15 critical/high/medium issues from self-review
CRITICAL fixes: - #14: Render source image on canvas via Konva Image + use-image hook - #3: Wire move tool handlers (onClick, onDragEnd, onTransformEnd, draggable) to all CanvasObjectRenderer shapes - #4: Implement image object rendering for fill/gradient output - #2: Show fallback text in histogram panel when no imageData provided - #1: Forward all args in zundo handleSet debounce wrapper HIGH fixes: - #5: Track raw screen cursor position for brush overlay instead of using canvas-space coordinates - #6: Export dialog uses Konva stage.toDataURL via module-level ref instead of DOM querySelector for correct export at any zoom/pan - #7: Add _historyVersion increment to setAdjustment, resetAdjustments, toggleFilter, and setFilterParam for undo tracking - #8: Include lastAction in partialize so history labels display correctly MEDIUM fixes: - #10: Move useEditorShortcuts from EditorCanvas to EditorPage with save/export callbacks - #11: Remove _historyVersion increment from updateObject to prevent brush strokes from flooding undo history - #12: Apply Konva filters (Brighten, Contrast, HSL, Blur, Grayscale, Sepia, Invert, Pixelate, Emboss, Posterize, Noise, Solarize, Threshold, Kaleidoscope) to source image node based on store state
This commit is contained in:
@@ -329,13 +329,15 @@ export const useEditorStore = create<EditorState>()(
|
||||
});
|
||||
},
|
||||
|
||||
// Issue #11: Don't increment _historyVersion here -- updateObject is called
|
||||
// on every mousemove during brush strokes for live preview. History is only
|
||||
// recorded when addObject finalizes the stroke.
|
||||
updateObject: (id, attrs) => {
|
||||
set({
|
||||
objects: get().objects.map((obj) =>
|
||||
obj.id === id ? ({ ...obj, attrs: { ...obj.attrs, ...attrs } } as CanvasObject) : obj,
|
||||
),
|
||||
isDirty: true,
|
||||
_historyVersion: get()._historyVersion + 1,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -524,6 +526,7 @@ export const useEditorStore = create<EditorState>()(
|
||||
},
|
||||
|
||||
// Adjustments
|
||||
// Issue #7: Include _historyVersion increment for undo tracking
|
||||
setAdjustment: (key, value) => {
|
||||
const clamps: Record<string, [number, number]> = {
|
||||
brightness: [-100, 100],
|
||||
@@ -541,14 +544,26 @@ export const useEditorStore = create<EditorState>()(
|
||||
...get().adjustments,
|
||||
[key]: Math.max(min, Math.min(max, value)),
|
||||
},
|
||||
isDirty: true,
|
||||
lastAction: `Adjust ${key.charAt(0).toUpperCase() + key.slice(1)}`,
|
||||
_historyVersion: get()._historyVersion + 1,
|
||||
});
|
||||
},
|
||||
|
||||
resetAdjustments: () => set({ adjustments: { ...DEFAULT_ADJUSTMENTS } }),
|
||||
resetAdjustments: () =>
|
||||
set({
|
||||
adjustments: { ...DEFAULT_ADJUSTMENTS },
|
||||
isDirty: true,
|
||||
lastAction: "Reset Adjustments",
|
||||
_historyVersion: get()._historyVersion + 1,
|
||||
}),
|
||||
|
||||
toggleFilter: (type) => {
|
||||
set({
|
||||
filters: get().filters.map((f) => (f.type === type ? { ...f, enabled: !f.enabled } : f)),
|
||||
isDirty: true,
|
||||
lastAction: `Toggle ${type.charAt(0).toUpperCase() + type.slice(1)} Filter`,
|
||||
_historyVersion: get()._historyVersion + 1,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -557,6 +572,9 @@ export const useEditorStore = create<EditorState>()(
|
||||
filters: get().filters.map((f) =>
|
||||
f.type === type ? { ...f, params: { ...f.params, [key]: value } } : f,
|
||||
),
|
||||
isDirty: true,
|
||||
lastAction: `Set ${type} ${key}`,
|
||||
_historyVersion: get()._historyVersion + 1,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -697,6 +715,7 @@ export const useEditorStore = create<EditorState>()(
|
||||
toggleRightPanel: () => set({ rightPanelVisible: !get().rightPanelVisible }),
|
||||
}),
|
||||
{
|
||||
// Issue #8: Include lastAction in partialize so history labels work
|
||||
partialize: (state) => ({
|
||||
layers: state.layers,
|
||||
objects: state.objects,
|
||||
@@ -706,17 +725,19 @@ export const useEditorStore = create<EditorState>()(
|
||||
guides: state.guides,
|
||||
sourceImageUrl: state.sourceImageUrl,
|
||||
sourceImageSize: state.sourceImageSize,
|
||||
lastAction: state.lastAction,
|
||||
_historyVersion: state._historyVersion,
|
||||
}),
|
||||
limit: MAX_HISTORY,
|
||||
equality: (a, b) =>
|
||||
(a as { _historyVersion: number })._historyVersion ===
|
||||
(b as { _historyVersion: number })._historyVersion,
|
||||
// Issue #1: Forward all arguments from zundo's internal _handleSet
|
||||
handleSet: (handleSet) => {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
return (state) => {
|
||||
return (...args: Parameters<typeof handleSet>) => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => handleSet(state), 500);
|
||||
timeout = setTimeout(() => handleSet(...args), 500);
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user