@umituz/react-native-ai-generation-content 1.17.144 → 1.17.145
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 +1 -1
- package/src/features/replace-background/index.ts +5 -50
- package/src/features/replace-background/presentation/components/index.ts +0 -12
- package/src/features/replace-background/presentation/hooks/index.ts +0 -3
- package/src/infrastructure/utils/index.ts +1 -0
- package/src/infrastructure/utils/result-validator.util.ts +0 -214
- package/src/infrastructure/utils/url-extractor.util.ts +209 -0
- package/src/presentation/hooks/flow-state.utils.ts +101 -0
- package/src/presentation/hooks/useGenerationFlow.ts +47 -178
- package/src/presentation/types/flow-config.types.ts +5 -99
- package/src/presentation/types/flow-default-configs.ts +106 -0
- package/src/features/replace-background/domain/entities/background.types.ts +0 -77
- package/src/features/replace-background/domain/entities/component.types.ts +0 -87
- package/src/features/replace-background/domain/entities/config.types.ts +0 -41
- package/src/features/replace-background/domain/entities/index.ts +0 -30
- package/src/features/replace-background/infrastructure/constants/index.ts +0 -5
- package/src/features/replace-background/infrastructure/constants/prompts.constants.ts +0 -15
- package/src/features/replace-background/infrastructure/index.ts +0 -5
- package/src/features/replace-background/presentation/components/BackgroundFeature.tsx +0 -143
- package/src/features/replace-background/presentation/components/ComparisonSlider.tsx +0 -187
- package/src/features/replace-background/presentation/components/ErrorDisplay.tsx +0 -60
- package/src/features/replace-background/presentation/components/FeatureHeader.tsx +0 -80
- package/src/features/replace-background/presentation/components/GenerateButton.tsx +0 -85
- package/src/features/replace-background/presentation/components/ImagePicker.tsx +0 -136
- package/src/features/replace-background/presentation/components/ModeSelector.tsx +0 -78
- package/src/features/replace-background/presentation/components/PromptInput.tsx +0 -142
- package/src/features/replace-background/presentation/components/ResultDisplay.tsx +0 -122
- package/src/features/replace-background/presentation/hooks/useBackgroundFeature.ts +0 -119
package/package.json
CHANGED
|
@@ -1,31 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Replace Background Feature
|
|
3
|
-
* AI-powered background replacement
|
|
3
|
+
* AI-powered background replacement feature for React Native
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
// Domain Types
|
|
7
|
-
export type {
|
|
8
|
-
BackgroundProcessRequest,
|
|
9
|
-
BackgroundProcessResult,
|
|
10
|
-
BackgroundFeatureState,
|
|
11
|
-
SamplePrompt,
|
|
12
|
-
StudioMode,
|
|
13
|
-
StudioModeConfig,
|
|
14
|
-
ComparisonState,
|
|
15
|
-
ImagePickerProps,
|
|
16
|
-
PromptInputProps,
|
|
17
|
-
GenerateButtonProps,
|
|
18
|
-
ResultDisplayProps,
|
|
19
|
-
ErrorDisplayProps,
|
|
20
|
-
FeatureHeaderProps,
|
|
21
|
-
ModeSelectorProps,
|
|
22
|
-
ComparisonSliderProps,
|
|
23
|
-
ProcessRequestParams,
|
|
24
|
-
BackgroundFeatureConfig,
|
|
25
|
-
UseBackgroundFeatureConfig,
|
|
26
|
-
} from "./domain/entities";
|
|
27
|
-
|
|
28
|
-
// Domain Types - Provider-Agnostic
|
|
6
|
+
// Domain Types
|
|
29
7
|
export type {
|
|
30
8
|
ReplaceBackgroundMode,
|
|
31
9
|
ReplaceBackgroundOptions,
|
|
@@ -37,36 +15,13 @@ export type {
|
|
|
37
15
|
ReplaceBackgroundResultExtractor,
|
|
38
16
|
} from "./domain/types";
|
|
39
17
|
|
|
40
|
-
// Constants
|
|
41
|
-
export { DEFAULT_SAMPLE_PROMPTS } from "./infrastructure/constants";
|
|
42
|
-
|
|
43
18
|
// Presentation Components
|
|
44
|
-
export {
|
|
45
|
-
|
|
46
|
-
ReplaceBackgroundFeature,
|
|
47
|
-
ImagePicker,
|
|
48
|
-
PromptInput,
|
|
49
|
-
GenerateButton,
|
|
50
|
-
ResultDisplay,
|
|
51
|
-
ErrorDisplay,
|
|
52
|
-
FeatureHeader,
|
|
53
|
-
ComparisonSlider,
|
|
54
|
-
ModeSelector,
|
|
55
|
-
} from "./presentation/components";
|
|
56
|
-
|
|
57
|
-
export type {
|
|
58
|
-
BackgroundFeatureProps,
|
|
59
|
-
ReplaceBackgroundFeatureProps,
|
|
60
|
-
} from "./presentation/components";
|
|
19
|
+
export { ReplaceBackgroundFeature } from "./presentation/components";
|
|
20
|
+
export type { ReplaceBackgroundFeatureProps } from "./presentation/components";
|
|
61
21
|
|
|
62
22
|
// Presentation Hooks
|
|
63
|
-
export {
|
|
64
|
-
useBackgroundFeature,
|
|
65
|
-
useReplaceBackgroundFeature,
|
|
66
|
-
} from "./presentation/hooks";
|
|
67
|
-
|
|
23
|
+
export { useReplaceBackgroundFeature } from "./presentation/hooks";
|
|
68
24
|
export type {
|
|
69
|
-
UseBackgroundFeatureReturn,
|
|
70
25
|
UseReplaceBackgroundFeatureProps,
|
|
71
26
|
UseReplaceBackgroundFeatureReturn,
|
|
72
27
|
} from "./presentation/hooks";
|
|
@@ -2,17 +2,5 @@
|
|
|
2
2
|
* Presentation Components Export
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { BackgroundFeature } from "./BackgroundFeature";
|
|
6
|
-
export type { BackgroundFeatureProps } from "./BackgroundFeature";
|
|
7
|
-
|
|
8
5
|
export { ReplaceBackgroundFeature } from "./ReplaceBackgroundFeature";
|
|
9
6
|
export type { ReplaceBackgroundFeatureProps } from "./ReplaceBackgroundFeature";
|
|
10
|
-
|
|
11
|
-
export { ImagePicker } from "./ImagePicker";
|
|
12
|
-
export { PromptInput } from "./PromptInput";
|
|
13
|
-
export { GenerateButton } from "./GenerateButton";
|
|
14
|
-
export { ResultDisplay } from "./ResultDisplay";
|
|
15
|
-
export { ErrorDisplay } from "./ErrorDisplay";
|
|
16
|
-
export { FeatureHeader } from "./FeatureHeader";
|
|
17
|
-
export { ComparisonSlider } from "./ComparisonSlider";
|
|
18
|
-
export { ModeSelector } from "./ModeSelector";
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
* Presentation Hooks Export
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { useBackgroundFeature } from "./useBackgroundFeature";
|
|
6
|
-
export type { UseBackgroundFeatureReturn } from "./useBackgroundFeature";
|
|
7
|
-
|
|
8
5
|
export { useReplaceBackgroundFeature } from "./useReplaceBackgroundFeature";
|
|
9
6
|
export type {
|
|
10
7
|
UseReplaceBackgroundFeatureProps,
|
|
@@ -7,6 +7,7 @@ export * from "./polling-interval.util";
|
|
|
7
7
|
export * from "./progress-calculator.util";
|
|
8
8
|
export * from "./status-checker.util";
|
|
9
9
|
export * from "./result-validator.util";
|
|
10
|
+
export * from "./url-extractor.util";
|
|
10
11
|
export * from "./photo-generation";
|
|
11
12
|
export * from "./feature-utils";
|
|
12
13
|
export * from "./video-helpers";
|
|
@@ -13,9 +13,7 @@ export interface ResultValidation {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface ValidateResultOptions {
|
|
16
|
-
/** Custom output field names to check */
|
|
17
16
|
outputFields?: string[];
|
|
18
|
-
/** Whether empty results are allowed */
|
|
19
17
|
allowEmpty?: boolean;
|
|
20
18
|
}
|
|
21
19
|
|
|
@@ -44,7 +42,6 @@ export function validateResult(
|
|
|
44
42
|
const { outputFields = DEFAULT_OUTPUT_FIELDS, allowEmpty = false } =
|
|
45
43
|
options ?? {};
|
|
46
44
|
|
|
47
|
-
// Handle null/undefined
|
|
48
45
|
if (result === null || result === undefined) {
|
|
49
46
|
return {
|
|
50
47
|
isValid: allowEmpty,
|
|
@@ -54,7 +51,6 @@ export function validateResult(
|
|
|
54
51
|
};
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
// Handle non-object results
|
|
58
54
|
if (typeof result !== "object") {
|
|
59
55
|
return {
|
|
60
56
|
isValid: true,
|
|
@@ -65,7 +61,6 @@ export function validateResult(
|
|
|
65
61
|
|
|
66
62
|
const resultObj = result as Record<string, unknown>;
|
|
67
63
|
|
|
68
|
-
// Check for error fields
|
|
69
64
|
const errorValue = resultObj.error || resultObj.detail;
|
|
70
65
|
const errorString = errorValue ? String(errorValue).toLowerCase() : "";
|
|
71
66
|
|
|
@@ -74,15 +69,12 @@ export function validateResult(
|
|
|
74
69
|
errorString.includes("500") ||
|
|
75
70
|
errorString === "internal server error";
|
|
76
71
|
|
|
77
|
-
// Check for empty object
|
|
78
72
|
const isEmpty = Object.keys(resultObj).length === 0;
|
|
79
73
|
|
|
80
|
-
// Check for output in expected fields
|
|
81
74
|
const hasOutput = outputFields.some((field) => {
|
|
82
75
|
const value = resultObj[field];
|
|
83
76
|
if (!value) return false;
|
|
84
77
|
|
|
85
|
-
// Handle nested output structures
|
|
86
78
|
if (typeof value === "object" && value !== null) {
|
|
87
79
|
const nested = value as Record<string, unknown>;
|
|
88
80
|
return !!(
|
|
@@ -96,7 +88,6 @@ export function validateResult(
|
|
|
96
88
|
return true;
|
|
97
89
|
});
|
|
98
90
|
|
|
99
|
-
// Determine if result has error
|
|
100
91
|
const hasError =
|
|
101
92
|
hasInternalServerError || (isEmpty && !hasOutput && !allowEmpty);
|
|
102
93
|
|
|
@@ -113,213 +104,8 @@ export function validateResult(
|
|
|
113
104
|
isValid: validation.isValid,
|
|
114
105
|
hasOutput: validation.hasOutput,
|
|
115
106
|
hasError: validation.hasError,
|
|
116
|
-
checkedFields: outputFields.join(", "),
|
|
117
107
|
});
|
|
118
108
|
}
|
|
119
109
|
|
|
120
110
|
return validation;
|
|
121
111
|
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Extract output URL from result
|
|
125
|
-
* Supports various AI provider response formats
|
|
126
|
-
*/
|
|
127
|
-
export function extractOutputUrl(
|
|
128
|
-
result: unknown,
|
|
129
|
-
urlFields?: string[],
|
|
130
|
-
): string | undefined {
|
|
131
|
-
if (!result || typeof result !== "object") {
|
|
132
|
-
return undefined;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const fields = urlFields ?? [
|
|
136
|
-
"url",
|
|
137
|
-
"image_url",
|
|
138
|
-
"video_url",
|
|
139
|
-
"output_url",
|
|
140
|
-
"result_url",
|
|
141
|
-
];
|
|
142
|
-
|
|
143
|
-
const resultObj = result as Record<string, unknown>;
|
|
144
|
-
|
|
145
|
-
// Check top-level fields
|
|
146
|
-
for (const field of fields) {
|
|
147
|
-
const value = resultObj[field];
|
|
148
|
-
if (typeof value === "string" && value.length > 0) {
|
|
149
|
-
return value;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Check nested data/output objects
|
|
154
|
-
const nested =
|
|
155
|
-
(resultObj.data as Record<string, unknown>) ||
|
|
156
|
-
(resultObj.output as Record<string, unknown>) ||
|
|
157
|
-
(resultObj.result as Record<string, unknown>);
|
|
158
|
-
|
|
159
|
-
if (nested && typeof nested === "object") {
|
|
160
|
-
for (const field of fields) {
|
|
161
|
-
const value = nested[field];
|
|
162
|
-
if (typeof value === "string" && value.length > 0) {
|
|
163
|
-
return value;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Check for nested image/video objects
|
|
168
|
-
const media =
|
|
169
|
-
(nested.image as Record<string, unknown>) ||
|
|
170
|
-
(nested.video as Record<string, unknown>);
|
|
171
|
-
if (media && typeof media === "object" && typeof media.url === "string") {
|
|
172
|
-
return media.url;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Extract multiple output URLs from result
|
|
181
|
-
*/
|
|
182
|
-
export function extractOutputUrls(
|
|
183
|
-
result: unknown,
|
|
184
|
-
urlFields?: string[],
|
|
185
|
-
): string[] {
|
|
186
|
-
if (!result || typeof result !== "object") {
|
|
187
|
-
return [];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const urls: string[] = [];
|
|
191
|
-
const resultObj = result as Record<string, unknown>;
|
|
192
|
-
|
|
193
|
-
// Check for arrays
|
|
194
|
-
const arrayFields = ["images", "videos", "outputs", "results", "urls"];
|
|
195
|
-
for (const field of arrayFields) {
|
|
196
|
-
const arr = resultObj[field];
|
|
197
|
-
if (Array.isArray(arr)) {
|
|
198
|
-
for (const item of arr) {
|
|
199
|
-
const url = extractOutputUrl(item, urlFields);
|
|
200
|
-
if (url) {
|
|
201
|
-
urls.push(url);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Check nested data/output
|
|
208
|
-
const nested = resultObj.data || resultObj.output;
|
|
209
|
-
if (nested && typeof nested === "object") {
|
|
210
|
-
for (const field of arrayFields) {
|
|
211
|
-
const arr = (nested as Record<string, unknown>)[field];
|
|
212
|
-
if (Array.isArray(arr)) {
|
|
213
|
-
for (const item of arr) {
|
|
214
|
-
const url = extractOutputUrl(item, urlFields);
|
|
215
|
-
if (url) {
|
|
216
|
-
urls.push(url);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// If no array found, try single URL
|
|
224
|
-
if (urls.length === 0) {
|
|
225
|
-
const singleUrl = extractOutputUrl(result, urlFields);
|
|
226
|
-
if (singleUrl) {
|
|
227
|
-
urls.push(singleUrl);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return urls;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Extract video URL from AI generation result
|
|
236
|
-
*/
|
|
237
|
-
export function extractVideoUrl(result: unknown): string | undefined {
|
|
238
|
-
return extractOutputUrl(result, [
|
|
239
|
-
"video_url",
|
|
240
|
-
"videoUrl",
|
|
241
|
-
"video",
|
|
242
|
-
"url",
|
|
243
|
-
]);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Extract thumbnail URL from AI generation result
|
|
248
|
-
*/
|
|
249
|
-
export function extractThumbnailUrl(result: unknown): string | undefined {
|
|
250
|
-
if (!result || typeof result !== "object") {
|
|
251
|
-
return undefined;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const resultObj = result as Record<string, unknown>;
|
|
255
|
-
|
|
256
|
-
// Check direct fields
|
|
257
|
-
const fields = ["thumbnail_url", "thumbnailUrl", "thumbnail", "poster"];
|
|
258
|
-
for (const field of fields) {
|
|
259
|
-
const value = resultObj[field];
|
|
260
|
-
if (typeof value === "string" && value.length > 0) {
|
|
261
|
-
return value;
|
|
262
|
-
}
|
|
263
|
-
if (value && typeof value === "object") {
|
|
264
|
-
const nested = value as Record<string, unknown>;
|
|
265
|
-
if (typeof nested.url === "string") {
|
|
266
|
-
return nested.url;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return undefined;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Extract audio URL from AI generation result
|
|
276
|
-
*/
|
|
277
|
-
export function extractAudioUrl(result: unknown): string | undefined {
|
|
278
|
-
return extractOutputUrl(result, [
|
|
279
|
-
"audio_url",
|
|
280
|
-
"audioUrl",
|
|
281
|
-
"audio",
|
|
282
|
-
"url",
|
|
283
|
-
]);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Extract image URLs from AI generation result
|
|
288
|
-
*/
|
|
289
|
-
export function extractImageUrls(result: unknown): string[] {
|
|
290
|
-
if (!result || typeof result !== "object") {
|
|
291
|
-
return [];
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const urls: string[] = [];
|
|
295
|
-
const resultObj = result as Record<string, unknown>;
|
|
296
|
-
|
|
297
|
-
// Check images array
|
|
298
|
-
if (Array.isArray(resultObj.images)) {
|
|
299
|
-
for (const img of resultObj.images) {
|
|
300
|
-
if (typeof img === "string" && img.length > 0) {
|
|
301
|
-
urls.push(img);
|
|
302
|
-
} else if (img && typeof img === "object") {
|
|
303
|
-
const imgObj = img as Record<string, unknown>;
|
|
304
|
-
if (typeof imgObj.url === "string") {
|
|
305
|
-
urls.push(imgObj.url);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Check single image
|
|
312
|
-
if (urls.length === 0) {
|
|
313
|
-
const singleUrl = extractOutputUrl(result, [
|
|
314
|
-
"image_url",
|
|
315
|
-
"imageUrl",
|
|
316
|
-
"image",
|
|
317
|
-
"url",
|
|
318
|
-
]);
|
|
319
|
-
if (singleUrl) {
|
|
320
|
-
urls.push(singleUrl);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return urls;
|
|
325
|
-
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Extractor Utility
|
|
3
|
+
* Extracts output URLs from AI generation results
|
|
4
|
+
* Supports various provider response formats
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Extract output URL from result
|
|
9
|
+
* Supports various AI provider response formats
|
|
10
|
+
*/
|
|
11
|
+
export function extractOutputUrl(
|
|
12
|
+
result: unknown,
|
|
13
|
+
urlFields?: string[],
|
|
14
|
+
): string | undefined {
|
|
15
|
+
if (!result || typeof result !== "object") {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const fields = urlFields ?? [
|
|
20
|
+
"url",
|
|
21
|
+
"image_url",
|
|
22
|
+
"video_url",
|
|
23
|
+
"output_url",
|
|
24
|
+
"result_url",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const resultObj = result as Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
// Check top-level fields
|
|
30
|
+
for (const field of fields) {
|
|
31
|
+
const value = resultObj[field];
|
|
32
|
+
if (typeof value === "string" && value.length > 0) {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check nested data/output objects
|
|
38
|
+
const nested =
|
|
39
|
+
(resultObj.data as Record<string, unknown>) ||
|
|
40
|
+
(resultObj.output as Record<string, unknown>) ||
|
|
41
|
+
(resultObj.result as Record<string, unknown>);
|
|
42
|
+
|
|
43
|
+
if (nested && typeof nested === "object") {
|
|
44
|
+
for (const field of fields) {
|
|
45
|
+
const value = nested[field];
|
|
46
|
+
if (typeof value === "string" && value.length > 0) {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check for nested image/video objects
|
|
52
|
+
const media =
|
|
53
|
+
(nested.image as Record<string, unknown>) ||
|
|
54
|
+
(nested.video as Record<string, unknown>);
|
|
55
|
+
if (media && typeof media === "object" && typeof media.url === "string") {
|
|
56
|
+
return media.url;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Extract multiple output URLs from result
|
|
65
|
+
*/
|
|
66
|
+
export function extractOutputUrls(
|
|
67
|
+
result: unknown,
|
|
68
|
+
urlFields?: string[],
|
|
69
|
+
): string[] {
|
|
70
|
+
if (!result || typeof result !== "object") {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const urls: string[] = [];
|
|
75
|
+
const resultObj = result as Record<string, unknown>;
|
|
76
|
+
|
|
77
|
+
// Check for arrays
|
|
78
|
+
const arrayFields = ["images", "videos", "outputs", "results", "urls"];
|
|
79
|
+
for (const field of arrayFields) {
|
|
80
|
+
const arr = resultObj[field];
|
|
81
|
+
if (Array.isArray(arr)) {
|
|
82
|
+
for (const item of arr) {
|
|
83
|
+
const url = extractOutputUrl(item, urlFields);
|
|
84
|
+
if (url) {
|
|
85
|
+
urls.push(url);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check nested data/output
|
|
92
|
+
const nested = resultObj.data || resultObj.output;
|
|
93
|
+
if (nested && typeof nested === "object") {
|
|
94
|
+
for (const field of arrayFields) {
|
|
95
|
+
const arr = (nested as Record<string, unknown>)[field];
|
|
96
|
+
if (Array.isArray(arr)) {
|
|
97
|
+
for (const item of arr) {
|
|
98
|
+
const url = extractOutputUrl(item, urlFields);
|
|
99
|
+
if (url) {
|
|
100
|
+
urls.push(url);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// If no array found, try single URL
|
|
108
|
+
if (urls.length === 0) {
|
|
109
|
+
const singleUrl = extractOutputUrl(result, urlFields);
|
|
110
|
+
if (singleUrl) {
|
|
111
|
+
urls.push(singleUrl);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return urls;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Extract video URL from AI generation result
|
|
120
|
+
*/
|
|
121
|
+
export function extractVideoUrl(result: unknown): string | undefined {
|
|
122
|
+
return extractOutputUrl(result, [
|
|
123
|
+
"video_url",
|
|
124
|
+
"videoUrl",
|
|
125
|
+
"video",
|
|
126
|
+
"url",
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Extract thumbnail URL from AI generation result
|
|
132
|
+
*/
|
|
133
|
+
export function extractThumbnailUrl(result: unknown): string | undefined {
|
|
134
|
+
if (!result || typeof result !== "object") {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const resultObj = result as Record<string, unknown>;
|
|
139
|
+
|
|
140
|
+
// Check direct fields
|
|
141
|
+
const fields = ["thumbnail_url", "thumbnailUrl", "thumbnail", "poster"];
|
|
142
|
+
for (const field of fields) {
|
|
143
|
+
const value = resultObj[field];
|
|
144
|
+
if (typeof value === "string" && value.length > 0) {
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
if (value && typeof value === "object") {
|
|
148
|
+
const nested = value as Record<string, unknown>;
|
|
149
|
+
if (typeof nested.url === "string") {
|
|
150
|
+
return nested.url;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Extract audio URL from AI generation result
|
|
160
|
+
*/
|
|
161
|
+
export function extractAudioUrl(result: unknown): string | undefined {
|
|
162
|
+
return extractOutputUrl(result, [
|
|
163
|
+
"audio_url",
|
|
164
|
+
"audioUrl",
|
|
165
|
+
"audio",
|
|
166
|
+
"url",
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Extract image URLs from AI generation result
|
|
172
|
+
*/
|
|
173
|
+
export function extractImageUrls(result: unknown): string[] {
|
|
174
|
+
if (!result || typeof result !== "object") {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const urls: string[] = [];
|
|
179
|
+
const resultObj = result as Record<string, unknown>;
|
|
180
|
+
|
|
181
|
+
// Check images array
|
|
182
|
+
if (Array.isArray(resultObj.images)) {
|
|
183
|
+
for (const img of resultObj.images) {
|
|
184
|
+
if (typeof img === "string" && img.length > 0) {
|
|
185
|
+
urls.push(img);
|
|
186
|
+
} else if (img && typeof img === "object") {
|
|
187
|
+
const imgObj = img as Record<string, unknown>;
|
|
188
|
+
if (typeof imgObj.url === "string") {
|
|
189
|
+
urls.push(imgObj.url);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check single image
|
|
196
|
+
if (urls.length === 0) {
|
|
197
|
+
const singleUrl = extractOutputUrl(result, [
|
|
198
|
+
"image_url",
|
|
199
|
+
"imageUrl",
|
|
200
|
+
"image",
|
|
201
|
+
"url",
|
|
202
|
+
]);
|
|
203
|
+
if (singleUrl) {
|
|
204
|
+
urls.push(singleUrl);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return urls;
|
|
209
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow State Utilities
|
|
3
|
+
* State initialization and update helpers for generation flow
|
|
4
|
+
*
|
|
5
|
+
* @package @umituz/react-native-ai-generation-content
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
GenerationFlowConfig,
|
|
10
|
+
GenerationFlowState,
|
|
11
|
+
PhotoStepData,
|
|
12
|
+
PhotoStepConfig,
|
|
13
|
+
} from "../types/flow-config.types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create initial flow state from config
|
|
17
|
+
*/
|
|
18
|
+
export function createInitialFlowState(
|
|
19
|
+
config: GenerationFlowConfig,
|
|
20
|
+
): GenerationFlowState {
|
|
21
|
+
return {
|
|
22
|
+
currentStepIndex: 0,
|
|
23
|
+
photoSteps: config.photoSteps.map((step) => ({
|
|
24
|
+
id: step.id,
|
|
25
|
+
imageUri: null,
|
|
26
|
+
previewUrl: undefined,
|
|
27
|
+
name: undefined,
|
|
28
|
+
isValid: undefined,
|
|
29
|
+
validationStatus: "pending" as const,
|
|
30
|
+
})),
|
|
31
|
+
textInput: config.textInputStep
|
|
32
|
+
? {
|
|
33
|
+
id: config.textInputStep.id,
|
|
34
|
+
text: "",
|
|
35
|
+
isValid: false,
|
|
36
|
+
}
|
|
37
|
+
: undefined,
|
|
38
|
+
isComplete: false,
|
|
39
|
+
isProcessing: false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Update photo step in state
|
|
45
|
+
*/
|
|
46
|
+
export function updatePhotoStep(
|
|
47
|
+
state: GenerationFlowState,
|
|
48
|
+
stepIndex: number,
|
|
49
|
+
updates: Partial<PhotoStepData>,
|
|
50
|
+
): GenerationFlowState {
|
|
51
|
+
const newPhotoSteps = [...state.photoSteps];
|
|
52
|
+
newPhotoSteps[stepIndex] = {
|
|
53
|
+
...newPhotoSteps[stepIndex],
|
|
54
|
+
...updates,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
...state,
|
|
59
|
+
photoSteps: newPhotoSteps,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if current step is valid
|
|
65
|
+
*/
|
|
66
|
+
export function isStepValid(
|
|
67
|
+
stepData: PhotoStepData | null,
|
|
68
|
+
stepConfig: PhotoStepConfig | null,
|
|
69
|
+
): boolean {
|
|
70
|
+
if (!stepData || !stepConfig) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check photo
|
|
75
|
+
if (!stepData.imageUri) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check name if required
|
|
80
|
+
if (stepConfig.requireNameInput && !stepData.name) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check validation if enabled
|
|
85
|
+
if (stepConfig.enableValidation && !stepData.isValid) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if text input is valid
|
|
94
|
+
*/
|
|
95
|
+
export function isTextInputValid(
|
|
96
|
+
text: string,
|
|
97
|
+
minLength: number,
|
|
98
|
+
maxLength: number,
|
|
99
|
+
): boolean {
|
|
100
|
+
return text.length >= minLength && text.length <= maxLength;
|
|
101
|
+
}
|