@umituz/react-native-ai-generation-content 1.27.14 → 1.27.16

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.14",
3
+ "version": "1.27.16",
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",
@@ -38,8 +38,7 @@
38
38
  "url": "git+https://github.com/umituz/react-native-ai-generation-content.git"
39
39
  },
40
40
  "dependencies": {
41
- "@umituz/react-native-auth": "*",
42
- "@umituz/react-native-firebase": "*"
41
+ "@umituz/react-native-auth": "^3.6.25"
43
42
  },
44
43
  "peerDependencies": {
45
44
  "@react-navigation/native": ">=6.0.0",
@@ -68,7 +67,7 @@
68
67
  "@typescript-eslint/eslint-plugin": "^8.0.0",
69
68
  "@typescript-eslint/parser": "^8.0.0",
70
69
  "@umituz/react-native-design-system": "^2.9.44",
71
- "@umituz/react-native-firebase": "*",
70
+ "@umituz/react-native-firebase": "^1.13.87",
72
71
  "@umituz/react-native-localization": "*",
73
72
  "@umituz/react-native-subscription": "*",
74
73
  "eslint": "^9.0.0",
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Image to Video Wizard Config
3
+ * Config-driven wizard steps for image-to-video generation
4
+ */
5
+
6
+ import type { WizardFeatureConfig } from "../domain/entities/wizard-config.types";
7
+
8
+ export const IMAGE_TO_VIDEO_WIZARD_CONFIG: WizardFeatureConfig = {
9
+ id: "image-to-video",
10
+ name: "Image to Video",
11
+ steps: [
12
+ {
13
+ id: "photo_1",
14
+ type: "photo_upload",
15
+ label: "Your Photo",
16
+ showFaceDetection: false,
17
+ showPhotoTips: true,
18
+ required: true,
19
+ },
20
+ {
21
+ id: "motion_prompt",
22
+ type: "text_input",
23
+ required: false,
24
+ placeholderKey: "imageToVideo.motionPromptPlaceholder",
25
+ maxLength: 200,
26
+ },
27
+ {
28
+ id: "duration",
29
+ type: "selection",
30
+ selectionType: "duration",
31
+ options: [
32
+ { id: "5s", label: "5 seconds", value: 5 },
33
+ { id: "10s", label: "10 seconds", value: 10 },
34
+ ],
35
+ required: true,
36
+ defaultValue: "5s",
37
+ },
38
+ ],
39
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Wizard Feature Configs
3
+ * Pre-built configs for common generation features
4
+ */
5
+
6
+ export { TEXT_TO_IMAGE_WIZARD_CONFIG } from "./text-to-image.config";
7
+ export { TEXT_TO_VIDEO_WIZARD_CONFIG } from "./text-to-video.config";
8
+ export { IMAGE_TO_VIDEO_WIZARD_CONFIG } from "./image-to-video.config";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Text to Image Wizard Config
3
+ * Config-driven wizard steps for text-to-image generation
4
+ */
5
+
6
+ import type { WizardFeatureConfig } from "../domain/entities/wizard-config.types";
7
+
8
+ export const TEXT_TO_IMAGE_WIZARD_CONFIG: WizardFeatureConfig = {
9
+ id: "text-to-image",
10
+ name: "Text to Image",
11
+ steps: [
12
+ {
13
+ id: "prompt",
14
+ type: "text_input",
15
+ required: true,
16
+ placeholderKey: "textToImage.promptPlaceholder",
17
+ minLength: 3,
18
+ maxLength: 1000,
19
+ multiline: true,
20
+ },
21
+ {
22
+ id: "style",
23
+ type: "selection",
24
+ selectionType: "style",
25
+ options: [],
26
+ required: false,
27
+ },
28
+ ],
29
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Text to Video Wizard Config
3
+ * Config-driven wizard steps for text-to-video generation
4
+ */
5
+
6
+ import type { WizardFeatureConfig } from "../domain/entities/wizard-config.types";
7
+
8
+ export const TEXT_TO_VIDEO_WIZARD_CONFIG: WizardFeatureConfig = {
9
+ id: "text-to-video",
10
+ name: "Text to Video",
11
+ steps: [
12
+ {
13
+ id: "prompt",
14
+ type: "text_input",
15
+ required: true,
16
+ placeholderKey: "textToVideo.promptPlaceholder",
17
+ minLength: 3,
18
+ maxLength: 500,
19
+ multiline: true,
20
+ },
21
+ {
22
+ id: "duration",
23
+ type: "selection",
24
+ selectionType: "duration",
25
+ options: [
26
+ { id: "5s", label: "5 seconds", value: 5 },
27
+ { id: "10s", label: "10 seconds", value: 10 },
28
+ ],
29
+ required: true,
30
+ defaultValue: "5s",
31
+ },
32
+ ],
33
+ };
@@ -51,3 +51,6 @@ export type { GenericWizardFlowProps } from "./presentation/components";
51
51
 
52
52
  // Presentation - Screens
53
53
  export { GeneratingScreen } from "./presentation/screens";
54
+
55
+ // Feature Configs
56
+ export * from "./configs";
@@ -24,10 +24,13 @@ declare const __DEV__: boolean;
24
24
  // ============================================================================
25
25
 
26
26
  export interface ImageGenerationInput {
27
+ /** Photos are optional for text-to-image */
27
28
  readonly photos: readonly string[];
28
29
  readonly prompt: string;
29
30
  /** Optional interaction style for multi-person images */
30
31
  readonly interactionStyle?: InteractionStyle;
32
+ /** Optional style from wizard selection */
33
+ readonly style?: string;
31
34
  }
32
35
 
33
36
  export interface ImageGenerationResult {
@@ -40,29 +43,29 @@ export interface ImageGenerationResult {
40
43
 
41
44
  async function extractPhotosFromWizardData(
42
45
  wizardData: Record<string, unknown>,
43
- ): Promise<string[] | null> {
46
+ ): Promise<string[]> {
44
47
  const photoKeys = Object.keys(wizardData)
45
48
  .filter((k) => k.includes(PHOTO_KEY_PREFIX))
46
49
  .sort();
47
50
 
48
51
  if (photoKeys.length === 0) {
49
- if (typeof __DEV__ !== "undefined" && __DEV__) {
50
- console.error("[ImageStrategy] No photos found", { keys: Object.keys(wizardData) });
51
- }
52
- return null;
52
+ return [];
53
53
  }
54
54
 
55
55
  const photoUris: string[] = [];
56
56
  for (const key of photoKeys) {
57
57
  const photo = wizardData[key] as { uri?: string };
58
- if (!photo?.uri) return null;
59
- photoUris.push(photo.uri);
58
+ if (photo?.uri) {
59
+ photoUris.push(photo.uri);
60
+ }
60
61
  }
61
62
 
62
- const photosBase64 = await Promise.all(photoUris.map((uri) => readFileAsBase64(uri)));
63
- const validPhotos = photosBase64.filter(Boolean) as string[];
63
+ if (photoUris.length === 0) {
64
+ return [];
65
+ }
64
66
 
65
- return validPhotos.length > 0 ? validPhotos : null;
67
+ const photosBase64 = await Promise.all(photoUris.map((uri) => readFileAsBase64(uri)));
68
+ return photosBase64.filter(Boolean) as string[];
66
69
  }
67
70
 
68
71
  // ============================================================================
@@ -85,43 +88,53 @@ async function executeImageGeneration(
85
88
  const formatBase64 = (base64: string): string =>
86
89
  base64.startsWith("data:") ? base64 : `${BASE64_IMAGE_PREFIX}${base64}`;
87
90
 
88
- const imageUrls = input.photos.map(formatBase64);
89
- if (imageUrls.length === 0) {
90
- return { success: false, error: "At least one image required" };
91
- }
91
+ const hasPhotos = input.photos.length > 0;
92
+ const imageUrls = hasPhotos ? input.photos.map(formatBase64) : [];
92
93
 
93
- // Build face preservation prompt dynamically based on number of people
94
- const facePrompt = buildFacePreservationPrompt({
95
- scenarioPrompt: input.prompt,
96
- personCount: imageUrls.length,
97
- });
94
+ let finalPrompt = input.prompt;
98
95
 
99
- // Build interaction style prompt for multi-person images
100
- const interactionPrompt = buildInteractionStylePrompt({
101
- style: input.interactionStyle ?? "romantic",
102
- personCount: imageUrls.length,
103
- });
104
-
105
- // Combine prompts: face preservation + interaction style
106
- const enhancedPrompt = interactionPrompt
107
- ? `${facePrompt}\n\n${interactionPrompt}`
108
- : facePrompt;
96
+ if (hasPhotos) {
97
+ // Photo-based: Build face preservation prompt
98
+ const facePrompt = buildFacePreservationPrompt({
99
+ scenarioPrompt: input.prompt,
100
+ personCount: imageUrls.length,
101
+ });
109
102
 
110
- if (typeof __DEV__ !== "undefined" && __DEV__) {
111
- console.log("[ImageStrategy] Prompt built for", imageUrls.length, "person(s)", {
112
- interactionStyle: input.interactionStyle ?? "romantic",
103
+ const interactionPrompt = buildInteractionStylePrompt({
104
+ style: input.interactionStyle ?? "romantic",
105
+ personCount: imageUrls.length,
113
106
  });
107
+
108
+ finalPrompt = interactionPrompt
109
+ ? `${facePrompt}\n\n${interactionPrompt}`
110
+ : facePrompt;
111
+
112
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
113
+ console.log("[ImageStrategy] Photo-based generation for", imageUrls.length, "person(s)");
114
+ }
115
+ } else {
116
+ // Text-to-image: Use prompt with optional style
117
+ if (input.style && input.style !== DEFAULT_STYLE_VALUE) {
118
+ finalPrompt = `${input.prompt}. Style: ${input.style}`;
119
+ }
120
+
121
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
122
+ console.log("[ImageStrategy] Text-to-image generation");
123
+ }
114
124
  }
115
125
 
116
- const modelInput = {
117
- image_urls: imageUrls,
118
- prompt: enhancedPrompt,
126
+ const modelInput: Record<string, unknown> = {
127
+ prompt: finalPrompt,
119
128
  aspect_ratio: MODEL_INPUT_DEFAULTS.aspectRatio,
120
129
  output_format: MODEL_INPUT_DEFAULTS.outputFormat,
121
130
  num_images: MODEL_INPUT_DEFAULTS.numImages,
122
131
  enable_safety_checker: MODEL_INPUT_DEFAULTS.enableSafetyChecker,
123
132
  };
124
133
 
134
+ if (hasPhotos) {
135
+ modelInput.image_urls = imageUrls;
136
+ }
137
+
125
138
  let lastStatus = "";
126
139
  const result = await provider.subscribe(model, modelInput, {
127
140
  timeoutMs: GENERATION_TIMEOUT_MS,
@@ -151,39 +164,47 @@ export async function buildImageInput(
151
164
  scenario: WizardScenarioData,
152
165
  ): Promise<ImageGenerationInput | null> {
153
166
  const photos = await extractPhotosFromWizardData(wizardData);
154
- if (!photos) return null;
155
167
 
156
- if (!scenario.aiPrompt?.trim()) {
157
- throw new Error(`Scenario "${scenario.id}" must have aiPrompt field`);
168
+ // Get prompt from wizardData (text_input step) OR scenario.aiPrompt
169
+ const userPrompt = wizardData.prompt as string | undefined;
170
+ const prompt = userPrompt?.trim() || scenario.aiPrompt?.trim();
171
+
172
+ if (!prompt) {
173
+ throw new Error("Prompt is required for image generation");
158
174
  }
159
175
 
160
- let prompt = scenario.aiPrompt;
176
+ // For photo-based generation, apply style enhancements
177
+ let finalPrompt = prompt;
178
+ if (photos.length > 0) {
179
+ const styleEnhancements: string[] = [];
161
180
 
162
- const styleEnhancements: string[] = [];
181
+ const romanticMoods = wizardData.selection_romantic_mood as string[] | undefined;
182
+ if (romanticMoods?.length) {
183
+ styleEnhancements.push(`Mood: ${romanticMoods.join(", ")}`);
184
+ }
163
185
 
164
- const romanticMoods = wizardData.selection_romantic_mood as string[] | undefined;
165
- if (romanticMoods?.length) {
166
- styleEnhancements.push(`Mood: ${romanticMoods.join(", ")}`);
167
- }
186
+ const artStyle = wizardData.selection_art_style as string | undefined;
187
+ if (artStyle && artStyle !== DEFAULT_STYLE_VALUE) {
188
+ styleEnhancements.push(`Art style: ${artStyle}`);
189
+ }
168
190
 
169
- const artStyle = wizardData.selection_art_style as string | undefined;
170
- if (artStyle && artStyle !== DEFAULT_STYLE_VALUE) {
171
- styleEnhancements.push(`Art style: ${artStyle}`);
172
- }
191
+ const artist = wizardData.selection_artist_style as string | undefined;
192
+ if (artist && artist !== DEFAULT_STYLE_VALUE) {
193
+ styleEnhancements.push(`Artist style: ${artist}`);
194
+ }
173
195
 
174
- const artist = wizardData.selection_artist_style as string | undefined;
175
- if (artist && artist !== DEFAULT_STYLE_VALUE) {
176
- styleEnhancements.push(`Artist style: ${artist}`);
196
+ if (styleEnhancements.length > 0) {
197
+ finalPrompt = `${prompt}. ${styleEnhancements.join(", ")}`;
198
+ }
177
199
  }
178
200
 
179
- if (styleEnhancements.length > 0) {
180
- prompt = `${prompt}. ${styleEnhancements.join(", ")}`;
181
- }
201
+ // Get style from wizard selection (for text-to-image)
202
+ const style = wizardData.style as string | undefined;
182
203
 
183
204
  // Get interaction style from scenario (default: romantic for couple apps)
184
205
  const interactionStyle = (scenario.interactionStyle as InteractionStyle) ?? "romantic";
185
206
 
186
- return { photos, prompt, interactionStyle };
207
+ return { photos, prompt: finalPrompt, style, interactionStyle };
187
208
  }
188
209
 
189
210
  // ============================================================================
@@ -11,16 +11,18 @@ import type { WizardScenarioData } from "../../presentation/hooks/useWizardGener
11
11
  import type { WizardStrategy } from "./wizard-strategy.types";
12
12
  import { PHOTO_KEY_PREFIX, VIDEO_FEATURE_PATTERNS } from "./wizard-strategy.constants";
13
13
 
14
- declare const __DEV__: boolean;
15
-
16
14
  // ============================================================================
17
15
  // Types
18
16
  // ============================================================================
19
17
 
20
18
  export interface VideoGenerationInput {
21
- readonly sourceImageBase64: string;
22
- readonly targetImageBase64: string;
19
+ /** Source image (optional for text-to-video) */
20
+ readonly sourceImageBase64?: string;
21
+ /** Target image (optional, uses source if not provided) */
22
+ readonly targetImageBase64?: string;
23
23
  readonly prompt: string;
24
+ /** Video duration in seconds */
25
+ readonly duration?: number;
24
26
  }
25
27
 
26
28
  export interface VideoGenerationResult {
@@ -33,29 +35,29 @@ export interface VideoGenerationResult {
33
35
 
34
36
  async function extractPhotosFromWizardData(
35
37
  wizardData: Record<string, unknown>,
36
- ): Promise<string[] | null> {
38
+ ): Promise<string[]> {
37
39
  const photoKeys = Object.keys(wizardData)
38
40
  .filter((k) => k.includes(PHOTO_KEY_PREFIX))
39
41
  .sort();
40
42
 
41
43
  if (photoKeys.length === 0) {
42
- if (typeof __DEV__ !== "undefined" && __DEV__) {
43
- console.error("[VideoStrategy] No photos found", { keys: Object.keys(wizardData) });
44
- }
45
- return null;
44
+ return [];
46
45
  }
47
46
 
48
47
  const photoUris: string[] = [];
49
48
  for (const key of photoKeys) {
50
49
  const photo = wizardData[key] as { uri?: string };
51
- if (!photo?.uri) return null;
52
- photoUris.push(photo.uri);
50
+ if (photo?.uri) {
51
+ photoUris.push(photo.uri);
52
+ }
53
53
  }
54
54
 
55
- const photosBase64 = await Promise.all(photoUris.map((uri) => readFileAsBase64(uri)));
56
- const validPhotos = photosBase64.filter(Boolean) as string[];
55
+ if (photoUris.length === 0) {
56
+ return [];
57
+ }
57
58
 
58
- return validPhotos.length > 0 ? validPhotos : null;
59
+ const photosBase64 = await Promise.all(photoUris.map((uri) => readFileAsBase64(uri)));
60
+ return photosBase64.filter(Boolean) as string[];
59
61
  }
60
62
 
61
63
  // ============================================================================
@@ -83,16 +85,24 @@ export async function buildVideoInput(
83
85
  scenario: WizardScenarioData,
84
86
  ): Promise<VideoGenerationInput | null> {
85
87
  const photos = await extractPhotosFromWizardData(wizardData);
86
- if (!photos || photos.length < 1) return null;
87
88
 
88
- if (!scenario.aiPrompt?.trim()) {
89
- throw new Error(`Scenario "${scenario.id}" must have aiPrompt field`);
89
+ // Get prompt from wizardData or scenario
90
+ const userPrompt = wizardData.prompt as string | undefined;
91
+ const motionPrompt = wizardData.motion_prompt as string | undefined;
92
+ const prompt = userPrompt?.trim() || motionPrompt?.trim() || scenario.aiPrompt?.trim();
93
+
94
+ if (!prompt) {
95
+ throw new Error("Prompt is required for video generation");
90
96
  }
91
97
 
98
+ // Get duration from wizardData
99
+ const duration = wizardData.duration as number | undefined;
100
+
92
101
  return {
93
102
  sourceImageBase64: photos[0],
94
103
  targetImageBase64: photos[1] || photos[0],
95
- prompt: scenario.aiPrompt,
104
+ prompt,
105
+ duration,
96
106
  };
97
107
  }
98
108
 
@@ -120,8 +130,8 @@ export function createVideoStrategy(options: CreateVideoStrategyOptions): Wizard
120
130
  const result = await executeVideoFeature(
121
131
  videoFeatureType,
122
132
  {
123
- sourceImageBase64: videoInput.sourceImageBase64,
124
- targetImageBase64: videoInput.targetImageBase64,
133
+ sourceImageBase64: videoInput.sourceImageBase64 || "",
134
+ targetImageBase64: videoInput.targetImageBase64 || videoInput.sourceImageBase64 || "",
125
135
  prompt: videoInput.prompt,
126
136
  },
127
137
  { onProgress },
@@ -29,4 +29,6 @@ export const MODEL_INPUT_DEFAULTS = {
29
29
  export const VIDEO_FEATURE_PATTERNS: Record<string, VideoFeatureType> = {
30
30
  kiss: "ai-kiss",
31
31
  hug: "ai-hug",
32
+ "text-to-video": "text-to-video",
33
+ "image-to-video": "image-to-video",
32
34
  };