@umituz/react-native-design-system 4.23.96 → 4.23.100
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/atoms/button/AtomicButton.tsx +7 -0
- package/src/atoms/button/types/index.ts +4 -0
- package/src/atoms/input/hooks/useInputState.ts +3 -7
- package/src/haptics/infrastructure/services/HapticService.ts +1 -1
- package/src/media/infrastructure/hooks/useGenericMediaGeneration.ts +170 -0
- package/src/media/presentation/hooks/useCardMediaGeneration.ts +9 -113
- package/src/media/presentation/hooks/useCardMediaUpload.ts +5 -5
- package/src/media/presentation/hooks/useCardMediaValidation.ts +4 -1
- package/src/media/presentation/hooks/useMediaGeneration.ts +4 -87
- package/src/molecules/navigation/components/NavigationHeader.tsx +3 -3
- package/src/molecules/navigation/utils/AppNavigation.ts +20 -1
- package/src/offline/index.ts +1 -0
- package/src/offline/infrastructure/storage/OfflineConfigStore.ts +34 -0
- package/src/offline/presentation/hooks/useOffline.ts +8 -4
- package/src/storage/domain/utils/devUtils.ts +0 -24
- package/src/storage/index.ts +1 -1
- package/src/storage/infrastructure/adapters/StorageService.ts +2 -7
- package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +0 -3
- package/src/storage/presentation/hooks/CacheStorageOperations.ts +2 -8
- package/src/storage/presentation/hooks/useStore.ts +14 -5
- package/src/utilities/sharing/presentation/hooks/useSharing.ts +3 -3
- package/src/layouts/ScreenLayout/ScreenLayout.example.tsx +0 -92
- package/src/media/domain/entities/CardMultimedia.types.ts +0 -120
- package/src/media/infrastructure/services/CardMediaGenerationService.README.md +0 -99
- package/src/media/infrastructure/services/CardMediaGenerationService.ts +0 -101
- package/src/media/infrastructure/services/CardMediaOptimizerService.README.md +0 -167
- package/src/media/infrastructure/services/CardMediaOptimizerService.ts +0 -36
- package/src/media/infrastructure/services/CardMediaUploadService.README.md +0 -123
- package/src/media/infrastructure/services/CardMediaUploadService.ts +0 -62
- package/src/media/infrastructure/services/CardMediaValidationService.README.md +0 -134
- package/src/media/infrastructure/services/CardMediaValidationService.ts +0 -81
- package/src/media/presentation/hooks/useCardMediaGeneration.README.md +0 -164
- package/src/media/presentation/hooks/useCardMediaUpload.README.md +0 -153
- package/src/media/presentation/hooks/useCardMediaValidation.README.md +0 -176
- package/src/storage/domain/utils/__tests__/devUtils.test.ts +0 -97
|
@@ -9,27 +9,3 @@ export const isDev = (): boolean => {
|
|
|
9
9
|
return __DEV__;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Log warning in development mode only
|
|
14
|
-
* All logs are disabled
|
|
15
|
-
*/
|
|
16
|
-
export const devWarn = (_message: string, ..._args: unknown[]): void => {
|
|
17
|
-
// Disabled
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Log error in development mode only
|
|
22
|
-
* All logs are disabled
|
|
23
|
-
*/
|
|
24
|
-
export const devError = (_message: string, ..._args: unknown[]): void => {
|
|
25
|
-
// Disabled
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Log info in development mode only
|
|
30
|
-
* All logs are disabled
|
|
31
|
-
*/
|
|
32
|
-
export const devLog = (_message: string, ..._args: unknown[]): void => {
|
|
33
|
-
// Disabled
|
|
34
|
-
};
|
|
35
|
-
|
package/src/storage/index.ts
CHANGED
|
@@ -82,7 +82,7 @@ export { TIME_MS, DEFAULT_TTL, CACHE_VERSION } from './domain/constants/CacheDef
|
|
|
82
82
|
// DOMAIN LAYER - Development Utilities
|
|
83
83
|
// =============================================================================
|
|
84
84
|
|
|
85
|
-
export { isDev
|
|
85
|
+
export { isDev } from './domain/utils/devUtils';
|
|
86
86
|
|
|
87
87
|
// =============================================================================
|
|
88
88
|
// DOMAIN LAYER - Store Types
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
9
|
-
import { devWarn } from '../../domain/utils/devUtils';
|
|
10
9
|
import type { StateStorage } from '../../domain/types/Store';
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -18,8 +17,6 @@ export const storageService: StateStorage = {
|
|
|
18
17
|
try {
|
|
19
18
|
return await AsyncStorage.getItem(name);
|
|
20
19
|
} catch (error) {
|
|
21
|
-
const errorMessage = `StorageService: Failed to get item "${name}"`;
|
|
22
|
-
devWarn(errorMessage, error);
|
|
23
20
|
return null;
|
|
24
21
|
}
|
|
25
22
|
},
|
|
@@ -28,8 +25,7 @@ export const storageService: StateStorage = {
|
|
|
28
25
|
try {
|
|
29
26
|
await AsyncStorage.setItem(name, value);
|
|
30
27
|
} catch (error) {
|
|
31
|
-
|
|
32
|
-
devWarn(errorMessage, error);
|
|
28
|
+
// Silent failure
|
|
33
29
|
}
|
|
34
30
|
},
|
|
35
31
|
|
|
@@ -37,8 +33,7 @@ export const storageService: StateStorage = {
|
|
|
37
33
|
try {
|
|
38
34
|
await AsyncStorage.removeItem(name);
|
|
39
35
|
} catch (error) {
|
|
40
|
-
|
|
41
|
-
devWarn(errorMessage, error);
|
|
36
|
+
// Silent failure
|
|
42
37
|
}
|
|
43
38
|
},
|
|
44
39
|
};
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
StorageSerializationError,
|
|
15
15
|
StorageDeserializationError,
|
|
16
16
|
} from '../../domain/errors/StorageError';
|
|
17
|
-
import { devWarn } from '../../domain/utils/devUtils';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Base storage operations implementation
|
|
@@ -81,8 +80,6 @@ export class BaseStorageOperations {
|
|
|
81
80
|
const value = await AsyncStorage.getItem(key);
|
|
82
81
|
return value !== null;
|
|
83
82
|
} catch (error) {
|
|
84
|
-
const errorMessage = `BaseStorageOperations: Failed to check if key "${key}" exists`;
|
|
85
|
-
devWarn(errorMessage, error);
|
|
86
83
|
return false;
|
|
87
84
|
}
|
|
88
85
|
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import { storageRepository } from '../../infrastructure/repositories/AsyncStorageRepository';
|
|
8
8
|
import type { CachedValue } from '../../domain/entities/CachedValue';
|
|
9
9
|
import { createCachedValue } from '../../domain/entities/CachedValue';
|
|
10
|
-
import { devWarn } from '../../domain/utils/devUtils';
|
|
11
10
|
import { isValidCachedValue } from '../../domain/utils/ValidationUtils';
|
|
12
11
|
|
|
13
12
|
export interface CacheStorageOptions {
|
|
@@ -56,16 +55,11 @@ export class CacheStorageOperations {
|
|
|
56
55
|
return parsed as CachedValue<T>;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
if (__DEV__) {
|
|
60
|
-
devWarn(`CacheStorageOperations: Invalid cached data structure for key "${key}"`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
58
|
return null;
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
return null;
|
|
67
62
|
} catch (error) {
|
|
68
|
-
devWarn(`CacheStorageOperations: Failed to load cache for key "${key}"`, error);
|
|
69
63
|
return null;
|
|
70
64
|
}
|
|
71
65
|
}
|
|
@@ -86,7 +80,7 @@ export class CacheStorageOperations {
|
|
|
86
80
|
const cached = createCachedValue(value, ttl || 0, version);
|
|
87
81
|
await storageRepository.setString(key, JSON.stringify(cached));
|
|
88
82
|
} catch (error) {
|
|
89
|
-
|
|
83
|
+
// Silent failure
|
|
90
84
|
}
|
|
91
85
|
}
|
|
92
86
|
|
|
@@ -99,7 +93,7 @@ export class CacheStorageOperations {
|
|
|
99
93
|
try {
|
|
100
94
|
await storageRepository.removeItem(key);
|
|
101
95
|
} catch (error) {
|
|
102
|
-
|
|
96
|
+
// Silent failure
|
|
103
97
|
}
|
|
104
98
|
}
|
|
105
99
|
}
|
|
@@ -3,14 +3,23 @@
|
|
|
3
3
|
* Helper for creating stores in components
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { useMemo
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
7
|
import { createStore } from '../../domain/factories/StoreFactory';
|
|
8
8
|
import type { StoreConfig } from '../../domain/types/Store';
|
|
9
9
|
|
|
10
10
|
export function useStore<T extends object>(config: StoreConfig<T>) {
|
|
11
|
-
//
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
// Stabilize entire config to track all property changes
|
|
12
|
+
const stableConfig = useMemo(
|
|
13
|
+
() => config,
|
|
14
|
+
[
|
|
15
|
+
config.name,
|
|
16
|
+
config.version,
|
|
17
|
+
config.persist,
|
|
18
|
+
config.storage,
|
|
19
|
+
config.ttl,
|
|
20
|
+
]
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const store = useMemo(() => createStore(stableConfig), [stableConfig]);
|
|
15
24
|
return store;
|
|
16
25
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @layer presentation/hooks
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { useState, useCallback, useEffect } from 'react';
|
|
11
|
+
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
12
12
|
import { SharingService } from '../../infrastructure/services/SharingService';
|
|
13
13
|
import type { ShareOptions } from '../../domain/entities/Share';
|
|
14
14
|
|
|
@@ -140,7 +140,7 @@ export const useSharing = () => {
|
|
|
140
140
|
[]
|
|
141
141
|
);
|
|
142
142
|
|
|
143
|
-
return {
|
|
143
|
+
return useMemo(() => ({
|
|
144
144
|
// Functions
|
|
145
145
|
share,
|
|
146
146
|
shareWithAutoType,
|
|
@@ -150,5 +150,5 @@ export const useSharing = () => {
|
|
|
150
150
|
isAvailable,
|
|
151
151
|
isSharing,
|
|
152
152
|
error,
|
|
153
|
-
};
|
|
153
|
+
}), [share, shareWithAutoType, shareMultiple, isAvailable, isSharing, error]);
|
|
154
154
|
};
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enhanced ScreenLayout Example
|
|
3
|
-
*
|
|
4
|
-
* This demonstrates the recommended usage of ScreenLayout with SafeAreaProvider
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React from 'react';
|
|
8
|
-
import { View, Text } from 'react-native';
|
|
9
|
-
import {
|
|
10
|
-
SafeAreaProvider,
|
|
11
|
-
ScreenLayout,
|
|
12
|
-
ScreenHeader,
|
|
13
|
-
AtomicButton,
|
|
14
|
-
AtomicText
|
|
15
|
-
} from '../../index';
|
|
16
|
-
|
|
17
|
-
// 1. Wrap your app root with SafeAreaProvider
|
|
18
|
-
export function App() {
|
|
19
|
-
return (
|
|
20
|
-
<SafeAreaProvider>
|
|
21
|
-
<View />
|
|
22
|
-
</SafeAreaProvider>
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// 2. Use ScreenLayout in your screens
|
|
27
|
-
export function HomeScreen() {
|
|
28
|
-
return (
|
|
29
|
-
<ScreenLayout
|
|
30
|
-
// Safe area edges - default is ['top']
|
|
31
|
-
edges={['top']}
|
|
32
|
-
// Enable scrolling - default is true
|
|
33
|
-
scrollable={true}
|
|
34
|
-
// Optional header
|
|
35
|
-
header={
|
|
36
|
-
<ScreenHeader
|
|
37
|
-
title="Home"
|
|
38
|
-
/>
|
|
39
|
-
}
|
|
40
|
-
// Optional footer
|
|
41
|
-
footer={
|
|
42
|
-
<View style={{ padding: 16 }}>
|
|
43
|
-
<AtomicButton onPress={() => console.log('Action')}>
|
|
44
|
-
Action Button
|
|
45
|
-
</AtomicButton>
|
|
46
|
-
</View>
|
|
47
|
-
}
|
|
48
|
-
>
|
|
49
|
-
<AtomicText type="headlineLarge">Welcome to Home</AtomicText>
|
|
50
|
-
<AtomicText type="bodyMedium">
|
|
51
|
-
This screen uses ScreenLayout with default safe area configuration.
|
|
52
|
-
</AtomicText>
|
|
53
|
-
</ScreenLayout>
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 3. Modal screen example with different safe area edges
|
|
58
|
-
export function ModalScreen() {
|
|
59
|
-
return (
|
|
60
|
-
<ScreenLayout
|
|
61
|
-
// Full safe area for modals
|
|
62
|
-
edges={['top', 'bottom']}
|
|
63
|
-
scrollable={false}
|
|
64
|
-
>
|
|
65
|
-
<AtomicText type="headlineMedium">Modal Content</AtomicText>
|
|
66
|
-
</ScreenLayout>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 4. Screen with custom scroll behavior
|
|
71
|
-
export function CustomScrollScreen() {
|
|
72
|
-
return (
|
|
73
|
-
<ScreenLayout scrollable={false}>
|
|
74
|
-
{/* Your custom scroll component */}
|
|
75
|
-
<View />
|
|
76
|
-
</ScreenLayout>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 5. Using safe area hooks directly
|
|
81
|
-
import { useContentSafeAreaPadding, useSafeAreaInsets } from '../../index';
|
|
82
|
-
|
|
83
|
-
export function CustomComponent() {
|
|
84
|
-
const insets = useSafeAreaInsets();
|
|
85
|
-
const { paddingBottom } = useContentSafeAreaPadding();
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<View style={{ paddingTop: insets.top, paddingBottom }}>
|
|
89
|
-
<Text>Custom Safe Area Usage</Text>
|
|
90
|
-
</View>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Card Multimedia Types
|
|
3
|
-
* Multimedia support for flashcard functionality
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export type CardMediaType = "image" | "audio" | "video";
|
|
7
|
-
export type CardMediaPosition = "front" | "back" | "both";
|
|
8
|
-
|
|
9
|
-
export interface CardMediaAttachment {
|
|
10
|
-
id: string;
|
|
11
|
-
type: CardMediaType;
|
|
12
|
-
position: CardMediaPosition;
|
|
13
|
-
url: string;
|
|
14
|
-
localPath?: string;
|
|
15
|
-
filename: string;
|
|
16
|
-
fileSize: number;
|
|
17
|
-
mimeType: string;
|
|
18
|
-
duration?: number; // For audio/video in seconds
|
|
19
|
-
thumbnailUrl?: string; // For videos
|
|
20
|
-
caption?: string;
|
|
21
|
-
isDownloaded: boolean;
|
|
22
|
-
createdAt: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface CardMultimediaFlashcard {
|
|
26
|
-
id: string;
|
|
27
|
-
front: string;
|
|
28
|
-
back: string;
|
|
29
|
-
difficulty: "easy" | "medium" | "hard";
|
|
30
|
-
tags: string[];
|
|
31
|
-
createdAt?: string;
|
|
32
|
-
updatedAt?: string;
|
|
33
|
-
// Extended properties for multimedia support
|
|
34
|
-
media: CardMediaAttachment[];
|
|
35
|
-
hasMedia: boolean; // Computed property
|
|
36
|
-
mediaType: CardMediaType[]; // Array of media types present
|
|
37
|
-
isDownloaded: boolean; // All media downloaded?
|
|
38
|
-
estimatedSize: number; // Total size in bytes
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface CardMediaGenerationRequest {
|
|
42
|
-
type: "text_to_image" | "text_to_audio" | "image_search";
|
|
43
|
-
input: {
|
|
44
|
-
text?: string;
|
|
45
|
-
prompt?: string;
|
|
46
|
-
language?: string;
|
|
47
|
-
voice?: "male" | "female" | "neutral";
|
|
48
|
-
style?: "realistic" | "cartoon" | "artistic";
|
|
49
|
-
};
|
|
50
|
-
options: {
|
|
51
|
-
maxResults?: number;
|
|
52
|
-
quality?: "low" | "medium" | "high";
|
|
53
|
-
format?: "jpeg" | "png" | "mp3" | "wav";
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface CardMediaGenerationResult {
|
|
58
|
-
success: boolean;
|
|
59
|
-
attachments: CardMediaAttachment[];
|
|
60
|
-
creditsUsed: number;
|
|
61
|
-
processingTime: number;
|
|
62
|
-
error?: string;
|
|
63
|
-
requestId: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface CardMediaUploadProgress {
|
|
67
|
-
fileId: string;
|
|
68
|
-
progress: number; // 0-100
|
|
69
|
-
status: "uploading" | "processing" | "completed" | "error";
|
|
70
|
-
error?: string;
|
|
71
|
-
url?: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface CardMediaCompressionOptions {
|
|
75
|
-
quality: number; // 0.1 - 1.0
|
|
76
|
-
maxWidth?: number;
|
|
77
|
-
maxHeight?: number;
|
|
78
|
-
maxFileSize?: number; // bytes
|
|
79
|
-
format?: "jpeg" | "png" | "webp";
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface CardMediaFile {
|
|
83
|
-
name: string;
|
|
84
|
-
type: string;
|
|
85
|
-
size: number;
|
|
86
|
-
uri?: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface CreateCardMultimediaData {
|
|
90
|
-
front: string;
|
|
91
|
-
back: string;
|
|
92
|
-
difficulty?: "easy" | "medium" | "hard";
|
|
93
|
-
tags?: string[];
|
|
94
|
-
media?: CardMediaAttachment[];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface CardMediaValidation {
|
|
98
|
-
isValid: boolean;
|
|
99
|
-
errors: string[];
|
|
100
|
-
warnings: string[];
|
|
101
|
-
recommendations: string[];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export interface CardMultimediaFlashcardService {
|
|
105
|
-
uploadMedia(
|
|
106
|
-
file: CardMediaFile,
|
|
107
|
-
options?: CardMediaCompressionOptions,
|
|
108
|
-
): Promise<CardMediaAttachment>;
|
|
109
|
-
generateMedia(
|
|
110
|
-
request: CardMediaGenerationRequest,
|
|
111
|
-
): Promise<CardMediaGenerationResult>;
|
|
112
|
-
validateMedia(file: CardMediaFile): Promise<CardMediaValidation>;
|
|
113
|
-
optimizeMedia(
|
|
114
|
-
attachment: CardMediaAttachment,
|
|
115
|
-
options: CardMediaCompressionOptions,
|
|
116
|
-
): Promise<CardMediaAttachment>;
|
|
117
|
-
deleteMedia(attachmentId: string): Promise<void>;
|
|
118
|
-
getMediaUrl(attachmentId: string): Promise<string>;
|
|
119
|
-
downloadMedia(attachmentId: string): Promise<string>; // Returns local path
|
|
120
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# CardMediaGenerationService
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
Service that performs AI-based media generation operations for flashcards, including text-to-image, text-to-audio, and image-search capabilities.
|
|
5
|
-
|
|
6
|
-
## File Location
|
|
7
|
-
`/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-media/src/infrastructure/services/CardMediaGenerationService`
|
|
8
|
-
|
|
9
|
-
## Strategy
|
|
10
|
-
- Generate media content using AI APIs for card applications
|
|
11
|
-
- Support multiple generation types (text-to-image, text-to-audio, image-search)
|
|
12
|
-
- Track processing time and credit/balance usage
|
|
13
|
-
- Return card-compatible media attachments with position support
|
|
14
|
-
- Provide detailed result information including success status and error messages
|
|
15
|
-
- Enable batch generation with multiple results
|
|
16
|
-
- Maintain unique request IDs for tracking and debugging
|
|
17
|
-
|
|
18
|
-
## Forbidden
|
|
19
|
-
- **DO NOT** generate media without checking available credits/balance
|
|
20
|
-
- **DO NOT** use empty or null prompts for generation
|
|
21
|
-
- **DO NOT** assume all generation requests will succeed
|
|
22
|
-
- **DO NOT** ignore generation timeout limits
|
|
23
|
-
- **DO NOT** generate inappropriate or copyrighted content
|
|
24
|
-
- **DO NOT** proceed without error handling for API failures
|
|
25
|
-
- **DO NOT** mix generation types in single requests
|
|
26
|
-
- **DO NOT** bypass request ID tracking
|
|
27
|
-
|
|
28
|
-
## Rules
|
|
29
|
-
1. All generation operations must include a valid prompt
|
|
30
|
-
2. Text-to-image generation requires prompt and optional style/maxResults
|
|
31
|
-
3. Text-to-audio generation requires prompt, language, and optional voice/speed
|
|
32
|
-
4. Image search requires keyword prompt and optional maxResults
|
|
33
|
-
5. Credit costs vary by type: text-to-image (5), text-to-audio (3), image-search (2)
|
|
34
|
-
6. All generated media must have position attribute set to 'both' by default
|
|
35
|
-
7. Processing time must be tracked and returned in milliseconds
|
|
36
|
-
8. Unique request IDs must be generated for each operation
|
|
37
|
-
9. Generation failures must return clear error messages
|
|
38
|
-
10. Audio files must be assigned a 10-second duration by default
|
|
39
|
-
11. maxResults defaults to 1 for text-to-image, 5 for image-search
|
|
40
|
-
|
|
41
|
-
## AI Agent Guidelines
|
|
42
|
-
|
|
43
|
-
When working with CardMediaGenerationService:
|
|
44
|
-
|
|
45
|
-
1. **Pre-generation Checks**: Always verify credit/balance before generation requests
|
|
46
|
-
2. **Prompt Quality**: Use clear, descriptive prompts for better results
|
|
47
|
-
3. **Type Selection**: Choose appropriate generation type for use case
|
|
48
|
-
4. **Result Validation**: Always check success flag before using results
|
|
49
|
-
5. **Error Handling**: Handle API failures, timeouts, and insufficient credits
|
|
50
|
-
6. **Position Assignment**: Set correct position (front/back/both) for card use
|
|
51
|
-
7. **Request Tracking**: Store request IDs for debugging and support
|
|
52
|
-
|
|
53
|
-
### Generation Type Guidelines
|
|
54
|
-
|
|
55
|
-
- **Text-to-Image**: Use for visual content on cards (front typically)
|
|
56
|
-
- Specify style (realistic, artistic) for better results
|
|
57
|
-
- Set maxResults to 1 unless multiple options needed
|
|
58
|
-
- Images receive position='both' by default
|
|
59
|
-
|
|
60
|
-
- **Text-to-Audio**: Use for pronunciation or audio explanations
|
|
61
|
-
- Specify language code (tr-TR, en-US, etc.)
|
|
62
|
-
- Set voice type (male, female) if applicable
|
|
63
|
-
- Adjust speed (0.5-2.0) for playback preferences
|
|
64
|
-
- Audio receives 10-second duration by default
|
|
65
|
-
|
|
66
|
-
- **Image Search**: Use for finding relevant visuals
|
|
67
|
-
- Use specific keywords for better results
|
|
68
|
-
- Set maxResults for multiple options
|
|
69
|
-
- Credits are cheaper than text-to-image
|
|
70
|
-
|
|
71
|
-
### Credit Management
|
|
72
|
-
|
|
73
|
-
- Calculate total credits before batch operations
|
|
74
|
-
- Text-to-image: 5 credits per request
|
|
75
|
-
- Text-to-audio: 3 credits per request
|
|
76
|
-
- Image search: 2 credits per request
|
|
77
|
-
- Handle insufficient credit errors gracefully
|
|
78
|
-
- Track credits used from generation results
|
|
79
|
-
|
|
80
|
-
### Error Handling Patterns
|
|
81
|
-
|
|
82
|
-
- Check success flag before accessing attachments
|
|
83
|
-
- Parse error messages for specific failure types:
|
|
84
|
-
- "insufficient credits" - balance too low
|
|
85
|
-
- "timeout" - operation took too long
|
|
86
|
-
- "invalid prompt" - prompt validation failed
|
|
87
|
-
- "api error" - underlying API failure
|
|
88
|
-
|
|
89
|
-
### Performance Considerations
|
|
90
|
-
|
|
91
|
-
- Generation operations are simulated (3 seconds delay)
|
|
92
|
-
- Batch multiple results in single request when possible
|
|
93
|
-
- Track processing time for performance monitoring
|
|
94
|
-
- Use request IDs for debugging slow operations
|
|
95
|
-
|
|
96
|
-
## Dependencies
|
|
97
|
-
- CardMediaAttachment type from domain layer
|
|
98
|
-
- AI generation APIs (simulated in development)
|
|
99
|
-
- Credit/balance management system
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Card Media Generation Service
|
|
3
|
-
* Handles AI media generation (text-to-image, text-to-audio, image-search)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
CardMediaAttachment,
|
|
8
|
-
CardMediaGenerationRequest,
|
|
9
|
-
CardMediaGenerationResult,
|
|
10
|
-
CardMediaType,
|
|
11
|
-
CardMediaPosition,
|
|
12
|
-
} from "../../domain/entities/CardMultimedia.types";
|
|
13
|
-
|
|
14
|
-
export class CardMediaGenerationService {
|
|
15
|
-
/**
|
|
16
|
-
* Generate media from AI (text-to-image, text-to-audio, etc.)
|
|
17
|
-
*/
|
|
18
|
-
async generateMedia(
|
|
19
|
-
request: CardMediaGenerationRequest,
|
|
20
|
-
): Promise<CardMediaGenerationResult> {
|
|
21
|
-
try {
|
|
22
|
-
const startTime = Date.now();
|
|
23
|
-
|
|
24
|
-
// Simulate AI generation
|
|
25
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
26
|
-
|
|
27
|
-
const attachments: CardMediaAttachment[] = [];
|
|
28
|
-
|
|
29
|
-
switch (request.type) {
|
|
30
|
-
case "text_to_image":
|
|
31
|
-
for (let i = 0; i < (request.options.maxResults || 1); i++) {
|
|
32
|
-
attachments.push({
|
|
33
|
-
id: `ai_img_${Date.now()}_${i}`,
|
|
34
|
-
type: "image" as CardMediaType,
|
|
35
|
-
position: "both" as CardMediaPosition,
|
|
36
|
-
url: `https://picsum.photos/400/300?random=${Date.now() + i}`,
|
|
37
|
-
filename: `ai_generated_${i}.jpg`,
|
|
38
|
-
fileSize: 150000, // 150KB
|
|
39
|
-
mimeType: "image/jpeg",
|
|
40
|
-
isDownloaded: false,
|
|
41
|
-
createdAt: new Date().toISOString(),
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
break;
|
|
45
|
-
|
|
46
|
-
case "text_to_audio":
|
|
47
|
-
attachments.push({
|
|
48
|
-
id: `ai_audio_${Date.now()}`,
|
|
49
|
-
type: "audio" as CardMediaType,
|
|
50
|
-
position: "back" as CardMediaPosition,
|
|
51
|
-
url: `https://example.com/audio_${Date.now()}.mp3`,
|
|
52
|
-
filename: `ai_generated_${Date.now()}.mp3`,
|
|
53
|
-
fileSize: 80000, // 80KB
|
|
54
|
-
mimeType: "audio/mp3",
|
|
55
|
-
duration: 10, // 10 seconds
|
|
56
|
-
isDownloaded: false,
|
|
57
|
-
createdAt: new Date().toISOString(),
|
|
58
|
-
});
|
|
59
|
-
break;
|
|
60
|
-
|
|
61
|
-
case "image_search":
|
|
62
|
-
for (let i = 0; i < (request.options.maxResults || 5); i++) {
|
|
63
|
-
attachments.push({
|
|
64
|
-
id: `search_img_${Date.now()}_${i}`,
|
|
65
|
-
type: "image" as CardMediaType,
|
|
66
|
-
position: "both" as CardMediaPosition,
|
|
67
|
-
url: `https://picsum.photos/400/300?random=${Date.now() + i}`,
|
|
68
|
-
filename: `search_result_${i}.jpg`,
|
|
69
|
-
fileSize: 120000, // 120KB
|
|
70
|
-
mimeType: "image/jpeg",
|
|
71
|
-
isDownloaded: false,
|
|
72
|
-
createdAt: new Date().toISOString(),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
success: true,
|
|
80
|
-
attachments,
|
|
81
|
-
creditsUsed:
|
|
82
|
-
request.type === "text_to_image"
|
|
83
|
-
? 5
|
|
84
|
-
: request.type === "text_to_audio"
|
|
85
|
-
? 3
|
|
86
|
-
: 2,
|
|
87
|
-
processingTime: Date.now() - startTime,
|
|
88
|
-
requestId: `req_${Date.now()}`,
|
|
89
|
-
};
|
|
90
|
-
} catch (error) {
|
|
91
|
-
return {
|
|
92
|
-
success: false,
|
|
93
|
-
attachments: [],
|
|
94
|
-
creditsUsed: 0,
|
|
95
|
-
processingTime: 0,
|
|
96
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
97
|
-
requestId: "",
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|