@umituz/react-native-ai-generation-content 1.83.69 → 1.83.71
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 -2
- package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.ts +1 -0
- package/src/domains/generation/wizard/infrastructure/strategies/shared/unified-prompt-builder.ts +28 -6
- package/src/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +4 -0
- package/src/domains/generation/wizard/presentation/components/WizardFlowContent.tsx +4 -0
- package/src/domains/generation/wizard/presentation/hooks/usePhotoUploadState.ts +3 -5
- package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +3 -1
- package/src/domains/generation/wizard/presentation/hooks/wizard-generation.types.ts +2 -0
- package/src/domains/prompts/domain/entities/MultiPersonPromptStructure.ts +22 -13
- package/src/domains/prompts/index.ts +1 -0
- package/src/presentation/components/display/VideoResultPlayer.tsx +14 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.83.
|
|
3
|
+
"version": "1.83.71",
|
|
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",
|
|
@@ -52,7 +52,6 @@
|
|
|
52
52
|
"@umituz/react-native-subscription": "*",
|
|
53
53
|
"@umituz/react-native-video-editor": "*",
|
|
54
54
|
"expo": ">=54.0.0",
|
|
55
|
-
"expo-video": ">=1.0.0",
|
|
56
55
|
"firebase": ">=10.0.0",
|
|
57
56
|
"react": ">=19.0.0",
|
|
58
57
|
"react-native": "*",
|
package/src/domains/generation/wizard/infrastructure/strategies/image-generation.executor.ts
CHANGED
|
@@ -65,6 +65,7 @@ export async function executeImageGeneration(
|
|
|
65
65
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
66
66
|
const mode = imageUrls.length > 0 ? "Photo-based" : "Text-to-image";
|
|
67
67
|
console.log(`[ImageExecutor] ${mode} generation`, { personCount: imageUrls.length });
|
|
68
|
+
console.log(`[ImageExecutor] Final prompt (${finalPrompt.length} chars):\n${finalPrompt.substring(0, 800)}${finalPrompt.length > 800 ? "\n...[truncated]" : ""}`);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
const modelInput: Record<string, unknown> = {
|
package/src/domains/generation/wizard/infrastructure/strategies/shared/unified-prompt-builder.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unified Prompt Builder
|
|
3
3
|
* Single prompt building logic for ALL generation types (image & video)
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* For photo-based scenarios: the app's scenario already uses createPhotorealisticPrompt()
|
|
6
|
+
* to build a structured prompt. This builder only injects the MULTI-PERSON block
|
|
7
|
+
* into that existing structure — it does NOT re-wrap.
|
|
8
|
+
*
|
|
9
|
+
* For text-only scenarios: uses createPhotorealisticPrompt for wrapping.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
|
-
import {
|
|
12
|
+
import { IDENTITY_PRESERVATION_CORE } from "../../../../../prompts/domain/base/constants";
|
|
13
|
+
import {
|
|
14
|
+
createMultiPersonBlock,
|
|
15
|
+
createMultiPersonPrompt,
|
|
16
|
+
} from "../../../../../prompts/domain/entities/MultiPersonPromptStructure";
|
|
9
17
|
import { createPhotorealisticPrompt } from "../../../../../prompts/domain/base/creators";
|
|
10
18
|
|
|
11
19
|
interface BuildPromptOptions {
|
|
@@ -21,7 +29,8 @@ interface BuildPromptOptions {
|
|
|
21
29
|
|
|
22
30
|
/**
|
|
23
31
|
* Build unified prompt for any generation type
|
|
24
|
-
* - Photo-based:
|
|
32
|
+
* - Photo-based (already structured): Injects MULTI-PERSON block into existing prompt
|
|
33
|
+
* - Photo-based (raw text): Full wrapping with createMultiPersonPrompt
|
|
25
34
|
* - Text-only: Uses createPhotorealisticPrompt
|
|
26
35
|
* - Custom: Uses basePrompt directly when skipIdentityPreservation is true
|
|
27
36
|
*/
|
|
@@ -42,8 +51,21 @@ export function buildUnifiedPrompt(options: BuildPromptOptions): string {
|
|
|
42
51
|
return basePrompt;
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
//
|
|
46
|
-
|
|
54
|
+
// Photo-based: check if prompt is already structured by createPhotorealisticPrompt
|
|
55
|
+
const isAlreadyStructured = basePrompt.includes(IDENTITY_PRESERVATION_CORE);
|
|
56
|
+
|
|
57
|
+
let finalPrompt: string;
|
|
58
|
+
|
|
59
|
+
if (isAlreadyStructured) {
|
|
60
|
+
// Prompt already has IDENTITY + PHOTOREALISTIC + POSE from createPhotorealisticPrompt.
|
|
61
|
+
// Only inject the MULTI-PERSON block after IDENTITY_PRESERVATION_CORE.
|
|
62
|
+
const multiPersonBlock = createMultiPersonBlock(photoCount);
|
|
63
|
+
const insertPos = basePrompt.indexOf(IDENTITY_PRESERVATION_CORE) + IDENTITY_PRESERVATION_CORE.length;
|
|
64
|
+
finalPrompt = basePrompt.slice(0, insertPos) + "\n\n" + multiPersonBlock + basePrompt.slice(insertPos);
|
|
65
|
+
} else {
|
|
66
|
+
// Raw text prompt — apply full wrapping
|
|
67
|
+
finalPrompt = createMultiPersonPrompt(basePrompt, photoCount);
|
|
68
|
+
}
|
|
47
69
|
|
|
48
70
|
// Add interaction style if specified by scenario (no defaults)
|
|
49
71
|
if (interactionStyle) {
|
|
@@ -36,6 +36,8 @@ export interface GenericWizardFlowProps {
|
|
|
36
36
|
readonly deductCredits?: (cost: number) => Promise<boolean>;
|
|
37
37
|
readonly skipResultStep?: boolean;
|
|
38
38
|
readonly onStepChange?: (stepId: string, stepType: StepType | string) => void;
|
|
39
|
+
/** When true, the GENERATING step is visible but generation waits (e.g. for prompt enhancement) */
|
|
40
|
+
readonly isPreparing?: boolean;
|
|
39
41
|
readonly onGenerationStart?: (
|
|
40
42
|
data: Record<string, unknown>,
|
|
41
43
|
proceedToGenerating: () => void,
|
|
@@ -66,6 +68,7 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = (props) => {
|
|
|
66
68
|
deductCredits,
|
|
67
69
|
skipResultStep = false,
|
|
68
70
|
onStepChange,
|
|
71
|
+
isPreparing = false,
|
|
69
72
|
onGenerationStart,
|
|
70
73
|
onGenerationComplete,
|
|
71
74
|
onGenerationError,
|
|
@@ -124,6 +127,7 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = (props) => {
|
|
|
124
127
|
calculateCredits={calculateCredits}
|
|
125
128
|
deductCredits={deductCredits}
|
|
126
129
|
skipResultStep={skipResultStep}
|
|
130
|
+
isPreparing={isPreparing}
|
|
127
131
|
onStepChange={onStepChange}
|
|
128
132
|
onGenerationStart={onGenerationStart}
|
|
129
133
|
onGenerationComplete={onGenerationComplete}
|
|
@@ -37,6 +37,8 @@ interface WizardFlowContentProps {
|
|
|
37
37
|
/** Called after successful generation to deduct credits — provided by the app */
|
|
38
38
|
readonly deductCredits?: (cost: number) => Promise<boolean>;
|
|
39
39
|
readonly skipResultStep?: boolean;
|
|
40
|
+
/** When true, the GENERATING step is visible but generation waits (e.g. for prompt enhancement) */
|
|
41
|
+
readonly isPreparing?: boolean;
|
|
40
42
|
readonly onStepChange?: (stepId: string, stepType: StepType | string) => void;
|
|
41
43
|
readonly onGenerationStart?: (
|
|
42
44
|
data: Record<string, unknown>,
|
|
@@ -66,6 +68,7 @@ export const WizardFlowContent: React.FC<WizardFlowContentProps> = (props) => {
|
|
|
66
68
|
calculateCredits,
|
|
67
69
|
deductCredits,
|
|
68
70
|
skipResultStep = false,
|
|
71
|
+
isPreparing = false,
|
|
69
72
|
onStepChange,
|
|
70
73
|
onGenerationStart,
|
|
71
74
|
onGenerationComplete,
|
|
@@ -189,6 +192,7 @@ export const WizardFlowContent: React.FC<WizardFlowContentProps> = (props) => {
|
|
|
189
192
|
wizardData: customData,
|
|
190
193
|
userId,
|
|
191
194
|
isGeneratingStep: currentStep?.type === StepType.GENERATING,
|
|
195
|
+
isPreparing,
|
|
192
196
|
alertMessages,
|
|
193
197
|
creditCost: calculatedCreditCost,
|
|
194
198
|
deductCredits,
|
|
@@ -80,12 +80,10 @@ export const usePhotoUploadState = ({
|
|
|
80
80
|
if (isLoading) {
|
|
81
81
|
timeoutRef.current = setTimeout(() => {
|
|
82
82
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
83
|
-
console.warn("[usePhotoUploadState] Image picker timeout - possible stuck state");
|
|
83
|
+
console.warn("[usePhotoUploadState] Image picker timeout - possible stuck state (DEV warning only)");
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
message: "Image selection is taking too long. Please try again.",
|
|
88
|
-
});
|
|
85
|
+
// NOTE: Do NOT call onError here — the picker may still complete successfully.
|
|
86
|
+
// Showing a modal alert while the picker is open blocks the UI.
|
|
89
87
|
}, 30000);
|
|
90
88
|
}
|
|
91
89
|
|
|
@@ -26,6 +26,7 @@ export const useWizardGeneration = (props: UseWizardGenerationProps): UseWizardG
|
|
|
26
26
|
wizardData,
|
|
27
27
|
userId,
|
|
28
28
|
isGeneratingStep,
|
|
29
|
+
isPreparing = false,
|
|
29
30
|
alertMessages,
|
|
30
31
|
creditCost,
|
|
31
32
|
deductCredits,
|
|
@@ -84,7 +85,7 @@ export const useWizardGeneration = (props: UseWizardGenerationProps): UseWizardG
|
|
|
84
85
|
useEffect(() => {
|
|
85
86
|
const isAlreadyGenerating = videoGeneration.isGenerating || photoGeneration.isGenerating;
|
|
86
87
|
|
|
87
|
-
if (isGeneratingStep && state.status === "IDLE" && !isAlreadyGenerating) {
|
|
88
|
+
if (isGeneratingStep && state.status === "IDLE" && !isAlreadyGenerating && !isPreparing) {
|
|
88
89
|
dispatch({ type: "START_PREPARATION" });
|
|
89
90
|
|
|
90
91
|
// Execute generation and handle errors properly
|
|
@@ -112,6 +113,7 @@ export const useWizardGeneration = (props: UseWizardGenerationProps): UseWizardG
|
|
|
112
113
|
}
|
|
113
114
|
}, [
|
|
114
115
|
isGeneratingStep,
|
|
116
|
+
isPreparing,
|
|
115
117
|
state.status,
|
|
116
118
|
scenario,
|
|
117
119
|
wizardData,
|
|
@@ -32,6 +32,8 @@ export interface UseWizardGenerationProps {
|
|
|
32
32
|
readonly wizardData: Record<string, unknown>;
|
|
33
33
|
readonly userId?: string;
|
|
34
34
|
readonly isGeneratingStep: boolean;
|
|
35
|
+
/** When true, generation waits even if on GENERATING step (e.g. prompt enhancement in progress) */
|
|
36
|
+
readonly isPreparing?: boolean;
|
|
35
37
|
/** Required - alert messages for error states */
|
|
36
38
|
readonly alertMessages: AlertMessages;
|
|
37
39
|
/** Credit cost for this generation - REQUIRED, determined by the app */
|
|
@@ -23,23 +23,15 @@ export const MULTI_PERSON_PRESERVATION_RULES: MultiPersonPreservationRules = {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Creates
|
|
27
|
-
*
|
|
28
|
-
* @param scenarioPrompt - The scenario description
|
|
29
|
-
* @param personCount - Number of people (1, 2, 3, N)
|
|
30
|
-
* @returns Complete prompt with identity preservation for all people
|
|
26
|
+
* Creates ONLY the multi-person identity block (no full wrapping).
|
|
27
|
+
* Used to inject into an already-structured prompt.
|
|
31
28
|
*/
|
|
32
|
-
export const
|
|
33
|
-
scenarioPrompt: string,
|
|
34
|
-
personCount: number,
|
|
35
|
-
): string => {
|
|
29
|
+
export const createMultiPersonBlock = (personCount: number): string => {
|
|
36
30
|
const personRefs = Array.from({ length: personCount }, (_, i) =>
|
|
37
31
|
`Person ${i + 1}: @image${i + 1} - preserve 100% facial identity`
|
|
38
32
|
).join("\n ");
|
|
39
33
|
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
MULTI-PERSON IDENTITY PRESERVATION (${personCount} people):
|
|
34
|
+
return `MULTI-PERSON IDENTITY PRESERVATION (${personCount} people):
|
|
43
35
|
{
|
|
44
36
|
"requirement": "${MULTI_PERSON_PRESERVATION_RULES.requirement}",
|
|
45
37
|
"references": [
|
|
@@ -47,7 +39,24 @@ MULTI-PERSON IDENTITY PRESERVATION (${personCount} people):
|
|
|
47
39
|
],
|
|
48
40
|
"forbidden": ${JSON.stringify(MULTI_PERSON_PRESERVATION_RULES.forbidden)},
|
|
49
41
|
"positioning": "${MULTI_PERSON_PRESERVATION_RULES.positioning}"
|
|
50
|
-
}
|
|
42
|
+
}`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a multi-person prompt dynamically
|
|
47
|
+
* Used as fallback when basePrompt is raw text (not already structured).
|
|
48
|
+
*
|
|
49
|
+
* @param scenarioPrompt - The scenario description
|
|
50
|
+
* @param personCount - Number of people (1, 2, 3, N)
|
|
51
|
+
* @returns Complete prompt with identity preservation for all people
|
|
52
|
+
*/
|
|
53
|
+
export const createMultiPersonPrompt = (
|
|
54
|
+
scenarioPrompt: string,
|
|
55
|
+
personCount: number,
|
|
56
|
+
): string => {
|
|
57
|
+
return `${IDENTITY_PRESERVATION_CORE}
|
|
58
|
+
|
|
59
|
+
${createMultiPersonBlock(personCount)}
|
|
51
60
|
|
|
52
61
|
${PHOTOREALISTIC_RENDERING}
|
|
53
62
|
|
|
@@ -54,6 +54,7 @@ export type { CreatePromptOptions } from './domain/base/types';
|
|
|
54
54
|
|
|
55
55
|
export {
|
|
56
56
|
MULTI_PERSON_PRESERVATION_RULES,
|
|
57
|
+
createMultiPersonBlock,
|
|
57
58
|
createMultiPersonPrompt,
|
|
58
59
|
} from './domain/entities/MultiPersonPromptStructure';
|
|
59
60
|
export type { MultiPersonPreservationRules } from './domain/entities/MultiPersonPromptStructure';
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
// expo-video is optional — module-level lazy require with null stubs
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
let useVideoPlayer: (...args: any[]) => any = () => null;
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
let VideoView: React.ComponentType<any> = () => null;
|
|
8
|
+
try {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
10
|
+
const expoVideo = require("expo-video");
|
|
11
|
+
useVideoPlayer = expoVideo.useVideoPlayer;
|
|
12
|
+
VideoView = expoVideo.VideoView;
|
|
13
|
+
} catch {
|
|
14
|
+
// expo-video not installed in consuming app
|
|
15
|
+
}
|
|
3
16
|
|
|
4
17
|
interface VideoResultPlayerProps {
|
|
5
18
|
uri: string;
|