@umituz/react-native-ai-generation-content 1.17.1 → 1.17.3

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 (63) hide show
  1. package/package.json +1 -1
  2. package/src/domains/creations/presentation/components/CreationsHomeCard.tsx +1 -1
  3. package/src/domains/creations/presentation/hooks/useAdvancedFilter.ts +0 -1
  4. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +9 -6
  5. package/src/domains/face-detection/infrastructure/validators/faceValidator.ts +1 -1
  6. package/src/domains/prompts/infrastructure/services/PromptGenerationService.ts +1 -1
  7. package/src/domains/prompts/presentation/hooks/useFaceSwap.ts +2 -2
  8. package/src/domains/prompts/presentation/hooks/usePromptGeneration.ts +4 -4
  9. package/src/features/image-to-video/domain/index.ts +1 -0
  10. package/src/features/image-to-video/domain/types/image-to-video.types.ts +71 -0
  11. package/src/features/image-to-video/domain/types/index.ts +10 -0
  12. package/src/features/image-to-video/index.ts +27 -0
  13. package/src/features/image-to-video/infrastructure/index.ts +1 -0
  14. package/src/features/image-to-video/infrastructure/services/image-to-video-executor.ts +112 -0
  15. package/src/features/image-to-video/infrastructure/services/index.ts +5 -0
  16. package/src/features/image-to-video/presentation/hooks/index.ts +5 -0
  17. package/src/features/image-to-video/presentation/hooks/useImageToVideoFeature.ts +121 -0
  18. package/src/features/image-to-video/presentation/index.ts +1 -0
  19. package/src/features/text-to-image/domain/index.ts +1 -0
  20. package/src/features/text-to-image/domain/types/index.ts +10 -0
  21. package/src/features/text-to-image/domain/types/text-to-image.types.ts +66 -0
  22. package/src/features/text-to-image/index.ts +27 -1
  23. package/src/features/text-to-image/infrastructure/index.ts +1 -0
  24. package/src/features/text-to-image/infrastructure/services/index.ts +5 -0
  25. package/src/features/text-to-image/infrastructure/services/text-to-image-executor.ts +113 -0
  26. package/src/features/text-to-image/presentation/hooks/index.ts +5 -0
  27. package/src/features/text-to-image/presentation/hooks/useTextToImageFeature.ts +111 -0
  28. package/src/features/text-to-image/presentation/index.ts +1 -0
  29. package/src/features/text-to-video/domain/index.ts +1 -0
  30. package/src/features/text-to-video/domain/types/index.ts +10 -0
  31. package/src/features/text-to-video/domain/types/text-to-video.types.ts +65 -0
  32. package/src/features/text-to-video/index.ts +27 -1
  33. package/src/features/text-to-video/infrastructure/index.ts +1 -0
  34. package/src/features/text-to-video/infrastructure/services/index.ts +5 -0
  35. package/src/features/text-to-video/infrastructure/services/text-to-video-executor.ts +108 -0
  36. package/src/features/text-to-video/presentation/hooks/index.ts +5 -0
  37. package/src/features/text-to-video/presentation/hooks/useTextToVideoFeature.ts +111 -0
  38. package/src/features/text-to-video/presentation/index.ts +1 -0
  39. package/src/features/text-to-voice/domain/index.ts +1 -0
  40. package/src/features/text-to-voice/domain/types/index.ts +10 -0
  41. package/src/features/text-to-voice/domain/types/text-to-voice.types.ts +65 -0
  42. package/src/features/text-to-voice/index.ts +27 -0
  43. package/src/features/text-to-voice/infrastructure/index.ts +1 -0
  44. package/src/features/text-to-voice/infrastructure/services/index.ts +5 -0
  45. package/src/features/text-to-voice/infrastructure/services/text-to-voice-executor.ts +111 -0
  46. package/src/features/text-to-voice/presentation/hooks/index.ts +5 -0
  47. package/src/features/text-to-voice/presentation/hooks/useTextToVoiceFeature.ts +105 -0
  48. package/src/features/text-to-voice/presentation/index.ts +1 -0
  49. package/src/index.ts +24 -0
  50. package/src/presentation/components/buttons/GenerateButton.tsx +141 -0
  51. package/src/presentation/components/buttons/index.ts +1 -0
  52. package/src/presentation/components/display/ErrorDisplay.tsx +111 -0
  53. package/src/presentation/components/display/ResultDisplay.tsx +122 -0
  54. package/src/presentation/components/display/index.ts +6 -0
  55. package/src/presentation/components/headers/FeatureHeader.tsx +85 -0
  56. package/src/presentation/components/headers/index.ts +1 -0
  57. package/src/presentation/components/image-picker/DualImagePicker.tsx +95 -0
  58. package/src/presentation/components/image-picker/ImagePickerBox.tsx +165 -0
  59. package/src/presentation/components/image-picker/index.ts +2 -0
  60. package/src/presentation/components/index.ts +4 -0
  61. package/src/features/text-to-image/domain/entities.ts +0 -58
  62. package/src/features/text-to-video/domain/entities.ts +0 -52
  63. package/src/types/jsx.d.ts +0 -19
