@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.35.0",
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 provider)
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 { useAppDesignTokens, useAlert, AlertType, AlertMode } from "@umituz/react-native-design-system";
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 { StepType, type StepDefinition } from "../../../../../domain/entities/flow-config.types";
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 { useWizardGeneration, type WizardScenarioData } from "../hooks/useWizardGeneration";
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 { useGenerationConfig } from "../../../../../infrastructure/providers";
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?: (data: Record<string, unknown>, proceedToGenerating: () => void) => void;
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
- featureConfig,
52
- scenario: scenarioProp,
53
- scenarioId,
54
- userId,
55
- alertMessages,
56
- skipResultStep = false,
57
- onStepChange,
58
- onGenerationStart,
59
- onGenerationComplete,
60
- onGenerationError,
61
- onCreditsExhausted,
62
- onBack,
63
- onTryAgain,
64
- t,
65
- translations: _translations,
66
- renderPreview,
67
- renderGenerating,
68
- renderResult,
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: use prop directly OR lookup by scenarioId from provider
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 = getScenarioById(scenarioId);
102
+ const found = getConfiguredScenario(scenarioId);
95
103
  if (found) {
96
104
  if (typeof __DEV__ !== "undefined" && __DEV__) {
97
- console.log("[GenericWizardFlow] Resolved scenario from provider:", {
105
+ console.log("[GenericWizardFlow] Resolved from registry:", {
98
106
  id: found.id,
99
107
  outputType: found.outputType,
100
108
  });
101
109
  }
102
- return found as unknown as WizardScenarioData;
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 found in provider:", scenarioId);
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, getScenarioById, defaultOutputType]);
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
- return buildFlowStepsFromWizard(featureConfig, {
121
- includePreview: true,
122
- includeGenerating: true,
123
- includeResult: !skipResultStep,
124
- });
125
- }, [featureConfig, skipResultStep]);
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 = currentCreation?.output?.imageUrl || currentCreation?.uri || "";
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
- videoUrl: resultVideoUrl,
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
- // Only go to result step if not skipping
160
- if (!skipResultStep) {
161
- nextStep();
162
- }
163
- }, [setResult, nextStep, onGenerationComplete, skipResultStep]);
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
- const currentStepId = currentStep.id;
179
- if (prevStepIdRef.current !== currentStepId) {
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, currentStepIndex, onStepChange]);
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("[GenericWizardFlow] Dismissing generating screen - generation continues in background");
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
- onBack?.();
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 nextIndex = currentStepIndex + 1;
218
- const nextStepDef = flowSteps[nextIndex];
219
-
220
- // If next step is GENERATING, we must call onGenerationStart for feature gating
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((stepId: string, image: UploadedImage) => {
240
- setCustomData(stepId, image);
241
- // Use handleNextStep which handles onGenerationStart check
242
- handleNextStep();
243
- }, [setCustomData, handleNextStep]);
244
-
245
- const handleOpenRatingPicker = useCallback(() => {
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 showRatingButton = Boolean(userId) && !hasRated;
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 style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
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={showRatingButton}
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={handleOpenRatingPicker}
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
@@ -143,7 +143,6 @@ export {
143
143
  type GenerationModels,
144
144
  type GenerationConfigValue,
145
145
  type GenerationConfigProviderProps,
146
- type ConfiguredScenario,
147
146
  } from "./infrastructure/providers";
148
147
 
149
148
  export * from "./domains/result-preview";
@@ -1,141 +1,64 @@
1
1
  /**
2
2
  * Generation Config Provider
3
- * Provides app-specific configuration to the package
4
- * NO hard-coded models/scenarios, everything comes from app!
3
+ * Provides app-specific AI models configuration
4
+ * For scenarios, use configureScenarios() from scenario-registry
5
5
  */
6
6
 
7
- import React, { createContext, useContext, useMemo, type ReactNode } from "react";
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
- // Context
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<GenerationConfigProviderProps> = ({
79
- children,
80
- models,
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 availableModels = Object.keys(models).filter(
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 for feature: ${featureType}.\n\n` +
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 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
-
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
  };
@@ -9,5 +9,4 @@ export {
9
9
  type GenerationModels,
10
10
  type GenerationConfigValue,
11
11
  type GenerationConfigProviderProps,
12
- type ConfiguredScenario,
13
12
  } from "./generation-config.provider";