@umituz/react-native-ai-generation-content 1.27.21 → 1.27.22

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.27.21",
3
+ "version": "1.27.22",
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",
@@ -3,27 +3,22 @@
3
3
  * Config-driven wizard steps for text-to-image generation
4
4
  */
5
5
 
6
- import type { WizardFeatureConfig } from "../domain/entities/wizard-config.types";
6
+ import type { WizardFeatureConfig, TextInputStepConfig } from "../domain/entities/wizard-config.types";
7
+
8
+ const promptStep: TextInputStepConfig = {
9
+ id: "prompt",
10
+ type: "text_input",
11
+ required: true,
12
+ titleKey: "text2image.wizard.prompt.title",
13
+ subtitleKey: "text2image.wizard.prompt.subtitle",
14
+ placeholderKey: "text2image.wizard.prompt.placeholder",
15
+ minLength: 3,
16
+ maxLength: 1000,
17
+ multiline: true,
18
+ };
7
19
 
8
20
  export const TEXT_TO_IMAGE_WIZARD_CONFIG: WizardFeatureConfig = {
9
21
  id: "text-to-image",
10
22
  name: "Text to Image",
11
- steps: [
12
- {
13
- id: "prompt",
14
- type: "text_input",
15
- required: true,
16
- placeholderKey: "textToImage.promptPlaceholder",
17
- minLength: 3,
18
- maxLength: 1000,
19
- multiline: true,
20
- },
21
- {
22
- id: "style",
23
- type: "selection",
24
- selectionType: "style",
25
- options: [],
26
- required: false,
27
- },
28
- ],
23
+ steps: [promptStep],
29
24
  };
@@ -50,7 +50,12 @@ export { GenericWizardFlow } from "./presentation/components";
50
50
  export type { GenericWizardFlowProps } from "./presentation/components";
51
51
 
52
52
  // Presentation - Screens
53
- export { GeneratingScreen } from "./presentation/screens";
53
+ export { GeneratingScreen, TextInputScreen } from "./presentation/screens";
54
+ export type {
55
+ TextInputScreenTranslations,
56
+ TextInputScreenConfig,
57
+ TextInputScreenProps,
58
+ } from "./presentation/screens";
54
59
 
55
60
  // Feature Configs
56
61
  export * from "./configs";
@@ -3,9 +3,10 @@ import { extractMediaUrl, getMediaTypeFromUrl } from "@umituz/react-native-desig
3
3
  import { StepType } from "../../../../../domain/entities/flow-config.types";
4
4
  import { GenericPhotoUploadScreen } from "../screens/GenericPhotoUploadScreen";
5
5
  import { GeneratingScreen } from "../screens/GeneratingScreen";
6
+ import { TextInputScreen } from "../screens/TextInputScreen";
6
7
  import { ScenarioPreviewScreen } from "../../../../scenarios/presentation/screens/ScenarioPreviewScreen";
7
8
  import { ResultPreviewScreen } from "../../../../result-preview/presentation/components/ResultPreviewScreen";
8
- import { getWizardStepConfig, getUploadedImage } from "./WizardStepRenderer.utils";
9
+ import { getWizardStepConfig, getTextInputConfig, getUploadedImage } from "./WizardStepRenderer.utils";
9
10
  import type { WizardStepRendererProps } from "./WizardStepRenderer.types";
10
11
 
11
12
  export type { WizardStepRendererProps } from "./WizardStepRenderer.types";
@@ -127,6 +128,44 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
127
128
  );
128
129
  }
129
130
 
131
+ case StepType.TEXT_INPUT: {
132
+ const textConfig = getTextInputConfig(step.config);
133
+ const titleKey = textConfig?.titleKey ?? `wizard.steps.${step.id}.title`;
134
+ const subtitleKey = textConfig?.subtitleKey ?? `wizard.steps.${step.id}.subtitle`;
135
+ const placeholderKey = textConfig?.placeholderKey ?? `wizard.steps.${step.id}.placeholder`;
136
+ const existingData = customData[step.id];
137
+ const existingText = typeof existingData === "string"
138
+ ? existingData
139
+ : typeof existingData === "object" && existingData !== null && "text" in existingData
140
+ ? String((existingData as { text: string }).text)
141
+ : "";
142
+
143
+ return (
144
+ <TextInputScreen
145
+ stepId={step.id}
146
+ translations={{
147
+ title: t(titleKey),
148
+ subtitle: subtitleKey ? t(subtitleKey) : undefined,
149
+ placeholder: t(placeholderKey),
150
+ continueButton: t("common.continue"),
151
+ backButton: t("common.back"),
152
+ examplesTitle: t("textInput.examplesTitle"),
153
+ }}
154
+ config={{
155
+ minLength: textConfig?.minLength ?? 3,
156
+ maxLength: textConfig?.maxLength ?? 1000,
157
+ multiline: textConfig?.multiline ?? true,
158
+ }}
159
+ initialValue={existingText}
160
+ onBack={onBack}
161
+ onContinue={(text) => {
162
+ // Store text in a structure compatible with existing handlers
163
+ onPhotoContinue(step.id, { uri: text, text, previewUrl: "" } as any);
164
+ }}
165
+ />
166
+ );
167
+ }
168
+
130
169
  default:
