@umituz/react-native-ai-generation-content 1.17.15 → 1.17.17

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 (30) hide show
  1. package/package.json +2 -1
  2. package/src/domains/creations/presentation/components/CreationCard.tsx +2 -1
  3. package/src/domains/creations/presentation/components/CreationDetail/DetailVideo.tsx +123 -0
  4. package/src/domains/creations/presentation/components/CreationDetail/index.ts +1 -0
  5. package/src/domains/creations/presentation/screens/CreationDetailScreen.tsx +24 -4
  6. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +89 -57
  7. package/src/features/text-to-image/domain/constants/index.ts +14 -0
  8. package/src/features/text-to-image/domain/constants/options.constants.ts +42 -0
  9. package/src/features/text-to-image/domain/constants/styles.constants.ts +34 -0
  10. package/src/features/text-to-image/domain/index.ts +6 -0
  11. package/src/features/text-to-image/domain/types/config.types.ts +71 -0
  12. package/src/features/text-to-image/domain/types/form.types.ts +58 -0
  13. package/src/features/text-to-image/domain/types/index.ts +29 -1
  14. package/src/features/text-to-image/domain/types/text-to-image.types.ts +0 -8
  15. package/src/features/text-to-image/index.ts +92 -4
  16. package/src/features/text-to-image/presentation/components/AspectRatioSelector.tsx +98 -0
  17. package/src/features/text-to-image/presentation/components/ExamplePrompts.tsx +88 -0
  18. package/src/features/text-to-image/presentation/components/ImageSizeSelector.tsx +98 -0
  19. package/src/features/text-to-image/presentation/components/NumImagesSelector.tsx +93 -0
  20. package/src/features/text-to-image/presentation/components/OutputFormatSelector.tsx +98 -0
  21. package/src/features/text-to-image/presentation/components/PromptInput.tsx +90 -0
  22. package/src/features/text-to-image/presentation/components/SettingsSheet.tsx +139 -0
  23. package/src/features/text-to-image/presentation/components/StyleSelector.tsx +110 -0
  24. package/src/features/text-to-image/presentation/components/TextToImageGenerateButton.tsx +84 -0
  25. package/src/features/text-to-image/presentation/components/index.ts +41 -0
  26. package/src/features/text-to-image/presentation/hooks/index.ts +25 -0
  27. package/src/features/text-to-image/presentation/hooks/useFormState.ts +103 -0
  28. package/src/features/text-to-image/presentation/hooks/useGeneration.ts +99 -0
  29. package/src/features/text-to-image/presentation/hooks/useTextToImageForm.ts +58 -0
  30. package/src/features/text-to-image/presentation/index.ts +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.17.15",
3
+ "version": "1.17.17",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -84,6 +84,7 @@
84
84
  "expo-haptics": "^15.0.8",
85
85
  "expo-image": "^3.0.11",
86
86
  "expo-linear-gradient": "~15.0.7",
87
+ "expo-video": "^2.0.0",
87
88
  "expo-localization": "^17.0.8",
88
89
  "expo-sharing": "^14.0.8",
89
90
  "firebase": "^12.6.0",
