@yogiswara/honcho-editor-ui 2.5.10 → 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.
- package/dist/components/editor/HBulkPreset.js +12 -2
- package/dist/hooks/demo/HonchoEditorBulkDemo.d.ts +3 -0
- package/dist/hooks/demo/HonchoEditorBulkDemo.js +228 -0
- package/dist/hooks/demo/HonchoEditorSingleCleanDemo.d.ts +3 -0
- package/dist/hooks/demo/HonchoEditorSingleCleanDemo.js +354 -0
- package/dist/hooks/demo/index.d.ts +2 -0
- package/dist/hooks/demo/index.js +2 -0
- package/dist/hooks/editor/type.d.ts +71 -0
- package/dist/hooks/editor/useHonchoEditorBulk.d.ts +10 -12
- package/dist/hooks/editor/useHonchoEditorBulk.js +126 -10
- package/dist/hooks/editor/useHonchoEditorSingle.d.ts +43 -0
- package/dist/hooks/editor/useHonchoEditorSingle.js +158 -0
- package/dist/hooks/useAdjustmentHistory.d.ts +9 -5
- package/dist/hooks/useAdjustmentHistory.js +187 -31
- package/dist/hooks/useAdjustmentHistoryBatch.d.ts +18 -1
- package/dist/hooks/useAdjustmentHistoryBatch.js +627 -201
- package/dist/hooks/useGallerySwipe.d.ts +1 -1
- package/dist/hooks/usePaging.d.ts +1 -1
- package/dist/hooks/usePaging.js +1 -1
- package/dist/hooks/usePreset.d.ts +1 -1
- package/dist/hooks/usePreset.js +35 -35
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/lib/context/EditorContext.d.ts +10 -0
- package/dist/lib/context/EditorContext.js +4 -2
- package/dist/lib/hooks/useEditorHeadless.d.ts +18 -2
- package/dist/lib/hooks/useEditorHeadless.js +142 -63
- package/dist/utils/adjustment.d.ts +2 -1
- package/dist/utils/adjustment.js +16 -0
- package/dist/utils/imageLoader.d.ts +11 -0
- package/dist/utils/imageLoader.js +53 -0
- package/package.json +1 -1
- package/dist/components/editor/GalleryAlbum/SimplifiedAlbumGallery.d.ts +0 -17
- package/dist/components/editor/GalleryAlbum/SimplifiedAlbumGallery.js +0 -14
- package/dist/components/editor/GalleryAlbum/SimplifiedImageItem.d.ts +0 -8
- package/dist/components/editor/GalleryAlbum/SimplifiedImageItem.js +0 -30
- package/dist/components/editor/HImageEditorPage.d.ts +0 -1
- package/dist/components/editor/HImageEditorPage.js +0 -187
- package/dist/hooks/__tests__/useGallerySwipe.test.d.ts +0 -0
- package/dist/hooks/__tests__/useGallerySwipe.test.js +0 -619
- package/dist/hooks/editor/useHonchoEditor.d.ts +0 -203
- package/dist/hooks/editor/useHonchoEditor.js +0 -716
- package/dist/hooks/useAdjustmentHistory.demo.d.ts +0 -8
- package/dist/hooks/useAdjustmentHistory.demo.js +0 -106
- package/dist/hooks/useAdjustmentHistory.example.d.ts +0 -38
- package/dist/hooks/useAdjustmentHistory.example.js +0 -182
- package/dist/hooks/useAdjustmentHistory.syncDemo.d.ts +0 -8
- package/dist/hooks/useAdjustmentHistory.syncDemo.js +0 -180
- package/dist/hooks/useGallerySwipe.example.d.ts +0 -24
- package/dist/hooks/useGallerySwipe.example.js +0 -184
package/dist/hooks/usePaging.js
CHANGED
|
@@ -72,7 +72,7 @@ export function usePaging(controller, firebaseUid, eventId, options = {}) {
|
|
|
72
72
|
}, [memoizedOptions.devWarnings]);
|
|
73
73
|
// Helper function to handle errors
|
|
74
74
|
const handleError = useCallback((operation, error) => {
|
|
75
|
-
const errorMessage = `Failed to ${operation}: ${error
|
|
75
|
+
const errorMessage = `Failed to ${operation}: ${error instanceof Error ? error.message : String(error)}`;
|
|
76
76
|
setError(errorMessage);
|
|
77
77
|
debugLog(`Error in ${operation}`, error);
|
|
78
78
|
}, [debugLog]);
|
package/dist/hooks/usePreset.js
CHANGED
|
@@ -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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 &&
|
|
293
|
+
if (memoizedOptions.autoLoad && stableControllerRef.current && stableFirebaseUidRef.current && !isInitialized) {
|
|
298
294
|
debugLog('Auto-loading presets...');
|
|
299
295
|
load();
|
|
300
296
|
}
|
|
301
|
-
}, [memoizedOptions.autoLoad,
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
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';
|
|
@@ -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
|
-
|
|
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 {};
|
|
@@ -1,41 +1,110 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export function useEditorHeadless() {
|
|
3
|
+
import { loadImageAsFile } from '../../utils/imageLoader';
|
|
4
|
+
export function useEditorHeadless(options = {}) {
|
|
5
|
+
const { scriptUrl = '/honcho-photo-editor.js', wasmUrl = '/honcho-photo-editor.wasm' } = options;
|
|
5
6
|
const editorRef = useRef(null);
|
|
6
7
|
const [isReady, setIsReady] = useState(false);
|
|
7
8
|
const [error, setError] = useState(null);
|
|
8
9
|
const [isScriptLoaded, setIsScriptLoaded] = useState(false);
|
|
9
10
|
// Load script dynamically without component
|
|
10
11
|
useEffect(() => {
|
|
11
|
-
const
|
|
12
|
-
// Check if
|
|
13
|
-
if (
|
|
12
|
+
const loadScripts = async () => {
|
|
13
|
+
// Check if HonchoEditor is already available
|
|
14
|
+
if (window.HonchoEditor) {
|
|
15
|
+
console.debug('HonchoEditor already available globally');
|
|
14
16
|
setIsScriptLoaded(true);
|
|
15
17
|
return;
|
|
16
18
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// Load WASM module first
|
|
20
|
+
const loadWasmModule = () => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
// Check if WASM module is already loaded
|
|
23
|
+
if (document.querySelector(`script[src="${scriptUrl}"]`)) {
|
|
24
|
+
console.debug('WASM module script already exists');
|
|
25
|
+
resolve();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
console.debug(`Loading WASM module from: ${scriptUrl}`);
|
|
29
|
+
const wasmScript = document.createElement('script');
|
|
30
|
+
wasmScript.src = scriptUrl;
|
|
31
|
+
wasmScript.async = true;
|
|
32
|
+
wasmScript.onload = () => {
|
|
33
|
+
console.debug('WASM module script loaded');
|
|
34
|
+
resolve();
|
|
35
|
+
};
|
|
36
|
+
wasmScript.onerror = () => {
|
|
37
|
+
console.error(`Failed to load WASM module from: ${scriptUrl}`);
|
|
38
|
+
reject(new Error('Failed to load WASM module'));
|
|
39
|
+
};
|
|
40
|
+
document.head.appendChild(wasmScript);
|
|
41
|
+
});
|
|
23
42
|
};
|
|
24
|
-
script
|
|
25
|
-
|
|
26
|
-
|
|
43
|
+
// Load wrapper script that creates HonchoEditor constructor
|
|
44
|
+
const loadWrapperScript = () => {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const wrapperUrl = '/honcho-editor.js';
|
|
47
|
+
// Check if wrapper script is already loaded
|
|
48
|
+
if (document.querySelector(`script[src="${wrapperUrl}"]`)) {
|
|
49
|
+
console.debug('Wrapper script already exists');
|
|
50
|
+
resolve();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
console.debug(`Loading HonchoEditor wrapper from: ${wrapperUrl}`);
|
|
54
|
+
const wrapperScript = document.createElement('script');
|
|
55
|
+
wrapperScript.src = wrapperUrl;
|
|
56
|
+
wrapperScript.async = true;
|
|
57
|
+
wrapperScript.onload = () => {
|
|
58
|
+
console.debug('Wrapper script loaded');
|
|
59
|
+
resolve();
|
|
60
|
+
};
|
|
61
|
+
wrapperScript.onerror = () => {
|
|
62
|
+
console.error(`Failed to load wrapper script from: ${wrapperUrl}`);
|
|
63
|
+
reject(new Error('Failed to load wrapper script'));
|
|
64
|
+
};
|
|
65
|
+
document.head.appendChild(wrapperScript);
|
|
66
|
+
});
|
|
27
67
|
};
|
|
28
|
-
|
|
68
|
+
try {
|
|
69
|
+
// Load WASM module first
|
|
70
|
+
await loadWasmModule();
|
|
71
|
+
// Then load the wrapper that creates the constructor
|
|
72
|
+
await loadWrapperScript();
|
|
73
|
+
// Wait for HonchoEditor constructor to be available
|
|
74
|
+
const waitForConstructor = () => {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const checkInterval = setInterval(() => {
|
|
77
|
+
if (window.HonchoEditor && typeof window.HonchoEditor === 'function') {
|
|
78
|
+
console.debug('HonchoEditor constructor now available');
|
|
79
|
+
clearInterval(checkInterval);
|
|
80
|
+
resolve();
|
|
81
|
+
}
|
|
82
|
+
}, 100);
|
|
83
|
+
// Timeout after 10 seconds
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
clearInterval(checkInterval);
|
|
86
|
+
if (!window.HonchoEditor) {
|
|
87
|
+
console.error('Timeout waiting for HonchoEditor constructor');
|
|
88
|
+
reject(new Error('Timeout waiting for HonchoEditor constructor'));
|
|
89
|
+
}
|
|
90
|
+
}, 10000);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
await waitForConstructor();
|
|
94
|
+
setIsScriptLoaded(true);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error('Failed to load HonchoEditor scripts:', error);
|
|
98
|
+
setError(error instanceof Error ? error : new Error(String(error)));
|
|
99
|
+
}
|
|
29
100
|
};
|
|
30
|
-
|
|
31
|
-
// Cleanup
|
|
101
|
+
loadScripts();
|
|
102
|
+
// Cleanup scripts on unmount (but be careful not to remove if other components need it)
|
|
32
103
|
return () => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
script.remove();
|
|
36
|
-
}
|
|
104
|
+
// Don't remove the scripts on unmount as other components might need them
|
|
105
|
+
// The scripts should stay loaded for the lifetime of the app
|
|
37
106
|
};
|
|
38
|
-
}, []);
|
|
107
|
+
}, [scriptUrl]);
|
|
39
108
|
// Initialize editor when script is loaded
|
|
40
109
|
useEffect(() => {
|
|
41
110
|
if (!isScriptLoaded)
|
|
@@ -43,10 +112,21 @@ export function useEditorHeadless() {
|
|
|
43
112
|
const initialize = async () => {
|
|
44
113
|
try {
|
|
45
114
|
console.debug('Script loaded, initializing editor...');
|
|
115
|
+
// Double-check that HonchoEditor is available
|
|
116
|
+
if (!window.HonchoEditor) {
|
|
117
|
+
throw new Error('window.HonchoEditor is not available');
|
|
118
|
+
}
|
|
119
|
+
if (typeof window.HonchoEditor !== 'function') {
|
|
120
|
+
throw new Error(`window.HonchoEditor is not a constructor (type: ${typeof window.HonchoEditor})`);
|
|
121
|
+
}
|
|
46
122
|
if (!editorRef.current) {
|
|
47
|
-
|
|
123
|
+
console.debug('Creating new HonchoEditor instance...');
|
|
124
|
+
editorRef.current = new window.HonchoEditor();
|
|
125
|
+
console.debug('HonchoEditor instance created successfully');
|
|
48
126
|
}
|
|
127
|
+
console.debug('Initializing HonchoEditor...');
|
|
49
128
|
await editorRef.current.initialize(false);
|
|
129
|
+
console.debug('HonchoEditor initialized successfully');
|
|
50
130
|
setIsReady(true);
|
|
51
131
|
}
|
|
52
132
|
catch (e) {
|
|
@@ -56,39 +136,12 @@ export function useEditorHeadless() {
|
|
|
56
136
|
};
|
|
57
137
|
initialize();
|
|
58
138
|
return () => {
|
|
59
|
-
editorRef.current
|
|
139
|
+
if (editorRef.current) {
|
|
140
|
+
console.debug('Cleaning up HonchoEditor...');
|
|
141
|
+
editorRef.current?.cleanup();
|
|
142
|
+
}
|
|
60
143
|
};
|
|
61
144
|
}, [isScriptLoaded]);
|
|
62
|
-
// Helper function to load image as blob with fallback
|
|
63
|
-
const loadImageAsBlob = async (url) => {
|
|
64
|
-
try {
|
|
65
|
-
// Try direct fetch first (faster, no server load)
|
|
66
|
-
const response = await fetch(url, {
|
|
67
|
-
mode: 'cors',
|
|
68
|
-
credentials: 'omit'
|
|
69
|
-
});
|
|
70
|
-
if (!response.ok) {
|
|
71
|
-
throw new Error(`Direct fetch failed: ${response.statusText}`);
|
|
72
|
-
}
|
|
73
|
-
return response.blob();
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
console.warn(`Direct fetch failed for ${url}, trying proxy fallback:`, error);
|
|
77
|
-
// Fallback to proxy API if CORS or other fetch issues
|
|
78
|
-
try {
|
|
79
|
-
const proxyUrl = `/api/image?imageUrl=${encodeURIComponent(url)}`;
|
|
80
|
-
const response = await fetch(proxyUrl);
|
|
81
|
-
if (!response.ok) {
|
|
82
|
-
throw new Error(`Proxy fetch failed: ${response.statusText}`);
|
|
83
|
-
}
|
|
84
|
-
return response.blob();
|
|
85
|
-
}
|
|
86
|
-
catch (proxyError) {
|
|
87
|
-
console.error(`Both direct and proxy fetch failed for ${url}:`, proxyError);
|
|
88
|
-
throw new Error(`Failed to load image: ${proxyError instanceof Error ? proxyError.message : 'Unknown error'}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
145
|
// Process single image task
|
|
93
146
|
const processImage = useCallback(async (task) => {
|
|
94
147
|
if (!editorRef.current || !isReady) {
|
|
@@ -96,20 +149,17 @@ export function useEditorHeadless() {
|
|
|
96
149
|
}
|
|
97
150
|
try {
|
|
98
151
|
console.debug(`Processing image: ${task.id}`);
|
|
99
|
-
// Load original image as
|
|
100
|
-
const
|
|
101
|
-
// Convert blob to File for HonchoEditor
|
|
102
|
-
const imageFile = new File([imageBlob], `image-${task.id}`, { type: imageBlob.type });
|
|
152
|
+
// Load original image as File using the new utility
|
|
153
|
+
const imageFile = await loadImageAsFile(task.path);
|
|
103
154
|
// Load frame if provided
|
|
104
155
|
let frameFile = null;
|
|
105
156
|
if (task.frame) {
|
|
106
|
-
|
|
107
|
-
frameFile = new File([frameBlob], `frame-${task.id}`, { type: frameBlob.type });
|
|
157
|
+
frameFile = await loadImageAsFile(task.frame);
|
|
108
158
|
}
|
|
109
159
|
// Process image using HonchoEditor's one-shot method
|
|
110
160
|
const processedImageData = await editorRef.current.processImageOneShot(imageFile, task.adjustments, frameFile);
|
|
111
161
|
// Convert ImageData to Blob
|
|
112
|
-
const processedBlob = await HonchoEditorUtils.imageDataToBlob(processedImageData);
|
|
162
|
+
const processedBlob = await window.HonchoEditorUtils.imageDataToBlob(processedImageData);
|
|
113
163
|
// Create blob URL for processed result
|
|
114
164
|
const blobUrl = URL.createObjectURL(processedBlob);
|
|
115
165
|
return { id: task.id, path: blobUrl };
|
|
@@ -118,11 +168,40 @@ export function useEditorHeadless() {
|
|
|
118
168
|
console.error(`Failed to process image ${task.id}:`, error);
|
|
119
169
|
throw new Error(`Failed to process image: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
120
170
|
}
|
|
121
|
-
}, [isReady
|
|
171
|
+
}, [isReady]);
|
|
172
|
+
// Helper function to load image from URL with CORS handling
|
|
173
|
+
const loadImageFromUrl = useCallback(async (url) => {
|
|
174
|
+
if (!editorRef.current || !isReady) {
|
|
175
|
+
throw new Error('Editor not ready');
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
console.debug(`Loading image from URL: ${url}`);
|
|
179
|
+
// First try direct load with CORS
|
|
180
|
+
try {
|
|
181
|
+
const size = await editorRef.current.loadImageFromUrl(url);
|
|
182
|
+
console.debug('Image loaded successfully via direct URL');
|
|
183
|
+
return size;
|
|
184
|
+
}
|
|
185
|
+
catch (directError) {
|
|
186
|
+
console.warn('Direct URL load failed, trying blob approach:', directError);
|
|
187
|
+
// Fallback: Load as File using the new utility
|
|
188
|
+
const imageFile = await loadImageAsFile(url);
|
|
189
|
+
// Load via file method
|
|
190
|
+
const size = await editorRef.current.loadImageFromFile(imageFile);
|
|
191
|
+
console.debug('Image loaded successfully via file fallback');
|
|
192
|
+
return size;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
console.error(`Failed to load image from URL ${url}:`, error);
|
|
197
|
+
throw new Error(`Failed to load image: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
198
|
+
}
|
|
199
|
+
}, [isReady]);
|
|
122
200
|
return {
|
|
123
201
|
editor: editorRef.current,
|
|
124
202
|
isReady,
|
|
125
203
|
error,
|
|
126
|
-
processImage
|
|
204
|
+
processImage,
|
|
205
|
+
loadImageFromUrl
|
|
127
206
|
};
|
|
128
207
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ColorAdjustment } from "../hooks/editor/type";
|
|
2
|
-
import { AdjustmentState } from "../hooks/editor/
|
|
2
|
+
import { AdjustmentState } from "../hooks/editor/type";
|
|
3
3
|
import { AdjustmentValues } from "../lib/editor/honcho-editor";
|
|
4
4
|
export declare function mapAdjustmentStateToAdjustmentEditor(state: AdjustmentState): AdjustmentValues;
|
|
5
5
|
export declare function mapColorAdjustmentToAdjustmentState(colors: ColorAdjustment): AdjustmentState;
|
|
6
|
+
export declare function mapAdjustmentStateToColorAdjustment(state: AdjustmentState): ColorAdjustment;
|
package/dist/utils/adjustment.js
CHANGED
|
@@ -30,3 +30,19 @@ export function mapColorAdjustmentToAdjustmentState(colors) {
|
|
|
30
30
|
sharpnessScore: colors.sharpness
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
export function mapAdjustmentStateToColorAdjustment(state) {
|
|
34
|
+
return {
|
|
35
|
+
temperature: state.tempScore,
|
|
36
|
+
tint: state.tintScore,
|
|
37
|
+
vibrance: state.vibranceScore,
|
|
38
|
+
saturation: state.saturationScore,
|
|
39
|
+
exposure: state.exposureScore,
|
|
40
|
+
contrast: state.contrastScore,
|
|
41
|
+
highlights: state.highlightsScore,
|
|
42
|
+
shadows: state.shadowsScore,
|
|
43
|
+
whites: state.whitesScore,
|
|
44
|
+
blacks: state.blacksScore,
|
|
45
|
+
clarity: state.clarityScore,
|
|
46
|
+
sharpness: state.sharpnessScore
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image loading utilities with CORS handling and fallback mechanisms
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Load image as blob with CORS handling and proxy fallback
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadImageAsBlob(url: string): Promise<Blob>;
|
|
8
|
+
/**
|
|
9
|
+
* Load image from URL and convert to File object with CORS handling and fallback mechanisms
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadImageAsFile(url: string): Promise<File>;
|