@umituz/react-native-ai-generation-content 1.17.86 → 1.17.88

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 (37) hide show
  1. package/package.json +2 -2
  2. package/src/domains/creations/index.ts +6 -3
  3. package/src/domains/creations/presentation/components/CreationBadges.tsx +3 -3
  4. package/src/domains/creations/presentation/components/CreationDetail/DetailStory.tsx +5 -8
  5. package/src/domains/creations/presentation/components/CreationThumbnail.tsx +1 -1
  6. package/src/domains/creations/presentation/components/index.ts +0 -3
  7. package/src/domains/creations/presentation/screens/CreationDetailScreen.tsx +5 -4
  8. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +2 -11
  9. package/src/domains/creations/presentation/utils/filterUtils.ts +18 -1
  10. package/src/domains/flashcard-generation/FlashcardGenerationService.ts +85 -311
  11. package/src/domains/flashcard-generation/builders/flashcard-prompt.builder.ts +55 -0
  12. package/src/domains/flashcard-generation/parsers/flashcard-response.parser.ts +68 -0
  13. package/src/domains/flashcard-generation/types/flashcard.types.ts +56 -0
  14. package/src/domains/flashcard-generation/validators/flashcard.validator.ts +67 -0
  15. package/src/features/image-to-video/presentation/components/MusicMoodSelector.tsx +3 -3
  16. package/src/features/replace-background/presentation/components/ResultDisplay.tsx +1 -2
  17. package/src/features/script-generator/presentation/components/ScriptDisplay.tsx +3 -4
  18. package/src/features/text-to-image/domain/constants/index.ts +3 -3
  19. package/src/features/text-to-image/domain/constants/options.constants.ts +12 -25
  20. package/src/features/text-to-image/index.ts +12 -4
  21. package/src/features/text-to-image/presentation/hooks/index.ts +8 -0
  22. package/src/features/text-to-image/presentation/hooks/useTextToImageCallbacksBuilder.ts +200 -0
  23. package/src/features/text-to-video/presentation/components/HeroSection.tsx +2 -8
  24. package/src/features/text-to-video/presentation/components/OptionsPanel.tsx +3 -5
  25. package/src/presentation/components/AIGenerationForm.tsx +0 -1
  26. package/src/presentation/components/AIGenerationHero.tsx +27 -24
  27. package/src/presentation/components/GenerationProgressContent.tsx +4 -2
  28. package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.tsx +12 -26
  29. package/src/presentation/components/StylePresetsGrid.tsx +5 -3
  30. package/src/presentation/components/buttons/GenerateButton.tsx +39 -102
  31. package/src/presentation/components/headers/FeatureHeader.tsx +15 -9
  32. package/src/presentation/components/image-picker/DualImagePicker.tsx +0 -6
  33. package/src/presentation/components/image-picker/ImagePickerBox.tsx +27 -16
  34. package/src/presentation/components/modals/SettingsSheet.tsx +4 -2
  35. package/src/presentation/components/result/ResultImageCard.tsx +12 -35
  36. package/src/presentation/components/result/ResultStoryCard.tsx +10 -16
  37. package/src/domains/creations/presentation/components/CreationsProvider.tsx +0 -56
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.17.86",
3
+ "version": "1.17.88",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -84,9 +84,9 @@
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",
88
87
  "expo-localization": "^17.0.8",
89
88
  "expo-sharing": "^14.0.8",
89
+ "expo-video": "^2.0.0",
90
90
  "firebase": "^12.6.0",
91
91
  "i18next": "^25.7.3",
92
92
  "react": "19.1.0",
@@ -149,10 +149,13 @@ export {
149
149
  // Gallery Components
150
150
  export { CreationsHomeCard } from "./presentation/components/CreationsHomeCard";
151
151
  export { EmptyState } from "./presentation/components/EmptyState";
152
+
153
+ // Utilities
152
154
  export {
153
- CreationsProvider,
154
- useCreationsProvider,
155
- } from "./presentation/components/CreationsProvider";
155
+ getLocalizedTitle,
156
+ getFilterCategoriesFromConfig,
157
+ getTranslatedTypes,
158
+ } from "./presentation/utils/filterUtils";
156
159
 
