@umituz/react-native-ai-generation-content 1.61.26 → 1.61.28
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/core/index.ts +14 -12
- package/src/domain/entities/generation.types.ts +1 -0
- package/src/domains/creations/presentation/hooks/useProcessingJobsPoller.ts +16 -4
- package/src/domains/generation/domain/generation.types.ts +1 -2
- package/src/domains/generation/index.ts +2 -1
- package/src/domains/generation/infrastructure/executors/image-executor.ts +1 -1
- package/src/domains/generation/infrastructure/executors/text-to-image-executor.ts +1 -1
- package/src/domains/generation/infrastructure/executors/video-executor.ts +1 -1
- package/src/domains/generation/infrastructure/flow/useFlowStore.ts +2 -1
- package/src/domains/generation/wizard/presentation/hooks/useGenerationPhase.ts +2 -3
- package/src/domains/generation/wizard/presentation/hooks/useVideoQueueGeneration.ts +4 -0
- package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +4 -2
- package/src/exports/infrastructure.ts +1 -1
- package/src/features/image-to-video/presentation/components/DurationSelector.tsx +1 -1
- package/src/infrastructure/orchestration/GenerationOrchestrator.ts +1 -1
- package/src/infrastructure/orchestration/index.ts +2 -1
- package/src/infrastructure/orchestration/orchestrator.types.ts +0 -1
- package/src/infrastructure/providers/generation-config.provider.tsx +4 -4
- package/src/infrastructure/services/generation-orchestrator.service.ts +1 -0
- package/src/infrastructure/services/job-poller.service.ts +2 -4
- package/src/infrastructure/services/multi-image-generation.executor.ts +6 -11
- package/src/infrastructure/utils/polling-interval.util.ts +1 -14
- package/src/presentation/components/AIGenerationForm.tsx +3 -3
- package/src/presentation/components/selectors/AspectRatioSelector.tsx +3 -3
- package/src/presentation/components/selectors/DurationSelector.tsx +3 -3
- package/src/presentation/components/selectors/GridSelector.tsx +5 -5
- package/src/presentation/components/selectors/StyleSelector.tsx +3 -3
- package/src/presentation/hooks/use-background-generation.ts +8 -10
- package/src/presentation/hooks/use-generation.ts +8 -1
- package/src/core/types/error.types.ts +0 -9
- package/src/core/types/index.ts +0 -8
- package/src/core/types/provider.types.ts +0 -39
- package/src/core/types/result.types.ts +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.61.
|
|
3
|
+
"version": "1.61.28",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
package/src/core/index.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Result Pattern
|
|
16
|
-
export type { Result, Success, Failure } from "
|
|
16
|
+
export type { Result, Success, Failure } from "../domain/types/result.types";
|
|
17
17
|
export {
|
|
18
18
|
success,
|
|
19
19
|
failure,
|
|
@@ -23,11 +23,11 @@ export {
|
|
|
23
23
|
andThen,
|
|
24
24
|
unwrap,
|
|
25
25
|
unwrapOr,
|
|
26
|
-
} from "
|
|
26
|
+
} from "../domain/types/result.types";
|
|
27
27
|
|
|
28
28
|
// Error Types
|
|
29
|
-
export { AIErrorType } from "
|
|
30
|
-
export type { AIErrorInfo, AIErrorMessages } from "
|
|
29
|
+
export { AIErrorType } from "../domain/entities/error.types";
|
|
30
|
+
export type { AIErrorInfo, AIErrorMessages } from "../domain/entities/error.types";
|
|
31
31
|
|
|
32
32
|
// Provider Types
|
|
33
33
|
export type {
|
|
@@ -50,12 +50,14 @@ export type {
|
|
|
50
50
|
// Input Data
|
|
51
51
|
ImageFeatureInputData,
|
|
52
52
|
VideoFeatureInputData,
|
|
53
|
-
// Provider
|
|
54
|
-
IAIProviderLifecycle,
|
|
55
|
-
IAIProviderCapabilities,
|
|
56
|
-
IAIProviderJobManager,
|
|
57
|
-
IAIProviderExecutor,
|
|
58
|
-
IAIProviderImageFeatures,
|
|
59
|
-
IAIProviderVideoFeatures,
|
|
53
|
+
// Main Provider Interface
|
|
60
54
|
IAIProvider,
|
|
61
|
-
} from "
|
|
55
|
+
} from "../domain/interfaces/ai-provider.interface";
|
|
56
|
+
|
|
57
|
+
// Segregated provider sub-interfaces
|
|
58
|
+
export type { IAIProviderLifecycle } from "../domain/interfaces/provider-lifecycle.interface";
|
|
59
|
+
export type { IAIProviderCapabilities } from "../domain/interfaces/provider-capabilities.interface";
|
|
60
|
+
export type { IAIProviderJobManager } from "../domain/interfaces/provider-job-manager.interface";
|
|
61
|
+
export type { IAIProviderExecutor } from "../domain/interfaces/provider-executor.interface";
|
|
62
|
+
export type { IAIProviderImageFeatures } from "../domain/interfaces/provider-image-features.interface";
|
|
63
|
+
export type { IAIProviderVideoFeatures } from "../domain/interfaces/provider-video-features.interface";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Uses provider registry internally - no need to pass FAL functions
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { useEffect, useRef, useCallback } from "react";
|
|
8
|
+
import { useEffect, useRef, useCallback, useMemo } from "react";
|
|
9
9
|
import { providerRegistry } from "../../../../infrastructure/services/provider-registry.service";
|
|
10
10
|
import { QUEUE_STATUS, CREATION_STATUS } from "../../../../domain/constants/queue-status.constants";
|
|
11
11
|
import {
|
|
@@ -43,9 +43,21 @@ export function useProcessingJobsPoller(
|
|
|
43
43
|
const pollingRef = useRef<Set<string>>(new Set());
|
|
44
44
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
45
45
|
|
|
46
|
-
// Find creations that need polling
|
|
47
|
-
const
|
|
48
|
-
(
|
|
46
|
+
// Find creations that need polling - stabilize reference with useMemo
|
|
47
|
+
const processingJobIds = useMemo(
|
|
48
|
+
() => creations
|
|
49
|
+
.filter((c) => c.status === CREATION_STATUS.PROCESSING && c.requestId && c.model)
|
|
50
|
+
.map((c) => c.id)
|
|
51
|
+
.join(","),
|
|
52
|
+
[creations],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const processingJobs = useMemo(
|
|
56
|
+
() => creations.filter(
|
|
57
|
+
(c) => c.status === CREATION_STATUS.PROCESSING && c.requestId && c.model,
|
|
58
|
+
),
|
|
59
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
+
[processingJobIds],
|
|
49
61
|
);
|
|
50
62
|
|
|
51
63
|
const pollJob = useCallback(
|
|
@@ -13,11 +13,10 @@ export interface GenerationOptions {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// ============================================================================
|
|
16
|
-
// Generation Result (
|
|
16
|
+
// Generation Result (canonical source: domain/entities/generation.types)
|
|
17
17
|
// ============================================================================
|
|
18
18
|
|
|
19
19
|
import type { GenerationResult as _GenerationResult } from "../../../domain/entities/generation.types";
|
|
20
|
-
export type { GenerationResult } from "../../../domain/entities/generation.types";
|
|
21
20
|
|
|
22
21
|
// ============================================================================
|
|
23
22
|
// Input Types (by generation type)
|
|
@@ -20,7 +20,6 @@ export type {
|
|
|
20
20
|
export type {
|
|
21
21
|
GenerationExecutor,
|
|
22
22
|
GenerationOptions,
|
|
23
|
-
GenerationResult,
|
|
24
23
|
ImageGenerationInput,
|
|
25
24
|
ImageGenerationOutput,
|
|
26
25
|
VideoGenerationInput,
|
|
@@ -31,6 +30,8 @@ export type {
|
|
|
31
30
|
TextToImageOutput,
|
|
32
31
|
} from "./domain/generation.types";
|
|
33
32
|
|
|
33
|
+
export type { GenerationResult } from "../../domain/entities/generation.types";
|
|
34
|
+
|
|
34
35
|
export { ExecutorFactory, type GenerationType as ExecutorGenerationType } from "./infrastructure/executors/executor-factory";
|
|
35
36
|
|
|
36
37
|
// Wizard Domain
|
|
@@ -8,8 +8,8 @@ import type {
|
|
|
8
8
|
ImageGenerationInput,
|
|
9
9
|
ImageGenerationOutput,
|
|
10
10
|
GenerationOptions,
|
|
11
|
-
GenerationResult,
|
|
12
11
|
} from "../../domain/generation.types";
|
|
12
|
+
import type { GenerationResult } from "../../../../domain/entities/generation.types";
|
|
13
13
|
import { providerRegistry } from "../../../../infrastructure/services/provider-registry.service";
|
|
14
14
|
|
|
15
15
|
declare const __DEV__: boolean;
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import type {
|
|
7
7
|
GenerationExecutor,
|
|
8
8
|
GenerationOptions,
|
|
9
|
-
GenerationResult,
|
|
10
9
|
} from "../../domain/generation.types";
|
|
10
|
+
import type { GenerationResult } from "../../../../domain/entities/generation.types";
|
|
11
11
|
import { providerRegistry } from "../../../../infrastructure/services/provider-registry.service";
|
|
12
12
|
|
|
13
13
|
declare const __DEV__: boolean;
|
|
@@ -8,8 +8,8 @@ import type {
|
|
|
8
8
|
VideoGenerationInput,
|
|
9
9
|
VideoGenerationOutput,
|
|
10
10
|
GenerationOptions,
|
|
11
|
-
GenerationResult,
|
|
12
11
|
} from "../../domain/generation.types";
|
|
12
|
+
import type { GenerationResult } from "../../../../domain/entities/generation.types";
|
|
13
13
|
import { providerRegistry } from "../../../../infrastructure/services/provider-registry.service";
|
|
14
14
|
|
|
15
15
|
declare const __DEV__: boolean;
|
|
@@ -133,7 +133,8 @@ export const createFlowStore = (config: FlowStoreConfig) => {
|
|
|
133
133
|
},
|
|
134
134
|
|
|
135
135
|
updateProgress: (progress: number) => {
|
|
136
|
-
|
|
136
|
+
const generationStatus = progress >= 100 ? "completed" : progress > 0 ? "generating" : "preparing";
|
|
137
|
+
set({ generationProgress: progress, generationStatus });
|
|
137
138
|
},
|
|
138
139
|
|
|
139
140
|
setResult: (result: unknown) => {
|
|
@@ -33,10 +33,9 @@ export function useGenerationPhase(options?: UseGenerationPhaseOptions): Generat
|
|
|
33
33
|
const interval = setInterval(() => {
|
|
34
34
|
const elapsed = Date.now() - startTimeRef.current;
|
|
35
35
|
|
|
36
|
-
if (elapsed
|
|
37
|
-
setPhase("queued");
|
|
38
|
-
} else {
|
|
36
|
+
if (elapsed >= queuedDuration) {
|
|
39
37
|
setPhase("processing");
|
|
38
|
+
clearInterval(interval);
|
|
40
39
|
}
|
|
41
40
|
}, 1000);
|
|
42
41
|
|
|
@@ -41,6 +41,7 @@ export function useVideoQueueGeneration(
|
|
|
41
41
|
const requestIdRef = useRef<string | null>(null);
|
|
42
42
|
const modelRef = useRef<string | null>(null);
|
|
43
43
|
const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
44
|
+
const isGeneratingRef = useRef(false);
|
|
44
45
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
45
46
|
|
|
46
47
|
// Cleanup polling on unmount
|
|
@@ -57,6 +58,7 @@ export function useVideoQueueGeneration(
|
|
|
57
58
|
creationIdRef.current = null;
|
|
58
59
|
requestIdRef.current = null;
|
|
59
60
|
modelRef.current = null;
|
|
61
|
+
isGeneratingRef.current = false;
|
|
60
62
|
setIsGenerating(false);
|
|
61
63
|
}, []);
|
|
62
64
|
|
|
@@ -138,6 +140,8 @@ export function useVideoQueueGeneration(
|
|
|
138
140
|
const startGeneration = useCallback(
|
|
139
141
|
async (input: unknown, prompt: string) => {
|
|
140
142
|
if (!strategy.submitToQueue) { onError?.("Queue submission not available"); return; }
|
|
143
|
+
if (isGeneratingRef.current) return;
|
|
144
|
+
isGeneratingRef.current = true;
|
|
141
145
|
setIsGenerating(true);
|
|
142
146
|
|
|
143
147
|
// Save to Firestore FIRST (enables background visibility)
|
|
@@ -186,8 +186,10 @@ export const useWizardGeneration = (
|
|
|
186
186
|
scenario,
|
|
187
187
|
wizardData,
|
|
188
188
|
isVideoMode,
|
|
189
|
-
videoGeneration,
|
|
190
|
-
|
|
189
|
+
videoGeneration.isGenerating,
|
|
190
|
+
videoGeneration.startGeneration,
|
|
191
|
+
photoGeneration.isGenerating,
|
|
192
|
+
photoGeneration.startGeneration,
|
|
191
193
|
onError,
|
|
192
194
|
]);
|
|
193
195
|
|
|
@@ -29,7 +29,7 @@ export type {
|
|
|
29
29
|
// Utils
|
|
30
30
|
export {
|
|
31
31
|
classifyError, isTransientError, isPermanentError, isResultNotReady, calculatePollingInterval,
|
|
32
|
-
|
|
32
|
+
checkStatusForErrors, isJobComplete, isJobProcessing, isJobFailed,
|
|
33
33
|
validateResult, extractOutputUrl, extractOutputUrls, extractVideoUrl, extractThumbnailUrl,
|
|
34
34
|
extractAudioUrl, extractImageUrls, cleanBase64, addBase64Prefix, preparePhoto, preparePhotos,
|
|
35
35
|
isValidBase64, getBase64Size, getBase64SizeMB, prepareImage, createDevCallbacks, createFeatureUtils,
|
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
} from "../../domains/content-moderation";
|
|
13
13
|
import type {
|
|
14
14
|
OrchestratorConfig,
|
|
15
|
-
GenerationCapability,
|
|
16
15
|
GenerationMetadata,
|
|
17
16
|
} from "./orchestrator.types";
|
|
17
|
+
import type { GenerationCapability } from "../../domain/entities/generation.types";
|
|
18
18
|
import {
|
|
19
19
|
NetworkUnavailableError,
|
|
20
20
|
InsufficientCreditsError,
|
|
@@ -13,10 +13,11 @@ export type {
|
|
|
13
13
|
NetworkService,
|
|
14
14
|
AuthService,
|
|
15
15
|
GenerationMetadata,
|
|
16
|
-
GenerationCapability,
|
|
17
16
|
OrchestratorConfig,
|
|
18
17
|
} from "./orchestrator.types";
|
|
19
18
|
|
|
19
|
+
export type { GenerationCapability } from "../../domain/entities/generation.types";
|
|
20
|
+
|
|
20
21
|
// Errors
|
|
21
22
|
export {
|
|
22
23
|
NetworkUnavailableError,
|
|
@@ -31,7 +31,6 @@ export interface GenerationMetadata {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
import type { GenerationCapability } from "../../domain/entities/generation.types";
|
|
34
|
-
export type { GenerationCapability } from "../../domain/entities/generation.types";
|
|
35
34
|
|
|
36
35
|
export interface OrchestratorConfig {
|
|
37
36
|
creditService: CreditService;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* For scenarios, use configureScenarios() from scenario-registry
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { createContext, useContext, type ReactNode } from "react";
|
|
7
|
+
import React, { createContext, useContext, useCallback, useMemo, type ReactNode } from "react";
|
|
8
8
|
|
|
9
9
|
declare const __DEV__: boolean;
|
|
10
10
|
|
|
@@ -43,7 +43,7 @@ export const GenerationConfigProvider: React.FC<
|
|
|
43
43
|
console.log("[GenerationConfigProvider] Models:", configuredModels);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const getModel = (featureType: keyof GenerationModels): string => {
|
|
46
|
+
const getModel = useCallback((featureType: keyof GenerationModels): string => {
|
|
47
47
|
const model = models[featureType];
|
|
48
48
|
if (!model) {
|
|
49
49
|
const available = Object.keys(models).filter(
|
|
@@ -54,9 +54,9 @@ export const GenerationConfigProvider: React.FC<
|
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
return model;
|
|
57
|
-
};
|
|
57
|
+
}, [models]);
|
|
58
58
|
|
|
59
|
-
const value
|
|
59
|
+
const value = useMemo<GenerationConfigValue>(() => ({ models, getModel }), [models, getModel]);
|
|
60
60
|
|
|
61
61
|
return (
|
|
62
62
|
<GenerationConfigContext.Provider value={value}>
|
|
@@ -66,6 +66,7 @@ class GenerationOrchestratorService {
|
|
|
66
66
|
model: request.model,
|
|
67
67
|
requestId: submission.requestId,
|
|
68
68
|
config: this.pollingConfig,
|
|
69
|
+
signal: request.signal,
|
|
69
70
|
onStatusChange: async (status) => {
|
|
70
71
|
if (this.onStatusUpdateCallback) {
|
|
71
72
|
await this.onStatusUpdateCallback(submission.requestId, status.status);
|
|
@@ -13,8 +13,6 @@ import type { PollJobOptions, PollJobResult } from "./job-poller.types";
|
|
|
13
13
|
|
|
14
14
|
declare const __DEV__: boolean;
|
|
15
15
|
|
|
16
|
-
const MAX_CONSECUTIVE_TRANSIENT_ERRORS = 5;
|
|
17
|
-
|
|
18
16
|
/**
|
|
19
17
|
* Poll job until completion with exponential backoff
|
|
20
18
|
* Only reports 100% on actual completion
|
|
@@ -33,7 +31,7 @@ export async function pollJob<T = unknown>(
|
|
|
33
31
|
} = options;
|
|
34
32
|
|
|
35
33
|
const pollingConfig = { ...DEFAULT_POLLING_CONFIG, ...config };
|
|
36
|
-
const { maxAttempts } = pollingConfig;
|
|
34
|
+
const { maxAttempts, maxConsecutiveErrors } = pollingConfig;
|
|
37
35
|
|
|
38
36
|
const startTime = Date.now();
|
|
39
37
|
let consecutiveTransientErrors = 0;
|
|
@@ -96,7 +94,7 @@ export async function pollJob<T = unknown>(
|
|
|
96
94
|
if (isTransientError(error)) {
|
|
97
95
|
consecutiveTransientErrors++;
|
|
98
96
|
|
|
99
|
-
if (consecutiveTransientErrors >=
|
|
97
|
+
if (consecutiveTransientErrors >= maxConsecutiveErrors) {
|
|
100
98
|
return {
|
|
101
99
|
success: false,
|
|
102
100
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
@@ -4,16 +4,14 @@
|
|
|
4
4
|
* Sends image_urls array as required by FAL AI nano-banana/edit model
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { validateProvider } from "../utils/provider-validator.util";
|
|
8
|
+
import { formatBase64 } from "../utils/base64.util";
|
|
8
9
|
|
|
9
10
|
declare const __DEV__: boolean;
|
|
10
11
|
|
|
11
12
|
/** Generation timeout in milliseconds (2 minutes) */
|
|
12
13
|
const GENERATION_TIMEOUT_MS = 120000;
|
|
13
14
|
|
|
14
|
-
/** Base64 image format prefix */
|
|
15
|
-
const BASE64_IMAGE_PREFIX = "data:image/jpeg;base64,";
|
|
16
|
-
|
|
17
15
|
/** Default model input values */
|
|
18
16
|
const MODEL_INPUT_DEFAULTS = {
|
|
19
17
|
aspectRatio: "1:1",
|
|
@@ -41,10 +39,6 @@ export interface MultiImageGenerationResult {
|
|
|
41
39
|
readonly error?: string;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
function formatBase64(base64: string): string {
|
|
45
|
-
return base64.startsWith("data:") ? base64 : `${BASE64_IMAGE_PREFIX}${base64}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
42
|
/**
|
|
49
43
|
* Execute image generation with multiple input images
|
|
50
44
|
* Sends image_urls array as required by FAL AI API
|
|
@@ -52,10 +46,11 @@ function formatBase64(base64: string): string {
|
|
|
52
46
|
export async function executeMultiImageGeneration(
|
|
53
47
|
input: MultiImageGenerationInput,
|
|
54
48
|
): Promise<MultiImageGenerationResult> {
|
|
55
|
-
const
|
|
56
|
-
if (!
|
|
57
|
-
return { success: false, error:
|
|
49
|
+
const validation = validateProvider("MultiImageExecutor");
|
|
50
|
+
if (!validation.success) {
|
|
51
|
+
return { success: false, error: validation.error };
|
|
58
52
|
}
|
|
53
|
+
const provider = validation.provider;
|
|
59
54
|
|
|
60
55
|
try {
|
|
61
56
|
const imageUrls = input.photos.map(formatBase64);
|
|
@@ -27,17 +27,4 @@ export function calculatePollingInterval(options: IntervalOptions): number {
|
|
|
27
27
|
|
|
28
28
|
const interval = initialIntervalMs * Math.pow(backoffMultiplier, attempt - 1);
|
|
29
29
|
return Math.min(interval, maxIntervalMs);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function createPollingDelay(
|
|
33
|
-
attempt: number,
|
|
34
|
-
config?: Partial<PollingConfig>,
|
|
35
|
-
): Promise<void> {
|
|
36
|
-
const interval = calculatePollingInterval({ attempt, config });
|
|
37
|
-
|
|
38
|
-
if (interval === 0) {
|
|
39
|
-
return Promise.resolve();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return new Promise((resolve) => setTimeout(resolve, interval));
|
|
43
|
-
}
|
|
30
|
+
}
|
|
@@ -113,7 +113,7 @@ export const AIGenerationForm: React.FC<AIGenerationFormProps> = ({
|
|
|
113
113
|
styles={styleOptions}
|
|
114
114
|
selectedStyle={selectedStyle}
|
|
115
115
|
onStyleSelect={onStyleSelect}
|
|
116
|
-
|
|
116
|
+
label={translations.styleTitle}
|
|
117
117
|
/>
|
|
118
118
|
)}
|
|
119
119
|
|
|
@@ -122,7 +122,7 @@ export const AIGenerationForm: React.FC<AIGenerationFormProps> = ({
|
|
|
122
122
|
ratios={aspectRatios}
|
|
123
123
|
selectedRatio={selectedAspectRatio}
|
|
124
124
|
onRatioSelect={onAspectRatioSelect}
|
|
125
|
-
|
|
125
|
+
label={translations.aspectRatioTitle}
|
|
126
126
|
/>
|
|
127
127
|
)}
|
|
128
128
|
|
|
@@ -131,7 +131,7 @@ export const AIGenerationForm: React.FC<AIGenerationFormProps> = ({
|
|
|
131
131
|
duration={duration}
|
|
132
132
|
durationOptions={durationOptions}
|
|
133
133
|
onDurationSelect={onDurationSelect}
|
|
134
|
-
|
|
134
|
+
label={translations.durationTitle}
|
|
135
135
|
formatLabel={formatDurationLabel}
|
|
136
136
|
/>
|
|
137
137
|
)}
|
|
@@ -16,14 +16,14 @@ export interface AspectRatioSelectorProps {
|
|
|
16
16
|
ratios: AspectRatioOption[];
|
|
17
17
|
selectedRatio: string;
|
|
18
18
|
onRatioSelect: (ratio: string) => void;
|
|
19
|
-
|
|
19
|
+
label: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const AspectRatioSelector: React.FC<AspectRatioSelectorProps> = ({
|
|
23
23
|
ratios,
|
|
24
24
|
selectedRatio,
|
|
25
25
|
onRatioSelect,
|
|
26
|
-
|
|
26
|
+
label,
|
|
27
27
|
}) => {
|
|
28
28
|
const tokens = useAppDesignTokens();
|
|
29
29
|
|
|
@@ -37,7 +37,7 @@ export const AspectRatioSelector: React.FC<AspectRatioSelectorProps> = ({
|
|
|
37
37
|
marginBottom: 12,
|
|
38
38
|
}}
|
|
39
39
|
>
|
|
40
|
-
{
|
|
40
|
+
{label}
|
|
41
41
|
</AtomicText>
|
|
42
42
|
<View style={componentStyles.aspectRatioGrid}>
|
|
43
43
|
{ratios.map((ratio) => (
|
|
@@ -15,7 +15,7 @@ export interface DurationSelectorProps<T extends DurationValue> {
|
|
|
15
15
|
duration: T;
|
|
16
16
|
durationOptions: readonly T[];
|
|
17
17
|
onDurationSelect: (duration: T) => void;
|
|
18
|
-
|
|
18
|
+
label: string;
|
|
19
19
|
formatLabel?: (duration: T) => string;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ export function DurationSelector<T extends DurationValue>({
|
|
|
23
23
|
duration,
|
|
24
24
|
durationOptions,
|
|
25
25
|
onDurationSelect,
|
|
26
|
-
|
|
26
|
+
label,
|
|
27
27
|
formatLabel = (d) => `${d}s`,
|
|
28
28
|
}: DurationSelectorProps<T>): React.ReactElement {
|
|
29
29
|
const tokens = useAppDesignTokens();
|
|
@@ -38,7 +38,7 @@ export function DurationSelector<T extends DurationValue>({
|
|
|
38
38
|
marginBottom: 12,
|
|
39
39
|
}}
|
|
40
40
|
>
|
|
41
|
-
{
|
|
41
|
+
{label}
|
|
42
42
|
</AtomicText>
|
|
43
43
|
<View style={componentStyles.durationGrid}>
|
|
44
44
|
{durationOptions.map((sec) => (
|
|
@@ -16,7 +16,7 @@ export interface GridSelectorProps<T> {
|
|
|
16
16
|
readonly options: readonly GridSelectorOption<T>[];
|
|
17
17
|
readonly selectedValue: T;
|
|
18
18
|
readonly onSelect: (value: T) => void;
|
|
19
|
-
readonly
|
|
19
|
+
readonly label?: string;
|
|
20
20
|
readonly columns?: number;
|
|
21
21
|
readonly disabled?: boolean;
|
|
22
22
|
readonly style?: ViewStyle;
|
|
@@ -26,7 +26,7 @@ export function GridSelector<T>({
|
|
|
26
26
|
options,
|
|
27
27
|
selectedValue,
|
|
28
28
|
onSelect,
|
|
29
|
-
|
|
29
|
+
label,
|
|
30
30
|
columns = 2,
|
|
31
31
|
disabled = false,
|
|
32
32
|
style,
|
|
@@ -35,7 +35,7 @@ export function GridSelector<T>({
|
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
37
|
<View style={[styles.section, style]}>
|
|
38
|
-
{
|
|
38
|
+
{label && (
|
|
39
39
|
<AtomicText
|
|
40
40
|
type="bodyMedium"
|
|
41
41
|
style={{
|
|
@@ -44,7 +44,7 @@ export function GridSelector<T>({
|
|
|
44
44
|
marginBottom: 12,
|
|
45
45
|
}}
|
|
46
46
|
>
|
|
47
|
-
{
|
|
47
|
+
{label}
|
|
48
48
|
</AtomicText>
|
|
49
49
|
)}
|
|
50
50
|
<View style={styles.grid}>
|
|
@@ -58,7 +58,7 @@ export function GridSelector<T>({
|
|
|
58
58
|
{
|
|
59
59
|
width: `${100 / columns - 4}%`,
|
|
60
60
|
backgroundColor: isSelected
|
|
61
|
-
? tokens.colors.primary + "
|
|
61
|
+
? tokens.colors.primary + "20"
|
|
62
62
|
: tokens.colors.surface,
|
|
63
63
|
borderColor: isSelected
|
|
64
64
|
? tokens.colors.primary
|
|
@@ -16,14 +16,14 @@ export interface StyleSelectorProps {
|
|
|
16
16
|
styles: StyleOption[];
|
|
17
17
|
selectedStyle: string;
|
|
18
18
|
onStyleSelect: (styleId: string) => void;
|
|
19
|
-
|
|
19
|
+
label: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const StyleSelector: React.FC<StyleSelectorProps> = ({
|
|
23
23
|
styles,
|
|
24
24
|
selectedStyle,
|
|
25
25
|
onStyleSelect,
|
|
26
|
-
|
|
26
|
+
label,
|
|
27
27
|
}) => {
|
|
28
28
|
const tokens = useAppDesignTokens();
|
|
29
29
|
|
|
@@ -37,7 +37,7 @@ export const StyleSelector: React.FC<StyleSelectorProps> = ({
|
|
|
37
37
|
marginBottom: 12,
|
|
38
38
|
}}
|
|
39
39
|
>
|
|
40
|
-
{
|
|
40
|
+
{label}
|
|
41
41
|
</AtomicText>
|
|
42
42
|
<ScrollView
|
|
43
43
|
horizontal
|
|
@@ -62,17 +62,17 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
|
|
|
62
62
|
queryKey: config.queryKey,
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
+
const { executor, onProgress, onJobComplete, onJobError, onAllComplete } = options;
|
|
66
|
+
|
|
65
67
|
const executeDirectly = useCallback(
|
|
66
68
|
async (input: TInput): Promise<DirectExecutionResult<TResult>> => {
|
|
67
|
-
const { executor } = options;
|
|
68
|
-
|
|
69
69
|
setIsProcessing(true);
|
|
70
70
|
setProgress(0);
|
|
71
71
|
|
|
72
72
|
try {
|
|
73
73
|
const result = await executor.execute(input, (p) => {
|
|
74
74
|
setProgress(p);
|
|
75
|
-
|
|
75
|
+
onProgress?.(p);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
setProgress(100);
|
|
@@ -84,13 +84,11 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
|
|
|
84
84
|
setIsProcessing(false);
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
|
-
[
|
|
87
|
+
[executor, onProgress],
|
|
88
88
|
);
|
|
89
89
|
|
|
90
90
|
const executeJob = useCallback(
|
|
91
91
|
async (jobId: string, input: TInput) => {
|
|
92
|
-
const { executor } = options;
|
|
93
|
-
|
|
94
92
|
try {
|
|
95
93
|
updateJob({
|
|
96
94
|
id: jobId,
|
|
@@ -114,7 +112,7 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
|
|
|
114
112
|
const completedJob = getJob(jobId);
|
|
115
113
|
if (completedJob) {
|
|
116
114
|
await executor.onComplete?.(completedJob);
|
|
117
|
-
|
|
115
|
+
onJobComplete?.(completedJob);
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
removeJob(jobId);
|
|
@@ -132,16 +130,16 @@ export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
|
|
|
132
130
|
failedJob,
|
|
133
131
|
error instanceof Error ? error : new Error(errorMsg),
|
|
134
132
|
);
|
|
135
|
-
|
|
133
|
+
onJobError?.(failedJob);
|
|
136
134
|
}
|
|
137
135
|
} finally {
|
|
138
136
|
activeJobsRef.current.delete(jobId);
|
|
139
137
|
if (activeJobsRef.current.size === 0) {
|
|
140
|
-
|
|
138
|
+
onAllComplete?.();
|
|
141
139
|
}
|
|
142
140
|
}
|
|
143
141
|
},
|
|
144
|
-
[
|
|
142
|
+
[executor, onJobComplete, onJobError, onAllComplete, updateJob, removeJob, getJob],
|
|
145
143
|
);
|
|
146
144
|
|
|
147
145
|
const startJob = useCallback(
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* React hook for AI generation with progress tracking
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { useState, useCallback, useRef } from "react";
|
|
6
|
+
import { useState, useCallback, useRef, useEffect } from "react";
|
|
7
7
|
import type {
|
|
8
8
|
GenerationRequest,
|
|
9
9
|
GenerationResult,
|
|
@@ -39,6 +39,13 @@ export function useGeneration<T = unknown>(
|
|
|
39
39
|
|
|
40
40
|
const abortRef = useRef(false);
|
|
41
41
|
|
|
42
|
+
// Abort on unmount to prevent state updates after unmount
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
return () => {
|
|
45
|
+
abortRef.current = true;
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
42
49
|
const handleProgress = useCallback(
|
|
43
50
|
(prog: GenerationProgress) => {
|
|
44
51
|
if (abortRef.current) return;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Generation Error Types
|
|
3
|
-
* Re-exports from canonical domain source
|
|
4
|
-
*
|
|
5
|
-
* @module @umituz/react-native-ai-generation-content/core
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { AIErrorType } from "../../domain/entities/error.types";
|
|
9
|
-
export type { AIErrorInfo, AIErrorMessages } from "../../domain/entities/error.types";
|
package/src/core/types/index.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Provider Types - Core interfaces for AI generation providers
|
|
3
|
-
* Re-exports from canonical domain sources
|
|
4
|
-
*
|
|
5
|
-
* @module @umituz/react-native-ai-generation-content/core
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Main provider types from ai-provider.interface
|
|
9
|
-
export type {
|
|
10
|
-
// Feature Types
|
|
11
|
-
ImageFeatureType,
|
|
12
|
-
VideoFeatureType,
|
|
13
|
-
// Config
|
|
14
|
-
AIProviderConfig,
|
|
15
|
-
// Status
|
|
16
|
-
AIJobStatusType,
|
|
17
|
-
AILogEntry,
|
|
18
|
-
JobSubmission,
|
|
19
|
-
JobStatus,
|
|
20
|
-
// Progress
|
|
21
|
-
ProviderProgressInfo,
|
|
22
|
-
SubscribeOptions,
|
|
23
|
-
RunOptions,
|
|
24
|
-
// Capabilities
|
|
25
|
-
ProviderCapabilities,
|
|
26
|
-
// Input Data
|
|
27
|
-
ImageFeatureInputData,
|
|
28
|
-
VideoFeatureInputData,
|
|
29
|
-
// Main Provider Interface
|
|
30
|
-
IAIProvider,
|
|
31
|
-
} from "../../domain/interfaces/ai-provider.interface";
|
|
32
|
-
|
|
33
|
-
// Segregated provider sub-interfaces
|
|
34
|
-
export type { IAIProviderLifecycle } from "../../domain/interfaces/provider-lifecycle.interface";
|
|
35
|
-
export type { IAIProviderCapabilities } from "../../domain/interfaces/provider-capabilities.interface";
|
|
36
|
-
export type { IAIProviderJobManager } from "../../domain/interfaces/provider-job-manager.interface";
|
|
37
|
-
export type { IAIProviderExecutor } from "../../domain/interfaces/provider-executor.interface";
|
|
38
|
-
export type { IAIProviderImageFeatures } from "../../domain/interfaces/provider-image-features.interface";
|
|
39
|
-
export type { IAIProviderVideoFeatures } from "../../domain/interfaces/provider-video-features.interface";
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Result Type Pattern for Functional Error Handling
|
|
3
|
-
* Re-exports from canonical domain source
|
|
4
|
-
*
|
|
5
|
-
* @module @umituz/react-native-ai-generation-content/core
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type { Result, Success, Failure } from "../../domain/types/result.types";
|
|
9
|
-
export {
|
|
10
|
-
success,
|
|
11
|
-
failure,
|
|
12
|
-
isSuccess,
|
|
13
|
-
isFailure,
|
|
14
|
-
mapResult,
|
|
15
|
-
andThen,
|
|
16
|
-
unwrap,
|
|
17
|
-
unwrapOr,
|
|
18
|
-
} from "../../domain/types/result.types";
|