@umituz/react-native-ai-generation-content 1.18.2 → 1.19.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.18.2",
3
+ "version": "1.19.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",
@@ -16,6 +16,13 @@ export type {
16
16
  export { COUPLE_FUTURE_DEFAULTS } from "./domain/types";
17
17
  export { useCoupleFutureGeneration } from "./presentation/hooks/useCoupleFutureGeneration";
18
18
  export type { CoupleFutureConfig as UseCoupleFutureGenerationConfig } from "./presentation/hooks/useCoupleFutureGeneration";
19
+ export { useCoupleFutureFlow } from "./presentation/hooks/useCoupleFutureFlow";
20
+ export type {
21
+ CoupleFutureFlowConfig,
22
+ CoupleFutureFlowState,
23
+ CoupleFutureFlowActions,
24
+ CoupleFutureFlowProps,
25
+ } from "./presentation/hooks/useCoupleFutureFlow";
19
26
  export {
20
27
  RomanticMoodSelector,
21
28
  ArtStyleSelector,
@@ -0,0 +1,219 @@
1
+ /**
2
+ * useCoupleFutureFlow Hook
3
+ * Handles couple future wizard flow logic
4
+ */
5
+
6
+ import { useCallback, useEffect, useRef } from "react";
7
+ import { InteractionManager } from "react-native";
8
+ import { useCoupleFutureGeneration } from "./useCoupleFutureGeneration";
9
+ import { buildGenerationInputFromConfig } from "../../infrastructure/generationUtils";
10
+ import type { UploadedImage } from "../../../partner-upload/domain/types";
11
+
12
+ export interface CoupleFutureFlowConfig<TStep, TScenarioId> {
13
+ steps: {
14
+ SCENARIO: TStep;
15
+ SCENARIO_PREVIEW: TStep;
16
+ COUPLE_FEATURE_SELECTOR: TStep;
17
+ TEXT_INPUT: TStep;
18
+ PARTNER_A: TStep;
19
+ PARTNER_B: TStep;
20
+ GENERATING: TStep;
21
+ };
22
+ customScenarioId: TScenarioId;
23
+ }
24
+
25
+ export interface CoupleFutureFlowState<TStep, TScenarioId> {
26
+ step: TStep;
27
+ selectedScenarioId: TScenarioId | null;
28
+ selectedFeature: string | null;
29
+ partnerA: unknown;
30
+ partnerB: unknown;
31
+ partnerAName: string;
32
+ partnerBName: string;
33
+ customPrompt: string | null;
34
+ visualStyle: string | null;
35
+ selection: unknown;
36
+ isProcessing: boolean;
37
+ scenarioConfig: unknown;
38
+ selectedScenarioData: { requiresPhoto?: boolean } | null;
39
+ }
40
+
41
+ export interface CoupleFutureFlowActions<TStep, TScenarioId, TResult> {
42
+ setStep: (step: TStep) => void;
43
+ selectScenario: (id: TScenarioId) => void;
44
+ setPartnerA: (image: unknown) => void;
45
+ setPartnerAName: (name: string) => void;
46
+ setPartnerB: (image: unknown) => void;
47
+ setPartnerBName: (name: string) => void;
48
+ setCustomPrompt: (prompt: string) => void;
49
+ setVisualStyle: (style: string) => void;
50
+ startGeneration: () => void;
51
+ generationSuccess: (result: TResult) => void;
52
+ generationError: (error: string) => void;
53
+ requireFeature: (callback: () => void) => void;
54
+ onNavigateToHistory: () => void;
55
+ }
56
+
57
+ export interface CoupleFutureFlowProps<TStep, TScenarioId, TResult> {
58
+ userId?: string;
59
+ config: CoupleFutureFlowConfig<TStep, TScenarioId>;
60
+ state: CoupleFutureFlowState<TStep, TScenarioId>;
61
+ actions: CoupleFutureFlowActions<TStep, TScenarioId, TResult>;
62
+ generationConfig: {
63
+ visualStyleModifiers: Record<string, string>;
64
+ defaultPartnerAName: string;
65
+ defaultPartnerBName: string;
66
+ };
67
+ alertMessages: {
68
+ networkError: string;
69
+ policyViolation: string;
70
+ saveFailed: string;
71
+ creditFailed: string;
72
+ unknown: string;
73
+ };
74
+ processResult: (imageUrl: string, input: unknown) => TResult;
75
+ buildCreation: (result: TResult, input: unknown) => unknown;
76
+ onCreditsExhausted: () => void;
77
+ }
78
+
79
+ export const useCoupleFutureFlow = <TStep, TScenarioId, TResult>(
80
+ props: CoupleFutureFlowProps<TStep, TScenarioId, TResult>,
81
+ ) => {
82
+ const { config, state, actions, generationConfig, alertMessages } = props;
83
+ const { processResult, buildCreation, onCreditsExhausted, userId } = props;
84
+ const hasStarted = useRef(false);
85
+
86
+ const { generate, isGenerating, progress } =
87
+ useCoupleFutureGeneration<TResult>({
88
+ userId,
89
+ onCreditsExhausted,
90
+ onSuccess: (result) => {
91
+ actions.generationSuccess(result);
92
+ InteractionManager.runAfterInteractions(() => {
93
+ setTimeout(() => actions.onNavigateToHistory(), 300);
94
+ });
95
+ },
96
+ onError: actions.generationError,
97
+ processResult: processResult as never,
98
+ buildCreation: buildCreation as never,
99
+ alertMessages,
100
+ });
101
+
102
+ useEffect(() => {
103
+ if (state.step !== config.steps.GENERATING) {
104
+ hasStarted.current = false;
105
+ return;
106
+ }
107
+ if (!state.isProcessing || hasStarted.current) return;
108
+ hasStarted.current = true;
109
+
110
+ const input = buildGenerationInputFromConfig({
111
+ partnerA: state.partnerA as never,
112
+ partnerB: state.partnerB as never,
113
+ partnerAName: state.partnerAName,
114
+ partnerBName: state.partnerBName,
115
+ scenario: state.scenarioConfig as never,
116
+ customPrompt: state.customPrompt || undefined,
117
+ visualStyle: state.visualStyle || "",
118
+ defaultPartnerAName: generationConfig.defaultPartnerAName,
119
+ defaultPartnerBName: generationConfig.defaultPartnerBName,
120
+ coupleFeatureSelection: state.selection as never,
121
+ visualStyles: generationConfig.visualStyleModifiers,
122
+ customScenarioId: config.customScenarioId as string,
123
+ });
124
+ if (input) generate(input);
125
+ }, [state, config, generationConfig, generate]);
126
+
127
+ const handleScenarioSelect = useCallback(
128
+ (id: string) => {
129
+ actions.selectScenario(id as TScenarioId);
130
+ actions.setStep(config.steps.SCENARIO_PREVIEW);
131
+ },
132
+ [actions, config.steps.SCENARIO_PREVIEW],
133
+ );
134
+
135
+ const handleScenarioPreviewBack = useCallback(
136
+ () => actions.setStep(config.steps.SCENARIO),
137
+ [actions, config.steps.SCENARIO],
138
+ );
139
+
140
+ const handleScenarioPreviewContinue = useCallback(() => {
141
+ if (state.selectedFeature) {
142
+ actions.setStep(config.steps.COUPLE_FEATURE_SELECTOR);
143
+ } else if (
144
+ state.selectedScenarioId === config.customScenarioId ||
145
+ state.selectedScenarioData?.requiresPhoto === false
146
+ ) {
147
+ actions.setStep(config.steps.TEXT_INPUT);
148
+ } else {
149
+ actions.setStep(config.steps.PARTNER_A);
150
+ }
151
+ }, [actions, config, state]);
152
+
153
+ const handlePartnerAContinue = useCallback(
154
+ (image: UploadedImage, name: string) => {
155
+ actions.setPartnerA(image);
156
+ actions.setPartnerAName(name);
157
+ actions.setStep(config.steps.PARTNER_B);
158
+ },
159
+ [actions, config.steps.PARTNER_B],
160
+ );
161
+
162
+ const handlePartnerABack = useCallback(() => {
163
+ actions.setStep(
164
+ state.selectedScenarioId === config.customScenarioId
165
+ ? config.steps.TEXT_INPUT
166
+ : config.steps.SCENARIO_PREVIEW,
167
+ );
168
+ }, [actions, config, state.selectedScenarioId]);
169
+
170
+ const handlePartnerBContinue = useCallback(
171
+ (image: UploadedImage, name: string) => {
172
+ actions.requireFeature(() => {
173
+ actions.setPartnerB(image);
174
+ actions.setPartnerBName(name);
175
+ actions.startGeneration();
176
+ });
177
+ },
178
+ [actions],
179
+ );
180
+
181
+ const handlePartnerBBack = useCallback(
182
+ () => actions.setStep(config.steps.PARTNER_A),
183
+ [actions, config.steps.PARTNER_A],
184
+ );
185
+
186
+ const handleMagicPromptContinue = useCallback(
187
+ (prompt: string, style: string) => {
188
+ actions.setCustomPrompt(prompt);
189
+ actions.setVisualStyle(style);
190
+ if (state.selectedScenarioId === config.customScenarioId) {
191
+ actions.setStep(config.steps.PARTNER_A);
192
+ } else {
193
+ actions.startGeneration();
194
+ }
195
+ },
196
+ [actions, config, state.selectedScenarioId],
197
+ );
198
+
199
+ const handleMagicPromptBack = useCallback(
200
+ () => actions.setStep(config.steps.SCENARIO_PREVIEW),
201
+ [actions, config.steps.SCENARIO_PREVIEW],
202
+ );
203
+
204
+ return {
205
+ isGenerating,
206
+ progress,
207
+ handlers: {
208
+ handleScenarioSelect,
209
+ handleScenarioPreviewBack,
210
+ handleScenarioPreviewContinue,
211
+ handlePartnerAContinue,
212
+ handlePartnerABack,
213
+ handlePartnerBContinue,
214
+ handlePartnerBBack,
215
+ handleMagicPromptContinue,
216
+ handleMagicPromptBack,
217
+ },
218
+ };
219
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * AIGenerationWizard Types
3
+ * Generic wizard for all AI generation flows
4
+ */
5
+
6
+ import type { ReactNode } from "react";
7
+ import type { UploadedImage } from "../../partner-upload/domain/types";
8
+
9
+ // Step types
10
+ export type WizardStepId = string;
11
+
12
+ export interface WizardStep {
13
+ id: WizardStepId;
14
+ component: React.ComponentType<WizardStepProps>;
15
+ }
16
+
17
+ export interface WizardStepProps {
18
+ onNext: () => void;
19
+ onBack: () => void;
20
+ onComplete: () => void;
21
+ isProcessing: boolean;
22
+ progress: number;
23
+ error: string | null;
24
+ }
25
+
26
+ // Wizard state
27
+ export interface WizardState {
28
+ currentStepIndex: number;
29
+ isProcessing: boolean;
30
+ progress: number;
31
+ error: string | null;
32
+ result: unknown | null;
33
+ images: Record<string, UploadedImage | null>;
34
+ names: Record<string, string>;
35
+ customData: Record<string, unknown>;
36
+ }
37
+
38
+ // Wizard config
39
+ export interface AIGenerationWizardConfig {
40
+ steps: WizardStep[];
41
+ initialStep?: number;
42
+ creditCost?: number;
43
+ }
44
+
45
+ // Wizard callbacks
46
+ export interface AIGenerationWizardCallbacks {
47
+ onAuthRequired?: (callback: () => void) => void;
48
+ onCreditsExhausted?: () => void;
49
+ onGenerationStart?: () => void;
50
+ onGenerationSuccess?: (result: unknown) => void;
51
+ onGenerationError?: (error: string) => void;
52
+ onStepChange?: (stepIndex: number, stepId: WizardStepId) => void;
53
+ }
54
+
55
+ // Main props
56
+ export interface AIGenerationWizardProps {
57
+ userId?: string;
58
+ isAuthenticated?: boolean;
59
+ hasCredits?: boolean;
60
+ config: AIGenerationWizardConfig;
61
+ callbacks?: AIGenerationWizardCallbacks;
62
+ renderStep?: (step: WizardStep, props: WizardStepProps) => ReactNode;
63
+ children?: ReactNode;
64
+ }
65
+
66
+ // Store actions
67
+ export interface WizardActions {
68
+ nextStep: () => void;
69
+ prevStep: () => void;
70
+ goToStep: (index: number) => void;
71
+ setImage: (key: string, image: UploadedImage | null) => void;
72
+ setName: (key: string, name: string) => void;
73
+ setCustomData: (key: string, value: unknown) => void;
74
+ setProcessing: (isProcessing: boolean) => void;
75
+ setProgress: (progress: number) => void;
76
+ setError: (error: string | null) => void;
77
+ setResult: (result: unknown) => void;
78
+ reset: () => void;
79
+ }
80
+
81
+ export type WizardStore = WizardState & WizardActions;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * AIGenerationWizard Feature
3
+ * Generic wizard for all AI generation flows
4
+ */
5
+
6
+ // Component
7
+ export { AIGenerationWizard, default as AIGenerationWizardDefault } from "./presentation/components/AIGenerationWizard";
8
+
9
+ // Hooks
10
+ export { useWizard, resetWizardStore } from "./presentation/hooks/useWizard";
11
+
12
+ // Store
13
+ export { createWizardStore, type WizardStoreType } from "./presentation/store/useWizardStore";
14
+
15
+ // Types (UploadedImage is already exported from partner-upload)
16
+ export type {
17
+ WizardStepId,
18
+ WizardStep,
19
+ WizardStepProps,
20
+ WizardState,
21
+ WizardActions,
22
+ WizardStore,
23
+ AIGenerationWizardConfig,
24
+ AIGenerationWizardCallbacks,
25
+ AIGenerationWizardProps,
26
+ } from "./domain/types";
@@ -0,0 +1,136 @@
1
+ /**
2
+ * AIGenerationWizard
3
+ * Generic wizard component for all AI generation flows
4
+ */
5
+
6
+ import React, { useMemo, useCallback, useEffect, useRef } from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+ import type {
9
+ AIGenerationWizardProps,
10
+ WizardStepProps,
11
+ WizardStep,
12
+ } from "../../domain/types";
13
+ import { createWizardStore, type WizardStoreType } from "../store/useWizardStore";
14
+
15
+ export const AIGenerationWizard: React.FC<AIGenerationWizardProps> = ({
16
+ userId,
17
+ isAuthenticated = false,
18
+ hasCredits = true,
19
+ config,
20
+ callbacks,
21
+ renderStep,
22
+ children,
23
+ }) => {
24
+ const { steps, initialStep = 0 } = config;
25
+ const storeRef = useRef<WizardStoreType | null>(null);
26
+
27
+ // Create store once
28
+ if (!storeRef.current) {
29
+ storeRef.current = createWizardStore({ totalSteps: steps.length });
30
+ }
31
+
32
+ const store = storeRef.current;
33
+ const state = store();
34
+
35
+ // Initialize to initialStep if provided
36
+ useEffect(() => {
37
+ if (initialStep > 0 && state.currentStepIndex === 0) {
38
+ state.goToStep(initialStep);
39
+ }
40
+ }, [initialStep, state]);
41
+
42
+ // Auth check before proceeding
43
+ const handleAuthCheck = useCallback(
44
+ (callback: () => void) => {
45
+ if (!isAuthenticated && callbacks?.onAuthRequired) {
46
+ callbacks.onAuthRequired(callback);
47
+ return false;
48
+ }
49
+ return true;
50
+ },
51
+ [isAuthenticated, callbacks],
52
+ );
53
+
54
+ // Credit check before generation
55
+ const handleCreditCheck = useCallback(() => {
56
+ if (!hasCredits && callbacks?.onCreditsExhausted) {
57
+ callbacks.onCreditsExhausted();
58
+ return false;
59
+ }
60
+ return true;
61
+ }, [hasCredits, callbacks]);
62
+
63
+ // Step navigation handlers
64
+ const handleNext = useCallback(() => {
65
+ const canProceed = handleAuthCheck(() => {
66
+ state.nextStep();
67
+ });
68
+ if (canProceed) {
69
+ state.nextStep();
70
+ callbacks?.onStepChange?.(
71
+ state.currentStepIndex + 1,
72
+ steps[state.currentStepIndex + 1]?.id ?? "",
73
+ );
74
+ }
75
+ }, [handleAuthCheck, state, callbacks, steps]);
76
+
77
+ const handleBack = useCallback(() => {
78
+ state.prevStep();
79
+ callbacks?.onStepChange?.(
80
+ state.currentStepIndex - 1,
81
+ steps[state.currentStepIndex - 1]?.id ?? "",
82
+ );
83
+ }, [state, callbacks, steps]);
84
+
85
+ const handleComplete = useCallback(() => {
86
+ if (!handleCreditCheck()) {
87
+ return;
88
+ }
89
+
90
+ state.setProcessing(true);
91
+ callbacks?.onGenerationStart?.();
92
+ }, [handleCreditCheck, state, callbacks]);
93
+
94
+ // Build step props
95
+ const stepProps: WizardStepProps = useMemo(
96
+ () => ({
97
+ onNext: handleNext,
98
+ onBack: handleBack,
99
+ onComplete: handleComplete,
100
+ isProcessing: state.isProcessing,
101
+ progress: state.progress,
102
+ error: state.error,
103
+ }),
104
+ [handleNext, handleBack, handleComplete, state.isProcessing, state.progress, state.error],
105
+ );
106
+
107
+ // Current step
108
+ const currentStep = steps[state.currentStepIndex];
109
+
110
+ // Render current step
111
+ const renderCurrentStep = useCallback(() => {
112
+ if (!currentStep) return null;
113
+
114
+ if (renderStep) {
115
+ return renderStep(currentStep, stepProps);
116
+ }
117
+
118
+ const StepComponent = currentStep.component;
119
+ return <StepComponent {...stepProps} />;
120
+ }, [currentStep, stepProps, renderStep]);
121
+
122
+ return (
123
+ <View style={styles.container}>
124
+ {renderCurrentStep()}
125
+ {children}
126
+ </View>
127
+ );
128
+ };
129
+
130
+ const styles = StyleSheet.create({
131
+ container: {
132
+ flex: 1,
133
+ },
134
+ });
135
+
136
+ export default AIGenerationWizard;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * useWizard Hook
3
+ * Access wizard store state and actions
4
+ */
5
+
6
+ import { useRef, useCallback } from "react";
7
+ import { createWizardStore, type WizardStoreType } from "../store/useWizardStore";
8
+ import type { WizardState, WizardActions } from "../../domain/types";
9
+ import type { UploadedImage } from "../../../partner-upload/domain/types";
10
+
11
+ interface UseWizardConfig {
12
+ totalSteps: number;
13
+ }
14
+
15
+ interface UseWizardReturn extends WizardState, WizardActions {
16
+ /** Check if user can go to next step */
17
+ canGoNext: boolean;
18
+ /** Check if user can go to previous step */
19
+ canGoBack: boolean;
20
+ /** Get image by key */
21
+ getImage: (key: string) => UploadedImage | null;
22
+ /** Get name by key */
23
+ getName: (key: string) => string;
24
+ /** Get custom data by key */
25
+ getData: <T>(key: string) => T | undefined;
26
+ /** Check if all required images are uploaded */
27
+ hasImages: (keys: string[]) => boolean;
28
+ /** Check if all required names are filled */
29
+ hasNames: (keys: string[]) => boolean;
30
+ }
31
+
32
+ // Store singleton per wizard instance
33
+ let wizardStoreInstance: WizardStoreType | null = null;
34
+
35
+ export const useWizard = (config: UseWizardConfig): UseWizardReturn => {
36
+ const storeRef = useRef<WizardStoreType | null>(null);
37
+
38
+ // Create or reuse store
39
+ if (!storeRef.current) {
40
+ if (!wizardStoreInstance) {
41
+ wizardStoreInstance = createWizardStore(config);
42
+ }
43
+ storeRef.current = wizardStoreInstance;
44
+ }
45
+
46
+ const store = storeRef.current;
47
+ const state = store();
48
+
49
+ // Computed helpers
50
+ const canGoNext = state.currentStepIndex < config.totalSteps - 1;
51
+ const canGoBack = state.currentStepIndex > 0;
52
+
53
+ const getImage = useCallback(
54
+ (key: string) => state.images[key] ?? null,
55
+ [state.images],
56
+ );
57
+
58
+ const getName = useCallback(
59
+ (key: string) => state.names[key] ?? "",
60
+ [state.names],
61
+ );
62
+
63
+ const getData = useCallback(
64
+ <T>(key: string) => state.customData[key] as T | undefined,
65
+ [state.customData],
66
+ );
67
+
68
+ const hasImages = useCallback(
69
+ (keys: string[]) => keys.every((key) => state.images[key] != null),
70
+ [state.images],
71
+ );
72
+
73
+ const hasNames = useCallback(
74
+ (keys: string[]) =>
75
+ keys.every((key) => state.names[key] && state.names[key].trim().length > 0),
76
+ [state.names],
77
+ );
78
+
79
+ return {
80
+ // State
81
+ currentStepIndex: state.currentStepIndex,
82
+ isProcessing: state.isProcessing,
83
+ progress: state.progress,
84
+ error: state.error,
85
+ result: state.result,
86
+ images: state.images,
87
+ names: state.names,
88
+ customData: state.customData,
89
+
90
+ // Actions
91
+ nextStep: state.nextStep,
92
+ prevStep: state.prevStep,
93
+ goToStep: state.goToStep,
94
+ setImage: state.setImage,
95
+ setName: state.setName,
96
+ setCustomData: state.setCustomData,
97
+ setProcessing: state.setProcessing,
98
+ setProgress: state.setProgress,
99
+ setError: state.setError,
100
+ setResult: state.setResult,
101
+ reset: state.reset,
102
+
103
+ // Helpers
104
+ canGoNext,
105
+ canGoBack,
106
+ getImage,
107
+ getName,
108
+ getData,
109
+ hasImages,
110
+ hasNames,
111
+ };
112
+ };
113
+
114
+ /** Reset the wizard store singleton */
115
+ export const resetWizardStore = () => {
116
+ if (wizardStoreInstance) {
117
+ wizardStoreInstance.getState().reset();
118
+ }
119
+ wizardStoreInstance = null;
120
+ };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * useWizardStore
3
+ * Built-in store for AIGenerationWizard using design-system createStore
4
+ */
5
+
6
+ import { createStore } from "@umituz/react-native-design-system";
7
+ import type { WizardState, WizardActions } from "../../domain/types";
8
+ import type { UploadedImage } from "../../../partner-upload/domain/types";
9
+
10
+ const INITIAL_STATE: WizardState = {
11
+ currentStepIndex: 0,
12
+ isProcessing: false,
13
+ progress: 0,
14
+ error: null,
15
+ result: null,
16
+ images: {},
17
+ names: {},
18
+ customData: {},
19
+ };
20
+
21
+ interface WizardStoreConfig {
22
+ totalSteps: number;
23
+ }
24
+
25
+ export const createWizardStore = (config: WizardStoreConfig) =>
26
+ createStore<WizardState, WizardActions>({
27
+ name: "wizard_store",
28
+ initialState: INITIAL_STATE,
29
+ persist: false,
30
+ actions: (set, get) => ({
31
+ nextStep: () => {
32
+ const { currentStepIndex } = get();
33
+ if (currentStepIndex < config.totalSteps - 1) {
34
+ set({ currentStepIndex: currentStepIndex + 1, error: null });
35
+ }
36
+ },
37
+
38
+ prevStep: () => {
39
+ const { currentStepIndex } = get();
40
+ if (currentStepIndex > 0) {
41
+ set({ currentStepIndex: currentStepIndex - 1, error: null });
42
+ }
43
+ },
44
+
45
+ goToStep: (index: number) => {
46
+ if (index >= 0 && index < config.totalSteps) {
47
+ set({ currentStepIndex: index, error: null });
48
+ }
49
+ },
50
+
51
+ setImage: (key: string, image: UploadedImage | null) => {
52
+ const { images } = get();
53
+ set({ images: { ...images, [key]: image } });
54
+ },
55
+
56
+ setName: (key: string, name: string) => {
57
+ const { names } = get();
58
+ set({ names: { ...names, [key]: name } });
59
+ },
60
+
61
+ setCustomData: (key: string, value: unknown) => {
62
+ const { customData } = get();
63
+ set({ customData: { ...customData, [key]: value } });
64
+ },
65
+
66
+ setProcessing: (isProcessing: boolean) => set({ isProcessing }),
67
+
68
+ setProgress: (progress: number) => set({ progress }),
69
+
70
+ setError: (error: string | null) => {
71
+ set({ error, isProcessing: false, progress: 0 });
72
+ },
73
+
74
+ setResult: (result: unknown) => {
75
+ set({ result, isProcessing: false, progress: 100 });
76
+ },
77
+
78
+ reset: () => set(INITIAL_STATE),
79
+ }),
80
+ });
81
+
82
+ export type WizardStoreType = ReturnType<typeof createWizardStore>;
package/src/index.ts CHANGED
@@ -188,3 +188,6 @@ export type {
188
188
  ComparisonTranslationKeys,
189
189
  PromptTranslationKeys,
190
190
  } from "./presentation/screens/ai-feature";
191
+
192
+ // AIGenerationWizard - Generic wizard for all AI generation flows
193
+ export * from "./features/wizard";