@yogiswara/honcho-editor-ui 2.8.10 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/color.d.ts +9 -0
- package/dist/color.js +9 -0
- package/dist/components/editor/GalleryAlbum/AlbumImageGallery.d.ts +8 -0
- package/dist/components/editor/GalleryAlbum/AlbumImageGallery.js +28 -0
- package/dist/components/editor/GalleryAlbum/ImageItem.d.ts +10 -0
- package/dist/components/editor/GalleryAlbum/ImageItem.js +81 -0
- package/dist/components/editor/HAccordionAspectRatio.d.ts +14 -0
- package/dist/components/editor/HAccordionAspectRatio.js +102 -0
- package/dist/components/editor/HAccordionColor.d.ts +16 -0
- package/dist/components/editor/HAccordionColor.js +282 -0
- package/dist/components/editor/HAccordionColorAdjustment.d.ts +35 -0
- package/dist/components/editor/HAccordionColorAdjustment.js +31 -0
- package/dist/components/editor/HAccordionDetails.d.ts +12 -0
- package/dist/components/editor/HAccordionDetails.js +183 -0
- package/dist/components/editor/HAccordionLight.d.ts +20 -0
- package/dist/components/editor/HAccordionLight.js +414 -0
- package/dist/components/editor/HAccordionPreset.d.ts +23 -0
- package/dist/components/editor/HAccordionPreset.js +52 -0
- package/dist/components/editor/HAlertBox.d.ts +8 -0
- package/dist/components/editor/HAlertBox.js +55 -0
- package/dist/components/editor/HAspectRatioMobile.d.ts +0 -0
- package/dist/components/editor/HAspectRatioMobile.js +1 -0
- package/dist/components/editor/HBulkAccordionColorAdjustment.d.ts +55 -0
- package/dist/components/editor/HBulkAccordionColorAdjustment.js +31 -0
- package/dist/components/editor/HBulkAccordionColorAdjustmentColors.d.ts +20 -0
- package/dist/components/editor/HBulkAccordionColorAdjustmentColors.js +121 -0
- package/dist/components/editor/HBulkAccordionColorAdjustmentDetails.d.ts +12 -0
- package/dist/components/editor/HBulkAccordionColorAdjustmentDetails.js +65 -0
- package/dist/components/editor/HBulkAccordionColorAdjustmentLight.d.ts +28 -0
- package/dist/components/editor/HBulkAccordionColorAdjustmentLight.js +177 -0
- package/dist/components/editor/HBulkColorAdjustmentMobile.d.ts +53 -0
- package/dist/components/editor/HBulkColorAdjustmentMobile.js +16 -0
- package/dist/components/editor/HBulkColorMobile.d.ts +20 -0
- package/dist/components/editor/HBulkColorMobile.js +121 -0
- package/dist/components/editor/HBulkDetailsMobile.d.ts +12 -0
- package/dist/components/editor/HBulkDetailsMobile.js +65 -0
- package/dist/components/editor/HBulkLightMobile.d.ts +28 -0
- package/dist/components/editor/HBulkLightMobile.js +192 -0
- package/dist/components/editor/HBulkPreset.d.ts +24 -0
- package/dist/components/editor/HBulkPreset.js +43 -0
- package/dist/components/editor/HBulkPresetMobile.d.ts +15 -0
- package/dist/components/editor/HBulkPresetMobile.js +26 -0
- package/dist/components/editor/HDialogBox.d.ts +18 -0
- package/dist/components/editor/HDialogBox.js +51 -0
- package/dist/components/editor/HDialogCopy.d.ts +41 -0
- package/dist/components/editor/HDialogCopy.js +80 -0
- package/dist/components/editor/HFooter.d.ts +12 -0
- package/dist/components/editor/HFooter.js +24 -0
- package/dist/components/editor/HHeaderEditor.d.ts +19 -0
- package/dist/components/editor/HHeaderEditor.js +36 -0
- package/dist/components/editor/HImageEditorBulkDekstop.d.ts +15 -0
- package/dist/components/editor/HImageEditorBulkDekstop.js +29 -0
- package/dist/components/editor/HImageEditorBulkMobile.d.ts +72 -0
- package/dist/components/editor/HImageEditorBulkMobile.js +81 -0
- package/dist/components/editor/HImageEditorDekstop.d.ts +15 -0
- package/dist/components/editor/HImageEditorDekstop.js +29 -0
- package/dist/components/editor/HImageEditorMobile.d.ts +53 -0
- package/dist/components/editor/HImageEditorMobile.js +92 -0
- package/dist/components/editor/HImageEditorMobileLayout.d.ts +14 -0
- package/dist/components/editor/HImageEditorMobileLayout.js +58 -0
- package/dist/components/editor/HModalEditorDekstop.d.ts +13 -0
- package/dist/components/editor/HModalEditorDekstop.js +22 -0
- package/dist/components/editor/HModalMobile.d.ts +13 -0
- package/dist/components/editor/HModalMobile.js +9 -0
- package/dist/components/editor/HPresetDelete.d.ts +7 -0
- package/dist/components/editor/HPresetDelete.js +7 -0
- package/dist/components/editor/HPresetOptionMenu.d.ts +9 -0
- package/dist/components/editor/HPresetOptionMenu.js +20 -0
- package/dist/components/editor/HSliderColorMobile.d.ts +16 -0
- package/dist/components/editor/HSliderColorMobile.js +270 -0
- package/dist/components/editor/HSliderDetailsMobile.d.ts +12 -0
- package/dist/components/editor/HSliderDetailsMobile.js +154 -0
- package/dist/components/editor/HSliderLightMobile.d.ts +20 -0
- package/dist/components/editor/HSliderLightMobile.js +420 -0
- package/dist/components/editor/HTabAspectRatioMobile.d.ts +0 -0
- package/dist/components/editor/HTabAspectRatioMobile.js +1 -0
- package/dist/components/editor/HTabColorAdjustmentMobile.d.ts +35 -0
- package/dist/components/editor/HTabColorAdjustmentMobile.js +8 -0
- package/dist/components/editor/HTabPresetMobile.d.ts +14 -0
- package/dist/components/editor/HTabPresetMobile.js +10 -0
- package/dist/components/editor/HTextField.d.ts +14 -0
- package/dist/components/editor/HTextField.js +51 -0
- package/dist/components/editor/HWatermarkView.d.ts +6 -0
- package/dist/components/editor/HWatermarkView.js +16 -0
- package/dist/components/editor/svg/Tick.d.ts +2 -0
- package/dist/components/editor/svg/Tick.js +6 -0
- package/dist/components/modal/HModalDialog.d.ts +12 -0
- package/dist/components/modal/HModalDialog.js +18 -0
- package/dist/components/modal/HModalRename.d.ts +14 -0
- package/dist/components/modal/HModalRename.js +35 -0
- package/dist/hooks/demo/HonchoEditorBulkDemo.d.ts +3 -0
- package/dist/hooks/demo/HonchoEditorBulkDemo.js +410 -0
- package/dist/hooks/demo/HonchoEditorSingleCleanDemo.d.ts +3 -0
- package/dist/hooks/demo/HonchoEditorSingleCleanDemo.js +354 -0
- package/dist/hooks/demo/index.d.ts +2 -0
- package/dist/hooks/demo/index.js +2 -0
- package/dist/hooks/editor/type.d.ts +174 -0
- package/dist/hooks/editor/type.js +1 -0
- package/dist/hooks/editor/useHonchoEditorBulk.d.ts +96 -0
- package/dist/hooks/editor/useHonchoEditorBulk.js +427 -0
- package/dist/hooks/editor/useHonchoEditorSingle.d.ts +44 -0
- package/dist/hooks/editor/useHonchoEditorSingle.js +163 -0
- package/dist/hooks/useAdjustmentHistory.d.ts +95 -0
- package/dist/hooks/useAdjustmentHistory.js +578 -0
- package/dist/hooks/useAdjustmentHistoryBatch.d.ts +177 -0
- package/dist/hooks/useAdjustmentHistoryBatch.js +1189 -0
- package/dist/hooks/useGallerySwipe.d.ts +36 -0
- package/dist/hooks/useGallerySwipe.js +344 -0
- package/dist/hooks/usePaging.d.ts +89 -0
- package/dist/hooks/usePaging.js +211 -0
- package/dist/hooks/usePreset.d.ts +82 -0
- package/dist/hooks/usePreset.js +344 -0
- package/dist/index.d.ts +39 -1474
- package/dist/index.js +44 -10960
- package/dist/lib/context/EditorContext.d.ts +28 -0
- package/dist/lib/context/EditorContext.js +60 -0
- package/dist/lib/context/EditorProcessingService.d.ts +36 -0
- package/dist/lib/context/EditorProcessingService.js +249 -0
- package/dist/lib/editor/honcho-editor.d.ts +324 -0
- package/dist/lib/editor/honcho-editor.js +825 -0
- package/dist/lib/hooks/useEditor.d.ts +22 -0
- package/dist/lib/hooks/useEditor.js +35 -0
- package/dist/lib/hooks/useEditorHeadless.d.ts +34 -0
- package/dist/lib/hooks/useEditorHeadless.js +207 -0
- package/dist/lib/hooks/useImageProcessor.d.ts +18 -0
- package/dist/lib/hooks/useImageProcessor.js +113 -0
- package/dist/setupTests.d.ts +1 -0
- package/dist/setupTests.js +1 -0
- package/dist/themes/colors.d.ts +12 -0
- package/dist/themes/colors.js +12 -0
- package/dist/themes/honchoTheme.d.ts +25 -0
- package/dist/themes/honchoTheme.js +94 -0
- package/dist/utils/adjustment.d.ts +6 -0
- package/dist/utils/adjustment.js +48 -0
- package/dist/utils/imageLoader.d.ts +11 -0
- package/dist/utils/imageLoader.js +48 -0
- package/dist/utils/isMobile.d.ts +1 -0
- package/dist/utils/isMobile.js +5 -0
- package/package.json +6 -12
- package/dist/index.d.mts +0 -1474
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -10939
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { AdjustmentState, Controller } from './editor/type';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for the adjustment history hook
|
|
4
|
+
*/
|
|
5
|
+
export interface HistoryOptions {
|
|
6
|
+
/** Maximum number of history entries to keep. Use 'unlimited' for no limit */
|
|
7
|
+
maxSize?: number | 'unlimited';
|
|
8
|
+
/** Whether to enable batch mode for grouping multiple changes */
|
|
9
|
+
enableBatching?: boolean;
|
|
10
|
+
/** Enable development warnings for performance issues */
|
|
11
|
+
devWarnings?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Information about the current history state
|
|
15
|
+
*/
|
|
16
|
+
export interface HistoryInfo {
|
|
17
|
+
/** Whether undo operation is available */
|
|
18
|
+
canUndo: boolean;
|
|
19
|
+
/** Whether redo operation is available */
|
|
20
|
+
canRedo: boolean;
|
|
21
|
+
/** Current position in history (0-based index) */
|
|
22
|
+
currentIndex: number;
|
|
23
|
+
/** Total number of states in history */
|
|
24
|
+
totalStates: number;
|
|
25
|
+
/** Current size of history in memory */
|
|
26
|
+
historySize: number;
|
|
27
|
+
/** Whether batch mode is currently active */
|
|
28
|
+
isBatchMode: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Actions available for history management
|
|
32
|
+
*/
|
|
33
|
+
export interface HistoryActions {
|
|
34
|
+
/** Add a new adjustment state to history */
|
|
35
|
+
pushState: (state: AdjustmentState) => void;
|
|
36
|
+
/** Undo to previous adjustment state */
|
|
37
|
+
undo: () => void;
|
|
38
|
+
/** Redo to next adjustment state */
|
|
39
|
+
redo: () => void;
|
|
40
|
+
/** Jump to specific index in history */
|
|
41
|
+
jumpToIndex: (index: number) => void;
|
|
42
|
+
/** Clear all history and start fresh */
|
|
43
|
+
clearHistory: () => void;
|
|
44
|
+
/** Get a copy of the entire history array */
|
|
45
|
+
getHistory: () => AdjustmentState[];
|
|
46
|
+
/** Trim history to specified size, keeping most recent entries */
|
|
47
|
+
trimHistory: (keepLast: number) => void;
|
|
48
|
+
/** Sync history from backend using getEditorHistory */
|
|
49
|
+
syncFromBackend: () => Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Configuration actions for runtime adjustment
|
|
53
|
+
*/
|
|
54
|
+
export interface HistoryConfig {
|
|
55
|
+
/** Set maximum history size */
|
|
56
|
+
setMaxSize: (size: number | 'unlimited') => void;
|
|
57
|
+
/** Enable or disable batch mode */
|
|
58
|
+
setBatchMode: (enabled: boolean, forceHistory?: boolean) => Promise<void>;
|
|
59
|
+
/** Get current memory usage estimate */
|
|
60
|
+
getMemoryUsage: () => number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Return type for the useAdjustmentHistory hook
|
|
64
|
+
*/
|
|
65
|
+
export interface UseAdjustmentHistoryReturn {
|
|
66
|
+
/** Current adjustment state value */
|
|
67
|
+
currentState: AdjustmentState;
|
|
68
|
+
/** Current index in history */
|
|
69
|
+
currentIndex: number;
|
|
70
|
+
/** Information about history state */
|
|
71
|
+
historyInfo: HistoryInfo;
|
|
72
|
+
/** Available history actions */
|
|
73
|
+
actions: HistoryActions;
|
|
74
|
+
/** Configuration options */
|
|
75
|
+
config: HistoryConfig;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Advanced hook for managing AdjustmentState history with undo/redo functionality.
|
|
79
|
+
*
|
|
80
|
+
* Features:
|
|
81
|
+
* - Unlimited or configurable history size
|
|
82
|
+
* - Batch mode for grouping multiple changes into single undo operations
|
|
83
|
+
* - Memory usage monitoring and optimization
|
|
84
|
+
* - Internal stabilization to prevent re-render issues
|
|
85
|
+
* - Jump to any point in history
|
|
86
|
+
* - Automatic AdjustmentState comparison
|
|
87
|
+
*
|
|
88
|
+
* @param initialState - The initial AdjustmentState value
|
|
89
|
+
* @param controller - Controller for backend operations (optional)
|
|
90
|
+
* @param firebaseUid - Firebase UID for backend operations (optional)
|
|
91
|
+
* @param currentImageId - Current image ID for backend operations (optional)
|
|
92
|
+
* @param options - Configuration options for history behavior
|
|
93
|
+
* @returns Object with current state, history info, actions, and config
|
|
94
|
+
*/
|
|
95
|
+
export declare function useAdjustmentHistory(initialState: AdjustmentState, controller?: Controller, firebaseUid?: string, currentImageId?: string, options?: HistoryOptions): UseAdjustmentHistoryReturn;
|
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Convert AdjustmentState to ColorAdjustment format for backend
|
|
4
|
+
*/
|
|
5
|
+
const convertAdjustmentStateToColorAdjustment = (adjustmentState) => {
|
|
6
|
+
return {
|
|
7
|
+
temperature: adjustmentState.tempScore,
|
|
8
|
+
tint: adjustmentState.tintScore,
|
|
9
|
+
saturation: adjustmentState.saturationScore,
|
|
10
|
+
vibrance: adjustmentState.vibranceScore,
|
|
11
|
+
exposure: adjustmentState.exposureScore,
|
|
12
|
+
contrast: adjustmentState.contrastScore,
|
|
13
|
+
highlights: adjustmentState.highlightsScore,
|
|
14
|
+
shadows: adjustmentState.shadowsScore,
|
|
15
|
+
whites: adjustmentState.whitesScore,
|
|
16
|
+
blacks: adjustmentState.blacksScore,
|
|
17
|
+
clarity: adjustmentState.clarityScore,
|
|
18
|
+
sharpness: adjustmentState.sharpnessScore,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Convert ColorAdjustment from backend to AdjustmentState format
|
|
23
|
+
*/
|
|
24
|
+
const convertColorAdjustmentToAdjustmentState = (colorAdjustment) => {
|
|
25
|
+
return {
|
|
26
|
+
tempScore: colorAdjustment.temperature,
|
|
27
|
+
tintScore: colorAdjustment.tint,
|
|
28
|
+
saturationScore: colorAdjustment.saturation,
|
|
29
|
+
vibranceScore: colorAdjustment.vibrance,
|
|
30
|
+
exposureScore: colorAdjustment.exposure,
|
|
31
|
+
contrastScore: colorAdjustment.contrast,
|
|
32
|
+
highlightsScore: colorAdjustment.highlights,
|
|
33
|
+
shadowsScore: colorAdjustment.shadows,
|
|
34
|
+
whitesScore: colorAdjustment.whites,
|
|
35
|
+
blacksScore: colorAdjustment.blacks,
|
|
36
|
+
clarityScore: colorAdjustment.clarity,
|
|
37
|
+
sharpnessScore: colorAdjustment.sharpness,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Compare two AdjustmentState objects for equality
|
|
42
|
+
* Uses JSON.stringify for deep comparison of all adjustment values
|
|
43
|
+
*/
|
|
44
|
+
const compareAdjustmentStates = (a, b) => {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
// Fallback to manual comparison if JSON.stringify fails
|
|
50
|
+
console.warn('Failed to compare adjustment states with JSON.stringify, falling back to manual comparison:', error);
|
|
51
|
+
return (a.tempScore === b.tempScore &&
|
|
52
|
+
a.tintScore === b.tintScore &&
|
|
53
|
+
a.vibranceScore === b.vibranceScore &&
|
|
54
|
+
a.saturationScore === b.saturationScore &&
|
|
55
|
+
a.exposureScore === b.exposureScore &&
|
|
56
|
+
a.highlightsScore === b.highlightsScore &&
|
|
57
|
+
a.shadowsScore === b.shadowsScore &&
|
|
58
|
+
a.whitesScore === b.whitesScore &&
|
|
59
|
+
a.blacksScore === b.blacksScore &&
|
|
60
|
+
a.contrastScore === b.contrastScore &&
|
|
61
|
+
a.clarityScore === b.clarityScore &&
|
|
62
|
+
a.sharpnessScore === b.sharpnessScore);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Advanced hook for managing AdjustmentState history with undo/redo functionality.
|
|
67
|
+
*
|
|
68
|
+
* Features:
|
|
69
|
+
* - Unlimited or configurable history size
|
|
70
|
+
* - Batch mode for grouping multiple changes into single undo operations
|
|
71
|
+
* - Memory usage monitoring and optimization
|
|
72
|
+
* - Internal stabilization to prevent re-render issues
|
|
73
|
+
* - Jump to any point in history
|
|
74
|
+
* - Automatic AdjustmentState comparison
|
|
75
|
+
*
|
|
76
|
+
* @param initialState - The initial AdjustmentState value
|
|
77
|
+
* @param controller - Controller for backend operations (optional)
|
|
78
|
+
* @param firebaseUid - Firebase UID for backend operations (optional)
|
|
79
|
+
* @param currentImageId - Current image ID for backend operations (optional)
|
|
80
|
+
* @param options - Configuration options for history behavior
|
|
81
|
+
* @returns Object with current state, history info, actions, and config
|
|
82
|
+
*/
|
|
83
|
+
export function useAdjustmentHistory(initialState, controller, firebaseUid, currentImageId, options = {}) {
|
|
84
|
+
// Internal stabilization - prevent re-renders from options object recreation
|
|
85
|
+
const internalOptions = useMemo(() => ({
|
|
86
|
+
maxSize: options.maxSize ?? 'unlimited',
|
|
87
|
+
enableBatching: options.enableBatching ?? false,
|
|
88
|
+
devWarnings: options.devWarnings ?? false,
|
|
89
|
+
controller: controller,
|
|
90
|
+
firebaseUid: firebaseUid,
|
|
91
|
+
currentImageId: currentImageId
|
|
92
|
+
}), [
|
|
93
|
+
options.maxSize,
|
|
94
|
+
options.enableBatching,
|
|
95
|
+
options.devWarnings,
|
|
96
|
+
controller,
|
|
97
|
+
firebaseUid,
|
|
98
|
+
currentImageId
|
|
99
|
+
]);
|
|
100
|
+
// Core state management
|
|
101
|
+
const [history, setHistory] = useState([{ state: initialState, taskId: `initial_${Date.now()}` }]);
|
|
102
|
+
const [currentState, setCurrentState] = useState(initialState);
|
|
103
|
+
const currentIndexRef = useRef(0);
|
|
104
|
+
const currentStateRef = useRef(initialState);
|
|
105
|
+
// Batch mode state - ref to avoid triggering effects
|
|
106
|
+
const batchModeRef = useRef(internalOptions.enableBatching);
|
|
107
|
+
const batchStartIndexRef = useRef(null);
|
|
108
|
+
const batchStartStateRef = useRef(null);
|
|
109
|
+
const batchModeProcessingRef = useRef(false); // Guard against double execution
|
|
110
|
+
// Configuration refs - prevent re-renders when config changes
|
|
111
|
+
const maxSizeRef = useRef(internalOptions.maxSize);
|
|
112
|
+
const devWarningsRef = useRef(internalOptions.devWarnings);
|
|
113
|
+
// Computed current index for return value (defined early for dependencies)
|
|
114
|
+
const currentIndex = currentIndexRef.current;
|
|
115
|
+
// Performance monitoring
|
|
116
|
+
const performanceRef = useRef({
|
|
117
|
+
lastHistorySize: 1,
|
|
118
|
+
lastUpdateTime: Date.now(),
|
|
119
|
+
largeHistoryWarningShown: false
|
|
120
|
+
});
|
|
121
|
+
// Sync currentState with history when not in batch mode
|
|
122
|
+
// useEffect(() => {
|
|
123
|
+
// if (!batchModeRef.current) {
|
|
124
|
+
// console.log(`[useAdjustmentHistory] Syncing currentState with history:`, history[currentIndex]?.state);
|
|
125
|
+
// setCurrentState(history[currentIndex]?.state || initialState);
|
|
126
|
+
// }
|
|
127
|
+
// }, [history, currentIndex, initialState]);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (internalOptions.currentImageId) {
|
|
130
|
+
syncFromBackend().catch(console.error);
|
|
131
|
+
}
|
|
132
|
+
}, [internalOptions.currentImageId, internalOptions.firebaseUid, internalOptions.controller]);
|
|
133
|
+
const getMemoryUsage = useCallback(() => {
|
|
134
|
+
try {
|
|
135
|
+
const historyString = JSON.stringify(history);
|
|
136
|
+
return historyString.length * 2; // Rough estimate: 2 bytes per character
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.warn('Failed to estimate memory usage:', error);
|
|
140
|
+
return history.length * 1000; // Fallback estimate
|
|
141
|
+
}
|
|
142
|
+
}, [history]);
|
|
143
|
+
// Development warnings for performance
|
|
144
|
+
const checkPerformance = useCallback(() => {
|
|
145
|
+
if (!devWarningsRef.current)
|
|
146
|
+
return;
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
const perfData = performanceRef.current;
|
|
149
|
+
// Warn about large history sizes
|
|
150
|
+
if (history.length > 1000 && !perfData.largeHistoryWarningShown) {
|
|
151
|
+
console.warn(`useAdjustmentHistory: Large history size detected (${history.length} entries). Consider setting a maxSize limit.`);
|
|
152
|
+
perfData.largeHistoryWarningShown = true;
|
|
153
|
+
}
|
|
154
|
+
// Update performance tracking
|
|
155
|
+
perfData.lastHistorySize = history.length;
|
|
156
|
+
perfData.lastUpdateTime = now;
|
|
157
|
+
}, [history.length]);
|
|
158
|
+
// Trim history to specified size, keeping most recent entries
|
|
159
|
+
const trimHistoryToSize = useCallback((size) => {
|
|
160
|
+
if (size <= 0)
|
|
161
|
+
return;
|
|
162
|
+
setHistory(prevHistory => {
|
|
163
|
+
if (prevHistory.length <= size)
|
|
164
|
+
return prevHistory;
|
|
165
|
+
const startIndex = Math.max(0, prevHistory.length - size);
|
|
166
|
+
const trimmedHistory = prevHistory.slice(startIndex);
|
|
167
|
+
// Adjust current index to maintain relative position
|
|
168
|
+
const prevIndex = currentIndexRef.current;
|
|
169
|
+
const adjustedIndex = prevIndex - startIndex;
|
|
170
|
+
currentIndexRef.current = Math.max(0, Math.min(adjustedIndex, trimmedHistory.length - 1));
|
|
171
|
+
return trimmedHistory;
|
|
172
|
+
});
|
|
173
|
+
}, []);
|
|
174
|
+
// Apply max size limit when history grows
|
|
175
|
+
const enforceMaxSize = useCallback(() => {
|
|
176
|
+
if (maxSizeRef.current === 'unlimited')
|
|
177
|
+
return;
|
|
178
|
+
const maxSize = maxSizeRef.current;
|
|
179
|
+
if (history.length > maxSize) {
|
|
180
|
+
trimHistoryToSize(maxSize);
|
|
181
|
+
}
|
|
182
|
+
}, [history.length, trimHistoryToSize]);
|
|
183
|
+
// Push new state to history
|
|
184
|
+
const pushState = useCallback((newState) => {
|
|
185
|
+
// Always update currentState immediately for smooth UI
|
|
186
|
+
currentStateRef.current = newState;
|
|
187
|
+
setCurrentState(newState);
|
|
188
|
+
if (batchModeRef.current) {
|
|
189
|
+
// In batch mode: Don't update history yet, just update UI state
|
|
190
|
+
// History will be updated when batch mode ends
|
|
191
|
+
console.log(`[useAdjustmentHistory] Pushing state in batch mode:`, newState);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Normal mode: Update history immediately
|
|
195
|
+
console.log(`[useAdjustmentHistory] Pushing new state to history:`, newState);
|
|
196
|
+
setHistory(prevHistory => {
|
|
197
|
+
const truncatedHistory = prevHistory.slice(0, currentIndexRef.current + 1);
|
|
198
|
+
// const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
199
|
+
// we put the taskId empty to check this state no need to save it
|
|
200
|
+
const newHistory = [...truncatedHistory, { state: newState, taskId: "" }];
|
|
201
|
+
currentIndexRef.current = newHistory.length - 1;
|
|
202
|
+
return newHistory;
|
|
203
|
+
});
|
|
204
|
+
}, []);
|
|
205
|
+
// Undo to previous state
|
|
206
|
+
const undo = useCallback(async () => {
|
|
207
|
+
if (currentIndexRef.current > 0) {
|
|
208
|
+
const newIndex = currentIndexRef.current - 1;
|
|
209
|
+
const historyEntry = history[newIndex];
|
|
210
|
+
const newState = historyEntry.state;
|
|
211
|
+
currentIndexRef.current = newIndex;
|
|
212
|
+
currentStateRef.current = newState;
|
|
213
|
+
setCurrentState(newState);
|
|
214
|
+
console.log(`[useAdjustmentHistory] Undoing to index ${newIndex}:`, newState);
|
|
215
|
+
// Call controller to set history index in backend if taskId exists
|
|
216
|
+
if (historyEntry.taskId && internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
|
|
217
|
+
try {
|
|
218
|
+
console.log(`🔙 Undo: Setting history index to taskId: ${historyEntry.taskId}`);
|
|
219
|
+
await internalOptions.controller.setHistoryIndex(internalOptions.firebaseUid, internalOptions.currentImageId, historyEntry.taskId);
|
|
220
|
+
console.log('✅ Successfully set history index for undo');
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
console.error('❌ Failed to set history index for undo:', error);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
console.log("🔙 Undo: No taskId available for setting history index", historyEntry.taskId, internalOptions.firebaseUid, internalOptions.currentImageId);
|
|
228
|
+
}
|
|
229
|
+
// Exit batch mode when undoing
|
|
230
|
+
if (batchModeRef.current) {
|
|
231
|
+
batchModeRef.current = false;
|
|
232
|
+
batchStartIndexRef.current = null;
|
|
233
|
+
batchStartStateRef.current = null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}, [history, internalOptions]);
|
|
237
|
+
// Redo to next state
|
|
238
|
+
const redo = useCallback(async () => {
|
|
239
|
+
if (currentIndexRef.current < history.length - 1) {
|
|
240
|
+
const newIndex = currentIndexRef.current + 1;
|
|
241
|
+
const historyEntry = history[newIndex];
|
|
242
|
+
const newState = historyEntry.state;
|
|
243
|
+
currentIndexRef.current = newIndex;
|
|
244
|
+
currentStateRef.current = newState;
|
|
245
|
+
setCurrentState(newState);
|
|
246
|
+
console.log(`[useAdjustmentHistory] Redoing to index ${newIndex}:`, newState);
|
|
247
|
+
// Call controller to set history index in backend if taskId exists
|
|
248
|
+
if (historyEntry.taskId && internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
|
|
249
|
+
try {
|
|
250
|
+
console.log(`🔄 Redo: Setting history index to taskId: ${historyEntry.taskId}`);
|
|
251
|
+
await internalOptions.controller.setHistoryIndex(internalOptions.firebaseUid, internalOptions.currentImageId, historyEntry.taskId);
|
|
252
|
+
console.log('✅ Successfully set history index for redo');
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
console.error('❌ Failed to set history index for redo:', error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
console.warn('🔄 Redo: No taskId available for setting history index', historyEntry.taskId, internalOptions.firebaseUid, internalOptions.currentImageId);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}, [history, internalOptions]);
|
|
263
|
+
// Reset history with new initial state
|
|
264
|
+
// const reset = useCallback((newInitialState: AdjustmentState) => {
|
|
265
|
+
// console.log("Reset called setHistory");
|
|
266
|
+
// setHistory([{ state: newInitialState, taskId: `reset_${Date.now()}` }]);
|
|
267
|
+
// currentIndexRef.current = 0;
|
|
268
|
+
// currentStateRef.current = newInitialState;
|
|
269
|
+
// batchModeRef.current = internalOptions.enableBatching;
|
|
270
|
+
// batchStartIndexRef.current = null;
|
|
271
|
+
// batchStartStateRef.current = null;
|
|
272
|
+
// }, [internalOptions.enableBatching]);
|
|
273
|
+
// Jump to specific index in history
|
|
274
|
+
const jumpToIndex = useCallback((index) => {
|
|
275
|
+
if (index >= 0 && index < history.length) {
|
|
276
|
+
const historyEntry = history[index];
|
|
277
|
+
const newState = historyEntry.state;
|
|
278
|
+
currentIndexRef.current = index;
|
|
279
|
+
currentStateRef.current = newState;
|
|
280
|
+
setCurrentState(newState);
|
|
281
|
+
// Exit batch mode when jumping
|
|
282
|
+
if (batchModeRef.current) {
|
|
283
|
+
batchModeRef.current = false;
|
|
284
|
+
batchStartIndexRef.current = null;
|
|
285
|
+
batchStartStateRef.current = null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}, [history]);
|
|
289
|
+
// Clear all history and start fresh
|
|
290
|
+
const clearHistory = useCallback(() => {
|
|
291
|
+
console.log("clearHistory called setHistory");
|
|
292
|
+
setHistory([{ state: currentStateRef.current, taskId: `clear_${Date.now()}` }]);
|
|
293
|
+
currentIndexRef.current = 0;
|
|
294
|
+
batchModeRef.current = internalOptions.enableBatching;
|
|
295
|
+
batchStartIndexRef.current = null;
|
|
296
|
+
batchStartStateRef.current = null;
|
|
297
|
+
}, [internalOptions.enableBatching]);
|
|
298
|
+
// Get copy of entire history
|
|
299
|
+
const getHistory = useCallback(() => {
|
|
300
|
+
return history.map(entry => entry.state);
|
|
301
|
+
}, [history]);
|
|
302
|
+
// Manually trim history
|
|
303
|
+
const trimHistory = useCallback((keepLast) => {
|
|
304
|
+
trimHistoryToSize(keepLast);
|
|
305
|
+
}, [trimHistoryToSize]);
|
|
306
|
+
// Sync/replace entire history with new list
|
|
307
|
+
const syncHistory = useCallback((newHistory, targetIndex) => {
|
|
308
|
+
throw Error('syncHistory is not implemented yet');
|
|
309
|
+
// // Validate input
|
|
310
|
+
// if (!Array.isArray(newHistory) || newHistory.length === 0) {
|
|
311
|
+
// console.warn('syncHistory: newHistory must be a non-empty array');
|
|
312
|
+
// return;
|
|
313
|
+
// }
|
|
314
|
+
// // Validate all items are AdjustmentState objects
|
|
315
|
+
// const isValidHistory = newHistory.every(state =>
|
|
316
|
+
// state && typeof state === 'object' &&
|
|
317
|
+
// typeof state.tempScore === 'number' &&
|
|
318
|
+
// typeof state.tintScore === 'number' &&
|
|
319
|
+
// typeof state.vibranceScore === 'number' &&
|
|
320
|
+
// typeof state.saturationScore === 'number' &&
|
|
321
|
+
// typeof state.exposureScore === 'number' &&
|
|
322
|
+
// typeof state.highlightsScore === 'number' &&
|
|
323
|
+
// typeof state.shadowsScore === 'number' &&
|
|
324
|
+
// typeof state.whitesScore === 'number' &&
|
|
325
|
+
// typeof state.blacksScore === 'number' &&
|
|
326
|
+
// typeof state.contrastScore === 'number' &&
|
|
327
|
+
// typeof state.clarityScore === 'number' &&
|
|
328
|
+
// typeof state.sharpnessScore === 'number'
|
|
329
|
+
// );
|
|
330
|
+
// if (!isValidHistory) {
|
|
331
|
+
// console.warn('syncHistory: All items in newHistory must be valid AdjustmentState objects');
|
|
332
|
+
// return;
|
|
333
|
+
// }
|
|
334
|
+
// // Exit batch mode if active
|
|
335
|
+
// if (batchModeRef.current) {
|
|
336
|
+
// batchModeRef.current = false;
|
|
337
|
+
// batchStartIndexRef.current = null;
|
|
338
|
+
// batchStartStateRef.current = null;
|
|
339
|
+
// }
|
|
340
|
+
// // Determine target index
|
|
341
|
+
// let finalIndex = targetIndex ?? newHistory.length - 1; // Default to last item
|
|
342
|
+
// finalIndex = Math.max(0, Math.min(finalIndex, newHistory.length - 1)); // Clamp to valid range
|
|
343
|
+
// // Create a copy of the new history to avoid mutations and convert to HistoryEntry format
|
|
344
|
+
// const historyToSet = newHistory.map((state, index) => ({
|
|
345
|
+
// state: { ...state },
|
|
346
|
+
// taskId: `sync_${Date.now()}_${index}`
|
|
347
|
+
// }));
|
|
348
|
+
// // Apply max size limit if needed
|
|
349
|
+
// if (maxSizeRef.current !== 'unlimited' && historyToSet.length > maxSizeRef.current) {
|
|
350
|
+
// const trimAmount = historyToSet.length - maxSizeRef.current;
|
|
351
|
+
// const trimmedHistory = historyToSet.slice(trimAmount);
|
|
352
|
+
// // Adjust target index
|
|
353
|
+
// finalIndex = Math.max(0, finalIndex - trimAmount);
|
|
354
|
+
// setHistory(trimmedHistory);
|
|
355
|
+
// setCurrentIndex(finalIndex);
|
|
356
|
+
// console.log(`syncHistory: Trimmed ${trimAmount} entries to respect maxSize of ${maxSizeRef.current}`);
|
|
357
|
+
// setCurrentState(trimmedHistory[finalIndex].state);
|
|
358
|
+
// if (devWarningsRef.current) {
|
|
359
|
+
// console.warn(`syncHistory: Trimmed ${trimAmount} entries to respect maxSize of ${maxSizeRef.current}`);
|
|
360
|
+
// }
|
|
361
|
+
// } else {
|
|
362
|
+
// // Set history as-is
|
|
363
|
+
// setHistory(historyToSet);
|
|
364
|
+
// setCurrentIndex(finalIndex);
|
|
365
|
+
// console.log(`syncHistory: Synchronized ${historyToSet.length} states, current index: ${finalIndex}`);
|
|
366
|
+
// setCurrentState(historyToSet[finalIndex].state);
|
|
367
|
+
// }
|
|
368
|
+
// if (devWarningsRef.current) {
|
|
369
|
+
// console.log(`syncHistory: Synchronized ${historyToSet.length} states, current index: ${finalIndex}`);
|
|
370
|
+
// }
|
|
371
|
+
}, []);
|
|
372
|
+
// Configuration setters
|
|
373
|
+
const setMaxSize = useCallback((size) => {
|
|
374
|
+
maxSizeRef.current = size;
|
|
375
|
+
if (size !== 'unlimited') {
|
|
376
|
+
enforceMaxSize();
|
|
377
|
+
}
|
|
378
|
+
}, [enforceMaxSize]);
|
|
379
|
+
const setBatchMode = useCallback(async (enabled, forceHistory = false) => {
|
|
380
|
+
const wasInBatch = batchModeRef.current;
|
|
381
|
+
console.log(`🔧 setBatchMode called: enabled=${enabled}, wasInBatch=${wasInBatch}, currentIndex=${currentIndexRef.current}, historyLength=${history.length}`);
|
|
382
|
+
if (enabled && !wasInBatch) {
|
|
383
|
+
// Starting batch mode - save current state as batch start
|
|
384
|
+
batchModeRef.current = true;
|
|
385
|
+
batchStartIndexRef.current = currentIndexRef.current;
|
|
386
|
+
batchStartStateRef.current = currentStateRef.current;
|
|
387
|
+
console.log("🔧 setBatchMode(true): Starting batch mode", {
|
|
388
|
+
currentState: currentStateRef.current,
|
|
389
|
+
currentIndex: currentIndexRef.current,
|
|
390
|
+
batchStartState: currentStateRef.current
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
else if (!enabled && wasInBatch) {
|
|
394
|
+
// Guard against double execution
|
|
395
|
+
console.log("🔧 setBatchMode: Ending batch mode", batchModeProcessingRef.current);
|
|
396
|
+
if (batchModeProcessingRef.current) {
|
|
397
|
+
console.log("⚠️ setBatchMode(false) already processing, skipping duplicate call");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
console.log("🔧 setBatchMode: Ending batch mode", currentStateRef.current, `batchStartIndex=${batchStartIndexRef.current}`);
|
|
401
|
+
// Ending batch mode - commit final state to history
|
|
402
|
+
batchModeProcessingRef.current = true;
|
|
403
|
+
batchModeRef.current = false;
|
|
404
|
+
// Only add to history if state actually changed from batch start
|
|
405
|
+
const statesEqual = batchStartStateRef.current ?
|
|
406
|
+
compareAdjustmentStates(currentStateRef.current, batchStartStateRef.current) : true;
|
|
407
|
+
console.log("🔧 setBatchMode(false): Comparing states", {
|
|
408
|
+
currentState: currentStateRef.current,
|
|
409
|
+
batchStartState: batchStartStateRef.current,
|
|
410
|
+
statesEqual,
|
|
411
|
+
forceHistory,
|
|
412
|
+
willAddToHistory: batchStartStateRef.current && (!statesEqual || forceHistory)
|
|
413
|
+
});
|
|
414
|
+
if (batchStartStateRef.current && (!statesEqual || forceHistory)) {
|
|
415
|
+
// Generate a unique task ID for this history entry
|
|
416
|
+
const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
417
|
+
console.log('📋 Before SetHistory state for backend:', batchStartIndexRef.current, currentStateRef.current, history.length);
|
|
418
|
+
// Store variables for backend call BEFORE setHistory
|
|
419
|
+
let replaceFromTaskId;
|
|
420
|
+
const batchStartIndex = batchStartIndexRef.current ?? currentIndexRef.current;
|
|
421
|
+
const wasInMiddleOfHistory = batchStartIndex < history.length - 1;
|
|
422
|
+
if (wasInMiddleOfHistory) {
|
|
423
|
+
// Get the task_id from the original history entry to use as replace_from
|
|
424
|
+
const currentHistoryEntry = history[batchStartIndex];
|
|
425
|
+
replaceFromTaskId = currentHistoryEntry?.taskId;
|
|
426
|
+
console.log(`📍 Was in middle of history (index ${batchStartIndex}), using replace_from: ${replaceFromTaskId}`);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
console.log(`📍 At latest history (index ${batchStartIndex}), no replace_from needed`);
|
|
430
|
+
}
|
|
431
|
+
setHistory(prevHistory => {
|
|
432
|
+
// Check if we were in the middle of history BEFORE any truncation
|
|
433
|
+
// Handle case where batchStartIndexRef.current might be null (e.g., after undo)
|
|
434
|
+
const batchStartIndex = batchStartIndexRef.current ?? currentIndexRef.current;
|
|
435
|
+
const truncatedHistory = prevHistory.slice(0, batchStartIndex + 1);
|
|
436
|
+
const newHistoryEntry = {
|
|
437
|
+
state: currentStateRef.current,
|
|
438
|
+
taskId: taskId
|
|
439
|
+
};
|
|
440
|
+
const newHistory = [...truncatedHistory, newHistoryEntry];
|
|
441
|
+
currentIndexRef.current = newHistory.length - 1;
|
|
442
|
+
return newHistory;
|
|
443
|
+
});
|
|
444
|
+
// Call controller to create history in backend - OUTSIDE of setHistory callback
|
|
445
|
+
if (internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
|
|
446
|
+
try {
|
|
447
|
+
console.log('🔄 Creating editor history in backend for batch mode end');
|
|
448
|
+
console.log('📋 Current state for backend:', batchStartIndexRef.current, currentStateRef.current, history.length);
|
|
449
|
+
const createEditorConfigPayload = {
|
|
450
|
+
gallery_id: internalOptions.currentImageId,
|
|
451
|
+
task_id: taskId,
|
|
452
|
+
color_adjustment: convertAdjustmentStateToColorAdjustment(currentStateRef.current),
|
|
453
|
+
...(replaceFromTaskId && { replace_from: replaceFromTaskId })
|
|
454
|
+
};
|
|
455
|
+
// Create editor config with current adjustments
|
|
456
|
+
internalOptions.controller.createEditorConfig(internalOptions.firebaseUid, createEditorConfigPayload).then(() => {
|
|
457
|
+
console.log('✅ Successfully created editor history in backend');
|
|
458
|
+
}).catch((error) => {
|
|
459
|
+
console.error('❌ Failed to create editor history in backend:', error);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
console.error('❌ Error calling controller.createEditorConfig:', error);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
if (internalOptions.devWarnings) {
|
|
468
|
+
console.warn('⚠️ Controller, firebaseUid, or currentImageId not provided - skipping backend history creation');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
if (forceHistory) {
|
|
474
|
+
console.log("🔧 setBatchMode: Force history requested but no batch start state");
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
console.log("🔧 setBatchMode: No changes since batch start, not adding to history");
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
batchStartIndexRef.current = null;
|
|
481
|
+
batchStartStateRef.current = null;
|
|
482
|
+
batchModeProcessingRef.current = false; // Reset processing flag
|
|
483
|
+
}
|
|
484
|
+
}, [internalOptions, history]);
|
|
485
|
+
// Sync history from backend using getEditorHistory
|
|
486
|
+
const syncFromBackend = useCallback(async () => {
|
|
487
|
+
if (!internalOptions.controller || !internalOptions.firebaseUid || !internalOptions.currentImageId) {
|
|
488
|
+
console.warn('⚠️ syncFromBackend: Controller, firebaseUid, or currentImageId not provided - cannot sync from backend');
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
try {
|
|
492
|
+
console.log('🔄 Syncing history from backend using getEditorHistory');
|
|
493
|
+
const historyResponse = await internalOptions.controller.getEditorHistory(internalOptions.firebaseUid, internalOptions.currentImageId);
|
|
494
|
+
// Sort history by timestamp (oldest first) before processing
|
|
495
|
+
const sortedHistory = [...historyResponse.history].sort((a, b) => {
|
|
496
|
+
const timeA = new Date(a.log.created_at).getTime();
|
|
497
|
+
const timeB = new Date(b.log.created_at).getTime();
|
|
498
|
+
return timeA - timeB; // Ascending order (oldest first)
|
|
499
|
+
});
|
|
500
|
+
// Convert backend history to AdjustmentState format with taskIds
|
|
501
|
+
const backendHistoryEntries = sortedHistory.map((entry) => ({
|
|
502
|
+
state: convertColorAdjustmentToAdjustmentState(entry.editor_config.color_adjustment),
|
|
503
|
+
taskId: entry.task_id
|
|
504
|
+
}));
|
|
505
|
+
// Find the index of the current task
|
|
506
|
+
let currentTaskIndex = -1;
|
|
507
|
+
if (historyResponse.current_task_id) {
|
|
508
|
+
currentTaskIndex = backendHistoryEntries.findIndex(entry => entry.taskId === historyResponse.current_task_id);
|
|
509
|
+
}
|
|
510
|
+
// If current_task_id is not found in history, default to the last entry
|
|
511
|
+
if (currentTaskIndex === -1 && backendHistoryEntries.length > 0) {
|
|
512
|
+
currentTaskIndex = backendHistoryEntries.length - 1;
|
|
513
|
+
console.warn(`⚠️ Current task ID "${historyResponse.current_task_id}" not found in history, defaulting to last entry`);
|
|
514
|
+
}
|
|
515
|
+
// Handle empty history case
|
|
516
|
+
if (backendHistoryEntries.length === 0) {
|
|
517
|
+
console.log('📝 Backend history is empty, keeping current local history');
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
// Exit batch mode if active
|
|
521
|
+
if (batchModeRef.current) {
|
|
522
|
+
batchModeRef.current = false;
|
|
523
|
+
batchStartIndexRef.current = null;
|
|
524
|
+
batchStartStateRef.current = null;
|
|
525
|
+
}
|
|
526
|
+
// Update history state with backend data
|
|
527
|
+
setHistory(backendHistoryEntries);
|
|
528
|
+
currentIndexRef.current = Math.max(0, currentTaskIndex);
|
|
529
|
+
console.log(`📍 Setting current index to: ${Math.max(0, currentTaskIndex)} (task_id: ${historyResponse.current_task_id})`);
|
|
530
|
+
currentStateRef.current = backendHistoryEntries[Math.max(0, currentTaskIndex)].state;
|
|
531
|
+
setCurrentState(backendHistoryEntries[Math.max(0, currentTaskIndex)].state);
|
|
532
|
+
console.log(`✅ Successfully synced ${backendHistoryEntries.length} history entries from backend`);
|
|
533
|
+
console.log(`📍 Set current index to: ${Math.max(0, currentTaskIndex)} (task_id: ${historyResponse.current_task_id})`);
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
console.error('❌ Failed to sync history from backend:', error);
|
|
537
|
+
throw error;
|
|
538
|
+
}
|
|
539
|
+
}, [internalOptions]);
|
|
540
|
+
// History info object
|
|
541
|
+
const historyInfo = useMemo(() => ({
|
|
542
|
+
canUndo: currentIndex > 0,
|
|
543
|
+
canRedo: currentIndex < history.length - 1,
|
|
544
|
+
currentIndex: currentIndex,
|
|
545
|
+
totalStates: history.length,
|
|
546
|
+
historySize: getMemoryUsage(),
|
|
547
|
+
isBatchMode: batchModeRef.current
|
|
548
|
+
}), [history.length, getMemoryUsage, currentIndex]);
|
|
549
|
+
// Actions object - stabilized with useMemo
|
|
550
|
+
const actions = useMemo(() => ({
|
|
551
|
+
pushState,
|
|
552
|
+
undo,
|
|
553
|
+
redo,
|
|
554
|
+
jumpToIndex,
|
|
555
|
+
clearHistory,
|
|
556
|
+
getHistory,
|
|
557
|
+
trimHistory,
|
|
558
|
+
syncFromBackend
|
|
559
|
+
}), [pushState, undo, redo, jumpToIndex, clearHistory, getHistory, trimHistory, syncFromBackend]);
|
|
560
|
+
// Config object - stabilized with useMemo
|
|
561
|
+
const config = useMemo(() => ({
|
|
562
|
+
setMaxSize,
|
|
563
|
+
setBatchMode,
|
|
564
|
+
getMemoryUsage
|
|
565
|
+
}), [setMaxSize, setBatchMode, getMemoryUsage]);
|
|
566
|
+
// Apply max size enforcement when history changes
|
|
567
|
+
useEffect(() => {
|
|
568
|
+
enforceMaxSize();
|
|
569
|
+
checkPerformance();
|
|
570
|
+
}, [enforceMaxSize, checkPerformance]);
|
|
571
|
+
return {
|
|
572
|
+
currentState,
|
|
573
|
+
currentIndex,
|
|
574
|
+
historyInfo,
|
|
575
|
+
actions,
|
|
576
|
+
config
|
|
577
|
+
};
|
|
578
|
+
}
|