@umituz/react-native-ai-generation-content 1.35.0 → 1.35.1
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 +188 -143
- package/src/domains/scenarios/index.ts +10 -0
- package/src/domains/scenarios/infrastructure/scenario-registry.ts +101 -0
- package/src/index.ts +0 -1
- package/src/infrastructure/providers/generation-config.provider.tsx +15 -99
- package/src/infrastructure/providers/index.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.35.
|
|
3
|
+
"version": "1.35.1",
|
|
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,17 +1,28 @@
|
|
|
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
|
|
4
|
+
* Supports both scenario object and scenarioId (resolved from registry)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React, { useMemo, useCallback, useEffect, useRef, useState } from "react";
|
|
8
8
|
import { View, StyleSheet } from "react-native";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
useAppDesignTokens,
|
|
11
|
+
useAlert,
|
|
12
|
+
AlertType,
|
|
13
|
+
AlertMode,
|
|
14
|
+
} from "@umituz/react-native-design-system";
|
|
10
15
|
import { useFlow } from "../../../infrastructure/flow/useFlow";
|
|
11
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
StepType,
|
|
18
|
+
type StepDefinition,
|
|
19
|
+
} from "../../../../../domain/entities/flow-config.types";
|
|
12
20
|
import type { WizardFeatureConfig } from "../../domain/entities/wizard-config.types";
|
|
13
21
|
import { buildFlowStepsFromWizard } from "../../infrastructure/builders/dynamic-step-builder";
|
|
14
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
useWizardGeneration,
|
|
24
|
+
type WizardScenarioData,
|
|
25
|
+
} from "../hooks/useWizardGeneration";
|
|
15
26
|
import type { AlertMessages } from "../../../../../presentation/hooks/generation/types";
|
|
16
27
|
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
17
28
|
import type { Creation } from "../../../../creations/domain/entities/Creation";
|
|
@@ -20,21 +31,25 @@ import { useResultActions } from "../../../../result-preview/presentation/hooks/
|
|
|
20
31
|
import { validateScenario } from "../utilities/validateScenario";
|
|
21
32
|
import { WizardStepRenderer } from "./WizardStepRenderer";
|
|
22
33
|
import { StarRatingPicker } from "../../../../result-preview/presentation/components/StarRatingPicker";
|
|
23
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
getConfiguredScenario,
|
|
36
|
+
getDefaultOutputType,
|
|
37
|
+
} from "../../../../scenarios/infrastructure/scenario-registry";
|
|
24
38
|
|
|
25
39
|
declare const __DEV__: boolean;
|
|
26
40
|
|
|
27
41
|
export interface GenericWizardFlowProps {
|
|
28
42
|
readonly featureConfig: WizardFeatureConfig;
|
|
29
|
-
/** Full scenario object - use this OR scenarioId */
|
|
30
43
|
readonly scenario?: WizardScenarioData;
|
|
31
|
-
/** Scenario ID - resolved from GenerationConfigProvider's scenarios */
|
|
32
44
|
readonly scenarioId?: string;
|
|
33
45
|
readonly userId?: string;
|
|
34
46
|
readonly alertMessages?: AlertMessages;
|
|
35
47
|
readonly skipResultStep?: boolean;
|
|
36
48
|
readonly onStepChange?: (stepId: string, stepType: StepType | string) => void;
|
|
37
|
-
readonly onGenerationStart?: (
|
|
49
|
+
readonly onGenerationStart?: (
|
|
50
|
+
data: Record<string, unknown>,
|
|
51
|
+
proceedToGenerating: () => void,
|
|
52
|
+
) => void;
|
|
38
53
|
readonly onGenerationComplete?: (result: unknown) => void;
|
|
39
54
|
readonly onGenerationError?: (error: string) => void;
|
|
40
55
|
readonly onCreditsExhausted?: () => void;
|
|
@@ -47,38 +62,32 @@ export interface GenericWizardFlowProps {
|
|
|
47
62
|
readonly renderResult?: (result: unknown) => React.ReactElement | null;
|
|
48
63
|
}
|
|
49
64
|
|
|
50
|
-
export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
65
|
+
export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = (props) => {
|
|
66
|
+
const {
|
|
67
|
+
featureConfig,
|
|
68
|
+
scenario: scenarioProp,
|
|
69
|
+
scenarioId,
|
|
70
|
+
userId,
|
|
71
|
+
alertMessages,
|
|
72
|
+
skipResultStep = false,
|
|
73
|
+
onStepChange,
|
|
74
|
+
onGenerationStart,
|
|
75
|
+
onGenerationComplete,
|
|
76
|
+
onGenerationError,
|
|
77
|
+
onCreditsExhausted,
|
|
78
|
+
onBack,
|
|
79
|
+
onTryAgain,
|
|
80
|
+
t,
|
|
81
|
+
renderPreview,
|
|
82
|
+
renderGenerating,
|
|
83
|
+
renderResult,
|
|
84
|
+
} = props;
|
|
85
|
+
|
|
70
86
|
const tokens = useAppDesignTokens();
|
|
71
87
|
const alert = useAlert();
|
|
72
|
-
const { getScenarioById, defaultOutputType } = useGenerationConfig();
|
|
73
|
-
const [currentCreation, setCurrentCreation] = useState<Creation | null>(null);
|
|
74
|
-
const [showRatingPicker, setShowRatingPicker] = useState(false);
|
|
75
|
-
const [hasRated, setHasRated] = useState(false);
|
|
76
|
-
const [, setIsGeneratingDismissed] = useState(false);
|
|
77
|
-
const prevStepIdRef = useRef<string | undefined>(undefined);
|
|
78
88
|
|
|
79
|
-
// Resolve scenario
|
|
89
|
+
// Resolve scenario from prop or registry
|
|
80
90
|
const scenario = useMemo<WizardScenarioData | undefined>(() => {
|
|
81
|
-
// If scenario prop is provided, use it directly
|
|
82
91
|
if (scenarioProp) {
|
|
83
92
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
84
93
|
console.log("[GenericWizardFlow] Using scenario from prop:", {
|
|
@@ -89,40 +98,101 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
89
98
|
return scenarioProp;
|
|
90
99
|
}
|
|
91
100
|
|
|
92
|
-
// If scenarioId is provided, lookup from provider
|
|
93
101
|
if (scenarioId) {
|
|
94
|
-
const found =
|
|
102
|
+
const found = getConfiguredScenario(scenarioId);
|
|
95
103
|
if (found) {
|
|
96
104
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
97
|
-
console.log("[GenericWizardFlow] Resolved
|
|
105
|
+
console.log("[GenericWizardFlow] Resolved from registry:", {
|
|
98
106
|
id: found.id,
|
|
99
107
|
outputType: found.outputType,
|
|
100
108
|
});
|
|
101
109
|
}
|
|
102
|
-
return found
|
|
110
|
+
return found;
|
|
103
111
|
}
|
|
104
|
-
// Scenario not found in provider - create minimal with default outputType
|
|
105
112
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
106
|
-
console.warn("[GenericWizardFlow] Scenario not
|
|
113
|
+
console.warn("[GenericWizardFlow] Scenario not in registry:", scenarioId);
|
|
107
114
|
}
|
|
108
|
-
return {
|
|
109
|
-
id: scenarioId,
|
|
110
|
-
outputType: defaultOutputType,
|
|
111
|
-
};
|
|
115
|
+
return { id: scenarioId, outputType: getDefaultOutputType() };
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
return undefined;
|
|
115
|
-
}, [scenarioProp, scenarioId
|
|
119
|
+
}, [scenarioProp, scenarioId]);
|
|
120
|
+
|
|
121
|
+
const validatedScenario = useMemo(
|
|
122
|
+
() => validateScenario(scenario),
|
|
123
|
+
[scenario],
|
|
124
|
+
);
|
|
116
125
|
|
|
126
|
+
return (
|
|
127
|
+
<WizardFlowContent
|
|
128
|
+
featureConfig={featureConfig}
|
|
129
|
+
scenario={scenario}
|
|
130
|
+
validatedScenario={validatedScenario}
|
|
131
|
+
userId={userId}
|
|
132
|
+
alertMessages={alertMessages}
|
|
133
|
+
skipResultStep={skipResultStep}
|
|
134
|
+
onStepChange={onStepChange}
|
|
135
|
+
onGenerationStart={onGenerationStart}
|
|
136
|
+
onGenerationComplete={onGenerationComplete}
|
|
137
|
+
onGenerationError={onGenerationError}
|
|
138
|
+
onCreditsExhausted={onCreditsExhausted}
|
|
139
|
+
onBack={onBack}
|
|
140
|
+
onTryAgain={onTryAgain}
|
|
141
|
+
t={t}
|
|
142
|
+
tokens={tokens}
|
|
143
|
+
alert={alert}
|
|
144
|
+
renderPreview={renderPreview}
|
|
145
|
+
renderGenerating={renderGenerating}
|
|
146
|
+
renderResult={renderResult}
|
|
147
|
+
/>
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
interface WizardFlowContentProps
|
|
152
|
+
extends Omit<GenericWizardFlowProps, "scenarioId" | "translations"> {
|
|
153
|
+
readonly validatedScenario: WizardScenarioData;
|
|
154
|
+
readonly tokens: ReturnType<typeof useAppDesignTokens>;
|
|
155
|
+
readonly alert: ReturnType<typeof useAlert>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const WizardFlowContent: React.FC<WizardFlowContentProps> = (props) => {
|
|
159
|
+
const {
|
|
160
|
+
featureConfig,
|
|
161
|
+
scenario,
|
|
162
|
+
validatedScenario,
|
|
163
|
+
userId,
|
|
164
|
+
alertMessages,
|
|
165
|
+
skipResultStep = false,
|
|
166
|
+
onStepChange,
|
|
167
|
+
onGenerationStart,
|
|
168
|
+
onGenerationComplete,
|
|
169
|
+
onGenerationError,
|
|
170
|
+
onCreditsExhausted,
|
|
171
|
+
onBack,
|
|
172
|
+
onTryAgain,
|
|
173
|
+
t,
|
|
174
|
+
tokens,
|
|
175
|
+
alert,
|
|
176
|
+
renderPreview,
|
|
177
|
+
renderGenerating,
|
|
178
|
+
renderResult,
|
|
179
|
+
} = props;
|
|
180
|
+
|
|
181
|
+
const [currentCreation, setCurrentCreation] = useState<Creation | null>(null);
|
|
182
|
+
const [showRatingPicker, setShowRatingPicker] = useState(false);
|
|
183
|
+
const [hasRated, setHasRated] = useState(false);
|
|
184
|
+
const prevStepIdRef = useRef<string | undefined>(undefined);
|
|
117
185
|
const repository = useMemo(() => createCreationsRepository("creations"), []);
|
|
118
186
|
|
|
119
|
-
const flowSteps = useMemo<StepDefinition[]>(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
187
|
+
const flowSteps = useMemo<StepDefinition[]>(
|
|
188
|
+
() =>
|
|
189
|
+
buildFlowStepsFromWizard(featureConfig, {
|
|
190
|
+
includePreview: true,
|
|
191
|
+
includeGenerating: true,
|
|
192
|
+
includeResult: !skipResultStep,
|
|
193
|
+
}),
|
|
194
|
+
[featureConfig, skipResultStep],
|
|
195
|
+
);
|
|
126
196
|
|
|
127
197
|
const flow = useFlow({ steps: flowSteps, initialStepIndex: 0 });
|
|
128
198
|
const {
|
|
@@ -137,30 +207,25 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
137
207
|
setResult,
|
|
138
208
|
} = flow;
|
|
139
209
|
|
|
140
|
-
const resultImageUrl =
|
|
210
|
+
const resultImageUrl =
|
|
211
|
+
currentCreation?.output?.imageUrl || currentCreation?.uri || "";
|
|
141
212
|
const resultVideoUrl = currentCreation?.output?.videoUrl || "";
|
|
142
|
-
const { isSaving, isSharing, handleDownload, handleShare } = useResultActions(
|
|
143
|
-
imageUrl: resultImageUrl,
|
|
144
|
-
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const validatedScenario = useMemo(() => validateScenario(scenario), [scenario]);
|
|
148
|
-
|
|
149
|
-
const handleGenerationComplete = useCallback((result: unknown) => {
|
|
150
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
151
|
-
console.log("[GenericWizardFlow] Generation completed", { skipResultStep });
|
|
152
|
-
}
|
|
153
|
-
setResult(result);
|
|
154
|
-
setCurrentCreation(result as Creation);
|
|
155
|
-
|
|
156
|
-
// Call onGenerationComplete first so parent can navigate if needed
|
|
157
|
-
onGenerationComplete?.(result);
|
|
213
|
+
const { isSaving, isSharing, handleDownload, handleShare } = useResultActions(
|
|
214
|
+
{ imageUrl: resultImageUrl, videoUrl: resultVideoUrl },
|
|
215
|
+
);
|
|
158
216
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
217
|
+
const handleGenerationComplete = useCallback(
|
|
218
|
+
(result: unknown) => {
|
|
219
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
220
|
+
console.log("[WizardFlowContent] Generation completed");
|
|
221
|
+
}
|
|
222
|
+
setResult(result);
|
|
223
|
+
setCurrentCreation(result as Creation);
|
|
224
|
+
onGenerationComplete?.(result);
|
|
225
|
+
if (!skipResultStep) nextStep();
|
|
226
|
+
},
|
|
227
|
+
[setResult, nextStep, onGenerationComplete, skipResultStep],
|
|
228
|
+
);
|
|
164
229
|
|
|
165
230
|
useWizardGeneration({
|
|
166
231
|
scenario: validatedScenario,
|
|
@@ -174,92 +239,74 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
174
239
|
});
|
|
175
240
|
|
|
176
241
|
useEffect(() => {
|
|
177
|
-
if (currentStep && onStepChange) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
prevStepIdRef.current = currentStepId;
|
|
181
|
-
onStepChange(currentStep.id, currentStep.type);
|
|
182
|
-
// Reset dismissed state when entering generating step
|
|
183
|
-
if (currentStep.type === StepType.GENERATING) {
|
|
184
|
-
setIsGeneratingDismissed(false);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
242
|
+
if (currentStep && onStepChange && prevStepIdRef.current !== currentStep.id) {
|
|
243
|
+
prevStepIdRef.current = currentStep.id;
|
|
244
|
+
onStepChange(currentStep.id, currentStep.type);
|
|
187
245
|
}
|
|
188
|
-
}, [currentStep,
|
|
246
|
+
}, [currentStep, onStepChange]);
|
|
189
247
|
|
|
190
|
-
// Handle dismiss generating - go back but generation continues in background
|
|
191
248
|
const handleDismissGenerating = useCallback(() => {
|
|
192
249
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
193
|
-
console.log("[
|
|
250
|
+
console.log("[WizardFlowContent] Dismissing - generation continues");
|
|
194
251
|
}
|
|
195
|
-
setIsGeneratingDismissed(true);
|
|
196
|
-
// Show alert that generation continues
|
|
197
252
|
alert.show(
|
|
198
253
|
AlertType.INFO,
|
|
199
254
|
AlertMode.TOAST,
|
|
200
255
|
t("generator.backgroundTitle"),
|
|
201
|
-
t("generator.backgroundMessage")
|
|
256
|
+
t("generator.backgroundMessage"),
|
|
202
257
|
);
|
|
203
|
-
// Go back to previous step (or close)
|
|
204
258
|
onBack?.();
|
|
205
259
|
}, [alert, t, onBack]);
|
|
206
260
|
|
|
207
261
|
const handleBack = useCallback(() => {
|
|
208
|
-
if (currentStepIndex === 0)
|
|
209
|
-
|
|
210
|
-
} else {
|
|
211
|
-
previousStep();
|
|
212
|
-
}
|
|
262
|
+
if (currentStepIndex === 0) onBack?.();
|
|
263
|
+
else previousStep();
|
|
213
264
|
}, [currentStepIndex, previousStep, onBack]);
|
|
214
265
|
|
|
215
|
-
// Wrapper for nextStep that checks onGenerationStart before transitioning to GENERATING
|
|
216
266
|
const handleNextStep = useCallback(() => {
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (nextStepDef?.type === StepType.GENERATING) {
|
|
222
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
223
|
-
console.log("[GenericWizardFlow] About to enter GENERATING step, calling onGenerationStart");
|
|
224
|
-
}
|
|
225
|
-
if (onGenerationStart) {
|
|
226
|
-
onGenerationStart(customData, () => {
|
|
227
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
228
|
-
console.log("[GenericWizardFlow] onGenerationStart callback invoked, proceeding to generation");
|
|
229
|
-
}
|
|
230
|
-
nextStep();
|
|
231
|
-
});
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
267
|
+
const nextStepDef = flowSteps[currentStepIndex + 1];
|
|
268
|
+
if (nextStepDef?.type === StepType.GENERATING && onGenerationStart) {
|
|
269
|
+
onGenerationStart(customData, nextStep);
|
|
270
|
+
return;
|
|
234
271
|
}
|
|
235
|
-
|
|
236
272
|
nextStep();
|
|
237
273
|
}, [currentStepIndex, flowSteps, customData, onGenerationStart, nextStep]);
|
|
238
274
|
|
|
239
|
-
const handlePhotoContinue = useCallback(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
setShowRatingPicker(true);
|
|
247
|
-
}, []);
|
|
248
|
-
|
|
249
|
-
const handleSubmitRating = useCallback(async (rating: number, description: string) => {
|
|
250
|
-
if (!currentCreation?.id || !userId) return;
|
|
251
|
-
const success = await repository.rate(userId, currentCreation.id, rating, description);
|
|
252
|
-
if (success) {
|
|
253
|
-
setHasRated(true);
|
|
254
|
-
alert.show(AlertType.SUCCESS, AlertMode.TOAST, t("result.rateSuccessTitle"), t("result.rateSuccessMessage"));
|
|
255
|
-
}
|
|
256
|
-
setShowRatingPicker(false);
|
|
257
|
-
}, [currentCreation, userId, repository, alert, t]);
|
|
275
|
+
const handlePhotoContinue = useCallback(
|
|
276
|
+
(stepId: string, image: UploadedImage) => {
|
|
277
|
+
setCustomData(stepId, image);
|
|
278
|
+
handleNextStep();
|
|
279
|
+
},
|
|
280
|
+
[setCustomData, handleNextStep],
|
|
281
|
+
);
|
|
258
282
|
|
|
259
|
-
const
|
|
283
|
+
const handleSubmitRating = useCallback(
|
|
284
|
+
async (rating: number, description: string) => {
|
|
285
|
+
if (!currentCreation?.id || !userId) return;
|
|
286
|
+
const success = await repository.rate(
|
|
287
|
+
userId,
|
|
288
|
+
currentCreation.id,
|
|
289
|
+
rating,
|
|
290
|
+
description,
|
|
291
|
+
);
|
|
292
|
+
if (success) {
|
|
293
|
+
setHasRated(true);
|
|
294
|
+
alert.show(
|
|
295
|
+
AlertType.SUCCESS,
|
|
296
|
+
AlertMode.TOAST,
|
|
297
|
+
t("result.rateSuccessTitle"),
|
|
298
|
+
t("result.rateSuccessMessage"),
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
setShowRatingPicker(false);
|
|
302
|
+
},
|
|
303
|
+
[currentCreation, userId, repository, alert, t],
|
|
304
|
+
);
|
|
260
305
|
|
|
261
306
|
return (
|
|
262
|
-
<View
|
|
307
|
+
<View
|
|
308
|
+
style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
|
|
309
|
+
>
|
|
263
310
|
<WizardStepRenderer
|
|
264
311
|
step={currentStep}
|
|
265
312
|
scenario={scenario}
|
|
@@ -268,13 +315,13 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
268
315
|
generationResult={generationResult}
|
|
269
316
|
isSaving={isSaving}
|
|
270
317
|
isSharing={isSharing}
|
|
271
|
-
showRating={
|
|
318
|
+
showRating={Boolean(userId) && !hasRated}
|
|
272
319
|
onNext={handleNextStep}
|
|
273
320
|
onBack={handleBack}
|
|
274
321
|
onPhotoContinue={handlePhotoContinue}
|
|
275
322
|
onDownload={handleDownload}
|
|
276
323
|
onShare={handleShare}
|
|
277
|
-
onRate={
|
|
324
|
+
onRate={() => setShowRatingPicker(true)}
|
|
278
325
|
onTryAgain={onTryAgain}
|
|
279
326
|
onDismissGenerating={handleDismissGenerating}
|
|
280
327
|
t={t}
|
|
@@ -296,7 +343,5 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
296
343
|
};
|
|
297
344
|
|
|
298
345
|
const styles = StyleSheet.create({
|
|
299
|
-
container: {
|
|
300
|
-
flex: 1,
|
|
301
|
-
},
|
|
346
|
+
container: { flex: 1 },
|
|
302
347
|
});
|
|
@@ -21,6 +21,16 @@ export {
|
|
|
21
21
|
} from "./infrastructure/scenario-helpers";
|
|
22
22
|
export type { AppScenarioConfig } from "./infrastructure/scenario-helpers";
|
|
23
23
|
|
|
24
|
+
// Scenario Registry - Singleton for app configuration
|
|
25
|
+
export {
|
|
26
|
+
configureScenarios,
|
|
27
|
+
getConfiguredScenario,
|
|
28
|
+
getDefaultOutputType,
|
|
29
|
+
isScenariosConfigured,
|
|
30
|
+
getAllConfiguredScenarios,
|
|
31
|
+
} from "./infrastructure/scenario-registry";
|
|
32
|
+
export type { ConfiguredScenario } from "./infrastructure/scenario-registry";
|
|
33
|
+
|
|
24
34
|
// Utils
|
|
25
35
|
export { createStoryTemplate } from "./infrastructure/utils/scenario-utils";
|
|
26
36
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario Registry
|
|
3
|
+
* Singleton registry for app-configured scenarios
|
|
4
|
+
* Apps configure once at startup, package uses internally
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Scenario, ScenarioOutputType } from "../domain/Scenario";
|
|
8
|
+
|
|
9
|
+
declare const __DEV__: boolean;
|
|
10
|
+
|
|
11
|
+
/** Configured scenario with required outputType */
|
|
12
|
+
export interface ConfiguredScenario extends Scenario {
|
|
13
|
+
readonly outputType: ScenarioOutputType;
|
|
14
|
+
readonly [key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ScenarioRegistryState {
|
|
18
|
+
scenarios: Map<string, ConfiguredScenario>;
|
|
19
|
+
defaultOutputType: ScenarioOutputType;
|
|
20
|
+
isConfigured: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const state: ScenarioRegistryState = {
|
|
24
|
+
scenarios: new Map(),
|
|
25
|
+
defaultOutputType: "video",
|
|
26
|
+
isConfigured: false,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configure scenarios for this app
|
|
31
|
+
* Call once at app startup
|
|
32
|
+
*/
|
|
33
|
+
export const configureScenarios = (
|
|
34
|
+
scenarios: readonly ConfiguredScenario[],
|
|
35
|
+
defaultOutputType: ScenarioOutputType = "video",
|
|
36
|
+
): void => {
|
|
37
|
+
state.scenarios.clear();
|
|
38
|
+
|
|
39
|
+
for (const scenario of scenarios) {
|
|
40
|
+
state.scenarios.set(scenario.id, scenario);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
state.defaultOutputType = defaultOutputType;
|
|
44
|
+
state.isConfigured = true;
|
|
45
|
+
|
|
46
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
47
|
+
console.log("[ScenarioRegistry] Configured:", {
|
|
48
|
+
count: scenarios.length,
|
|
49
|
+
defaultOutputType,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get scenario by ID from configured scenarios
|
|
56
|
+
*/
|
|
57
|
+
export const getConfiguredScenario = (
|
|
58
|
+
id: string,
|
|
59
|
+
): ConfiguredScenario | undefined => {
|
|
60
|
+
const found = state.scenarios.get(id);
|
|
61
|
+
|
|
62
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
63
|
+
console.log("[ScenarioRegistry] getConfiguredScenario:", {
|
|
64
|
+
id,
|
|
65
|
+
found: !!found,
|
|
66
|
+
outputType: found?.outputType,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return found;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get default output type for this app
|
|
75
|
+
*/
|
|
76
|
+
export const getDefaultOutputType = (): ScenarioOutputType => {
|
|
77
|
+
return state.defaultOutputType;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if scenarios are configured
|
|
82
|
+
*/
|
|
83
|
+
export const isScenariosConfigured = (): boolean => {
|
|
84
|
+
return state.isConfigured;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get all configured scenarios
|
|
89
|
+
*/
|
|
90
|
+
export const getAllConfiguredScenarios = (): readonly ConfiguredScenario[] => {
|
|
91
|
+
return Array.from(state.scenarios.values());
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Reset registry (for testing)
|
|
96
|
+
*/
|
|
97
|
+
export const resetScenarioRegistry = (): void => {
|
|
98
|
+
state.scenarios.clear();
|
|
99
|
+
state.defaultOutputType = "video";
|
|
100
|
+
state.isConfigured = false;
|
|
101
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -1,141 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generation Config Provider
|
|
3
|
-
* Provides app-specific
|
|
4
|
-
*
|
|
3
|
+
* Provides app-specific AI models configuration
|
|
4
|
+
* For scenarios, use configureScenarios() from scenario-registry
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { createContext, useContext,
|
|
8
|
-
import type { Scenario, ScenarioOutputType } from "../../domains/scenarios/domain/Scenario";
|
|
7
|
+
import React, { createContext, useContext, type ReactNode } from "react";
|
|
9
8
|
|
|
10
9
|
declare const __DEV__: boolean;
|
|
11
10
|
|
|
12
|
-
// ============================================================================
|
|
13
|
-
// Types
|
|
14
|
-
// ============================================================================
|
|
15
|
-
|
|
16
11
|
export interface GenerationModels {
|
|
17
|
-
/** Image generation with face identity preservation (couple photos) */
|
|
18
12
|
readonly imageCoupleMultiRef?: string;
|
|
19
|
-
/** Text-to-image generation */
|
|
20
13
|
readonly imageTextToImage?: string;
|
|
21
|
-
/** Image-to-video generation */
|
|
22
14
|
readonly imageToVideo?: string;
|
|
23
|
-
/** Text-to-video generation */
|
|
24
15
|
readonly textToVideo?: string;
|
|
25
|
-
/** AI Kiss video */
|
|
26
16
|
readonly aiKiss?: string;
|
|
27
|
-
/** AI Hug video */
|
|
28
17
|
readonly aiHug?: string;
|
|
29
|
-
/** Face swap */
|
|
30
18
|
readonly faceSwap?: string;
|
|
31
|
-
/** Meme generation (caption) */
|
|
32
19
|
readonly memeCaption?: string;
|
|
33
|
-
/** Meme generation (image) */
|
|
34
20
|
readonly memeImage?: string;
|
|
35
|
-
/** Text to voice */
|
|
36
21
|
readonly textToVoice?: string;
|
|
37
22
|
}
|
|
38
23
|
|
|
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
|
-
|
|
45
24
|
export interface GenerationConfigValue {
|
|
46
|
-
/** AI models configuration from app */
|
|
47
25
|
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;
|
|
52
|
-
/** Get model for specific feature type */
|
|
53
26
|
readonly getModel: (featureType: keyof GenerationModels) => string;
|
|
54
|
-
/** Get scenario by ID from configured scenarios */
|
|
55
|
-
readonly getScenarioById: (id: string) => ConfiguredScenario | undefined;
|
|
56
27
|
}
|
|
57
28
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const GenerationConfigContext = createContext<GenerationConfigValue | null>(null);
|
|
63
|
-
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// Provider
|
|
66
|
-
// ============================================================================
|
|
29
|
+
const GenerationConfigContext = createContext<GenerationConfigValue | null>(
|
|
30
|
+
null,
|
|
31
|
+
);
|
|
67
32
|
|
|
68
33
|
export interface GenerationConfigProviderProps {
|
|
69
34
|
readonly children: ReactNode;
|
|
70
|
-
/** AI models configuration */
|
|
71
35
|
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;
|
|
76
36
|
}
|
|
77
37
|
|
|
78
|
-
export const GenerationConfigProvider: React.FC<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
scenarios = [],
|
|
82
|
-
defaultOutputType = "video",
|
|
83
|
-
}) => {
|
|
38
|
+
export const GenerationConfigProvider: React.FC<
|
|
39
|
+
GenerationConfigProviderProps
|
|
40
|
+
> = ({ children, models }) => {
|
|
84
41
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
85
42
|
const configuredModels = Object.entries(models)
|
|
86
43
|
.filter(([, value]) => !!value)
|
|
87
44
|
.map(([key]) => key);
|
|
88
|
-
|
|
89
|
-
console.log("[GenerationConfigProvider] Initialized:", {
|
|
90
|
-
models: configuredModels,
|
|
91
|
-
scenariosCount: scenarios.length,
|
|
92
|
-
defaultOutputType,
|
|
93
|
-
});
|
|
45
|
+
console.log("[GenerationConfigProvider] Models:", configuredModels);
|
|
94
46
|
}
|
|
95
47
|
|
|
96
48
|
const getModel = (featureType: keyof GenerationModels): string => {
|
|
97
49
|
const model = models[featureType];
|
|
98
50
|
if (!model) {
|
|
99
|
-
const
|
|
100
|
-
(key) => models[key as keyof GenerationModels]
|
|
51
|
+
const available = Object.keys(models).filter(
|
|
52
|
+
(key) => models[key as keyof GenerationModels],
|
|
101
53
|
);
|
|
102
|
-
|
|
103
54
|
throw new Error(
|
|
104
|
-
`Model not configured
|
|
105
|
-
`This app only supports: ${availableModels.join(", ") || "none"}.\n` +
|
|
106
|
-
`Please configure '${featureType}' in your GenerationConfigProvider if you need it.`
|
|
55
|
+
`Model not configured: ${featureType}. Available: ${available.join(", ") || "none"}`,
|
|
107
56
|
);
|
|
108
57
|
}
|
|
109
58
|
return model;
|
|
110
59
|
};
|
|
111
60
|
|
|
112
|
-
const
|
|
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
|
-
|
|
132
|
-
const value: GenerationConfigValue = {
|
|
133
|
-
models,
|
|
134
|
-
scenarios,
|
|
135
|
-
defaultOutputType,
|
|
136
|
-
getModel,
|
|
137
|
-
getScenarioById,
|
|
138
|
-
};
|
|
61
|
+
const value: GenerationConfigValue = { models, getModel };
|
|
139
62
|
|
|
140
63
|
return (
|
|
141
64
|
<GenerationConfigContext.Provider value={value}>
|
|
@@ -144,19 +67,12 @@ export const GenerationConfigProvider: React.FC<GenerationConfigProviderProps> =
|
|
|
144
67
|
);
|
|
145
68
|
};
|
|
146
69
|
|
|
147
|
-
// ============================================================================
|
|
148
|
-
// Hook
|
|
149
|
-
// ============================================================================
|
|
150
|
-
|
|
151
70
|
export const useGenerationConfig = (): GenerationConfigValue => {
|
|
152
71
|
const context = useContext(GenerationConfigContext);
|
|
153
|
-
|
|
154
72
|
if (!context) {
|
|
155
73
|
throw new Error(
|
|
156
|
-
"useGenerationConfig must be used within GenerationConfigProvider
|
|
157
|
-
"Wrap your app with <GenerationConfigProvider models={{...}}>"
|
|
74
|
+
"useGenerationConfig must be used within GenerationConfigProvider",
|
|
158
75
|
);
|
|
159
76
|
}
|
|
160
|
-
|
|
161
77
|
return context;
|
|
162
78
|
};
|