@@ -82,7 +82,8 @@ export function CreationCard({
82
82
  }: CreationCardProps) {
83
83
  const tokens = useAppDesignTokens();
84
84
  // Support both output object and direct uri
85
- const previewUrl = creation.uri || getPreviewUrl(creation.output);
85
+ // Prefer getPreviewUrl (which returns thumbnailUrl first) over direct uri
86
+ const previewUrl = getPreviewUrl(creation.output) || creation.uri;
86
87
  const title = getCreationTitle(creation.prompt, creation.type as CreationTypeId);
87
88
 
88
89
  // Format date
@@ -0,0 +1,123 @@
1
+ /**
2
+ * DetailVideo Component
3
+ * Video player with thumbnail and play controls for creation detail view
4
+ */
5
+
6
+ import React, { useState, useCallback } from "react";
7
+ import { View, StyleSheet, Dimensions, TouchableOpacity } from "react-native";
8
+ import {
9
+ useAppDesignTokens,
10
+ AtomicIcon,
11
+ type DesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+ import { Image } from "expo-image";
14
+ import { useVideoPlayer, VideoView } from "expo-video";
15
+
16
+ interface DetailVideoProps {
17
+ readonly videoUrl: string;
18
+ readonly thumbnailUrl?: string;
19
+ }
20
+
21
+ const { width } = Dimensions.get("window");
22
+
23
+ export const DetailVideo: React.FC<DetailVideoProps> = ({
24
+ videoUrl,
25
+ thumbnailUrl,
26
+ }) => {
27
+ const tokens = useAppDesignTokens();
28
+ const styles = useStyles(tokens);
29
+ const [isPlaying, setIsPlaying] = useState(false);
30
+
31
+ const player = useVideoPlayer(videoUrl, (p) => {
32
+ p.loop = true;
33
+ });
34
+
35
+ const handlePlay = useCallback(() => {
36
+ setIsPlaying(true);
37
+ player.play();
38
+ }, [player]);
39
+
40
+ return (
41
+ <View style={styles.container}>
42
+ <View style={styles.frame}>
43
+ {isPlaying ? (
44
+ <VideoView
45
+ player={player}
46
+ style={styles.video}
47
+ contentFit="cover"
48
+ nativeControls
49
+ />
50
+ ) : (
51
+ <TouchableOpacity
52
+ style={styles.thumbnailContainer}
53
+ onPress={handlePlay}
54
+ activeOpacity={0.8}
55
+ >
56
+ {thumbnailUrl ? (
57
+ <Image
58
+ source={{ uri: thumbnailUrl }}
59
+ style={styles.thumbnail}
60
+ contentFit="cover"
61
+ />
62
+ ) : (
63
+ <View style={styles.placeholder} />
64
+ )}
65
+ <View style={styles.playButtonContainer}>
66
+ <View style={styles.playButton}>
67
+ <AtomicIcon name="play" customSize={32} color="onPrimary" />
68
+ </View>
69
+ </View>
70
+ </TouchableOpacity>
71
+ )}
72
+ </View>
73
+ </View>
74
+ );
75
+ };
76
+
77
+ const useStyles = (tokens: DesignTokens) =>
78
+ StyleSheet.create({
79
+ container: {
80
+ paddingHorizontal: tokens.spacing.lg,
81
+ marginVertical: tokens.spacing.lg,
82
+ },
83
+ frame: {
84
+ width: width - tokens.spacing.lg * 2,
85
+ height: width - tokens.spacing.lg * 2,
86
+ borderRadius: 24,
87
+ overflow: "hidden",
88
+ backgroundColor: tokens.colors.surface,
89
+ },
90
+ video: {
91
+ width: "100%",
92
+ height: "100%",
93
+ },
94
+ thumbnailContainer: {
95
+ width: "100%",
96
+ height: "100%",
97
+ justifyContent: "center",
98
+ alignItems: "center",
99
+ },
100
+ thumbnail: {
101
+ width: "100%",
102
+ height: "100%",
103
+ },
104
+ placeholder: {
105
+ width: "100%",
106
+ height: "100%",
107
+ backgroundColor: tokens.colors.surfaceSecondary,
108
+ },
109
+ playButtonContainer: {
110
+ ...StyleSheet.absoluteFillObject,
111
+ justifyContent: "center",
112
+ alignItems: "center",
113
+ },
114
+ playButton: {
115
+ width: 64,
116
+ height: 64,
117
+ borderRadius: 32,
118
+ backgroundColor: tokens.colors.primary,
119
+ justifyContent: "center",
120
+ alignItems: "center",
121
+ paddingLeft: 4,
122
+ },
123
+ });
@@ -1,4 +1,5 @@
1
1
  export * from './DetailHeader';
2
2
  export * from './DetailImage';
3
+ export * from './DetailVideo';
3
4
  export * from './DetailStory';
4
5
  export * from './DetailActions';
@@ -1,14 +1,19 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import { StyleSheet } from 'react-native';
3
3
  import { useAppDesignTokens, type DesignTokens, ScreenLayout } from "@umituz/react-native-design-system";
4
4
  import type { Creation } from '../../domain/entities/Creation';
5
+ import { hasVideoContent, getPreviewUrl } from '../../domain/utils';
5
6
  import { DetailHeader } from '../components/CreationDetail/DetailHeader';
6
7
  import { DetailImage } from '../components/CreationDetail/DetailImage';
8
+ import { DetailVideo } from '../components/CreationDetail/DetailVideo';
7
9
  import { DetailStory } from '../components/CreationDetail/DetailStory';
8
10
  import { DetailActions } from '../components/CreationDetail/DetailActions';
9
11
 
10
12
  import { useCreationsProvider } from '../components/CreationsProvider';
11
13
 
14
+ /** Video creation types */
15
+ const VIDEO_TYPES = ['text-to-video', 'image-to-video'] as const;
16
+
12
17
  interface CreationDetailScreenProps {
13
18
  readonly creation: Creation;
14
19
  readonly onClose: () => void;
@@ -45,8 +50,19 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
45
50
  const story = metadata.story || metadata.description || "";
46
51
  const date = metadata.date || new Date(creation.createdAt).toLocaleDateString();
47
52
 
53
+ // Detect if this is a video creation
54
+ const isVideo = useMemo(() => {
55
+ if (VIDEO_TYPES.includes(creation.type as typeof VIDEO_TYPES[number])) return true;
56
+ if (hasVideoContent(creation.output)) return true;
57
+ return false;
58
+ }, [creation.type, creation.output]);
59
+
48
60
  const styles = useStyles(tokens);
49
61
 
62
+ // Get video URL and thumbnail for video content
63
+ const videoUrl = creation.output?.videoUrl || creation.uri;
64
+ const thumbnailUrl = getPreviewUrl(creation.output) || undefined;
65
+
50
66
  return (
51
67
  <ScreenLayout
52
68
  scrollable={true}
@@ -61,7 +77,11 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
61
77
  }
62
78
  contentContainerStyle={styles.scrollContent}
63
79
  >
64
- <DetailImage uri={creation.uri} />
80
+ {isVideo ? (
81
+ <DetailVideo videoUrl={videoUrl} thumbnailUrl={thumbnailUrl} />
82
+ ) : (
83
+ <DetailImage uri={creation.uri} />
84
+ )}
65
85
 
66
86
  {story ? (
67
87
  <DetailStory story={story} />
@@ -70,8 +90,8 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
70
90
  <DetailActions
71
91
  onShare={() => onShare(creation)}
72
92
  onDelete={() => onDelete(creation)}
73
- shareLabel={t("result.shareButton") || "Share"}
74
- deleteLabel={t("common.delete") || "Delete"}
93
+ shareLabel={t("result.shareButton")}
94
+ deleteLabel={t("common.delete")}
75
95
  />
76
96
  </ScreenLayout>
77
97
  );
@@ -1,6 +1,8 @@
1
1
  declare const __DEV__: boolean;
2
2
 
3
3
  import React, { useMemo, useCallback, useState } from "react";
4
+ import { View, FlatList, RefreshControl, StyleSheet } from "react-native";
5
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
4
6
  import {
5
7
  useAppDesignTokens,
6
8
  useAlert,
@@ -9,7 +11,7 @@ import {
9
11
  useSharing,
10
12
  FilterBottomSheet,
11
13
  type BottomSheetModalRef,
12
- ScreenLayout,
14
+ type DesignTokens,
13
15
  } from "@umituz/react-native-design-system";
14
16
  import { useFocusEffect } from "@react-navigation/native";
15
17
  import { useCreations } from "../hooks/useCreations";
@@ -17,7 +19,7 @@ import { useDeleteCreation } from "../hooks/useDeleteCreation";
17
19
  import { useCreationsFilter } from "../hooks/useCreationsFilter";
18
20
  import {
19
21
  GalleryHeader,
20
- CreationsGrid,
22
+ CreationCard,
21
23
  CreationImageViewer,
22
24
  GalleryEmptyStates,
23
25
  } from "../components";
@@ -26,6 +28,7 @@ import type { Creation } from "../../domain/entities/Creation";
26
28
  import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
27
29
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
28
30
  import { CreationDetailScreen } from "./CreationDetailScreen";
31
+ import { CreationsProvider } from "../components/CreationsProvider";
29
32
 
30
33
  interface CreationsGalleryScreenProps {
31
34
  readonly userId: string | null;
@@ -40,8 +43,6 @@ interface CreationsGalleryScreenProps {
40
43
  readonly showFilter?: boolean;
41
44
  }
42
45
 
43
- import { CreationsProvider } from "../components/CreationsProvider";
44
-
45
46
  export function CreationsGalleryScreen(props: CreationsGalleryScreenProps) {
46
47
  return (
47
48
  <CreationsProvider config={props.config} t={props.t}>
@@ -62,6 +63,7 @@ function CreationsGalleryScreenContent({
62
63
  emptyActionLabel,
63
64
  showFilter = config.showFilter ?? true,
64
65
  }: CreationsGalleryScreenProps) {
66
+ const insets = useSafeAreaInsets();
65
67
  const tokens = useAppDesignTokens();
66
68
  const { share } = useSharing();
67
69
  const alert = useAlert();
@@ -76,14 +78,12 @@ function CreationsGalleryScreenContent({
76
78
  const deleteMutation = useDeleteCreation({ userId, repository });
77
79
  const { filtered, selectedIds, toggleFilter, clearFilters, isFiltered } = useCreationsFilter({ creations });
78
80
 
79
- // Refetch creations when screen comes into focus
80
81
  useFocusEffect(
81
82
  useCallback(() => {
82
83
  void refetch();
83
84
  }, [refetch])
84
85
  );
85
86
 
86
- // Prepare data for UI using utils
87
87
  const allCategories = useMemo(
88
88
  () => getFilterCategoriesFromConfig(config, t),
89
89
  [config, t],
@@ -118,27 +118,62 @@ function CreationsGalleryScreenContent({
118
118
  );
119
119
  }, [alert, config, deleteMutation, t]);
120
120
 
121
- // Handle viewing a creation - shows detail screen
122
121
  const handleView = useCallback((creation: Creation) => {
123
122
  setSelectedCreation(creation);
124
123
  }, []);
125
124
 
126
- // Handle favorite toggle
127
125
  const handleFavorite = useCallback((creation: Creation, isFavorite: boolean) => {
128
126
  void (async () => {
129
127
  if (!userId) return;
130
- const success = await repository.updateFavorite(
131
- userId,
132
- creation.id,
133
- isFavorite,
134
- );
128
+ const success = await repository.updateFavorite(userId, creation.id, isFavorite);
135
129
  if (success) {
136
130
  void refetch();
137
131
  }
138
132
  })();
139
133
  }, [userId, repository, refetch]);
140
134
 
141
- const renderEmptyComponent = useMemo(() => (
135
+ const styles = useStyles(tokens, insets);
136
+
137
+ const renderItem = useCallback(
138
+ ({ item }: { item: Creation }) => (
139
+ <CreationCard
140
+ creation={item}
141
+ callbacks={{
142
+ onPress: () => handleView(item),
143
+ onShare: async () => handleShare(item),
144
+ onDelete: () => handleDelete(item),
145
+ onFavorite: () => handleFavorite(item, !item.isFavorite),
146
+ }}
147
+ />
148
+ ),
149
+ [handleView, handleShare, handleDelete, handleFavorite]
150
+ );
151
+
152
+ const renderHeader = useMemo(() => {
153
+ if ((!creations || creations.length === 0) && !isLoading) return null;
154
+
155
+ return (
156
+ <View style={styles.header}>
157
+ <GalleryHeader
158
+ title={t(config.translations.title)}
159
+ count={filtered.length}
160
+ countLabel={t(config.translations.photoCount)}
161
+ isFiltered={isFiltered}
162
+ showFilter={showFilter}
163
+ filterLabel={t(config.translations.filterLabel)}
164
+ onFilterPress={() => {
165
+ if (__DEV__) {
166
+ // eslint-disable-next-line no-console
167
+ console.log('[CreationsGallery] Filter button pressed');
168
+ }
169
+ filterSheetRef.current?.present();
170
+ }}
171
+ />
172
+ </View>
173
+ );
174
+ }, [creations, isLoading, filtered.length, isFiltered, showFilter, t, config, styles.header]);
175
+
176
+ const renderEmpty = useMemo(() => (
142
177
  <GalleryEmptyStates
143
178
  isLoading={isLoading}
144
179
  creations={creations}
@@ -166,48 +201,25 @@ function CreationsGalleryScreenContent({
166
201
  }
167
202
 
168
203
  return (
169
- <ScreenLayout
170
- scrollable={false}
171
- edges={["top"]}
172
- backgroundColor={tokens.colors.background}
173
- header={
174
- (!creations || creations?.length === 0) && !isLoading ? null : (
175
- <GalleryHeader
176
- title={t(config.translations.title) || 'My Creations'}
177
- count={filtered.length}
178
- countLabel={t(config.translations.photoCount) || 'photos'}
179
- isFiltered={isFiltered}
180
- showFilter={showFilter}
181
- filterLabel={t(config.translations.filterLabel) || 'Filter'}
182
- onFilterPress={() => {
183
- if (__DEV__) {
184
- // eslint-disable-next-line no-console
185
- console.log('[CreationsGallery] Filter button pressed');
186
- // eslint-disable-next-line no-console
187
- console.log('[CreationsGallery] filterSheetRef.current:', filterSheetRef.current);
188
- // eslint-disable-next-line no-console
189
- console.log('[CreationsGallery] allCategories:', allCategories);
190
- }
191
- filterSheetRef.current?.present();
192
- }}
204
+ <View style={[styles.container, { backgroundColor: tokens.colors.background }]}>
205
+ <FlatList
206
+ data={filtered}
207
+ renderItem={renderItem}
208
+ keyExtractor={(item) => item.id}
209
+ ListHeaderComponent={renderHeader}
210
+ ListEmptyComponent={renderEmpty}
211
+ contentContainerStyle={[
212
+ styles.listContent,
213
+ (!filtered || filtered.length === 0) && styles.emptyContent,
214
+ ]}
215
+ showsVerticalScrollIndicator={false}
216
+ refreshControl={
217
+ <RefreshControl
218
+ refreshing={isLoading}
219
+ onRefresh={() => void refetch()}
220
+ tintColor={tokens.colors.primary}
193
221
  />
194
- )
195
- }
196
- >
197
- {/* Main Content Grid - handles empty/loading via ListEmptyComponent */}
198
- <CreationsGrid
199
- creations={filtered}
200
- isLoading={isLoading}
201
- onRefresh={() => void refetch()}
202
- onPress={(creation) => handleView(creation as Creation)}
203
- onShare={async (creation) => handleShare(creation as Creation)}
204
- onDelete={(creation) => handleDelete(creation as Creation)}
205
- onFavorite={(creation) => {
206
- const c = creation as Creation;
207
- handleFavorite(c, !c.isFavorite);
208
- }}
209
- contentContainerStyle={{ paddingBottom: tokens.spacing.xl }}
210
- ListEmptyComponent={renderEmptyComponent}
222
+ }
211
223
  />
212
224
 
213
225
  <CreationImageViewer
@@ -229,9 +241,29 @@ function CreationsGalleryScreenContent({
229
241
  toggleFilter(id, category?.multiSelect);
230
242
  }}
231
243
  onClearFilters={clearFilters}
232
- title={t(config.translations.filterTitle) || t("common.filter")}
244
+ title={t(config.translations.filterTitle)}
233
245
  />
234
- </ScreenLayout>
246
+ </View>
235
247
  );
236
248
  }
237
249
 
250
+ const useStyles = (tokens: DesignTokens, insets: { top: number; bottom: number }) =>
251
+ StyleSheet.create({
252
+ container: {
253
+ flex: 1,
254
+ },
255
+ header: {
256
+ paddingTop: insets.top + tokens.spacing.md,
257
+ backgroundColor: tokens.colors.surface,
258
+ borderBottomWidth: 1,
259
+ borderBottomColor: tokens.colors.border,
260
+ },
261
+ listContent: {
262
+ paddingHorizontal: tokens.spacing.md,
263
+ paddingTop: tokens.spacing.md,
264
+ paddingBottom: insets.bottom + 100,
265
+ },
266
+ emptyContent: {
267
+ flexGrow: 1,
268
+ },
269
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Text-to-Image Constants
3
+ * All constant exports for text-to-image feature
4
+ */
5
+
6
+ export { DEFAULT_IMAGE_STYLES } from "./styles.constants";
7
+
8
+ export {
9
+ DEFAULT_NUM_IMAGES_OPTIONS,
10
+ DEFAULT_ASPECT_RATIO_OPTIONS,
11
+ DEFAULT_SIZE_OPTIONS,
12
+ DEFAULT_OUTPUT_FORMAT_OPTIONS,
13
+ DEFAULT_FORM_VALUES,
14
+ } from "./options.constants";
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Text-to-Image Options Constants
3
+ * Default option values for text-to-image generation
4
+ */
5
+
6
+ import type {
7
+ AspectRatio,
8
+ ImageSize,
9
+ NumImages,
10
+ OutputFormat,
11
+ TextToImageFormDefaults,
12
+ } from "../types/form.types";
13
+
14
+ export const DEFAULT_NUM_IMAGES_OPTIONS: NumImages[] = [1, 2, 3, 4];
15
+
16
+ export const DEFAULT_ASPECT_RATIO_OPTIONS: { value: AspectRatio; label: string }[] = [
17
+ { value: "9:16", label: "Portrait (9:16)" },
18
+ { value: "16:9", label: "Landscape (16:9)" },
19
+ { value: "1:1", label: "Square (1:1)" },
20
+ ];
21
+
22
+ export const DEFAULT_SIZE_OPTIONS: { value: ImageSize; label: string }[] = [
23
+ { value: "512x512", label: "512×512" },
24
+ { value: "768x768", label: "768×768" },
25
+ { value: "1024x1024", label: "1024×1024" },
26
+ { value: "1024x1792", label: "1024×1792" },
27
+ { value: "1792x1024", label: "1792×1024" },
28
+ ];
29
+
30
+ export const DEFAULT_OUTPUT_FORMAT_OPTIONS: { value: OutputFormat; label: string }[] = [
31
+ { value: "png", label: "PNG" },
32
+ { value: "jpeg", label: "JPEG" },
33
+ ];
34
+
35
+ export const DEFAULT_FORM_VALUES: TextToImageFormDefaults = {
36
+ aspectRatio: "9:16",
37
+ size: "512x512",
38
+ numImages: 1,
39
+ guidanceScale: 7.5,
40
+ outputFormat: "png",
41
+ selectedStyle: "realistic",
42
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Default Image Styles
3
+ * Predefined style options for text-to-image generation
4
+ */
5
+
6
+ import type { StyleOption } from "../types/form.types";
7
+
8
+ export const DEFAULT_IMAGE_STYLES: StyleOption[] = [
9
+ {
10
+ id: "realistic",
11
+ name: "Realistic",
12
+ description: "Photorealistic images",
13
+ },
14
+ {
15
+ id: "artistic",
16
+ name: "Artistic",
17
+ description: "Creative and artistic style",
18
+ },
19
+ {
20
+ id: "anime",
21
+ name: "Anime",
22
+ description: "Japanese animation style",
23
+ },
24
+ {
25
+ id: "minimalist",
26
+ name: "Minimalist",
27
+ description: "Clean and simple design",
28
+ },
29
+ {
30
+ id: "vintage",
31
+ name: "Vintage",
32
+ description: "Retro and classic look",
33
+ },
34
+ ];
@@ -1 +1,7 @@
1
+ /**
2
+ * Text-to-Image Domain Layer
3
+ * Types and constants exports
4
+ */
5
+
1
6
  export * from "./types";
7
+ export * from "./constants";
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Text-to-Image Configuration Types
3
+ * Callback and configuration types for app integration
4
+ */
5
+
6
+ import type {
7
+ AspectRatio,
8
+ ImageSize,
9
+ NumImages,
10
+ OutputFormat,
11
+ StyleOption,
12
+ TextToImageFormDefaults,
13
+ } from "./form.types";
14
+
15
+ export interface GenerationRequest {
16
+ prompt: string;
17
+ model?: string;
18
+ aspectRatio: AspectRatio;
19
+ size: ImageSize;
20
+ negativePrompt?: string;
21
+ guidanceScale: number;
22
+ numImages: NumImages;
23
+ style?: string;
24
+ outputFormat?: OutputFormat;
25
+ }
26
+
27
+ export interface GenerationResultSuccess {
28
+ success: true;
29
+ imageUrls: string[];
30
+ }
31
+
32
+ export interface GenerationResultError {
33
+ success: false;
34
+ error: string;
35
+ }
36
+
37
+ export type GenerationResult = GenerationResultSuccess | GenerationResultError;
38
+
39
+ export interface TextToImageCallbacks {
40
+ executeGeneration: (request: GenerationRequest) => Promise<GenerationResult>;
41
+ calculateCost: (numImages: NumImages, model?: string | null) => number;
42
+ canAfford: (cost: number) => boolean;
43
+ isAuthenticated: () => boolean;
44
+ onAuthRequired?: () => void;
45
+ onCreditsRequired?: (cost: number) => void;
46
+ onSuccess?: (imageUrls: string[]) => void;
47
+ onError?: (error: string) => void;
48
+ }
49
+
50
+ export interface TextToImageFormConfig {
51
+ defaults?: TextToImageFormDefaults;
52
+ numImagesOptions?: NumImages[];
53
+ styleOptions?: StyleOption[];
54
+ aspectRatioOptions?: { value: AspectRatio; label: string }[];
55
+ sizeOptions?: { value: ImageSize; label: string }[];
56
+ outputFormatOptions?: { value: OutputFormat; label: string }[];
57
+ }
58
+
59
+ export interface TextToImageTranslations {
60
+ promptLabel: string;
61
+ promptPlaceholder: string;
62
+ promptCharacterCount?: string;
63
+ examplesLabel: string;
64
+ numImagesLabel: string;
65
+ styleLabel: string;
66
+ generateButton: string;
67
+ generateButtonMultiple?: string;
68
+ costLabel?: string;
69
+ settingsTitle?: string;
70
+ doneButton?: string;
71
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Text-to-Image Form Types
3
+ * Generic form state types for text-to-image generation
4
+ */
5
+
6
+ export type AspectRatio = "16:9" | "9:16" | "1:1";
7
+
8
+ export type ImageSize =
9
+ | "512x512"
10
+ | "768x768"
11
+ | "1024x1024"
12
+ | "1024x1792"
13
+ | "1792x1024";
14
+
15
+ export type OutputFormat = "png" | "jpeg";
16
+
17
+ export type NumImages = 1 | 2 | 3 | 4;
18
+
19
+ export interface StyleOption {
20
+ id: string;
21
+ name: string;
22
+ description?: string;
23
+ icon?: string;
24
+ }
25
+
26
+ export interface TextToImageFormState {
27
+ prompt: string;
28
+ aspectRatio: AspectRatio;
29
+ size: ImageSize;
30
+ numImages: NumImages;
31
+ negativePrompt: string;
32
+ guidanceScale: number;
33
+ selectedModel: string | null;
34
+ outputFormat: OutputFormat;
35
+ selectedStyle: string;
36
+ }
37
+
38
+ export interface TextToImageFormActions {
39
+ setPrompt: (prompt: string) => void;
40
+ setAspectRatio: (ratio: AspectRatio) => void;
41
+ setSize: (size: ImageSize) => void;
42
+ setNumImages: (num: NumImages) => void;
43
+ setNegativePrompt: (prompt: string) => void;
44
+ setGuidanceScale: (scale: number) => void;
45
+ setSelectedModel: (model: string | null) => void;
46
+ setOutputFormat: (format: OutputFormat) => void;
47
+ setSelectedStyle: (style: string) => void;
48
+ reset: () => void;
49
+ }
50
+
51
+ export interface TextToImageFormDefaults {
52
+ aspectRatio?: AspectRatio;
53
+ size?: ImageSize;
54
+ numImages?: NumImages;
55
+ guidanceScale?: number;
56
+ outputFormat?: OutputFormat;
57
+ selectedStyle?: string;
58
+ }