@umituz/react-native-ai-generation-content 1.15.1 → 1.15.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.15.1",
3
+ "version": "1.15.3",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -0,0 +1 @@
1
+ export * from "./types";
@@ -0,0 +1,2 @@
1
+ export * from "./upscale.types";
2
+ export * from "./provider.types";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Upscale Provider Types
3
+ * Interface for provider implementations
4
+ */
5
+
6
+ import type { UpscaleRequest, UpscaleResult } from "./upscale.types";
7
+
8
+ export interface IUpscaleProvider {
9
+ readonly providerId: string;
10
+ readonly providerName: string;
11
+
12
+ isAvailable(): boolean;
13
+
14
+ upscale(
15
+ request: UpscaleRequest,
16
+ onProgress?: (progress: number) => void,
17
+ ): Promise<UpscaleResult>;
18
+ }
19
+
20
+ export interface UpscaleProviderEntry {
21
+ provider: IUpscaleProvider;
22
+ priority: number;
23
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Upscale Feature Types
3
+ * Request, Result, Config types for upscaling
4
+ */
5
+
6
+ export type UpscaleScaleFactor = 2 | 4 | 8;
7
+
8
+ export interface UpscaleOptions {
9
+ scaleFactor?: UpscaleScaleFactor;
10
+ enhanceFaces?: boolean;
11
+ }
12
+
13
+ export interface UpscaleRequest {
14
+ imageUri: string;
15
+ userId: string;
16
+ options?: UpscaleOptions;
17
+ }
18
+
19
+ export interface UpscaleResult {
20
+ success: boolean;
21
+ imageUrl?: string;
22
+ imageBase64?: string;
23
+ error?: string;
24
+ requestId?: string;
25
+ }
26
+
27
+ export interface UpscaleFeatureState {
28
+ imageUri: string | null;
29
+ processedUrl: string | null;
30
+ isProcessing: boolean;
31
+ progress: number;
32
+ error: string | null;
33
+ }
34
+
35
+ export interface UpscaleTranslations {
36
+ uploadTitle: string;
37
+ uploadSubtitle: string;
38
+ uploadChange: string;
39
+ uploadAnalyzing: string;
40
+ description: string;
41
+ processingText: string;
42
+ processButtonText: string;
43
+ successText: string;
44
+ saveButtonText: string;
45
+ tryAnotherText: string;
46
+ }
47
+
48
+ export interface UpscaleFeatureConfig {
49
+ providerId?: string;
50
+ defaultScaleFactor?: UpscaleScaleFactor;
51
+ creditCost?: number;
52
+ onImageSelect?: (uri: string) => void;
53
+ onProcessingStart?: () => void;
54
+ onProcessingComplete?: (result: UpscaleResult) => void;
55
+ onError?: (error: string) => void;
56
+ }
@@ -1 +1,40 @@
1
- export * from './domain/entities';
1
+ /**
2
+ * Upscaling Feature
3
+ * Provider-agnostic image upscaling feature
4
+ */
5
+
6
+ // Domain Types
7
+ export type {
8
+ UpscaleScaleFactor,
9
+ UpscaleOptions,
10
+ UpscaleRequest,
11
+ UpscaleResult,
12
+ UpscaleFeatureState,
13
+ UpscaleTranslations,
14
+ UpscaleFeatureConfig,
15
+ IUpscaleProvider,
16
+ UpscaleProviderEntry,
17
+ } from "./domain";
18
+
19
+ // Infrastructure Services
20
+ export {
21
+ upscaleProviderRegistry,
22
+ executeUpscale,
23
+ getAvailableProvider,
24
+ hasUpscaleProvider,
25
+ } from "./infrastructure";
26
+ export type { ExecuteUpscaleOptions } from "./infrastructure";
27
+
28
+ // Presentation Hooks
29
+ export { useUpscaleFeature } from "./presentation";
30
+ export type {
31
+ UseUpscaleFeatureProps,
32
+ UseUpscaleFeatureReturn,
33
+ } from "./presentation";
34
+
35
+ // Presentation Components
36
+ export { UpscaleFeature, UpscaleResultView } from "./presentation";
37
+ export type {
38
+ UpscaleFeatureProps,
39
+ UpscaleResultViewProps,
40
+ } from "./presentation";
@@ -0,0 +1 @@
1
+ export * from "./services";
@@ -0,0 +1,7 @@
1
+ export { upscaleProviderRegistry } from "./upscale-provider-registry";
2
+ export {
3
+ executeUpscale,
4
+ getAvailableProvider,
5
+ hasUpscaleProvider,
6
+ } from "./upscale-executor";
7
+ export type { ExecuteUpscaleOptions } from "./upscale-executor";
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Upscale Executor
3
+ * Executes upscale operations using registered providers
4
+ */
5
+
6
+ import { upscaleProviderRegistry } from "./upscale-provider-registry";
7
+ import type { UpscaleRequest, UpscaleResult } from "../../domain/types";
8
+
9
+ declare const __DEV__: boolean;
10
+
11
+ export interface ExecuteUpscaleOptions {
12
+ providerId?: string;
13
+ onProgress?: (progress: number) => void;
14
+ }
15
+
16
+ export async function executeUpscale(
17
+ request: UpscaleRequest,
18
+ options?: ExecuteUpscaleOptions,
19
+ ): Promise<UpscaleResult> {
20
+ const provider = upscaleProviderRegistry.get(options?.providerId);
21
+
22
+ if (!provider) {
23
+ return {
24
+ success: false,
25
+ error: "No upscale provider available",
26
+ };
27
+ }
28
+
29
+ if (!provider.isAvailable()) {
30
+ return {
31
+ success: false,
32
+ error: `Provider ${provider.providerId} is not available`,
33
+ };
34
+ }
35
+
36
+ if (__DEV__) {
37
+ // eslint-disable-next-line no-console
38
+ console.log(`[UpscaleExecutor] Using provider: ${provider.providerId}`);
39
+ }
40
+
41
+ try {
42
+ return await provider.upscale(request, options?.onProgress);
43
+ } catch (error) {
44
+ const message = error instanceof Error ? error.message : String(error);
45
+
46
+ if (__DEV__) {
47
+ // eslint-disable-next-line no-console
48
+ console.error("[UpscaleExecutor] Error:", message);
49
+ }
50
+
51
+ return {
52
+ success: false,
53
+ error: message,
54
+ };
55
+ }
56
+ }
57
+
58
+ export function getAvailableProvider(providerId?: string) {
59
+ return upscaleProviderRegistry.get(providerId);
60
+ }
61
+
62
+ export function hasUpscaleProvider(providerId: string): boolean {
63
+ return upscaleProviderRegistry.hasProvider(providerId);
64
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Upscale Provider Registry
3
+ * Manages registered upscale providers
4
+ */
5
+
6
+ import type {
7
+ IUpscaleProvider,
8
+ UpscaleProviderEntry,
9
+ } from "../../domain/types";
10
+
11
+ declare const __DEV__: boolean;
12
+
13
+ class UpscaleProviderRegistry {
14
+ private providers: Map<string, UpscaleProviderEntry> = new Map();
15
+ private defaultProviderId: string | null = null;
16
+
17
+ register(provider: IUpscaleProvider, priority = 0): void {
18
+ this.providers.set(provider.providerId, { provider, priority });
19
+
20
+ if (__DEV__) {
21
+ // eslint-disable-next-line no-console
22
+ console.log(
23
+ `[UpscaleRegistry] Registered provider: ${provider.providerId}`,
24
+ );
25
+ }
26
+ }
27
+
28
+ unregister(providerId: string): void {
29
+ this.providers.delete(providerId);
30
+ }
31
+
32
+ setDefault(providerId: string): void {
33
+ if (!this.providers.has(providerId)) {
34
+ throw new Error(`Provider ${providerId} not registered`);
35
+ }
36
+ this.defaultProviderId = providerId;
37
+ }
38
+
39
+ get(providerId?: string): IUpscaleProvider | null {
40
+ const id = providerId || this.defaultProviderId;
41
+ if (!id) return this.getHighestPriority();
42
+
43
+ const entry = this.providers.get(id);
44
+ return entry?.provider || null;
45
+ }
46
+
47
+ getAvailable(): IUpscaleProvider | null {
48
+ const sorted = this.getSortedProviders();
49
+ return sorted.find((p) => p.isAvailable()) || null;
50
+ }
51
+
52
+ hasProvider(providerId: string): boolean {
53
+ return this.providers.has(providerId);
54
+ }
55
+
56
+ getProviderIds(): string[] {
57
+ return Array.from(this.providers.keys());
58
+ }
59
+
60
+ private getHighestPriority(): IUpscaleProvider | null {
61
+ const sorted = this.getSortedProviders();
62
+ return sorted[0] || null;
63
+ }
64
+
65
+ private getSortedProviders(): IUpscaleProvider[] {
66
+ return Array.from(this.providers.values())
67
+ .sort((a, b) => b.priority - a.priority)
68
+ .map((entry) => entry.provider);
69
+ }
70
+
71
+ reset(): void {
72
+ this.providers.clear();
73
+ this.defaultProviderId = null;
74
+ }
75
+ }
76
+
77
+ export const upscaleProviderRegistry = new UpscaleProviderRegistry();
@@ -0,0 +1,168 @@
1
+ /**
2
+ * UpscaleFeature Component
3
+ * Main upscale feature UI component
4
+ */
5
+
6
+ import React, { useCallback, useMemo } from "react";
7
+ import { View, ScrollView, StyleSheet } from "react-native";
8
+ import {
9
+ useAppDesignTokens,
10
+ PhotoUploadCard,
11
+ AtomicText,
12
+ AtomicButton,
13
+ } from "@umituz/react-native-design-system";
14
+ import { UpscaleResultView } from "./UpscaleResultView";
15
+ import type {
16
+ UpscaleTranslations,
17
+ UpscaleFeatureConfig,
18
+ } from "../../domain/types";
19
+
20
+ export interface UpscaleFeatureProps {
21
+ imageUri: string | null;
22
+ processedUrl: string | null;
23
+ isProcessing: boolean;
24
+ progress: number;
25
+ error: string | null;
26
+ translations: UpscaleTranslations;
27
+ config?: UpscaleFeatureConfig;
28
+ onSelectImage: () => void;
29
+ onProcess: () => void;
30
+ onSave: () => void;
31
+ onReset: () => void;
32
+ renderProcessingModal?: (props: {
33
+ visible: boolean;
34
+ progress: number;
35
+ }) => React.ReactNode;
36
+ }
37
+
38
+ export const UpscaleFeature: React.FC<UpscaleFeatureProps> = ({
39
+ imageUri,
40
+ processedUrl,
41
+ isProcessing,
42
+ progress,
43
+ error,
44
+ translations,
45
+ onSelectImage,
46
+ onProcess,
47
+ onSave,
48
+ onReset,
49
+ renderProcessingModal,
50
+ }) => {
51
+ const tokens = useAppDesignTokens();
52
+
53
+ const photoTranslations = useMemo(
54
+ () => ({
55
+ tapToUpload: translations.uploadTitle,
56
+ selectPhoto: translations.uploadSubtitle,
57
+ change: translations.uploadChange,
58
+ analyzing: translations.uploadAnalyzing,
59
+ }),
60
+ [translations],
61
+ );
62
+
63
+ const handleProcess = useCallback(() => {
64
+ onProcess();
65
+ }, [onProcess]);
66
+
67
+ if (processedUrl) {
68
+ return (
69
+ <ScrollView
70
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
71
+ contentContainerStyle={styles.content}
72
+ showsVerticalScrollIndicator={false}
73
+ >
74
+ <UpscaleResultView
75
+ imageUrl={processedUrl}
76
+ translations={translations}
77
+ onSave={onSave}
78
+ onReset={onReset}
79
+ />
80
+ </ScrollView>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <>
86
+ <ScrollView
87
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
88
+ contentContainerStyle={styles.content}
89
+ showsVerticalScrollIndicator={false}
90
+ >
91
+ <AtomicText
92
+ type="bodyLarge"
93
+ style={[styles.description, { color: tokens.colors.textSecondary }]}
94
+ >
95
+ {translations.description}
96
+ </AtomicText>
97
+
98
+ <PhotoUploadCard
99
+ imageUri={imageUri}
100
+ onPress={onSelectImage}
101
+ isValidating={isProcessing}
102
+ disabled={isProcessing}
103
+ translations={photoTranslations}
104
+ config={{
105
+ aspectRatio: 1,
106
+ borderRadius: 24,
107
+ showValidationStatus: false,
108
+ allowChange: true,
109
+ }}
110
+ />
111
+
112
+ {error && (
113
+ <View
114
+ style={[
115
+ styles.errorContainer,
116
+ { backgroundColor: `${tokens.colors.error}15` },
117
+ ]}
118
+ >
119
+ <AtomicText type="bodyMedium" style={{ color: tokens.colors.error }}>
120
+ {error}
121
+ </AtomicText>
122
+ </View>
123
+ )}
124
+
125
+ <View style={styles.buttonContainer}>
126
+ <AtomicButton
127
+ title={
128
+ isProcessing
129
+ ? translations.processingText
130
+ : translations.processButtonText
131
+ }
132
+ onPress={handleProcess}
133
+ disabled={!imageUri || isProcessing}
134
+ variant="primary"
135
+ size="lg"
136
+ />
137
+ </View>
138
+ </ScrollView>
139
+
140
+ {renderProcessingModal?.({ visible: isProcessing, progress })}
141
+ </>
142
+ );
143
+ };
144
+
145
+ const styles = StyleSheet.create({
146
+ container: {
147
+ flex: 1,
148
+ },
149
+ content: {
150
+ paddingVertical: 16,
151
+ },
152
+ description: {
153
+ textAlign: "center",
154
+ marginHorizontal: 24,
155
+ marginBottom: 24,
156
+ lineHeight: 24,
157
+ },
158
+ errorContainer: {
159
+ marginHorizontal: 24,
160
+ marginBottom: 16,
161
+ padding: 16,
162
+ borderRadius: 12,
163
+ },
164
+ buttonContainer: {
165
+ marginHorizontal: 24,
166
+ marginTop: 8,
167
+ },
168
+ });
@@ -0,0 +1,94 @@
1
+ /**
2
+ * UpscaleResultView Component
3
+ * Displays the upscaled image result
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Image, StyleSheet } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicButton,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+ import type { UpscaleTranslations } from "../../domain/types";
14
+
15
+ export interface UpscaleResultViewProps {
16
+ imageUrl: string;
17
+ translations: Pick<
18
+ UpscaleTranslations,
19
+ "successText" | "saveButtonText" | "tryAnotherText"
20
+ >;
21
+ onSave: () => void;
22
+ onReset: () => void;
23
+ }
24
+
25
+ export const UpscaleResultView: React.FC<UpscaleResultViewProps> = ({
26
+ imageUrl,
27
+ translations,
28
+ onSave,
29
+ onReset,
30
+ }) => {
31
+ const tokens = useAppDesignTokens();
32
+
33
+ return (
34
+ <View style={styles.container}>
35
+ <AtomicText
36
+ type="headlineMedium"
37
+ style={[styles.title, { color: tokens.colors.success }]}
38
+ >
39
+ {translations.successText}
40
+ </AtomicText>
41
+
42
+ <View
43
+ style={[
44
+ styles.imageCard,
45
+ { backgroundColor: tokens.colors.surfaceSecondary },
46
+ ]}
47
+ >
48
+ <Image
49
+ source={{ uri: imageUrl }}
50
+ style={styles.image}
51
+ resizeMode="contain"
52
+ />
53
+ </View>
54
+
55
+ <View style={styles.actions}>
56
+ <AtomicButton
57
+ title={translations.saveButtonText}
58
+ onPress={onSave}
59
+ variant="primary"
60
+ size="lg"
61
+ />
62
+ <AtomicButton
63
+ title={translations.tryAnotherText}
64
+ onPress={onReset}
65
+ variant="secondary"
66
+ size="lg"
67
+ />
68
+ </View>
69
+ </View>
70
+ );
71
+ };
72
+
73
+ const styles = StyleSheet.create({
74
+ container: {
75
+ flex: 1,
76
+ paddingHorizontal: 24,
77
+ },
78
+ title: {
79
+ textAlign: "center",
80
+ marginBottom: 24,
81
+ },
82
+ imageCard: {
83
+ borderRadius: 24,
84
+ overflow: "hidden",
85
+ marginBottom: 24,
86
+ },
87
+ image: {
88
+ width: "100%",
89
+ aspectRatio: 1,
90
+ },
91
+ actions: {
92
+ gap: 12,
93
+ },
94
+ });
@@ -0,0 +1,4 @@
1
+ export { UpscaleFeature } from "./UpscaleFeature";
2
+ export { UpscaleResultView } from "./UpscaleResultView";
3
+ export type { UpscaleFeatureProps } from "./UpscaleFeature";
4
+ export type { UpscaleResultViewProps } from "./UpscaleResultView";
@@ -0,0 +1,5 @@
1
+ export { useUpscaleFeature } from "./useUpscaleFeature";
2
+ export type {
3
+ UseUpscaleFeatureProps,
4
+ UseUpscaleFeatureReturn,
5
+ } from "./useUpscaleFeature";
@@ -0,0 +1,133 @@
1
+ /**
2
+ * useUpscaleFeature Hook
3
+ * Manages upscale feature state and actions
4
+ */
5
+
6
+ import { useState, useCallback } from "react";
7
+ import { executeUpscale } from "../../infrastructure/services";
8
+ import type {
9
+ UpscaleFeatureState,
10
+ UpscaleFeatureConfig,
11
+ UpscaleResult,
12
+ } from "../../domain/types";
13
+
14
+ declare const __DEV__: boolean;
15
+
16
+ export interface UseUpscaleFeatureProps {
17
+ config?: UpscaleFeatureConfig;
18
+ userId: string;
19
+ onSelectImage: () => Promise<string | null>;
20
+ onSaveImage: (imageUrl: string) => Promise<void>;
21
+ }
22
+
23
+ export interface UseUpscaleFeatureReturn extends UpscaleFeatureState {
24
+ selectImage: () => Promise<void>;
25
+ process: () => Promise<void>;
26
+ save: () => Promise<void>;
27
+ reset: () => void;
28
+ }
29
+
30
+ const initialState: UpscaleFeatureState = {
31
+ imageUri: null,
32
+ processedUrl: null,
33
+ isProcessing: false,
34
+ progress: 0,
35
+ error: null,
36
+ };
37
+
38
+ export function useUpscaleFeature(
39
+ props: UseUpscaleFeatureProps,
40
+ ): UseUpscaleFeatureReturn {
41
+ const { config, userId, onSelectImage, onSaveImage } = props;
42
+ const [state, setState] = useState<UpscaleFeatureState>(initialState);
43
+
44
+ const selectImage = useCallback(async () => {
45
+ try {
46
+ const uri = await onSelectImage();
47
+ if (uri) {
48
+ setState((prev) => ({ ...prev, imageUri: uri, error: null }));
49
+ config?.onImageSelect?.(uri);
50
+ }
51
+ } catch (error) {
52
+ const message = error instanceof Error ? error.message : String(error);
53
+ setState((prev) => ({ ...prev, error: message }));
54
+ }
55
+ }, [onSelectImage, config]);
56
+
57
+ const handleProgress = useCallback((progress: number) => {
58
+ setState((prev) => ({ ...prev, progress }));
59
+ }, []);
60
+
61
+ const process = useCallback(async () => {
62
+ if (!state.imageUri) return;
63
+
64
+ setState((prev) => ({
65
+ ...prev,
66
+ isProcessing: true,
67
+ progress: 0,
68
+ error: null,
69
+ }));
70
+
71
+ config?.onProcessingStart?.();
72
+
73
+ if (__DEV__) {
74
+ // eslint-disable-next-line no-console
75
+ console.log("[useUpscaleFeature] Starting upscale process");
76
+ }
77
+
78
+ const result: UpscaleResult = await executeUpscale(
79
+ {
80
+ imageUri: state.imageUri,
81
+ userId,
82
+ options: { scaleFactor: config?.defaultScaleFactor || 2 },
83
+ },
84
+ {
85
+ providerId: config?.providerId,
86
+ onProgress: handleProgress,
87
+ },
88
+ );
89
+
90
+ if (result.success && result.imageUrl) {
91
+ const url = result.imageUrl;
92
+ setState((prev) => ({
93
+ ...prev,
94
+ isProcessing: false,
95
+ processedUrl: url,
96
+ progress: 100,
97
+ }));
98
+ config?.onProcessingComplete?.(result);
99
+ } else {
100
+ const errorMessage = result.error || "Processing failed";
101
+ setState((prev) => ({
102
+ ...prev,
103
+ isProcessing: false,
104
+ error: errorMessage,
105
+ progress: 0,
106
+ }));
107
+ config?.onError?.(errorMessage);
108
+ }
109
+ }, [state.imageUri, userId, config, handleProgress]);
110
+
111
+ const save = useCallback(async () => {
112
+ if (!state.processedUrl) return;
113
+
114
+ try {
115
+ await onSaveImage(state.processedUrl);
116
+ } catch (error) {
117
+ const message = error instanceof Error ? error.message : String(error);
118
+ setState((prev) => ({ ...prev, error: message }));
119
+ }
120
+ }, [state.processedUrl, onSaveImage]);
121
+
122
+ const reset = useCallback(() => {
123
+ setState(initialState);
124
+ }, []);
125
+
126
+ return {
127
+ ...state,
128
+ selectImage,
129
+ process,
130
+ save,
131
+ reset,
132
+ };
133
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./hooks";
2
+ export * from "./components";
package/src/index.ts CHANGED
@@ -297,9 +297,14 @@ export * from "./domains/creations";
297
297
  export * from "./domains/face-detection";
298
298
 
299
299
  // =============================================================================
300
- // DOMAINS - Feature Background
300
+ // FEATURES - Background
301
301
  // =============================================================================
302
302
 
303
303
  export * from "./features/background";
304
304
 
305
+ // =============================================================================
306
+ // FEATURES - Upscaling
307
+ // =============================================================================
308
+
309
+ export * from "./features/upscaling";
305
310
 
@@ -1,42 +0,0 @@
1
- /**
2
- * Upscaling Domain Entities
3
- */
4
-
5
- export interface UpscaleConfig {
6
- /**
7
- * The factor to upscale the image by
8
- * @default 2
9
- */
10
- scaleFactor?: 2 | 4;
11
-
12
- /**
13
- * Whether to enhance faces during upscaling
14
- * @default false
15
- */
16
- enhanceFaces?: boolean;
17
- }
18
-
19
- export interface UpscaleRequest {
20
- /**
21
- * The image to upscale.
22
- * Can be a Base64 string or a remote URL.
23
- */
24
- image: string;
25
-
26
- /**
27
- * Optional configuration
28
- */
29
- options?: UpscaleConfig;
30
- }
31
-
32
- export interface UpscaleResult {
33
- /**
34
- * The upscaled image URL or Base64
35
- */
36
- imageUrl: string;
37
-
38
- /**
39
- * Metadata about the generation
40
- */
41
- metadata?: Record<string, unknown>;
42
- }