@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.
- package/package.json +1 -1
- package/src/domains/creations/presentation/components/CreationsHomeCard.tsx +1 -1
- package/src/domains/creations/presentation/hooks/useAdvancedFilter.ts +0 -1
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +9 -6
- package/src/domains/face-detection/infrastructure/validators/faceValidator.ts +1 -1
- package/src/domains/prompts/infrastructure/services/PromptGenerationService.ts +1 -1
- package/src/domains/prompts/presentation/hooks/useFaceSwap.ts +2 -2
- package/src/domains/prompts/presentation/hooks/usePromptGeneration.ts +4 -4
- package/src/features/image-to-video/domain/index.ts +1 -0
- package/src/features/image-to-video/domain/types/image-to-video.types.ts +71 -0
- package/src/features/image-to-video/domain/types/index.ts +10 -0
- package/src/features/image-to-video/index.ts +27 -0
- package/src/features/image-to-video/infrastructure/index.ts +1 -0
- package/src/features/image-to-video/infrastructure/services/image-to-video-executor.ts +112 -0
- package/src/features/image-to-video/infrastructure/services/index.ts +5 -0
- package/src/features/image-to-video/presentation/hooks/index.ts +5 -0
- package/src/features/image-to-video/presentation/hooks/useImageToVideoFeature.ts +121 -0
- package/src/features/image-to-video/presentation/index.ts +1 -0
- package/src/features/text-to-image/domain/index.ts +1 -0
- package/src/features/text-to-image/domain/types/index.ts +10 -0
- package/src/features/text-to-image/domain/types/text-to-image.types.ts +66 -0
- package/src/features/text-to-image/index.ts +27 -1
- package/src/features/text-to-image/infrastructure/index.ts +1 -0
- package/src/features/text-to-image/infrastructure/services/index.ts +5 -0
- package/src/features/text-to-image/infrastructure/services/text-to-image-executor.ts +113 -0
- package/src/features/text-to-image/presentation/hooks/index.ts +5 -0
- package/src/features/text-to-image/presentation/hooks/useTextToImageFeature.ts +111 -0
- package/src/features/text-to-image/presentation/index.ts +1 -0
- package/src/features/text-to-video/domain/index.ts +1 -0
- package/src/features/text-to-video/domain/types/index.ts +10 -0
- package/src/features/text-to-video/domain/types/text-to-video.types.ts +65 -0
- package/src/features/text-to-video/index.ts +27 -1
- package/src/features/text-to-video/infrastructure/index.ts +1 -0
- package/src/features/text-to-video/infrastructure/services/index.ts +5 -0
- package/src/features/text-to-video/infrastructure/services/text-to-video-executor.ts +108 -0
- package/src/features/text-to-video/presentation/hooks/index.ts +5 -0
- package/src/features/text-to-video/presentation/hooks/useTextToVideoFeature.ts +111 -0
- package/src/features/text-to-video/presentation/index.ts +1 -0
- package/src/features/text-to-voice/domain/index.ts +1 -0
- package/src/features/text-to-voice/domain/types/index.ts +10 -0
- package/src/features/text-to-voice/domain/types/text-to-voice.types.ts +65 -0
- package/src/features/text-to-voice/index.ts +27 -0
- package/src/features/text-to-voice/infrastructure/index.ts +1 -0
- package/src/features/text-to-voice/infrastructure/services/index.ts +5 -0
- package/src/features/text-to-voice/infrastructure/services/text-to-voice-executor.ts +111 -0
- package/src/features/text-to-voice/presentation/hooks/index.ts +5 -0
- package/src/features/text-to-voice/presentation/hooks/useTextToVoiceFeature.ts +105 -0
- package/src/features/text-to-voice/presentation/index.ts +1 -0
- package/src/index.ts +24 -0
- package/src/presentation/components/buttons/GenerateButton.tsx +141 -0
- package/src/presentation/components/buttons/index.ts +1 -0
- package/src/presentation/components/display/ErrorDisplay.tsx +111 -0
- package/src/presentation/components/display/ResultDisplay.tsx +122 -0
- package/src/presentation/components/display/index.ts +6 -0
- package/src/presentation/components/headers/FeatureHeader.tsx +85 -0
- package/src/presentation/components/headers/index.ts +1 -0
- package/src/presentation/components/image-picker/DualImagePicker.tsx +95 -0
- package/src/presentation/components/image-picker/ImagePickerBox.tsx +165 -0
- package/src/presentation/components/image-picker/index.ts +2 -0
- package/src/presentation/components/index.ts +4 -0
- package/src/features/text-to-image/domain/entities.ts +0 -58
- package/src/features/text-to-video/domain/entities.ts +0 -52
- package/src/types/jsx.d.ts +0 -19
package/package.json
CHANGED
|
@@ -34,7 +34,7 @@ export function CreationsHomeCard({
|
|
|
34
34
|
}: CreationsHomeCardProps) {
|
|
35
35
|
const tokens = useAppDesignTokens();
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
if (__DEV__) {
|
|
39
39
|
// eslint-disable-next-line no-console
|
|
40
40
|
console.log("[CreationsHomeCard] Render:", {
|
|
@@ -55,7 +55,7 @@ function CreationsGalleryScreenContent({
|
|
|
55
55
|
repository,
|
|
56
56
|
config,
|
|
57
57
|
t,
|
|
58
|
-
locale = "en-US",
|
|
58
|
+
locale: _locale = "en-US",
|
|
59
59
|
enableEditing = false,
|
|
60
60
|
onImageEdit,
|
|
61
61
|
onEmptyAction,
|
|
@@ -196,13 +196,16 @@ function CreationsGalleryScreenContent({
|
|
|
196
196
|
>
|
|
197
197
|
{/* Main Content Grid - handles empty/loading via ListEmptyComponent */}
|
|
198
198
|
<CreationsGrid
|
|
199
|
-
creations={filtered
|
|
199
|
+
creations={filtered}
|
|
200
200
|
isLoading={isLoading}
|
|
201
201
|
onRefresh={() => void refetch()}
|
|
202
|
-
onPress={handleView}
|
|
203
|
-
onShare={async (creation) => handleShare(creation as
|
|
204
|
-
onDelete={(creation) => handleDelete(creation as
|
|
205
|
-
onFavorite={(creation) =>
|
|
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
|
+
}}
|
|
206
209
|
contentContainerStyle={{ paddingBottom: tokens.spacing.xl }}
|
|
207
210
|
ListEmptyComponent={renderEmptyComponent}
|
|
208
211
|
/>
|
|
@@ -17,7 +17,7 @@ export class PromptGenerationService implements IPromptGenerationService {
|
|
|
17
17
|
|
|
18
18
|
const generatedText = this.replaceTemplateVariables(template.template, variables);
|
|
19
19
|
resolve({ success: true, data: generatedText });
|
|
20
|
-
} catch
|
|
20
|
+
} catch {
|
|
21
21
|
resolve({
|
|
22
22
|
success: false,
|
|
23
23
|
error: 'GENERATION_FAILED',
|
|
@@ -62,7 +62,7 @@ export const useFaceSwap = (
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
setResult(generationResult);
|
|
65
|
-
} catch
|
|
65
|
+
} catch {
|
|
66
66
|
setError('An unexpected error occurred');
|
|
67
67
|
}
|
|
68
68
|
}, [faceSwapService, historyRepository, setError, setResult, clearError]);
|
|
@@ -70,7 +70,7 @@ export const useFaceSwap = (
|
|
|
70
70
|
const getAvailableStyles = useCallback(async (): Promise<string[]> => {
|
|
71
71
|
try {
|
|
72
72
|
return await faceSwapService.getAvailableStyles();
|
|
73
|
-
} catch
|
|
73
|
+
} catch {
|
|
74
74
|
setError('Failed to load available styles');
|
|
75
75
|
return [];
|
|
76
76
|
}
|
|
@@ -70,7 +70,7 @@ export const usePromptGeneration = (
|
|
|
70
70
|
setGeneratedPrompt(newPrompt);
|
|
71
71
|
|
|
72
72
|
void loadHistory(50);
|
|
73
|
-
} catch
|
|
73
|
+
} catch {
|
|
74
74
|
setError('An unexpected error occurred');
|
|
75
75
|
}
|
|
76
76
|
},
|
|
@@ -87,7 +87,7 @@ export const usePromptGeneration = (
|
|
|
87
87
|
} else {
|
|
88
88
|
setError(('message' in result && result.message) || 'Failed to load history');
|
|
89
89
|
}
|
|
90
|
-
} catch
|
|
90
|
+
} catch {
|
|
91
91
|
setError('Failed to load history');
|
|
92
92
|
}
|
|
93
93
|
},
|
|
@@ -99,7 +99,7 @@ export const usePromptGeneration = (
|
|
|
99
99
|
try {
|
|
100
100
|
await historyRepository.clear();
|
|
101
101
|
setHistory([]);
|
|
102
|
-
} catch
|
|
102
|
+
} catch {
|
|
103
103
|
setError('Failed to clear history');
|
|
104
104
|
}
|
|
105
105
|
}, [historyRepository, setHistory, setError, clearError]);
|
|
@@ -110,7 +110,7 @@ export const usePromptGeneration = (
|
|
|
110
110
|
try {
|
|
111
111
|
await historyRepository.save(prompt);
|
|
112
112
|
void loadHistory(50);
|
|
113
|
-
} catch
|
|
113
|
+
} catch {
|
|
114
114
|
setError('Failed to save to history');
|
|
115
115
|
}
|
|
116
116
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types";
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image-to-Video Feature Types
|
|
3
|
+
* Request, Result, Config types for image-to-video generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ImageToVideoOptions {
|
|
7
|
+
duration?: number;
|
|
8
|
+
motionStrength?: number;
|
|
9
|
+
aspectRatio?: "16:9" | "9:16" | "1:1";
|
|
10
|
+
fps?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ImageToVideoRequest {
|
|
14
|
+
imageUri: string;
|
|
15
|
+
imageBase64?: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
motionPrompt?: string;
|
|
18
|
+
options?: ImageToVideoOptions;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ImageToVideoResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
videoUrl?: string;
|
|
24
|
+
thumbnailUrl?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
requestId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ImageToVideoFeatureState {
|
|
30
|
+
imageUri: string | null;
|
|
31
|
+
motionPrompt: string;
|
|
32
|
+
videoUrl: string | null;
|
|
33
|
+
thumbnailUrl: string | null;
|
|
34
|
+
isProcessing: boolean;
|
|
35
|
+
progress: number;
|
|
36
|
+
error: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ImageToVideoTranslations {
|
|
40
|
+
uploadTitle: string;
|
|
41
|
+
uploadSubtitle: string;
|
|
42
|
+
motionPromptPlaceholder: string;
|
|
43
|
+
generateButtonText: string;
|
|
44
|
+
processingText: string;
|
|
45
|
+
successText: string;
|
|
46
|
+
saveButtonText: string;
|
|
47
|
+
tryAnotherText: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type ImageToVideoInputBuilder = (
|
|
51
|
+
imageBase64: string,
|
|
52
|
+
motionPrompt?: string,
|
|
53
|
+
options?: ImageToVideoOptions,
|
|
54
|
+
) => Record<string, unknown>;
|
|
55
|
+
|
|
56
|
+
export type ImageToVideoResultExtractor = (
|
|
57
|
+
result: unknown,
|
|
58
|
+
) => { videoUrl?: string; thumbnailUrl?: string } | undefined;
|
|
59
|
+
|
|
60
|
+
export interface ImageToVideoFeatureConfig {
|
|
61
|
+
providerId?: string;
|
|
62
|
+
creditCost?: number;
|
|
63
|
+
model: string;
|
|
64
|
+
buildInput: ImageToVideoInputBuilder;
|
|
65
|
+
extractResult?: ImageToVideoResultExtractor;
|
|
66
|
+
prepareImage: (imageUri: string) => Promise<string>;
|
|
67
|
+
onImageSelect?: (uri: string) => void;
|
|
68
|
+
onProcessingStart?: () => void;
|
|
69
|
+
onProcessingComplete?: (result: ImageToVideoResult) => void;
|
|
70
|
+
onError?: (error: string) => void;
|
|
71
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ImageToVideoOptions,
|
|
3
|
+
ImageToVideoRequest,
|
|
4
|
+
ImageToVideoResult,
|
|
5
|
+
ImageToVideoFeatureState,
|
|
6
|
+
ImageToVideoTranslations,
|
|
7
|
+
ImageToVideoInputBuilder,
|
|
8
|
+
ImageToVideoResultExtractor,
|
|
9
|
+
ImageToVideoFeatureConfig,
|
|
10
|
+
} from "./image-to-video.types";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image-to-Video Feature
|
|
3
|
+
* Provider-agnostic image-to-video generation feature
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Domain Types
|
|
7
|
+
export type {
|
|
8
|
+
ImageToVideoOptions,
|
|
9
|
+
ImageToVideoRequest,
|
|
10
|
+
ImageToVideoResult,
|
|
11
|
+
ImageToVideoFeatureState,
|
|
12
|
+
ImageToVideoTranslations,
|
|
13
|
+
ImageToVideoInputBuilder,
|
|
14
|
+
ImageToVideoResultExtractor,
|
|
15
|
+
ImageToVideoFeatureConfig,
|
|
16
|
+
} from "./domain";
|
|
17
|
+
|
|
18
|
+
// Infrastructure Services
|
|
19
|
+
export { executeImageToVideo, hasImageToVideoSupport } from "./infrastructure";
|
|
20
|
+
export type { ExecuteImageToVideoOptions } from "./infrastructure";
|
|
21
|
+
|
|
22
|
+
// Presentation Hooks
|
|
23
|
+
export { useImageToVideoFeature } from "./presentation";
|
|
24
|
+
export type {
|
|
25
|
+
UseImageToVideoFeatureProps,
|
|
26
|
+
UseImageToVideoFeatureReturn,
|
|
27
|
+
} from "./presentation";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./services";
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image-to-Video Executor
|
|
3
|
+
* Provider-agnostic image-to-video execution using active AI provider
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { providerRegistry } from "../../../../infrastructure/services";
|
|
7
|
+
import { cleanBase64 } from "../../../../infrastructure/utils";
|
|
8
|
+
import type {
|
|
9
|
+
ImageToVideoRequest,
|
|
10
|
+
ImageToVideoResult,
|
|
11
|
+
ImageToVideoInputBuilder,
|
|
12
|
+
ImageToVideoResultExtractor,
|
|
13
|
+
} from "../../domain/types";
|
|
14
|
+
|
|
15
|
+
declare const __DEV__: boolean;
|
|
16
|
+
|
|
17
|
+
export interface ExecuteImageToVideoOptions {
|
|
18
|
+
model: string;
|
|
19
|
+
buildInput: ImageToVideoInputBuilder;
|
|
20
|
+
extractResult?: ImageToVideoResultExtractor;
|
|
21
|
+
onProgress?: (progress: number) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function defaultExtractResult(
|
|
25
|
+
result: unknown,
|
|
26
|
+
): { videoUrl?: string; thumbnailUrl?: string } | undefined {
|
|
27
|
+
if (typeof result !== "object" || result === null) return undefined;
|
|
28
|
+
|
|
29
|
+
const r = result as Record<string, unknown>;
|
|
30
|
+
|
|
31
|
+
if (typeof r.video === "string") {
|
|
32
|
+
return { videoUrl: r.video };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (r.video && typeof r.video === "object") {
|
|
36
|
+
const video = r.video as Record<string, unknown>;
|
|
37
|
+
if (typeof video.url === "string") {
|
|
38
|
+
return {
|
|
39
|
+
videoUrl: video.url,
|
|
40
|
+
thumbnailUrl:
|
|
41
|
+
typeof r.thumbnail === "string" ? r.thumbnail : undefined,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function executeImageToVideo(
|
|
50
|
+
request: ImageToVideoRequest,
|
|
51
|
+
options: ExecuteImageToVideoOptions,
|
|
52
|
+
): Promise<ImageToVideoResult> {
|
|
53
|
+
const provider = providerRegistry.getActiveProvider();
|
|
54
|
+
|
|
55
|
+
if (!provider) {
|
|
56
|
+
return { success: false, error: "No AI provider configured" };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!provider.isInitialized()) {
|
|
60
|
+
return { success: false, error: "AI provider not initialized" };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!request.imageBase64) {
|
|
64
|
+
return { success: false, error: "Image base64 is required" };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { model, buildInput, extractResult, onProgress } = options;
|
|
68
|
+
|
|
69
|
+
if (__DEV__) {
|
|
70
|
+
// eslint-disable-next-line no-console
|
|
71
|
+
console.log(`[ImageToVideo] Provider: ${provider.providerId}, Model: ${model}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
onProgress?.(10);
|
|
76
|
+
|
|
77
|
+
const imageBase64 = cleanBase64(request.imageBase64);
|
|
78
|
+
onProgress?.(30);
|
|
79
|
+
|
|
80
|
+
const input = buildInput(imageBase64, request.motionPrompt, request.options);
|
|
81
|
+
onProgress?.(40);
|
|
82
|
+
|
|
83
|
+
const result = await provider.run(model, input);
|
|
84
|
+
onProgress?.(90);
|
|
85
|
+
|
|
86
|
+
const extractor = extractResult || defaultExtractResult;
|
|
87
|
+
const extracted = extractor(result);
|
|
88
|
+
onProgress?.(100);
|
|
89
|
+
|
|
90
|
+
if (!extracted?.videoUrl) {
|
|
91
|
+
return { success: false, error: "No video in response" };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
videoUrl: extracted.videoUrl,
|
|
97
|
+
thumbnailUrl: extracted.thumbnailUrl,
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
if (__DEV__) {
|
|
102
|
+
// eslint-disable-next-line no-console
|
|
103
|
+
console.error("[ImageToVideo] Error:", message);
|
|
104
|
+
}
|
|
105
|
+
return { success: false, error: message };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function hasImageToVideoSupport(): boolean {
|
|
110
|
+
const provider = providerRegistry.getActiveProvider();
|
|
111
|
+
return provider !== null && provider.isInitialized();
|
|
112
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image-to-Video Feature Hook
|
|
3
|
+
* Provider-agnostic hook for image-to-video generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback } from "react";
|
|
7
|
+
import { executeImageToVideo } from "../../infrastructure/services";
|
|
8
|
+
import type {
|
|
9
|
+
ImageToVideoFeatureState,
|
|
10
|
+
ImageToVideoFeatureConfig,
|
|
11
|
+
ImageToVideoResult,
|
|
12
|
+
ImageToVideoOptions,
|
|
13
|
+
} from "../../domain/types";
|
|
14
|
+
|
|
15
|
+
export interface UseImageToVideoFeatureProps {
|
|
16
|
+
config: ImageToVideoFeatureConfig;
|
|
17
|
+
userId: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface UseImageToVideoFeatureReturn {
|
|
21
|
+
state: ImageToVideoFeatureState;
|
|
22
|
+
setImageUri: (uri: string) => void;
|
|
23
|
+
setMotionPrompt: (prompt: string) => void;
|
|
24
|
+
generate: (options?: ImageToVideoOptions) => Promise<ImageToVideoResult>;
|
|
25
|
+
reset: () => void;
|
|
26
|
+
isReady: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const initialState: ImageToVideoFeatureState = {
|
|
30
|
+
imageUri: null,
|
|
31
|
+
motionPrompt: "",
|
|
32
|
+
videoUrl: null,
|
|
33
|
+
thumbnailUrl: null,
|
|
34
|
+
isProcessing: false,
|
|
35
|
+
progress: 0,
|
|
36
|
+
error: null,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function useImageToVideoFeature(
|
|
40
|
+
props: UseImageToVideoFeatureProps,
|
|
41
|
+
): UseImageToVideoFeatureReturn {
|
|
42
|
+
const { config, userId } = props;
|
|
43
|
+
const [state, setState] = useState<ImageToVideoFeatureState>(initialState);
|
|
44
|
+
|
|
45
|
+
const setImageUri = useCallback(
|
|
46
|
+
(uri: string) => {
|
|
47
|
+
setState((prev) => ({ ...prev, imageUri: uri, error: null }));
|
|
48
|
+
config.onImageSelect?.(uri);
|
|
49
|
+
},
|
|
50
|
+
[config],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const setMotionPrompt = useCallback((prompt: string) => {
|
|
54
|
+
setState((prev) => ({ ...prev, motionPrompt: prompt }));
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
const generate = useCallback(
|
|
58
|
+
async (options?: ImageToVideoOptions): Promise<ImageToVideoResult> => {
|
|
59
|
+
if (!state.imageUri) {
|
|
60
|
+
const error = "Image is required";
|
|
61
|
+
setState((prev) => ({ ...prev, error }));
|
|
62
|
+
return { success: false, error };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setState((prev) => ({
|
|
66
|
+
...prev,
|
|
67
|
+
isProcessing: true,
|
|
68
|
+
progress: 0,
|
|
69
|
+
error: null,
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
config.onProcessingStart?.();
|
|
73
|
+
|
|
74
|
+
const imageBase64 = await config.prepareImage(state.imageUri);
|
|
75
|
+
|
|
76
|
+
const result = await executeImageToVideo(
|
|
77
|
+
{
|
|
78
|
+
imageUri: state.imageUri,
|
|
79
|
+
imageBase64,
|
|
80
|
+
userId,
|
|
81
|
+
motionPrompt: state.motionPrompt || undefined,
|
|
82
|
+
options,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
model: config.model,
|
|
86
|
+
buildInput: config.buildInput,
|
|
87
|
+
extractResult: config.extractResult,
|
|
88
|
+
onProgress: (progress) => {
|
|
89
|
+
setState((prev) => ({ ...prev, progress }));
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (result.success && result.videoUrl) {
|
|
95
|
+
setState((prev) => ({
|
|
96
|
+
...prev,
|
|
97
|
+
videoUrl: result.videoUrl ?? null,
|
|
98
|
+
thumbnailUrl: result.thumbnailUrl ?? null,
|
|
99
|
+
isProcessing: false,
|
|
100
|
+
progress: 100,
|
|
101
|
+
}));
|
|
102
|
+
} else {
|
|
103
|
+
const error = result.error || "Generation failed";
|
|
104
|
+
setState((prev) => ({ ...prev, isProcessing: false, error }));
|
|
105
|
+
config.onError?.(error);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
config.onProcessingComplete?.(result);
|
|
109
|
+
return result;
|
|
110
|
+
},
|
|
111
|
+
[state.imageUri, state.motionPrompt, userId, config],
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const reset = useCallback(() => {
|
|
115
|
+
setState(initialState);
|
|
116
|
+
}, []);
|
|
117
|
+
|
|
118
|
+
const isReady = state.imageUri !== null && !state.isProcessing;
|
|
119
|
+
|
|
120
|
+
return { state, setImageUri, setMotionPrompt, generate, reset, isReady };
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./hooks";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text-to-Image Feature Types
|
|
3
|
+
* Request, Result, Config types for text-to-image generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface TextToImageOptions {
|
|
7
|
+
aspectRatio?: "16:9" | "9:16" | "1:1";
|
|
8
|
+
size?: "512x512" | "768x768" | "1024x1024" | "1024x1792" | "1792x1024";
|
|
9
|
+
numImages?: number;
|
|
10
|
+
guidanceScale?: number;
|
|
11
|
+
seed?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface TextToImageRequest {
|
|
15
|
+
prompt: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
negativePrompt?: string;
|
|
18
|
+
options?: TextToImageOptions;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TextToImageResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
imageUrl?: string;
|
|
24
|
+
imageUrls?: string[];
|
|
25
|
+
error?: string;
|
|
26
|
+
requestId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TextToImageFeatureState {
|
|
30
|
+
prompt: string;
|
|
31
|
+
imageUrl: string | null;
|
|
32
|
+
imageUrls: string[];
|
|
33
|
+
isProcessing: boolean;
|
|
34
|
+
progress: number;
|
|
35
|
+
error: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface TextToImageTranslations {
|
|
39
|
+
promptPlaceholder: string;
|
|
40
|
+
generateButtonText: string;
|
|
41
|
+
processingText: string;
|
|
42
|
+
successText: string;
|
|
43
|
+
saveButtonText: string;
|
|
44
|
+
tryAnotherText: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type TextToImageInputBuilder = (
|
|
48
|
+
prompt: string,
|
|
49
|
+
options?: TextToImageOptions,
|
|
50
|
+
) => Record<string, unknown>;
|
|
51
|
+
|
|
52
|
+
export type TextToImageResultExtractor = (
|
|
53
|
+
result: unknown,
|
|
54
|
+
) => { imageUrl?: string; imageUrls?: string[] } | undefined;
|
|
55
|
+
|
|
56
|
+
export interface TextToImageFeatureConfig {
|
|
57
|
+
providerId?: string;
|
|
58
|
+
creditCost?: number;
|
|
59
|
+
model: string;
|
|
60
|
+
buildInput: TextToImageInputBuilder;
|
|
61
|
+
extractResult?: TextToImageResultExtractor;
|
|
62
|
+
onPromptChange?: (prompt: string) => void;
|
|
63
|
+
onProcessingStart?: () => void;
|
|
64
|
+
onProcessingComplete?: (result: TextToImageResult) => void;
|
|
65
|
+
onError?: (error: string) => void;
|
|
66
|
+
}
|
|
@@ -1 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Text-to-Image Feature
|
|
3
|
+
* Provider-agnostic text-to-image generation feature
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Domain Types
|
|
7
|
+
export type {
|
|
8
|
+
TextToImageOptions,
|
|
9
|
+
TextToImageRequest,
|
|
10
|
+
TextToImageResult,
|
|
11
|
+
TextToImageFeatureState,
|
|
12
|
+
TextToImageTranslations,
|
|
13
|
+
TextToImageInputBuilder,
|
|
14
|
+
TextToImageResultExtractor,
|
|
15
|
+
TextToImageFeatureConfig,
|
|
16
|
+
} from "./domain";
|
|
17
|
+
|
|
18
|
+
// Infrastructure Services
|
|
19
|
+
export { executeTextToImage, hasTextToImageSupport } from "./infrastructure";
|
|
20
|
+
export type { ExecuteTextToImageOptions } from "./infrastructure";
|
|
21
|
+
|
|
22
|
+
// Presentation Hooks
|
|
23
|
+
export { useTextToImageFeature } from "./presentation";
|
|
24
|
+
export type {
|
|
25
|
+
UseTextToImageFeatureProps,
|
|
26
|
+
UseTextToImageFeatureReturn,
|
|
27
|
+
} from "./presentation";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./services";
|