@@ -0,0 +1,5 @@
1
+ export {
2
+ executeTextToVoice,
3
+ hasTextToVoiceSupport,
4
+ } from "./text-to-voice-executor";
5
+ export type { ExecuteTextToVoiceOptions } from "./text-to-voice-executor";
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Text-to-Voice Executor
3
+ * Provider-agnostic text-to-voice execution using active AI provider
4
+ */
5
+
6
+ import { providerRegistry } from "../../../../infrastructure/services";
7
+ import type {
8
+ TextToVoiceRequest,
9
+ TextToVoiceResult,
10
+ TextToVoiceInputBuilder,
11
+ TextToVoiceResultExtractor,
12
+ } from "../../domain/types";
13
+
14
+ declare const __DEV__: boolean;
15
+
16
+ export interface ExecuteTextToVoiceOptions {
17
+ model: string;
18
+ buildInput: TextToVoiceInputBuilder;
19
+ extractResult?: TextToVoiceResultExtractor;
20
+ onProgress?: (progress: number) => void;
21
+ }
22
+
23
+ function defaultExtractResult(
24
+ result: unknown,
25
+ ): { audioUrl?: string; duration?: number } | undefined {
26
+ if (typeof result !== "object" || result === null) return undefined;
27
+
28
+ const r = result as Record<string, unknown>;
29
+
30
+ if (typeof r.audio === "string") {
31
+ return { audioUrl: r.audio };
32
+ }
33
+
34
+ if (typeof r.audio_url === "string") {
35
+ return { audioUrl: r.audio_url };
36
+ }
37
+
38
+ if (r.audio && typeof r.audio === "object") {
39
+ const audio = r.audio as Record<string, unknown>;
40
+ if (typeof audio.url === "string") {
41
+ return {
42
+ audioUrl: audio.url,
43
+ duration: typeof audio.duration === "number" ? audio.duration : undefined,
44
+ };
45
+ }
46
+ }
47
+
48
+ return undefined;
49
+ }
50
+
51
+ export async function executeTextToVoice(
52
+ request: TextToVoiceRequest,
53
+ options: ExecuteTextToVoiceOptions,
54
+ ): Promise<TextToVoiceResult> {
55
+ const provider = providerRegistry.getActiveProvider();
56
+
57
+ if (!provider) {
58
+ return { success: false, error: "No AI provider configured" };
59
+ }
60
+
61
+ if (!provider.isInitialized()) {
62
+ return { success: false, error: "AI provider not initialized" };
63
+ }
64
+
65
+ if (!request.text) {
66
+ return { success: false, error: "Text is required" };
67
+ }
68
+
69
+ const { model, buildInput, extractResult, onProgress } = options;
70
+
71
+ if (__DEV__) {
72
+ // eslint-disable-next-line no-console
73
+ console.log(`[TextToVoice] Provider: ${provider.providerId}, Model: ${model}`);
74
+ }
75
+
76
+ try {
77
+ onProgress?.(10);
78
+
79
+ const input = buildInput(request.text, request.options);
80
+ onProgress?.(20);
81
+
82
+ const result = await provider.run(model, input);
83
+ onProgress?.(90);
84
+
85
+ const extractor = extractResult || defaultExtractResult;
86
+ const extracted = extractor(result);
87
+ onProgress?.(100);
88
+
89
+ if (!extracted?.audioUrl) {
90
+ return { success: false, error: "No audio in response" };
91
+ }
92
+
93
+ return {
94
+ success: true,
95
+ audioUrl: extracted.audioUrl,
96
+ duration: extracted.duration,
97
+ };
98
+ } catch (error) {
99
+ const message = error instanceof Error ? error.message : String(error);
100
+ if (__DEV__) {
101
+ // eslint-disable-next-line no-console
102
+ console.error("[TextToVoice] Error:", message);
103
+ }
104
+ return { success: false, error: message };
105
+ }
106
+ }
107
+
108
+ export function hasTextToVoiceSupport(): boolean {
109
+ const provider = providerRegistry.getActiveProvider();
110
+ return provider !== null && provider.isInitialized();
111
+ }
@@ -0,0 +1,5 @@
1
+ export { useTextToVoiceFeature } from "./useTextToVoiceFeature";
2
+ export type {
3
+ UseTextToVoiceFeatureProps,
4
+ UseTextToVoiceFeatureReturn,
5
+ } from "./useTextToVoiceFeature";
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Text-to-Voice Feature Hook
3
+ * Provider-agnostic hook for text-to-voice generation
4
+ */
5
+
6
+ import { useState, useCallback } from "react";
7
+ import { executeTextToVoice } from "../../infrastructure/services";
8
+ import type {
9
+ TextToVoiceFeatureState,
10
+ TextToVoiceFeatureConfig,
11
+ TextToVoiceResult,
12
+ TextToVoiceOptions,
13
+ } from "../../domain/types";
14
+
15
+ export interface UseTextToVoiceFeatureProps {
16
+ config: TextToVoiceFeatureConfig;
17
+ userId: string;
18
+ }
19
+
20
+ export interface UseTextToVoiceFeatureReturn {
21
+ state: TextToVoiceFeatureState;
22
+ setText: (text: string) => void;
23
+ generate: (options?: TextToVoiceOptions) => Promise<TextToVoiceResult>;
24
+ reset: () => void;
25
+ isReady: boolean;
26
+ }
27
+
28
+ const initialState: TextToVoiceFeatureState = {
29
+ text: "",
30
+ audioUrl: null,
31
+ isProcessing: false,
32
+ progress: 0,
33
+ error: null,
34
+ };
35
+
36
+ export function useTextToVoiceFeature(
37
+ props: UseTextToVoiceFeatureProps,
38
+ ): UseTextToVoiceFeatureReturn {
39
+ const { config, userId } = props;
40
+ const [state, setState] = useState<TextToVoiceFeatureState>(initialState);
41
+
42
+ const setText = useCallback(
43
+ (text: string) => {
44
+ setState((prev) => ({ ...prev, text, error: null }));
45
+ config.onTextChange?.(text);
46
+ },
47
+ [config],
48
+ );
49
+
50
+ const generate = useCallback(
51
+ async (options?: TextToVoiceOptions): Promise<TextToVoiceResult> => {
52
+ if (!state.text) {
53
+ const error = "Text is required";
54
+ setState((prev) => ({ ...prev, error }));
55
+ return { success: false, error };
56
+ }
57
+
58
+ setState((prev) => ({
59
+ ...prev,
60
+ isProcessing: true,
61
+ progress: 0,
62
+ error: null,
63
+ }));
64
+
65
+ config.onProcessingStart?.();
66
+
67
+ const result = await executeTextToVoice(
68
+ { text: state.text, userId, options },
69
+ {
70
+ model: config.model,
71
+ buildInput: config.buildInput,
72
+ extractResult: config.extractResult,
73
+ onProgress: (progress) => {
74
+ setState((prev) => ({ ...prev, progress }));
75
+ },
76
+ },
77
+ );
78
+
79
+ if (result.success && result.audioUrl) {
80
+ setState((prev) => ({
81
+ ...prev,
82
+ audioUrl: result.audioUrl ?? null,
83
+ isProcessing: false,
84
+ progress: 100,
85
+ }));
86
+ } else {
87
+ const error = result.error || "Generation failed";
88
+ setState((prev) => ({ ...prev, isProcessing: false, error }));
89
+ config.onError?.(error);
90
+ }
91
+
92
+ config.onProcessingComplete?.(result);
93
+ return result;
94
+ },
95
+ [state.text, userId, config],
96
+ );
97
+
98
+ const reset = useCallback(() => {
99
+ setState(initialState);
100
+ }, []);
101
+
102
+ const isReady = state.text.length > 0 && !state.isProcessing;
103
+
104
+ return { state, setText, generate, reset, isReady };
105
+ }
@@ -0,0 +1 @@
1
+ export * from "./hooks";
package/src/index.ts CHANGED
@@ -353,3 +353,27 @@ export * from "./features/remove-background";
353
353
 
