@yogiswara/honcho-editor-ui 2.7.15 → 2.7.16

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.
@@ -3,6 +3,8 @@ interface Props {
3
3
  anchorEl?: null | HTMLElement;
4
4
  valueSelect?: string;
5
5
  isPasteEnabled?: boolean;
6
+ canUndo: boolean;
7
+ canRedo: boolean;
6
8
  onBack?: () => void;
7
9
  onUndo?: () => void;
8
10
  onRedo?: () => void;
@@ -22,7 +22,7 @@ export default function HHeaderEditor(props) {
22
22
  transform: 'scale(0.92)',
23
23
  },
24
24
  transition: 'transform 0.1s ease-in-out',
25
- }, children: _jsx(CardMedia, { title: "back", src: "svg/Back.svg", component: "img" }) }) }), _jsxs(Stack, { direction: "row", justifyContent: "flex-end", alignItems: "center", sx: { pt: "20px", pb: "12px" }, spacing: 0.1, children: [_jsx(Button, { variant: "text", onClick: props.onSelectButton, sx: { color: colors.outlineVariant }, children: props.valueSelect }), _jsx(IconButton, { "aria-label": "undo", onClick: props.onUndo, sx: { color: colors.outlineVariant }, children: _jsx(CardMedia, { component: "img", image: "/v1/svg/undo-editor.svg" }) }), _jsx(IconButton, { "aria-label": "redo", onClick: props.onRedo, sx: { color: colors.outlineVariant }, children: _jsx(CardMedia, { component: "img", image: "/v1/svg/redo-editor.svg" }) }), _jsx(IconButton, { "aria-label": "option", onClick: props.onMenuClick, "aria-controls": open ? 'options-menu' : undefined, "aria-haspopup": "true", "aria-expanded": open ? 'true' : undefined, children: _jsx(CardMedia, { component: "img", image: "/v1/svg/dots-editor.svg" }) }), _jsxs(Menu, { id: "options-menu", anchorEl: props.anchorEl, open: open, onClose: props.onMenuClose, transformOrigin: { horizontal: 'right', vertical: 'top' }, anchorOrigin: { horizontal: 'right', vertical: 'bottom' }, slotProps: {
25
+ }, children: _jsx(CardMedia, { title: "back", src: "svg/Back.svg", component: "img" }) }) }), _jsxs(Stack, { direction: "row", justifyContent: "flex-end", alignItems: "center", sx: { pt: "20px", pb: "12px" }, spacing: 0.1, children: [_jsx(Button, { variant: "text", onClick: props.onSelectButton, sx: { color: colors.outlineVariant }, children: props.valueSelect }), _jsx(IconButton, { "aria-label": "undo", onClick: props.onUndo, sx: { color: colors.outlineVariant }, disabled: !props.canUndo, children: _jsx(CardMedia, { component: "img", image: "/v1/svg/undo-editor.svg" }) }), _jsx(IconButton, { "aria-label": "redo", onClick: props.onRedo, sx: { color: colors.outlineVariant }, disabled: !props.canRedo, children: _jsx(CardMedia, { component: "img", image: "/v1/svg/redo-editor.svg" }) }), _jsx(IconButton, { "aria-label": "option", onClick: props.onMenuClick, "aria-controls": open ? 'options-menu' : undefined, "aria-haspopup": "true", "aria-expanded": open ? 'true' : undefined, children: _jsx(CardMedia, { component: "img", image: "/v1/svg/dots-editor.svg" }) }), _jsxs(Menu, { id: "options-menu", anchorEl: props.anchorEl, open: open, onClose: props.onMenuClose, transformOrigin: { horizontal: 'right', vertical: 'top' }, anchorOrigin: { horizontal: 'right', vertical: 'bottom' }, slotProps: {
26
26
  paper: {
27
27
  sx: {
28
28
  backgroundColor: colors.onBackground,
@@ -37,8 +37,6 @@ export interface HistoryActions {
37
37
  undo: () => void;
38
38
  /** Redo to next adjustment state */
39
39
  redo: () => void;
40
- /** Reset history with new initial adjustment state */
41
- reset: (newInitialState: AdjustmentState) => void;
42
40
  /** Jump to specific index in history */
43
41
  jumpToIndex: (index: number) => void;
44
42
  /** Clear all history and start fresh */
@@ -47,8 +45,6 @@ export interface HistoryActions {
47
45
  getHistory: () => AdjustmentState[];
48
46
  /** Trim history to specified size, keeping most recent entries */
49
47
  trimHistory: (keepLast: number) => void;
50
- /** Replace entire history with new list of adjustment states */
51
- syncHistory: (newHistory: AdjustmentState[], targetIndex?: number) => void;
52
48
  /** Sync history from backend using getEditorHistory */
53
49
  syncFromBackend: () => Promise<void>;
54
50
  }
@@ -69,6 +65,8 @@ export interface HistoryConfig {
69
65
  export interface UseAdjustmentHistoryReturn {
70
66
  /** Current adjustment state value */
71
67
  currentState: AdjustmentState;
68
+ /** Current index in history */
69
+ currentIndex: number;
72
70
  /** Information about history state */
73
71
  historyInfo: HistoryInfo;
74
72
  /** Available history actions */
@@ -99,8 +99,9 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
99
99
  ]);
100
100
  // Core state management
101
101
  const [history, setHistory] = useState([{ state: initialState, taskId: `initial_${Date.now()}` }]);
102
- const [currentIndex, setCurrentIndex] = useState(0);
103
102
  const [currentState, setCurrentState] = useState(initialState);
103
+ const currentIndexRef = useRef(0);
104
+ const currentStateRef = useRef(initialState);
104
105
  // Batch mode state - ref to avoid triggering effects
105
106
  const batchModeRef = useRef(internalOptions.enableBatching);
106
107
  const batchStartIndexRef = useRef(null);
@@ -109,6 +110,8 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
109
110
  // Configuration refs - prevent re-renders when config changes
110
111
  const maxSizeRef = useRef(internalOptions.maxSize);
111
112
  const devWarningsRef = useRef(internalOptions.devWarnings);
113
+ // Computed current index for return value (defined early for dependencies)
114
+ const currentIndex = currentIndexRef.current;
112
115
  // Performance monitoring
113
116
  const performanceRef = useRef({
114
117
  lastHistorySize: 1,
@@ -162,10 +165,9 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
162
165
  const startIndex = Math.max(0, prevHistory.length - size);
163
166
  const trimmedHistory = prevHistory.slice(startIndex);
164
167
  // Adjust current index to maintain relative position
165
- setCurrentIndex(prevIndex => {
166
- const adjustedIndex = prevIndex - startIndex;
167
- return Math.max(0, Math.min(adjustedIndex, trimmedHistory.length - 1));
168
- });
168
+ const prevIndex = currentIndexRef.current;
169
+ const adjustedIndex = prevIndex - startIndex;
170
+ currentIndexRef.current = Math.max(0, Math.min(adjustedIndex, trimmedHistory.length - 1));
169
171
  return trimmedHistory;
170
172
  });
171
173
  }, []);
@@ -181,6 +183,7 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
181
183
  // Push new state to history
182
184
  const pushState = useCallback((newState) => {
183
185
  // Always update currentState immediately for smooth UI
186
+ currentStateRef.current = newState;
184
187
  setCurrentState(newState);
185
188
  if (batchModeRef.current) {
186
189
  // In batch mode: Don't update history yet, just update UI state
@@ -189,22 +192,24 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
189
192
  return;
190
193
  }
191
194
  // Normal mode: Update history immediately
195
+ console.log(`[useAdjustmentHistory] Pushing new state to history:`, newState);
192
196
  setHistory(prevHistory => {
193
- const truncatedHistory = prevHistory.slice(0, currentIndex + 1);
197
+ const truncatedHistory = prevHistory.slice(0, currentIndexRef.current + 1);
194
198
  // const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
195
199
  // we put the taskId empty to check this state no need to save it
196
200
  const newHistory = [...truncatedHistory, { state: newState, taskId: "" }];
197
- setCurrentIndex(newHistory.length - 1);
201
+ currentIndexRef.current = newHistory.length - 1;
198
202
  return newHistory;
199
203
  });
200
- }, [currentState, currentIndex]);
204
+ }, []);
201
205
  // Undo to previous state
202
206
  const undo = useCallback(async () => {
203
- if (currentIndex > 0) {
204
- const newIndex = currentIndex - 1;
207
+ if (currentIndexRef.current > 0) {
208
+ const newIndex = currentIndexRef.current - 1;
205
209
  const historyEntry = history[newIndex];
206
210
  const newState = historyEntry.state;
207
- setCurrentIndex(newIndex);
211
+ currentIndexRef.current = newIndex;
212
+ currentStateRef.current = newState;
208
213
  setCurrentState(newState);
209
214
  console.log(`[useAdjustmentHistory] Undoing to index ${newIndex}:`, newState);
210
215
  // Call controller to set history index in backend if taskId exists
@@ -228,14 +233,15 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
228
233
  batchStartStateRef.current = null;
229
234
  }
230
235
  }
231
- }, [currentIndex, history, internalOptions]);
236
+ }, [history, internalOptions]);
232
237
  // Redo to next state
233
238
  const redo = useCallback(async () => {
234
- if (currentIndex < history.length - 1) {
235
- const newIndex = currentIndex + 1;
239
+ if (currentIndexRef.current < history.length - 1) {
240
+ const newIndex = currentIndexRef.current + 1;
236
241
  const historyEntry = history[newIndex];
237
242
  const newState = historyEntry.state;
238
- setCurrentIndex(newIndex);
243
+ currentIndexRef.current = newIndex;
244
+ currentStateRef.current = newState;
239
245
  setCurrentState(newState);
240
246
  console.log(`[useAdjustmentHistory] Redoing to index ${newIndex}:`, newState);
241
247
  // Call controller to set history index in backend if taskId exists
@@ -253,22 +259,24 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
253
259
  console.warn('🔄 Redo: No taskId available for setting history index', historyEntry.taskId, internalOptions.firebaseUid, internalOptions.currentImageId);
254
260
  }
255
261
  }
256
- }, [currentIndex, history, internalOptions]);
262
+ }, [history, internalOptions]);
257
263
  // Reset history with new initial state
258
- const reset = useCallback((newInitialState) => {
259
- setHistory([{ state: newInitialState, taskId: `reset_${Date.now()}` }]);
260
- setCurrentIndex(0);
261
- setCurrentState(newInitialState);
262
- batchModeRef.current = internalOptions.enableBatching;
263
- batchStartIndexRef.current = null;
264
- batchStartStateRef.current = null;
265
- }, [internalOptions.enableBatching]);
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]);
266
273
  // Jump to specific index in history
