@umituz/react-native-ai-generation-content 1.27.9 → 1.27.11

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.27.9",
3
+ "version": "1.27.11",
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",
@@ -7,11 +7,18 @@
7
7
  // Feature Type Enums
8
8
  // ============================================================================
9
9
 
10
- export type GenerationType = "image" | "video" | "meme";
10
+ export type GenerationType =
11
+ | "image"
12
+ | "video"
13
+ | "text-to-image"
14
+ | "text-to-video"
15
+ | "image-to-video"
16
+ | "meme";
11
17
 
12
18
  export type InputType =
13
19
  | "single-photo"
14
20
  | "two-photos"
21
+ | "text-only"
15
22
  | "text"
16
23
  | "photo-text"
17
24
  | "photo-photo-text";
@@ -33,6 +33,22 @@ export interface ImageGenerationInput {
33
33
  outputFormat?: "jpeg" | "png" | "webp";
34
34
  }
35
35
 
36
+ export interface TextToImageInput {
37
+ prompt: string;
38
+ negativePrompt?: string;
39
+ aspectRatio?: string;
40
+ size?: string;
41
+ numImages?: number;
42
+ guidanceScale?: number;
43
+ style?: string;
44
+ outputFormat?: "jpeg" | "png" | "webp";
45
+ }
46
+
47
+ export interface TextToImageOutput {
48
+ imageUrl: string;
49
+ imageUrls: string[];
50
+ }
51
+
36
52
  export interface VideoGenerationInput {
37
53
  sourceImageBase64: string;
38
54
  targetImageBase64?: string;
@@ -27,7 +27,11 @@ export type {
27
27
  VideoGenerationOutput,
28
28
  MemeGenerationInput,
29
29
  MemeGenerationOutput,
30
+ TextToImageInput,
31
+ TextToImageOutput,
30
32
  } from "./domain/generation.types";
31
33
 
34
+ export { ExecutorFactory, type GenerationType as ExecutorGenerationType } from "./infrastructure/executors/executor-factory";
35
+
32
36
  export * from "./wizard";
33
37
  export * from "./infrastructure/flow";
@@ -6,10 +6,11 @@
6
6
  import type { GenerationExecutor } from "../../domain/generation.types";
7
7
  import { ImageExecutor } from "./image-executor";
8
8
  import { VideoExecutor } from "./video-executor";
9
+ import { TextToImageExecutor } from "./text-to-image-executor";
9
10
 
10
11
  declare const __DEV__: boolean;
11
12
 
12
- type GenerationType = "image" | "video" | "meme";
13
+ export type GenerationType = "image" | "video" | "text-to-image" | "text-to-video" | "image-to-video" | "meme";
13
14
 
14
15
  export class ExecutorFactory {
15
16
  private static executors = new Map<
@@ -27,7 +28,12 @@ export class ExecutorFactory {
27
28
  case "image":
28
29
  this.executors.set(type, new ImageExecutor());
29
30
  break;
31
+ case "text-to-image":
32
+ this.executors.set(type, new TextToImageExecutor());
33
+ break;
30
34
  case "video":
35
+ case "text-to-video":
36
+ case "image-to-video":
31
37
  this.executors.set(type, new VideoExecutor());
32
38
  break;
33
39
  case "meme":
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Text-to-Image Executor
3
+ * Uses provider.run() for quick prompt-based image generation
4
+ */
5
+
6
+ import type {
7
+ GenerationExecutor,
8
+ GenerationOptions,
9
+ GenerationResult,
10
+ } from "../../domain/generation.types";
11
+ import { providerRegistry } from "../../../../infrastructure/services/provider-registry.service";
12
+
13
+ declare const __DEV__: boolean;
14
+
15
+ export interface TextToImageInput {
16
+ prompt: string;
17
+ negativePrompt?: string;
18
+ aspectRatio?: string;
19
+ size?: string;
20
+ numImages?: number;
21
+ guidanceScale?: number;
22
+ style?: string;
23
+ outputFormat?: "jpeg" | "png" | "webp";
24
+ }
25
+
26
+ export interface TextToImageOutput {
27
+ imageUrl: string;
28
+ imageUrls: string[];
29
+ }
30
+
31
+ export class TextToImageExecutor
32
+ implements GenerationExecutor<TextToImageInput, TextToImageOutput>
33
+ {
34
+ async generate(
35
+ model: string,
36
+ input: TextToImageInput,
37
+ options?: GenerationOptions,
38
+ ): Promise<GenerationResult<TextToImageOutput>> {
39
+ try {
40
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
41
+ console.log("[TextToImageExecutor] Starting", {
42
+ model,
43
+ promptLength: input.prompt?.length || 0,
44
+ aspectRatio: input.aspectRatio,
45
+ numImages: input.numImages,
46
+ });
47
+ }
48
+
49
+ const provider = providerRegistry.getActiveProvider();
50
+
51
+ if (!provider?.isInitialized()) {
52
+ return { success: false, error: "AI provider not initialized" };
53
+ }
54
+
55
+ if (!input.prompt?.trim()) {
56
+ return { success: false, error: "Prompt is required" };
57
+ }
58
+
59
+ options?.onProgress?.(10);
60
+
61
+ const modelInput = this.buildModelInput(input);
62
+ options?.onProgress?.(20);
63
+
64
+ const result = await provider.run(model, modelInput);
65
+ options?.onProgress?.(90);
66
+
67
+ const extracted = this.extractResult(result);
68
+ options?.onProgress?.(100);
69
+
70
+ if (!extracted?.imageUrl) {
71
+ return { success: false, error: "No image in response" };
72
+ }
73
+
74
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
75
+ console.log("[TextToImageExecutor] Success", {
76
+ imageCount: extracted.imageUrls.length,
77
+ });
78
+ }
79
+
80
+ return { success: true, data: extracted };
81
+ } catch (error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+
84
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
85
+ console.error("[TextToImageExecutor] Error:", message);
86
+ }
87
+
88
+ return { success: false, error: message };
89
+ }
90
+ }
91
+
92
+ private buildModelInput(input: TextToImageInput): Record<string, unknown> {
93
+ const {
94
+ prompt,
95
+ negativePrompt,
96
+ aspectRatio,
97
+ size,
98
+ numImages,
99
+ guidanceScale,
100
+ style,
101
+ outputFormat,
102
+ } = input;
103
+
104
+ let finalPrompt = prompt;
105
+ if (style && style !== "none") {
106
+ finalPrompt = `${prompt}, ${style} style`;
107
+ }
108
+
109
+ return {
110
+ prompt: finalPrompt,
111
+ ...(negativePrompt && { negative_prompt: negativePrompt }),
112
+ ...(aspectRatio && { aspect_ratio: aspectRatio }),
113
+ ...(size && { image_size: size }),
114
+ ...(numImages && { num_images: numImages }),
115
+ ...(guidanceScale && { guidance_scale: guidanceScale }),
116
+ ...(outputFormat && { output_format: outputFormat }),
117
+ };
118
+ }
119
+
120
+ private extractResult(
121
+ result: unknown,
122
+ ): { imageUrl: string; imageUrls: string[] } | undefined {
123
+ if (typeof result !== "object" || result === null) {
124
+ return undefined;
125
+ }
126
+
127
+ const r = result as Record<string, unknown>;
128
+
129
+ // Check nested 'data' object
130
+ if (r.data && typeof r.data === "object") {
131
+ const urls = this.extractImagesFromObject(r.data as Record<string, unknown>);
132
+ if (urls.length > 0) {
133
+ return { imageUrl: urls[0], imageUrls: urls };
134
+ }
135
+ }
136
+
137
+ // Check direct images array
138
+ const directUrls = this.extractImagesFromObject(r);
139
+ if (directUrls.length > 0) {
140
+ return { imageUrl: directUrls[0], imageUrls: directUrls };
141
+ }
142
+
143
+ // Check for imageUrl
144
+ if (typeof r.imageUrl === "string") {
145
+ return { imageUrl: r.imageUrl, imageUrls: [r.imageUrl] };
146
+ }
147
+
148
+ // Fallback: base64
149
+ if (typeof r.imageBase64 === "string") {
150
+ const mimeType = typeof r.mimeType === "string" ? r.mimeType : "image/png";
151
+ const dataUrl = `data:${mimeType};base64,${r.imageBase64}`;
152
+ return { imageUrl: dataUrl, imageUrls: [dataUrl] };
153
+ }
154
+
155
+ return undefined;
156
+ }
157
+
158
+ private extractImagesFromObject(obj: Record<string, unknown>): string[] {
159
+ if (!Array.isArray(obj.images)) {
160
+ return [];
161
+ }
162
+
163
+ return obj.images
164
+ .map((img) => {
165
+ if (typeof img === "string") return img;
166
+ if (img && typeof img === "object" && "url" in img) {
167
+ return (img as { url: string }).url;
168
+ }
169
+ return null;
170
+ })
171
+ .filter((url): url is string => url !== null);
172
+ }
173
+ }
@@ -39,6 +39,8 @@ export type TextToImageGenerationResult =
39
39
  | TextToImageGenerationResultError;
