@umituz/react-native-ai-generation-content 1.26.40 โ 1.26.42
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/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +40 -82
- package/src/domains/generation/wizard/presentation/hooks/usePhotoUploadState.ts +3 -1
- package/src/domains/generation/wizard/presentation/screens/GenericPhotoUploadScreen.tsx +229 -0
- package/src/domains/generation/wizard/presentation/screens/index.ts +6 -0
- package/src/domains/scenarios/domain/Scenario.ts +1 -0
- package/src/domains/scenarios/infrastructure/data/celebration-scenarios.ts +1 -1
- package/src/domains/scenarios/infrastructure/data/connection-scenarios.ts +1 -1
- package/src/domains/scenarios/infrastructure/data/daily-essence-scenarios.ts +2 -2
- package/src/domains/scenarios/infrastructure/data/iconic-moments-scenarios.ts +1 -1
- package/src/domains/scenarios/infrastructure/data/music-scenarios.ts +1 -1
- package/src/domains/scenarios/infrastructure/data/steampunk-scenarios.ts +1 -1
- package/src/domains/scenarios/infrastructure/data/stolen-moments-scenarios.ts +1 -1
- package/src/domains/scenarios/infrastructure/data/surreal-dreams-scenarios.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.42",
|
|
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",
|
|
@@ -21,8 +21,8 @@ import type { WizardFeatureConfig, WizardStepConfig } from "../../domain/entitie
|
|
|
21
21
|
import { buildFlowStepsFromWizard } from "../../infrastructure/builders/dynamic-step-builder";
|
|
22
22
|
import { useWizardGeneration, type WizardScenarioData } from "../hooks/useWizardGeneration";
|
|
23
23
|
import type { AlertMessages } from "../../../../../presentation/hooks/generation/types";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
24
|
+
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
25
|
+
import { GenericPhotoUploadScreen } from "../screens/GenericPhotoUploadScreen";
|
|
26
26
|
|
|
27
27
|
export interface GenericWizardFlowProps {
|
|
28
28
|
readonly featureConfig: WizardFeatureConfig;
|
|
@@ -54,7 +54,7 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
54
54
|
onCreditsExhausted,
|
|
55
55
|
onBack,
|
|
56
56
|
t,
|
|
57
|
-
translations,
|
|
57
|
+
translations: _translations,
|
|
58
58
|
renderPreview,
|
|
59
59
|
renderGenerating,
|
|
60
60
|
renderResult,
|
|
@@ -187,44 +187,6 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
187
187
|
}
|
|
188
188
|
}, [currentStep, currentStepIndex, onStepChange]);
|
|
189
189
|
|
|
190
|
-
// Handle step continue
|
|
191
|
-
const handleStepContinue = useCallback(
|
|
192
|
-
(stepData: Record<string, unknown>) => {
|
|
193
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
194
|
-
console.log("[GenericWizardFlow] Step continue", {
|
|
195
|
-
stepId: currentStep?.id,
|
|
196
|
-
data: stepData,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Store step data in custom data
|
|
201
|
-
Object.entries(stepData).forEach(([key, value]) => {
|
|
202
|
-
setCustomData(key, value);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
// Check if this is the last step before generating
|
|
206
|
-
if (currentStepIndex === flowSteps.length - 2) {
|
|
207
|
-
// Next step is GENERATING
|
|
208
|
-
// Notify parent and provide callback to proceed to generating
|
|
209
|
-
// Parent will call proceedToGenerating() after feature gate passes
|
|
210
|
-
if (onGenerationStart) {
|
|
211
|
-
onGenerationStart(customData, () => {
|
|
212
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
213
|
-
console.log("[GenericWizardFlow] Proceeding to GENERATING step");
|
|
214
|
-
}
|
|
215
|
-
nextStep();
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
// DON'T call nextStep() here - parent will call it via proceedToGenerating callback
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Move to next step (for all non-generation steps)
|
|
223
|
-
nextStep();
|
|
224
|
-
},
|
|
225
|
-
[currentStep, currentStepIndex, customData, setCustomData, nextStep, flowSteps.length, onGenerationStart],
|
|
226
|
-
);
|
|
227
|
-
|
|
228
190
|
// Handle back
|
|
229
191
|
const handleBack = useCallback(() => {
|
|
230
192
|
if (currentStepIndex === 0) {
|
|
@@ -234,24 +196,26 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
234
196
|
}
|
|
235
197
|
}, [currentStepIndex, previousStep, onBack]);
|
|
236
198
|
|
|
237
|
-
//
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
199
|
+
// Handle photo continue - saves photo and moves to next step
|
|
200
|
+
const handlePhotoContinue = useCallback((stepId: string, image: UploadedImage) => {
|
|
201
|
+
setCustomData(stepId, image);
|
|
202
|
+
|
|
203
|
+
// Check if this is the last step before generating
|
|
204
|
+
if (currentStepIndex === flowSteps.length - 2) {
|
|
205
|
+
// Next step is GENERATING - call onGenerationStart
|
|
206
|
+
if (onGenerationStart) {
|
|
207
|
+
onGenerationStart({ ...customData, [stepId]: image }, () => {
|
|
208
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
209
|
+
console.log("[GenericWizardFlow] Proceeding to GENERATING step");
|
|
210
|
+
}
|
|
211
|
+
nextStep();
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
253
215
|
}
|
|
254
|
-
|
|
216
|
+
|
|
217
|
+
nextStep();
|
|
218
|
+
}, [currentStepIndex, flowSteps.length, customData, setCustomData, nextStep, onGenerationStart]);
|
|
255
219
|
|
|
256
220
|
// Render current step
|
|
257
221
|
const renderCurrentStep = useCallback(() => {
|
|
@@ -290,37 +254,31 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
290
254
|
const titleKey = wizardConfig?.titleKey || `wizard.steps.${step.id}.title`;
|
|
291
255
|
const title = t(titleKey);
|
|
292
256
|
|
|
293
|
-
// Subtitle
|
|
294
|
-
const
|
|
257
|
+
// Subtitle from config
|
|
258
|
+
const subtitleKey = wizardConfig?.subtitleKey || `wizard.steps.${step.id}.subtitle`;
|
|
259
|
+
const subtitle = t(subtitleKey);
|
|
295
260
|
|
|
296
261
|
// Get existing photo for this step from customData
|
|
297
|
-
const existingPhoto = customData[step.id];
|
|
298
|
-
const imageUri = existingPhoto && typeof existingPhoto === "object" && "uri" in existingPhoto
|
|
299
|
-
? (existingPhoto.uri as string)
|
|
300
|
-
: photoUploadHook.image?.uri || null;
|
|
262
|
+
const existingPhoto = customData[step.id] as UploadedImage | undefined;
|
|
301
263
|
|
|
302
264
|
return (
|
|
303
|
-
<
|
|
304
|
-
config={{
|
|
305
|
-
enabled: true,
|
|
306
|
-
order: currentStepIndex,
|
|
307
|
-
id: step.id,
|
|
308
|
-
header: {},
|
|
309
|
-
photoCard: {},
|
|
310
|
-
enableValidation: false,
|
|
311
|
-
}}
|
|
312
|
-
imageUri={imageUri}
|
|
313
|
-
isValidating={false}
|
|
314
|
-
isValid={null}
|
|
315
|
-
onPhotoSelect={photoUploadHook.handlePickImage}
|
|
316
|
-
disabled={false}
|
|
317
|
-
title={title}
|
|
318
|
-
subtitle={subtitle}
|
|
265
|
+
<GenericPhotoUploadScreen
|
|
319
266
|
translations={{
|
|
267
|
+
title,
|
|
268
|
+
subtitle,
|
|
269
|
+
continue: t("common.continue"),
|
|
320
270
|
tapToUpload: t("photoUpload.tapToUpload"),
|
|
321
271
|
selectPhoto: t("photoUpload.selectPhoto"),
|
|
322
272
|
change: t("common.change"),
|
|
273
|
+
fileTooLarge: t("common.errors.file_too_large"),
|
|
274
|
+
maxFileSize: t("common.errors.max_file_size"),
|
|
275
|
+
error: t("common.error"),
|
|
276
|
+
uploadFailed: t("common.errors.upload_failed"),
|
|
323
277
|
}}
|
|
278
|
+
t={t}
|
|
279
|
+
onBack={handleBack}
|
|
280
|
+
onContinue={(image) => handlePhotoContinue(step.id, image)}
|
|
281
|
+
existingImage={existingPhoto}
|
|
324
282
|
/>
|
|
325
283
|
);
|
|
326
284
|
}
|
|
@@ -334,16 +292,16 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
334
292
|
}
|
|
335
293
|
}, [
|
|
336
294
|
currentStep,
|
|
295
|
+
customData,
|
|
337
296
|
generationProgress,
|
|
338
297
|
generationResult,
|
|
339
298
|
nextStep,
|
|
340
299
|
renderPreview,
|
|
341
300
|
renderGenerating,
|
|
342
301
|
renderResult,
|
|
343
|
-
|
|
302
|
+
handlePhotoContinue,
|
|
344
303
|
handleBack,
|
|
345
304
|
t,
|
|
346
|
-
translations,
|
|
347
305
|
]);
|
|
348
306
|
|
|
349
307
|
return (
|
|
@@ -23,6 +23,7 @@ export interface PhotoUploadTranslations {
|
|
|
23
23
|
export interface UsePhotoUploadStateProps {
|
|
24
24
|
readonly config?: PhotoUploadConfig;
|
|
25
25
|
readonly translations: PhotoUploadTranslations;
|
|
26
|
+
readonly initialImage?: UploadedImage;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export interface UsePhotoUploadStateReturn {
|
|
@@ -36,8 +37,9 @@ const DEFAULT_MAX_FILE_SIZE_MB = 10;
|
|
|
36
37
|
export const usePhotoUploadState = ({
|
|
37
38
|
config,
|
|
38
39
|
translations,
|
|
40
|
+
initialImage,
|
|
39
41
|
}: UsePhotoUploadStateProps): UsePhotoUploadStateReturn => {
|
|
40
|
-
const [image, setImage] = useState<UploadedImage | null>(null);
|
|
42
|
+
const [image, setImage] = useState<UploadedImage | null>(initialImage || null);
|
|
41
43
|
|
|
42
44
|
const maxFileSizeMB = config?.maxFileSizeMB ?? DEFAULT_MAX_FILE_SIZE_MB;
|
|
43
45
|
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Photo Upload Screen
|
|
3
|
+
* Used by wizard domain for ANY photo upload step
|
|
4
|
+
* NO feature-specific concepts (no partner, couple, etc.)
|
|
5
|
+
* Works for: couple features, face-swap, image-to-video, ANY photo upload need
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useMemo } from "react";
|
|
9
|
+
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
10
|
+
import {
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
ScreenLayout,
|
|
13
|
+
AtomicText,
|
|
14
|
+
AtomicIcon,
|
|
15
|
+
NavigationHeader,
|
|
16
|
+
InfoGrid,
|
|
17
|
+
type DesignTokens,
|
|
18
|
+
type InfoGridItem,
|
|
19
|
+
} from "@umituz/react-native-design-system";
|
|
20
|
+
import { PhotoUploadCard } from "../../../../../presentation/components";
|
|
21
|
+
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
22
|
+
import { usePhotoUploadState } from "../hooks/usePhotoUploadState";
|
|
23
|
+
|
|
24
|
+
export interface PhotoUploadScreenTranslations {
|
|
25
|
+
readonly title: string;
|
|
26
|
+
readonly subtitle: string;
|
|
27
|
+
readonly continue: string;
|
|
28
|
+
readonly tapToUpload: string;
|
|
29
|
+
readonly selectPhoto: string;
|
|
30
|
+
readonly change: string;
|
|
31
|
+
readonly analyzing?: string;
|
|
32
|
+
readonly fileTooLarge: string;
|
|
33
|
+
readonly maxFileSize: string;
|
|
34
|
+
readonly error: string;
|
|
35
|
+
readonly uploadFailed: string;
|
|
36
|
+
readonly aiDisclosure?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PhotoUploadScreenConfig {
|
|
40
|
+
readonly showPhotoTips?: boolean;
|
|
41
|
+
readonly maxFileSizeMB?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface PhotoUploadScreenProps {
|
|
45
|
+
readonly translations: PhotoUploadScreenTranslations;
|
|
46
|
+
readonly t: (key: string) => string;
|
|
47
|
+
readonly config?: PhotoUploadScreenConfig;
|
|
48
|
+
readonly onBack: () => void;
|
|
49
|
+
readonly onContinue: (image: UploadedImage) => void;
|
|
50
|
+
readonly existingImage?: UploadedImage | null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const DEFAULT_CONFIG: PhotoUploadScreenConfig = {
|
|
54
|
+
showPhotoTips: true,
|
|
55
|
+
maxFileSizeMB: 10,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const GenericPhotoUploadScreen: React.FC<PhotoUploadScreenProps> = ({
|
|
59
|
+
translations,
|
|
60
|
+
t,
|
|
61
|
+
config = DEFAULT_CONFIG,
|
|
62
|
+
onBack,
|
|
63
|
+
onContinue,
|
|
64
|
+
existingImage,
|
|
65
|
+
}) => {
|
|
66
|
+
const tokens = useAppDesignTokens();
|
|
67
|
+
|
|
68
|
+
const { image, handlePickImage, canContinue } = usePhotoUploadState({
|
|
69
|
+
config: { maxFileSizeMB: config.maxFileSizeMB },
|
|
70
|
+
translations: {
|
|
71
|
+
fileTooLarge: translations.fileTooLarge,
|
|
72
|
+
maxFileSize: translations.maxFileSize,
|
|
73
|
+
error: translations.error,
|
|
74
|
+
uploadFailed: translations.uploadFailed,
|
|
75
|
+
},
|
|
76
|
+
initialImage: existingImage || undefined,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const handleContinuePress = () => {
|
|
80
|
+
if (!canContinue || !image) return;
|
|
81
|
+
onContinue(image);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const styles = useMemo(() => createStyles(tokens), [tokens]);
|
|
85
|
+
const showPhotoTips = config.showPhotoTips ?? true;
|
|
86
|
+
|
|
87
|
+
// Build photo tips items from translations
|
|
88
|
+
const photoTipsItems: InfoGridItem[] = useMemo(() => {
|
|
89
|
+
const tipKeys = [
|
|
90
|
+
{ key: "photoUpload.tips.clearFace", icon: "Smile" },
|
|
91
|
+
{ key: "photoUpload.tips.goodLighting", icon: "Sun" },
|
|
92
|
+
{ key: "photoUpload.tips.recentPhoto", icon: "Clock" },
|
|
93
|
+
{ key: "photoUpload.tips.noFilters", icon: "Image" },
|
|
94
|
+
];
|
|
95
|
+
return tipKeys.map(({ key, icon }) => ({
|
|
96
|
+
text: t(key),
|
|
97
|
+
icon,
|
|
98
|
+
}));
|
|
99
|
+
}, [t]);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
103
|
+
<NavigationHeader
|
|
104
|
+
title={translations.title}
|
|
105
|
+
onBackPress={onBack}
|
|
106
|
+
rightElement={
|
|
107
|
+
<TouchableOpacity
|
|
108
|
+
onPress={handleContinuePress}
|
|
109
|
+
activeOpacity={0.7}
|
|
110
|
+
disabled={!canContinue || !image}
|
|
111
|
+
style={[
|
|
112
|
+
styles.continueButton,
|
|
113
|
+
{
|
|
114
|
+
backgroundColor: canContinue && image ? tokens.colors.primary : tokens.colors.surfaceVariant,
|
|
115
|
+
opacity: canContinue && image ? 1 : 0.5,
|
|
116
|
+
},
|
|
117
|
+
]}
|
|
118
|
+
>
|
|
119
|
+
<AtomicText
|
|
120
|
+
type="bodyMedium"
|
|
121
|
+
style={[
|
|
122
|
+
styles.continueText,
|
|
123
|
+
{ color: canContinue && image ? tokens.colors.onPrimary : tokens.colors.textSecondary },
|
|
124
|
+
]}
|
|
125
|
+
>
|
|
126
|
+
{translations.continue}
|
|
127
|
+
</AtomicText>
|
|
128
|
+
<AtomicIcon
|
|
129
|
+
name="ChevronRight"
|
|
130
|
+
size="sm"
|
|
131
|
+
color={canContinue && image ? "onPrimary" : "textSecondary"}
|
|
132
|
+
/>
|
|
133
|
+
</TouchableOpacity>
|
|
134
|
+
}
|
|
135
|
+
/>
|
|
136
|
+
<ScreenLayout
|
|
137
|
+
edges={["left", "right"]}
|
|
138
|
+
backgroundColor="transparent"
|
|
139
|
+
scrollable={true}
|
|
140
|
+
keyboardAvoiding={true}
|
|
141
|
+
contentContainerStyle={styles.scrollContent}
|
|
142
|
+
hideScrollIndicator={true}
|
|
143
|
+
>
|
|
144
|
+
<AtomicText style={[styles.subtitle, { color: tokens.colors.textSecondary }]}>
|
|
145
|
+
{translations.subtitle}
|
|
146
|
+
</AtomicText>
|
|
147
|
+
|
|
148
|
+
{/* Photo Tips - InfoGrid version */}
|
|
149
|
+
{showPhotoTips && (
|
|
150
|
+
<View style={styles.tipsContainer}>
|
|
151
|
+
<InfoGrid
|
|
152
|
+
items={photoTipsItems}
|
|
153
|
+
columns={2}
|
|
154
|
+
title={t("photoUpload.tips.title")}
|
|
155
|
+
headerIcon="Lightbulb"
|
|
156
|
+
/>
|
|
157
|
+
</View>
|
|
158
|
+
)}
|
|
159
|
+
|
|
160
|
+
<PhotoUploadCard
|
|
161
|
+
imageUri={image?.previewUrl || image?.uri || null}
|
|
162
|
+
onPress={handlePickImage}
|
|
163
|
+
isValidating={false}
|
|
164
|
+
isValid={null}
|
|
165
|
+
translations={{
|
|
166
|
+
tapToUpload: translations.tapToUpload,
|
|
167
|
+
selectPhoto: translations.selectPhoto,
|
|
168
|
+
change: translations.change,
|
|
169
|
+
analyzing: translations.analyzing,
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
{translations.aiDisclosure && (
|
|
174
|
+
<View style={styles.disclosureContainer}>
|
|
175
|
+
<AtomicText
|
|
176
|
+
type="labelSmall"
|
|
177
|
+
style={[styles.disclosureText, { color: tokens.colors.textSecondary }]}
|
|
178
|
+
>
|
|
179
|
+
{translations.aiDisclosure}
|
|
180
|
+
</AtomicText>
|
|
181
|
+
</View>
|
|
182
|
+
)}
|
|
183
|
+
</ScreenLayout>
|
|
184
|
+
</View>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const createStyles = (tokens: DesignTokens) =>
|
|
189
|
+
StyleSheet.create({
|
|
190
|
+
container: {
|
|
191
|
+
flex: 1,
|
|
192
|
+
},
|
|
193
|
+
scrollContent: {
|
|
194
|
+
paddingBottom: 40,
|
|
195
|
+
},
|
|
196
|
+
subtitle: {
|
|
197
|
+
fontSize: 16,
|
|
198
|
+
textAlign: "center",
|
|
199
|
+
marginHorizontal: 24,
|
|
200
|
+
marginBottom: 24,
|
|
201
|
+
},
|
|
202
|
+
tipsContainer: {
|
|
203
|
+
marginHorizontal: 24,
|
|
204
|
+
marginBottom: 20,
|
|
205
|
+
},
|
|
206
|
+
continueButton: {
|
|
207
|
+
flexDirection: "row",
|
|
208
|
+
alignItems: "center",
|
|
209
|
+
paddingHorizontal: tokens.spacing.md,
|
|
210
|
+
paddingVertical: tokens.spacing.xs,
|
|
211
|
+
borderRadius: tokens.borders.radius.full,
|
|
212
|
+
},
|
|
213
|
+
continueText: {
|
|
214
|
+
fontWeight: "800",
|
|
215
|
+
marginRight: 4,
|
|
216
|
+
},
|
|
217
|
+
disclosureContainer: {
|
|
218
|
+
marginTop: 24,
|
|
219
|
+
marginHorizontal: 24,
|
|
220
|
+
padding: 16,
|
|
221
|
+
borderRadius: 12,
|
|
222
|
+
backgroundColor: tokens.colors.surfaceVariant + "40",
|
|
223
|
+
},
|
|
224
|
+
disclosureText: {
|
|
225
|
+
textAlign: "center",
|
|
226
|
+
lineHeight: 18,
|
|
227
|
+
opacity: 0.8,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
@@ -92,7 +92,7 @@ export const CONNECTION_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>[] =
|
|
|
92
92
|
description: "Love that deepens with time",
|
|
93
93
|
icon: "๐ณ",
|
|
94
94
|
imageUrl:
|
|
95
|
-
"https://images.unsplash.com/photo-
|
|
95
|
+
"https://images.unsplash.com/photo-1447005497901-b3e9ee359928?w=800",
|
|
96
96
|
aiPrompt:
|
|
97
97
|
"A couple in their older years but with the same spark in their eyes, looking at a photo album of their younger selves, looking at the camera with wise and deeply happy smiles, warm sun-drenched living room with mementos, celebrating a lifetime of growth",
|
|
98
98
|
storyTemplate: createStoryTemplate(
|
|
@@ -8,7 +8,7 @@ export const DAILY_ESSENCE_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>[
|
|
|
8
8
|
description: "Moving in together",
|
|
9
9
|
icon: "๐ฆ",
|
|
10
10
|
imageUrl:
|
|
11
|
-
"https://images.unsplash.com/photo-
|
|
11
|
+
"https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=800",
|
|
12
12
|
aiPrompt:
|
|
13
13
|
"A couple in an empty living room surrounded by cardboard boxes, both looking at the camera with exhausted but happy smiles, eating pizza directly from the box on the floor, messy hair, sunlight streaming through bare windows, sense of new beginnings and shared excitement",
|
|
14
14
|
storyTemplate: createStoryTemplate(
|
|
@@ -22,7 +22,7 @@ export const DAILY_ESSENCE_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>[
|
|
|
22
22
|
description: "Fun in the mundane",
|
|
23
23
|
icon: "๐งบ",
|
|
24
24
|
imageUrl:
|
|
25
|
-
"https://images.unsplash.com/photo-
|
|
25
|
+
"https://images.unsplash.com/photo-1545173168-9f1947eebb7f?w=800",
|
|
26
26
|
aiPrompt:
|
|
27
27
|
"A couple doing laundry together in a bright laundry room, both looking at the camera with playful laughs, throwing a clean sock at each other, piles of colorful clothes around, warm domestic atmosphere, authentic joy in a daily chore",
|
|
28
28
|
storyTemplate: createStoryTemplate(
|
|
@@ -78,7 +78,7 @@ export const ICONIC_MOMENTS_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>
|
|
|
78
78
|
description: "High-flying duo",
|
|
79
79
|
icon: "๐๏ธ",
|
|
80
80
|
imageUrl:
|
|
81
|
-
"https://images.unsplash.com/photo-
|
|
81
|
+
"https://images.unsplash.com/photo-1480714378408-67cf0d13bc1b?w=800",
|
|
82
82
|
aiPrompt:
|
|
83
83
|
"A couple sitting together on a steel beam high above a city skyline, recreating the iconic 1932 photo, both looking at camera with relaxed daring smiles, legs dangling over the edge, misty city and skyscrapers in background, sepia-toned vintage photography, adventurous and brave",
|
|
84
84
|
storyTemplate: createStoryTemplate(
|
|
@@ -63,7 +63,7 @@ export const MUSIC_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>[] = [
|
|
|
63
63
|
description: "Soulful rhythm",
|
|
64
64
|
icon: "๐ท",
|
|
65
65
|
imageUrl:
|
|
66
|
-
"https://images.unsplash.com/photo-
|
|
66
|
+
"https://images.unsplash.com/photo-1511192336575-5a79af67a629?w=800",
|
|
67
67
|
aiPrompt:
|
|
68
68
|
"A couple in a smoky dark jazz club, man playing a saxophone, woman leaning against a piano looking at camera with a soulful expression, wearing elegant evening attire, blue and amber spotlight lighting, intimate and moody",
|
|
69
69
|
storyTemplate: createStoryTemplate(
|
|
@@ -8,7 +8,7 @@ export const STEAMPUNK_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>[] =
|
|
|
8
8
|
description: "Commanders of the clouds",
|
|
9
9
|
icon: "๐",
|
|
10
10
|
imageUrl:
|
|
11
|
-
"https://images.unsplash.com/photo-
|
|
11
|
+
"https://images.unsplash.com/photo-1534447677768-be436bb09401?w=800",
|
|
12
12
|
aiPrompt:
|
|
13
13
|
"A couple as captains on the bridge of a massive brass-plated airship, looking at the camera with confident adventurous smiles, wearing leather aviator coats and intricate brass goggles, steering wheels and glowing pressure gauges in background, clouds and sunset outside the windows, cinematic steampunk aesthetic",
|
|
14
14
|
storyTemplate: createStoryTemplate(
|
|
@@ -36,7 +36,7 @@ export const STOLEN_MOMENTS_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>
|
|
|
36
36
|
description: "Close enough to touch",
|
|
37
37
|
icon: "๐",
|
|
38
38
|
imageUrl:
|
|
39
|
-
"https://images.unsplash.com/photo-
|
|
39
|
+
"https://images.unsplash.com/photo-1527684651001-731c474bbb5a?w=800",
|
|
40
40
|
aiPrompt:
|
|
41
41
|
"A couple standing very close in a modern glass elevator, man behind woman leaning in towards her neck, woman looking at the camera through the mirror reflection with a breathless expression, silver metal surfaces and digital floor numbers in background, high-tension proximity",
|
|
42
42
|
storyTemplate: createStoryTemplate(
|
|
@@ -36,7 +36,7 @@ export const SURREAL_DREAMS_SCENARIOS: Omit<Scenario, 'outputType' | 'category'>
|
|
|
36
36
|
description: "Symmetry of love",
|
|
37
37
|
icon: "๐ช",
|
|
38
38
|
imageUrl:
|
|
39
|
-
"https://images.unsplash.com/photo-
|
|
39
|
+
"https://images.unsplash.com/photo-1518837695005-2083093ee35b?w=800",
|
|
40
40
|
aiPrompt:
|
|
41
41
|
"A couple standing on a perfectly reflective black crystal floor, surrounded by giant floating mirrors that show different versions of their future together, looking at their reflections with profound smiles, dark void environment with glowing crystalline structures, highly artistic and surreal",
|
|
42
42
|
storyTemplate: createStoryTemplate(
|