@umituz/react-native-ai-generation-content 1.35.4 → 1.35.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.35.4",
3
+ "version": "1.35.6",
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",
@@ -154,6 +154,7 @@ export {
154
154
  // Gallery Components
155
155
  export { CreationsHomeCard } from "./presentation/components/CreationsHomeCard";
156
156
  export { EmptyState } from "./presentation/components/EmptyState";
157
+ export { PendingJobsSection, type PendingJobsSectionProps } from "./presentation/components/PendingJobsSection";
157
158
 
158
159
  // Utilities
159
160
  export {
@@ -0,0 +1,78 @@
1
+ /**
2
+ * PendingJobsSection Component
3
+ * Displays pending/processing AI generation jobs in CreationsGallery
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+ import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
9
+ import type { BackgroundJob } from "../../../../domain/entities/job.types";
10
+ import { PendingJobCard } from "../../../../presentation/components/PendingJobCard";
11
+
12
+ export interface PendingJobsSectionProps {
13
+ readonly jobs: BackgroundJob[];
14
+ readonly onCancel?: (id: string) => void;
15
+ readonly onRetry?: (id: string) => void;
16
+ readonly title?: string;
17
+ readonly statusLabels?: {
18
+ readonly queued?: string;
19
+ readonly processing?: string;
20
+ readonly uploading?: string;
21
+ readonly completed?: string;
22
+ readonly failed?: string;
23
+ };
24
+ readonly getTypeLabel?: (type: string) => string;
25
+ }
26
+
27
+ export function PendingJobsSection({
28
+ jobs,
29
+ onCancel,
30
+ onRetry,
31
+ title,
32
+ statusLabels,
33
+ getTypeLabel,
34
+ }: PendingJobsSectionProps): React.ReactElement | null {
35
+ const tokens = useAppDesignTokens();
36
+
37
+ // Only show processing/queued jobs
38
+ const activeJobs = jobs.filter(
39
+ (job) => job.status === "processing" || job.status === "queued" || job.status === "failed",
40
+ );
41
+
42
+ if (activeJobs.length === 0) {
43
+ return null;
44
+ }
45
+
46
+ const styles = StyleSheet.create({
47
+ container: {
48
+ marginBottom: 16,
49
+ },
50
+ title: {
51
+ fontSize: 14,
52
+ fontWeight: "600",
53
+ color: tokens.colors.textSecondary,
54
+ marginBottom: 12,
55
+ },
56
+ jobsContainer: {
57
+ gap: 12,
58
+ },
59
+ });
60
+
61
+ return (
62
+ <View style={styles.container}>
63
+ {title && <AtomicText style={styles.title}>{title}</AtomicText>}
64
+ <View style={styles.jobsContainer}>
65
+ {activeJobs.map((job) => (
66
+ <PendingJobCard
67
+ key={job.id}
68
+ job={job}
69
+ onCancel={onCancel}
70
+ onRetry={onRetry}
71
+ typeLabel={getTypeLabel ? getTypeLabel(job.type) : job.type}
72
+ statusLabels={statusLabels}
73
+ />
74
+ ))}
75
+ </View>
76
+ </View>
77
+ );
78
+ }
@@ -32,3 +32,4 @@ export { GalleryEmptyStates } from "./GalleryEmptyStates";
32
32
  export { CreationsHomeCard } from "./CreationsHomeCard";
33
33
  export { CreationRating } from "./CreationRating";
34
34
  export { CreationsGrid } from "./CreationsGrid";
35
+ export { PendingJobsSection, type PendingJobsSectionProps } from "./PendingJobsSection";
@@ -13,10 +13,11 @@ import {
13
13
  import { useCreations } from "../hooks/useCreations";
14
14
  import { useDeleteCreation } from "../hooks/useDeleteCreation";
15
15
  import { useGalleryFilters } from "../hooks/useGalleryFilters";
16
- import { GalleryHeader, CreationCard, GalleryEmptyStates } from "../components";
16
+ import { GalleryHeader, CreationCard, GalleryEmptyStates, PendingJobsSection } from "../components";
17
17
  import { ResultPreviewScreen } from "../../../result-preview/presentation/components/ResultPreviewScreen";
18
18
  import { StarRatingPicker } from "../../../result-preview/presentation/components/StarRatingPicker";
19
19
  import { useResultActions } from "../../../result-preview/presentation/hooks/useResultActions";
20
+ import { usePendingJobs } from "../../../../presentation/hooks/use-pending-jobs";
20
21
  import { MEDIA_FILTER_OPTIONS, STATUS_FILTER_OPTIONS } from "../../domain/types/creation-filter";
21
22
  import { getPreviewUrl } from "../../domain/utils";
22
23
  import type { Creation } from "../../domain/entities/Creation";
@@ -32,6 +33,10 @@ interface CreationsGalleryScreenProps {
32
33
  readonly onEmptyAction?: () => void;
33
34
  readonly emptyActionLabel?: string;
34
35
  readonly showFilter?: boolean;
36
+ /** Show pending generation jobs at the top */
37
+ readonly showPendingJobs?: boolean;
38
+ /** Title for the pending jobs section */
39
+ readonly pendingJobsTitle?: string;
35
40
  }
36
41
 
37
42
  export function CreationsGalleryScreen({
@@ -43,6 +48,8 @@ export function CreationsGalleryScreen({
43
48
  onEmptyAction,
44
49
  emptyActionLabel,
45
50
  showFilter = config.showFilter ?? true,
51
+ showPendingJobs = true,
52
+ pendingJobsTitle,
46
53
  }: CreationsGalleryScreenProps) {
47
54
  const tokens = useAppDesignTokens();
48
55
  const { share } = useSharing();
@@ -53,6 +60,9 @@ export function CreationsGalleryScreen({
53
60
 
54
61
  const { data: creations, isLoading, refetch } = useCreations({ userId, repository });
55
62
 
63
+ // Background jobs for pending generations
64
+ const { jobs: pendingJobs, removeJob } = usePendingJobs();
65
+
56
66
  // Auto-select creation when initialCreationId is provided
57
67
  useEffect(() => {
58
68
  if (initialCreationId && creations && creations.length > 0 && !hasAutoSelectedRef.current) {
@@ -180,20 +190,49 @@ export function CreationsGalleryScreen({
180
190
  />
181
191
  ), [handleShareCard, handleDelete, handleFavorite, handleCardPress, getScenarioTitle]);
182
192
 
193
+ // Status labels for pending jobs
194
+ const pendingJobStatusLabels = useMemo(() => ({
195
+ queued: t("generator.status.queued") || "Waiting in queue...",
196
+ processing: t("generator.status.processing") || "Processing...",
197
+ uploading: t("generator.status.uploading") || "Uploading...",
198
+ completed: t("generator.status.completed") || "Completed",
199
+ failed: t("generator.status.failed") || "Failed",
200
+ }), [t]);
201
+
183
202
  const renderHeader = useMemo(() => {
184
- if ((!creations || creations.length === 0) && !isLoading) return null;
203
+ const hasPendingJobs = showPendingJobs && pendingJobs.length > 0;
204
+ const hasCreations = creations && creations.length > 0;
205
+
206
+ if (!hasPendingJobs && !hasCreations && !isLoading) return null;
207
+
185
208
  return (
186
- <View style={[styles.header, { backgroundColor: tokens.colors.surface, borderBottomColor: tokens.colors.border }]}>
187
- <GalleryHeader
188
- title={t(config.translations.title)}
189
- count={filters.filtered.length}
190
- countLabel={t(config.translations.photoCount)}
191
- showFilter={showFilter}
192
- filterButtons={filterButtons}
193
- />
209
+ <View>
210
+ {/* Pending Jobs Section */}
211
+ {showPendingJobs && (
212
+ <PendingJobsSection
213
+ jobs={pendingJobs}
214
+ onCancel={removeJob}
215
+ title={pendingJobsTitle || t("creations.pendingJobs") || "Processing..."}
216
+ statusLabels={pendingJobStatusLabels}
217
+ getTypeLabel={getScenarioTitle}
218
+ />
219
+ )}
220
+
221
+ {/* Gallery Header */}
222
+ {hasCreations && (
223
+ <View style={[styles.header, { backgroundColor: tokens.colors.surface, borderBottomColor: tokens.colors.border }]}>
224
+ <GalleryHeader
225
+ title={t(config.translations.title)}
226
+ count={filters.filtered.length}
227
+ countLabel={t(config.translations.photoCount)}
228
+ showFilter={showFilter}
229
+ filterButtons={filterButtons}
230
+ />
231
+ </View>
232
+ )}
194
233
  </View>
195
234
  );
196
- }, [creations, isLoading, filters.filtered.length, showFilter, filterButtons, t, config, tokens]);
235
+ }, [creations, isLoading, filters.filtered.length, showFilter, filterButtons, t, config, tokens, showPendingJobs, pendingJobs, removeJob, pendingJobsTitle, pendingJobStatusLabels, getScenarioTitle]);
197
236
 
198
237
  const renderEmpty = useMemo(() => (
199
238
  <GalleryEmptyStates
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * useWizardGeneration Hook
3
3
  * Wizard generation using orchestrator + strategy factory pattern
4
+ * Includes background job tracking for CreationsGallery display
4
5
  */
5
6
 
6
- import { useEffect, useRef, useMemo } from "react";
7
+ import { useEffect, useRef, useMemo, useCallback } from "react";
7
8
  import { useGenerationOrchestrator } from "../../../../../presentation/hooks/generation";
8
9
  import type { AlertMessages } from "../../../../../presentation/hooks/generation/types";
10
+ import { usePendingJobs } from "../../../../../presentation/hooks/use-pending-jobs";
9
11
  import { createWizardStrategy, buildWizardInput } from "../../infrastructure/strategies";
10
12
 
11
13
  declare const __DEV__: boolean;
@@ -32,10 +34,14 @@ export interface UseWizardGenerationProps {
32
34
  readonly onSuccess?: (result: unknown) => void;
33
35
  readonly onError?: (error: string) => void;
34
36
  readonly onCreditsExhausted?: () => void;
37
+ /** Enable background job tracking for CreationsGallery display */
38
+ readonly trackAsBackgroundJob?: boolean;
35
39
  }
36
40
 
37
41
  export interface UseWizardGenerationReturn {
38
42
  readonly isGenerating: boolean;
43
+ /** Current job ID if tracking is enabled */
44
+ readonly currentJobId: string | null;
39
45
  }
40
46
 
41
47
  export const useWizardGeneration = (
@@ -50,18 +56,24 @@ export const useWizardGeneration = (
50
56
  onSuccess,
51
57
  onError,
52
58
  onCreditsExhausted,
59
+ trackAsBackgroundJob = true,
53
60
  } = props;
54
61
 
55
62
  const hasStarted = useRef(false);
63
+ const currentJobIdRef = useRef<string | null>(null);
64
+
65
+ // Background job tracking
66
+ const { addJob, updateJob, removeJob } = usePendingJobs();
56
67
 
57
68
  useEffect(() => {
58
69
  if (typeof __DEV__ !== "undefined" && __DEV__) {
59
70
  console.log("[useWizardGeneration] Initialized", {
60
71
  scenarioId: scenario.id,
61
72
  outputType: scenario.outputType || "video",
73
+ trackAsBackgroundJob,
62
74
  });
63
75
  }
64
- }, [scenario.id, scenario.outputType]);
76
+ }, [scenario.id, scenario.outputType, trackAsBackgroundJob]);
65
77
 
66
78
  const strategy = useMemo(() => {
67
79
  return createWizardStrategy({
@@ -71,6 +83,44 @@ export const useWizardGeneration = (
71
83
  });
72
84
  }, [scenario, wizardData]);
73
85
 
86
+ // Handle generation success - remove job from queue
87
+ const handleSuccess = useCallback(
88
+ (result: unknown) => {
89
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
90
+ console.log("[useWizardGeneration] Success");
91
+ }
92
+
93
+ // Remove job from pending queue (creation is saved)
94
+ if (trackAsBackgroundJob && currentJobIdRef.current) {
95
+ removeJob(currentJobIdRef.current);
96
+ currentJobIdRef.current = null;
97
+ }
98
+
99
+ onSuccess?.(result);
100
+ },
101
+ [trackAsBackgroundJob, removeJob, onSuccess],
102
+ );
103
+
104
+ // Handle generation error - update job status
105
+ const handleError = useCallback(
106
+ (err: { message: string }) => {
107
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
108
+ console.log("[useWizardGeneration] Error:", err.message);
109
+ }
110
+
111
+ // Update job to failed status
112
+ if (trackAsBackgroundJob && currentJobIdRef.current) {
113
+ updateJob({
114
+ id: currentJobIdRef.current,
115
+ updates: { status: "failed", error: err.message, progress: 0 },
116
+ });
117
+ }
118
+
119
+ onError?.(err.message);
120
+ },
121
+ [trackAsBackgroundJob, updateJob, onError],
122
+ );
123
+
74
124
  const { generate, isGenerating } = useGenerationOrchestrator(
75
125
  strategy,
76
126
  {
@@ -83,18 +133,8 @@ export const useWizardGeneration = (
83
133
  unknown: "An error occurred",
84
134
  },
85
135
  onCreditsExhausted,
86
- onSuccess: (result) => {
87
- if (typeof __DEV__ !== "undefined" && __DEV__) {
88
- console.log("[useWizardGeneration] Success");
89
- }
90
- onSuccess?.(result);
91
- },
92
- onError: (err) => {
93
- if (typeof __DEV__ !== "undefined" && __DEV__) {
94
- console.log("[useWizardGeneration] Error:", err.message);
95
- }
96
- onError?.(err.message);
97
- },
136
+ onSuccess: handleSuccess,
137
+ onError: handleError,
98
138
  },
99
139
  );
100
140
 
@@ -116,6 +156,29 @@ export const useWizardGeneration = (
116
156
  onError?.("Failed to build generation input");
117
157
  return;
118
158
  }
159
+
160
+ // Create background job for tracking
161
+ if (trackAsBackgroundJob) {
162
+ const jobId = `wizard-${scenario.id}-${Date.now()}`;
163
+ currentJobIdRef.current = jobId;
164
+
165
+ addJob({
166
+ id: jobId,
167
+ input: {
168
+ scenarioId: scenario.id,
169
+ scenarioTitle: scenario.title || scenario.id,
170
+ outputType: scenario.outputType || "video",
171
+ },
172
+ type: scenario.outputType || "video",
173
+ status: "processing",
174
+ progress: 10,
175
+ });
176
+
177
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
178
+ console.log("[useWizardGeneration] Created background job:", jobId);
179
+ }
180
+ }
181
+
119
182
  generate(input);
120
183
  })
121
184
  .catch((error) => {
@@ -130,7 +193,19 @@ export const useWizardGeneration = (
130
193
  if (!isGeneratingStep && hasStarted.current) {
131
194
  hasStarted.current = false;
132
195
  }
133
- }, [isGeneratingStep, scenario, wizardData, isGenerating, generate, onError]);
134
-
135
- return { isGenerating };
196
+ }, [
197
+ isGeneratingStep,
198
+ scenario,
199
+ wizardData,
200
+ isGenerating,
201
+ generate,
202
+ onError,
203
+ trackAsBackgroundJob,
204
+ addJob,
205
+ ]);
206
+
207
+ return {
208
+ isGenerating,
209
+ currentJobId: currentJobIdRef.current,
210
+ };
136
211
  };
@@ -20,7 +20,6 @@ export const IDENTITY_PRESERVATION_CORE = `CRITICAL IDENTITY PRESERVATION (HIGHE
20
20
  "mandatory_rules": [
21
21
  "The face must be EXACTLY as it appears in the reference photo - 100% identical",
22
22
  "Preserve every facial detail: bone structure, eye shape, eye color, nose shape, lip shape",
23
- "Maintain natural skin texture with pores, marks, and realistic details",
24
23
  "Keep the person instantly recognizable - any deviation is NOT acceptable"
25
24
  ],
26
25
  "forbidden_modifications": [
@@ -28,7 +27,6 @@ export const IDENTITY_PRESERVATION_CORE = `CRITICAL IDENTITY PRESERVATION (HIGHE
28
27
  "Do NOT alter eye color, eye shape, or eye spacing",
29
28
  "Do NOT modify nose shape, size, or position",
30
29
  "Do NOT change lip shape, thickness, or natural expression",
31
- "Do NOT alter skin tone or smooth skin texture",
32
30
  "Do NOT remove natural features like freckles, moles, or wrinkles"
33
31
  ],
34
32
  "verification": "Before output: confirm face matches reference photo with 100% accuracy"
@@ -36,17 +34,14 @@ export const IDENTITY_PRESERVATION_CORE = `CRITICAL IDENTITY PRESERVATION (HIGHE
36
34
 
37
35
  /**
38
36
  * Photorealistic rendering instruction
39
- * Ensures high-quality, professional photography output
40
37
  */
41
38
  export const PHOTOREALISTIC_RENDERING = `PHOTOREALISTIC RENDERING REQUIREMENTS:
42
39
  {
43
- "style": "HIGH-END PHOTOREALISTIC PHOTOGRAPH",
44
- "quality": "8k resolution, ultra-detailed textures, professional photography",
45
- "lighting": "Cinematic lighting with natural shadows and highlights",
46
- "camera": "Shot on professional DSLR camera (Canon EOS R5, Sony A7R V, or equivalent)",
47
- "lens": "Professional lens with appropriate focal length (35mm, 50mm, 85mm)",
40
+ "style": "PHOTOREALISTIC PHOTOGRAPH",
41
+ "quality": "high quality, professional photography",
42
+ "lighting": "Natural lighting with realistic shadows and highlights",
48
43
  "prohibited": "STRICTLY NO anime, cartoons, illustrations, sketches, 3D renders, or non-photorealistic styles",
49
- "output": "Must look like a real photograph taken by a professional photographer"
44
+ "output": "Must look like a real photograph"
50
45
  }`;
51
46
 
52
47
  /**
@@ -150,14 +145,14 @@ TRANSFORMATION REQUEST:
150
145
  "environment_update": "${background.replace(/\n/g, ' ').trim()}"
151
146
  },
152
147
  "visual_constraints": {
153
- "style_matching": "Render as a premium DSLR photograph",
148
+ "style_matching": "Render as a premium photograph",
154
149
  "face_preservation": "Maintain 100% identity of the person",
155
- "lighting": "Realistic professional studio or outdoor cinematic lighting",
150
+ "lighting": "Realistic professional recording lighting",
156
151
  "pose": "Natural, contextually appropriate pose"
157
152
  }
158
153
  }
159
154
 
160
- FINAL COMMAND: Transform the input person into a strictly photorealistic ${styleName}. The result MUST be a real-life looking person in high-quality ${styleName} attire, maintaining perfect facial identity.`;
155
+ FINAL COMMAND: Transform the input person into a photorealistic ${styleName}. The result MUST be a real-life looking person in high-quality ${styleName} attire, maintaining perfect facial identity.`;
161
156
 
162
157
  /**
163
158
  * Simplified prompt for scenarios that already include detailed instructions
@@ -171,54 +166,3 @@ export const enhanceExistingPrompt = (existingPrompt: string): string => {
171
166
 
172
167
  ${existingPrompt}`;
173
168
  };
174
-
175
- /**
176
- * Multi-person identity preservation rules
177
- * Ensures all people maintain their identities with strict rules
178
- * Supports any number of people (1, 2, 3, N)
179
- */
180
- export const MULTI_PERSON_PRESERVATION_RULES = {
181
- requirement: "ALL individuals must have 100% identical facial appearance to their reference photos",
182
- perPersonRule: "Use EXACTLY the person from @imageN - preserve 100% identical facial features",
183
- forbidden: [
184
- "Do NOT swap, mix, or blend facial features between people",
185
- "Do NOT idealize or beautify any face",
186
- "Do NOT alter facial proportions or characteristics",
187
- ],
188
- positioning: "Natural positioning, all looking at camera with natural expressions",
189
- } as const;
190
-
191
- /**
192
- * Creates a multi-person prompt dynamically
193
- *
194
- * @param scenarioPrompt - The scenario description
195
- * @param personCount - Number of people (1, 2, 3, N)
196
- * @returns Complete prompt with identity preservation for all people
197
- */
198
- export const createMultiPersonPrompt = (
199
- scenarioPrompt: string,
200
- personCount: number,
201
- ): string => {
202
- const personRefs = Array.from({ length: personCount }, (_, i) =>
203
- `Person ${i + 1}: @image${i + 1} - preserve 100% facial identity`
204
- ).join("\n ");
205
-
206
- return `${IDENTITY_PRESERVATION_CORE}
207
-
208
- MULTI-PERSON IDENTITY PRESERVATION (${personCount} people):
209
- {
210
- "requirement": "${MULTI_PERSON_PRESERVATION_RULES.requirement}",
211
- "references": [
212
- ${personRefs}
213
- ],
214
- "forbidden": ${JSON.stringify(MULTI_PERSON_PRESERVATION_RULES.forbidden)},
215
- "positioning": "${MULTI_PERSON_PRESERVATION_RULES.positioning}"
216
- }
217
-
218
- ${PHOTOREALISTIC_RENDERING}
219
-
220
- ${NATURAL_POSE_GUIDELINES}
221
-
222
- SCENARIO DESCRIPTION:
223
- ${scenarioPrompt}`;
224
- };
@@ -0,0 +1,59 @@
1
+ import { IDENTITY_PRESERVATION_CORE, NATURAL_POSE_GUIDELINES, PHOTOREALISTIC_RENDERING } from "./BasePromptStructure";
2
+
3
+ /**
4
+ * Multi-person identity preservation rules
5
+ * Ensures all people maintain their identities with strict rules
6
+ * Supports any number of people (1, 2, 3, N)
7
+ */
8
+ export interface MultiPersonPreservationRules {
9
+ requirement: string;
10
+ perPersonRule: string;
11
+ forbidden: string[];
12
+ positioning: string;
13
+ }
14
+
15
+ export const MULTI_PERSON_PRESERVATION_RULES: MultiPersonPreservationRules = {
16
+ requirement: "ALL individuals must have 100% identical facial appearance to their reference photos",
17
+ perPersonRule: "Use EXACTLY the person from @imageN - preserve 100% identical facial features",
18
+ forbidden: [
19
+ "Do NOT swap, mix, or blend facial features between people",
20
+ "Do NOT idealize or beautify any face",
21
+ "Do NOT alter facial proportions or characteristics",
22
+ ],
23
+ positioning: "Natural positioning, all looking at camera with natural expressions",
24
+ };
25
+
26
+ /**
27
+ * Creates a multi-person prompt dynamically
28
+ *
29
+ * @param scenarioPrompt - The scenario description
30
+ * @param personCount - Number of people (1, 2, 3, N)
31
+ * @returns Complete prompt with identity preservation for all people
32
+ */
33
+ export const createMultiPersonPrompt = (
34
+ scenarioPrompt: string,
35
+ personCount: number,
36
+ ): string => {
37
+ const personRefs = Array.from({ length: personCount }, (_, i) =>
38
+ `Person ${i + 1}: @image${i + 1} - preserve 100% facial identity`
39
+ ).join("\n ");
40
+
41
+ return `${IDENTITY_PRESERVATION_CORE}
42
+
43
+ MULTI-PERSON IDENTITY PRESERVATION (${personCount} people):
44
+ {
45
+ "requirement": "${MULTI_PERSON_PRESERVATION_RULES.requirement}",
46
+ "references": [
47
+ ${personRefs}
48
+ ],
49
+ "forbidden": ${JSON.stringify(MULTI_PERSON_PRESERVATION_RULES.forbidden)},
50
+ "positioning": "${MULTI_PERSON_PRESERVATION_RULES.positioning}"
51
+ }
52
+
53
+ ${PHOTOREALISTIC_RENDERING}
54
+
55
+ ${NATURAL_POSE_GUIDELINES}
56
+
57
+ SCENARIO DESCRIPTION:
58
+ ${scenarioPrompt}`;
59
+ };
@@ -8,10 +8,8 @@
8
8
  * These ensure consistent, realistic output across all scenarios
9
9
  */
10
10
  export const PHOTOREALISTIC_BASE = {
11
- quality: "ultra-realistic photograph, photorealistic, RAW photo quality, 8K UHD resolution",
12
- skin: "natural skin with subtle texture, visible pores, authentic subtle imperfections, realistic human features",
13
- camera: "shot on professional DSLR camera with 85mm f/1.4 lens, shallow depth of field, soft bokeh background",
14
- lighting: "natural volumetric lighting, cinematic color grading",
11
+ quality: "photorealistic",
12
+ lighting: "cinematic lighting",
15
13
  } as const;
16
14
 
17
15
  /**
@@ -25,7 +23,7 @@ export const createPhotorealisticPrompt = (
25
23
  lightingOverride?: string,
26
24
  ): string => {
27
25
  const lighting = lightingOverride ?? PHOTOREALISTIC_BASE.lighting;
28
- return `${PHOTOREALISTIC_BASE.quality}, ${scene}, ${PHOTOREALISTIC_BASE.skin}, ${lighting}, ${PHOTOREALISTIC_BASE.camera}`;
26
+ return `${PHOTOREALISTIC_BASE.quality}, ${scene}, ${lighting}`;
29
27
  };
30
28
 
31
29
  export const createStoryTemplate = (