131
170
  if (typeof __DEV__ !== "undefined" && __DEV__) {
132
171
  console.warn("[WizardStepRenderer] Unhandled step type", { stepType: step.type });
@@ -1,4 +1,8 @@
1
- import type { WizardStepConfig } from "../../domain/entities/wizard-config.types";
1
+ import type {
2
+ WizardStepConfig,
3
+ TextInputStepConfig,
4
+ PhotoUploadStepConfig,
5
+ } from "../../domain/entities/wizard-config.types";
2
6
  import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
3
7
 
4
8
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -19,6 +23,18 @@ export function getWizardStepConfig(config: unknown): WizardStepConfig | undefin
19
23
  return undefined;
20
24
  }
21
25
 
26
+ export function getTextInputConfig(config: unknown): TextInputStepConfig | undefined {
27
+ if (!isRecord(config)) return undefined;
28
+ if (config.type === "text_input") return config as unknown as TextInputStepConfig;
29
+ return undefined;
30
+ }
31
+
32
+ export function getPhotoUploadConfig(config: unknown): PhotoUploadStepConfig | undefined {
33
+ if (!isRecord(config)) return undefined;
34
+ if (config.type === "photo_upload") return config as unknown as PhotoUploadStepConfig;
35
+ return undefined;
36
+ }
37
+
22
38
  export function getUploadedImage(data: unknown): UploadedImage | undefined {
23
39
  if (isUploadedImage(data)) return data;
24
40
  return undefined;
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Text Input Screen
3
+ * Generic text input step for wizard flows
4
+ */
5
+
6
+ import React, { useState, useCallback } from "react";
7
+ import { View, ScrollView, TextInput, StyleSheet } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicButton,
11
+ AtomicIcon,
12
+ useAppDesignTokens,
13
+ } from "@umituz/react-native-design-system";
14
+
15
+ export interface TextInputScreenTranslations {
16
+ readonly title: string;
17
+ readonly subtitle?: string;
18
+ readonly placeholder: string;
19
+ readonly continueButton: string;
20
+ readonly backButton?: string;
21
+ readonly examplesTitle?: string;
22
+ }
23
+
24
+ export interface TextInputScreenConfig {
25
+ readonly minLength?: number;
26
+ readonly maxLength?: number;
27
+ readonly multiline?: boolean;
28
+ }
29
+
30
+ export interface TextInputScreenProps {
31
+ readonly stepId: string;
32
+ readonly translations: TextInputScreenTranslations;
33
+ readonly config?: TextInputScreenConfig;
34
+ readonly examplePrompts?: string[];
35
+ readonly initialValue?: string;
36
+ readonly onBack: () => void;
37
+ readonly onContinue: (text: string) => void;
38
+ }
39
+
40
+ export const TextInputScreen: React.FC<TextInputScreenProps> = ({
41
+ stepId: _stepId,
42
+ translations,
43
+ config,
44
+ examplePrompts = [],
45
+ initialValue = "",
46
+ onBack,
47
+ onContinue,
48
+ }) => {
49
+ const tokens = useAppDesignTokens();
50
+ const [text, setText] = useState(initialValue);
51
+
52
+ const minLength = config?.minLength ?? 3;
53
+ const maxLength = config?.maxLength ?? 1000;
54
+ const canContinue = text.trim().length >= minLength;
55
+
56
+ const handleContinue = useCallback(() => {
57
+ if (canContinue) {
58
+ onContinue(text.trim());
59
+ }
60
+ }, [canContinue, text, onContinue]);
61
+
62
+ const handleExampleSelect = useCallback((example: string) => {
63
+ setText(example);
64
+ }, []);
65
+
66
+ return (
67
+ <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
68
+ <View style={[styles.header, { paddingHorizontal: tokens.spacing.md }]}>
69
+ <AtomicButton
70
+ variant="text"
71
+ size="sm"
72
+ onPress={onBack}
73
+ >
74
+ <View style={styles.backButtonContent}>
75
+ <AtomicIcon name="arrow-back" size="sm" color="textPrimary" />
76
+ {translations.backButton ? (
77
+ <AtomicText type="labelMedium" color="textPrimary" style={styles.backButtonText}>
78
+ {translations.backButton}
79
+ </AtomicText>
80
+ ) : null}
81
+ </View>
82
+ </AtomicButton>
83
+ </View>
84
+
85
+ <ScrollView
86
+ style={styles.scrollView}
87
+ contentContainerStyle={{ padding: tokens.spacing.md }}
88
+ keyboardShouldPersistTaps="handled"
89
+ >
90
+ <AtomicText type="headlineMedium" color="textPrimary" style={styles.title}>
91
+ {translations.title}
92
+ </AtomicText>
93
+
94
+ {translations.subtitle ? (
95
+ <AtomicText
96
+ type="bodyMedium"
97
+ color="textSecondary"
98
+ style={{ marginBottom: tokens.spacing.lg }}
99
+ >
100
+ {translations.subtitle}
101
+ </AtomicText>
102
+ ) : null}
103
+
104
+ <View
105
+ style={[
106
+ styles.inputContainer,
107
+ {
108
+ backgroundColor: tokens.colors.backgroundSecondary,
109
+ borderRadius: tokens.borders.radius.md,
110
+ borderColor: tokens.colors.border,
111
+ },
112
+ ]}
113
+ >
114
+ <TextInput
115
+ style={[
116
+ styles.textInput,
117
+ {
118
+ color: tokens.colors.textPrimary,
119
+ minHeight: config?.multiline ? 120 : 48,
120
+ },
121
+ ]}
122
+ placeholder={translations.placeholder}
123
+ placeholderTextColor={tokens.colors.textTertiary}
124
+ value={text}
125
+ onChangeText={setText}
126
+ multiline={config?.multiline ?? true}
127
+ maxLength={maxLength}
128
+ textAlignVertical="top"
129
+ />
130
+ <AtomicText
131
+ type="bodySmall"
132
+ color="textTertiary"
133
+ style={styles.charCount}
134
+ >
135
+ {text.length}/{maxLength}
136
+ </AtomicText>
137
+ </View>
138
+
139
+ {examplePrompts.length > 0 && translations.examplesTitle ? (
140
+ <View style={{ marginTop: tokens.spacing.lg }}>
141
+ <AtomicText
142
+ type="labelLarge"
143
+ color="textSecondary"
144
+ style={{ marginBottom: tokens.spacing.sm }}
145
+ >
146
+ {translations.examplesTitle}
147
+ </AtomicText>
148
+ {examplePrompts.slice(0, 4).map((example, index) => (
149
+ <AtomicButton
150
+ key={index}
151
+ variant="outline"
152
+ size="sm"
153
+ onPress={() => handleExampleSelect(example)}
154
+ style={{ marginBottom: tokens.spacing.xs }}
155
+ >
156
+ {example.length > 50 ? `${example.slice(0, 50)}...` : example}
157
+ </AtomicButton>
158
+ ))}
159
+ </View>
160
+ ) : null}
161
+ </ScrollView>
162
+
163
+ <View style={[styles.footer, { padding: tokens.spacing.md }]}>
164
+ <AtomicButton
165
+ variant="primary"
166
+ size="lg"
167
+ onPress={handleContinue}
168
+ disabled={!canContinue}
169
+ style={styles.continueButton}
170
+ >
171
+ {translations.continueButton}
172
+ </AtomicButton>
173
+ </View>
174
+ </View>
175
+ );
176
+ };
177
+
178
+ const styles = StyleSheet.create({
179
+ container: {
180
+ flex: 1,
181
+ },
182
+ header: {
183
+ flexDirection: "row",
184
+ alignItems: "center",
185
+ paddingVertical: 8,
186
+ },
187
+ backButtonContent: {
188
+ flexDirection: "row",
189
+ alignItems: "center",
190
+ },
191
+ backButtonText: {
192
+ marginLeft: 4,
193
+ },
194
+ scrollView: {
195
+ flex: 1,
196
+ },
197
+ title: {
198
+ marginBottom: 8,
199
+ },
200
+ inputContainer: {
201
+ borderWidth: 1,
202
+ padding: 12,
203
+ },
204
+ textInput: {
205
+ fontSize: 16,
206
+ lineHeight: 24,
207
+ },
208
+ charCount: {
209
+ textAlign: "right",
210
+ marginTop: 4,
211
+ },
212
+ footer: {
213
+ borderTopWidth: 1,
214
+ borderTopColor: "rgba(0,0,0,0.1)",
215
+ },
216
+ continueButton: {
217
+ width: "100%",
218
+ },
219
+ });
@@ -5,3 +5,9 @@ export type {
5
5
  PhotoUploadScreenConfig,
6
6
  PhotoUploadScreenProps,
7
7
  } from "./GenericPhotoUploadScreen";
8
+ export { TextInputScreen } from "./TextInputScreen";
9
+ export type {
10
+ TextInputScreenTranslations,
11
+ TextInputScreenConfig,
12
+ TextInputScreenProps,
13
+ } from "./TextInputScreen";