@umituz/react-native-ai-generation-content 1.17.7 → 1.17.9

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 (27) hide show
  1. package/package.json +1 -1
  2. package/src/features/ai-hug/index.ts +4 -0
  3. package/src/features/ai-hug/presentation/components/AIHugFeature.tsx +209 -0
  4. package/src/features/ai-hug/presentation/components/index.ts +2 -0
  5. package/src/features/ai-hug/presentation/index.ts +1 -0
  6. package/src/features/ai-kiss/index.ts +4 -0
  7. package/src/features/ai-kiss/presentation/components/AIKissFeature.tsx +202 -0
  8. package/src/features/ai-kiss/presentation/components/index.ts +2 -0
  9. package/src/features/ai-kiss/presentation/index.ts +1 -0
  10. package/src/features/anime-selfie/index.ts +4 -0
  11. package/src/features/anime-selfie/presentation/components/AnimeSelfieFeature.tsx +199 -0
  12. package/src/features/anime-selfie/presentation/components/index.ts +2 -0
  13. package/src/features/anime-selfie/presentation/index.ts +1 -0
  14. package/src/features/face-swap/index.ts +4 -0
  15. package/src/features/face-swap/presentation/components/FaceSwapFeature.tsx +202 -0
  16. package/src/features/face-swap/presentation/components/index.ts +2 -0
  17. package/src/features/face-swap/presentation/index.ts +1 -0
  18. package/src/features/photo-restoration/presentation/components/PhotoRestoreFeature.tsx +47 -35
  19. package/src/features/remove-background/index.ts +4 -0
  20. package/src/features/remove-background/presentation/components/RemoveBackgroundFeature.tsx +199 -0
  21. package/src/features/remove-background/presentation/components/index.ts +2 -0
  22. package/src/features/remove-background/presentation/index.ts +1 -0
  23. package/src/features/remove-object/index.ts +4 -0
  24. package/src/features/remove-object/presentation/components/RemoveObjectFeature.tsx +249 -0
  25. package/src/features/remove-object/presentation/components/index.ts +2 -0
  26. package/src/features/remove-object/presentation/index.ts +1 -0
  27. package/src/features/upscaling/presentation/components/UpscaleFeature.tsx +47 -35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.17.7",
3
+ "version": "1.17.9",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -25,3 +25,7 @@ export type {
25
25
  UseAIHugFeatureProps,
26
26
  UseAIHugFeatureReturn,
27
27
  } from "./presentation";
