@umituz/react-native-ai-generation-content 1.89.33 → 1.89.35
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.89.
|
|
3
|
+
"version": "1.89.35",
|
|
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",
|
package/src/domains/generation/wizard/infrastructure/strategies/image-generation.strategy.ts
CHANGED
|
@@ -7,9 +7,10 @@ import type { WizardScenarioData } from "../../presentation/hooks/useWizardGener
|
|
|
7
7
|
import type { WizardStrategy } from "./wizard-strategy.types";
|
|
8
8
|
import { DEFAULT_STYLE_VALUE, IMAGE_PROCESSING_PROMPTS } from "./wizard-strategy.constants";
|
|
9
9
|
import { extractPrompt, extractSelection } from "../utils";
|
|
10
|
-
import { extractPhotosAsBase64 } from "./shared/photo-extraction.utils";
|
|
10
|
+
import { extractPhotosAsBase64, extractPhotoUris } from "./shared/photo-extraction.utils";
|
|
11
11
|
import { executeImageGeneration } from "./image-generation.executor";
|
|
12
12
|
import type { WizardImageInput, CreateImageStrategyOptions } from "./image-generation.types";
|
|
13
|
+
import { enhancePromptWithAnalysis } from "../../../infrastructure/appearance-analysis";
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
// ============================================================================
|
|
@@ -20,6 +21,8 @@ export async function buildImageInput(
|
|
|
20
21
|
wizardData: Record<string, unknown>,
|
|
21
22
|
scenario: WizardScenarioData,
|
|
22
23
|
): Promise<WizardImageInput | null> {
|
|
24
|
+
// Extract photo URIs first (for couple refinement)
|
|
25
|
+
const photoUris = extractPhotoUris(wizardData);
|
|
23
26
|
const photos = await extractPhotosAsBase64(wizardData);
|
|
24
27
|
|
|
25
28
|
// Extract prompt with fallback to default
|
|
@@ -38,6 +41,20 @@ export async function buildImageInput(
|
|
|
38
41
|
let finalPrompt = prompt;
|
|
39
42
|
if (photos.length > 0) {
|
|
40
43
|
finalPrompt = applyStyleEnhancements(prompt, wizardData);
|
|
44
|
+
|
|
45
|
+
// ✅ Apply couple refinement (same logic as Wardrobe)
|
|
46
|
+
// This ensures consistency across all couple generation scenarios
|
|
47
|
+
const isCoupleMode = photos.length >= 2;
|
|
48
|
+
finalPrompt = await enhancePromptWithAnalysis(finalPrompt, photoUris, isCoupleMode);
|
|
49
|
+
|
|
50
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
51
|
+
console.log("[ImageStrategy] Prompt enhanced with couple refinement", {
|
|
52
|
+
photoCount: photos.length,
|
|
53
|
+
isCoupleMode,
|
|
54
|
+
originalPromptLength: prompt.length,
|
|
55
|
+
finalPromptLength: finalPrompt.length,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
41
58
|
}
|
|
42
59
|
|
|
43
60
|
// Extract style for text-to-image
|
package/src/domains/generation/wizard/infrastructure/strategies/shared/photo-extraction.utils.ts
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* Shared photo extraction logic for wizard strategies
|
|
4
4
|
*
|
|
5
5
|
* Resize strategy:
|
|
6
|
-
* - Small images (<
|
|
6
|
+
* - Small images (<768px): scale UP to 768px minimum (good face preservation)
|
|
7
7
|
* - Large images (>1536px): scale DOWN to 1536px maximum (reduces upload size ~10x)
|
|
8
8
|
* - Normal images: pass through unchanged
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: 768px minimum ensures AI models have enough detail for face preservation
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { readFileAsBase64 } from "@umituz/react-native-design-system/filesystem";
|
|
@@ -14,7 +16,7 @@ import { Image } from "react-native";
|
|
|
14
16
|
import { PHOTO_KEY_PREFIX } from "../wizard-strategy.constants";
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
const MIN_IMAGE_DIMENSION =
|
|
19
|
+
const MIN_IMAGE_DIMENSION = 768; // Minimum for good face preservation
|
|
18
20
|
const MAX_IMAGE_DIMENSION = 1536;
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -28,7 +30,7 @@ function getImageSize(uri: string): Promise<{ width: number; height: number }> {
|
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Ensure image is within optimal dimensions for AI generation.
|
|
31
|
-
* - Too small (<
|
|
33
|
+
* - Too small (<768px): scale up (good face preservation)
|
|
32
34
|
* - Too large (>1536px): scale down (reduces upload size, prevents timeouts)
|
|
33
35
|
* - Within range: return as-is
|
|
34
36
|
*/
|
|
@@ -37,49 +39,85 @@ async function ensureOptimalSize(uri: string): Promise<string> {
|
|
|
37
39
|
const { width, height } = await getImageSize(uri);
|
|
38
40
|
const maxDim = Math.max(width, height);
|
|
39
41
|
|
|
42
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
43
|
+
console.log("[PhotoExtraction] Analyzing image", {
|
|
44
|
+
originalDimensions: `${width}x${height}`,
|
|
45
|
+
maxDim,
|
|
46
|
+
minDim: Math.min(width, height),
|
|
47
|
+
isTooSmall: width < MIN_IMAGE_DIMENSION || height < MIN_IMAGE_DIMENSION,
|
|
48
|
+
isTooLarge: maxDim > MAX_IMAGE_DIMENSION,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
// Already within optimal range
|
|
41
53
|
if (width >= MIN_IMAGE_DIMENSION && height >= MIN_IMAGE_DIMENSION && maxDim <= MAX_IMAGE_DIMENSION) {
|
|
54
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
55
|
+
console.log("[PhotoExtraction] Image already optimal, skipping resize", {
|
|
56
|
+
dimensions: `${width}x${height}`,
|
|
57
|
+
minRequired: MIN_IMAGE_DIMENSION,
|
|
58
|
+
maxAllowed: MAX_IMAGE_DIMENSION,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
42
61
|
return uri;
|
|
43
62
|
}
|
|
44
63
|
|
|
45
64
|
let newWidth: number;
|
|
46
65
|
let newHeight: number;
|
|
66
|
+
let direction: string;
|
|
47
67
|
|
|
48
68
|
if (maxDim > MAX_IMAGE_DIMENSION) {
|
|
49
69
|
// Scale DOWN — largest dimension becomes MAX_IMAGE_DIMENSION
|
|
50
70
|
const scale = MAX_IMAGE_DIMENSION / maxDim;
|
|
51
71
|
newWidth = Math.round(width * scale);
|
|
52
72
|
newHeight = Math.round(height * scale);
|
|
73
|
+
direction = "down";
|
|
53
74
|
} else {
|
|
54
75
|
// Scale UP — smallest dimension becomes MIN_IMAGE_DIMENSION
|
|
55
76
|
const scale = Math.max(MIN_IMAGE_DIMENSION / width, MIN_IMAGE_DIMENSION / height);
|
|
56
77
|
newWidth = Math.ceil(width * scale);
|
|
57
78
|
newHeight = Math.ceil(height * scale);
|
|
79
|
+
direction = "up";
|
|
58
80
|
}
|
|
59
81
|
|
|
82
|
+
const compressQuality = maxDim > MAX_IMAGE_DIMENSION ? 0.8 : 1.0; // Lossless for scale-up
|
|
83
|
+
|
|
60
84
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
61
|
-
const direction = maxDim > MAX_IMAGE_DIMENSION ? "down" : "up";
|
|
62
85
|
console.log(`[PhotoExtraction] Resizing ${direction}`, {
|
|
63
86
|
from: `${width}x${height}`,
|
|
64
87
|
to: `${newWidth}x${newHeight}`,
|
|
88
|
+
scaleChange: `${((Math.max(newWidth, newHeight) / maxDim - 1) * 100).toFixed(0)}%`,
|
|
89
|
+
compressQuality,
|
|
65
90
|
});
|
|
66
91
|
}
|
|
67
92
|
|
|
68
93
|
const result = await manipulateAsync(uri, [{ resize: { width: newWidth, height: newHeight } }], {
|
|
69
94
|
format: SaveFormat.JPEG,
|
|
70
|
-
compress:
|
|
95
|
+
compress: compressQuality,
|
|
71
96
|
});
|
|
72
97
|
|
|
98
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
99
|
+
console.log("[PhotoExtraction] Resize complete", {
|
|
100
|
+
original: `${width}x${height}`,
|
|
101
|
+
resized: `${newWidth}x${newHeight}`,
|
|
102
|
+
action: direction,
|
|
103
|
+
quality: compressQuality === 1.0 ? "lossless" : `${compressQuality * 100}%`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
73
107
|
return result.uri;
|
|
74
|
-
} catch {
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
110
|
+
console.error("[PhotoExtraction] Resize failed, using original", error);
|
|
111
|
+
}
|
|
75
112
|
return uri;
|
|
76
113
|
}
|
|
77
114
|
}
|
|
78
115
|
|
|
79
116
|
/**
|
|
80
117
|
* Extracts photo URIs from wizard data
|
|
118
|
+
* Exported for use in other strategies (e.g., couple refinement)
|
|
81
119
|
*/
|
|
82
|
-
function extractPhotoUris(wizardData: Record<string, unknown>): string[] {
|
|
120
|
+
export function extractPhotoUris(wizardData: Record<string, unknown>): string[] {
|
|
83
121
|
const photoKeys = Object.keys(wizardData)
|
|
84
122
|
.filter((k) => k.includes(PHOTO_KEY_PREFIX))
|
|
85
123
|
.sort();
|
|
@@ -108,18 +146,27 @@ export async function extractPhotosAsBase64(
|
|
|
108
146
|
enableDebugLogs = false,
|
|
109
147
|
): Promise<string[]> {
|
|
110
148
|
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
111
|
-
console.log("[PhotoExtraction]
|
|
149
|
+
console.log("[PhotoExtraction] >>> extractPhotosAsBase64 START", {
|
|
112
150
|
wizardDataKeys: Object.keys(wizardData),
|
|
151
|
+
photoKeyPrefix: PHOTO_KEY_PREFIX,
|
|
152
|
+
minDimension: MIN_IMAGE_DIMENSION,
|
|
153
|
+
maxDimension: MAX_IMAGE_DIMENSION,
|
|
113
154
|
});
|
|
114
155
|
}
|
|
115
156
|
|
|
116
157
|
const photoUris = extractPhotoUris(wizardData);
|
|
117
158
|
|
|
118
159
|
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
119
|
-
console.log("[PhotoExtraction]
|
|
160
|
+
console.log("[PhotoExtraction] Photo URIs extracted", {
|
|
161
|
+
count: photoUris.length,
|
|
162
|
+
keys: Object.keys(wizardData).filter(k => k.includes(PHOTO_KEY_PREFIX)),
|
|
163
|
+
});
|
|
120
164
|
}
|
|
121
165
|
|
|
122
166
|
if (photoUris.length === 0) {
|
|
167
|
+
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
168
|
+
console.log("[PhotoExtraction] No photos found, returning empty array");
|
|
169
|
+
}
|
|
123
170
|
return [];
|
|
124
171
|
}
|
|
125
172
|
|
|
@@ -127,11 +174,29 @@ export async function extractPhotosAsBase64(
|
|
|
127
174
|
const results = await Promise.allSettled(
|
|
128
175
|
photoUris.map(async (uri, index) => {
|
|
129
176
|
try {
|
|
177
|
+
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
178
|
+
console.log(`[PhotoExtraction] Processing photo ${index + 1}/${photoUris.length}`, {
|
|
179
|
+
uri: uri.substring(0, 50) + "...",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
130
183
|
const optimizedUri = await ensureOptimalSize(uri);
|
|
131
|
-
|
|
184
|
+
const base64 = await readFileAsBase64(optimizedUri);
|
|
185
|
+
|
|
186
|
+
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
187
|
+
console.log(`[PhotoExtraction] Photo ${index + 1} processed`, {
|
|
188
|
+
sizeKB: (base64.length / 1024).toFixed(1),
|
|
189
|
+
originalUri: uri.substring(0, 30) + "...",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return base64;
|
|
132
194
|
} catch (error) {
|
|
133
195
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
134
|
-
console.error(`[PhotoExtraction] Failed to
|
|
196
|
+
console.error(`[PhotoExtraction] ❌ Failed to process photo ${index + 1}:`, {
|
|
197
|
+
uri: uri.substring(0, 50) + "...",
|
|
198
|
+
error: error instanceof Error ? error.message : String(error),
|
|
199
|
+
});
|
|
135
200
|
}
|
|
136
201
|
return null;
|
|
137
202
|
}
|
|
@@ -143,15 +208,21 @@ export async function extractPhotosAsBase64(
|
|
|
143
208
|
.map((result) => (result.status === "fulfilled" ? result.value : null))
|
|
144
209
|
.filter((photo): photo is string => typeof photo === "string" && photo.length > 0);
|
|
145
210
|
|
|
211
|
+
const failedCount = results.filter((r) => r.status === "rejected").length;
|
|
212
|
+
|
|
146
213
|
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
147
|
-
|
|
148
|
-
console.log("[PhotoExtraction] Converted photos", {
|
|
214
|
+
console.log("[PhotoExtraction] <<< extractPhotosAsBase64 COMPLETE", {
|
|
149
215
|
total: photoUris.length,
|
|
150
|
-
|
|
216
|
+
successful: validPhotos.length,
|
|
151
217
|
failed: failedCount,
|
|
152
|
-
sizes: validPhotos.map((p) =>
|
|
218
|
+
sizes: validPhotos.map((p, i) => `Photo ${i + 1}: ${(p.length / 1024).toFixed(1)}KB`),
|
|
219
|
+
totalSizeMB: ((validPhotos.reduce((sum, p) => sum + p.length, 0) / 1024 / 1024).toFixed(2)),
|
|
153
220
|
});
|
|
154
221
|
}
|
|
155
222
|
|
|
223
|
+
if (failedCount > 0 && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
224
|
+
console.warn(`[PhotoExtraction] ⚠️ ${failedCount} photo(s) failed to process`);
|
|
225
|
+
}
|
|
226
|
+
|
|
156
227
|
return validPhotos;
|
|
157
228
|
}
|