@umituz/react-native-ai-generation-content 1.17.310 → 1.18.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.17.310",
3
+ "version": "1.18.1",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -59,6 +59,7 @@ export interface AnimeSelfieTranslations {
59
59
  export type AnimeSelfieResultExtractor = (result: unknown) => string | undefined;
60
60
 
61
61
  export interface AnimeSelfieFeatureConfig {
62
+ featureType: "anime-selfie";
62
63
  creditCost?: number;
63
64
  defaultStyle?: AnimeSelfieStyle;
64
65
  extractResult?: AnimeSelfieResultExtractor;
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * useAnimeSelfieFeature Hook
3
- * Manages anime selfie feature state and actions
3
+ * Uses base single image hook for anime selfie transformation
4
+ * Uses centralized orchestrator for credit/error handling
4
5
  */
5
6
 
6
- import { useState, useCallback, useRef, useMemo } from "react";
7
- import { generateUUID } from "@umituz/react-native-design-system";
8
- import { executeImageFeature } from "../../../../infrastructure/services";
7
+ import { useMemo } from "react";
8
+ import {
9
+ useSingleImageFeature,
10
+ type BaseSingleImageHookReturn,
11
+ } from "../../../image-to-image";
12
+ import type { AlertMessages } from "../../../../presentation/hooks/generation";
9
13
  import { createAnimeSelfiePrompt } from "../../../../domains/prompts";
10
- import type {
11
- AnimeSelfieFeatureState,
12
- AnimeSelfieFeatureConfig,
13
- AnimeSelfieResult,
14
- } from "../../domain/types";
14
+ import type { AnimeSelfieFeatureConfig } from "../../domain/types";
15
15
 
16
16
  export interface UseAnimeSelfieFeatureProps {
17
17
  config: AnimeSelfieFeatureConfig;
@@ -20,135 +20,40 @@ export interface UseAnimeSelfieFeatureProps {
20
20
  onBeforeProcess?: () => Promise<boolean>;
21
21
  }
22
22
 
23
- export interface UseAnimeSelfieFeatureReturn extends AnimeSelfieFeatureState {
24
- selectImage: () => Promise<void>;
25
- process: () => Promise<void>;
26
- save: () => Promise<void>;
27
- reset: () => void;
23
+ export interface UseAnimeSelfieFeatureOptions {
24
+ /** Alert messages for error handling */
25
+ alertMessages?: AlertMessages;
26
+ /** User ID for credit operations */
27
+ userId?: string;
28
+ /** Callback when credits are exhausted */
29
+ onCreditsExhausted?: () => void;
28
30
  }
29
31
 
30
- const initialState: AnimeSelfieFeatureState = {
31
- imageUri: null,
32
- processedUrl: null,
33
- isProcessing: false,
34
- progress: 0,
35
- error: null,
36
- };
32
+ export interface UseAnimeSelfieFeatureReturn extends BaseSingleImageHookReturn {}
37
33
 
38
34
  export function useAnimeSelfieFeature(
39
35
  props: UseAnimeSelfieFeatureProps,
36
+ options?: UseAnimeSelfieFeatureOptions,
40
37
  ): UseAnimeSelfieFeatureReturn {
41
38
  const { config, onSelectImage, onSaveImage, onBeforeProcess } = props;
42
- const [state, setState] = useState<AnimeSelfieFeatureState>(initialState);
43
- const creationIdRef = useRef<string | null>(null);
44
39
 
45
40
  const promptConfig = useMemo(
46
41
  () => createAnimeSelfiePrompt(config.defaultStyle),
47
42
  [config.defaultStyle],
48
43
  );
49
44
 
50
- const selectImage = useCallback(async () => {
51
- try {
52
- const uri = await onSelectImage();
53
- if (uri) {
54
- setState((prev) => ({ ...prev, imageUri: uri, error: null }));
55
- config.onImageSelect?.(uri);
56
- }
57
- } catch (error) {
58
- const message = error instanceof Error ? error.message : String(error);
59
- setState((prev) => ({ ...prev, error: message }));
60
- }
61
- }, [onSelectImage, config]);
62
-
63
- const handleProgress = useCallback((progress: number) => {
64
- setState((prev) => ({ ...prev, progress }));
65
- }, []);
66
-
67
- const process = useCallback(async () => {
68
- if (!state.imageUri) return;
69
-
70
- if (onBeforeProcess) {
71
- const canProceed = await onBeforeProcess();
72
- if (!canProceed) return;
73
- }
74
-
75
- const creationId = generateUUID();
76
- creationIdRef.current = creationId;
77
-
78
- setState((prev) => ({
79
- ...prev,
80
- isProcessing: true,
81
- progress: 0,
82
- error: null,
83
- }));
84
-
85
- config.onProcessingStart?.({ creationId, imageUri: state.imageUri });
86
-
87
- try {
88
- const imageBase64 = await config.prepareImage(state.imageUri);
89
-
90
- const result = await executeImageFeature(
91
- "anime-selfie",
92
- {
93
- imageBase64,
94
- prompt: promptConfig.prompt,
95
- options: {
96
- guidance_scale: promptConfig.guidance_scale,
97
- },
45
+ // Cast config to any to bypass strict type checking while maintaining runtime behavior
46
+ return useSingleImageFeature(
47
+ { config: config as never, onSelectImage, onSaveImage, onBeforeProcess },
48
+ {
49
+ buildInput: (imageBase64) => ({
50
+ imageBase64,
51
+ prompt: promptConfig.prompt,
52
+ options: {
53
+ guidance_scale: promptConfig.guidance_scale,
98
54
  },
99
- { extractResult: config.extractResult, onProgress: handleProgress },
100
- );
101
-
102
- if (result.success && result.imageUrl) {
103
- setState((prev) => ({
104
- ...prev,
105
- isProcessing: false,
106
- processedUrl: result.imageUrl!,
107
- progress: 100,
108
- }));
109
- config.onProcessingComplete?.({ ...result, creationId } as AnimeSelfieResult);
110
- } else {
111
- const errorMessage = result.error || "Processing failed";
112
- setState((prev) => ({
113
- ...prev,
114
- isProcessing: false,
115
- error: errorMessage,
116
- progress: 0,
117
- }));
118
- config.onError?.(errorMessage, creationId);
119
- }
120
- } catch (error) {
121
- const errorMessage = error instanceof Error ? error.message : String(error);
122
- setState((prev) => ({
123
- ...prev,
124
- isProcessing: false,
125
- error: errorMessage,
126
- progress: 0,
127
- }));
128
- config.onError?.(errorMessage, creationIdRef.current ?? undefined);
129
- }
130
- }, [state.imageUri, config, handleProgress, onBeforeProcess, promptConfig]);
131
-
132
- const save = useCallback(async () => {
133
- if (!state.processedUrl) return;
134
-
135
- try {
136
- await onSaveImage(state.processedUrl);
137
- } catch (error) {
138
- const message = error instanceof Error ? error.message : String(error);
139
- setState((prev) => ({ ...prev, error: message }));
140
- }
141
- }, [state.processedUrl, onSaveImage]);
142
-
143
- const reset = useCallback(() => {
144
- setState(initialState);
145
- }, []);
146
-
147
- return {
148
- ...state,
149
- selectImage,
150
- process,
151
- save,
152
- reset,
153
- };
55
+ }),
56
+ ...options,
57
+ },
58
+ );
154
59
  }
@@ -51,6 +51,7 @@ export interface HDTouchUpTranslations {
51
51
  export type HDTouchUpResultExtractor = (result: unknown) => string | undefined;
52
52
 
53
53
  export interface HDTouchUpFeatureConfig {
54
+ featureType: "hd-touch-up";
54
55
  creditCost?: number;
55
56
  extractResult?: HDTouchUpResultExtractor;
56
57
  prepareImage: (imageUri: string) => Promise<string>;
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * useHDTouchUpFeature Hook
3
- * Manages HD touch up feature state and actions
3
+ * Uses base single image hook for HD touch up
4
+ * Uses centralized orchestrator for credit/error handling
4
5
  */
5
6
 
6
- import { useState, useCallback } from "react";
7
- import { executeImageFeature } from "../../../../infrastructure/services";
8
- import type {
9
- HDTouchUpFeatureState,
10
- HDTouchUpFeatureConfig,
11
- HDTouchUpResult,
12
- } from "../../domain/types";
7
+ import {
8
+ useSingleImageFeature,
9
+ type BaseSingleImageHookReturn,
10
+ } from "../../../image-to-image";
11
+ import type { AlertMessages } from "../../../../presentation/hooks/generation";
12
+ import type { HDTouchUpFeatureConfig } from "../../domain/types";
13
13
 
14
14
  export interface UseHDTouchUpFeatureProps {
15
15
  config: HDTouchUpFeatureConfig;
@@ -18,109 +18,29 @@ export interface UseHDTouchUpFeatureProps {
18
18
  onBeforeProcess?: () => Promise<boolean>;
19
19
  }
20
20
 
21
- export interface UseHDTouchUpFeatureReturn extends HDTouchUpFeatureState {
22
- selectImage: () => Promise<void>;
23
- process: () => Promise<void>;
24
- save: () => Promise<void>;
25
- reset: () => void;
21
+ export interface UseHDTouchUpFeatureOptions {
22
+ /** Alert messages for error handling */
23
+ alertMessages?: AlertMessages;
24
+ /** User ID for credit operations */
25
+ userId?: string;
26
+ /** Callback when credits are exhausted */
27
+ onCreditsExhausted?: () => void;
26
28
  }
27
29
 
28
- const initialState: HDTouchUpFeatureState = {
29
- imageUri: null,
30
- processedUrl: null,
31
- isProcessing: false,
32
- progress: 0,
33
- error: null,
34
- };
30
+ export interface UseHDTouchUpFeatureReturn extends BaseSingleImageHookReturn {}
35
31
 
36
32
  export function useHDTouchUpFeature(
37
33
  props: UseHDTouchUpFeatureProps,
34
+ options?: UseHDTouchUpFeatureOptions,
38
35
  ): UseHDTouchUpFeatureReturn {
39
36
  const { config, onSelectImage, onSaveImage, onBeforeProcess } = props;
40
- const [state, setState] = useState<HDTouchUpFeatureState>(initialState);
41
37
 
42
- const selectImage = useCallback(async () => {
43
- try {
44
- const uri = await onSelectImage();
45
- if (uri) {
46
- setState((prev) => ({ ...prev, imageUri: uri, error: null }));
47
- config.onImageSelect?.(uri);
48
- }
49
- } catch (error) {
50
- const message = error instanceof Error ? error.message : String(error);
51
- setState((prev) => ({ ...prev, error: message }));
52
- }
53
- }, [onSelectImage, config]);
54
-
55
- const handleProgress = useCallback((progress: number) => {
56
- setState((prev) => ({ ...prev, progress }));
57
- }, []);
58
-
59
- const process = useCallback(async () => {
60
- if (!state.imageUri) return;
61
-
62
- if (onBeforeProcess) {
63
- const canProceed = await onBeforeProcess();
64
- if (!canProceed) return;
65
- }
66
-
67
- setState((prev) => ({
68
- ...prev,
69
- isProcessing: true,
70
- progress: 0,
71
- error: null,
72
- }));
73
-
74
- config.onProcessingStart?.();
75
-
76
- const imageBase64 = await config.prepareImage(state.imageUri);
77
-
78
- const result = await executeImageFeature(
79
- "hd-touch-up",
80
- { imageBase64 },
81
- { extractResult: config.extractResult, onProgress: handleProgress },
82
- );
83
-
84
- if (result.success && result.imageUrl) {
85
- setState((prev) => ({
86
- ...prev,
87
- isProcessing: false,
88
- processedUrl: result.imageUrl!,
89
- progress: 100,
90
- }));
91
- config.onProcessingComplete?.(result as HDTouchUpResult);
92
- } else {
93
- const errorMessage = result.error || "Processing failed";
94
- setState((prev) => ({
95
- ...prev,
96
- isProcessing: false,
97
- error: errorMessage,
98
- progress: 0,
99
- }));
100
- config.onError?.(errorMessage);
101
- }
102
- }, [state.imageUri, config, handleProgress, onBeforeProcess]);
103
-
104
- const save = useCallback(async () => {
105
- if (!state.processedUrl) return;
106
-
107
- try {
108
- await onSaveImage(state.processedUrl);
109
- } catch (error) {
110
- const message = error instanceof Error ? error.message : String(error);
111
- setState((prev) => ({ ...prev, error: message }));
112
- }
113
- }, [state.processedUrl, onSaveImage]);
114
-
115
- const reset = useCallback(() => {
116
- setState(initialState);
117
- }, []);
118
-
119
- return {
120
- ...state,
121
- selectImage,
122
- process,
123
- save,
124
- reset,
125
- };
38
+ // Cast config to any to bypass strict type checking while maintaining runtime behavior
39
+ return useSingleImageFeature(
40
+ { config: config as never, onSelectImage, onSaveImage, onBeforeProcess },
41
+ {
42
+ buildInput: (imageBase64) => ({ imageBase64 }),
43
+ ...options,
44
+ },
45
+ );
126
46
  }
@@ -1,34 +1,50 @@
1
1
  /**
2
2
  * useDualImageFeature Hook Factory
3
3
  * Base hook for dual image processing features (e.g., face-swap)
4
+ * Uses centralized orchestrator for credit/error handling
4
5
  */
5
6
 
6
- import { useState, useCallback, useRef } from "react";
7
+ import { useState, useCallback, useRef, useMemo } from "react";
7
8
  import { generateUUID } from "@umituz/react-native-design-system";
8
9
  import { executeImageFeature } from "../../../../infrastructure/services";
10
+ import {
11
+ useGenerationOrchestrator,
12
+ type GenerationStrategy,
13
+ type AlertMessages,
14
+ } from "../../../../presentation/hooks/generation";
9
15
  import type {
10
- BaseDualImageState,
11
16
  BaseDualImageHookProps,
12
17
  BaseDualImageHookReturn,
13
18
  DualImageConfig,
14
19
  BaseImageResult,
15
20
  } from "../../domain/types";
16
21
 
17
- const INITIAL_STATE: BaseDualImageState = {
18
- sourceImageUri: null,
19
- targetImageUri: null,
20
- processedUrl: null,
21
- isProcessing: false,
22
- progress: 0,
23
- error: null,
24
- };
25
-
26
22
  export interface DualImageFeatureOptions<TConfig extends DualImageConfig> {
27
23
  buildInput?: (
28
24
  sourceBase64: string,
29
25
  targetBase64: string,
30
26
  config: TConfig,
31
27
  ) => Record<string, unknown>;
28
+ /** Alert messages for error handling */
29
+ alertMessages?: AlertMessages;
30
+ /** User ID for credit operations */
31
+ userId?: string;
32
+ /** Callback when credits are exhausted */
33
+ onCreditsExhausted?: () => void;
34
+ }
35
+
36
+ const DEFAULT_ALERT_MESSAGES: AlertMessages = {
37
+ networkError: "No internet connection. Please check your network.",
38
+ policyViolation: "Content not allowed. Please try different images.",
39
+ saveFailed: "Failed to save result. Please try again.",
40
+ creditFailed: "Credit operation failed. Please try again.",
41
+ unknown: "An error occurred. Please try again.",
42
+ };
43
+
44
+ interface DualImageInput {
45
+ sourceImageBase64: string;
46
+ targetImageBase64: string;
47
+ options?: Record<string, unknown>;
32
48
  }
33
49
 
34
50
  export function useDualImageFeature<
@@ -39,19 +55,65 @@ export function useDualImageFeature<
39
55
  options?: DualImageFeatureOptions<TConfig>,
40
56
  ): BaseDualImageHookReturn {
41
57
  const { config, onSelectSourceImage, onSelectTargetImage, onSaveImage, onBeforeProcess } = props;
42
- const [state, setState] = useState<BaseDualImageState>(INITIAL_STATE);
58
+
59
+ // Image selection state (separate from orchestrator state)
60
+ const [sourceImageUri, setSourceImageUri] = useState<string | null>(null);
61
+ const [targetImageUri, setTargetImageUri] = useState<string | null>(null);
62
+ const [imageError, setImageError] = useState<string | null>(null);
43
63
  const creationIdRef = useRef<string | null>(null);
44
64
 
65
+ // Create strategy for orchestrator
66
+ const strategy: GenerationStrategy<DualImageInput, string> = useMemo(
67
+ () => ({
68
+ execute: async (input, onProgress) => {
69
+ const executorInput = input.options
70
+ ? { ...input.options }
71
+ : { imageBase64: input.sourceImageBase64, targetImageBase64: input.targetImageBase64 };
72
+
73
+ const result = await executeImageFeature(
74
+ config.featureType,
75
+ executorInput,
76
+ { extractResult: config.extractResult, onProgress },
77
+ );
78
+
79
+ if (!result.success || !result.imageUrl) {
80
+ throw new Error(result.error || "Processing failed");
81
+ }
82
+
83
+ // Notify completion with creationId
84
+ const creationId = creationIdRef.current;
85
+ if (creationId) {
86
+ config.onProcessingComplete?.({ ...result, creationId } as unknown as TResult);
87
+ }
88
+
89
+ return result.imageUrl;
90
+ },
91
+ getCreditCost: () => config.creditCost || 1,
92
+ }),
93
+ [config],
94
+ );
95
+
96
+ // Use orchestrator for generation
97
+ const orchestrator = useGenerationOrchestrator(strategy, {
98
+ userId: options?.userId,
99
+ alertMessages: options?.alertMessages || DEFAULT_ALERT_MESSAGES,
100
+ onCreditsExhausted: options?.onCreditsExhausted,
101
+ onError: (error) => {
102
+ config.onError?.(error.message, creationIdRef.current ?? undefined);
103
+ },
104
+ });
105
+
45
106
  const selectSourceImage = useCallback(async () => {
46
107
  try {
47
108
  const uri = await onSelectSourceImage();
48
109
  if (uri) {
49
- setState((prev) => ({ ...prev, sourceImageUri: uri, error: null }));
110
+ setSourceImageUri(uri);
111
+ setImageError(null);
50
112
  config.onSourceImageSelect?.(uri);
51
113
  }
52
114
  } catch (error) {
53
115
  const message = error instanceof Error ? error.message : String(error);
54
- setState((prev) => ({ ...prev, error: message }));
116
+ setImageError(message);
55
117
  }
56
118
  }, [onSelectSourceImage, config]);
57
119
 
@@ -59,21 +121,18 @@ export function useDualImageFeature<
59
121
  try {
60
122
  const uri = await onSelectTargetImage();
61
123
  if (uri) {
62
- setState((prev) => ({ ...prev, targetImageUri: uri, error: null }));
124
+ setTargetImageUri(uri);
125
+ setImageError(null);
63
126
  config.onTargetImageSelect?.(uri);
64
127
  }
65
128
  } catch (error) {
66
129
  const message = error instanceof Error ? error.message : String(error);
67
- setState((prev) => ({ ...prev, error: message }));
130
+ setImageError(message);
68
131
  }
69
132
  }, [onSelectTargetImage, config]);
70
133
 
71
- const handleProgress = useCallback((progress: number) => {
72
- setState((prev) => ({ ...prev, progress }));
73
- }, []);
74
-
75
134
  const process = useCallback(async () => {
76
- if (!state.sourceImageUri || !state.targetImageUri) return;
135
+ if (!sourceImageUri || !targetImageUri) return;
77
136
 
78
137
  if (onBeforeProcess) {
79
138
  const canProceed = await onBeforeProcess();
@@ -83,82 +142,59 @@ export function useDualImageFeature<
83
142
  const creationId = generateUUID();
84
143
  creationIdRef.current = creationId;
85
144
 
86
- setState((prev) => ({
87
- ...prev,
88
- isProcessing: true,
89
- progress: 0,
90
- error: null,
91
- }));
92
-
93
145
  config.onProcessingStart?.({
94
146
  creationId,
95
- sourceImageUri: state.sourceImageUri,
96
- targetImageUri: state.targetImageUri,
147
+ sourceImageUri,
148
+ targetImageUri,
97
149
  });
98
150
 
99
151
  try {
100
152
  const [sourceBase64, targetBase64] = await Promise.all([
101
- config.prepareImage(state.sourceImageUri),
102
- config.prepareImage(state.targetImageUri),
153
+ config.prepareImage(sourceImageUri),
154
+ config.prepareImage(targetImageUri),
103
155
  ]);
104
156
 
105
- const input = options?.buildInput
106
- ? options.buildInput(sourceBase64, targetBase64, config)
157
+ const input: DualImageInput = options?.buildInput
158
+ ? {
159
+ sourceImageBase64: sourceBase64,
160
+ targetImageBase64: targetBase64,
161
+ options: options.buildInput(sourceBase64, targetBase64, config),
162
+ }
107
163
  : { sourceImageBase64: sourceBase64, targetImageBase64: targetBase64 };
108
164
 
109
- const result = await executeImageFeature(
110
- config.featureType,
111
- input,
112
- { extractResult: config.extractResult, onProgress: handleProgress },
113
- );
114
-
115
- if (result.success && result.imageUrl) {
116
- setState((prev) => ({
117
- ...prev,
118
- isProcessing: false,
119
- processedUrl: result.imageUrl!,
120
- progress: 100,
121
- }));
122
- config.onProcessingComplete?.({ ...result, creationId } as unknown as TResult);
123
- } else {
124
- const errorMessage = result.error || "Processing failed";
125
- setState((prev) => ({
126
- ...prev,
127
- isProcessing: false,
128
- error: errorMessage,
129
- progress: 0,
130
- }));
131
- config.onError?.(errorMessage, creationId);
132
- }
165
+ await orchestrator.generate(input);
133
166
  } catch (error) {
134
- const message = error instanceof Error ? error.message : String(error);
135
- setState((prev) => ({
136
- ...prev,
137
- isProcessing: false,
138
- error: message,
139
- progress: 0,
140
- }));
141
- config.onError?.(message, creationIdRef.current ?? undefined);
167
+ // Error already handled by orchestrator
142
168
  }
143
- }, [state.sourceImageUri, state.targetImageUri, config, options, handleProgress, onBeforeProcess]);
169
+ }, [sourceImageUri, targetImageUri, config, options, onBeforeProcess, orchestrator]);
144
170
 
145
171
  const save = useCallback(async () => {
146
- if (!state.processedUrl) return;
172
+ if (!orchestrator.result) return;
147
173
 
148
174
  try {
149
- await onSaveImage(state.processedUrl);
175
+ await onSaveImage(orchestrator.result);
150
176
  } catch (error) {
151
177
  const message = error instanceof Error ? error.message : String(error);
152
- setState((prev) => ({ ...prev, error: message }));
178
+ setImageError(message);
153
179
  }
154
- }, [state.processedUrl, onSaveImage]);
180
+ }, [orchestrator.result, onSaveImage]);
155
181
 
156
182
  const reset = useCallback(() => {
157
- setState(INITIAL_STATE);
158
- }, []);
159
-
183
+ setSourceImageUri(null);
184
+ setTargetImageUri(null);
185
+ setImageError(null);
186
+ creationIdRef.current = null;
187
+ orchestrator.reset();
188
+ }, [orchestrator]);
189
+
190
+ // Combine states for backward compatibility
160
191
  return {
161
- ...state,
192
+ sourceImageUri,
193
+ targetImageUri,
194
+ processedUrl: orchestrator.result,
195
+ isProcessing: orchestrator.isGenerating,
196
+ progress: orchestrator.progress,
197
+ error: orchestrator.error?.message || imageError,
162
198
  selectSourceImage,
163
199
  selectTargetImage,
164
200
  process,