@umituz/react-native-ai-generation-content 1.17.0 → 1.17.1
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 +13 -14
- package/src/domains/creations/domain/entities/Creation.ts +33 -2
- package/src/domains/creations/domain/entities/index.ts +1 -1
- package/src/domains/creations/domain/types/creation-categories.ts +133 -0
- package/src/domains/creations/domain/types/creation-filter.ts +131 -0
- package/src/domains/creations/domain/types/creation-types.ts +63 -0
- package/src/domains/creations/domain/types/index.ts +44 -0
- package/src/domains/creations/domain/utils/creation-helpers.ts +134 -0
- package/src/domains/creations/domain/utils/index.ts +8 -0
- package/src/domains/creations/domain/utils/preview-helpers.ts +84 -0
- package/src/domains/creations/domain/utils/status-helpers.ts +90 -0
- package/src/domains/creations/index.ts +95 -21
- package/src/domains/creations/infrastructure/repositories/CreationsRepository.ts +14 -1
- package/src/domains/creations/presentation/components/CreationActions.tsx +120 -0
- package/src/domains/creations/presentation/components/CreationBadges.tsx +111 -0
- package/src/domains/creations/presentation/components/CreationCard.tsx +201 -102
- package/src/domains/creations/presentation/components/CreationPreview.tsx +117 -0
- package/src/domains/creations/presentation/components/CreationsFilterBar.tsx +254 -0
- package/src/domains/creations/presentation/components/CreationsGrid.tsx +121 -68
- package/src/domains/creations/presentation/components/index.ts +23 -3
- package/src/domains/creations/presentation/hooks/index.ts +1 -0
- package/src/domains/creations/presentation/hooks/useAdvancedFilter.ts +262 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +5 -6
- package/src/features/ai-hug/domain/index.ts +5 -0
- package/src/features/ai-hug/domain/types/ai-hug.types.ts +72 -0
- package/src/features/ai-hug/domain/types/index.ts +14 -0
- package/src/features/ai-hug/index.ts +27 -0
- package/src/features/ai-hug/infrastructure/index.ts +5 -0
- package/src/features/ai-hug/infrastructure/services/ai-hug-executor.ts +96 -0
- package/src/features/ai-hug/infrastructure/services/index.ts +6 -0
- package/src/features/ai-hug/presentation/hooks/index.ts +9 -0
- package/src/features/ai-hug/presentation/hooks/useAIHugFeature.ts +157 -0
- package/src/features/ai-hug/presentation/index.ts +5 -0
- package/src/features/ai-kiss/domain/index.ts +5 -0
- package/src/features/ai-kiss/domain/types/ai-kiss.types.ts +72 -0
- package/src/features/ai-kiss/domain/types/index.ts +14 -0
- package/src/features/ai-kiss/index.ts +27 -0
- package/src/features/ai-kiss/infrastructure/index.ts +5 -0
- package/src/features/ai-kiss/infrastructure/services/ai-kiss-executor.ts +96 -0
- package/src/features/ai-kiss/infrastructure/services/index.ts +6 -0
- package/src/features/ai-kiss/presentation/hooks/index.ts +9 -0
- package/src/features/ai-kiss/presentation/hooks/useAIKissFeature.ts +157 -0
- package/src/features/ai-kiss/presentation/index.ts +5 -0
- package/src/features/anime-selfie/domain/index.ts +5 -0
- package/src/features/anime-selfie/domain/types/anime-selfie.types.ts +72 -0
- package/src/features/anime-selfie/domain/types/index.ts +15 -0
- package/src/features/anime-selfie/index.ts +28 -0
- package/src/features/anime-selfie/infrastructure/index.ts +5 -0
- package/src/features/anime-selfie/infrastructure/services/anime-selfie-executor.ts +95 -0
- package/src/features/anime-selfie/infrastructure/services/index.ts +6 -0
- package/src/features/anime-selfie/presentation/hooks/index.ts +9 -0
- package/src/features/anime-selfie/presentation/hooks/useAnimeSelfieFeature.ts +138 -0
- package/src/features/anime-selfie/presentation/index.ts +5 -0
- package/src/features/background/domain/types/index.ts +15 -0
- package/src/features/background/domain/types/replace-background.types.ts +82 -0
- package/src/features/background/index.ts +31 -3
- package/src/features/background/infrastructure/index.ts +5 -0
- package/src/features/background/infrastructure/services/index.ts +6 -0
- package/src/features/background/infrastructure/services/replace-background-executor.ts +95 -0
- package/src/features/background/presentation/hooks/index.ts +6 -1
- package/src/features/background/presentation/hooks/useReplaceBackgroundFeature.ts +160 -0
- package/src/features/face-swap/domain/index.ts +5 -0
- package/src/features/face-swap/domain/types/face-swap.types.ts +72 -0
- package/src/features/face-swap/domain/types/index.ts +14 -0
- package/src/features/face-swap/index.ts +27 -1
- package/src/features/face-swap/infrastructure/index.ts +5 -0
- package/src/features/face-swap/infrastructure/services/face-swap-executor.ts +96 -0
- package/src/features/face-swap/infrastructure/services/index.ts +6 -0
- package/src/features/face-swap/presentation/hooks/index.ts +9 -0
- package/src/features/face-swap/presentation/hooks/useFaceSwapFeature.ts +157 -0
- package/src/features/face-swap/presentation/index.ts +5 -0
- package/src/features/photo-restoration/domain/types/index.ts +2 -5
- package/src/features/photo-restoration/domain/types/photo-restore.types.ts +14 -0
- package/src/features/photo-restoration/index.ts +3 -8
- package/src/features/photo-restoration/infrastructure/services/index.ts +1 -6
- package/src/features/photo-restoration/infrastructure/services/photo-restore-executor.ts +64 -30
- package/src/features/photo-restoration/presentation/hooks/usePhotoRestoreFeature.ts +11 -6
- package/src/features/remove-background/domain/index.ts +5 -0
- package/src/features/remove-background/domain/types/index.ts +14 -0
- package/src/features/remove-background/domain/types/remove-background.types.ts +69 -0
- package/src/features/remove-background/index.ts +27 -0
- package/src/features/remove-background/infrastructure/index.ts +5 -0
- package/src/features/remove-background/infrastructure/services/index.ts +6 -0
- package/src/features/remove-background/infrastructure/services/remove-background-executor.ts +95 -0
- package/src/features/remove-background/presentation/hooks/index.ts +9 -0
- package/src/features/remove-background/presentation/hooks/useRemoveBackgroundFeature.ts +137 -0
- package/src/features/remove-background/presentation/index.ts +5 -0
- package/src/features/remove-object/domain/index.ts +5 -0
- package/src/features/remove-object/domain/types/index.ts +14 -0
- package/src/features/remove-object/domain/types/remove-object.types.ts +77 -0
- package/src/features/remove-object/index.ts +27 -0
- package/src/features/remove-object/infrastructure/index.ts +5 -0
- package/src/features/remove-object/infrastructure/services/index.ts +6 -0
- package/src/features/remove-object/infrastructure/services/remove-object-executor.ts +99 -0
- package/src/features/remove-object/presentation/hooks/index.ts +9 -0
- package/src/features/remove-object/presentation/hooks/useRemoveObjectFeature.ts +168 -0
- package/src/features/remove-object/presentation/index.ts +5 -0
- package/src/features/upscaling/domain/types/index.ts +0 -1
- package/src/features/upscaling/domain/types/upscale.types.ts +14 -0
- package/src/features/upscaling/index.ts +3 -11
- package/src/features/upscaling/infrastructure/services/index.ts +1 -6
- package/src/features/upscaling/infrastructure/services/upscale-executor.ts +64 -30
- package/src/features/upscaling/presentation/hooks/useUpscaleFeature.ts +12 -7
- package/src/index.ts +39 -0
- package/src/types/jsx.d.ts +19 -0
- package/src/features/face-swap/domain/entities.ts +0 -48
- package/src/features/photo-restoration/domain/types/provider.types.ts +0 -23
- package/src/features/photo-restoration/infrastructure/services/photo-restore-provider-registry.ts +0 -77
- package/src/features/upscaling/domain/types/provider.types.ts +0 -23
- package/src/features/upscaling/infrastructure/services/upscale-provider-registry.ts +0 -77
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Kiss Feature Types
|
|
3
|
+
* Request, Result, Config types for AI kiss generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface AIKissOptions {
|
|
7
|
+
style?: "romantic" | "cheek" | "forehead";
|
|
8
|
+
preserveFaces?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AIKissRequest {
|
|
12
|
+
sourceImageUri: string;
|
|
13
|
+
targetImageUri: string;
|
|
14
|
+
sourceImageBase64?: string;
|
|
15
|
+
targetImageBase64?: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
options?: AIKissOptions;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AIKissResult {
|
|
21
|
+
success: boolean;
|
|
22
|
+
imageUrl?: string;
|
|
23
|
+
imageBase64?: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
requestId?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface AIKissFeatureState {
|
|
29
|
+
sourceImageUri: string | null;
|
|
30
|
+
targetImageUri: string | null;
|
|
31
|
+
processedUrl: string | null;
|
|
32
|
+
isProcessing: boolean;
|
|
33
|
+
progress: number;
|
|
34
|
+
error: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AIKissTranslations {
|
|
38
|
+
sourceUploadTitle: string;
|
|
39
|
+
sourceUploadSubtitle: string;
|
|
40
|
+
targetUploadTitle: string;
|
|
41
|
+
targetUploadSubtitle: string;
|
|
42
|
+
uploadChange: string;
|
|
43
|
+
uploadAnalyzing: string;
|
|
44
|
+
description: string;
|
|
45
|
+
processingText: string;
|
|
46
|
+
processButtonText: string;
|
|
47
|
+
successText: string;
|
|
48
|
+
saveButtonText: string;
|
|
49
|
+
tryAnotherText: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type AIKissInputBuilder = (
|
|
53
|
+
sourceBase64: string,
|
|
54
|
+
targetBase64: string,
|
|
55
|
+
options?: AIKissOptions,
|
|
56
|
+
) => Record<string, unknown>;
|
|
57
|
+
|
|
58
|
+
export type AIKissResultExtractor = (result: unknown) => string | undefined;
|
|
59
|
+
|
|
60
|
+
export interface AIKissFeatureConfig {
|
|
61
|
+
providerId?: string;
|
|
62
|
+
creditCost?: number;
|
|
63
|
+
model: string;
|
|
64
|
+
buildInput: AIKissInputBuilder;
|
|
65
|
+
extractResult?: AIKissResultExtractor;
|
|
66
|
+
prepareImage: (imageUri: string) => Promise<string>;
|
|
67
|
+
onSourceImageSelect?: (uri: string) => void;
|
|
68
|
+
onTargetImageSelect?: (uri: string) => void;
|
|
69
|
+
onProcessingStart?: () => void;
|
|
70
|
+
onProcessingComplete?: (result: AIKissResult) => void;
|
|
71
|
+
onError?: (error: string) => void;
|
|
72
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Kiss Domain Types Index
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
AIKissOptions,
|
|
7
|
+
AIKissRequest,
|
|
8
|
+
AIKissResult,
|
|
9
|
+
AIKissFeatureState,
|
|
10
|
+
AIKissTranslations,
|
|
11
|
+
AIKissInputBuilder,
|
|
12
|
+
AIKissResultExtractor,
|
|
13
|
+
AIKissFeatureConfig,
|
|
14
|
+
} from "./ai-kiss.types";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Kiss Feature
|
|
3
|
+
* Provider-agnostic AI kiss generation feature
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Domain Types
|
|
7
|
+
export type {
|
|
8
|
+
AIKissOptions,
|
|
9
|
+
AIKissRequest,
|
|
10
|
+
AIKissResult,
|
|
11
|
+
AIKissFeatureState,
|
|
12
|
+
AIKissTranslations,
|
|
13
|
+
AIKissFeatureConfig,
|
|
14
|
+
AIKissInputBuilder,
|
|
15
|
+
AIKissResultExtractor,
|
|
16
|
+
} from "./domain";
|
|
17
|
+
|
|
18
|
+
// Infrastructure Services
|
|
19
|
+
export { executeAIKiss, hasAIKissSupport } from "./infrastructure";
|
|
20
|
+
export type { ExecuteAIKissOptions } from "./infrastructure";
|
|
21
|
+
|
|
22
|
+
// Presentation Hooks
|
|
23
|
+
export { useAIKissFeature } from "./presentation";
|
|
24
|
+
export type {
|
|
25
|
+
UseAIKissFeatureProps,
|
|
26
|
+
UseAIKissFeatureReturn,
|
|
27
|
+
} from "./presentation";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Kiss Executor
|
|
3
|
+
* Provider-agnostic AI kiss execution using active AI provider
|
|
4
|
+
* Model and input format are provided via options from app level
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { providerRegistry } from "../../../../infrastructure/services";
|
|
8
|
+
import { cleanBase64 } from "../../../../infrastructure/utils";
|
|
9
|
+
import type {
|
|
10
|
+
AIKissRequest,
|
|
11
|
+
AIKissResult,
|
|
12
|
+
AIKissInputBuilder,
|
|
13
|
+
AIKissResultExtractor,
|
|
14
|
+
} from "../../domain/types";
|
|
15
|
+
|
|
16
|
+
declare const __DEV__: boolean;
|
|
17
|
+
|
|
18
|
+
export interface ExecuteAIKissOptions {
|
|
19
|
+
model: string;
|
|
20
|
+
buildInput: AIKissInputBuilder;
|
|
21
|
+
extractResult?: AIKissResultExtractor;
|
|
22
|
+
onProgress?: (progress: number) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function defaultExtractResult(result: unknown): string | undefined {
|
|
26
|
+
if (typeof result !== "object" || result === null) return undefined;
|
|
27
|
+
|
|
28
|
+
const r = result as Record<string, unknown>;
|
|
29
|
+
|
|
30
|
+
if (typeof r.image === "string") return r.image;
|
|
31
|
+
if (Array.isArray(r.images) && r.images[0]?.url) return r.images[0].url;
|
|
32
|
+
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function executeAIKiss(
|
|
37
|
+
request: AIKissRequest,
|
|
38
|
+
options: ExecuteAIKissOptions,
|
|
39
|
+
): Promise<AIKissResult> {
|
|
40
|
+
const provider = providerRegistry.getActiveProvider();
|
|
41
|
+
|
|
42
|
+
if (!provider) {
|
|
43
|
+
return { success: false, error: "No AI provider configured" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!provider.isInitialized()) {
|
|
47
|
+
return { success: false, error: "AI provider not initialized" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!request.sourceImageBase64 || !request.targetImageBase64) {
|
|
51
|
+
return { success: false, error: "Both source and target image base64 are required" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { model, buildInput, extractResult, onProgress } = options;
|
|
55
|
+
|
|
56
|
+
if (__DEV__) {
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.log(`[AIKiss] Provider: ${provider.providerId}, Model: ${model}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
onProgress?.(10);
|
|
63
|
+
|
|
64
|
+
const sourceBase64 = cleanBase64(request.sourceImageBase64);
|
|
65
|
+
const targetBase64 = cleanBase64(request.targetImageBase64);
|
|
66
|
+
onProgress?.(30);
|
|
67
|
+
|
|
68
|
+
const input = buildInput(sourceBase64, targetBase64, request.options);
|
|
69
|
+
onProgress?.(40);
|
|
70
|
+
|
|
71
|
+
const result = await provider.run(model, input);
|
|
72
|
+
onProgress?.(90);
|
|
73
|
+
|
|
74
|
+
const extractor = extractResult || defaultExtractResult;
|
|
75
|
+
const imageUrl = extractor(result);
|
|
76
|
+
onProgress?.(100);
|
|
77
|
+
|
|
78
|
+
if (!imageUrl) {
|
|
79
|
+
return { success: false, error: "No image in response" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { success: true, imageUrl };
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
85
|
+
if (__DEV__) {
|
|
86
|
+
// eslint-disable-next-line no-console
|
|
87
|
+
console.error("[AIKiss] Error:", message);
|
|
88
|
+
}
|
|
89
|
+
return { success: false, error: message };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function hasAIKissSupport(): boolean {
|
|
94
|
+
const provider = providerRegistry.getActiveProvider();
|
|
95
|
+
return provider !== null && provider.isInitialized();
|
|
96
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAIKissFeature Hook
|
|
3
|
+
* Manages AI kiss feature state and actions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback } from "react";
|
|
7
|
+
import { executeAIKiss } from "../../infrastructure/services";
|
|
8
|
+
import type {
|
|
9
|
+
AIKissFeatureState,
|
|
10
|
+
AIKissFeatureConfig,
|
|
11
|
+
AIKissResult,
|
|
12
|
+
} from "../../domain/types";
|
|
13
|
+
|
|
14
|
+
declare const __DEV__: boolean;
|
|
15
|
+
|
|
16
|
+
export interface UseAIKissFeatureProps {
|
|
17
|
+
config: AIKissFeatureConfig;
|
|
18
|
+
userId: string;
|
|
19
|
+
onSelectSourceImage: () => Promise<string | null>;
|
|
20
|
+
onSelectTargetImage: () => Promise<string | null>;
|
|
21
|
+
onSaveImage: (imageUrl: string) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface UseAIKissFeatureReturn extends AIKissFeatureState {
|
|
25
|
+
selectSourceImage: () => Promise<void>;
|
|
26
|
+
selectTargetImage: () => Promise<void>;
|
|
27
|
+
process: () => Promise<void>;
|
|
28
|
+
save: () => Promise<void>;
|
|
29
|
+
reset: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const initialState: AIKissFeatureState = {
|
|
33
|
+
sourceImageUri: null,
|
|
34
|
+
targetImageUri: null,
|
|
35
|
+
processedUrl: null,
|
|
36
|
+
isProcessing: false,
|
|
37
|
+
progress: 0,
|
|
38
|
+
error: null,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function useAIKissFeature(
|
|
42
|
+
props: UseAIKissFeatureProps,
|
|
43
|
+
): UseAIKissFeatureReturn {
|
|
44
|
+
const { config, userId, onSelectSourceImage, onSelectTargetImage, onSaveImage } = props;
|
|
45
|
+
const [state, setState] = useState<AIKissFeatureState>(initialState);
|
|
46
|
+
|
|
47
|
+
const selectSourceImage = useCallback(async () => {
|
|
48
|
+
try {
|
|
49
|
+
const uri = await onSelectSourceImage();
|
|
50
|
+
if (uri) {
|
|
51
|
+
setState((prev) => ({ ...prev, sourceImageUri: uri, error: null }));
|
|
52
|
+
config.onSourceImageSelect?.(uri);
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
+
setState((prev) => ({ ...prev, error: message }));
|
|
57
|
+
}
|
|
58
|
+
}, [onSelectSourceImage, config]);
|
|
59
|
+
|
|
60
|
+
const selectTargetImage = useCallback(async () => {
|
|
61
|
+
try {
|
|
62
|
+
const uri = await onSelectTargetImage();
|
|
63
|
+
if (uri) {
|
|
64
|
+
setState((prev) => ({ ...prev, targetImageUri: uri, error: null }));
|
|
65
|
+
config.onTargetImageSelect?.(uri);
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
setState((prev) => ({ ...prev, error: message }));
|
|
70
|
+
}
|
|
71
|
+
}, [onSelectTargetImage, config]);
|
|
72
|
+
|
|
73
|
+
const handleProgress = useCallback((progress: number) => {
|
|
74
|
+
setState((prev) => ({ ...prev, progress }));
|
|
75
|
+
}, []);
|
|
76
|
+
|
|
77
|
+
const process = useCallback(async () => {
|
|
78
|
+
if (!state.sourceImageUri || !state.targetImageUri) return;
|
|
79
|
+
|
|
80
|
+
setState((prev) => ({
|
|
81
|
+
...prev,
|
|
82
|
+
isProcessing: true,
|
|
83
|
+
progress: 0,
|
|
84
|
+
error: null,
|
|
85
|
+
}));
|
|
86
|
+
|
|
87
|
+
config.onProcessingStart?.();
|
|
88
|
+
|
|
89
|
+
if (__DEV__) {
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.log("[useAIKissFeature] Starting AI kiss process");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const sourceImageBase64 = await config.prepareImage(state.sourceImageUri);
|
|
95
|
+
const targetImageBase64 = await config.prepareImage(state.targetImageUri);
|
|
96
|
+
|
|
97
|
+
const result: AIKissResult = await executeAIKiss(
|
|
98
|
+
{
|
|
99
|
+
sourceImageUri: state.sourceImageUri,
|
|
100
|
+
targetImageUri: state.targetImageUri,
|
|
101
|
+
sourceImageBase64,
|
|
102
|
+
targetImageBase64,
|
|
103
|
+
userId,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
model: config.model,
|
|
107
|
+
buildInput: config.buildInput,
|
|
108
|
+
extractResult: config.extractResult,
|
|
109
|
+
onProgress: handleProgress,
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (result.success && result.imageUrl) {
|
|
114
|
+
const url = result.imageUrl;
|
|
115
|
+
setState((prev) => ({
|
|
116
|
+
...prev,
|
|
117
|
+
isProcessing: false,
|
|
118
|
+
processedUrl: url,
|
|
119
|
+
progress: 100,
|
|
120
|
+
}));
|
|
121
|
+
config.onProcessingComplete?.(result);
|
|
122
|
+
} else {
|
|
123
|
+
const errorMessage = result.error || "Processing failed";
|
|
124
|
+
setState((prev) => ({
|
|
125
|
+
...prev,
|
|
126
|
+
isProcessing: false,
|
|
127
|
+
error: errorMessage,
|
|
128
|
+
progress: 0,
|
|
129
|
+
}));
|
|
130
|
+
config.onError?.(errorMessage);
|
|
131
|
+
}
|
|
132
|
+
}, [state.sourceImageUri, state.targetImageUri, userId, config, handleProgress]);
|
|
133
|
+
|
|
134
|
+
const save = useCallback(async () => {
|
|
135
|
+
if (!state.processedUrl) return;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
await onSaveImage(state.processedUrl);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
141
|
+
setState((prev) => ({ ...prev, error: message }));
|
|
142
|
+
}
|
|
143
|
+
}, [state.processedUrl, onSaveImage]);
|
|
144
|
+
|
|
145
|
+
const reset = useCallback(() => {
|
|
146
|
+
setState(initialState);
|
|
147
|
+
}, []);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
...state,
|
|
151
|
+
selectSourceImage,
|
|
152
|
+
selectTargetImage,
|
|
153
|
+
process,
|
|
154
|
+
save,
|
|
155
|
+
reset,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anime Selfie Feature Types
|
|
3
|
+
* Request, Result, Config types for anime selfie generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type AnimeSelfieStyle = "anime" | "manga" | "cartoon" | "disney" | "pixar";
|
|
7
|
+
|
|
8
|
+
export interface AnimeSelfieOptions {
|
|
9
|
+
style?: AnimeSelfieStyle;
|
|
10
|
+
preserveBackground?: boolean;
|
|
11
|
+
enhanceDetails?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AnimeSelfieRequest {
|
|
15
|
+
imageUri: string;
|
|
16
|
+
imageBase64?: string;
|
|
17
|
+
userId: string;
|
|
18
|
+
options?: AnimeSelfieOptions;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AnimeSelfieResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
imageUrl?: string;
|
|
24
|
+
imageBase64?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
requestId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AnimeSelfieFeatureState {
|
|
30
|
+
imageUri: string | null;
|
|
31
|
+
processedUrl: string | null;
|
|
32
|
+
isProcessing: boolean;
|
|
33
|
+
progress: number;
|
|
34
|
+
error: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AnimeSelfieTranslations {
|
|
38
|
+
uploadTitle: string;
|
|
39
|
+
uploadSubtitle: string;
|
|
40
|
+
uploadChange: string;
|
|
41
|
+
uploadAnalyzing: string;
|
|
42
|
+
description: string;
|
|
43
|
+
processingText: string;
|
|
44
|
+
processButtonText: string;
|
|
45
|
+
successText: string;
|
|
46
|
+
saveButtonText: string;
|
|
47
|
+
tryAnotherText: string;
|
|
48
|
+
beforeLabel?: string;
|
|
49
|
+
afterLabel?: string;
|
|
50
|
+
compareHint?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type AnimeSelfieInputBuilder = (
|
|
54
|
+
base64: string,
|
|
55
|
+
options?: AnimeSelfieOptions,
|
|
56
|
+
) => Record<string, unknown>;
|
|
57
|
+
|
|
58
|
+
export type AnimeSelfieResultExtractor = (result: unknown) => string | undefined;
|
|
59
|
+
|
|
60
|
+
export interface AnimeSelfieFeatureConfig {
|
|
61
|
+
providerId?: string;
|
|
62
|
+
creditCost?: number;
|
|
63
|
+
defaultStyle?: AnimeSelfieStyle;
|
|
64
|
+
model: string;
|
|
65
|
+
buildInput: AnimeSelfieInputBuilder;
|
|
66
|
+
extractResult?: AnimeSelfieResultExtractor;
|
|
67
|
+
prepareImage: (imageUri: string) => Promise<string>;
|
|
68
|
+
onImageSelect?: (uri: string) => void;
|
|
69
|
+
onProcessingStart?: () => void;
|
|
70
|
+
onProcessingComplete?: (result: AnimeSelfieResult) => void;
|
|
71
|
+
onError?: (error: string) => void;
|
|
72
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anime Selfie Domain Types Index
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
AnimeSelfieStyle,
|
|
7
|
+
AnimeSelfieOptions,
|
|
8
|
+
AnimeSelfieRequest,
|
|
9
|
+
AnimeSelfieResult,
|
|
10
|
+
AnimeSelfieFeatureState,
|
|
11
|
+
AnimeSelfieTranslations,
|
|
12
|
+
AnimeSelfieInputBuilder,
|
|
13
|
+
AnimeSelfieResultExtractor,
|
|
14
|
+
AnimeSelfieFeatureConfig,
|
|
15
|
+
} from "./anime-selfie.types";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anime Selfie Feature
|
|
3
|
+
* Provider-agnostic anime selfie generation feature
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Domain Types
|
|
7
|
+
export type {
|
|
8
|
+
AnimeSelfieStyle,
|
|
9
|
+
AnimeSelfieOptions,
|
|
10
|
+
AnimeSelfieRequest,
|
|
11
|
+
AnimeSelfieResult,
|
|
12
|
+
AnimeSelfieFeatureState,
|
|
13
|
+
AnimeSelfieTranslations,
|
|
14
|
+
AnimeSelfieFeatureConfig,
|
|
15
|
+
AnimeSelfieInputBuilder,
|
|
16
|
+
AnimeSelfieResultExtractor,
|
|
17
|
+
} from "./domain";
|
|
18
|
+
|
|
19
|
+
// Infrastructure Services
|
|
20
|
+
export { executeAnimeSelfie, hasAnimeSelfieSupport } from "./infrastructure";
|
|
21
|
+
export type { ExecuteAnimeSelfieOptions } from "./infrastructure";
|
|
22
|
+
|
|
23
|
+
// Presentation Hooks
|
|
24
|
+
export { useAnimeSelfieFeature } from "./presentation";
|
|
25
|
+
export type {
|
|
26
|
+
UseAnimeSelfieFeatureProps,
|
|
27
|
+
UseAnimeSelfieFeatureReturn,
|
|
28
|
+
} from "./presentation";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anime Selfie Executor
|
|
3
|
+
* Provider-agnostic anime selfie execution using active AI provider
|
|
4
|
+
* Model and input format are provided via options from app level
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { providerRegistry } from "../../../../infrastructure/services";
|
|
8
|
+
import { cleanBase64 } from "../../../../infrastructure/utils";
|
|
9
|
+
import type {
|
|
10
|
+
AnimeSelfieRequest,
|
|
11
|
+
AnimeSelfieResult,
|
|
12
|
+
AnimeSelfieInputBuilder,
|
|
13
|
+
AnimeSelfieResultExtractor,
|
|
14
|
+
} from "../../domain/types";
|
|
15
|
+
|
|
16
|
+
declare const __DEV__: boolean;
|
|
17
|
+
|
|
18
|
+
export interface ExecuteAnimeSelfieOptions {
|
|
19
|
+
model: string;
|
|
20
|
+
buildInput: AnimeSelfieInputBuilder;
|
|
21
|
+
extractResult?: AnimeSelfieResultExtractor;
|
|
22
|
+
onProgress?: (progress: number) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function defaultExtractResult(result: unknown): string | undefined {
|
|
26
|
+
if (typeof result !== "object" || result === null) return undefined;
|
|
27
|
+
|
|
28
|
+
const r = result as Record<string, unknown>;
|
|
29
|
+
|
|
30
|
+
if (typeof r.image === "string") return r.image;
|
|
31
|
+
if (Array.isArray(r.images) && r.images[0]?.url) return r.images[0].url;
|
|
32
|
+
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function executeAnimeSelfie(
|
|
37
|
+
request: AnimeSelfieRequest,
|
|
38
|
+
options: ExecuteAnimeSelfieOptions,
|
|
39
|
+
): Promise<AnimeSelfieResult> {
|
|
40
|
+
const provider = providerRegistry.getActiveProvider();
|
|
41
|
+
|
|
42
|
+
if (!provider) {
|
|
43
|
+
return { success: false, error: "No AI provider configured" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!provider.isInitialized()) {
|
|
47
|
+
return { success: false, error: "AI provider not initialized" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!request.imageBase64) {
|
|
51
|
+
return { success: false, error: "Image base64 is required" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { model, buildInput, extractResult, onProgress } = options;
|
|
55
|
+
|
|
56
|
+
if (__DEV__) {
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.log(`[AnimeSelfie] Provider: ${provider.providerId}, Model: ${model}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
onProgress?.(10);
|
|
63
|
+
|
|
64
|
+
const base64 = cleanBase64(request.imageBase64);
|
|
65
|
+
onProgress?.(30);
|
|
66
|
+
|
|
67
|
+
const input = buildInput(base64, request.options);
|
|
68
|
+
onProgress?.(40);
|
|
69
|
+
|
|
70
|
+
const result = await provider.run(model, input);
|
|
71
|
+
onProgress?.(90);
|
|
72
|
+
|
|
73
|
+
const extractor = extractResult || defaultExtractResult;
|
|
74
|
+
const imageUrl = extractor(result);
|
|
75
|
+
onProgress?.(100);
|
|
76
|
+
|
|
77
|
+
if (!imageUrl) {
|
|
78
|
+
return { success: false, error: "No image in response" };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { success: true, imageUrl };
|
|
82
|
+
} catch (error) {
|
|
83
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
+
if (__DEV__) {
|
|
85
|
+
// eslint-disable-next-line no-console
|
|
86
|
+
console.error("[AnimeSelfie] Error:", message);
|
|
87
|
+
}
|
|
88
|
+
return { success: false, error: message };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function hasAnimeSelfieSupport(): boolean {
|
|
93
|
+
const provider = providerRegistry.getActiveProvider();
|
|
94
|
+
return provider !== null && provider.isInitialized();
|
|
95
|
+
}
|