28
+
29
+ // Presentation Components
30
+ export { AIHugFeature } from "./presentation";
31
+ export type { AIHugFeatureProps } from "./presentation";
@@ -0,0 +1,209 @@
1
+ /**
2
+ * AIHugFeature Component
3
+ * Self-contained AI hug feature UI component
4
+ * Uses hook internally, only requires config and translations
5
+ */
6
+
7
+ import React, { useCallback } from "react";
8
+ import { View, ScrollView, StyleSheet, Image, Dimensions } from "react-native";
9
+ import {
10
+ useAppDesignTokens,
11
+ AtomicText,
12
+ AtomicButton,
13
+ } from "@umituz/react-native-design-system";
14
+ import { DualImagePicker } from "../../../../presentation/components/image-picker/DualImagePicker";
15
+ import { ErrorDisplay } from "../../../../presentation/components/display/ErrorDisplay";
16
+ import { useAIHugFeature } from "../hooks";
17
+ import type {
18
+ AIHugTranslations,
19
+ AIHugFeatureConfig,
20
+ } from "../../domain/types";
21
+
22
+ export interface AIHugFeatureProps {
23
+ /** Feature configuration with provider-specific settings */
24
+ config: AIHugFeatureConfig;
25
+ /** User ID for the generation request */
26
+ userId: string;
27
+ /** Translations for all UI text */
28
+ translations: AIHugTranslations;
29
+ /** Source image picker callback */
30
+ onSelectSourceImage: () => Promise<string | null>;
31
+ /** Target image picker callback */
32
+ onSelectTargetImage: () => Promise<string | null>;
33
+ /** Save image callback */
34
+ onSaveImage: (imageUrl: string) => Promise<void>;
35
+ /** Optional custom processing modal renderer */
36
+ renderProcessingModal?: (props: {
37
+ visible: boolean;
38
+ progress: number;
39
+ }) => React.ReactNode;
40
+ }
41
+
42
+ export const AIHugFeature: React.FC<AIHugFeatureProps> = ({
43
+ config,
44
+ userId,
45
+ translations,
46
+ onSelectSourceImage,
47
+ onSelectTargetImage,
48
+ onSaveImage,
49
+ renderProcessingModal,
50
+ }) => {
51
+ const tokens = useAppDesignTokens();
52
+
53
+ const feature = useAIHugFeature({
54
+ config,
55
+ userId,
56
+ onSelectSourceImage,
57
+ onSelectTargetImage,
58
+ onSaveImage,
59
+ });
60
+
61
+ const handleProcess = useCallback(() => {
62
+ void feature.process();
63
+ }, [feature]);
64
+
65
+ const handleSave = useCallback(() => {
66
+ void feature.save();
67
+ }, [feature]);
68
+
69
+ const handleSelectSource = useCallback(() => {
70
+ void feature.selectSourceImage();
71
+ }, [feature]);
72
+
73
+ const handleSelectTarget = useCallback(() => {
74
+ void feature.selectTargetImage();
75
+ }, [feature]);
76
+
77
+ const canProcess = feature.sourceImageUri && feature.targetImageUri && !feature.isProcessing;
78
+
79
+ if (feature.processedUrl) {
80
+ const screenWidth = Dimensions.get("window").width;
81
+ const imageSize = screenWidth - 48;
82
+
83
+ return (
84
+ <ScrollView
85
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
86
+ contentContainerStyle={styles.content}
87
+ showsVerticalScrollIndicator={false}
88
+ >
89
+ <AtomicText
90
+ type="headlineMedium"
91
+ style={[styles.successText, { color: tokens.colors.success }]}
92
+ >
93
+ {translations.successText}
94
+ </AtomicText>
95
+
96
+ <View style={styles.resultImageContainer}>
97
+ <Image
98
+ source={{ uri: feature.processedUrl }}
99
+ style={[styles.resultImage, { width: imageSize, height: imageSize }]}
100
+ resizeMode="contain"
101
+ />
102
+ </View>
103
+
104
+ <View style={styles.resultActions}>
105
+ <AtomicButton
106
+ title={translations.saveButtonText}
107
+ onPress={handleSave}
108
+ variant="primary"
109
+ size="lg"
110
+ />
111
+ <AtomicButton
112
+ title={translations.tryAnotherText}
113
+ onPress={feature.reset}
114
+ variant="secondary"
115
+ size="lg"
116
+ />
117
+ </View>
118
+ </ScrollView>
119
+ );
120
+ }
121
+
122
+ return (
123
+ <>
124
+ <ScrollView
125
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
126
+ contentContainerStyle={styles.content}
127
+ showsVerticalScrollIndicator={false}
128
+ >
129
+ <AtomicText
130
+ type="bodyLarge"
131
+ style={[styles.description, { color: tokens.colors.textSecondary }]}
132
+ >
133
+ {translations.description}
134
+ </AtomicText>
135
+
136
+ <View style={styles.pickerContainer}>
137
+ <DualImagePicker
138
+ sourceImageUri={feature.sourceImageUri}
139
+ targetImageUri={feature.targetImageUri}
140
+ isDisabled={feature.isProcessing}
141
+ onSelectSource={handleSelectSource}
142
+ onSelectTarget={handleSelectTarget}
143
+ sourcePlaceholder={translations.sourceUploadTitle}
144
+ targetPlaceholder={translations.targetUploadTitle}
145
+ layout="horizontal"
146
+ variant="portrait"
147
+ />
148
+ </View>
149
+
150
+ <ErrorDisplay error={feature.error} />
151
+
152
+ <View style={styles.buttonContainer}>
153
+ <AtomicButton
154
+ title={
155
+ feature.isProcessing
156
+ ? translations.processingText
157
+ : translations.processButtonText
158
+ }
159
+ onPress={handleProcess}
160
+ disabled={!canProcess}
161
+ variant="primary"
162
+ size="lg"
163
+ />
164
+ </View>
165
+ </ScrollView>
166
+
167
+ {renderProcessingModal?.({ visible: feature.isProcessing, progress: feature.progress })}
168
+ </>
169
+ );
170
+ };
171
+
172
+ const styles = StyleSheet.create({
173
+ container: {
174
+ flex: 1,
175
+ },
176
+ content: {
177
+ paddingVertical: 16,
178
+ },
179
+ description: {
180
+ textAlign: "center",
181
+ marginHorizontal: 24,
182
+ marginBottom: 24,
183
+ lineHeight: 24,
184
+ },
185
+ pickerContainer: {
186
+ marginHorizontal: 16,
187
+ marginBottom: 16,
188
+ },
189
+ successText: {
190
+ textAlign: "center",
191
+ marginBottom: 24,
192
+ },
193
+ resultImageContainer: {
194
+ alignItems: "center",
195
+ marginHorizontal: 24,
196
+ marginBottom: 24,
197
+ },
198
+ resultImage: {
199
+ borderRadius: 16,
200
+ },
201
+ resultActions: {
202
+ marginHorizontal: 24,
203
+ gap: 12,
204
+ },
205
+ buttonContainer: {
206
+ marginHorizontal: 24,
207
+ marginTop: 8,
208
+ },
209
+ });
@@ -0,0 +1,2 @@
1
+ export { AIHugFeature } from "./AIHugFeature";
2
+ export type { AIHugFeatureProps } from "./AIHugFeature";
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from "./hooks";
6
+ export * from "./components";
@@ -25,3 +25,7 @@ export type {
25
25
  UseAIKissFeatureProps,
26
26
  UseAIKissFeatureReturn,
27
27
  } from "./presentation";