267
274
  const jumpToIndex = useCallback((index) => {
268
275
  if (index >= 0 && index < history.length) {
269
276
  const historyEntry = history[index];
270
277
  const newState = historyEntry.state;
271
- setCurrentIndex(index);
278
+ currentIndexRef.current = index;
279
+ currentStateRef.current = newState;
272
280
  setCurrentState(newState);
273
281
  // Exit batch mode when jumping
274
282
  if (batchModeRef.current) {
@@ -280,12 +288,13 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
280
288
  }, [history]);
281
289
  // Clear all history and start fresh
282
290
  const clearHistory = useCallback(() => {
283
- setHistory([{ state: currentState, taskId: `clear_${Date.now()}` }]);
284
- setCurrentIndex(0);
291
+ console.log("clearHistory called setHistory");
292
+ setHistory([{ state: currentStateRef.current, taskId: `clear_${Date.now()}` }]);
293
+ currentIndexRef.current = 0;
285
294
  batchModeRef.current = internalOptions.enableBatching;
286
295
  batchStartIndexRef.current = null;
287
296
  batchStartStateRef.current = null;
288
- }, [currentState, internalOptions.enableBatching]);
297
+ }, [internalOptions.enableBatching]);
289
298
  // Get copy of entire history
290
299
  const getHistory = useCallback(() => {
291
300
  return history.map(entry => entry.state);
@@ -369,16 +378,16 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
369
378
  }, [enforceMaxSize]);
