@umituz/react-native-image 1.1.5 → 1.2.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.
Files changed (37) hide show
  1. package/package.json +25 -10
  2. package/src/domain/entities/EditorTypes.ts +180 -0
  3. package/src/domain/entities/ImageFilterTypes.ts +70 -0
  4. package/src/index.ts +54 -0
  5. package/src/infrastructure/services/ImageAIEnhancementService.ts +136 -0
  6. package/src/infrastructure/services/ImageAnnotationService.ts +189 -0
  7. package/src/infrastructure/services/ImageBatchService.ts +199 -0
  8. package/src/infrastructure/services/ImageEditorService.ts +274 -0
  9. package/src/infrastructure/services/ImageFilterService.ts +168 -0
  10. package/src/infrastructure/services/ImageMetadataService.ts +187 -0
  11. package/src/infrastructure/services/ImageSpecializedEnhancementService.ts +57 -0
  12. package/src/infrastructure/utils/AIImageAnalysisUtils.ts +122 -0
  13. package/src/infrastructure/utils/CanvasRenderingService.ts +134 -0
  14. package/src/infrastructure/utils/CropTool.ts +260 -0
  15. package/src/infrastructure/utils/DrawingEngine.ts +210 -0
  16. package/src/infrastructure/utils/FilterEffects.ts +51 -0
  17. package/src/infrastructure/utils/FilterProcessor.ts +361 -0
  18. package/src/infrastructure/utils/ImageQualityPresets.ts +110 -0
  19. package/src/infrastructure/utils/LayerManager.ts +158 -0
  20. package/src/infrastructure/utils/ShapeRenderer.ts +168 -0
  21. package/src/infrastructure/utils/TextEditor.ts +273 -0
  22. package/src/presentation/components/CropComponent.tsx +183 -0
  23. package/src/presentation/components/Editor.tsx +261 -0
  24. package/src/presentation/components/EditorCanvas.tsx +135 -0
  25. package/src/presentation/components/EditorPanel.tsx +321 -0
  26. package/src/presentation/components/EditorToolbar.tsx +180 -0
  27. package/src/presentation/components/FilterSlider.tsx +123 -0
  28. package/src/presentation/components/GalleryHeader.tsx +87 -25
  29. package/src/presentation/components/ImageGallery.tsx +97 -48
  30. package/src/presentation/hooks/useEditorTools.ts +188 -0
  31. package/src/presentation/hooks/useImage.ts +33 -2
  32. package/src/presentation/hooks/useImageAIEnhancement.ts +33 -0
  33. package/src/presentation/hooks/useImageAnnotation.ts +32 -0
  34. package/src/presentation/hooks/useImageBatch.ts +33 -0
  35. package/src/presentation/hooks/useImageEditor.ts +165 -38
  36. package/src/presentation/hooks/useImageFilter.ts +38 -0
  37. package/src/presentation/hooks/useImageMetadata.ts +28 -0
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Image Infrastructure - Annotation Service
3
+ *
4
+ * Handles text overlay, drawing, and annotation features
5
+ */
6
+
7
+ import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
8
+ import { ImageValidator } from '../utils/ImageValidator';
9
+ import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
10
+ import { CanvasRenderingService } from '../utils/CanvasRenderingService';
11
+
12
+ export interface TextOverlay {
13
+ text: string;
14
+ x: number;
15
+ y: number;
16
+ fontSize?: number;
17
+ fontFamily?: string;
18
+ color?: string;
19
+ backgroundColor?: string;
20
+ maxWidth?: number;
21
+ rotation?: number;
22
+ }
23
+
24
+ export interface DrawingElement {
25
+ type: 'line' | 'rectangle' | 'circle' | 'arrow' | 'freehand';
26
+ points: Array<{ x: number; y: number }>;
27
+ color?: string;
28
+ strokeWidth?: number;
29
+ fillColor?: string;
30
+ }
31
+
32
+ export interface WatermarkOptions {
33
+ text?: string;
34
+ imageUri?: string;
35
+ position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center';
36
+ opacity?: number;
37
+ size?: number;
38
+ margin?: number;
39
+ }
40
+
41
+ export interface ImageAnnotation {
42
+ textOverlays?: TextOverlay[];
43
+ drawings?: DrawingElement[];
44
+ watermark?: WatermarkOptions;
45
+ }
46
+
47
+ export class ImageAnnotationService {
48
+ private static getPositionCoordinates(
49
+ position: string,
50
+ imageWidth: number,
51
+ imageHeight: number,
52
+ elementWidth: number,
53
+ elementHeight: number,
54
+ margin: number = 10
55
+ ): { x: number; y: number } {
56
+ switch (position) {
57
+ case 'top-left':
58
+ return { x: margin, y: margin };
59
+ case 'top-right':
60
+ return { x: imageWidth - elementWidth - margin, y: margin };
61
+ case 'bottom-left':
62
+ return { x: margin, y: imageHeight - elementHeight - margin };
63
+ case 'bottom-right':
64
+ return { x: imageWidth - elementWidth - margin, y: imageHeight - elementHeight - margin };
65
+ case 'center':
66
+ return {
67
+ x: (imageWidth - elementWidth) / 2,
68
+ y: (imageHeight - elementHeight) / 2
69
+ };
70
+ default:
71
+ return { x: margin, y: margin };
72
+ }
73
+ }
74
+
75
+ static async addTextOverlay(
76
+ uri: string,
77
+ overlay: TextOverlay
78
+ ): Promise<ImageManipulationResult> {
79
+ try {
80
+ const uriValidation = ImageValidator.validateUri(uri);
81
+ if (!uriValidation.isValid) {
82
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addTextOverlay');
83
+ }
84
+
85
+ // In a real implementation, we would:
86
+ // 1. Load image into canvas
87
+ // 2. Apply text overlay using canvas rendering
88
+ // 3. Export canvas to new URI
89
+
90
+ return {
91
+ uri, // Would be processed URI
92
+ width: 0,
93
+ height: 0,
94
+ };
95
+ } catch (error) {
96
+ throw ImageErrorHandler.handleUnknownError(error, 'addTextOverlay');
97
+ }
98
+ }
99
+
100
+ static async addDrawingElements(
101
+ uri: string,
102
+ elements: DrawingElement[]
103
+ ): Promise<ImageManipulationResult> {
104
+ try {
105
+ const uriValidation = ImageValidator.validateUri(uri);
106
+ if (!uriValidation.isValid) {
107
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addDrawingElements');
108
+ }
109
+
110
+ // Mock implementation
111
+ return {
112
+ uri,
113
+ width: 0,
114
+ height: 0,
115
+ };
116
+ } catch (error) {
117
+ throw ImageErrorHandler.handleUnknownError(error, 'addDrawingElements');
118
+ }
119
+ }
120
+
121
+ static async addWatermark(
122
+ uri: string,
123
+ options: WatermarkOptions
124
+ ): Promise<ImageManipulationResult> {
125
+ try {
126
+ const uriValidation = ImageValidator.validateUri(uri);
127
+ if (!uriValidation.isValid) {
128
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addWatermark');
129
+ }
130
+
131
+ if (!options.text && !options.imageUri) {
132
+ throw ImageErrorHandler.createError(
133
+ 'Either text or imageUri must be provided for watermark',
134
+ IMAGE_ERROR_CODES.VALIDATION_ERROR,
135
+ 'addWatermark'
136
+ );
137
+ }
138
+
139
+ // Mock implementation
140
+ return {
141
+ uri,
142
+ width: 0,
143
+ height: 0,
144
+ };
145
+ } catch (error) {
146
+ throw ImageErrorHandler.handleUnknownError(error, 'addWatermark');
147
+ }
148
+ }
149
+
150
+ static async applyAnnotation(
151
+ uri: string,
152
+ annotation: ImageAnnotation
153
+ ): Promise<ImageManipulationResult> {
154
+ try {
155
+ const uriValidation = ImageValidator.validateUri(uri);
156
+ if (!uriValidation.isValid) {
157
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'applyAnnotation');
158
+ }
159
+
160
+ // Apply all annotations in order
161
+ let resultUri = uri;
162
+
163
+ if (annotation.textOverlays) {
164
+ for (const overlay of annotation.textOverlays) {
165
+ const result = await ImageAnnotationService.addTextOverlay(resultUri, overlay);
166
+ resultUri = result.uri;
167
+ }
168
+ }
169
+
170
+ if (annotation.drawings) {
171
+ const result = await ImageAnnotationService.addDrawingElements(resultUri, annotation.drawings);
172
+ resultUri = result.uri;
173
+ }
174
+
175
+ if (annotation.watermark) {
176
+ const result = await ImageAnnotationService.addWatermark(resultUri, annotation.watermark);
177
+ resultUri = result.uri;
178
+ }
179
+
180
+ return {
181
+ uri: resultUri,
182
+ width: 0,
183
+ height: 0,
184
+ };
185
+ } catch (error) {
186
+ throw ImageErrorHandler.handleUnknownError(error, 'applyAnnotation');
187
+ }
188
+ }
189
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Image Infrastructure - Batch Processing Service
3
+ *
4
+ * Handles processing multiple images concurrently with progress tracking
5
+ */
6
+
7
+ import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
8
+ import type { ImageFilter } from '../../domain/entities/ImageFilterTypes';
9
+ import { ImageTransformService } from './ImageTransformService';
10
+ import { ImageConversionService } from './ImageConversionService';
11
+ import { ImageFilterService } from './ImageFilterService';
12
+ import { ImageValidator } from '../utils/ImageValidator';
13
+ import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
14
+
15
+ export interface BatchProcessingOptions {
16
+ concurrency?: number;
17
+ onProgress?: (completed: number, total: number, currentUri?: string) => void;
18
+ onError?: (error: Error, uri: string) => void;
19
+ }
20
+
21
+ export interface BatchProcessingResult {
22
+ successful: Array<{
23
+ uri: string;
24
+ result: ImageManipulationResult;
25
+ }>;
26
+ failed: Array<{
27
+ uri: string;
28
+ error: Error;
29
+ }>;
30
+ totalProcessed: number;
31
+ successCount: number;
32
+ failureCount: number;
33
+ }
34
+
35
+ export interface BatchOperation {
36
+ uri: string;
37
+ type: 'resize' | 'crop' | 'filter' | 'compress' | 'convert';
38
+ params: any;
39
+ options?: any;
40
+ }
41
+
42
+ export class ImageBatchService {
43
+ private static async processBatchItem(
44
+ operation: BatchOperation,
45
+ options: BatchProcessingOptions = {}
46
+ ): Promise<{ uri: string; result: ImageManipulationResult | null; error?: Error }> {
47
+ try {
48
+ const uriValidation = ImageValidator.validateUri(operation.uri);
49
+ if (!uriValidation.isValid) {
50
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'batchProcess');
51
+ }
52
+
53
+ let result: ImageManipulationResult;
54
+
55
+ switch (operation.type) {
56
+ case 'resize':
57
+ result = await ImageTransformService.resize(
58
+ operation.uri,
59
+ operation.params.width,
60
+ operation.params.height,
61
+ operation.options
62
+ );
63
+ break;
64
+
65
+ case 'crop':
66
+ result = await ImageTransformService.crop(
67
+ operation.uri,
68
+ operation.params,
69
+ operation.options
70
+ );
71
+ break;
72
+
73
+ case 'filter':
74
+ result = await ImageFilterService.applyFilter(
75
+ operation.uri,
76
+ operation.params
77
+ );
78
+ break;
79
+
80
+ case 'compress':
81
+ result = await ImageConversionService.compress(
82
+ operation.uri,
83
+ operation.params.quality
84
+ );
85
+ break;
86
+
87
+ case 'convert':
88
+ result = await ImageConversionService.convertFormat(
89
+ operation.uri,
90
+ operation.params.format,
91
+ operation.params.quality
92
+ );
93
+ break;
94
+
95
+ default:
96
+ throw ImageErrorHandler.createError(
97
+ `Unknown operation type: ${operation.type}`,
98
+ IMAGE_ERROR_CODES.VALIDATION_ERROR,
99
+ 'batchProcess'
100
+ );
101
+ }
102
+
103
+ return { uri: operation.uri, result };
104
+ } catch (error) {
105
+ return {
106
+ uri: operation.uri,
107
+ result: null,
108
+ error: error instanceof Error ? error : new Error('Unknown error')
109
+ };
110
+ }
111
+ }
112
+
113
+ static async processBatch(
114
+ operations: BatchOperation[],
115
+ options: BatchProcessingOptions = {}
116
+ ): Promise<BatchProcessingResult> {
117
+ const concurrency = options.concurrency || 3;
118
+ const successful: Array<{ uri: string; result: ImageManipulationResult }> = [];
119
+ const failed: Array<{ uri: string; error: Error }> = [];
120
+
121
+ let completed = 0;
122
+ const total = operations.length;
123
+
124
+ // Process operations in chunks based on concurrency
125
+ for (let i = 0; i < operations.length; i += concurrency) {
126
+ const chunk = operations.slice(i, i + concurrency);
127
+
128
+ const chunkResults = await Promise.all(
129
+ chunk.map(operation => this.processBatchItem(operation, options))
130
+ );
131
+
132
+ // Process results
133
+ for (const result of chunkResults) {
134
+ completed++;
135
+
136
+ options.onProgress?.(completed, total, result.uri);
137
+
138
+ if (result.error) {
139
+ failed.push({ uri: result.uri, error: result.error });
140
+ options.onError?.(result.error, result.uri);
141
+ } else if (result.result) {
142
+ successful.push({ uri: result.uri, result: result.result });
143
+ }
144
+ }
145
+ }
146
+
147
+ return {
148
+ successful,
149
+ failed,
150
+ totalProcessed: total,
151
+ successCount: successful.length,
152
+ failureCount: failed.length,
153
+ };
154
+ }
155
+
156
+ static async resizeBatch(
157
+ uris: string[],
158
+ width?: number,
159
+ height?: number,
160
+ options: BatchProcessingOptions & { saveOptions?: any } = {}
161
+ ): Promise<BatchProcessingResult> {
162
+ const operations: BatchOperation[] = uris.map(uri => ({
163
+ uri,
164
+ type: 'resize' as const,
165
+ params: { width, height },
166
+ options: options.saveOptions,
167
+ }));
168
+
169
+ return this.processBatch(operations, options);
170
+ }
171
+
172
+ static async compressBatch(
173
+ uris: string[],
174
+ quality: number = 0.8,
175
+ options: BatchProcessingOptions = {}
176
+ ): Promise<BatchProcessingResult> {
177
+ const operations: BatchOperation[] = uris.map(uri => ({
178
+ uri,
179
+ type: 'compress' as const,
180
+ params: { quality },
181
+ }));
182
+
183
+ return this.processBatch(operations, options);
184
+ }
185
+
186
+ static async filterBatch(
187
+ uris: string[],
188
+ filter: ImageFilter,
189
+ options: BatchProcessingOptions = {}
190
+ ): Promise<BatchProcessingResult> {
191
+ const operations: BatchOperation[] = uris.map(uri => ({
192
+ uri,
193
+ type: 'filter' as const,
194
+ params: filter,
195
+ }));
196
+
197
+ return this.processBatch(operations, options);
198
+ }
199
+ }
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Infrastructure - Advanced Editor Service
3
+ *
4
+ * Core editing functionality with history management
5
+ */
6
+
7
+ import { EditorTool, type EditorState, type EditorLayer, type EditorHistory, type EditorOptions } from '../../domain/entities/EditorTypes';
8
+ import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
9
+ import { ImageValidator } from '../utils/ImageValidator';
10
+ import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
11
+
12
+ export class ImageEditorService {
13
+ private static generateId(): string {
14
+ return Math.random().toString(36).substr(2, 9);
15
+ }
16
+
17
+ static createInitialState(
18
+ uri: string,
19
+ dimensions: { width: number; height: number },
20
+ options: EditorOptions = {}
21
+ ): EditorState {
22
+ const defaultLayer: EditorLayer = {
23
+ id: 'background',
24
+ name: 'Background',
25
+ visible: true,
26
+ opacity: 1,
27
+ locked: true,
28
+ elements: [],
29
+ };
30
+
31
+ return {
32
+ originalUri: uri,
33
+ tool: EditorTool.MOVE,
34
+ layers: [defaultLayer],
35
+ history: [{
36
+ id: ImageEditorService.generateId(),
37
+ timestamp: new Date(),
38
+ layers: [defaultLayer],
39
+ }],
40
+ historyIndex: 0,
41
+ isDirty: false,
42
+ dimensions,
43
+ zoom: 1,
44
+ pan: { x: 0, y: 0 },
45
+ };
46
+ }
47
+
48
+ static addLayer(
49
+ state: EditorState,
50
+ name?: string
51
+ ): EditorState {
52
+ const newLayer: EditorLayer = {
53
+ id: ImageEditorService.generateId(),
54
+ name: name || `Layer ${state.layers.length}`,
55
+ visible: true,
56
+ opacity: 1,
57
+ locked: false,
58
+ elements: [],
59
+ };
60
+
61
+ const newHistory: EditorHistory = {
62
+ id: ImageEditorService.generateId(),
63
+ timestamp: new Date(),
64
+ layers: [...state.layers, newLayer],
65
+ };
66
+
67
+ const newHistoryState = ImageEditorService.addToHistory(state, newHistory);
68
+
69
+ return {
70
+ ...newHistoryState,
71
+ layers: [...state.layers, newLayer],
72
+ selectedLayer: newLayer.id,
73
+ isDirty: true,
74
+ };
75
+ }
76
+
77
+ static removeLayer(
78
+ state: EditorState,
79
+ layerId: string
80
+ ): EditorState {
81
+ if (state.layers.length <= 1) {
82
+ throw ImageErrorHandler.createError(
83
+ 'Cannot remove the last layer',
84
+ IMAGE_ERROR_CODES.VALIDATION_ERROR,
85
+ 'removeLayer'
86
+ );
87
+ }
88
+
89
+ const newLayers = state.layers.filter(layer => layer.id !== layerId);
90
+ const newHistory: EditorHistory = {
91
+ id: ImageEditorService.generateId(),
92
+ timestamp: new Date(),
93
+ layers: newLayers,
94
+ };
95
+
96
+ const newHistoryState = ImageEditorService.addToHistory(state, newHistory);
97
+
98
+ return {
99
+ ...newHistoryState,
100
+ layers: newLayers,
101
+ selectedLayer: newLayers[0].id,
102
+ isDirty: true,
103
+ };
104
+ }
105
+
106
+ static updateLayer(
107
+ state: EditorState,
108
+ layerId: string,
109
+ updates: Partial<EditorLayer>
110
+ ): EditorState {
111
+ const newLayers = state.layers.map(layer =>
112
+ layer.id === layerId ? { ...layer, ...updates } : layer
113
+ );
114
+
115
+ const newHistory: EditorHistory = {
116
+ id: ImageEditorService.generateId(),
117
+ timestamp: new Date(),
118
+ layers: newLayers,
119
+ };
120
+
121
+ const newHistoryState = ImageEditorService.addToHistory(state, newHistory);
122
+
123
+ return {
124
+ ...newHistoryState,
125
+ layers: newLayers,
126
+ isDirty: true,
127
+ };
128
+ }
129
+
130
+ static addElementToLayer(
131
+ state: EditorState,
132
+ layerId: string,
133
+ element: any
134
+ ): EditorState {
135
+ const targetLayer = state.layers.find(layer => layer.id === layerId);
136
+ if (!targetLayer) {
137
+ throw ImageErrorHandler.createError(
138
+ 'Layer not found',
139
+ IMAGE_ERROR_CODES.VALIDATION_ERROR,
140
+ 'addElementToLayer'
141
+ );
142
+ }
143
+
144
+ if (targetLayer.locked) {
145
+ throw ImageErrorHandler.createError(
146
+ 'Cannot add element to locked layer',
147
+ IMAGE_ERROR_CODES.VALIDATION_ERROR,
148
+ 'addElementToLayer'
149
+ );
150
+ }
151
+
152
+ const newLayers = state.layers.map(layer =>
153
+ layer.id === layerId
154
+ ? { ...layer, elements: [...layer.elements, element] }
155
+ : layer
156
+ );
157
+
158
+ const newHistory: EditorHistory = {
159
+ id: ImageEditorService.generateId(),
160
+ timestamp: new Date(),
161
+ layers: newLayers,
162
+ };
163
+
164
+ const newHistoryState = ImageEditorService.addToHistory(state, newHistory);
165
+
166
+ return {
167
+ ...newHistoryState,
168
+ layers: newLayers,
169
+ isDirty: true,
170
+ };
171
+ }
172
+
173
+ static undo(state: EditorState): EditorState {
174
+ if (state.historyIndex <= 0) {
175
+ return state;
176
+ }
177
+
178
+ const newIndex = state.historyIndex - 1;
179
+ const historyState = state.history[newIndex];
180
+
181
+ return {
182
+ ...state,
183
+ layers: historyState.layers,
184
+ historyIndex: newIndex,
185
+ isDirty: true,
186
+ };
187
+ }
188
+
189
+ static redo(state: EditorState): EditorState {
190
+ if (state.historyIndex >= state.history.length - 1) {
191
+ return state;
192
+ }
193
+
194
+ const newIndex = state.historyIndex + 1;
195
+ const historyState = state.history[newIndex];
196
+
197
+ return {
198
+ ...state,
199
+ layers: historyState.layers,
200
+ historyIndex: newIndex,
201
+ isDirty: true,
202
+ };
203
+ }
204
+
205
+ private static addToHistory(
206
+ state: EditorState,
207
+ newHistory: EditorHistory,
208
+ maxHistory: number = 50
209
+ ): EditorState {
210
+ const newHistoryArray = [...state.history.slice(0, state.historyIndex + 1), newHistory];
211
+
212
+ // Keep only the last maxHistory states
213
+ if (newHistoryArray.length > maxHistory) {
214
+ newHistoryArray.shift();
215
+ }
216
+
217
+ return {
218
+ ...state,
219
+ history: newHistoryArray,
220
+ historyIndex: newHistoryArray.length - 1,
221
+ };
222
+ }
223
+
224
+ static setTool(state: EditorState, tool: EditorTool): EditorState {
225
+ return { ...state, tool };
226
+ }
227
+
228
+ static setSelectedLayer(state: EditorState, layerId?: string): EditorState {
229
+ return { ...state, selectedLayer: layerId };
230
+ }
231
+
232
+ static setZoom(state: EditorState, zoom: number): EditorState {
233
+ return { ...state, zoom: Math.max(0.1, Math.min(5, zoom)) };
234
+ }
235
+
236
+ static setPan(state: EditorState, pan: { x: number; y: number }): EditorState {
237
+ return { ...state, pan };
238
+ }
239
+
240
+ static canUndo(state: EditorState): boolean {
241
+ return state.historyIndex > 0;
242
+ }
243
+
244
+ static canRedo(state: EditorState): boolean {
245
+ return state.historyIndex < state.history.length - 1;
246
+ }
247
+
248
+ static getVisibleLayers(state: EditorState): EditorLayer[] {
249
+ return state.layers.filter(layer => layer.visible);
250
+ }
251
+
252
+ static getActiveLayers(state: EditorState): EditorLayer[] {
253
+ return state.layers.filter(layer => layer.visible && !layer.locked);
254
+ }
255
+
256
+ static exportState(state: EditorState): EditorState {
257
+ return {
258
+ ...state,
259
+ currentUri: undefined,
260
+ };
261
+ }
262
+
263
+ static importState(data: any, uri: string): EditorState {
264
+ try {
265
+ return {
266
+ ...data,
267
+ originalUri: uri,
268
+ currentUri: undefined,
269
+ };
270
+ } catch (error) {
271
+ throw ImageErrorHandler.handleUnknownError(error, 'importState');
272
+ }
273
+ }
274
+ }