28
+
29
+ // Presentation Components
30
+ export { AIKissFeature } from "./presentation";
31
+ export type { AIKissFeatureProps } from "./presentation";
@@ -0,0 +1,202 @@
1
+ /**
2
+ * AIKissFeature Component
3
+ * Self-contained AI kiss feature UI component
4
+ * Uses hook internally, only requires config and translations
5
+ */
6
+
7
+ import React, { useCallback } from "react";
8
+ import { View, ScrollView, StyleSheet, Image, Dimensions } from "react-native";
9
+ import {
10
+ useAppDesignTokens,
11
+ AtomicText,
12
+ AtomicButton,
13
+ } from "@umituz/react-native-design-system";
14
+ import { DualImagePicker } from "../../../../presentation/components/image-picker/DualImagePicker";
15
+ import { ErrorDisplay } from "../../../../presentation/components/display/ErrorDisplay";
16
+ import { useAIKissFeature } from "../hooks";
17
+ import type {
18
+ AIKissTranslations,
19
+ AIKissFeatureConfig,
20
+ } from "../../domain/types";
21
+
22
+ export interface AIKissFeatureProps {
23
+ config: AIKissFeatureConfig;
24
+ userId: string;
25
+ translations: AIKissTranslations;
26
+ onSelectSourceImage: () => Promise<string | null>;
27
+ onSelectTargetImage: () => Promise<string | null>;
28
+ onSaveImage: (imageUrl: string) => Promise<void>;
29
+ renderProcessingModal?: (props: {
30
+ visible: boolean;
31
+ progress: number;
32
+ }) => React.ReactNode;
33
+ }
34
+
35
+ export const AIKissFeature: React.FC<AIKissFeatureProps> = ({
36
+ config,
37
+ userId,
38
+ translations,
39
+ onSelectSourceImage,
40
+ onSelectTargetImage,
41
+ onSaveImage,
42
+ renderProcessingModal,
43
+ }) => {
44
+ const tokens = useAppDesignTokens();
45
+
46
+ const feature = useAIKissFeature({
47
+ config,
48
+ userId,
49
+ onSelectSourceImage,
50
+ onSelectTargetImage,
51
+ onSaveImage,
52
+ });
53
+
54
+ const handleProcess = useCallback(() => {
55
+ void feature.process();
56
+ }, [feature]);
57
+
58
+ const handleSave = useCallback(() => {
59
+ void feature.save();
60
+ }, [feature]);
61
+
62
+ const handleSelectSource = useCallback(() => {
63
+ void feature.selectSourceImage();
64
+ }, [feature]);
65
+
66
+ const handleSelectTarget = useCallback(() => {
67
+ void feature.selectTargetImage();
68
+ }, [feature]);
69
+
70
+ const canProcess = feature.sourceImageUri && feature.targetImageUri && !feature.isProcessing;
71
+
72
+ if (feature.processedUrl) {
73
+ const screenWidth = Dimensions.get("window").width;
74
+ const imageSize = screenWidth - 48;
75
+
76
+ return (
77
+ <ScrollView
78
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
79
+ contentContainerStyle={styles.content}
80
+ showsVerticalScrollIndicator={false}
81
+ >
82
+ <AtomicText
83
+ type="headlineMedium"
84
+ style={[styles.successText, { color: tokens.colors.success }]}
85
+ >
86
+ {translations.successText}
87
+ </AtomicText>
88
+
89
+ <View style={styles.resultImageContainer}>
90
+ <Image
91
+ source={{ uri: feature.processedUrl }}
92
+ style={[styles.resultImage, { width: imageSize, height: imageSize }]}
93
+ resizeMode="contain"
94
+ />
95
+ </View>
96
+
97
+ <View style={styles.resultActions}>
98
+ <AtomicButton
99
+ title={translations.saveButtonText}
100
+ onPress={handleSave}
101
+ variant="primary"
102
+ size="lg"
103
+ />
104
+ <AtomicButton
105
+ title={translations.tryAnotherText}
106
+ onPress={feature.reset}
107
+ variant="secondary"
108
+ size="lg"
109
+ />
110
+ </View>
111
+ </ScrollView>
112
+ );
113
+ }
114
+
115
+ return (
116
+ <>
117
+ <ScrollView
118
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
119
+ contentContainerStyle={styles.content}
120
+ showsVerticalScrollIndicator={false}
121
+ >
122
+ <AtomicText
123
+ type="bodyLarge"
124
+ style={[styles.description, { color: tokens.colors.textSecondary }]}
125
+ >
126
+ {translations.description}
127
+ </AtomicText>
128
+
129
+ <View style={styles.pickerContainer}>
130
+ <DualImagePicker
131
+ sourceImageUri={feature.sourceImageUri}
132
+ targetImageUri={feature.targetImageUri}
133
+ isDisabled={feature.isProcessing}
134
+ onSelectSource={handleSelectSource}
135
+ onSelectTarget={handleSelectTarget}
136
+ sourcePlaceholder={translations.sourceUploadTitle}
137
+ targetPlaceholder={translations.targetUploadTitle}
138
+ layout="horizontal"
139
+ variant="portrait"
140
+ />
141
+ </View>
142
+
143
+ <ErrorDisplay error={feature.error} />
144
+
145
+ <View style={styles.buttonContainer}>
146
+ <AtomicButton
147
+ title={
148
+ feature.isProcessing
149
+ ? translations.processingText
150
+ : translations.processButtonText
151
+ }
152
+ onPress={handleProcess}
153
+ disabled={!canProcess}
154
+ variant="primary"
155
+ size="lg"
156
+ />
157
+ </View>
158
+ </ScrollView>
159
+
160
+ {renderProcessingModal?.({ visible: feature.isProcessing, progress: feature.progress })}
161
+ </>
162
+ );
163
+ };
164
+
165
+ const styles = StyleSheet.create({
166
+ container: {
167
+ flex: 1,
168
+ },
169
+ content: {
170
+ paddingVertical: 16,
171
+ },
172
+ description: {
173
+ textAlign: "center",
174
+ marginHorizontal: 24,
175
+ marginBottom: 24,
176
+ lineHeight: 24,
177
+ },
178
+ pickerContainer: {
179
+ marginHorizontal: 16,
180
+ marginBottom: 16,
181
+ },
182
+ successText: {
183
+ textAlign: "center",
184
+ marginBottom: 24,
185
+ },
186
+ resultImageContainer: {
187
+ alignItems: "center",
188
+ marginHorizontal: 24,
189
+ marginBottom: 24,
190
+ },
191
+ resultImage: {
192
+ borderRadius: 16,
193
+ },
194
+ resultActions: {
195
+ marginHorizontal: 24,
196
+ gap: 12,
197
+ },
198
+ buttonContainer: {
199
+ marginHorizontal: 24,
200
+ marginTop: 8,
201
+ },
202
+ });
@@ -0,0 +1,2 @@
1
+ export { AIKissFeature } from "./AIKissFeature";
2
+ export type { AIKissFeatureProps } from "./AIKissFeature";
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from "./hooks";
6
+ export * from "./components";
@@ -26,3 +26,7 @@ export type {
26
26
  UseAnimeSelfieFeatureProps,
27
27
  UseAnimeSelfieFeatureReturn,
28
28
  } from "./presentation";