370
379
  const setBatchMode = useCallback(async (enabled, forceHistory = false) => {
371
380
  const wasInBatch = batchModeRef.current;
372
- console.log(`🔧 setBatchMode called: enabled=${enabled}, wasInBatch=${wasInBatch}, currentIndex=${currentIndex}, historyLength=${history.length}`);
381
+ console.log(`🔧 setBatchMode called: enabled=${enabled}, wasInBatch=${wasInBatch}, currentIndex=${currentIndexRef.current}, historyLength=${history.length}`);
373
382
  if (enabled && !wasInBatch) {
374
383
  // Starting batch mode - save current state as batch start
375
384
  batchModeRef.current = true;
376
- batchStartIndexRef.current = currentIndex;
377
- batchStartStateRef.current = currentState;
385
+ batchStartIndexRef.current = currentIndexRef.current;
386
+ batchStartStateRef.current = currentStateRef.current;
378
387
  console.log("🔧 setBatchMode(true): Starting batch mode", {
379
- currentState,
380
- currentIndex,
381
- batchStartState: currentState
388
+ currentState: currentStateRef.current,
389
+ currentIndex: currentIndexRef.current,
390
+ batchStartState: currentStateRef.current
382
391
  });
383
392
  }
384
393
  else if (!enabled && wasInBatch) {
@@ -388,15 +397,15 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
388
397
  console.log("⚠️ setBatchMode(false) already processing, skipping duplicate call");
389
398
  return;
390
399
  }
391
- console.log("🔧 setBatchMode: Ending batch mode", currentState, `batchStartIndex=${batchStartIndexRef.current}`);
400
+ console.log("🔧 setBatchMode: Ending batch mode", currentStateRef.current, `batchStartIndex=${batchStartIndexRef.current}`);
392
401
  // Ending batch mode - commit final state to history
393
402
  batchModeProcessingRef.current = true;
394
403
  batchModeRef.current = false;
395
404
  // Only add to history if state actually changed from batch start
396
405
  const statesEqual = batchStartStateRef.current ?
397
- compareAdjustmentStates(currentState, batchStartStateRef.current) : true;
406
+ compareAdjustmentStates(currentStateRef.current, batchStartStateRef.current) : true;
398
407
  console.log("🔧 setBatchMode(false): Comparing states", {
399
- currentState,
408
+ currentState: currentStateRef.current,
400
409
  batchStartState: batchStartStateRef.current,
401
410
  statesEqual,
402
411
  forceHistory,
@@ -405,39 +414,42 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
405
414
  if (batchStartStateRef.current && (!statesEqual || forceHistory)) {
406
415
  // Generate a unique task ID for this history entry
407
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
+ }
408
431
  setHistory(prevHistory => {
409
432
  // Check if we were in the middle of history BEFORE any truncation
410
433
  // Handle case where batchStartIndexRef.current might be null (e.g., after undo)
411
- const batchStartIndex = batchStartIndexRef.current ?? currentIndex;
434
+ const batchStartIndex = batchStartIndexRef.current ?? currentIndexRef.current;
412
435
  const truncatedHistory = prevHistory.slice(0, batchStartIndex + 1);
413
436
  const newHistoryEntry = {
414
- state: currentState,
437
+ state: currentStateRef.current,
415
438
  taskId: taskId
416
439
  };
417
440
  const newHistory = [...truncatedHistory, newHistoryEntry];
418
- setCurrentIndex(newHistory.length - 1);
441
+ currentIndexRef.current = newHistory.length - 1;
419
442
  return newHistory;
420
443
  });
421
444
  // Call controller to create history in backend - OUTSIDE of setHistory callback
422
445
  if (internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
423
446
  try {
424
447
  console.log('🔄 Creating editor history in backend for batch mode end');
425
- let replaceFromTaskId;
426
- const batchStartIndex = batchStartIndexRef.current ?? currentIndex;
427
- const wasInMiddleOfHistory = batchStartIndex < history.length - 1;
428
- if (wasInMiddleOfHistory) {
429
- // Get the task_id from the original history entry to use as replace_from
430
- const currentHistoryEntry = history[batchStartIndex];
431
- replaceFromTaskId = currentHistoryEntry?.taskId;
432
- console.log(`📍 Was in middle of history (index ${batchStartIndex}), using replace_from: ${replaceFromTaskId}`);
433
- }
434
- else {
435
- console.log(`📍 At latest history (index ${batchStartIndex}), no replace_from needed`);
436
- }
448
+ console.log('📋 Current state for backend:', batchStartIndexRef.current, currentStateRef.current, history.length);
437
449
  const createEditorConfigPayload = {
438
450
  gallery_id: internalOptions.currentImageId,
439
451
  task_id: taskId,
440
- color_adjustment: convertAdjustmentStateToColorAdjustment(currentState),
452
+ color_adjustment: convertAdjustmentStateToColorAdjustment(currentStateRef.current),
441
453
  ...(replaceFromTaskId && { replace_from: replaceFromTaskId })
442
454
  };
443
455
  // Create editor config with current adjustments
@@ -469,7 +481,7 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
469
481
  batchStartStateRef.current = null;
470
482
  batchModeProcessingRef.current = false; // Reset processing flag
471
483
  }
472
- }, [currentIndex, currentState, internalOptions]);
484
+ }, [internalOptions, history]);
473
485
  // Sync history from backend using getEditorHistory
474
486
  const syncFromBackend = useCallback(async () => {
475
487
  if (!internalOptions.controller || !internalOptions.firebaseUid || !internalOptions.currentImageId) {
@@ -479,17 +491,12 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
479
491
  try {
480
492
  console.log('🔄 Syncing history from backend using getEditorHistory');
481
493
  const historyResponse = await internalOptions.controller.getEditorHistory(internalOptions.firebaseUid, internalOptions.currentImageId);
482
- console.log('📥 Received history response from backend:', historyResponse);
483
494
  // Sort history by timestamp (oldest first) before processing
484
495
  const sortedHistory = [...historyResponse.history].sort((a, b) => {
485
496
  const timeA = new Date(a.log.created_at).getTime();
486
497
  const timeB = new Date(b.log.created_at).getTime();
487
498
  return timeA - timeB; // Ascending order (oldest first)
488
499
  });
489
- console.log('📋 Sorted history by timestamp:', sortedHistory.map(entry => ({
490
- task_id: entry.task_id,
491
- timestamp: entry.log.created_at
492
- })));
493
500
  // Convert backend history to AdjustmentState format with taskIds
494
501
  const backendHistoryEntries = sortedHistory.map((entry) => ({
495
502
  state: convertColorAdjustmentToAdjustmentState(entry.editor_config.color_adjustment),
@@ -518,8 +525,9 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
518
525
  }
519
526
  // Update history state with backend data
520
527
  setHistory(backendHistoryEntries);
521
- setCurrentIndex(Math.max(0, currentTaskIndex));
528
+ currentIndexRef.current = Math.max(0, currentTaskIndex);
522
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;
523
531
  setCurrentState(backendHistoryEntries[Math.max(0, currentTaskIndex)].state);
524
532
  console.log(`✅ Successfully synced ${backendHistoryEntries.length} history entries from backend`);
525
533
  console.log(`📍 Set current index to: ${Math.max(0, currentTaskIndex)} (task_id: ${historyResponse.current_task_id})`);
@@ -531,26 +539,24 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
531
539
  }, [internalOptions]);
532
540
  // History info object
533
541
  const historyInfo = useMemo(() => ({
534
- canUndo: currentIndex > 0,
535
- canRedo: currentIndex < history.length - 1,
536
- currentIndex,
542
+ canUndo: currentIndexRef.current > 0,
543
+ canRedo: currentIndexRef.current < history.length - 1,
544
+ currentIndex: currentIndexRef.current,
537
545
  totalStates: history.length,
538
546
  historySize: getMemoryUsage(),
539
547
  isBatchMode: batchModeRef.current
540
- }), [currentIndex, history.length, getMemoryUsage]);
548
+ }), [history.length, getMemoryUsage]);
541
549
  // Actions object - stabilized with useMemo
542
550
  const actions = useMemo(() => ({
543
551
  pushState,
544
552
  undo,
545
553
  redo,
546
- reset,
547
554
  jumpToIndex,
548
555
  clearHistory,
549
556
  getHistory,
550
557
  trimHistory,
551
- syncHistory,
552
558
  syncFromBackend
553
- }), [pushState, undo, redo, reset, jumpToIndex, clearHistory, getHistory, trimHistory, syncHistory, syncFromBackend]);
559
+ }), [pushState, undo, redo, jumpToIndex, clearHistory, getHistory, trimHistory, syncFromBackend]);
554
560
  // Config object - stabilized with useMemo
555
561
  const config = useMemo(() => ({
556
562
  setMaxSize,
@@ -564,6 +570,7 @@ export function useAdjustmentHistory(initialState, controller, firebaseUid, curr
564
570
  }, [enforceMaxSize, checkPerformance]);
565
571
  return {
566
572
  currentState,
573
+ currentIndex,
567
574
  historyInfo,
568
575
  actions,
569
576
  config
@@ -125,7 +125,7 @@ export function useEditorHeadless(options = {}) {
125
125
  console.debug('HonchoEditor instance created successfully');
126
126
  }
127
127
  console.debug('Initializing HonchoEditor...');
128
- await editorRef.current.initialize(true);
128
+ await editorRef.current.initialize(false);
129
129
  console.debug('HonchoEditor initialized successfully');
130
130
  setIsReady(true);
131
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yogiswara/honcho-editor-ui",
3
- "version": "2.7.15",
3
+ "version": "2.7.16",
4
4
  "description": "A complete UI component library for the Honcho photo editor.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",