157
160
  // =============================================================================
158
161
  // PRESENTATION LAYER - Screens
@@ -78,17 +78,17 @@ export function CreationBadges({
78
78
  paddingHorizontal: 8,
79
79
  paddingVertical: 4,
80
80
  borderRadius: 12,
81
- backgroundColor: "rgba(0,0,0,0.6)",
81
+ backgroundColor: tokens.colors.modalOverlay,
82
82
  gap: 4,
83
83
  },
84
84
  typeText: {
85
85
  fontSize: 10,
86
86
  fontWeight: "600",
87
- color: "white",
87
+ color: tokens.colors.textInverse,
88
88
  textTransform: "capitalize",
89
89
  },
90
90
  }),
91
- [statusColor]
91
+ [statusColor, tokens]
92
92
  );
93
93
 
94
94
  return (
@@ -2,7 +2,6 @@
2
2
  import React from 'react';
3
3
  import { View, StyleSheet } from 'react-native';
4
4
  import { AtomicText, useAppDesignTokens, type DesignTokens } from "@umituz/react-native-design-system";
5
- import { LinearGradient } from 'expo-linear-gradient';
6
5
 
7
6
  interface DetailStoryProps {
8
7
  readonly story: string;
@@ -16,16 +15,13 @@ export const DetailStory: React.FC<DetailStoryProps> = ({ story }) => {
16
15
 
17
16
  return (
18
17
  <View style={styles.container}>
19
- <LinearGradient
20
- colors={[tokens.colors.primary + '15', tokens.colors.primary + '05']}
21
- style={styles.gradient}
22
- >
18
+ <View style={styles.storyContainer}>
23
19
  <AtomicText style={styles.quoteMark}>&quot;</AtomicText>
24
20
  <AtomicText style={styles.text}>{story}</AtomicText>
25
21
  <View style={styles.quoteEndRow}>
26
22
  <AtomicText style={[styles.quoteMark, styles.quoteEnd]}>&quot;</AtomicText>
27
23
  </View>
28
- </LinearGradient>
24
+ </View>
29
25
  </View>
30
26
  );
31
27
  };
@@ -35,11 +31,12 @@ const useStyles = (tokens: DesignTokens) => StyleSheet.create({
35
31
  paddingHorizontal: tokens.spacing.lg,
36
32
  marginBottom: tokens.spacing.lg,
37
33
  },
38
- gradient: {
34
+ storyContainer: {
39
35
  padding: tokens.spacing.lg,
40
36
  borderRadius: 20,
41
37
  borderWidth: 1,
42
- borderColor: tokens.colors.primary + '20',
38
+ borderColor: tokens.colors.border,
39
+ backgroundColor: tokens.colors.surface,
43
40
  },
44
41
  quoteMark: {
45
42
  fontSize: 48,
@@ -35,7 +35,7 @@ export function CreationThumbnail({
35
35
  },
36
36
  overlay: {
37
37
  ...StyleSheet.absoluteFillObject,
38
- backgroundColor: "rgba(0, 0, 0, 0.5)",
38
+ backgroundColor: tokens.colors.modalOverlay,
39
39
  borderRadius: tokens.spacing.sm,
40
40
  justifyContent: "center",
41
41
  alignItems: "center",
@@ -31,9 +31,6 @@ export { CreationsHomeCard } from "./CreationsHomeCard";
31
31
  export { CreationImageViewer } from "./CreationImageViewer";
32
32
  export { CreationsGrid } from "./CreationsGrid";
33
33
 
34
- // Provider
35
- export { CreationsProvider, useCreationsProvider } from "./CreationsProvider";
36
-
37
34
  // Detail Components
38
35
  export { DetailHeader } from "./CreationDetail/DetailHeader";
39
36
  export { DetailImage } from "./CreationDetail/DetailImage";
@@ -3,20 +3,21 @@ import { View, ScrollView, StyleSheet } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
4
  import { useAppDesignTokens } from "@umituz/react-native-design-system";
5
5
  import type { Creation } from '../../domain/entities/Creation';
6
+ import type { CreationsConfig } from '../../domain/value-objects/CreationsConfig';
6
7
  import { hasVideoContent, getPreviewUrl } from '../../domain/utils';
7
8
  import { DetailHeader } from '../components/CreationDetail/DetailHeader';
8
9
  import { DetailImage } from '../components/CreationDetail/DetailImage';
9
10
  import { DetailVideo } from '../components/CreationDetail/DetailVideo';
10
11
  import { DetailStory } from '../components/CreationDetail/DetailStory';
11
12
  import { DetailActions } from '../components/CreationDetail/DetailActions';
12
-
13
- import { useCreationsProvider } from '../components/CreationsProvider';
13
+ import { getLocalizedTitle } from '../utils/filterUtils';
14
14
 
15
15
  /** Video creation types */
16
16
  const VIDEO_TYPES = ['text-to-video', 'image-to-video'] as const;
17
17
 
18
18
  interface CreationDetailScreenProps {
19
19
  readonly creation: Creation;
20
+ readonly config: CreationsConfig;
20
21
  readonly onClose: () => void;
21
22
  readonly onShare: (creation: Creation) => void;
22
23
  readonly onDelete: (creation: Creation) => void;
@@ -32,6 +33,7 @@ interface CreationMetadata {
32
33
 
33
34
  export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
34
35
  creation,
36
+ config,
35
37
  onClose,
36
38
  onShare,
37
39
  onDelete,
@@ -39,7 +41,6 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
39
41
  }) => {
40
42
  const tokens = useAppDesignTokens();
41
43
  const insets = useSafeAreaInsets();
42
- const { getLocalizedTitle } = useCreationsProvider();
43
44
 
44
45
  // Extract data safely
45
46
  const metadata = (creation.metadata || {}) as CreationMetadata;
@@ -48,7 +49,7 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
48
49
  // 1. Manually set names in metadata
49
50
  // 2. Localized title from config types mapping
50
51
  // 3. Fallback to raw creation type (formatted)
51
- const title = metadata.names || getLocalizedTitle(creation.type);
52
+ const title = metadata.names || getLocalizedTitle(config, t, creation.type);
52
53
  const story = metadata.story || metadata.description || "";
53
54
  const date = metadata.date || new Date(creation.createdAt).toLocaleDateString();
54
55
 
@@ -19,7 +19,6 @@ import type { Creation } from "../../domain/entities/Creation";
19
19
  import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
20
20
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
21
21
  import { CreationDetailScreen } from "./CreationDetailScreen";
22
- import { CreationsProvider } from "../components/CreationsProvider";
23
22
 
24
23
  interface CreationsGalleryScreenProps {
25
24
  readonly userId: string | null;
@@ -34,15 +33,7 @@ interface CreationsGalleryScreenProps {
34
33
  readonly showFilter?: boolean;
35
34
  }
36
35
 
37
- export function CreationsGalleryScreen(props: CreationsGalleryScreenProps) {
38
- return (
39
- <CreationsProvider config={props.config} t={props.t}>
40
- <CreationsGalleryScreenContent {...props} />
41
- </CreationsProvider>
42
- );
43
- }
44
-
45
- function CreationsGalleryScreenContent({
36
+ export function CreationsGalleryScreen({
46
37
  userId,
47
38
  repository,
48
39
  config,
@@ -164,7 +155,7 @@ function CreationsGalleryScreenContent({
164
155
  ), [isLoading, creations, filters.isFiltered, tokens, t, config, emptyActionLabel, onEmptyAction, filters.clearAllFilters]);
165
156
 
166
157
  if (selectedCreation) {
167
- return <CreationDetailScreen creation={selectedCreation} onClose={() => setSelectedCreation(null)} onShare={handleShare} onDelete={handleDelete} t={t} />;
158
+ return <CreationDetailScreen creation={selectedCreation} config={config} onClose={() => setSelectedCreation(null)} onShare={handleShare} onDelete={handleDelete} t={t} />;
168
159
  }
169
160
 
170
161
  return (
@@ -36,7 +36,7 @@ export const getFilterCategoriesFromConfig = (
36
36
 
37
37
  /**
38
38
  * Translates the creation types for display.
39
- *
39
+ *
40
40
  * @param config The creations configuration object
41
41
  * @param t Translation function
42
42
  * @returns Array of types with translated labels
@@ -50,3 +50,20 @@ export const getTranslatedTypes = (
50
50
  labelKey: t(type.labelKey)
51
51
  }));
52
52
  };
53
+
54
+ /**
55
+ * Gets the localized title for a creation type.
56
+ *
57
+ * @param config The creations configuration object
58
+ * @param t Translation function
59
+ * @param typeId The creation type ID
60
+ * @returns Localized title string
61
+ */
62
+ export const getLocalizedTitle = (
63
+ config: CreationsConfig,
64
+ t: (key: string) => string,
65
+ typeId: string
66
+ ): string => {
67
+ const typeConfig = config.types.find(type => type.id === typeId);
68
+ return typeConfig ? t(typeConfig.labelKey) : typeId;
69
+ };
@@ -3,328 +3,102 @@
3
3
  * AI-powered flashcard generation for educational content
4
4
  */
5
5
 
6
- export interface FlashcardGenerationRequest {
7
- topic: string;
8
- difficulty: "beginner" | "intermediate" | "advanced";
9
- count: number;
10
- language?: string;
11
- format?: "qa" | "definition" | "fill_blank" | "multiple_choice";
12
- context?: string;
13
- tags?: string[];
14
- includeImages?: boolean;
15
- }
16
-
17
- export interface GeneratedFlashcard {
18
- id: string;
19
- front: string;
20
- back: string;
21
- difficulty: "easy" | "medium" | "hard";
22
- tags: string[];
23
- source: "ai_generated";
24
- generationRequest: FlashcardGenerationRequest;
25
- confidence: number; // 0-1 score
26
- createdAt?: string;
27
- }
28
-
29
- export interface FlashcardGenerationResult {
30
- success: boolean;
31
- flashcards: GeneratedFlashcard[];
32
- creditsUsed: number;
33
- tokensUsed: number;
34
- processingTime: number; // milliseconds
35
- error?: string;
36
- requestId: string;
37
- }
38
-
39
- export class FlashcardGenerationService {
40
- private static instance: FlashcardGenerationService;
41
-
42
- static getInstance(): FlashcardGenerationService {
43
- if (!FlashcardGenerationService.instance) {
44
- FlashcardGenerationService.instance = new FlashcardGenerationService();
45
- }
46
- return FlashcardGenerationService.instance;
47
- }
6
+ import type {
7
+ FlashcardGenerationRequest,
8
+ FlashcardGenerationResult,
9
+ FlashcardGenerationResponse,
10
+ FlashcardValidationResult,
11
+ } from "./types/flashcard.types";
12
+ import { buildFlashcardPrompt, calculateMaxTokens } from "./builders/flashcard-prompt.builder";
13
+ import { parseFlashcardsFromResponse } from "./parsers/flashcard-response.parser";
14
+ import { validateFlashcard } from "./validators/flashcard.validator";
15
+
16
+ // Re-export types for backward compatibility
17
+ export type {
18
+ FlashcardGenerationRequest,
19
+ FlashcardGenerationResult,
20
+ FlashcardValidationResult,
21
+ GeneratedFlashcard,
22
+ } from "./types/flashcard.types";
23
+
24
+ const CREDITS_PER_FLASHCARD = 2;
25
+ const MOCK_DELAY_MS = 2000;
48
26
 
49
- /**
50
- * Generate flashcards using AI
51
- */
52
- async generateFlashcards(
53
- request: FlashcardGenerationRequest,
54
- ): Promise<FlashcardGenerationResult> {
55
- try {
56
- const startTime = Date.now();
57
-
58
- // Create AI generation prompt
59
- const prompt = this.buildFlashcardPrompt(request);
60
-
61
- // Execute generation
62
- const result = await this.executeGeneration(prompt, request.count);
63
-
64
- // Parse AI response into flashcards
65
- const flashcards = this.parseFlashcardsFromResult(result, request);
66
- const processingTime = Date.now() - startTime;
67
-
68
- return {
69
- success: true,
70
- flashcards,
71
- creditsUsed: request.count * 2, // 2 credits per flashcard
72
- tokensUsed: result.metadata?.tokensUsed || 0,
73
- processingTime,
74
- requestId: result.jobId || `req_${Date.now()}`,
75
- };
76
- } catch (error) {
77
- return {
78
- success: false,
79
- flashcards: [],
80
- creditsUsed: 0,
81
- tokensUsed: 0,
82
- processingTime: 0,
83
- error: error instanceof Error ? error.message : "Unknown error",
84
- requestId: "",
85
- };
86
- }
87
- }
27
+ /**
28
+ * Generate flashcards using AI
29
+ */
30
+ export async function generateFlashcards(
31
+ request: FlashcardGenerationRequest,
32
+ ): Promise<FlashcardGenerationResult> {
33
+ const startTime = Date.now();
88
34
 
89
- /**
90
- * Validate generated flashcard content
91
- */
92
- async validateFlashcard(
93
- front: string,
94
- back: string,
95
- ): Promise<{
96
- accuracy: number;
97
- relevance: number;
98
- clarity: number;
99
- completeness: number;
100
- overall: number;
101
- }> {
102
- // Simple validation heuristic
103
- const accuracy = this.calculateAccuracy(front, back);
104
- const relevance = this.calculateRelevance(front, back);
105
- const clarity = this.calculateClarity(front, back);
106
- const completeness = this.calculateCompleteness(front, back);
107
- const overall = (accuracy + relevance + clarity + completeness) / 4;
35
+ try {
36
+ const prompt = buildFlashcardPrompt(request);
37
+ const response = await executeGeneration(prompt, request.count);
38
+ const flashcards = parseFlashcardsFromResponse(response, request);
108
39
 
109
40
  return {
110
- accuracy,
111
- relevance,
112
- clarity,
113
- completeness,
114
- overall,
115
- };
116
- }
117
-
118
- private buildFlashcardPrompt(request: FlashcardGenerationRequest): string {
119
- const qualityMap = {
120
- beginner:
121
- "simple, clear language appropriate for learners just starting out",
122
- intermediate:
123
- "moderate complexity with some technical terms expected to be known",
124
- advanced:
125
- "complex content with specialized terminology and nuanced concepts",
126
- };
127
-
128
- const formatInstructions = {
129
- qa: "Format as Question-Answer pairs",
130
- definition: "Format as Term-Definition pairs",
131
- fill_blank: "Format as Fill-in-the-blank exercises",
132
- multiple_choice:
133
- "Format as Multiple Choice questions with one correct answer",
41
+ success: true,
42
+ flashcards,
43
+ creditsUsed: request.count * CREDITS_PER_FLASHCARD,
44
+ tokensUsed: response.metadata?.tokensUsed ?? 0,
45
+ processingTime: Date.now() - startTime,
46
+ requestId: response.jobId ?? `req_${Date.now()}`,
134
47
  };
135
-
136
- return `Generate ${request.count} educational flashcards about "${request.topic}".
137
-
138
- Topic Context: ${request.context || "General learning"}
139
- Difficulty Level: ${request.difficulty} - ${qualityMap[request.difficulty]}
140
- Format: ${request.format || "qa"} - ${formatInstructions[request.format || "qa"]}
141
- Language: ${request.language || "English"}
142
- Tags to include: ${request.tags?.join(", ") || "auto-generated"}
143
-
144
- Requirements:
145
- - Questions should be clear and concise
146
- - Answers should be accurate and comprehensive
147
- - Content should be age and difficulty appropriate
148
- - Include relevant educational context
149
- - Make it engaging and memorable
150
-
151
- Output format: JSON array with structure:
152
- [
153
- {
154
- "front": "Question text here",
155
- "back": "Answer text here",
156
- "difficulty": "easy|medium|hard",
157
- "tags": ["tag1", "tag2"]
158
- }
159
- ]`;
160
- }
161
-
162
- private calculateMaxTokens(count: number): number {
163
- // Estimate ~50 tokens per flashcard + overhead
164
- return Math.max(count * 50, 200);
165
- }
166
-
167
- private async executeGeneration(
168
- prompt: string,
169
- count: number,
170
- ): Promise<{
171
- success: boolean;
172
- result: string;
173
- metadata: { tokensUsed: number; processingTime: number };
174
- jobId: string;
175
- }> {
176
- // This would integrate with the actual AI generation orchestrator
177
- // For now, return mock result
178
- await new Promise((resolve) => setTimeout(resolve, 2000));
179
-
180
- const maxTokens = this.calculateMaxTokens(count);
48
+ } catch (error) {
181
49
  return {
182
- success: true,
183
- result: this.generateMockContent(maxTokens),
184
- metadata: {
185
- tokensUsed: maxTokens,
186
- processingTime: 2000,
187
- },
188
- jobId: `job_${Date.now()}`,
50
+ success: false,
51
+ flashcards: [],
52
+ creditsUsed: 0,
53
+ tokensUsed: 0,
54
+ processingTime: 0,
55
+ error: error instanceof Error ? error.message : "Unknown error",
56
+ requestId: "",
189
57
  };
190
58
  }
59
+ }
191
60
 
192
- private generateMockContent(maxTokens: number): string {
193
- const mockFlashcards = [
194
- {
195
- front: "What is photosynthesis?",
196
- back: "The process by which plants convert sunlight, water, and carbon dioxide into glucose and oxygen.",
197
- difficulty: "medium",
198
- tags: ["biology", "science", "plants"],
199
- },
200
- {
201
- front: "Define gravity",
202
- back: "A fundamental force that attracts objects with mass toward each other.",
203
- difficulty: "easy",
204
- tags: ["physics", "science", "forces"],
205
- },
206
- {
207
- front: "What is the formula for water?",
208
- back: "H₂O",
209
- difficulty: "easy",
210
- tags: ["chemistry", "science", "molecules"],
211
- },
212
- ];
61
+ /**
62
+ * Validate flashcard content quality
63
+ */
64
+ export function validateFlashcardContent(
65
+ front: string,
66
+ back: string,
67
+ ): FlashcardValidationResult {
68
+ return validateFlashcard(front, back);
69
+ }
213
70
 
214
- return JSON.stringify(mockFlashcards.slice(0, Math.floor(maxTokens / 100)));
215
- }
71
+ async function executeGeneration(
72
+ _prompt: string,
73
+ count: number,
74
+ ): Promise<FlashcardGenerationResponse> {
75
+ // Mock implementation - integrate with actual AI orchestrator
76
+ await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY_MS));
77
+
78
+ const maxTokens = calculateMaxTokens(count);
79
+ return {
80
+ success: true,
81
+ result: generateMockContent(count),
82
+ metadata: { tokensUsed: maxTokens, processingTime: MOCK_DELAY_MS },
83
+ jobId: `job_${Date.now()}`,
84
+ };
85
+ }
216
86
 
217
- private parseFlashcardsFromResult(
218
- result: {
219
- success: boolean;
220
- result: string | unknown[];
221
- metadata: { tokensUsed: number; processingTime: number };
222
- jobId: string;
87
+ function generateMockContent(count: number): string {
88
+ const mockFlashcards = [
89
+ {
90
+ front: "What is photosynthesis?",
91
+ back: "The process by which plants convert sunlight into glucose.",
92
+ difficulty: "medium",
93
+ tags: ["biology", "science"],
223
94
  },
224
- request: FlashcardGenerationRequest,
225
- ): GeneratedFlashcard[] {
226
- try {
227
- let flashcards: unknown[];
228
-
229
- if (typeof result.result === "string") {
230
- flashcards = JSON.parse(result.result);
231
- } else if (Array.isArray(result.result)) {
232
- flashcards = result.result;
233
- } else {
234
- throw new Error("Invalid AI response format");
235
- }
236
-
237
- return flashcards.map((item: unknown, index) => {
238
- const flashcard = item as {
239
- front?: string;
240
- back?: string;
241
- difficulty?: "easy" | "medium" | "hard";
242
- tags?: string | string[];
243
- };
244
- return {
245
- id: `generated_${Date.now()}_${index}`,
246
- front: flashcard.front || "",
247
- back: flashcard.back || "",
248
- difficulty: flashcard.difficulty || "medium",
249
- tags: Array.isArray(flashcard.tags)
250
- ? flashcard.tags
251
- : flashcard.tags
252
- ? [flashcard.tags]
253
- : [],
254
- source: "ai_generated" as const,
255
- generationRequest: request,
256
- confidence: 0.8 + Math.random() * 0.2, // 0.8-1.0
257
- createdAt: new Date().toISOString(),
258
- };
259
- });
260
- } catch (error) {
261
- if (__DEV__) {
262
- // eslint-disable-next-line no-console
263
- console.error("Failed to parse AI response:", error);
264
- }
265
- return [];
266
- }
267
- }
268
-
269
- private calculateAccuracy(front: string, back: string): number {
270
- // Simple heuristics for accuracy assessment
271
- let score = 0.5; // Base score
272
-
273
- // Check for reasonable length
274
- if (front.length >= 5 && front.length <= 200) score += 0.2;
275
- if (back.length >= 10 && back.length <= 500) score += 0.2;
276
-
277
- // Check for balanced content
278
- const frontWords = front.split(/\s+/).length;
279
- const backWords = back.split(/\s+/).length;
280
- if (backWords >= frontWords * 0.5 && backWords <= frontWords * 3)
281
- score += 0.1;
282
-
283
- return Math.min(score, 1.0);
284
- }
285
-
286
- private calculateRelevance(front: string, back: string): number {
287
- // Simple relevance check
288
- let score = 0.6; // Base score
289
-
290
- // Check for educational content indicators
291
- const educationalTerms = [
292
- "define",
293
- "explain",
294
- "describe",
295
- "what is",
296
- "how does",
297
- "formula",
298
- "process",
299
- "function",
300
- ];
301
- const hasEducationalTerms = educationalTerms.some(
302
- (term) =>
303
- front.toLowerCase().includes(term) || back.toLowerCase().includes(term),
304
- );
305
-
306
- if (hasEducationalTerms) score += 0.3;
307
- if (front.includes("?") || front.toLowerCase().includes("what is"))
308
- score += 0.1;
309
-
310
- return Math.min(score, 1.0);
311
- }
312
-
313
- private calculateClarity(front: string, back: string): number {
314
- let score = 0.5; // Base score
315
-
316
- // Check for clear structure
317
- if (front.trim().endsWith("?")) score += 0.2;
318
- if (!front.includes("...") && !back.includes("...")) score += 0.2;
319
- if (!/[A-Z]{2,}/.test(front)) score += 0.1; // Not too many caps
320
-
321
- return Math.min(score, 1.0);
322
- }
323
-
324
- private calculateCompleteness(front: string, back: string): number {
325
- const frontScore = Math.min(front.length / 20, 1.0); // Ideal 20 chars
326
- const backScore = Math.min(back.length / 50, 1.0); // Ideal 50 chars
95
+ {
96
+ front: "Define gravity",
97
+ back: "A force that attracts objects with mass toward each other.",
98
+ difficulty: "easy",
99
+ tags: ["physics", "science"],
100
+ },
101
+ ];
327
102
 
328
- return (frontScore + backScore) / 2;
329
- }
103
+ return JSON.stringify(mockFlashcards.slice(0, count));
330
104
  }