@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,4 @@
1
- import { Controller } from "./editor/useHonchoEditor";
1
+ import { Controller } from "./editor/type";
2
2
  import { Gallery } from "./editor/type";
3
3
  /**
4
4
  * Return type for the useGallerySwipe hook
@@ -0,0 +1,89 @@
1
+ import { Gallery } from './editor/type';
2
+ import { Controller } from "./editor/type";
3
+ /**
4
+ * Configuration options for the paging hook
5
+ */
6
+ export interface PagingOptions {
7
+ /** Enable development warnings for debugging */
8
+ devWarnings?: boolean;
9
+ /** Auto-load first page on hook initialization */
10
+ autoLoad?: boolean;
11
+ /** Reset pagination when dependencies change */
12
+ autoReset?: boolean;
13
+ }
14
+ /**
15
+ * Information about the current paging state
16
+ */
17
+ export interface PagingInfo {
18
+ /** Whether images are currently being loaded */
19
+ isLoading: boolean;
20
+ /** Whether more pages are being loaded */
21
+ isLoadingMore: boolean;
22
+ /** Error message if any operation failed */
23
+ error: string | null;
24
+ /** Current page number */
25
+ currentPage: number;
26
+ /** Whether there are more pages to load */
27
+ hasMore: boolean;
28
+ /** Total number of images loaded */
29
+ totalImages: number;
30
+ /** Whether the hook has been initialized */
31
+ isInitialized: boolean;
32
+ }
33
+ /**
34
+ * Actions available for paging management
35
+ */
36
+ export interface PagingActions {
37
+ /** Load more images (next page) - only one instance can run at a time */
38
+ loadMore: () => Promise<void>;
39
+ /** Refresh/reload from first page */
40
+ refresh: () => Promise<void>;
41
+ /** Reset pagination state */
42
+ reset: () => void;
43
+ }
44
+ /**
45
+ * Return type for the usePaging hook
46
+ */
47
+ export interface UsePagingReturn {
48
+ /** Current list of images */
49
+ images: Gallery[];
50
+ /** Information about paging state */
51
+ info: PagingInfo;
52
+ /** Available paging actions */
53
+ actions: PagingActions;
54
+ }
55
+ /**
56
+ * Hook for managing paginated image loading with ControllerBulk.
57
+ *
58
+ * **Key Features:**
59
+ * - **Paginated Loading**: Handles page-by-page image loading
60
+ * - **State Management**: Maintains image list and pagination state
61
+ * - **Load More**: Seamlessly loads and appends next pages
62
+ * - **Mutation**: Update specific images without full reload
63
+ * - **Error Handling**: Provides error states for failed operations
64
+ * - **Auto-loading**: Optional automatic first page loading
65
+ *
66
+ * **Typical Usage:**
67
+ * ```typescript
68
+ * const { images, info, actions } = usePaging(controller, firebaseUid, eventId, {
69
+ * autoLoad: true,
70
+ * autoReset: true
71
+ * });
72
+ *
73
+ * // Load more images
74
+ * await actions.loadMore();
75
+ *
76
+ * // Refresh from beginning
77
+ * await actions.refresh();
78
+ *
79
+ * // Update specific image
80
+ * actions.mutateImage(imageId, (img) => ({ ...img, isSelected: true }));
81
+ * ```
82
+ *
83
+ * @param controller - Backend controller for API communication
84
+ * @param firebaseUid - User identifier for backend operations
85
+ * @param eventId - Event identifier for image list
86
+ * @param options - Configuration options
87
+ * @returns Object with images, info, and actions
88
+ */
89
+ export declare function usePaging(controller: Controller | null, firebaseUid: string, eventId: string, options?: PagingOptions): UsePagingReturn;
@@ -0,0 +1,211 @@
1
+ import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
2
+ /**
3
+ * Hook for managing paginated image loading with ControllerBulk.
4
+ *
5
+ * **Key Features:**
6
+ * - **Paginated Loading**: Handles page-by-page image loading
7
+ * - **State Management**: Maintains image list and pagination state
8
+ * - **Load More**: Seamlessly loads and appends next pages
9
+ * - **Mutation**: Update specific images without full reload
10
+ * - **Error Handling**: Provides error states for failed operations
11
+ * - **Auto-loading**: Optional automatic first page loading
12
+ *
13
+ * **Typical Usage:**
14
+ * ```typescript
15
+ * const { images, info, actions } = usePaging(controller, firebaseUid, eventId, {
16
+ * autoLoad: true,
17
+ * autoReset: true
18
+ * });
19
+ *
20
+ * // Load more images
21
+ * await actions.loadMore();
22
+ *
23
+ * // Refresh from beginning
24
+ * await actions.refresh();
25
+ *
26
+ * // Update specific image
27
+ * actions.mutateImage(imageId, (img) => ({ ...img, isSelected: true }));
28
+ * ```
29
+ *
30
+ * @param controller - Backend controller for API communication
31
+ * @param firebaseUid - User identifier for backend operations
32
+ * @param eventId - Event identifier for image list
33
+ * @param options - Configuration options
34
+ * @returns Object with images, info, and actions
35
+ */
36
+ export function usePaging(controller, firebaseUid, eventId, options = {}) {
37
+ // Memoize options to prevent re-renders when object is recreated with same values
38
+ const memoizedOptions = useMemo(() => ({
39
+ devWarnings: options.devWarnings ?? false,
40
+ autoLoad: options.autoLoad ?? false,
41
+ autoReset: options.autoReset ?? true
42
+ }), [options.devWarnings, options.autoLoad, options.autoReset]);
43
+ // Core state
44
+ const [images, setImages] = useState([]);
45
+ const [isLoading, setIsLoading] = useState(false);
46
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
47
+ const [error, setError] = useState(null);
48
+ const [currentPage, setCurrentPage] = useState(1);
49
+ const [hasMore, setHasMore] = useState(true);
50
+ const [isInitialized, setIsInitialized] = useState(false);
51
+ // Track if loadMore is currently running to prevent spam
52
+ const isLoadMoreRunningRef = useRef(false);
53
+ // Track dependencies with stable refs
54
+ const controllerRef = useRef(controller);
55
+ const firebaseUidRef = useRef(firebaseUid);
56
+ const eventIdRef = useRef(eventId);
57
+ // Only update refs when values actually change
58
+ if (controllerRef.current !== controller) {
59
+ controllerRef.current = controller;
60
+ }
61
+ if (firebaseUidRef.current !== firebaseUid) {
62
+ firebaseUidRef.current = firebaseUid;
63
+ }
64
+ if (eventIdRef.current !== eventId) {
65
+ eventIdRef.current = eventId;
66
+ }
67
+ // Helper function to log debug messages
68
+ const debugLog = useCallback((message, data) => {
69
+ if (memoizedOptions.devWarnings) {
70
+ console.log(`[usePaging] ${message}`, data || '');
71
+ }
72
+ }, [memoizedOptions.devWarnings]);
73
+ // Helper function to handle errors
74
+ const handleError = useCallback((operation, error) => {
75
+ const errorMessage = `Failed to ${operation}: ${error instanceof Error ? error.message : String(error)}`;
76
+ setError(errorMessage);
77
+ debugLog(`Error in ${operation}`, error);
78
+ }, [debugLog]);
79
+ // Load images for a specific page
80
+ const loadPage = useCallback(async (pageNum, isLoadMore = false) => {
81
+ if (!controllerRef.current || !firebaseUidRef.current || !eventIdRef.current) {
82
+ debugLog('Load skipped: missing controller, firebaseUid, or eventId');
83
+ return;
84
+ }
85
+ // Set appropriate loading state
86
+ if (isLoadMore) {
87
+ setIsLoadingMore(true);
88
+ }
89
+ else {
90
+ setIsLoading(true);
91
+ }
92
+ setError(null);
93
+ try {
94
+ debugLog(`Loading page ${pageNum}...`, { isLoadMore });
95
+ const response = await controllerRef.current.getImageList(firebaseUidRef.current, eventIdRef.current, pageNum);
96
+ debugLog('Page loaded successfully', {
97
+ page: response.current_page,
98
+ imageCount: response.gallery.length,
99
+ hasNext: response.next_page > 0
100
+ });
101
+ // Update images list
102
+ setImages(prev => {
103
+ if (isLoadMore) {
104
+ // Append new images for load more
105
+ return [...prev, ...response.gallery];
106
+ }
107
+ else {
108
+ // Replace all images for initial load or refresh
109
+ return response.gallery;
110
+ }
111
+ });
112
+ // Update pagination state
113
+ setCurrentPage(response.current_page);
114
+ setHasMore(response.next_page > 0 && response.gallery.length > 0);
115
+ if (!isInitialized) {
116
+ setIsInitialized(true);
117
+ }
118
+ }
119
+ catch (err) {
120
+ handleError(`load page ${pageNum}`, err);
121
+ // On error, don't change images if it's load more
122
+ if (!isLoadMore) {
123
+ setImages([]);
124
+ }
125
+ }
126
+ finally {
127
+ if (isLoadMore) {
128
+ setIsLoadingMore(false);
129
+ }
130
+ else {
131
+ setIsLoading(false);
132
+ }
133
+ }
134
+ }, [debugLog, handleError, isInitialized]);
135
+ // Load more images (next page) - spam-protected
136
+ const loadMore = useCallback(async () => {
137
+ // Prevent multiple concurrent loadMore calls
138
+ if (isLoadMoreRunningRef.current || isLoadingMore || !hasMore) {
139
+ debugLog('Load more skipped', {
140
+ isLoadMoreRunning: isLoadMoreRunningRef.current,
141
+ isLoadingMore,
142
+ hasMore
143
+ });
144
+ return;
145
+ }
146
+ // Mark as running to prevent spam
147
+ isLoadMoreRunningRef.current = true;
148
+ try {
149
+ debugLog('Loading more images...', { nextPage: currentPage + 1 });
150
+ await loadPage(currentPage + 1, true);
151
+ }
152
+ finally {
153
+ // Always reset the running flag
154
+ isLoadMoreRunningRef.current = false;
155
+ }
156
+ }, [isLoadingMore, hasMore, currentPage, loadPage, debugLog]);
157
+ // Refresh from first page
158
+ const refresh = useCallback(async () => {
159
+ debugLog('Refreshing from first page...');
160
+ setCurrentPage(1);
161
+ setHasMore(true);
162
+ await loadPage(1, false);
163
+ }, [loadPage, debugLog]);
164
+ // Reset pagination state
165
+ const reset = useCallback(() => {
166
+ debugLog('Resetting pagination state');
167
+ setImages([]);
168
+ setCurrentPage(1);
169
+ setHasMore(true);
170
+ setError(null);
171
+ setIsInitialized(false);
172
+ // Reset loadMore running flag when page changes
173
+ isLoadMoreRunningRef.current = false;
174
+ }, [debugLog]);
175
+ // Auto-load first page on initialization
176
+ useEffect(() => {
177
+ if (memoizedOptions.autoLoad && controller && firebaseUid && eventId && !isInitialized) {
178
+ debugLog('Auto-loading first page...');
179
+ loadPage(1, false);
180
+ }
181
+ }, [memoizedOptions.autoLoad, controller, firebaseUid, eventId, isInitialized, loadPage, debugLog]);
182
+ // Reset when dependencies change (if autoReset is enabled)
183
+ useEffect(() => {
184
+ if (memoizedOptions.autoReset && isInitialized) {
185
+ debugLog('Dependencies changed, resetting state');
186
+ reset();
187
+ }
188
+ }, [controller, firebaseUid, eventId, memoizedOptions.autoReset, isInitialized, reset, debugLog]);
189
+ // Paging info object - memoized to prevent re-renders
190
+ const info = useMemo(() => ({
191
+ isLoading,
192
+ isLoadingMore,
193
+ error,
194
+ currentPage,
195
+ hasMore,
196
+ totalImages: images.length,
197
+ isInitialized
198
+ }), [isLoading, isLoadingMore, error, currentPage, hasMore, images.length, isInitialized]);
199
+ // Actions object - memoized to prevent re-renders
200
+ const actions = useMemo(() => ({
201
+ loadMore,
202
+ refresh,
203
+ reset
204
+ }), [loadMore, refresh, reset]);
205
+ // Return object - memoized to prevent re-renders
206
+ return useMemo(() => ({
207
+ images,
208
+ info,
209
+ actions
210
+ }), [images, info, actions]);
211
+ }
@@ -1,4 +1,4 @@
1
- import { Controller, Preset, AdjustmentState } from './editor/useHonchoEditor';
1
+ import { Controller, Preset, AdjustmentState } from './editor/type';
2
2
  /**
3
3
  * Configuration options for the preset hook
4
4
  */
