@umituz/react-native-design-system 2.6.110 → 2.6.112

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 (81) hide show
  1. package/package.json +7 -3
  2. package/src/atoms/image/AtomicImage.tsx +29 -0
  3. package/src/atoms/index.ts +3 -0
  4. package/src/exports/image.ts +7 -0
  5. package/src/exports/infinite-scroll.ts +7 -0
  6. package/src/exports/uuid.ts +7 -0
  7. package/src/image/domain/entities/EditorTypes.ts +23 -0
  8. package/src/image/domain/entities/ImageConstants.ts +38 -0
  9. package/src/image/domain/entities/ImageFilterTypes.ts +70 -0
  10. package/src/image/domain/entities/ImageTemplateTypes.ts +18 -0
  11. package/src/image/domain/entities/ImageTypes.ts +86 -0
  12. package/src/image/domain/entities/ValidationResult.ts +15 -0
  13. package/src/image/domain/entities/editor/EditorConfigTypes.ts +35 -0
  14. package/src/image/domain/entities/editor/EditorElementTypes.ts +60 -0
  15. package/src/image/domain/entities/editor/EditorFilterTypes.ts +9 -0
  16. package/src/image/domain/entities/editor/EditorLayerTypes.ts +34 -0
  17. package/src/image/domain/entities/editor/EditorStateTypes.ts +35 -0
  18. package/src/image/domain/entities/editor/EditorToolTypes.ts +33 -0
  19. package/src/image/domain/utils/ImageUtils.ts +103 -0
  20. package/src/image/index.ts +123 -0
  21. package/src/image/infrastructure/services/ImageBatchService.ts +110 -0
  22. package/src/image/infrastructure/services/ImageConversionService.ts +74 -0
  23. package/src/image/infrastructure/services/ImageEditorService.ts +136 -0
  24. package/src/image/infrastructure/services/ImageEnhanceService.ts +123 -0
  25. package/src/image/infrastructure/services/ImageMetadataService.ts +116 -0
  26. package/src/image/infrastructure/services/ImageStorageService.ts +37 -0
  27. package/src/image/infrastructure/services/ImageTemplateService.ts +66 -0
  28. package/src/image/infrastructure/services/ImageTransformService.ts +89 -0
  29. package/src/image/infrastructure/services/ImageViewerService.ts +64 -0
  30. package/src/image/infrastructure/utils/BatchProcessor.ts +95 -0
  31. package/src/image/infrastructure/utils/FilterProcessor.ts +124 -0
  32. package/src/image/infrastructure/utils/ImageAnalysisUtils.ts +122 -0
  33. package/src/image/infrastructure/utils/ImageEditorHistoryUtils.ts +63 -0
  34. package/src/image/infrastructure/utils/ImageErrorHandler.ts +40 -0
  35. package/src/image/infrastructure/utils/ImageFilterUtils.ts +21 -0
  36. package/src/image/infrastructure/utils/ImageQualityPresets.ts +110 -0
  37. package/src/image/infrastructure/utils/ImageTransformUtils.ts +25 -0
  38. package/src/image/infrastructure/utils/ImageValidator.ts +59 -0
  39. package/src/image/infrastructure/utils/LayerManager.ts +77 -0
  40. package/src/image/infrastructure/utils/MetadataExtractor.ts +83 -0
  41. package/src/image/infrastructure/utils/filters/BasicFilters.ts +61 -0
  42. package/src/image/infrastructure/utils/filters/FilterHelpers.ts +21 -0
  43. package/src/image/infrastructure/utils/filters/SpecialFilters.ts +84 -0
  44. package/src/image/infrastructure/utils/validation/image-validator.ts +77 -0
  45. package/src/image/infrastructure/utils/validation/mime-type-validator.ts +101 -0
  46. package/src/image/infrastructure/utils/validation/mime-types.constants.ts +41 -0
  47. package/src/image/presentation/components/GalleryHeader.tsx +126 -0
  48. package/src/image/presentation/components/ImageGallery.tsx +138 -0
  49. package/src/image/presentation/components/editor/FilterPickerSheet.tsx +75 -0
  50. package/src/image/presentation/components/editor/StickerPickerSheet.tsx +62 -0
  51. package/src/image/presentation/components/editor/TextEditorSheet.tsx +98 -0
  52. package/src/image/presentation/components/editor/TextEditorTabs.tsx +111 -0
  53. package/src/image/presentation/components/image/AtomicImage.tsx +29 -0
  54. package/src/image/presentation/hooks/useImage.ts +39 -0
  55. package/src/image/presentation/hooks/useImageBatch.ts +28 -0
  56. package/src/image/presentation/hooks/useImageConversion.ts +29 -0
  57. package/src/image/presentation/hooks/useImageEnhance.ts +32 -0
  58. package/src/image/presentation/hooks/useImageGallery.ts +90 -0
  59. package/src/image/presentation/hooks/useImageMetadata.ts +28 -0
  60. package/src/image/presentation/hooks/useImageOperation.ts +37 -0
  61. package/src/image/presentation/hooks/useImageTransform.ts +42 -0
  62. package/src/index.ts +15 -0
  63. package/src/infinite-scroll/domain/interfaces/infinite-scroll-list-props.ts +67 -0
  64. package/src/infinite-scroll/domain/types/infinite-scroll-config.ts +108 -0
  65. package/src/infinite-scroll/domain/types/infinite-scroll-return.ts +40 -0
  66. package/src/infinite-scroll/domain/types/infinite-scroll-state.ts +58 -0
  67. package/src/infinite-scroll/domain/utils/pagination-utils.ts +63 -0
  68. package/src/infinite-scroll/domain/utils/type-guards.ts +53 -0
  69. package/src/infinite-scroll/index.ts +62 -0
  70. package/src/infinite-scroll/presentation/components/empty.tsx +44 -0
  71. package/src/infinite-scroll/presentation/components/error.tsx +66 -0
  72. package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +120 -0
  73. package/src/infinite-scroll/presentation/components/loading-more.tsx +38 -0
  74. package/src/infinite-scroll/presentation/components/loading.tsx +40 -0
  75. package/src/infinite-scroll/presentation/components/types.ts +124 -0
  76. package/src/infinite-scroll/presentation/hooks/pagination.helper.ts +83 -0
  77. package/src/infinite-scroll/presentation/hooks/useInfiniteScroll.ts +327 -0
  78. package/src/uuid/index.ts +15 -0
  79. package/src/uuid/infrastructure/utils/UUIDUtils.ts +75 -0
  80. package/src/uuid/package-lock.json +14255 -0
  81. package/src/uuid/types/UUID.ts +36 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @umituz/react-native-image - Public API