40
40
 
41
41
  export interface TextToImageCallbacks {
42
+ /** User ID for orchestrator - required for credit deduction */
43
+ userId: string | null;
42
44
  executeGeneration: (
43
45
  request: TextToImageGenerationRequest,
44
46
  ) => Promise<TextToImageGenerationResult>;
@@ -46,7 +46,10 @@ const DEFAULT_ALERT_MESSAGES: AlertMessages = {
46
46
  };
47
47
 
48
48
  export function useGeneration(options: UseGenerationOptions): UseGenerationReturn {
49
- const { formState, callbacks, userId, onPromptCleared } = options;
49
+ const { formState, callbacks, onPromptCleared } = options;
50
+
51
+ // Get userId from callbacks (from app layer via useAIFeatureCallbacks)
52
+ const userId = callbacks.userId ?? undefined;
50
53
 
51
54
  const totalCost = callbacks.calculateCost(formState.numImages, formState.selectedModel);
52
55
 
@@ -100,6 +103,24 @@ export function useGeneration(options: UseGenerationOptions): UseGenerationRetur
100
103
  return { success: false, error: "Prompt is required" };
101
104
  }
102
105
 
106
+ // Auth check BEFORE generation
107
+ if (!callbacks.isAuthenticated()) {
108
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
109
+ console.log("[TextToImage] Auth required");
110
+ }
111
+ callbacks.onAuthRequired?.();
112
+ return { success: false, error: "Authentication required" };
113
+ }
114
+
115
+ // Credit check BEFORE generation
116
+ if (!callbacks.canAfford(totalCost)) {
117
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
118
+ console.log("[TextToImage] Insufficient credits", { totalCost });
119
+ }
120
+ callbacks.onCreditsRequired?.(totalCost);
121
+ return { success: false, error: "Insufficient credits" };
122
+ }
123
+
103
124
  const request: TextToImageGenerationRequest = {
104
125
  prompt: trimmedPrompt,
105
126
  model: formState.selectedModel ?? undefined,
@@ -120,7 +141,7 @@ export function useGeneration(options: UseGenerationOptions): UseGenerationRetur
120
141
 
121
142
  // Return result based on orchestrator state
122
143
  return null; // Result handled via callbacks
123
- }, [formState, generate, callbacks]);
144
+ }, [formState, generate, callbacks, totalCost]);
124
145
 