@@ -42,25 +42,27 @@ export function usePreset(controller, firebaseUid, options = {}) {
42
42
  const [isLoading, setIsLoading] = useState(false);
43
43
  const [error, setError] = useState(null);
44
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
45
  // Helper function to log debug messages - memoized to prevent re-renders
56
46
  const debugLog = useCallback((message, data) => {
57
47
  if (memoizedOptions.devWarnings) {
58
48
  console.log(`[usePreset] ${message}`, data || '');
59
49
  }
60
50
  }, [memoizedOptions.devWarnings]);
51
+ // Stable references for controller and firebaseUid to detect actual changes
52
+ const stableControllerRef = useRef(controller);
53
+ const stableFirebaseUidRef = useRef(firebaseUid);
54
+ const prevStableControllerRef = useRef(controller);
55
+ const prevStableFirebaseUidRef = useRef(firebaseUid);
56
+ // Update refs only when values actually change (by reference)
57
+ if (stableControllerRef.current !== controller) {
58
+ stableControllerRef.current = controller;
59
+ }
60
+ if (stableFirebaseUidRef.current !== firebaseUid) {
61
+ stableFirebaseUidRef.current = firebaseUid;
62
+ }
61
63
  // Helper function to handle errors
62
64
  const handleError = useCallback((operation, error) => {
63
- const errorMessage = `Failed to ${operation}: ${error?.message || error}`;
65
+ const errorMessage = `Failed to ${operation}: ${error instanceof Error ? error.message : String(error)}`;
64
66
  setError(errorMessage);
65
67
  debugLog(`Error in ${operation}`, error);
66
68
  return false;
@@ -68,50 +70,44 @@ export function usePreset(controller, firebaseUid, options = {}) {
68
70
  // Load presets from backend
69
71
  const load = useCallback(async () => {
70
72
  console.log("Load Presets Get Function Called");
71
- if (!controllerRef.current || !firebaseUidRef.current) {
73
+ if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
72
74
  debugLog('Load skipped: missing controller or firebaseUid');
73
75
  return;
74
76
  }
75
77
  setIsLoading(true);
76
78
  setError(null);
77
- console.log('before GOINT to load 2.5. STATE UPDATE: setPresets is being called with:', presets);
78
79
  try {
79
80
  debugLog('Loading presets from backend...');
80
- const loadedPresets = await controllerRef.current.getPresets(firebaseUidRef.current);
81
- console.log('✅ 3. STATE UPDATE: setPresets is being called with:', loadedPresets);
81
+ const loadedPresets = await stableControllerRef.current.getPresets(stableFirebaseUidRef.current);
82
82
  setPresets(loadedPresets);
83
- console.log('✅ 4. STATE UPDATE: setIsInitialized is being called with:', true);
84
- console.log('presets thats called:', presets);
85
83
  setIsInitialized(true);
86
84
  debugLog('Presets loaded successfully', { count: loadedPresets.length });
87
85
  }
88
86
  catch (err) {
89
87
  handleError('load presets', err);
90
- console.log('4. catch ERROR!');
91
88
  setPresets([]); // Clear presets on error
92
89
  }
93
90
  finally {
94
- console.log('5. STATE UPDATE: setIsLoading is being called with:', false);
95
91
  setIsLoading(false);
96
92
  }
97
93
  }, [debugLog, handleError]);
98
94
  // Fire-and-forget version of load for internal use
99
95
  const loadInBackground = useCallback(() => {
100
- if (!controllerRef.current || !firebaseUidRef.current) {
96
+ if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
101
97
  debugLog('Background load skipped: missing controller or firebaseUid');
102
98
  return;
103
99
  }
104
100
  debugLog('Background loading presets...');
105
101
  // Don't set loading state for background operations
106
- controllerRef.current.getPresets(firebaseUidRef.current)
107
- .then(loadedPresets => {
102
+ stableControllerRef.current.getPresets(stableFirebaseUidRef.current)
103
+ .then((loadedPresets) => {
108
104
  setPresets(loadedPresets);
109
105
  if (!isInitialized) {
110
106
  setIsInitialized(true);
111
107
  }
112
108
  debugLog('Background presets loaded successfully', { count: loadedPresets.length });
113
109
  })
114
- .catch(err => {
110
+ .catch((err) => {
115
111
  debugLog('Background load failed:', err);
116
112
  // Don't set error state for background operations
117
113
  });
@@ -119,7 +115,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
119
115
  // Create a new preset
120
116
  const create = useCallback(async (name, settings) => {
121
117
  console.log("Create Preset Get Function Called");
122
- if (!controllerRef.current || !firebaseUidRef.current) {
118
+ if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
123
119
  debugLog('Create skipped: missing controller or firebaseUid');
124
120
  return null;
125
121
  }
@@ -137,7 +133,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
137
133
  try {
138
134
  debugLog('Creating preset...', { name, settings });
139
135
  // Fire the create request but don't wait for preset data in response
140
- await controllerRef.current.createPreset(firebaseUidRef.current, name, settings);
136
+ await stableControllerRef.current.createPreset(stableFirebaseUidRef.current, name, settings);
141
137
  debugLog('Preset creation request completed');
142
138
  const newPreset = {
143
139
  id: `temp-${Date.now()}`, // Use a temporary ID
@@ -178,7 +174,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
178
174
  }, [presets, debugLog, handleError, loadInBackground]);
179
175
  // Rename an existing preset
180
176
  const rename = useCallback(async (presetId, newName) => {
181
- if (!controllerRef.current || !firebaseUidRef.current) {
177
+ if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
182
178
  debugLog('Rename skipped: missing controller or firebaseUid');
183
179
  return false;
184
180
  }
@@ -201,7 +197,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
201
197
  try {
202
198
  debugLog('Renaming preset...', { presetId, oldName: existingPreset.name, newName });
203
199
  const updatedPreset = { ...existingPreset, name: newName };
204
- await controllerRef.current.updatePreset(firebaseUidRef.current, updatedPreset);
200
+ await stableControllerRef.current.updatePreset(stableFirebaseUidRef.current, updatedPreset);
205
201
  // Update local state
206
202
  setPresets(prev => prev.map(p => p.id === presetId ? updatedPreset : p));
207
203
  debugLog('Preset renamed successfully');
@@ -218,7 +214,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
218
214
  // Delete a preset
219
215
  const deletePreset = useCallback(async (presetId) => {
220
216
  console.log("Delete Presets Get Function Called");
221
- if (!controllerRef.current || !firebaseUidRef.current) {
217
+ if (!stableControllerRef.current || !stableFirebaseUidRef.current) {
222
218
  debugLog('Delete skipped: missing controller or firebaseUid');
223
219
  return false;
224
220
  }
@@ -231,7 +227,7 @@ export function usePreset(controller, firebaseUid, options = {}) {
231
227
  setError(null);
232
228
  try {
233
229
  debugLog('Deleting preset...', { presetId, name: existingPreset.name });
234
- await controllerRef.current.deletePreset(firebaseUidRef.current, presetId);
230
+ await stableControllerRef.current.deletePreset(stableFirebaseUidRef.current, presetId);
235
231
  // Remove from local state
236
232
  setPresets(prev => prev.filter(p => p.id !== presetId));
237
233
  debugLog('Preset deleted successfully');
@@ -294,20 +290,24 @@ export function usePreset(controller, firebaseUid, options = {}) {
294
290
  }, [presets, debugLog]);
295
291
  // Auto-load presets on initialization - stable dependencies
296
292
  useEffect(() => {
297
- if (memoizedOptions.autoLoad && controller && firebaseUid && !isInitialized) {
293
+ if (memoizedOptions.autoLoad && stableControllerRef.current && stableFirebaseUidRef.current && !isInitialized) {
298
294
  debugLog('Auto-loading presets...');
299
295
  load();
300
296
  }
301
- }, [memoizedOptions.autoLoad, controller, firebaseUid, isInitialized, load, debugLog]);
302
- // Clear state when controller or firebaseUid changes
297
+ }, [memoizedOptions.autoLoad, isInitialized, load]);
298
+ // Clear state when controller or firebaseUid changes - but only when they actually change
303
299
  useEffect(() => {
304
- if (isInitialized) {
300
+ const controllerChanged = prevStableControllerRef.current !== stableControllerRef.current;
301
+ const firebaseUidChanged = prevStableFirebaseUidRef.current !== stableFirebaseUidRef.current;
302
+ if ((controllerChanged || firebaseUidChanged) && isInitialized) {
305
303
  debugLog('Controller or firebaseUid changed, clearing state');
306
- // setPresets([]);
307
304
  setError(null);
308
305
  setIsInitialized(false);
306
+ // Don't clear presets to avoid flicker - they'll be reloaded
309
307
  }
310
- }, [controller, firebaseUid, isInitialized, debugLog]);
308
+ prevStableControllerRef.current = stableControllerRef.current;
309
+ prevStableFirebaseUidRef.current = stableFirebaseUidRef.current;
310
+ }, [stableControllerRef.current, stableFirebaseUidRef.current, isInitialized]);
311
311
  // Preset info object - memoized to prevent re-renders
312
312
  const info = useMemo(() => ({
313
313
  isLoading,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { useHonchoEditor } from './hooks/editor/useHonchoEditor';
2
- export type { Controller, AdjustmentState, Preset, ImageItem, } from './hooks/editor/useHonchoEditor';
1
+ export { useHonchoEditorSingle } from './hooks/editor/useHonchoEditorSingle';
3
2
  export { useHonchoEditorBulk } from './hooks/editor/useHonchoEditorBulk';
4
- export type { PhotoData, ControllerBulk } from './hooks/editor/useHonchoEditorBulk';
3
+ export type { Controller, AdjustmentState, Preset, ImageItem, } from './hooks/editor/type';
4
+ export type { PhotoData } from './hooks/editor/useHonchoEditorBulk';
5
5
  export type { Gallery, Content } from './hooks/editor/type';
6
6
  export { default as HHeaderEditor } from './components/editor/HHeaderEditor';
7
7
  export { default as HFooter } from './components/editor/HFooter';
@@ -29,6 +29,7 @@ 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
31
  export { usePreset, type UsePresetReturn, type PresetInfo, type PresetActions, type PresetOptions } from './hooks/usePreset';
32
+ export { usePaging, type UsePagingReturn, type PagingInfo, type PagingActions, type PagingOptions } from './hooks/usePaging';
32
33
  export { default as useColors } from './themes/colors';
33
34
  export { default as useHonchoTypography } from './themes/honchoTheme';
34
35
  export { default as useIsMobile } from './utils/isMobile';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { useHonchoEditor } from './hooks/editor/useHonchoEditor';
1
+ export { useHonchoEditorSingle } from './hooks/editor/useHonchoEditorSingle';
2
2
  export { useHonchoEditorBulk } from './hooks/editor/useHonchoEditorBulk';
3
3
  export { default as HHeaderEditor } from './components/editor/HHeaderEditor';
4
4
  export { default as HFooter } from './components/editor/HFooter';
@@ -28,6 +28,8 @@ export { useAdjustmentHistory } from './hooks/useAdjustmentHistory';
28
28
  export { useAdjustmentHistoryBatch } from './hooks/useAdjustmentHistoryBatch';
29
29
  // --- Preset Hook ---
30
30
  export { usePreset } from './hooks/usePreset';
31
+ // --- Paging Hook ---
32
+ export { usePaging } from './hooks/usePaging';
31
33
  // --- Theme & Utils ---
32
34
  export { default as useColors } from './themes/colors';
33
35
  export { default as useHonchoTypography } from './themes/honchoTheme';
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { EditorProcessingService } from '../context/EditorProcessingService';
3
+ import HonchoEditor from "../editor/honcho-editor";
3
4
  interface EditorContextValue {
4
5
  isReady: boolean;
5
6
  error: Error | null;
@@ -9,9 +10,18 @@ interface EditorContextValue {
9
10
  isProcessing: boolean;
10
11
  hasProcessor: boolean;
11
12
  };
13
+ editor: HonchoEditor | null;
14
+ loadImageFromUrl: ((url: string) => Promise<{
15
+ width: number;
16
+ height: number;
17
+ }>) | null;
12
18
  }
13
19
  interface EditorProviderProps {
14
20
  children: React.ReactNode;
21
+ /** URL to the honcho-photo-editor.js script (defaults to '/honcho-photo-editor.js') */
22
+ scriptUrl?: string;
23
+ /** URL to the honcho-photo-editor.wasm file (defaults to '/honcho-photo-editor.wasm') */
24
+ wasmUrl?: string;
15
25
  }
16
26
  export declare const EditorProvider: React.FC<EditorProviderProps>;
17
27
  export declare const useEditorContext: () => EditorContextValue;
@@ -4,9 +4,9 @@ import { createContext, useContext, useEffect, useState } from 'react';
4
4
  import { useEditorHeadless } from '../hooks/useEditorHeadless';
5
5
  import { EditorProcessingService } from '../context/EditorProcessingService';
6
6
  const EditorContext = createContext(null);
7
- export const EditorProvider = ({ children }) => {
7
+ export const EditorProvider = ({ children, scriptUrl = '/honcho-photo-editor.js', wasmUrl = '/honcho-photo-editor.wasm' }) => {
8
8
  // Single editor instance for the entire app
9
- const { editor, isReady, error, processImage } = useEditorHeadless();
9
+ const { editor, isReady, error, processImage, loadImageFromUrl } = useEditorHeadless({ scriptUrl, wasmUrl });
10
10
  // Single processing service instance
11
11
  const [processingService] = useState(() => new EditorProcessingService());
12
12
  const [queueStatus, setQueueStatus] = useState(processingService.getQueueStatus());
@@ -45,6 +45,8 @@ export const EditorProvider = ({ children }) => {
45
45
  error,
46
46
  processingService,
47
47
  queueStatus,
48
+ editor: editor,
49
+ loadImageFromUrl
48
50
  };
49
51
  return (_jsx(EditorContext.Provider, { value: contextValue, children: children }));
50
52
  };
@@ -1,4 +1,12 @@
1
- import { AdjustmentValues, HonchoEditor } from '../editor/honcho-editor';
1
+ import type { AdjustmentValues, HonchoEditor } from '../editor/honcho-editor';
2
+ declare global {
3
+ interface Window {
4
+ HonchoEditor: new () => any;
5
+ HonchoEditorUtils: {
6
+ imageDataToBlob: (imageData: ImageData, format?: string, quality?: number) => Promise<Blob>;
7
+ };
8
+ }
9
+ }
2
10
  interface EditorTask {
3
11
  id: string;
4
12
  path: string;
@@ -9,10 +17,18 @@ interface EditorResponse {
9
17
  id: string;
10
18
  path: string;
11
19
  }
12
- export declare function useEditorHeadless(): {
20
+ interface UseEditorHeadlessOptions {
21
+ scriptUrl?: string;
22
+ wasmUrl?: string;
23
+ }
24
+ export declare function useEditorHeadless(options?: UseEditorHeadlessOptions): {
13
25
  editor: HonchoEditor | null;
14
26
  isReady: boolean;
15
27
  error: Error | null;
16
28
  processImage: (task: EditorTask) => Promise<EditorResponse>;
29
+ loadImageFromUrl: (url: string) => Promise<{
30
+ width: number;
31
+ height: number;
32
+ }>;
17
33
  };
18
34
  export {};