@yogiswara/honcho-editor-ui 2.2.7 → 2.3.1

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.
@@ -10,8 +10,9 @@ export interface Controller {
10
10
  syncConfig(firebaseUid: string): Promise<void>;
11
11
  handleBack(firebaseUid: string, imageID: string): void;
12
12
  getPresets(firebaseUid: string): Promise<Preset[]>;
13
- createPreset(firebaseUid: string, name: string, settings: AdjustmentState): Promise<Preset>;
13
+ createPreset(firebaseUid: string, name: string, settings: AdjustmentState): Promise<void>;
14
14
  deletePreset(firebaseUid: string, presetId: string): Promise<void>;
15
+ updatePreset(firebaseUid: string, data: Preset): Promise<void>;
15
16
  }
16
17
  export type AdjustmentState = {
17
18
  tempScore: number;
@@ -30,6 +31,19 @@ export type AdjustmentState = {
30
31
  export type Preset = {
31
32
  id: string;
32
33
  name: string;
34
+ is_default: boolean;
35
+ temperature: number;
36
+ tint: number;
37
+ saturation: number;
38
+ vibrance: number;
39
+ exposure: number;
40
+ contrast: number;
41
+ highlights: number;
42
+ shadows: number;
43
+ whites: number;
44
+ blacks: number;
45
+ clarity: number;
46
+ sharpness: number;
33
47
  };
34
48
  export type ImageItem = {
35
49
  id: string;
@@ -170,14 +184,11 @@ export declare function useHonchoEditor(controller: Controller, initImageId: str
170
184
  handleSelectDesktopPreset: (presetId: string) => void;
171
185
  handlePresetMenuClick: (event: React.MouseEvent<HTMLElement>, presetId: string) => void;
172
186
  handlePresetMenuClose: () => void;
173
- handleCreatePreset: () => Promise<void>;
174
187
  handleRemovePreset: () => void;
175
- handleDeletePreset: () => Promise<void>;
176
188
  handleOpenPresetModal: () => void;
177
189
  handleClosePresetModal: () => void;
178
190
  handleOpenPresetModalMobile: () => void;
179
191
  handleClosePresetModalMobile: () => void;
180
- handleCreatePresetMobile: () => Promise<void>;
181
192
  setPresetName: import("react").Dispatch<import("react").SetStateAction<string>>;
182
193
  handleNameChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
183
194
  isRenameModalOpen: boolean;
@@ -22,6 +22,7 @@ export function useHonchoEditor(controller, initImageId, firebaseUid) {
22
22
  const [isEditorReady, setIsEditorReady] = useState(false);
23
23
  const [isImageLoaded, setIsImageLoaded] = useState(false);
24
24
  const [zoomLevel, setZoomLevel] = useState(1);
25
+ const [presets, setPresets] = useState([]);
25
26
  // MARK: - Adjustment & History State
26
27
  const [copiedAdjustments, setCopiedAdjustments] = useState(null);
27
28
  const [copyColorChecks, setCopyColorChecks] = useState({ temperature: true, tint: true, vibrance: true, saturation: true });
@@ -46,7 +47,6 @@ export function useHonchoEditor(controller, initImageId, firebaseUid) {
46
47
  // Preset State
47
48
  const [isPresetModalOpen, setPresetModalOpen] = useState(false);
48
49
  const [isPresetModalOpenMobile, setPresetModalOpenMobile] = useState(false);
49
- const [presets, setPresets] = useState([]);
50
50
  const [presetName, setPresetName] = useState("Type Here");
51
51
  const [isPresetCreated, setIsPresetCreated] = useState(false);
52
52
  const [selectedMobilePreset, setSelectedMobilePreset] = useState('preset1');
@@ -232,66 +232,11 @@ export function useHonchoEditor(controller, initImageId, firebaseUid) {
232
232
  };
233
233
  const handlePresetMenuClose = () => { setPresetMenuAnchorEl(null); setActivePresetMenuId(null); };
234
234
  const handleRemovePreset = () => { console.log(`Remove: ${activePresetMenuId}`); handlePresetMenuClose(); };
235
- const handleDeletePreset = useCallback(async () => {
236
- if (!controller || !activePresetMenuId)
237
- return;
238
- try {
239
- await controller.deletePreset(firebaseUid, activePresetMenuId);
240
- // On success, remove the preset from local state
241
- setPresets(prevPresets => prevPresets.filter(p => p.id !== activePresetMenuId));
242
- }
243
- catch (error) {
244
- console.error("Failed to delete preset:", error);
245
- }
246
- handlePresetMenuClose(); // Close the options menu
247
- }, [controller, activePresetMenuId]);
248
235
  // Preset Modal Handlers
249
236
  const handleOpenPresetModal = () => { setIsPresetCreated(false); setPresetModalOpen(true); };
250
237
  const handleClosePresetModal = () => setPresetModalOpen(false);
251
- const handleCreatePreset = useCallback(async () => {
252
- if (!controller)
253
- return;
254
- // The current adjustment state from the history hook
255
- const currentAdjustments = { ...currentAdjustmentsState };
256
- try {
257
- // Call the controller, which now calls the real API
258
- const newPresetFromApi = await controller.createPreset(firebaseUid, presetName, currentAdjustments);
259
- // If the API call was successful and returned a preset...
260
- if (newPresetFromApi) {
261
- // ...add the new preset from the API response to our local state
262
- setPresets(prevPresets => [...prevPresets, newPresetFromApi]);
263
- }
264
- }
265
- catch (error) {
266
- console.error("Failed to create preset:", error);
267
- // Optionally: show an error message to the user here
268
- }
269
- // Close the modal and show a confirmation
270
- setIsPresetCreated(true);
271
- handleClosePresetModal();
272
- setTimeout(() => setIsPresetCreated(false), 1000); // Hide confirmation after 1s
273
- }, [controller, presetName, currentAdjustmentsState, firebaseUid]);
274
238
  const handleOpenPresetModalMobile = () => { setIsPresetCreated(false); setPresetModalOpenMobile(true); };
275
239
  const handleClosePresetModalMobile = () => setPresetModalOpenMobile(false);
276
- const handleCreatePresetMobile = useCallback(async () => {
277
- if (!controller)
278
- return;
279
- console.log("Creating mobile preset:", presetName);
280
- const currentAdjustments = { ...currentAdjustmentsState };
281
- try {
282
- // RE-USE THE SAME LOGIC AS THE DESKTOP VERSION
283
- const newPresetFromApi = await controller.createPreset(firebaseUid, presetName, currentAdjustments);
284
- if (newPresetFromApi) {
285
- setPresets(prevPresets => [...prevPresets, newPresetFromApi]);
286
- }
287
- }
288
- catch (error) {
289
- console.error("Failed to create mobile preset:", error);
290
- }
291
- setIsPresetCreated(true);
292
- handleClosePresetModalMobile();
293
- setTimeout(() => setIsPresetCreated(false), 1000);
294
- }, [controller, presetName, currentAdjustmentsState, firebaseUid]);
295
240
  const handleNameChange = (event) => setPresetName(event.target.value);
296
241
  // Watermark Handlers
297
242
  const handleOpenWatermarkView = () => setIsCreatingWatermark(true);
@@ -697,14 +642,11 @@ export function useHonchoEditor(controller, initImageId, firebaseUid) {
697
642
  handleSelectDesktopPreset,
698
643
  handlePresetMenuClick,
699
644
  handlePresetMenuClose,
700
- handleCreatePreset,
701
645
  handleRemovePreset,
702
- handleDeletePreset,
703
646
  handleOpenPresetModal,
704
647
  handleClosePresetModal,
705
648
  handleOpenPresetModalMobile,
706
649
  handleClosePresetModalMobile,
707
- handleCreatePresetMobile,
708
650
  setPresetName,
709
651
  handleNameChange,
710
652
  isRenameModalOpen,
@@ -0,0 +1,82 @@
1
+ import { Controller, Preset, AdjustmentState } from './editor/useHonchoEditor';
2
+ /**
3
+ * Configuration options for the preset hook
4
+ */
5
+ export interface PresetOptions {
6
+ /** Enable development warnings for debugging */
7
+ devWarnings?: boolean;
8
+ /** Auto-load presets on hook initialization */
9
+ autoLoad?: boolean;
10
+ }
11
+ /**
12
+ * Information about the current preset state
13
+ */
14
+ export interface PresetInfo {
15
+ /** Whether presets are currently being loaded */
16
+ isLoading: boolean;
17
+ /** Error message if any operation failed */
18
+ error: string | null;
19
+ /** Number of presets currently managed */
20
+ count: number;
21
+ /** Whether the hook has been initialized */
22
+ isInitialized: boolean;
23
+ }
24
+ /**
25
+ * Actions available for preset management
26
+ */
27
+ export interface PresetActions {
28
+ /** Create a new preset */
29
+ create: (name: string, settings: AdjustmentState) => Promise<Preset | null>;
30
+ /** Rename an existing preset */
31
+ rename: (presetId: string, newName: string) => Promise<boolean>;
32
+ /** Delete a preset */
33
+ delete: (presetId: string) => Promise<boolean>;
34
+ /** Load/refresh presets from backend */
35
+ load: () => Promise<void>;
36
+ /** Find preset that matches the given adjustment state */
37
+ findByAdjustments: (adjustments: AdjustmentState) => Preset | null;
38
+ }
39
+ /**
40
+ * Return type for the usePreset hook
41
+ */
42
+ export interface UsePresetReturn {
43
+ /** Current list of presets */
44
+ presets: Preset[];
45
+ /** Information about preset state */
46
+ info: PresetInfo;
47
+ /** Available preset actions */
48
+ actions: PresetActions;
49
+ }
50
+ /**
51
+ * Hook for managing presets with backend communication through Controller.
52
+ *
53
+ * **Key Features:**
54
+ * - **Backend Communication**: All operations go through the provided Controller
55
+ * - **Local State Management**: Maintains local preset list for UI performance
56
+ * - **CRUD Operations**: Create, rename, delete, and list presets
57
+ * - **Error Handling**: Provides error states for failed operations
58
+ * - **Auto-loading**: Optional automatic preset loading on initialization
59
+ *
60
+ * **Typical Usage:**
61
+ * ```typescript
62
+ * const { presets, actions, info } = usePreset(controller, firebaseUid, { autoLoad: true });
63
+ *
64
+ * // Create preset
65
+ * const newPreset = await actions.create('My Preset', adjustmentState);
66
+ *
67
+ * // Rename preset
68
+ * const success = await actions.rename(presetId, 'New Name');
69
+ *
70
+ * // Delete preset
71
+ * const deleted = await actions.delete(presetId);
72
+ *
73
+ * // Manual reload
74
+ * await actions.load();
75
+ * ```
76
+ *
77
+ * @param controller - Backend controller for API communication
78
+ * @param firebaseUid - User identifier for backend operations
79
+ * @param options - Configuration options
80
+ * @returns Object with presets, info, and actions
81
+ */
82
+ export declare function usePreset(controller: Controller | null, firebaseUid: string, options?: PresetOptions): UsePresetReturn;
@@ -0,0 +1,302 @@
1
+ import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
2
+ /**
3
+ * Hook for managing presets with backend communication through Controller.
4
+ *
5
+ * **Key Features:**
6
+ * - **Backend Communication**: All operations go through the provided Controller
7
+ * - **Local State Management**: Maintains local preset list for UI performance
8
+ * - **CRUD Operations**: Create, rename, delete, and list presets
9
+ * - **Error Handling**: Provides error states for failed operations
10
+ * - **Auto-loading**: Optional automatic preset loading on initialization
11
+ *
12
+ * **Typical Usage:**
13
+ * ```typescript
14
+ * const { presets, actions, info } = usePreset(controller, firebaseUid, { autoLoad: true });
15
+ *
16
+ * // Create preset
17
+ * const newPreset = await actions.create('My Preset', adjustmentState);
18
+ *
19
+ * // Rename preset
20
+ * const success = await actions.rename(presetId, 'New Name');
21
+ *
22
+ * // Delete preset
23
+ * const deleted = await actions.delete(presetId);
24
+ *
25
+ * // Manual reload
26
+ * await actions.load();
27
+ * ```
28
+ *
29
+ * @param controller - Backend controller for API communication
30
+ * @param firebaseUid - User identifier for backend operations
31
+ * @param options - Configuration options
32
+ * @returns Object with presets, info, and actions
33
+ */
34
+ export function usePreset(controller, firebaseUid, options = {}) {
35
+ // Memoize options to prevent re-renders when object is recreated with same values
36
+ const memoizedOptions = useMemo(() => ({
37
+ devWarnings: options.devWarnings ?? false,
38
+ autoLoad: options.autoLoad ?? false
39
+ }), [options.devWarnings, options.autoLoad]);
40
+ // Core state
41
+ const [presets, setPresets] = useState([]);
42
+ const [isLoading, setIsLoading] = useState(false);
43
+ const [error, setError] = useState(null);
44
+ const [isInitialized, setIsInitialized] = useState(false);
45
+ // Track controller and firebaseUid changes with stable refs
46
+ const controllerRef = useRef(controller);
47
+ const firebaseUidRef = useRef(firebaseUid);
48
+ // Only update refs when values actually change
49
+ if (controllerRef.current !== controller) {
50
+ controllerRef.current = controller;
51
+ }
52
+ if (firebaseUidRef.current !== firebaseUid) {
53
+ firebaseUidRef.current = firebaseUid;
54
+ }
55
+ // Helper function to log debug messages - memoized to prevent re-renders
56
+ const debugLog = useCallback((message, data) => {
57
+ if (memoizedOptions.devWarnings) {
58
+ console.log(`[usePreset] ${message}`, data || '');
59
+ }
60
+ }, [memoizedOptions.devWarnings]);
61
+ // Helper function to handle errors
62
+ const handleError = useCallback((operation, error) => {
63
+ const errorMessage = `Failed to ${operation}: ${error?.message || error}`;
64
+ setError(errorMessage);
65
+ debugLog(`Error in ${operation}`, error);
66
+ return false;
67
+ }, [debugLog]);
68
+ // Load presets from backend
69
+ const load = useCallback(async () => {
70
+ if (!controllerRef.current || !firebaseUidRef.current) {
71
+ debugLog('Load skipped: missing controller or firebaseUid');
72
+ return;
73
+ }
74
+ setIsLoading(true);
75
+ setError(null);
76
+ try {
77
+ debugLog('Loading presets from backend...');
78
+ const loadedPresets = await controllerRef.current.getPresets(firebaseUidRef.current);
79
+ setPresets(loadedPresets);
80
+ setIsInitialized(true);
81
+ debugLog('Presets loaded successfully', { count: loadedPresets.length });
82
+ }
83
+ catch (err) {
84
+ handleError('load presets', err);
85
+ setPresets([]); // Clear presets on error
86
+ }
87
+ finally {
88
+ setIsLoading(false);
89
+ }
90
+ }, [debugLog, handleError]);
91
+ // Fire-and-forget version of load for internal use
92
+ const loadInBackground = useCallback(() => {
93
+ if (!controllerRef.current || !firebaseUidRef.current) {
94
+ debugLog('Background load skipped: missing controller or firebaseUid');
95
+ return;
96
+ }
97
+ debugLog('Background loading presets...');
98
+ // Don't set loading state for background operations
99
+ controllerRef.current.getPresets(firebaseUidRef.current)
100
+ .then(loadedPresets => {
101
+ setPresets(loadedPresets);
102
+ if (!isInitialized) {
103
+ setIsInitialized(true);
104
+ }
105
+ debugLog('Background presets loaded successfully', { count: loadedPresets.length });
106
+ })
107
+ .catch(err => {
108
+ debugLog('Background load failed:', err);
109
+ // Don't set error state for background operations
110
+ });
111
+ }, [debugLog, isInitialized]);
112
+ // Create a new preset
113
+ const create = useCallback(async (name, settings) => {
114
+ if (!controllerRef.current || !firebaseUidRef.current) {
115
+ debugLog('Create skipped: missing controller or firebaseUid');
116
+ return null;
117
+ }
118
+ if (!name.trim()) {
119
+ setError('Preset name cannot be empty');
120
+ return null;
121
+ }
122
+ // Check for duplicate names
123
+ if (presets.some(p => p.name.toLowerCase() === name.toLowerCase())) {
124
+ setError('A preset with this name already exists');
125
+ return null;
126
+ }
127
+ setIsLoading(true);
128
+ setError(null);
129
+ try {
130
+ debugLog('Creating preset...', { name, settings });
131
+ // Fire the create request but don't wait for preset data in response
132
+ await controllerRef.current.createPreset(firebaseUidRef.current, name, settings);
133
+ debugLog('Preset creation request completed');
134
+ // Fire and forget: Schedule a delayed refresh to get updated preset list
135
+ setTimeout(() => {
136
+ debugLog('Refreshing presets after create (fire and forget)');
137
+ loadInBackground();
138
+ }, 500); // 500ms delay to allow backend processing
139
+ // Return a minimal success indicator since we don't have the actual preset data
140
+ return { id: 'pending', name, is_default: false };
141
+ }
142
+ catch (err) {
143
+ handleError('create preset', err);
144
+ return null;
145
+ }
146
+ finally {
147
+ setIsLoading(false);
148
+ }
149
+ }, [presets, debugLog, handleError, loadInBackground]);
150
+ // Rename an existing preset
151
+ const rename = useCallback(async (presetId, newName) => {
152
+ if (!controllerRef.current || !firebaseUidRef.current) {
153
+ debugLog('Rename skipped: missing controller or firebaseUid');
154
+ return false;
155
+ }
156
+ if (!newName.trim()) {
157
+ setError('Preset name cannot be empty');
158
+ return false;
159
+ }
160
+ const existingPreset = presets.find(p => p.id === presetId);
161
+ if (!existingPreset) {
162
+ setError('Preset not found');
163
+ return false;
164
+ }
165
+ // Check for duplicate names (excluding the current preset)
166
+ if (presets.some(p => p.id !== presetId && p.name.toLowerCase() === newName.toLowerCase())) {
167
+ setError('A preset with this name already exists');
168
+ return false;
169
+ }
170
+ setIsLoading(true);
171
+ setError(null);
172
+ try {
173
+ debugLog('Renaming preset...', { presetId, oldName: existingPreset.name, newName });
174
+ const updatedPreset = { ...existingPreset, name: newName };
175
+ await controllerRef.current.updatePreset(firebaseUidRef.current, updatedPreset);
176
+ // Update local state
177
+ setPresets(prev => prev.map(p => p.id === presetId ? updatedPreset : p));
178
+ debugLog('Preset renamed successfully');
179
+ return true;
180
+ }
181
+ catch (err) {
182
+ handleError('rename preset', err);
183
+ return false;
184
+ }
185
+ finally {
186
+ setIsLoading(false);
187
+ }
188
+ }, [presets, debugLog, handleError]);
189
+ // Delete a preset
190
+ const deletePreset = useCallback(async (presetId) => {
191
+ if (!controllerRef.current || !firebaseUidRef.current) {
192
+ debugLog('Delete skipped: missing controller or firebaseUid');
193
+ return false;
194
+ }
195
+ const existingPreset = presets.find(p => p.id === presetId);
196
+ if (!existingPreset) {
197
+ setError('Preset not found');
198
+ return false;
199
+ }
200
+ setIsLoading(true);
201
+ setError(null);
202
+ try {
203
+ debugLog('Deleting preset...', { presetId, name: existingPreset.name });
204
+ await controllerRef.current.deletePreset(firebaseUidRef.current, presetId);
205
+ // Remove from local state
206
+ setPresets(prev => prev.filter(p => p.id !== presetId));
207
+ debugLog('Preset deleted successfully');
208
+ return true;
209
+ }
210
+ catch (err) {
211
+ handleError('delete preset', err);
212
+ return false;
213
+ }
214
+ finally {
215
+ setIsLoading(false);
216
+ }
217
+ }, [presets, debugLog, handleError]);
218
+ // Find preset that matches the given adjustment state
219
+ const findByAdjustments = useCallback((adjustments) => {
220
+ // Helper function to normalize adjustment values (treat null/undefined as 0)
221
+ const normalizeValue = (value) => {
222
+ return value ?? 0;
223
+ };
224
+ // Helper function to convert Preset to AdjustmentState for comparison
225
+ const presetToAdjustmentState = (preset) => {
226
+ return {
227
+ tempScore: normalizeValue(preset.temperature),
228
+ tintScore: normalizeValue(preset.tint),
229
+ vibranceScore: normalizeValue(preset.vibrance),
230
+ saturationScore: normalizeValue(preset.saturation),
231
+ exposureScore: normalizeValue(preset.exposure),
232
+ highlightsScore: normalizeValue(preset.highlights),
233
+ shadowsScore: normalizeValue(preset.shadows),
234
+ whitesScore: normalizeValue(preset.whites),
235
+ blacksScore: normalizeValue(preset.blacks),
236
+ contrastScore: normalizeValue(preset.contrast),
237
+ clarityScore: normalizeValue(preset.clarity),
238
+ sharpnessScore: normalizeValue(preset.sharpness)
239
+ };
240
+ };
241
+ // Helper function to compare two adjustment states
242
+ const adjustmentsMatch = (presetSettings, current) => {
243
+ const keys = [
244
+ 'tempScore', 'tintScore', 'vibranceScore', 'saturationScore',
245
+ 'exposureScore', 'highlightsScore', 'shadowsScore', 'whitesScore',
246
+ 'blacksScore', 'contrastScore', 'clarityScore', 'sharpnessScore'
247
+ ];
248
+ return keys.every(key => normalizeValue(presetSettings[key]) === normalizeValue(current[key]));
249
+ };
250
+ debugLog('Finding preset by adjustments...', adjustments);
251
+ // Find preset that matches the current adjustments
252
+ const matchingPreset = presets.find(preset => {
253
+ const presetAdjustments = presetToAdjustmentState(preset);
254
+ return adjustmentsMatch(presetAdjustments, adjustments);
255
+ });
256
+ if (matchingPreset) {
257
+ debugLog('Found matching preset:', matchingPreset);
258
+ return matchingPreset;
259
+ }
260
+ else {
261
+ debugLog('No matching preset found');
262
+ return null;
263
+ }
264
+ }, [presets, debugLog]);
265
+ // Auto-load presets on initialization - stable dependencies
266
+ useEffect(() => {
267
+ if (memoizedOptions.autoLoad && controller && firebaseUid && !isInitialized) {
268
+ debugLog('Auto-loading presets...');
269
+ load();
270
+ }
271
+ }, [memoizedOptions.autoLoad, controller, firebaseUid, isInitialized, load, debugLog]);
272
+ // Clear state when controller or firebaseUid changes
273
+ useEffect(() => {
274
+ if (isInitialized) {
275
+ debugLog('Controller or firebaseUid changed, clearing state');
276
+ setPresets([]);
277
+ setError(null);
278
+ setIsInitialized(false);
279
+ }
280
+ }, [controller, firebaseUid, isInitialized, debugLog]);
281
+ // Preset info object - memoized to prevent re-renders
282
+ const info = useMemo(() => ({
283
+ isLoading,
284
+ error,
285
+ count: presets.length,
286
+ isInitialized
287
+ }), [isLoading, error, presets.length, isInitialized]);
288
+ // Actions object - memoized to prevent re-renders when functions don't change
289
+ const actions = useMemo(() => ({
290
+ create,
291
+ rename,
292
+ delete: deletePreset,
293
+ load,
294
+ findByAdjustments
295
+ }), [create, rename, deletePreset, load, findByAdjustments]);
296
+ // Return object - memoized to prevent re-renders when values don't change
297
+ return useMemo(() => ({
298
+ presets,
299
+ info,
300
+ actions
301
+ }), [presets, info, actions]);
302
+ }
package/dist/index.d.ts CHANGED
@@ -28,6 +28,7 @@ export { useImageProcessor } from './lib/hooks/useImageProcessor';
28
28
  export { useEditorHeadless } from './lib/hooks/useEditorHeadless';
29
29
  export { useAdjustmentHistory, type UseAdjustmentHistoryReturn, type HistoryInfo, type HistoryActions, type HistoryConfig } from './hooks/useAdjustmentHistory';
30
30
  export { useAdjustmentHistoryBatch, type UseAdjustmentHistoryBatchReturn, type BatchAdjustmentState, type ImageAdjustmentConfig, type BatchHistoryInfo, type BatchHistoryActions, type BatchHistoryConfig } from './hooks/useAdjustmentHistoryBatch';
31
+ export { usePreset, type UsePresetReturn, type PresetInfo, type PresetActions, type PresetOptions } from './hooks/usePreset';
31
32
  export { default as useColors } from './themes/colors';
32
33
  export { default as useHonchoTypography } from './themes/honchoTheme';
33
34
  export { default as useIsMobile } from './utils/isMobile';
package/dist/index.js CHANGED
@@ -26,6 +26,8 @@ export { useEditorHeadless } from './lib/hooks/useEditorHeadless';
26
26
  // --- History Hooks ---
27
27
  export { useAdjustmentHistory } from './hooks/useAdjustmentHistory';
28
28
  export { useAdjustmentHistoryBatch } from './hooks/useAdjustmentHistoryBatch';
29
+ // --- Preset Hook ---
30
+ export { usePreset } from './hooks/usePreset';
29
31
  // --- Theme & Utils ---
30
32
  export { default as useColors } from './themes/colors';
31
33
  export { default as useHonchoTypography } from './themes/honchoTheme';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yogiswara/honcho-editor-ui",
3
- "version": "2.2.7",
3
+ "version": "2.3.1",
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",