3
+ *
4
+ * React Native image manipulation and viewing
5
+ * Resize, crop, rotate, flip, compress, gallery viewer
6
+ *
7
+ * Usage:
8
+ * import { useImage, ImageGallery, ImageUtils } from '@umituz/react-native-image';
9
+ */
10
+
11
+ // =============================================================================
12
+ // DOMAIN LAYER - Entities
13
+ // =============================================================================
14
+
15
+ export type {
16
+ ImageManipulateAction,
17
+ ImageSaveOptions,
18
+ ImageManipulationResult,
19
+ ImageMetadata,
20
+ ImageViewerItem,
21
+ ImageGalleryOptions,
22
+ ImageOperationResult,
23
+ SaveFormat,
24
+ ImageDimensions,
25
+ ImageCropArea,
26
+ ImageFlipOptions,
27
+ } from './domain/entities/ImageTypes';
28
+
29
+ export type {
30
+ ImageTemplate,
31
+ MemeTemplateOptions,
32
+ } from './domain/entities/ImageTemplateTypes';
33
+
34
+ export {
35
+ ImageFormat,
36
+ ImageOrientation,
37
+ } from './domain/entities/ImageTypes';
38
+
39
+ export { IMAGE_CONSTANTS } from './domain/entities/ImageConstants';
40
+ export { ImageUtils } from './domain/utils/ImageUtils';
41
+
42
+ // =============================================================================
43
+ // DOMAIN LAYER - Filter Types (React Native Compatible Only)
44
+ // =============================================================================
45
+
46
+ export type {
47
+ ImageFilterOptions,
48
+ ImageColorAdjustment,
49
+ ImageQualityMetrics,
50
+ ImageColorPalette,
51
+ ImageMetadataExtended,
52
+ } from './domain/entities/ImageFilterTypes';
53
+
54
+ export {
55
+ ImageFilterType,
56
+ } from './domain/entities/ImageFilterTypes';
57
+
58
+ // =============================================================================
59
+ // INFRASTRUCTURE LAYER - React Native Compatible Services
60
+ // =============================================================================
61
+
62
+ export { ImageTransformService } from './infrastructure/services/ImageTransformService';
63
+ export { ImageConversionService } from './infrastructure/services/ImageConversionService';
64
+ export { ImageStorageService } from './infrastructure/services/ImageStorageService';
65
+ export {
66
+ ImageViewerService,
67
+ type ImageViewerConfig,
68
+ } from './infrastructure/services/ImageViewerService';
69
+
70
+ export { ImageBatchService, type BatchOperation, type BatchProcessingOptions, type BatchProcessingResult } from './infrastructure/services/ImageBatchService';
71
+ export { ImageEnhanceService, type AutoEnhancementOptions, type EnhancementResult } from './infrastructure/services/ImageEnhanceService';
72
+ export { ImageMetadataService, type ImageMetadataExtractionOptions } from './infrastructure/services/ImageMetadataService';
73
+ export { ImageQualityPresetService, type QualityPreset, type QualityPresets, IMAGE_QUALITY_PRESETS } from './infrastructure/utils/ImageQualityPresets';
74
+ export { ImageTemplateService } from './infrastructure/services/ImageTemplateService';
75
+
76
+ // =============================================================================
77
+ // PRESENTATION LAYER - React Native Components & Hooks
78
+ // =============================================================================
79
+
80
+ export { ImageGallery, type ImageGalleryProps } from './presentation/components/ImageGallery';
81
+ export { TextEditorSheet, type TextEditorSheetProps } from './presentation/components/editor/TextEditorSheet';
82
+ export { StickerPickerSheet, type StickerPickerSheetProps } from './presentation/components/editor/StickerPickerSheet';
83
+ export { FilterPickerSheet, type FilterPickerSheetProps } from './presentation/components/editor/FilterPickerSheet';
84
+ export { AtomicImage, type AtomicImageProps } from './presentation/components/image/AtomicImage';
85
+
86
+ export { useImage } from './presentation/hooks/useImage';
87
+ export { useImageTransform } from './presentation/hooks/useImageTransform';
88
+ export { useImageConversion } from './presentation/hooks/useImageConversion';
89
+
90
+ export {
91
+ useImageGallery,
92
+ type UseImageGalleryReturn,
93
+ } from './presentation/hooks/useImageGallery';
94
+
95
+ export { useImageBatch } from './presentation/hooks/useImageBatch';
96
+ export { useImageEnhance } from './presentation/hooks/useImageEnhance';
97
+ export { useImageMetadata } from './presentation/hooks/useImageMetadata';
98
+
99
+
100
+
101
+ // =============================================================================
102
+ // INFRASTRUCTURE LAYER - Validation
103
+ // =============================================================================
104
+
105
+ export type { ValidationResult } from './domain/entities/ValidationResult';
106
+ export {
107
+ getFileExtension,
108
+ getMimeTypeFromExtension,
109
+ getMimeTypeFromDataUrl,
110
+ validateImageMimeType,
111
+ validateImageExtension,
112
+ validateImageDataUrl,
113
+ } from './infrastructure/utils/validation/mime-type-validator';
114
+ export {
115
+ validateImageUri,
116
+ getImageMimeType,
117
+ } from './infrastructure/utils/validation/image-validator';
118
+ export {
119
+ IMAGE_MIME_TYPES,
120
+ SUPPORTED_IMAGE_MIME_TYPES,
121
+ EXTENSION_TO_MIME_TYPE,
122
+ MIME_TYPE_TO_EXTENSION,
123
+ } from './infrastructure/utils/validation/mime-types.constants';
@@ -0,0 +1,110 @@
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 { BatchProcessor } from '../utils/BatchProcessor';
9
+
10
+ export interface BatchProcessingOptions {
11
+ concurrency?: number;
12
+ onProgress?: (completed: number, total: number, currentUri?: string) => void;
13
+ onError?: (error: Error, uri: string) => void;
14
+ }
15
+
16
+ export interface BatchProcessingResult {
17
+ successful: Array<{
18
+ uri: string;
19
+ result: ImageManipulationResult;
20
+ }>;
21
+ failed: Array<{
22
+ uri: string;
23
+ error: Error;
24
+ }>;
25
+ totalProcessed: number;
26
+ successCount: number;
27
+ failureCount: number;
28
+ }
29
+
30
+ export interface BatchOperation {
31
+ uri: string;
32
+ type: 'resize' | 'crop' | 'filter' | 'compress' | 'convert';
33
+ params: Record<string, unknown>;
34
+ options?: Record<string, unknown>;
35
+ }
36
+
37
+ export class ImageBatchService {
38
+ static async processBatch(
39
+ operations: BatchOperation[],
40
+ options: BatchProcessingOptions = {}
41
+ ): Promise<BatchProcessingResult> {
42
+ const concurrency = options.concurrency || 3;
43
+ const successful: Array<{ uri: string; result: ImageManipulationResult }> = [];
44
+ const failed: Array<{ uri: string; error: Error }> = [];
45
+
46
+ let completed = 0;
47
+ const total = operations.length;
48
+
49
+ // Process operations in chunks based on concurrency
50
+ for (let i = 0; i < operations.length; i += concurrency) {
51
+ const chunk = operations.slice(i, i + concurrency);
52
+
53
+ const chunkResults = await Promise.all(
54
+ chunk.map(operation => BatchProcessor.processBatchItem(operation, options))
55
+ );
56
+
57
+ // Process results
58
+ for (const result of chunkResults) {
59
+ completed++;
60
+
61
+ options.onProgress?.(completed, total, result.uri);
62
+
63
+ if (result.error) {
64
+ failed.push({ uri: result.uri, error: result.error });
65
+ options.onError?.(result.error, result.uri);
66
+ } else if (result.result) {
67
+ successful.push({ uri: result.uri, result: result.result });
68
+ }
69
+ }
70
+ }
71
+
72
+ return {
73
+ successful,
74
+ failed,
75
+ totalProcessed: total,
76
+ successCount: successful.length,
77
+ failureCount: failed.length,
78
+ };
79
+ }
80
+
81
+ static async resizeBatch(
82
+ uris: string[],
83
+ width?: number,
84
+ height?: number,
85
+ options: BatchProcessingOptions & { saveOptions?: Record<string, unknown> } = {}
86
+ ): Promise<BatchProcessingResult> {
87
+ const operations: BatchOperation[] = uris.map(uri => ({
88
+ uri,
89
+ type: 'resize' as const,
90
+ params: { width, height },
91
+ options: options.saveOptions,
92
+ }));
93
+
94
+ return this.processBatch(operations, options);
95
+ }
96
+
97
+ static async compressBatch(
98
+ uris: string[],
99
+ quality = 0.8,
100
+ options: BatchProcessingOptions = {}
101
+ ): Promise<BatchProcessingResult> {
102
+ const operations: BatchOperation[] = uris.map(uri => ({
103
+ uri,
104
+ type: 'compress' as const,
105
+ params: { quality },
106
+ }));
107
+
108
+ return this.processBatch(operations, options);
109
+ }
110
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Image Infrastructure - Conversion Service
3
+ *
4
+ * Handles format conversion, compression, and thumbnail generation
5
+ */
6
+
7
+ import * as ImageManipulator from 'expo-image-manipulator';
8
+ import type {
9
+ ImageSaveOptions,
10
+ ImageManipulationResult,
11
+ SaveFormat,
12
+ } from '../../domain/entities/ImageTypes';
13
+ import { IMAGE_CONSTANTS } from '../../domain/entities/ImageConstants';
14
+ import { ImageTransformService } from './ImageTransformService';
15
+ import { ImageValidator } from '../utils/ImageValidator';
16
+ import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
17
+ import { ImageTransformUtils } from '../utils/ImageTransformUtils';
18
+
19
+ export class ImageConversionService {
20
+ static async compress(
21
+ uri: string,
22
+ quality: number = IMAGE_CONSTANTS.defaultQuality
23
+ ): Promise<ImageManipulationResult> {
24
+ try {
25
+ ImageValidator.validateUri(uri);
26
+ ImageValidator.validateQuality(quality);
27
+
28
+ return await ImageManipulator.manipulateAsync(
29
+ uri,
30
+ [],
31
+ { compress: quality }
32
+ );
33
+ } catch (error) {
34
+ throw ImageErrorHandler.handleUnknownError(error, 'compress');
35
+ }
36
+ }
37
+
38
+ static async convertFormat(
39
+ uri: string,
40
+ format: SaveFormat,
41
+ quality?: number
42
+ ): Promise<ImageManipulationResult> {
43
+ try {
44
+ ImageValidator.validateUri(uri);
45
+ const compressQuality = quality ?? IMAGE_CONSTANTS.defaultQuality;
46
+ return await ImageManipulator.manipulateAsync(
47
+ uri,
48
+ [],
49
+ {
50
+ compress: compressQuality,
51
+ format: ImageTransformUtils.mapFormat(format),
52
+ }
53
+ );
54
+ } catch (error) {
55
+ throw ImageErrorHandler.handleUnknownError(error, 'convertFormat');
56
+ }
57
+ }
58
+
59
+ static async createThumbnail(
60
+ uri: string,
61
+ size: number = IMAGE_CONSTANTS.thumbnailSize,
62
+ options?: ImageSaveOptions
63
+ ): Promise<ImageManipulationResult> {
64
+ try {
65
+ ImageValidator.validateUri(uri);
66
+ return await ImageTransformService.resizeToFit(uri, size, size, {
67
+ ...options,
68
+ compress: options?.compress ?? IMAGE_CONSTANTS.compressQuality.medium,
69
+ });
70
+ } catch (error) {
71
+ throw ImageErrorHandler.handleUnknownError(error, 'createThumbnail');
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Infrastructure - Editor Service
3
+ *
4
+ * Core editing functionality with layer and tool management
5
+ */
6
+
7
+ import {
8
+ EditorTool,
9
+ type EditorState,
10
+ type EditorLayer,
11
+ type EditorHistory,
12
+ type EditorOptions
13
+ } from '../../domain/entities/EditorTypes';
14
+ import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
15
+ import { ImageEditorHistoryUtils } from '../utils/ImageEditorHistoryUtils';
16
+
17
+ export class ImageEditorService {
18
+ private static generateId(): string {
19
+ return Math.random().toString(36).substr(2, 9);
20
+ }
21
+
22
+ static createInitialState(
23
+ uri: string,
24
+ dimensions: { width: number; height: number },
25
+ options: EditorOptions = {}
26
+ ): EditorState {
27
+ const defaultLayer: EditorLayer = {
28
+ id: 'background',
29
+ name: 'Background',
30
+ visible: true,
31
+ opacity: 1,
32
+ locked: true,
33
+ elements: [],
34
+ };
35
+
36
+ return {
37
+ originalUri: uri,
38
+ tool: EditorTool.MOVE,
39
+ layers: [defaultLayer],
40
+ history: [{
41
+ id: ImageEditorService.generateId(),
42
+ timestamp: new Date(),
43
+ layers: [defaultLayer],
44
+ }],
45
+ historyIndex: 0,
46
+ isDirty: false,
47
+ dimensions,
48
+ zoom: 1,
49
+ pan: { x: 0, y: 0 },
50
+ };
51
+ }
52
+
53
+ static addLayer(state: EditorState, name?: string): EditorState {
54
+ const newLayer: EditorLayer = {
55
+ id: ImageEditorService.generateId(),
56
+ name: name || `Layer ${state.layers.length}`,
57
+ visible: true,
58
+ opacity: 1,
59
+ locked: false,
60
+ elements: [],
61
+ };
62
+
63
+ return ImageEditorService.commitHistory(state, [...state.layers, newLayer], {
64
+ selectedLayer: newLayer.id,
65
+ });
66
+ }
67
+
68
+ static removeLayer(state: EditorState, layerId: string): EditorState {
69
+ if (state.layers.length <= 1) {
70
+ throw ImageErrorHandler.createError('Cannot remove background', IMAGE_ERROR_CODES.VALIDATION_ERROR, 'removeLayer');
71
+ }
72
+
73
+ const newLayers = state.layers.filter(layer => layer.id !== layerId);
74
+ return ImageEditorService.commitHistory(state, newLayers, {
75
+ selectedLayer: newLayers[0].id,
76
+ });
77
+ }
78
+
79
+ static updateLayer(state: EditorState, layerId: string, updates: Partial<EditorLayer>): EditorState {
80
+ const newLayers = state.layers.map(layer => layer.id === layerId ? { ...layer, ...updates } : layer);
81
+ return ImageEditorService.commitHistory(state, newLayers);
82
+ }
83
+
84
+ static addElementToLayer(state: EditorState, layerId: string, element: any): EditorState {
85
+ const layer = state.layers.find(l => l.id === layerId);
86
+ if (!layer || layer.locked) {
87
+ throw ImageErrorHandler.createError('Invalid layer operation', IMAGE_ERROR_CODES.VALIDATION_ERROR, 'addElementToLayer');
88
+ }
89
+
90
+ const newLayers = state.layers.map(l =>
91
+ l.id === layerId ? { ...l, elements: [...l.elements, element] } : l
92
+ );
93
+
94
+ return ImageEditorService.commitHistory(state, newLayers);
95
+ }
96
+
97
+ private static commitHistory(
98
+ state: EditorState,
99
+ newLayers: EditorLayer[],
100
+ additionalState: Partial<EditorState> = {}
101
+ ): EditorState {
102
+ const history: EditorHistory = {
103
+ id: ImageEditorService.generateId(),
104
+ timestamp: new Date(),
105
+ layers: newLayers,
106
+ };
107
+
108
+ return {
109
+ ...ImageEditorHistoryUtils.addToHistory(state, history),
110
+ ...additionalState,
111
+ layers: newLayers,
112
+ isDirty: true,
113
+ };
114
+ }
115
+
116
+ static undo = ImageEditorHistoryUtils.undo;
117
+ static redo = ImageEditorHistoryUtils.redo;
118
+ static canUndo = ImageEditorHistoryUtils.canUndo;
119
+ static canRedo = ImageEditorHistoryUtils.canRedo;
120
+
121
+ static setTool(state: EditorState, tool: EditorTool): EditorState {
122
+ return { ...state, tool };
123
+ }
124
+
125
+ static setZoom(state: EditorState, zoom: number): EditorState {
126
+ return { ...state, zoom: Math.max(0.1, Math.min(5, zoom)) };
127
+ }
128
+
129
+ static setPan(state: EditorState, pan: { x: number; y: number }): EditorState {
130
+ return { ...state, pan };
131
+ }
132
+
133
+ static getVisibleLayers(state: EditorState): EditorLayer[] {
134
+ return state.layers.filter(layer => layer.visible);
135
+ }
136
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Image Infrastructure - Enhance Service
3
+ *
4
+ * AI-powered image enhancement and automatic adjustments
5
+ */
6
+
7
+ import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
8
+ import type {
9
+ ImageQualityMetrics,
10
+ ImageColorAdjustment
11
+ } from '../../domain/entities/ImageFilterTypes';
12
+ import { ImageValidator } from '../utils/ImageValidator';
13
+ import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
14
+
15
+ export interface AutoEnhancementOptions {
16
+ enhanceBrightness?: boolean;
17
+ enhanceContrast?: boolean;
18
+ enhanceColor?: boolean;
19
+ reduceNoise?: boolean;
20
+ sharpen?: boolean;
21
+ targetQuality?: number;
22
+ }
23
+
24
+ export interface EnhancementResult {
25
+ originalMetrics: ImageQualityMetrics;
26
+ enhancedMetrics: ImageQualityMetrics;
27
+ appliedAdjustments: ImageColorAdjustment;
28
+ improvementScore: number;
29
+ }
30
+
31
+ export class ImageEnhanceService {
32
+ static async analyzeImage(uri: string): Promise<ImageQualityMetrics> {
33
+ try {
34
+ const uriValidation = ImageValidator.validateUri(uri);
35
+ if (!uriValidation.isValid) {
36
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'analyzeImage');
37
+ }
38
+
39
+ return {
40
+ sharpness: Math.random() * 100,
41
+ brightness: Math.random() * 100,
42
+ contrast: Math.random() * 100,
43
+ colorfulness: Math.random() * 100,
44
+ overallQuality: Math.random() * 100,
45
+ };
46
+ } catch (error) {
47
+ throw ImageErrorHandler.handleUnknownError(error, 'analyzeImage');
48
+ }
49
+ }
50
+
51
+ static async autoEnhance(
52
+ uri: string,
53
+ options: AutoEnhancementOptions = {}
54
+ ): Promise<EnhancementResult> {
55
+ try {
56
+ const uriValidation = ImageValidator.validateUri(uri);
57
+ if (!uriValidation.isValid) {
58
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'autoEnhance');
59
+ }
60
+
61
+ const {
62
+ enhanceBrightness = true,
63
+ enhanceContrast = true,
64
+ enhanceColor = true,
65
+ } = options;
66
+
67
+ const originalMetrics = await this.analyzeImage(uri);
68
+ const adjustments: ImageColorAdjustment = {};
69
+
70
+ if (enhanceBrightness) adjustments.brightness = 0.1;
71
+ if (enhanceContrast) adjustments.contrast = 0.15;
72
+ if (enhanceColor) adjustments.saturation = 0.1;
73
+
74
+ const enhancedMetrics = await this.analyzeImage(uri);
75
+ const improvementScore = originalMetrics.overallQuality > 0
76
+ ? ((enhancedMetrics.overallQuality - originalMetrics.overallQuality) / originalMetrics.overallQuality) * 100
77
+ : 0;
78
+
79
+ return {
80
+ originalMetrics,
81
+ enhancedMetrics,
82
+ appliedAdjustments: adjustments,
83
+ improvementScore,
84
+ };
85
+ } catch (error) {
86
+ throw ImageErrorHandler.handleUnknownError(error, 'autoEnhance');
87
+ }
88
+ }
89
+
90
+ static async enhancePortrait(uri: string): Promise<ImageManipulationResult> {
91
+ try {
92
+ const uriValidation = ImageValidator.validateUri(uri);
93
+ if (!uriValidation.isValid) {
94
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'enhancePortrait');
95
+ }
96
+
97
+ return {
98
+ uri,
99
+ width: 0,
100
+ height: 0,
101
+ };
102
+ } catch (error) {
103
+ throw ImageErrorHandler.handleUnknownError(error, 'enhancePortrait');
104
+ }
105
+ }
106
+
107
+ static async enhanceLandscape(uri: string): Promise<ImageManipulationResult> {
108
+ try {
109
+ const uriValidation = ImageValidator.validateUri(uri);
110
+ if (!uriValidation.isValid) {
111
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'enhanceLandscape');
112
+ }
113
+
114
+ return {
115
+ uri,
116
+ width: 0,
117
+ height: 0,
118
+ };
119
+ } catch (error) {
120
+ throw ImageErrorHandler.handleUnknownError(error, 'enhanceLandscape');
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Image Infrastructure - Metadata Service
3
+ *
4
+ * Extracts and manages image metadata including EXIF data
5
+ */
6
+
7
+ import type { ImageMetadataExtended } from '../../domain/entities/ImageFilterTypes';
8
+ import { ImageValidator } from '../utils/ImageValidator';
9
+ import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
10
+ import { MetadataExtractor } from '../utils/MetadataExtractor';
11
+
12
+ export interface ImageMetadataExtractionOptions {
13
+ includeExif?: boolean;
14
+ includeGPS?: boolean;
15
+ includeCamera?: boolean;
16
+ }
17
+
18
+ export class ImageMetadataService {
19
+
20
+ static async extractMetadata(
21
+ uri: string,
22
+ options: ImageMetadataExtractionOptions = {}
23
+ ): Promise<ImageMetadataExtended> {
24
+ try {
25
+ const uriValidation = ImageValidator.validateUri(uri);
26
+ if (!uriValidation.isValid) {
27
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'extractMetadata');
28
+ }
29
+
30
+ const {
31
+ includeExif = true,
32
+ includeGPS = true,
33
+ includeCamera = true,
34
+ } = options;
35
+
36
+ // Get basic image info
37
+ const dimensions = await MetadataExtractor.getImageDimensions(uri);
38
+ const size = await MetadataExtractor.getFileSize(uri);
39
+ const format = MetadataExtractor.detectFormat(uri);
40
+
41
+ // Build metadata object
42
+ const metadata: ImageMetadataExtended = {
43
+ format,
44
+ size,
45
+ dimensions,
46
+ colorSpace: 'sRGB',
47
+ hasAlpha: format === 'PNG' || format === 'WebP',
48
+ orientation: 1,
49
+ };
50
+
51
+ // Extract EXIF data if requested
52
+ if (includeExif) {
53
+ const exifData = await MetadataExtractor.extractExifData(uri);
54
+ if (exifData) {
55
+ metadata.creationDate = exifData.DateTimeOriginal ? new Date(exifData.DateTimeOriginal) : undefined;
56
+ metadata.modificationDate = new Date();
57
+
58
+ if (includeCamera) {
59
+ metadata.camera = {
60
+ make: exifData.Make,
61
+ model: exifData.Model,
62
+ iso: exifData.ISO,
63
+ flash: exifData.Flash,
64
+ focalLength: exifData.FocalLength,
65
+ };
66
+ }
67
+ }
68
+ }
69
+
70
+ // Extract GPS data if requested
71
+ if (includeGPS) {
72
+ const gps = await MetadataExtractor.extractGPSData(uri);
73
+ metadata.gps = gps || undefined;
74
+ }
75
+
76
+ return metadata;
77
+ } catch (error) {
78
+ throw ImageErrorHandler.handleUnknownError(error, 'extractMetadata');
79
+ }
80
+ }
81
+
82
+ static async getBasicInfo(uri: string): Promise<{
83
+ format: string;
84
+ size: number;
85
+ dimensions: { width: number; height: number };
86
+ }> {
87
+ try {
88
+ const uriValidation = ImageValidator.validateUri(uri);
89
+ if (!uriValidation.isValid) {
90
+ throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'getBasicInfo');
91
+ }
92
+
93
+ const dimensions = await MetadataExtractor.getImageDimensions(uri);
94
+ const size = await MetadataExtractor.getFileSize(uri);
95
+ const format = MetadataExtractor.detectFormat(uri);
96
+
97
+ return {
98
+ format,
99
+ size,
100
+ dimensions,
101
+ };
102
+ } catch (error) {
103
+ throw ImageErrorHandler.handleUnknownError(error, 'getBasicInfo');
104
+ }
105
+ }
106
+
107
+ static async hasMetadata(uri: string): Promise<boolean> {
108
+ try {
109
+ const exifData = await MetadataExtractor.extractExifData(uri);
110
+ const gpsData = await MetadataExtractor.extractGPSData(uri);
111
+ return !!(exifData || gpsData);
112
+ } catch {
113
+ return false;
114
+ }
115
+ }
116
+ }