@umituz/react-native-ai-generation-content 1.12.4 → 1.12.5

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.
Files changed (67) hide show
  1. package/package.json +29 -6
  2. package/src/domains/creations/application/services/CreationsService.ts +71 -0
  3. package/src/domains/creations/domain/entities/Creation.ts +51 -0
  4. package/src/domains/creations/domain/entities/index.ts +6 -0
  5. package/src/domains/creations/domain/repositories/ICreationsRepository.ts +23 -0
  6. package/src/domains/creations/domain/repositories/index.ts +5 -0
  7. package/src/domains/creations/domain/services/ICreationsStorageService.ts +13 -0
  8. package/src/domains/creations/domain/value-objects/CreationsConfig.ts +76 -0
  9. package/src/domains/creations/domain/value-objects/index.ts +12 -0
  10. package/src/domains/creations/index.ts +84 -0
  11. package/src/domains/creations/infrastructure/adapters/createRepository.ts +54 -0
  12. package/src/domains/creations/infrastructure/adapters/index.ts +5 -0
  13. package/src/domains/creations/infrastructure/repositories/CreationsRepository.ts +233 -0
  14. package/src/domains/creations/infrastructure/repositories/index.ts +8 -0
  15. package/src/domains/creations/infrastructure/services/CreationsStorageService.ts +48 -0
  16. package/src/domains/creations/presentation/components/CreationCard.tsx +136 -0
  17. package/src/domains/creations/presentation/components/CreationDetail/DetailActions.tsx +76 -0
  18. package/src/domains/creations/presentation/components/CreationDetail/DetailHeader.tsx +81 -0
  19. package/src/domains/creations/presentation/components/CreationDetail/DetailImage.tsx +41 -0
  20. package/src/domains/creations/presentation/components/CreationDetail/DetailStory.tsx +67 -0
  21. package/src/domains/creations/presentation/components/CreationDetail/index.ts +4 -0
  22. package/src/domains/creations/presentation/components/CreationImageViewer.tsx +43 -0
  23. package/src/domains/creations/presentation/components/CreationThumbnail.tsx +63 -0
  24. package/src/domains/creations/presentation/components/CreationsGrid.tsx +75 -0
  25. package/src/domains/creations/presentation/components/CreationsHomeCard.tsx +176 -0
  26. package/src/domains/creations/presentation/components/EmptyState.tsx +75 -0
  27. package/src/domains/creations/presentation/components/FilterBottomSheet.tsx +158 -0
  28. package/src/domains/creations/presentation/components/FilterChips.tsx +105 -0
  29. package/src/domains/creations/presentation/components/GalleryHeader.tsx +106 -0
  30. package/src/domains/creations/presentation/components/index.ts +19 -0
  31. package/src/domains/creations/presentation/hooks/index.ts +7 -0
  32. package/src/domains/creations/presentation/hooks/useCreations.ts +33 -0
  33. package/src/domains/creations/presentation/hooks/useCreationsFilter.ts +70 -0
  34. package/src/domains/creations/presentation/hooks/useDeleteCreation.ts +51 -0
  35. package/src/domains/creations/presentation/screens/CreationDetailScreen.tsx +71 -0
  36. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +217 -0
  37. package/src/domains/creations/presentation/screens/index.ts +5 -0
  38. package/src/domains/creations/presentation/utils/filterUtils.ts +52 -0
  39. package/src/domains/creations/types.d.ts +107 -0
  40. package/src/domains/face-detection/domain/constants/faceDetectionConstants.ts +16 -0
  41. package/src/domains/face-detection/domain/entities/FaceDetection.ts +19 -0
  42. package/src/domains/face-detection/index.ts +26 -0
  43. package/src/domains/face-detection/infrastructure/analyzers/faceAnalyzer.ts +36 -0
  44. package/src/domains/face-detection/infrastructure/validators/faceValidator.ts +52 -0
  45. package/src/domains/face-detection/presentation/components/FaceValidationStatus.tsx +111 -0
  46. package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +58 -0
  47. package/src/domains/feature-background/domain/entities/background.types.ts +77 -0
  48. package/src/domains/feature-background/domain/entities/component.types.ts +96 -0
  49. package/src/domains/feature-background/domain/entities/config.types.ts +41 -0
  50. package/src/domains/feature-background/domain/entities/index.ts +31 -0
  51. package/src/domains/feature-background/index.ts +72 -0
  52. package/src/domains/feature-background/infrastructure/constants/index.ts +5 -0
  53. package/src/domains/feature-background/infrastructure/constants/prompts.constants.ts +15 -0
  54. package/src/domains/feature-background/presentation/components/BackgroundFeature.tsx +145 -0
  55. package/src/domains/feature-background/presentation/components/ComparisonSlider.tsx +199 -0
  56. package/src/domains/feature-background/presentation/components/ErrorDisplay.tsx +58 -0
  57. package/src/domains/feature-background/presentation/components/FeatureHeader.tsx +80 -0
  58. package/src/domains/feature-background/presentation/components/GenerateButton.tsx +86 -0
  59. package/src/domains/feature-background/presentation/components/ImagePicker.tsx +136 -0
  60. package/src/domains/feature-background/presentation/components/ModeSelector.tsx +78 -0
  61. package/src/domains/feature-background/presentation/components/ProcessingModal.tsx +113 -0
  62. package/src/domains/feature-background/presentation/components/PromptInput.tsx +142 -0
  63. package/src/domains/feature-background/presentation/components/ResultDisplay.tsx +123 -0
  64. package/src/domains/feature-background/presentation/components/index.ts +16 -0
  65. package/src/domains/feature-background/presentation/hooks/index.ts +7 -0
  66. package/src/domains/feature-background/presentation/hooks/useBackgroundFeature.ts +118 -0
  67. package/src/index.ts +18 -0
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @umituz/react-native-ai-feature-background
3
+ * AI-powered background replacement and removal feature for React Native
4
+ *
5
+ * Usage:
6
+ * import {
7
+ * BackgroundFeature,
8
+ * useBackgroundFeature,
9
+ * ImagePicker,
10
+ * ComparisonSlider,
11
+ * ModeSelector,
12
+ * } from '@umituz/react-native-ai-feature-background';
13
+ */
14
+
15
+ // =============================================================================
16
+ // DOMAIN LAYER - Types & Interfaces
17
+ // =============================================================================
18
+
19
+ export type {
20
+ BackgroundProcessRequest,
21
+ BackgroundProcessResult,
22
+ BackgroundFeatureState,
23
+ SamplePrompt,
24
+ StudioMode,
25
+ StudioModeConfig,
26
+ ComparisonState,
27
+ ImagePickerProps,
28
+ PromptInputProps,
29
+ GenerateButtonProps,
30
+ ResultDisplayProps,
31
+ ErrorDisplayProps,
32
+ ProcessingModalProps,
33
+ FeatureHeaderProps,
34
+ ModeSelectorProps,
35
+ ComparisonSliderProps,
36
+ ProcessRequestParams,
37
+ BackgroundFeatureConfig,
38
+ UseBackgroundFeatureConfig,
39
+ } from "./domain/entities";
40
+
41
+ // =============================================================================
42
+ // INFRASTRUCTURE LAYER - Constants
43
+ // =============================================================================
44
+
45
+ export { DEFAULT_SAMPLE_PROMPTS } from "./infrastructure/constants";
46
+
47
+ // =============================================================================
48
+ // PRESENTATION LAYER - Components
49
+ // =============================================================================
50
+
51
+ export {
52
+ BackgroundFeature,
53
+ ImagePicker,
54
+ PromptInput,
55
+ GenerateButton,
56
+ ResultDisplay,
57
+ ErrorDisplay,
58
+ ProcessingModal,
59
+ FeatureHeader,
60
+ ComparisonSlider,
61
+ ModeSelector,
62
+ } from "./presentation/components";
63
+
64
+ export type { BackgroundFeatureProps } from "./presentation/components";
65
+
66
+ // =============================================================================
67
+ // PRESENTATION LAYER - Hooks
68
+ // =============================================================================
69
+
70
+ export { useBackgroundFeature } from "./presentation/hooks";
71
+
72
+ export type { UseBackgroundFeatureReturn } from "./presentation/hooks";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Infrastructure Constants Export
3
+ */
4
+
5
+ export { DEFAULT_SAMPLE_PROMPTS } from "./prompts.constants";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Default Sample Prompts
3
+ * @description Default prompt suggestions for background replacement
4
+ */
5
+
6
+ import type { SamplePrompt } from "../../domain/entities";
7
+
8
+ export const DEFAULT_SAMPLE_PROMPTS: readonly SamplePrompt[] = [
9
+ { id: "beach", text: "Beach sunset with palm trees" },
10
+ { id: "office", text: "Modern office with city view" },
11
+ { id: "mountain", text: "Mountain landscape with snow" },
12
+ { id: "living-room", text: "Cozy living room interior" },
13
+ { id: "garden", text: "Japanese garden with cherry blossoms" },
14
+ { id: "cityscape", text: "Futuristic cityscape at night" },
15
+ ] as const;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Background Feature Component
3
+ * @description Main feature component composing all sub-components
4
+ */
5
+
6
+ import React, { useCallback, useState } from "react";
7
+ import { ScrollView, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
+ import type {
10
+ BackgroundFeatureConfig,
11
+ SamplePrompt,
12
+ } from "../../domain/entities";
13
+ import { ImagePicker } from "./ImagePicker";
14
+ import { PromptInput } from "./PromptInput";
15
+ import { GenerateButton } from "./GenerateButton";
16
+ import { ResultDisplay } from "./ResultDisplay";
17
+ import { ErrorDisplay } from "./ErrorDisplay";
18
+ import { ProcessingModal } from "./ProcessingModal";
19
+ import { FeatureHeader } from "./FeatureHeader";
20
+ import { useBackgroundFeature } from "../hooks";
21
+ import type { ImageSourcePropType } from "react-native";
22
+
23
+ export interface BackgroundFeatureProps {
24
+ readonly config: BackgroundFeatureConfig;
25
+ readonly onSelectImage: () => Promise<string | null>;
26
+ readonly heroImage?: ImageSourcePropType;
27
+ readonly description?: string;
28
+ readonly promptLabel?: string;
29
+ readonly promptPlaceholder?: string;
30
+ readonly samplePrompts?: readonly SamplePrompt[];
31
+ readonly samplePromptsLabel?: string;
32
+ readonly generateButtonText?: string;
33
+ readonly saveButtonText?: string;
34
+ readonly resetButtonText?: string;
35
+ readonly processingText?: string;
36
+ readonly placeholderText?: string;
37
+ }
38
+
39
+ export const BackgroundFeature: React.FC<BackgroundFeatureProps> = ({
40
+ config,
41
+ onSelectImage,
42
+ heroImage,
43
+ description,
44
+ promptLabel,
45
+ promptPlaceholder,
46
+ samplePrompts,
47
+ samplePromptsLabel,
48
+ generateButtonText,
49
+ saveButtonText,
50
+ resetButtonText,
51
+ processingText,
52
+ placeholderText,
53
+ }) => {
54
+ const tokens = useAppDesignTokens();
55
+ const [prompt, setPrompt] = useState("");
56
+
57
+ const feature = useBackgroundFeature({
58
+ processRequest: config.onProcess,
59
+ onSelectImage,
60
+ });
61
+
62
+ const handleProcess = useCallback(async () => {
63
+ await feature.process(prompt);
64
+ if (feature.processedUrl && config.onSuccess) {
65
+ config.onSuccess({
66
+ success: true,
67
+ imageUrl: feature.processedUrl,
68
+ });
69
+ }
70
+ }, [feature, prompt, config]);
71
+
72
+ const handleSave = useCallback(async () => {
73
+ if (feature.processedUrl && config.onSave) {
74
+ await config.onSave(feature.processedUrl);
75
+ }
76
+ }, [feature.processedUrl, config]);
77
+
78
+ const handleReset = useCallback(() => {
79
+ feature.reset();
80
+ setPrompt("");
81
+ }, [feature]);
82
+
83
+ const isDisabled = !feature.imageUri || !prompt.trim();
84
+
85
+ return (
86
+ <>
87
+ <ScrollView
88
+ style={[styles.container, { padding: tokens.spacing.lg }]}
89
+ showsVerticalScrollIndicator={false}
90
+ >
91
+ <FeatureHeader
92
+ heroImage={heroImage}
93
+ description={description}
94
+ />
95
+
96
+ <ImagePicker
97
+ imageUri={feature.imageUri}
98
+ isProcessing={feature.isProcessing}
99
+ onSelectImage={feature.selectImage}
100
+ placeholderText={placeholderText}
101
+ />
102
+
103
+ <PromptInput
104
+ value={prompt}
105
+ onChangeText={setPrompt}
106
+ isProcessing={feature.isProcessing}
107
+ label={promptLabel}
108
+ placeholder={promptPlaceholder}
109
+ samplePrompts={samplePrompts}
110
+ samplePromptsLabel={samplePromptsLabel}
111
+ />
112
+
113
+ <ErrorDisplay error={feature.error} />
114
+
115
+ <ResultDisplay
116
+ imageUrl={feature.processedUrl}
117
+ isProcessing={feature.isProcessing}
118
+ onSave={handleSave}
119
+ onReset={handleReset}
120
+ saveButtonText={saveButtonText}
121
+ resetButtonText={resetButtonText}
122
+ />
123
+
124
+ <GenerateButton
125
+ isDisabled={isDisabled}
126
+ isProcessing={feature.isProcessing}
127
+ onPress={handleProcess}
128
+ buttonText={generateButtonText}
129
+ />
130
+ </ScrollView>
131
+
132
+ <ProcessingModal
133
+ visible={feature.isProcessing}
134
+ progress={feature.progress}
135
+ title={processingText}
136
+ />
137
+ </>
138
+ );
139
+ };
140
+
141
+ const styles = StyleSheet.create({
142
+ container: {
143
+ flex: 1,
144
+ },
145
+ });
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Comparison Slider Component
3
+ * @description Before/After comparison slider for images
4
+ */
5
+
6
+ import React, { memo, useState, useRef } from "react";
7
+ import {
8
+ View,
9
+ StyleSheet,
10
+ Image,
11
+ PanResponder,
12
+ Dimensions,
13
+ } from "react-native";
14
+ import {
15
+ AtomicText,
16
+ useAppDesignTokens,
17
+ } from "@umituz/react-native-design-system";
18
+ import type { ComparisonSliderProps } from "../../domain/entities";
19
+
20
+ const { width: SCREEN_WIDTH } = Dimensions.get("window");
21
+
22
+ export const ComparisonSlider: React.FC<ComparisonSliderProps> = memo(
23
+ function ComparisonSlider({
24
+ originalUri,
25
+ processedUri,
26
+ beforeLabel,
27
+ afterLabel,
28
+ }) {
29
+ const tokens = useAppDesignTokens();
30
+ const [sliderPosition, setSliderPosition] = useState(50);
31
+ const containerWidth = useRef(SCREEN_WIDTH - 32);
32
+
33
+ const panResponder = useRef(
34
+ PanResponder.create({
35
+ onStartShouldSetPanResponder: () => true,
36
+ onMoveShouldSetPanResponder: () => true,
37
+ onPanResponderMove: (_, gestureState) => {
38
+ const newPosition =
39
+ ((gestureState.moveX - 16) / containerWidth.current) * 100;
40
+ setSliderPosition(Math.max(0, Math.min(100, newPosition)));
41
+ },
42
+ })
43
+ ).current;
44
+
45
+ return (
46
+ <View
47
+ style={styles.container}
48
+ onLayout={(e) => {
49
+ containerWidth.current = e.nativeEvent.layout.width;
50
+ }}
51
+ >
52
+ <View style={styles.imageContainer}>
53
+ <Image
54
+ source={{ uri: processedUri }}
55
+ style={styles.image}
56
+ resizeMode="cover"
57
+ />
58
+
59
+ <View
60
+ style={[styles.originalContainer, { width: `${sliderPosition}%` }]}
61
+ >
62
+ <Image
63
+ source={{ uri: originalUri }}
64
+ style={[styles.image, { width: containerWidth.current }]}
65
+ resizeMode="cover"
66
+ />
67
+ </View>
68
+
69
+ <View
70
+ style={[styles.sliderLine, { left: `${sliderPosition}%` }]}
71
+ {...panResponder.panHandlers}
72
+ >
73
+ <View
74
+ style={[
75
+ styles.sliderHandle,
76
+ { backgroundColor: tokens.colors.backgroundPrimary },
77
+ ]}
78
+ >
79
+ <View style={styles.handleBars}>
80
+ <View
81
+ style={[
82
+ styles.handleBar,
83
+ { backgroundColor: tokens.colors.primary },
84
+ ]}
85
+ />
86
+ <View
87
+ style={[
88
+ styles.handleBar,
89
+ { backgroundColor: tokens.colors.primary },
90
+ ]}
91
+ />
92
+ </View>
93
+ </View>
94
+ </View>
95
+
96
+ {beforeLabel && (
97
+ <View
98
+ style={[
99
+ styles.label,
100
+ styles.labelLeft,
101
+ { backgroundColor: tokens.colors.surface },
102
+ ]}
103
+ >
104
+ <AtomicText
105
+ type="bodySmall"
106
+ style={{ color: tokens.colors.textPrimary }}
107
+ >
108
+ {beforeLabel}
109
+ </AtomicText>
110
+ </View>
111
+ )}
112
+
113
+ {afterLabel && (
114
+ <View
115
+ style={[
116
+ styles.label,
117
+ styles.labelRight,
118
+ { backgroundColor: tokens.colors.primary },
119
+ ]}
120
+ >
121
+ <AtomicText
122
+ type="bodySmall"
123
+ style={{ color: tokens.colors.backgroundPrimary }}
124
+ >
125
+ {afterLabel}
126
+ </AtomicText>
127
+ </View>
128
+ )}
129
+ </View>
130
+ </View>
131
+ );
132
+ }
133
+ );
134
+
135
+ const styles = StyleSheet.create({
136
+ container: {
137
+ width: "100%",
138
+ aspectRatio: 1,
139
+ borderRadius: 20,
140
+ overflow: "hidden",
141
+ },
142
+ imageContainer: {
143
+ flex: 1,
144
+ position: "relative",
145
+ },
146
+ image: {
147
+ width: "100%",
148
+ height: "100%",
149
+ },
150
+ originalContainer: {
151
+ position: "absolute",
152
+ top: 0,
153
+ left: 0,
154
+ bottom: 0,
155
+ overflow: "hidden",
156
+ borderRightWidth: 2,
157
+ borderRightColor: "#FFFFFF",
158
+ },
159
+ sliderLine: {
160
+ position: "absolute",
161
+ top: 0,
162
+ bottom: 0,
163
+ width: 2,
164
+ marginLeft: -1,
165
+ },
166
+ sliderHandle: {
167
+ position: "absolute",
168
+ top: "50%",
169
+ left: -20,
170
+ width: 40,
171
+ height: 40,
172
+ borderRadius: 20,
173
+ justifyContent: "center",
174
+ alignItems: "center",
175
+ marginTop: -20,
176
+ },
177
+ handleBars: {
178
+ flexDirection: "row",
179
+ gap: 4,
180
+ },
181
+ handleBar: {
182
+ width: 3,
183
+ height: 16,
184
+ borderRadius: 2,
185
+ },
186
+ label: {
187
+ position: "absolute",
188
+ top: 12,
189
+ paddingHorizontal: 8,
190
+ paddingVertical: 4,
191
+ borderRadius: 12,
192
+ },
193
+ labelLeft: {
194
+ left: 12,
195
+ },
196
+ labelRight: {
197
+ right: 12,
198
+ },
199
+ });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Error Display Component
3
+ * @description Displays error messages
4
+ */
5
+
6
+ import React, { memo } from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicIcon,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+ import type { ErrorDisplayProps } from "../../domain/entities";
14
+
15
+ export const ErrorDisplay: React.FC<ErrorDisplayProps> = memo(
16
+ function ErrorDisplay({ error }) {
17
+ const tokens = useAppDesignTokens();
18
+
19
+ if (!error) {
20
+ return null;
21
+ }
22
+
23
+ return (
24
+ <View
25
+ style={[
26
+ styles.container,
27
+ { backgroundColor: tokens.colors.errorContainer },
28
+ ]}
29
+ >
30
+ <AtomicIcon
31
+ name="alert-circle"
32
+ size={20}
33
+ color="error"
34
+ />
35
+ <AtomicText
36
+ type="bodyMedium"
37
+ style={[styles.errorText, { color: tokens.colors.error }]}
38
+ >
39
+ {error}
40
+ </AtomicText>
41
+ </View>
42
+ );
43
+ }
44
+ );
45
+
46
+ const styles = StyleSheet.create({
47
+ container: {
48
+ flexDirection: "row",
49
+ alignItems: "center",
50
+ gap: 12,
51
+ padding: 16,
52
+ borderRadius: 12,
53
+ marginVertical: 12,
54
+ },
55
+ errorText: {
56
+ flex: 1,
57
+ },
58
+ });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Feature Header Component
3
+ * @description Header with hero image and description
4
+ */
5
+
6
+ import React, { memo } from "react";
7
+ import { View, StyleSheet, Image, Dimensions } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ useAppDesignTokens,
11
+ } from "@umituz/react-native-design-system";
12
+ import type { FeatureHeaderProps } from "../../domain/entities";
13
+
14
+ const { width: SCREEN_WIDTH } = Dimensions.get("window");
15
+ const IMAGE_WIDTH = SCREEN_WIDTH - 32;
16
+ const IMAGE_HEIGHT = IMAGE_WIDTH * 0.5;
17
+
18
+ export const FeatureHeader: React.FC<FeatureHeaderProps> = memo(
19
+ function FeatureHeader({ heroImage, description }) {
20
+ const tokens = useAppDesignTokens();
21
+
22
+ if (!heroImage && !description) {
23
+ return null;
24
+ }
25
+
26
+ return (
27
+ <View style={styles.container}>
28
+ {heroImage && (
29
+ <View
30
+ style={[
31
+ styles.imageContainer,
32
+ { backgroundColor: tokens.colors.surface },
33
+ ]}
34
+ >
35
+ <Image
36
+ source={heroImage}
37
+ style={[
38
+ styles.heroImage,
39
+ { width: IMAGE_WIDTH, height: IMAGE_HEIGHT },
40
+ ]}
41
+ resizeMode="cover"
42
+ />
43
+ </View>
44
+ )}
45
+ {description && (
46
+ <AtomicText
47
+ type="bodyMedium"
48
+ style={[
49
+ styles.description,
50
+ {
51
+ color: tokens.colors.textSecondary,
52
+ marginTop: tokens.spacing.md,
53
+ },
54
+ ]}
55
+ >
56
+ {description}
57
+ </AtomicText>
58
+ )}
59
+ </View>
60
+ );
61
+ }
62
+ );
63
+
64
+ const styles = StyleSheet.create({
65
+ container: {
66
+ marginBottom: 8,
67
+ },
68
+ imageContainer: {
69
+ borderRadius: 16,
70
+ overflow: "hidden",
71
+ },
72
+ heroImage: {
73
+ borderRadius: 16,
74
+ },
75
+ description: {
76
+ textAlign: "center",
77
+ lineHeight: 22,
78
+ paddingHorizontal: 8,
79
+ },
80
+ });
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Generate Button Component
3
+ * @description Action button to trigger processing
4
+ */
5
+
6
+ import React, { memo } from "react";
7
+ import { StyleSheet, TouchableOpacity, ActivityIndicator } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicIcon,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+ import type { GenerateButtonProps } from "../../domain/entities";
14
+
15
+ export const GenerateButton: React.FC<GenerateButtonProps> = memo(
16
+ function GenerateButton({
17
+ isDisabled,
18
+ isProcessing,
19
+ onPress,
20
+ buttonText,
21
+ }) {
22
+ const tokens = useAppDesignTokens();
23
+
24
+ const disabled = isDisabled || isProcessing;
25
+
26
+ return (
27
+ <TouchableOpacity
28
+ onPress={onPress}
29
+ disabled={disabled}
30
+ activeOpacity={0.8}
31
+ style={[
32
+ styles.container,
33
+ {
34
+ backgroundColor: disabled
35
+ ? tokens.colors.surfaceSecondary
36
+ : tokens.colors.primary,
37
+ },
38
+ ]}
39
+ >
40
+ {isProcessing ? (
41
+ <ActivityIndicator color={tokens.colors.backgroundPrimary} />
42
+ ) : (
43
+ <>
44
+ <AtomicIcon
45
+ name="sparkles"
46
+ size={20}
47
+ color={disabled ? "surfaceVariant" : "onPrimary"}
48
+ style={styles.icon}
49
+ />
50
+ <AtomicText
51
+ type="headlineSmall"
52
+ style={[
53
+ styles.text,
54
+ {
55
+ color: disabled
56
+ ? tokens.colors.textTertiary
57
+ : tokens.colors.backgroundPrimary,
58
+ },
59
+ ]}
60
+ >
61
+ {buttonText}
62
+ </AtomicText>
63
+ </>
64
+ )}
65
+ </TouchableOpacity>
66
+ );
67
+ }
68
+ );
69
+
70
+ const styles = StyleSheet.create({
71
+ container: {
72
+ marginVertical: 24,
73
+ borderRadius: 28,
74
+ flexDirection: "row",
75
+ alignItems: "center",
76
+ justifyContent: "center",
77
+ paddingVertical: 16,
78
+ paddingHorizontal: 32,
79
+ },
80
+ icon: {
81
+ marginRight: 8,
82
+ },
83
+ text: {
84
+ fontWeight: "bold",
85
+ },
86
+ });