@yogiswara/honcho-editor-ui 2.5.9 → 2.6.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.
Files changed (50) hide show
  1. package/dist/components/editor/HBulkPreset.js +12 -2
  2. package/dist/hooks/demo/HonchoEditorBulkDemo.d.ts +3 -0
  3. package/dist/hooks/demo/HonchoEditorBulkDemo.js +228 -0
  4. package/dist/hooks/demo/HonchoEditorSingleCleanDemo.d.ts +3 -0
  5. package/dist/hooks/demo/HonchoEditorSingleCleanDemo.js +354 -0
  6. package/dist/hooks/demo/index.d.ts +2 -0
  7. package/dist/hooks/demo/index.js +2 -0
  8. package/dist/hooks/editor/type.d.ts +71 -0
  9. package/dist/hooks/editor/useHonchoEditorBulk.d.ts +12 -12
  10. package/dist/hooks/editor/useHonchoEditorBulk.js +155 -42
  11. package/dist/hooks/editor/useHonchoEditorSingle.d.ts +43 -0
  12. package/dist/hooks/editor/useHonchoEditorSingle.js +158 -0
  13. package/dist/hooks/useAdjustmentHistory.d.ts +9 -5
  14. package/dist/hooks/useAdjustmentHistory.js +187 -31
  15. package/dist/hooks/useAdjustmentHistoryBatch.d.ts +18 -1
  16. package/dist/hooks/useAdjustmentHistoryBatch.js +627 -201
  17. package/dist/hooks/useGallerySwipe.d.ts +1 -1
  18. package/dist/hooks/usePaging.d.ts +89 -0
  19. package/dist/hooks/usePaging.js +211 -0
  20. package/dist/hooks/usePreset.d.ts +1 -1
  21. package/dist/hooks/usePreset.js +35 -35
  22. package/dist/index.d.ts +4 -3
  23. package/dist/index.js +3 -1
  24. package/dist/lib/context/EditorContext.d.ts +10 -0
  25. package/dist/lib/context/EditorContext.js +4 -2
  26. package/dist/lib/hooks/useEditorHeadless.d.ts +18 -2
  27. package/dist/lib/hooks/useEditorHeadless.js +142 -63
  28. package/dist/utils/adjustment.d.ts +2 -1
  29. package/dist/utils/adjustment.js +16 -0
  30. package/dist/utils/imageLoader.d.ts +11 -0
  31. package/dist/utils/imageLoader.js +53 -0
  32. package/package.json +1 -1
  33. package/dist/components/editor/GalleryAlbum/SimplifiedAlbumGallery.d.ts +0 -17
  34. package/dist/components/editor/GalleryAlbum/SimplifiedAlbumGallery.js +0 -14
  35. package/dist/components/editor/GalleryAlbum/SimplifiedImageItem.d.ts +0 -8
  36. package/dist/components/editor/GalleryAlbum/SimplifiedImageItem.js +0 -30
  37. package/dist/components/editor/HImageEditorPage.d.ts +0 -1
  38. package/dist/components/editor/HImageEditorPage.js +0 -187
  39. package/dist/hooks/__tests__/useGallerySwipe.test.d.ts +0 -0
  40. package/dist/hooks/__tests__/useGallerySwipe.test.js +0 -619
  41. package/dist/hooks/editor/useHonchoEditor.d.ts +0 -203
  42. package/dist/hooks/editor/useHonchoEditor.js +0 -716
  43. package/dist/hooks/useAdjustmentHistory.demo.d.ts +0 -8
  44. package/dist/hooks/useAdjustmentHistory.demo.js +0 -106
  45. package/dist/hooks/useAdjustmentHistory.example.d.ts +0 -38
  46. package/dist/hooks/useAdjustmentHistory.example.js +0 -182
  47. package/dist/hooks/useAdjustmentHistory.syncDemo.d.ts +0 -8
  48. package/dist/hooks/useAdjustmentHistory.syncDemo.js +0 -180
  49. package/dist/hooks/useGallerySwipe.example.d.ts +0 -24
  50. package/dist/hooks/useGallerySwipe.example.js +0 -184