125
146
  return {
126
147
  generationState: {
@@ -86,13 +86,20 @@ export const GenerateButton: React.FC<GenerateButtonProps> = ({
86
86
  >
87
87
  <View style={styles.buttonContent}>
88
88
  {isProcessing ? (
89
- <AtomicSpinner size="sm" color={tokens.colors.textInverse} />
89
+ <AtomicSpinner size="sm" color={disabled ? tokens.colors.textSecondary : tokens.colors.textInverse} />
90
90
  ) : (
91
- <AtomicIcon name={icon} customSize={iconSize} customColor={tokens.colors.textInverse} />
91
+ <AtomicIcon
92
+ name={icon}
93
+ customSize={iconSize}
94
+ customColor={disabled ? tokens.colors.textSecondary : tokens.colors.textInverse}
95
+ />
92
96
  )}
93
97
  <AtomicText
94
98
  type="bodyLarge"
95
- style={[styles.buttonText, { color: tokens.colors.textInverse }]}
99
+ style={[
100
+ styles.buttonText,
101
+ { color: disabled ? tokens.colors.textSecondary : tokens.colors.textInverse }
102
+ ]}
96
103
  >
97
104
  {finalDisplayText}
98
105
  </AtomicText>
@@ -45,6 +45,9 @@ export type AIFeatureGenerationResult =
45
45
  * Universal callbacks interface that maps to all feature-specific ones
46
46
  */
47
47
  export interface AIFeatureCallbacks<TRequest = unknown, TResult = unknown> {
48
+ // User state - needed by orchestrator
49
+ userId: string | null;
50
+
48
51
  // TextToImageCallbacks compatible
49
52
  executeGeneration: (request: TRequest) => Promise<AIFeatureGenerationResult>;
50
53
  calculateCost: (multiplier?: number, _model?: string | null) => number;
@@ -140,6 +143,7 @@ export function useAIFeatureCallbacks<TRequest = unknown, TResult = unknown>(
140
143
 
141
144
  return useMemo(
142
145
  () => ({
146
+ userId,
143
147
  executeGeneration,
144
148
  calculateCost,
145
149
  canAfford,
@@ -153,6 +157,7 @@ export function useAIFeatureCallbacks<TRequest = unknown, TResult = unknown>(
153
157
  onShowPaywall,
154
158
  }),
155
159
  [
160
+ userId,
156
161
  executeGeneration,
157
162
  calculateCost,
158
163
  canAfford,