354
354
  export * from "./features/remove-object";
355
355
 
356
+ // =============================================================================
357
+ // FEATURES - Text-to-Video
358
+ // =============================================================================
359
+
360
+ export * from "./features/text-to-video";
361
+
362
+ // =============================================================================
363
+ // FEATURES - Text-to-Image
364
+ // =============================================================================
365
+
366
+ export * from "./features/text-to-image";
367
+
368
+ // =============================================================================
369
+ // FEATURES - Image-to-Video
370
+ // =============================================================================
371
+
372
+ export * from "./features/image-to-video";
373
+
374
+ // =============================================================================
375
+ // FEATURES - Text-to-Voice
376
+ // =============================================================================
377
+
378
+ export * from "./features/text-to-voice";
379
+
@@ -0,0 +1,141 @@
1
+ /**
2
+ * GenerateButton Component
3
+ * Generic AI generation button with gradient/solid variants
4
+ * Props-driven for 100+ apps compatibility
5
+ */
6
+
7
+ import React from "react";
8
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
9
+ import {
10
+ AtomicText,
11
+ useAppDesignTokens,
12
+ AtomicIcon,
13
+ } from "@umituz/react-native-design-system";
14
+ import { LinearGradient } from "expo-linear-gradient";
15
+
16
+ export interface GenerateButtonProps {
17
+ readonly isDisabled?: boolean;
18
+ readonly isProcessing?: boolean;
19
+ readonly onPress: () => void;
20
+ readonly text: string;
21
+ readonly processingText?: string;
22
+ readonly variant?: "gradient" | "solid";
23
+ readonly gradientColors?: readonly [string, string, ...string[]];
24
+ readonly icon?: string;
25
+ readonly iconSize?: number;
26
+ }
27
+
28
+ export const GenerateButton: React.FC<GenerateButtonProps> = ({
29
+ isDisabled = false,
30
+ isProcessing = false,
31
+ onPress,
32
+ text,
33
+ processingText,
34
+ variant = "gradient",
35
+ gradientColors = ["#FF6B9D", "#C74375", "#FF6B9D"],
36
+ icon = "sparkles",
37
+ iconSize = 24,
38
+ }) => {
39
+ const tokens = useAppDesignTokens();
40
+ const disabled = isDisabled || isProcessing;
41
+ const displayText = isProcessing && processingText ? processingText : text;
42
+
43
+ if (variant === "solid") {
44
+ return (
45
+ <View style={[styles.solidContainer, { marginTop: tokens.spacing.xl }]}>
46
+ <TouchableOpacity
47
+ onPress={onPress}
48
+ disabled={disabled}
49
+ activeOpacity={0.8}
50
+ style={[
51
+ styles.solidButton,
52
+ {
53
+ backgroundColor: disabled
54
+ ? tokens.colors.surfaceSecondary
55
+ : tokens.colors.primary,
56
+ },
57
+ ]}
58
+ >
59
+ <View style={styles.buttonContent}>
60
+ <AtomicIcon name={icon} customSize={20} customColor="#FFFFFF" />
61
+ <AtomicText type="bodyLarge" style={styles.solidButtonText}>
62
+ {displayText}
63
+ </AtomicText>
64
+ </View>
65
+ </TouchableOpacity>
66
+ </View>
67
+ );
68
+ }
69
+
70
+ return (
71
+ <View style={[styles.gradientContainer, { marginTop: tokens.spacing.xl }]}>
72
+ <TouchableOpacity
73
+ onPress={onPress}
74
+ disabled={disabled}
75
+ activeOpacity={0.85}
76
+ style={styles.buttonWrapper}
77
+ >
78
+ <LinearGradient
79
+ colors={disabled ? ["#9CA3AF", "#6B7280"] : gradientColors}
80
+ start={[0, 0]}
81
+ end={[1, 0]}
82
+ style={[styles.gradientButton, disabled && styles.disabledButton]}
83
+ >
84
+ <View style={styles.buttonContent}>
85
+ <AtomicIcon name={icon} customSize={iconSize} customColor="#FFF" />
86
+ <AtomicText type="bodyLarge" style={styles.gradientButtonText}>
87
+ {displayText}
88
+ </AtomicText>
89
+ </View>
90
+ </LinearGradient>
91
+ </TouchableOpacity>
92
+ </View>
93
+ );
94
+ };
95
+
96
+ const styles = StyleSheet.create({
97
+ gradientContainer: {
98
+ alignItems: "center",
99
+ paddingHorizontal: 16,
100
+ },
101
+ solidContainer: {
102
+ paddingHorizontal: 16,
103
+ },
104
+ buttonWrapper: {
105
+ width: "100%",
106
+ maxWidth: 320,
107
+ borderRadius: 30,
108
+ },
109
+ gradientButton: {
110
+ paddingVertical: 18,
111
+ paddingHorizontal: 32,
112
+ borderRadius: 30,
113
+ alignItems: "center",
114
+ justifyContent: "center",
115
+ },
116
+ solidButton: {
117
+ borderRadius: 16,
118
+ paddingVertical: 16,
119
+ paddingHorizontal: 24,
120
+ alignItems: "center",
121
+ justifyContent: "center",
122
+ },
123
+ disabledButton: {
124
+ opacity: 0.5,
125
+ },
126
+ buttonContent: {
127
+ flexDirection: "row",
128
+ alignItems: "center",
129
+ justifyContent: "center",
130
+ gap: 12,
131
+ },
132
+ gradientButtonText: {
133
+ color: "#FFFFFF",
134
+ fontWeight: "700",
135
+ fontSize: 18,
136
+ },
137
+ solidButtonText: {
138
+ color: "#FFFFFF",
139
+ fontWeight: "600",
140
+ },
141
+ });
@@ -0,0 +1 @@
1
+ export { GenerateButton, type GenerateButtonProps } from "./GenerateButton";
@@ -0,0 +1,111 @@
1
+ /**
2
+ * ErrorDisplay Component
3
+ * Generic error display with retry action
4
+ * Props-driven for 100+ apps compatibility
5
+ */
6
+
7
+ import React from "react";
8
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
9
+ import {
10
+ AtomicText,
11
+ useAppDesignTokens,
12
+ AtomicIcon,
13
+ } from "@umituz/react-native-design-system";
14
+
15
+ export interface ErrorDisplayProps {
16
+ readonly error: string | null;
17
+ readonly onRetry?: () => void;
18
+ readonly retryText?: string;
19
+ readonly icon?: string;
20
+ }
21
+
22
+ export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({
23
+ error,
24
+ onRetry,
25
+ retryText,
26
+ icon = "alert-circle",
27
+ }) => {
28
+ const tokens = useAppDesignTokens();
29
+
30
+ if (!error) {
31
+ return null;
32
+ }
33
+
34
+ return (
35
+ <View
36
+ style={[
37
+ styles.container,
38
+ {
39
+ backgroundColor: `${tokens.colors.error}15`,
40
+ borderColor: `${tokens.colors.error}30`,
41
+ padding: tokens.spacing.md,
42
+ marginTop: tokens.spacing.md,
43
+ },
44
+ ]}
45
+ >
46
+ <View style={styles.header}>
47
+ <AtomicIcon
48
+ name={icon}
49
+ customSize={20}
50
+ customColor={tokens.colors.error}
51
+ />
52
+ <AtomicText
53
+ type="bodyMedium"
54
+ style={[styles.errorText, { color: tokens.colors.error }]}
55
+ >
56
+ {error}
57
+ </AtomicText>
58
+ </View>
59
+ {onRetry && retryText && (
60
+ <TouchableOpacity
61
+ onPress={onRetry}
62
+ style={[
63
+ styles.retryButton,
64
+ {
65
+ backgroundColor: tokens.colors.error,
66
+ marginTop: tokens.spacing.sm,
67
+ },
68
+ ]}
69
+ >
70
+ <AtomicIcon
71
+ name="refresh"
72
+ customSize={16}
73
+ customColor={tokens.colors.onError}
74
+ />
75
+ <AtomicText
76
+ type="bodySmall"
77
+ style={{ color: tokens.colors.onError, fontWeight: "600" }}
78
+ >
79
+ {retryText}
80
+ </AtomicText>
81
+ </TouchableOpacity>
82
+ )}
83
+ </View>
84
+ );
85
+ };
86
+
87
+ const styles = StyleSheet.create({
88
+ container: {
89
+ borderRadius: 12,
90
+ borderWidth: 1,
91
+ },
92
+ header: {
93
+ flexDirection: "row",
94
+ alignItems: "flex-start",
95
+ gap: 10,
96
+ },
97
+ errorText: {
98
+ flex: 1,
99
+ lineHeight: 20,
100
+ },
101
+ retryButton: {
102
+ flexDirection: "row",
103
+ alignItems: "center",
104
+ justifyContent: "center",
105
+ gap: 6,
106
+ paddingVertical: 10,
107
+ paddingHorizontal: 16,
108
+ borderRadius: 8,
109
+ alignSelf: "flex-start",
110
+ },
111
+ });
@@ -0,0 +1,122 @@
1
+ /**
2
+ * ResultDisplay Component
3
+ * Generic result display with save/reset actions
4
+ * Props-driven for 100+ apps compatibility
5
+ */
6
+
7
+ import React from "react";
8
+ import { View, StyleSheet } from "react-native";
9
+ import {
10
+ AtomicText,
11
+ AtomicButton,
12
+ useAppDesignTokens,
13
+ AtomicIcon,
14
+ } from "@umituz/react-native-design-system";
15
+
16
+ export interface ResultDisplayAction {
17
+ readonly id: string;
18
+ readonly label: string;
19
+ readonly onPress: () => void;
20
+ readonly variant?: "primary" | "outline";
21
+ readonly icon?: string;
22
+ }
23
+
24
+ export interface ResultDisplayProps {
25
+ readonly visible?: boolean;
26
+ readonly successText: string;
27
+ readonly actions: ResultDisplayAction[];
28
+ readonly successIcon?: string;
29
+ }
30
+
31
+ export const ResultDisplay: React.FC<ResultDisplayProps> = ({
32
+ visible = true,
33
+ successText,
34
+ actions,
35
+ successIcon = "checkmark-circle",
36
+ }) => {
37
+ const tokens = useAppDesignTokens();
38
+
39
+ if (!visible) {
40
+ return null;
41
+ }
42
+
43
+ return (
44
+ <View style={[styles.container, { marginTop: tokens.spacing.lg }]}>
45
+ <View style={styles.successHeader}>
46
+ <AtomicIcon
47
+ name={successIcon}
48
+ customSize={24}
49
+ customColor={tokens.colors.success}
50
+ />
51
+ <AtomicText
52
+ type="bodyMedium"
53
+ style={[styles.successText, { color: tokens.colors.success }]}
54
+ >
55
+ {successText}
56
+ </AtomicText>
57
+ </View>
58
+ <View style={[styles.actionButtons, { gap: tokens.spacing.sm }]}>
59
+ {actions.map((action) => (
60
+ <AtomicButton
61
+ key={action.id}
62
+ onPress={action.onPress}
63
+ variant={action.variant === "outline" ? "outline" : undefined}
64
+ style={styles.actionButton}
65
+ >
66
+ <View style={styles.buttonContent}>
67
+ {action.icon && (
68
+ <AtomicIcon
69
+ name={action.icon}
70
+ customSize={18}
71
+ customColor={
72
+ action.variant === "outline"
73
+ ? tokens.colors.primary
74
+ : tokens.colors.onPrimary
75
+ }
76
+ />
77
+ )}
78
+ <AtomicText
79
+ type="bodyMedium"
80
+ style={{
81
+ color:
82
+ action.variant === "outline"
83
+ ? tokens.colors.primary
84
+ : tokens.colors.onPrimary,
85
+ }}
86
+ >
87
+ {action.label}
88
+ </AtomicText>
89
+ </View>
90
+ </AtomicButton>
91
+ ))}
92
+ </View>
93
+ </View>
94
+ );
95
+ };
96
+
97
+ const styles = StyleSheet.create({
98
+ container: {
99
+ alignItems: "center",
100
+ },
101
+ successHeader: {
102
+ flexDirection: "row",
103
+ alignItems: "center",
104
+ gap: 8,
105
+ marginBottom: 16,
106
+ },
107
+ successText: {
108
+ textAlign: "center",
109
+ },
110
+ actionButtons: {
111
+ width: "100%",
112
+ maxWidth: 280,
113
+ },
114
+ actionButton: {
115
+ flex: 1,
116
+ },
117
+ buttonContent: {
118
+ flexDirection: "row",
119
+ alignItems: "center",
120
+ gap: 8,
121
+ },
122
+ });
@@ -0,0 +1,6 @@
1
+ export {
2
+ ResultDisplay,
3
+ type ResultDisplayProps,
4
+ type ResultDisplayAction,
5
+ } from "./ResultDisplay";
6
+ export { ErrorDisplay, type ErrorDisplayProps } from "./ErrorDisplay";