@umituz/react-native-ai-generation-content 1.34.1 → 1.35.0
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/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +47 -2
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.tsx +4 -2
- package/src/domains/scenarios/domain/Scenario.ts +2 -1
- package/src/domains/scenarios/index.ts +10 -0
- package/src/domains/scenarios/infrastructure/ScenariosData.ts +8 -4
- package/src/domains/scenarios/infrastructure/scenario-helpers.ts +122 -0
- package/src/index.ts +1 -0
- package/src/infrastructure/providers/generation-config.provider.tsx +49 -8
- package/src/infrastructure/providers/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.0",
|
|
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",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic Wizard Flow Component
|
|
3
3
|
* Config-driven wizard for AI generation features
|
|
4
|
+
* Supports both scenario object and scenarioId (resolved from provider)
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import React, { useMemo, useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -19,12 +20,16 @@ import { useResultActions } from "../../../../result-preview/presentation/hooks/
|
|
|
19
20
|
import { validateScenario } from "../utilities/validateScenario";
|
|
20
21
|
import { WizardStepRenderer } from "./WizardStepRenderer";
|
|
21
22
|
import { StarRatingPicker } from "../../../../result-preview/presentation/components/StarRatingPicker";
|
|
23
|
+
import { useGenerationConfig } from "../../../../../infrastructure/providers";
|
|
22
24
|
|
|
23
25
|
declare const __DEV__: boolean;
|
|
24
26
|
|
|
25
27
|
export interface GenericWizardFlowProps {
|
|
26
28
|
readonly featureConfig: WizardFeatureConfig;
|
|
29
|
+
/** Full scenario object - use this OR scenarioId */
|
|
27
30
|
readonly scenario?: WizardScenarioData;
|
|
31
|
+
/** Scenario ID - resolved from GenerationConfigProvider's scenarios */
|
|
32
|
+
readonly scenarioId?: string;
|
|
28
33
|
readonly userId?: string;
|
|
29
34
|
readonly alertMessages?: AlertMessages;
|
|
30
35
|
readonly skipResultStep?: boolean;
|
|
@@ -44,7 +49,8 @@ export interface GenericWizardFlowProps {
|
|
|
44
49
|
|
|
45
50
|
export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
46
51
|
featureConfig,
|
|
47
|
-
scenario,
|
|
52
|
+
scenario: scenarioProp,
|
|
53
|
+
scenarioId,
|
|
48
54
|
userId,
|
|
49
55
|
alertMessages,
|
|
50
56
|
skipResultStep = false,
|
|
@@ -63,12 +69,51 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
63
69
|
}) => {
|
|
64
70
|
const tokens = useAppDesignTokens();
|
|
65
71
|
const alert = useAlert();
|
|
72
|
+
const { getScenarioById, defaultOutputType } = useGenerationConfig();
|
|
66
73
|
const [currentCreation, setCurrentCreation] = useState<Creation | null>(null);
|
|
67
74
|
const [showRatingPicker, setShowRatingPicker] = useState(false);
|
|
68
75
|
const [hasRated, setHasRated] = useState(false);
|
|
69
|
-
const [
|
|
76
|
+
const [, setIsGeneratingDismissed] = useState(false);
|
|
70
77
|
const prevStepIdRef = useRef<string | undefined>(undefined);
|
|
71
78
|
|
|
79
|
+
// Resolve scenario: use prop directly OR lookup by scenarioId from provider
|
|
80
|
+
const scenario = useMemo<WizardScenarioData | undefined>(() => {
|
|
81
|
+
// If scenario prop is provided, use it directly
|
|
82
|
+
if (scenarioProp) {
|
|
83
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
84
|
+
console.log("[GenericWizardFlow] Using scenario from prop:", {
|
|
85
|
+
id: scenarioProp.id,
|
|
86
|
+
outputType: scenarioProp.outputType,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return scenarioProp;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If scenarioId is provided, lookup from provider
|
|
93
|
+
if (scenarioId) {
|
|
94
|
+
const found = getScenarioById(scenarioId);
|
|
95
|
+
if (found) {
|
|
96
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
97
|
+
console.log("[GenericWizardFlow] Resolved scenario from provider:", {
|
|
98
|
+
id: found.id,
|
|
99
|
+
outputType: found.outputType,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return found as unknown as WizardScenarioData;
|
|
103
|
+
}
|
|
104
|
+
// Scenario not found in provider - create minimal with default outputType
|
|
105
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
106
|
+
console.warn("[GenericWizardFlow] Scenario not found in provider:", scenarioId);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
id: scenarioId,
|
|
110
|
+
outputType: defaultOutputType,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return undefined;
|
|
115
|
+
}, [scenarioProp, scenarioId, getScenarioById, defaultOutputType]);
|
|
116
|
+
|
|
72
117
|
const repository = useMemo(() => createCreationsRepository("creations"), []);
|
|
73
118
|
|
|
74
119
|
const flowSteps = useMemo<StepDefinition[]>(() => {
|
|
@@ -167,7 +167,8 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
167
167
|
onBack={onBack}
|
|
168
168
|
onContinue={(text) => {
|
|
169
169
|
// Store text in a structure compatible with existing handlers
|
|
170
|
-
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
+
onPhotoContinue(step.id, { uri: text, text, previewUrl: "" } as unknown as any);
|
|
171
172
|
}}
|
|
172
173
|
/>
|
|
173
174
|
);
|
|
@@ -204,7 +205,8 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
204
205
|
onBack={onBack}
|
|
205
206
|
onContinue={(value) => {
|
|
206
207
|
// Store selection value
|
|
207
|
-
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
209
|
+
onPhotoContinue(step.id, { uri: String(value), selection: value, previewUrl: "" } as unknown as any);
|
|
208
210
|
}}
|
|
209
211
|
/>
|
|
210
212
|
);
|
|
@@ -573,7 +573,8 @@ export interface Scenario {
|
|
|
573
573
|
storyTemplate: string;
|
|
574
574
|
requiresPhoto?: boolean;
|
|
575
575
|
hidden?: boolean;
|
|
576
|
-
|
|
576
|
+
/** Output type - optional, apps should configure via createScenariosForApp() */
|
|
577
|
+
outputType?: ScenarioOutputType;
|
|
577
578
|
model?: string; // AI model from app config
|
|
578
579
|
enabled?: boolean;
|
|
579
580
|
// Optional custom generating screen messages
|
|
@@ -11,6 +11,16 @@ export type { Scenario } from "./domain/Scenario";
|
|
|
11
11
|
// Scenario Data
|
|
12
12
|
export { SCENARIOS } from "./infrastructure/ScenariosData";
|
|
13
13
|
|
|
14
|
+
// Scenario Helpers - For app-level configuration
|
|
15
|
+
export {
|
|
16
|
+
createScenariosForApp,
|
|
17
|
+
filterScenariosByOutputType,
|
|
18
|
+
filterScenariosByCategory,
|
|
19
|
+
getScenarioCategories,
|
|
20
|
+
findScenarioById,
|
|
21
|
+
} from "./infrastructure/scenario-helpers";
|
|
22
|
+
export type { AppScenarioConfig } from "./infrastructure/scenario-helpers";
|
|
23
|
+
|
|
14
24
|
// Utils
|
|
15
25
|
export { createStoryTemplate } from "./infrastructure/utils/scenario-utils";
|
|
16
26
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Generic for all AI generation apps (image/video)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Scenario, ScenarioCategory
|
|
7
|
+
import { Scenario, ScenarioCategory } from "../domain/Scenario";
|
|
8
8
|
import { TIME_BASED_SCENARIOS } from "./data/time-based-scenarios";
|
|
9
9
|
import { FAMILY_SCENARIOS } from "./data/family-scenarios";
|
|
10
10
|
import { LIFESTYLE_SCENARIOS } from "./data/lifestyle-scenarios";
|
|
@@ -78,11 +78,15 @@ import { UNDERWATER_SCENARIOS } from "./data/underwater-scenarios";
|
|
|
78
78
|
import { ARABIAN_NIGHTS_SCENARIOS } from "./data/arabian-nights-scenarios";
|
|
79
79
|
import { PREHISTORIC_WORLD_SCENARIOS } from "./data/prehistoric-world-scenarios";
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Assigns category to scenarios
|
|
83
|
+
* Note: outputType is NOT assigned here - apps configure via createScenariosForApp()
|
|
84
|
+
*/
|
|
85
|
+
const assignCategory = (scenarios: Omit<Scenario, 'category'>[], category: ScenarioCategory) =>
|
|
86
|
+
scenarios.map((s) => ({ ...s, category }));
|
|
83
87
|
|
|
84
88
|
export const SCENARIOS: Scenario[] = [
|
|
85
|
-
{ ...CUSTOM_SCENARIO, category: ScenarioCategory.FANTASY
|
|
89
|
+
{ ...CUSTOM_SCENARIO, category: ScenarioCategory.FANTASY },
|
|
86
90
|
|
|
87
91
|
// Time & Future
|
|
88
92
|
...assignCategory(TIME_BASED_SCENARIOS, ScenarioCategory.TIME_TRAVEL),
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario Helpers
|
|
3
|
+
* Utilities for configuring scenarios in apps
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Scenario, ScenarioOutputType } from "../domain/Scenario";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for creating app-specific scenarios
|
|
10
|
+
*/
|
|
11
|
+
export interface AppScenarioConfig {
|
|
12
|
+
/** Output type for all scenarios (image or video) */
|
|
13
|
+
readonly outputType: ScenarioOutputType;
|
|
14
|
+
/** Optional AI model to assign to all scenarios */
|
|
15
|
+
readonly model?: string;
|
|
16
|
+
/** Optional filter to exclude certain scenarios by ID */
|
|
17
|
+
readonly excludeIds?: readonly string[];
|
|
18
|
+
/** Optional filter to include only certain category IDs */
|
|
19
|
+
readonly includeCategories?: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates app-configured scenarios from package scenarios
|
|
24
|
+
* Apps use this to set their desired outputType and model
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Video generation app
|
|
28
|
+
* const scenarios = createScenariosForApp(SCENARIOS, {
|
|
29
|
+
* outputType: "video",
|
|
30
|
+
* model: "fal-ai/veo3/image-to-video"
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Image generation app
|
|
35
|
+
* const scenarios = createScenariosForApp(SCENARIOS, {
|
|
36
|
+
* outputType: "image",
|
|
37
|
+
* model: "fal-ai/nano-banana/edit",
|
|
38
|
+
* excludeIds: ["custom"]
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
export const createScenariosForApp = (
|
|
42
|
+
scenarios: readonly Scenario[],
|
|
43
|
+
config: AppScenarioConfig,
|
|
44
|
+
): Scenario[] => {
|
|
45
|
+
const { outputType, model, excludeIds, includeCategories } = config;
|
|
46
|
+
|
|
47
|
+
return scenarios
|
|
48
|
+
.filter((scenario) => {
|
|
49
|
+
// Filter by excluded IDs
|
|
50
|
+
if (excludeIds?.includes(scenario.id)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// Filter by included categories
|
|
54
|
+
if (
|
|
55
|
+
includeCategories &&
|
|
56
|
+
includeCategories.length > 0 &&
|
|
57
|
+
scenario.category &&
|
|
58
|
+
!includeCategories.includes(scenario.category)
|
|
59
|
+
) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
})
|
|
64
|
+
.map((scenario) => ({
|
|
65
|
+
...scenario,
|
|
66
|
+
outputType,
|
|
67
|
+
...(model && { model }),
|
|
68
|
+
}));
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Filters scenarios by output type (if they have one set)
|
|
73
|
+
* Useful for apps that have mixed scenarios with different output types
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const videoScenarios = filterScenariosByOutputType(scenarios, "video");
|
|
77
|
+
*/
|
|
78
|
+
export const filterScenariosByOutputType = (
|
|
79
|
+
scenarios: readonly Scenario[],
|
|
80
|
+
outputType: ScenarioOutputType,
|
|
81
|
+
): Scenario[] => scenarios.filter((s) => s.outputType === outputType);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Filters scenarios by category
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* const weddingScenarios = filterScenariosByCategory(scenarios, "wedding");
|
|
88
|
+
*/
|
|
89
|
+
export const filterScenariosByCategory = (
|
|
90
|
+
scenarios: readonly Scenario[],
|
|
91
|
+
category: string,
|
|
92
|
+
): Scenario[] => scenarios.filter((s) => s.category === category);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gets unique categories from scenarios
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* const categories = getScenarioCategories(scenarios);
|
|
99
|
+
* // ["wedding", "fantasy", "adventure", ...]
|
|
100
|
+
*/
|
|
101
|
+
export const getScenarioCategories = (
|
|
102
|
+
scenarios: readonly Scenario[],
|
|
103
|
+
): string[] => {
|
|
104
|
+
const categories = new Set<string>();
|
|
105
|
+
scenarios.forEach((s) => {
|
|
106
|
+
if (s.category) {
|
|
107
|
+
categories.add(s.category);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return Array.from(categories);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Finds a scenario by ID
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* const scenario = findScenarioById(scenarios, "beach_wedding");
|
|
118
|
+
*/
|
|
119
|
+
export const findScenarioById = (
|
|
120
|
+
scenarios: readonly Scenario[],
|
|
121
|
+
id: string,
|
|
122
|
+
): Scenario | undefined => scenarios.find((s) => s.id === id);
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generation Config Provider
|
|
3
3
|
* Provides app-specific configuration to the package
|
|
4
|
-
* NO hard-coded models, everything comes from app!
|
|
4
|
+
* NO hard-coded models/scenarios, everything comes from app!
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { createContext, useContext, type ReactNode } from "react";
|
|
7
|
+
import React, { createContext, useContext, useMemo, type ReactNode } from "react";
|
|
8
|
+
import type { Scenario, ScenarioOutputType } from "../../domains/scenarios/domain/Scenario";
|
|
8
9
|
|
|
9
10
|
declare const __DEV__: boolean;
|
|
10
11
|
|
|
@@ -35,11 +36,23 @@ export interface GenerationModels {
|
|
|
35
36
|
readonly textToVoice?: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/** Configured scenario from app */
|
|
40
|
+
export interface ConfiguredScenario extends Scenario {
|
|
41
|
+
/** Output type - REQUIRED when configured by app */
|
|
42
|
+
readonly outputType: ScenarioOutputType;
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
export interface GenerationConfigValue {
|
|
39
46
|
/** AI models configuration from app */
|
|
40
47
|
readonly models: GenerationModels;
|
|
48
|
+
/** Configured scenarios from app (with outputType set) */
|
|
49
|
+
readonly scenarios: readonly ConfiguredScenario[];
|
|
50
|
+
/** Default output type for this app */
|
|
51
|
+
readonly defaultOutputType: ScenarioOutputType;
|
|
41
52
|
/** Get model for specific feature type */
|
|
42
53
|
readonly getModel: (featureType: keyof GenerationModels) => string;
|
|
54
|
+
/** Get scenario by ID from configured scenarios */
|
|
55
|
+
readonly getScenarioById: (id: string) => ConfiguredScenario | undefined;
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
// ============================================================================
|
|
@@ -54,24 +67,29 @@ const GenerationConfigContext = createContext<GenerationConfigValue | null>(null
|
|
|
54
67
|
|
|
55
68
|
export interface GenerationConfigProviderProps {
|
|
56
69
|
readonly children: ReactNode;
|
|
70
|
+
/** AI models configuration */
|
|
57
71
|
readonly models: GenerationModels;
|
|
72
|
+
/** App-configured scenarios (optional - for scenario-based generation) */
|
|
73
|
+
readonly scenarios?: readonly ConfiguredScenario[];
|
|
74
|
+
/** Default output type for this app (default: "video") */
|
|
75
|
+
readonly defaultOutputType?: ScenarioOutputType;
|
|
58
76
|
}
|
|
59
77
|
|
|
60
78
|
export const GenerationConfigProvider: React.FC<GenerationConfigProviderProps> = ({
|
|
61
79
|
children,
|
|
62
80
|
models,
|
|
81
|
+
scenarios = [],
|
|
82
|
+
defaultOutputType = "video",
|
|
63
83
|
}) => {
|
|
64
84
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
65
85
|
const configuredModels = Object.entries(models)
|
|
66
86
|
.filter(([, value]) => !!value)
|
|
67
87
|
.map(([key]) => key);
|
|
68
88
|
|
|
69
|
-
console.log("[GenerationConfigProvider] Initialized
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
imageToVideo: models.imageToVideo || "not configured",
|
|
74
|
-
textToVideo: models.textToVideo || "not configured",
|
|
89
|
+
console.log("[GenerationConfigProvider] Initialized:", {
|
|
90
|
+
models: configuredModels,
|
|
91
|
+
scenariosCount: scenarios.length,
|
|
92
|
+
defaultOutputType,
|
|
75
93
|
});
|
|
76
94
|
}
|
|
77
95
|
|
|
@@ -91,9 +109,32 @@ export const GenerationConfigProvider: React.FC<GenerationConfigProviderProps> =
|
|
|
91
109
|
return model;
|
|
92
110
|
};
|
|
93
111
|
|
|
112
|
+
const scenarioMap = useMemo(() => {
|
|
113
|
+
const map = new Map<string, ConfiguredScenario>();
|
|
114
|
+
for (const scenario of scenarios) {
|
|
115
|
+
map.set(scenario.id, scenario);
|
|
116
|
+
}
|
|
117
|
+
return map;
|
|
118
|
+
}, [scenarios]);
|
|
119
|
+
|
|
120
|
+
const getScenarioById = (id: string): ConfiguredScenario | undefined => {
|
|
121
|
+
const found = scenarioMap.get(id);
|
|
122
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
123
|
+
console.log("[GenerationConfigProvider] getScenarioById:", {
|
|
124
|
+
id,
|
|
125
|
+
found: !!found,
|
|
126
|
+
outputType: found?.outputType,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return found;
|
|
130
|
+
};
|
|
131
|
+
|
|
94
132
|
const value: GenerationConfigValue = {
|
|
95
133
|
models,
|
|
134
|
+
scenarios,
|
|
135
|
+
defaultOutputType,
|
|
96
136
|
getModel,
|
|
137
|
+
getScenarioById,
|
|
97
138
|
};
|
|
98
139
|
|
|
99
140
|
return (
|