29
+
30
+ // Presentation Components
31
+ export { AnimeSelfieFeature } from "./presentation";
32
+ export type { AnimeSelfieFeatureProps } from "./presentation";
@@ -0,0 +1,199 @@
1
+ /**
2
+ * AnimeSelfieFeature Component
3
+ * Self-contained anime selfie feature UI component
4
+ * Uses hook internally, only requires config and translations
5
+ */
6
+
7
+ import React, { useCallback, useMemo } from "react";
8
+ import { View, ScrollView, StyleSheet, Image, Dimensions } from "react-native";
9
+ import {
10
+ useAppDesignTokens,
11
+ AtomicText,
12
+ AtomicButton,
13
+ } from "@umituz/react-native-design-system";
14
+ import { PhotoUploadCard } from "../../../../presentation/components/PhotoUploadCard";
15
+ import { ErrorDisplay } from "../../../../presentation/components/display/ErrorDisplay";
16
+ import { useAnimeSelfieFeature } from "../hooks";
17
+ import type {
18
+ AnimeSelfieTranslations,
19
+ AnimeSelfieFeatureConfig,
20
+ } from "../../domain/types";
21
+
22
+ export interface AnimeSelfieFeatureProps {
23
+ config: AnimeSelfieFeatureConfig;
24
+ userId: string;
25
+ translations: AnimeSelfieTranslations;
26
+ onSelectImage: () => Promise<string | null>;
27
+ onSaveImage: (imageUrl: string) => Promise<void>;
28
+ renderProcessingModal?: (props: {
29
+ visible: boolean;
30
+ progress: number;
31
+ }) => React.ReactNode;
32
+ }
33
+
34
+ export const AnimeSelfieFeature: React.FC<AnimeSelfieFeatureProps> = ({
35
+ config,
36
+ userId,
37
+ translations,
38
+ onSelectImage,
39
+ onSaveImage,
40
+ renderProcessingModal,
41
+ }) => {
42
+ const tokens = useAppDesignTokens();
43
+
44
+ const feature = useAnimeSelfieFeature({
45
+ config,
46
+ userId,
47
+ onSelectImage,
48
+ onSaveImage,
49
+ });
50
+
51
+ const photoTranslations = useMemo(
52
+ () => ({
53
+ tapToUpload: translations.uploadTitle,
54
+ selectPhoto: translations.uploadSubtitle,
55
+ change: translations.uploadChange,
56
+ analyzing: translations.uploadAnalyzing,
57
+ }),
58
+ [translations],
59
+ );
60
+
61
+ const handleProcess = useCallback(() => {
62
+ void feature.process();
63
+ }, [feature]);
64
+
65
+ const handleSave = useCallback(() => {
66
+ void feature.save();
67
+ }, [feature]);
68
+
69
+ const handleSelectImage = useCallback(() => {
70
+ void feature.selectImage();
71
+ }, [feature]);
72
+
73
+ if (feature.processedUrl) {
74
+ const screenWidth = Dimensions.get("window").width;
75
+ const imageSize = screenWidth - 48;
76
+
77
+ return (
78
+ <ScrollView
79
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
80
+ contentContainerStyle={styles.content}
81
+ showsVerticalScrollIndicator={false}
82
+ >
83
+ <AtomicText
84
+ type="headlineMedium"
85
+ style={[styles.successText, { color: tokens.colors.success }]}
86
+ >
87
+ {translations.successText}
88
+ </AtomicText>
89
+
90
+ <View style={styles.resultImageContainer}>
91
+ <Image
92
+ source={{ uri: feature.processedUrl }}
93
+ style={[styles.resultImage, { width: imageSize, height: imageSize }]}
94
+ resizeMode="contain"
95
+ />
96
+ </View>
97
+
98
+ <View style={styles.resultActions}>
99
+ <AtomicButton
100
+ title={translations.saveButtonText}
101
+ onPress={handleSave}
102
+ variant="primary"
103
+ size="lg"
104
+ />
105
+ <AtomicButton
106
+ title={translations.tryAnotherText}
107
+ onPress={feature.reset}
108
+ variant="secondary"
109
+ size="lg"
110
+ />
111
+ </View>
112
+ </ScrollView>
113
+ );
114
+ }
115
+
116
+ return (
117
+ <>
118
+ <ScrollView
119
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
120
+ contentContainerStyle={styles.content}
121
+ showsVerticalScrollIndicator={false}
122
+ >
123
+ <AtomicText
124
+ type="bodyLarge"
125
+ style={[styles.description, { color: tokens.colors.textSecondary }]}
126
+ >
127
+ {translations.description}
128
+ </AtomicText>
129
+
130
+ <PhotoUploadCard
131
+ imageUri={feature.imageUri}
132
+ onPress={handleSelectImage}
133
+ isValidating={feature.isProcessing}
134
+ disabled={feature.isProcessing}
135
+ translations={photoTranslations}
136
+ config={{
137
+ aspectRatio: 1,
138
+ borderRadius: 24,
139
+ showValidationStatus: false,
140
+ allowChange: true,
141
+ }}
142
+ />
143
+
144
+ <ErrorDisplay error={feature.error} />
145
+
146
+ <View style={styles.buttonContainer}>
147
+ <AtomicButton
148
+ title={
149
+ feature.isProcessing
150
+ ? translations.processingText
151
+ : translations.processButtonText
152
+ }
153
+ onPress={handleProcess}
154
+ disabled={!feature.imageUri || feature.isProcessing}
155
+ variant="primary"
156
+ size="lg"
157
+ />
158
+ </View>
159
+ </ScrollView>
160
+
161
+ {renderProcessingModal?.({ visible: feature.isProcessing, progress: feature.progress })}
162
+ </>
163
+ );
164
+ };
165
+
166
+ const styles = StyleSheet.create({
167
+ container: {
168
+ flex: 1,
169
+ },
170
+ content: {
171
+ paddingVertical: 16,
172
+ },
173
+ description: {
174
+ textAlign: "center",
175
+ marginHorizontal: 24,
176
+ marginBottom: 24,
177
+ lineHeight: 24,
178
+ },
179
+ successText: {
180
+ textAlign: "center",
181
+ marginBottom: 24,
182
+ },
183
+ resultImageContainer: {
184
+ alignItems: "center",
185
+ marginHorizontal: 24,
186
+ marginBottom: 24,
187
+ },
188
+ resultImage: {
189
+ borderRadius: 16,
190
+ },
191
+ resultActions: {
192
+ marginHorizontal: 24,
193
+ gap: 12,
194
+ },
195
+ buttonContainer: {
196
+ marginHorizontal: 24,
197
+ marginTop: 8,
198
+ },
199
+ });
@@ -0,0 +1,2 @@
1
+ export { AnimeSelfieFeature } from "./AnimeSelfieFeature";
2
+ export type { AnimeSelfieFeatureProps } from "./AnimeSelfieFeature";
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from "./hooks";
6
+ export * from "./components";
@@ -25,3 +25,7 @@ export type {
25
25
  UseFaceSwapFeatureProps,
26
26
  UseFaceSwapFeatureReturn,
27
27
  } from "./presentation";
28
+
29
+ // Presentation Components
30
+ export { FaceSwapFeature } from "./presentation";
31
+ export type { FaceSwapFeatureProps } from "./presentation";