@@ -1,4 +1,23 @@
1
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
+ };
2
21
  /**
3
22
  * Compare two AdjustmentState objects for equality
4
23
  * Uses JSON.stringify for deep comparison of all adjustment values
@@ -36,28 +55,38 @@ const compareAdjustmentStates = (a, b) => {
36
55
  * - Automatic AdjustmentState comparison
37
56
  *
38
57
  * @param initialState - The initial AdjustmentState value
58
+ * @param controller - Controller for backend operations (optional)
59
+ * @param firebaseUid - Firebase UID for backend operations (optional)
60
+ * @param currentImageId - Current image ID for backend operations (optional)
39
61
  * @param options - Configuration options for history behavior
40
62
  * @returns Object with current state, history info, actions, and config
41
63
  */
42
- export function useAdjustmentHistory(initialState, options = {}) {
64
+ export function useAdjustmentHistory(initialState, controller, firebaseUid, currentImageId, options = {}) {
43
65
  // Internal stabilization - prevent re-renders from options object recreation
44
66
  const internalOptions = useMemo(() => ({
45
67
  maxSize: options.maxSize ?? 'unlimited',
46
68
  enableBatching: options.enableBatching ?? false,
47
- devWarnings: options.devWarnings ?? false
69
+ devWarnings: options.devWarnings ?? false,
70
+ controller: controller,
71
+ firebaseUid: firebaseUid,
72
+ currentImageId: currentImageId
48
73
  }), [
49
74
  options.maxSize,
50
75
  options.enableBatching,
51
- options.devWarnings
76
+ options.devWarnings,
77
+ controller,
78
+ firebaseUid,
79
+ currentImageId
52
80
  ]);
53
81
  // Core state management
54
- const [history, setHistory] = useState([initialState]);
82
+ const [history, setHistory] = useState([{ state: initialState }]);
55
83
  const [currentIndex, setCurrentIndex] = useState(0);
56
84
  const [currentState, setCurrentState] = useState(initialState);
57
85
  // Batch mode state - ref to avoid triggering effects
58
86
  const batchModeRef = useRef(internalOptions.enableBatching);
59
87
  const batchStartIndexRef = useRef(null);
60
88
  const batchStartStateRef = useRef(null);
89
+ const batchModeProcessingRef = useRef(false); // Guard against double execution
61
90
  // Configuration refs - prevent re-renders when config changes
62
91
  const maxSizeRef = useRef(internalOptions.maxSize);
63
92
  const devWarningsRef = useRef(internalOptions.devWarnings);
@@ -70,9 +99,9 @@ export function useAdjustmentHistory(initialState, options = {}) {
70
99
  // Sync currentState with history when not in batch mode
71
100
  useEffect(() => {
72
101
  if (!batchModeRef.current) {
73
- setCurrentState(history[currentIndex]);
102
+ setCurrentState(history[currentIndex]?.state || initialState);
74
103
  }
75
- }, [history, currentIndex]);
104
+ }, [history, currentIndex, initialState]);
76
105
  const getMemoryUsage = useCallback(() => {
77
106
  try {
78
107
  const historyString = JSON.stringify(history);
@@ -126,10 +155,6 @@ export function useAdjustmentHistory(initialState, options = {}) {
126
155
  }, [history.length, trimHistoryToSize]);
127
156
  // Push new state to history
128
157
  const pushState = useCallback((newState) => {
129
- // Skip if state hasn't changed
130
- if (compareAdjustmentStates(newState, currentState)) {
131
- return;
132
- }
133
158
  // Always update currentState immediately for smooth UI
134
159
  setCurrentState(newState);
135
160
  if (batchModeRef.current) {
@@ -140,17 +165,30 @@ export function useAdjustmentHistory(initialState, options = {}) {
140
165
  // Normal mode: Update history immediately
141
166
  setHistory(prevHistory => {
142
167
  const truncatedHistory = prevHistory.slice(0, currentIndex + 1);
143
- const newHistory = [...truncatedHistory, newState];
168
+ const newHistory = [...truncatedHistory, { state: newState }];
144
169
  setCurrentIndex(newHistory.length - 1);
145
170
  return newHistory;
146
171
  });
147
172
  }, [currentState, currentIndex]);
148
173
  // Undo to previous state
149
- const undo = useCallback(() => {
174
+ const undo = useCallback(async () => {
150
175
  if (currentIndex > 0) {
151
176
  const newIndex = currentIndex - 1;
177
+ const historyEntry = history[newIndex];
178
+ const newState = historyEntry.state;
152
179
  setCurrentIndex(newIndex);
153
- setCurrentState(history[newIndex]);
180
+ setCurrentState(newState);
181
+ // Call controller to set history index in backend if taskId exists
182
+ if (historyEntry.taskId && internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
183
+ try {
184
+ console.log(`🔙 Undo: Setting history index to taskId: ${historyEntry.taskId}`);
185
+ await internalOptions.controller.setHistoryIndex(internalOptions.firebaseUid, internalOptions.currentImageId, historyEntry.taskId);
186
+ console.log('✅ Successfully set history index for undo');
187
+ }
188
+ catch (error) {
189
+ console.error('❌ Failed to set history index for undo:', error);
190
+ }
191
+ }
154
192
  // Exit batch mode when undoing
155
193
  if (batchModeRef.current) {
156
194
  batchModeRef.current = false;
@@ -158,18 +196,31 @@ export function useAdjustmentHistory(initialState, options = {}) {
158
196
  batchStartStateRef.current = null;
159
197
  }
160
198
  }
161
- }, [currentIndex, history]);
199
+ }, [currentIndex, history, internalOptions]);
162
200
  // Redo to next state
163
- const redo = useCallback(() => {
201
+ const redo = useCallback(async () => {
164
202
  if (currentIndex < history.length - 1) {
165
203
  const newIndex = currentIndex + 1;
204
+ const historyEntry = history[newIndex];
205
+ const newState = historyEntry.state;
166
206
  setCurrentIndex(newIndex);
167
- setCurrentState(history[newIndex]);
207
+ setCurrentState(newState);
208
+ // Call controller to set history index in backend if taskId exists
209
+ if (historyEntry.taskId && internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
210
+ try {
211
+ console.log(`🔄 Redo: Setting history index to taskId: ${historyEntry.taskId}`);
212
+ await internalOptions.controller.setHistoryIndex(internalOptions.firebaseUid, internalOptions.currentImageId, historyEntry.taskId);
213
+ console.log('✅ Successfully set history index for redo');
214
+ }
215
+ catch (error) {
216
+ console.error('❌ Failed to set history index for redo:', error);
217
+ }
218
+ }
168
219
  }
169
- }, [currentIndex, history]);
220
+ }, [currentIndex, history, internalOptions]);
170
221
  // Reset history with new initial state
171
222
  const reset = useCallback((newInitialState) => {
172
- setHistory([newInitialState]);
223
+ setHistory([{ state: newInitialState }]);
173
224
  setCurrentIndex(0);
174
225
  setCurrentState(newInitialState);
175
226
  batchModeRef.current = internalOptions.enableBatching;
@@ -179,8 +230,10 @@ export function useAdjustmentHistory(initialState, options = {}) {
179
230
  // Jump to specific index in history
180
231
  const jumpToIndex = useCallback((index) => {
181
232
  if (index >= 0 && index < history.length) {
233
+ const historyEntry = history[index];
234
+ const newState = historyEntry.state;
182
235
  setCurrentIndex(index);
183
- setCurrentState(history[index]);
236
+ setCurrentState(newState);
184
237
  // Exit batch mode when jumping
185
238
  if (batchModeRef.current) {
186
239
  batchModeRef.current = false;
@@ -191,7 +244,7 @@ export function useAdjustmentHistory(initialState, options = {}) {
191
244
  }, [history]);
192
245
  // Clear all history and start fresh
193
246
  const clearHistory = useCallback(() => {
194
- setHistory([currentState]);
247
+ setHistory([{ state: currentState }]);
195
248
  setCurrentIndex(0);
196
249
  batchModeRef.current = internalOptions.enableBatching;
197
250
  batchStartIndexRef.current = null;
@@ -199,7 +252,7 @@ export function useAdjustmentHistory(initialState, options = {}) {
199
252
  }, [currentState, internalOptions.enableBatching]);
200
253
  // Get copy of entire history
201
254
  const getHistory = useCallback(() => {
202
- return [...history];
255
+ return history.map(entry => entry.state);
203
256
  }, [history]);
204
257
  // Manually trim history
205
258
  const trimHistory = useCallback((keepLast) => {
@@ -239,8 +292,8 @@ export function useAdjustmentHistory(initialState, options = {}) {
239
292
  // Determine target index
240
293
  let finalIndex = targetIndex ?? newHistory.length - 1; // Default to last item
241
294
  finalIndex = Math.max(0, Math.min(finalIndex, newHistory.length - 1)); // Clamp to valid range
242
- // Create a copy of the new history to avoid mutations
243
- const historyToSet = newHistory.map(state => ({ ...state }));
295
+ // Create a copy of the new history to avoid mutations and convert to HistoryEntry format
296
+ const historyToSet = newHistory.map(state => ({ state: { ...state } }));
244
297
  // Apply max size limit if needed
245
298
  if (maxSizeRef.current !== 'unlimited' && historyToSet.length > maxSizeRef.current) {
246
299
  const trimAmount = historyToSet.length - maxSizeRef.current;
@@ -249,7 +302,7 @@ export function useAdjustmentHistory(initialState, options = {}) {
249
302
  finalIndex = Math.max(0, finalIndex - trimAmount);
250
303
  setHistory(trimmedHistory);
251
304
  setCurrentIndex(finalIndex);
252
- setCurrentState(trimmedHistory[finalIndex]);
305
+ setCurrentState(trimmedHistory[finalIndex].state);
253
306
  if (devWarningsRef.current) {
254
307
  console.warn(`syncHistory: Trimmed ${trimAmount} entries to respect maxSize of ${maxSizeRef.current}`);
255
308
  }
@@ -258,7 +311,7 @@ export function useAdjustmentHistory(initialState, options = {}) {
258
311
  // Set history as-is
259
312
  setHistory(historyToSet);
260
313
  setCurrentIndex(finalIndex);
261
- setCurrentState(historyToSet[finalIndex]);
314
+ setCurrentState(historyToSet[finalIndex].state);
262
315
  }
263
316
  if (devWarningsRef.current) {
264
317
  console.log(`syncHistory: Synchronized ${historyToSet.length} states, current index: ${finalIndex}`);
@@ -271,31 +324,88 @@ export function useAdjustmentHistory(initialState, options = {}) {
271
324
  enforceMaxSize();
272
325
  }
273
326
  }, [enforceMaxSize]);
274
- const setBatchMode = useCallback((enabled) => {
327
+ const setBatchMode = useCallback(async (enabled) => {
275
328
  const wasInBatch = batchModeRef.current;
329
+ console.log(`🔧 setBatchMode called: enabled=${enabled}, wasInBatch=${wasInBatch}, currentIndex=${currentIndex}, historyLength=${history.length}`);
276
330
  if (enabled && !wasInBatch) {
277
331
  // Starting batch mode - save current state as batch start
278
332
  batchModeRef.current = true;
279
333
  batchStartIndexRef.current = currentIndex;
280
334
  batchStartStateRef.current = currentState;
335
+ console.log("Current start batch ", currentState, `batchStartIndex=${currentIndex}`);
281
336
  }
282
337
  else if (!enabled && wasInBatch) {
338
+ // Guard against double execution
339
+ if (batchModeProcessingRef.current) {
340
+ console.log("⚠️ setBatchMode(false) already processing, skipping duplicate call");
341
+ return;
342
+ }
283
343
  // Ending batch mode - commit final state to history
344
+ batchModeProcessingRef.current = true;
284
345
  batchModeRef.current = false;
285
346
  // Only add to history if state actually changed from batch start
286
347
  if (batchStartStateRef.current &&
287
348
  !compareAdjustmentStates(currentState, batchStartStateRef.current)) {
349
+ // Generate a unique task ID for this history entry
350
+ const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
288
351
  setHistory(prevHistory => {
289
- const truncatedHistory = prevHistory.slice(0, batchStartIndexRef.current + 1);
290
- const newHistory = [...truncatedHistory, currentState];
352
+ // Check if we were in the middle of history BEFORE any truncation
353
+ // Handle case where batchStartIndexRef.current might be null (e.g., after undo)
354
+ const batchStartIndex = batchStartIndexRef.current ?? currentIndex;
355
+ const wasInMiddleOfHistory = batchStartIndex < prevHistory.length - 1;
356
+ const truncatedHistory = prevHistory.slice(0, batchStartIndex + 1);
357
+ const newHistoryEntry = {
358
+ state: currentState,
359
+ taskId: taskId
360
+ };
361
+ const newHistory = [...truncatedHistory, newHistoryEntry];
291
362
  setCurrentIndex(newHistory.length - 1);
292
363
  return newHistory;
293
364
  });
365
+ // Call controller to create history in backend - OUTSIDE of setHistory callback
366
+ if (internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
367
+ try {
368
+ console.log('🔄 Creating editor history in backend for batch mode end');
369
+ let replaceFromTaskId;
370
+ const batchStartIndex = batchStartIndexRef.current ?? currentIndex;
371
+ const wasInMiddleOfHistory = batchStartIndex < history.length - 1;
372
+ if (wasInMiddleOfHistory) {
373
+ // Get the task_id from the original history entry to use as replace_from
374
+ const currentHistoryEntry = history[batchStartIndex];
375
+ replaceFromTaskId = currentHistoryEntry?.taskId;
376
+ console.log(`📍 Was in middle of history (index ${batchStartIndex}), using replace_from: ${replaceFromTaskId}`);
377
+ }
378
+ else {
379
+ console.log(`📍 At latest history (index ${batchStartIndex}), no replace_from needed`);
380
+ }
381
+ const createEditorConfigPayload = {
382
+ gallery_id: internalOptions.currentImageId,
383
+ task_id: taskId,
384
+ color_adjustment: convertAdjustmentStateToColorAdjustment(currentState),
385
+ ...(replaceFromTaskId && { replace_from: replaceFromTaskId })
386
+ };
387
+ // Create editor config with current adjustments
388
+ internalOptions.controller.createEditorConfig(internalOptions.firebaseUid, createEditorConfigPayload).then(() => {
389
+ console.log('✅ Successfully created editor history in backend');
390
+ }).catch((error) => {
391
+ console.error('❌ Failed to create editor history in backend:', error);
392
+ });
393
+ }
394
+ catch (error) {
395
+ console.error('❌ Error calling controller.createEditorConfig:', error);
396
+ }
397
+ }
398
+ else {
399
+ if (internalOptions.devWarnings) {
400
+ console.warn('⚠️ Controller, firebaseUid, or currentImageId not provided - skipping backend history creation');
401
+ }
402
+ }
294
403
  }
295
404
  batchStartIndexRef.current = null;
296
405
  batchStartStateRef.current = null;
406
+ batchModeProcessingRef.current = false; // Reset processing flag
297
407
  }
298
- }, [currentIndex, currentState]);
408
+ }, [currentIndex, currentState, internalOptions]);
299
409
  // History info object
300
410
  const historyInfo = useMemo(() => ({
301
411
  canUndo: currentIndex > 0,
@@ -317,12 +427,58 @@ export function useAdjustmentHistory(initialState, options = {}) {
317
427
  trimHistory,
318
428
  syncHistory
319
429
  }), [pushState, undo, redo, reset, jumpToIndex, clearHistory, getHistory, trimHistory, syncHistory]);
430
+ // Force sync current state to backend
431
+ const syncToBackend = useCallback(async () => {
432
+ if (internalOptions.controller && internalOptions.firebaseUid && internalOptions.currentImageId) {
433
+ try {
434
+ console.log('🔄 Force syncing current state to backend');
435
+ // Check if we're in middle of history (not at the latest)
436
+ const wasInMiddleOfHistory = currentIndex < history.length - 1;
437
+ let replaceFromTaskId;
438
+ if (wasInMiddleOfHistory) {
439
+ // Get the task_id from the current history entry to use as replace_from
440
+ const currentHistoryEntry = history[currentIndex];
441
+ replaceFromTaskId = currentHistoryEntry?.taskId;
442
+ console.log(`📍 In middle of history (index ${currentIndex}), using replace_from: ${replaceFromTaskId}`);
443
+ }
444
+ // Generate a unique task ID for this sync
445
+ const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
446
+ const createEditorConfigPayload = {
447
+ gallery_id: internalOptions.currentImageId,
448
+ task_id: taskId,
449
+ color_adjustment: convertAdjustmentStateToColorAdjustment(currentState),
450
+ ...(replaceFromTaskId && { replace_from: replaceFromTaskId })
451
+ };
452
+ await internalOptions.controller.createEditorConfig(internalOptions.firebaseUid, createEditorConfigPayload);
453
+ console.log('✅ Successfully synced current state to backend');
454
+ // Update the current history entry with the new task ID
455
+ setHistory(prevHistory => {
456
+ const newHistory = [...prevHistory];
457
+ if (newHistory[currentIndex]) {
458
+ newHistory[currentIndex] = {
459
+ ...newHistory[currentIndex],
460
+ taskId: taskId
461
+ };
462
+ }
463
+ return newHistory;
464
+ });
465
+ }
466
+ catch (error) {
467
+ console.error('❌ Failed to sync current state to backend:', error);
468
+ throw error;
469
+ }
470
+ }
471
+ else {
472
+ console.warn('⚠️ Controller, firebaseUid, or currentImageId not provided - skipping backend sync');
473
+ }
474
+ }, [currentState, currentIndex, history, internalOptions]);
320
475
  // Config object - stabilized with useMemo
321
476
  const config = useMemo(() => ({
322
477
  setMaxSize,
323
478
  setBatchMode,
324
- getMemoryUsage
325
- }), [setMaxSize, setBatchMode, getMemoryUsage]);
479
+ getMemoryUsage,
480
+ syncToBackend
481
+ }), [setMaxSize, setBatchMode, getMemoryUsage, syncToBackend]);
326
482
  // Apply max size enforcement when history changes
327
483
  useEffect(() => {
328
484
  enforceMaxSize();
@@ -1,4 +1,13 @@
1
- import { AdjustmentState } from './editor/useHonchoEditor';
1
+ import { AdjustmentState, Controller } from './editor/type';
2
+ export interface HistoryAdjustmentBatch {
3
+ imageId: string;
4
+ currentHistoryEntryId: string;
5
+ history: HistoryAdjustmentEntry[];
6
+ }
7
+ export interface HistoryAdjustmentEntry {
8
+ id: string;
9
+ adjustment: AdjustmentState;
10
+ }
2
11
  /**
3
12
  * Configuration for image with adjustment state
4
13
  */
@@ -34,6 +43,12 @@ interface BatchHistoryOptions {
34
43
  devWarnings?: boolean;
35
44
  /** Default adjustment state for new images */
36
45
  defaultAdjustmentState?: Partial<AdjustmentState>;
46
+ /** Controller for backend operations */
47
+ controller?: Controller;
48
+ /** Firebase UID for backend operations */
49
+ firebaseUid?: string;
50
+ /** Event ID for backend operations */
51
+ eventId?: string;
37
52
  }
38
53
  /**
39
54
  * Information about the current batch history state
@@ -60,6 +75,8 @@ export interface BatchHistoryInfo {
60
75
  export interface BatchHistoryActions {
61
76
  /** Apply adjustment deltas to selected images */
62
77
  adjustSelected: (delta: Partial<AdjustmentState>) => void;
78
+ /** Apply preset values directly to selected images (preserves history) */
79
+ adjustSelectedWithPreset: (presetAdjustments: AdjustmentState) => void;
63
80
  /** Undo last changes to selected images */
64
81
  undo: () => void;
65
82
  /** Redo next changes to selected images */