@umituz/react-native-ai-generation-content 1.17.267 → 1.17.269
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/features/couple-future/index.ts +19 -0
- package/src/features/couple-future/infrastructure/generationUtils.ts +228 -0
- package/src/features/scenarios/domain/types.ts +75 -0
- package/src/features/scenarios/index.ts +45 -0
- package/src/features/scenarios/presentation/components/InspirationChips.tsx +82 -0
- package/src/features/scenarios/presentation/components/MagicPromptHeadline.tsx +79 -0
- package/src/features/scenarios/presentation/components/ScenarioGrid.tsx +225 -0
- package/src/features/scenarios/presentation/components/ScenarioHeader.tsx +56 -0
- package/src/features/scenarios/presentation/components/StyleSelector.tsx +119 -0
- package/src/features/scenarios/presentation/components/index.ts +18 -0
- package/src/features/scenarios/presentation/screens/MagicPromptScreen.tsx +242 -0
- package/src/features/scenarios/presentation/screens/ScenarioPreviewScreen.tsx +164 -0
- package/src/features/scenarios/presentation/screens/ScenarioSelectorScreen.tsx +65 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.269",
|
|
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",
|
|
@@ -41,3 +41,22 @@ export type {
|
|
|
41
41
|
CoupleFeatureOption,
|
|
42
42
|
} from "./infrastructure/coupleFeatureRegistry";
|
|
43
43
|
export { enhanceCouplePrompt } from "./infrastructure/couplePromptEnhancer";
|
|
44
|
+
|
|
45
|
+
// Generation utilities
|
|
46
|
+
export {
|
|
47
|
+
buildGenerationInputFromConfig,
|
|
48
|
+
processGenerationResultFromConfig,
|
|
49
|
+
buildCreationFromConfig,
|
|
50
|
+
DEFAULT_VISUAL_STYLES,
|
|
51
|
+
} from "./infrastructure/generationUtils";
|
|
52
|
+
export type {
|
|
53
|
+
ScenarioConfig,
|
|
54
|
+
VisualStyleConfig,
|
|
55
|
+
GenerationImage,
|
|
56
|
+
BuildGenerationInputConfig,
|
|
57
|
+
GenerationInputResult,
|
|
58
|
+
ProcessResultConfig,
|
|
59
|
+
GenerationResultData,
|
|
60
|
+
BuildCreationConfig,
|
|
61
|
+
CoupleFutureCreationData,
|
|
62
|
+
} from "./infrastructure/generationUtils";
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Utilities
|
|
3
|
+
* Config-driven input building and result processing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { enhanceCouplePrompt } from "./couplePromptEnhancer";
|
|
7
|
+
import type { CoupleFeatureSelection } from "../domain/types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Scenario config interface - app provides this data
|
|
11
|
+
*/
|
|
12
|
+
export interface ScenarioConfig {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly aiPrompt: string;
|
|
15
|
+
readonly storyTemplate: string;
|
|
16
|
+
readonly title?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Visual style modifiers config
|
|
21
|
+
*/
|
|
22
|
+
export interface VisualStyleConfig {
|
|
23
|
+
readonly [key: string]: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_VISUAL_STYLES: VisualStyleConfig = {
|
|
27
|
+
realistic: "photorealistic, highly detailed, professional photography",
|
|
28
|
+
anime: "anime art style, vibrant colors, manga aesthetic, cel-shaded",
|
|
29
|
+
cinematic: "cinematic lighting, dramatic composition, film photography, movie scene",
|
|
30
|
+
"3d": "3D render, octane render, unreal engine, raytraced lighting",
|
|
31
|
+
} as const;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Uploaded image interface
|
|
35
|
+
*/
|
|
36
|
+
export interface GenerationImage {
|
|
37
|
+
readonly uri: string;
|
|
38
|
+
readonly base64?: string;
|
|
39
|
+
readonly previewUrl?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generation input builder config
|
|
44
|
+
*/
|
|
45
|
+
export interface BuildGenerationInputConfig {
|
|
46
|
+
readonly partnerA: GenerationImage | null;
|
|
47
|
+
readonly partnerB: GenerationImage | null;
|
|
48
|
+
readonly partnerAName: string;
|
|
49
|
+
readonly partnerBName: string;
|
|
50
|
+
readonly scenario: ScenarioConfig | null;
|
|
51
|
+
readonly customPrompt?: string;
|
|
52
|
+
readonly visualStyle: string;
|
|
53
|
+
readonly defaultPartnerAName: string;
|
|
54
|
+
readonly defaultPartnerBName: string;
|
|
55
|
+
readonly coupleFeatureSelection?: CoupleFeatureSelection;
|
|
56
|
+
readonly visualStyles?: VisualStyleConfig;
|
|
57
|
+
readonly customScenarioId?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generation input with base64
|
|
62
|
+
*/
|
|
63
|
+
export interface GenerationInputResult {
|
|
64
|
+
readonly partnerA: GenerationImage;
|
|
65
|
+
readonly partnerB: GenerationImage;
|
|
66
|
+
readonly partnerABase64: string;
|
|
67
|
+
readonly partnerBBase64: string;
|
|
68
|
+
readonly prompt: string;
|
|
69
|
+
readonly partnerAName: string;
|
|
70
|
+
readonly partnerBName: string;
|
|
71
|
+
readonly scenarioId: string;
|
|
72
|
+
readonly customPrompt?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build generation input from config
|
|
77
|
+
*/
|
|
78
|
+
export const buildGenerationInputFromConfig = (
|
|
79
|
+
config: BuildGenerationInputConfig,
|
|
80
|
+
): GenerationInputResult | null => {
|
|
81
|
+
const {
|
|
82
|
+
partnerA,
|
|
83
|
+
partnerB,
|
|
84
|
+
partnerAName,
|
|
85
|
+
partnerBName,
|
|
86
|
+
scenario,
|
|
87
|
+
customPrompt,
|
|
88
|
+
visualStyle,
|
|
89
|
+
defaultPartnerAName,
|
|
90
|
+
defaultPartnerBName,
|
|
91
|
+
coupleFeatureSelection = {},
|
|
92
|
+
visualStyles = DEFAULT_VISUAL_STYLES,
|
|
93
|
+
customScenarioId = "custom",
|
|
94
|
+
} = config;
|
|
95
|
+
|
|
96
|
+
if (!partnerA || !partnerB || !scenario) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let prompt = scenario.aiPrompt;
|
|
101
|
+
|
|
102
|
+
if (scenario.id === customScenarioId && customPrompt) {
|
|
103
|
+
prompt = prompt.replace("{{customPrompt}}", customPrompt);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const styleModifier = visualStyles[visualStyle];
|
|
107
|
+
if (visualStyle !== "realistic" && styleModifier) {
|
|
108
|
+
prompt = prompt.replace(/photorealistic/gi, styleModifier);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
prompt = enhanceCouplePrompt(prompt, coupleFeatureSelection);
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
partnerA,
|
|
115
|
+
partnerB,
|
|
116
|
+
partnerABase64: partnerA.base64 || "",
|
|
117
|
+
partnerBBase64: partnerB.base64 || "",
|
|
118
|
+
prompt,
|
|
119
|
+
partnerAName: partnerAName || defaultPartnerAName,
|
|
120
|
+
partnerBName: partnerBName || defaultPartnerBName,
|
|
121
|
+
scenarioId: scenario.id,
|
|
122
|
+
customPrompt,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Process result config
|
|
128
|
+
*/
|
|
129
|
+
export interface ProcessResultConfig {
|
|
130
|
+
readonly scenarioId: string;
|
|
131
|
+
readonly partnerAName: string;
|
|
132
|
+
readonly partnerBName: string;
|
|
133
|
+
readonly scenario: ScenarioConfig;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Generation result
|
|
138
|
+
*/
|
|
139
|
+
export interface GenerationResultData {
|
|
140
|
+
readonly imageUrl: string;
|
|
141
|
+
readonly story: string;
|
|
142
|
+
readonly date: string;
|
|
143
|
+
readonly scenarioId: string;
|
|
144
|
+
readonly names: string;
|
|
145
|
+
readonly timestamp: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Process generation result from config
|
|
150
|
+
*/
|
|
151
|
+
export const processGenerationResultFromConfig = (
|
|
152
|
+
imageUrl: string,
|
|
153
|
+
config: ProcessResultConfig,
|
|
154
|
+
): GenerationResultData => {
|
|
155
|
+
const { scenario, partnerAName, partnerBName, scenarioId } = config;
|
|
156
|
+
|
|
157
|
+
const futureYear = new Date().getFullYear() + 5 + Math.floor(Math.random() * 20);
|
|
158
|
+
const months = ["May", "June", "August"];
|
|
159
|
+
const randomMonth = months[Math.floor(Math.random() * months.length)];
|
|
160
|
+
const randomDay = Math.floor(Math.random() * 28) + 1;
|
|
161
|
+
|
|
162
|
+
const story = scenario.storyTemplate
|
|
163
|
+
.replace(/\{\{partnerA\}\}/g, partnerAName)
|
|
164
|
+
.replace(/\{\{partnerB\}\}/g, partnerBName)
|
|
165
|
+
.replace(/\{\{year\}\}/g, String(futureYear));
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
imageUrl,
|
|
169
|
+
story,
|
|
170
|
+
date: `${randomMonth} ${randomDay}, ${futureYear}`,
|
|
171
|
+
scenarioId,
|
|
172
|
+
names: `${partnerAName} & ${partnerBName}`,
|
|
173
|
+
timestamp: Date.now(),
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build creation config
|
|
179
|
+
*/
|
|
180
|
+
export interface BuildCreationConfig {
|
|
181
|
+
readonly scenarioId: string;
|
|
182
|
+
readonly partnerAName: string;
|
|
183
|
+
readonly partnerBName: string;
|
|
184
|
+
readonly scenarioTitle?: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Creation data
|
|
189
|
+
*/
|
|
190
|
+
export interface CoupleFutureCreationData {
|
|
191
|
+
readonly id: string;
|
|
192
|
+
readonly uri: string;
|
|
193
|
+
readonly type: string;
|
|
194
|
+
readonly prompt: string;
|
|
195
|
+
readonly metadata: {
|
|
196
|
+
readonly scenarioId: string;
|
|
197
|
+
readonly partnerAName: string;
|
|
198
|
+
readonly partnerBName: string;
|
|
199
|
+
};
|
|
200
|
+
readonly createdAt: Date;
|
|
201
|
+
readonly isShared: boolean;
|
|
202
|
+
readonly isFavorite: boolean;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Build creation from result
|
|
207
|
+
*/
|
|
208
|
+
export const buildCreationFromConfig = (
|
|
209
|
+
result: GenerationResultData,
|
|
210
|
+
config: BuildCreationConfig,
|
|
211
|
+
): CoupleFutureCreationData => {
|
|
212
|
+
const { scenarioId, partnerAName, partnerBName, scenarioTitle } = config;
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
id: `${scenarioId}_${Date.now()}`,
|
|
216
|
+
uri: result.imageUrl,
|
|
217
|
+
type: scenarioId,
|
|
218
|
+
prompt: scenarioTitle || scenarioId,
|
|
219
|
+
metadata: {
|
|
220
|
+
scenarioId,
|
|
221
|
+
partnerAName,
|
|
222
|
+
partnerBName,
|
|
223
|
+
},
|
|
224
|
+
createdAt: new Date(),
|
|
225
|
+
isShared: false,
|
|
226
|
+
isFavorite: false,
|
|
227
|
+
};
|
|
228
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario Domain Types
|
|
3
|
+
* Generic types for scenario selection feature
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export enum ScenarioCategory {
|
|
7
|
+
TIME_TRAVEL = "time_travel",
|
|
8
|
+
FAMILY = "family",
|
|
9
|
+
LIFESTYLE = "lifestyle",
|
|
10
|
+
FANTASY = "fantasy",
|
|
11
|
+
CAREER = "career",
|
|
12
|
+
TRAVEL = "travel",
|
|
13
|
+
CULTURAL = "cultural",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ScenarioData {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly category?: ScenarioCategory;
|
|
19
|
+
readonly title: string;
|
|
20
|
+
readonly description: string;
|
|
21
|
+
readonly icon: string;
|
|
22
|
+
readonly imageUrl?: string;
|
|
23
|
+
readonly previewImageUrl?: string;
|
|
24
|
+
readonly aiPrompt: string;
|
|
25
|
+
readonly storyTemplate: string;
|
|
26
|
+
readonly requiresPhoto?: boolean;
|
|
27
|
+
readonly hidden?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ScenarioSelectorConfig {
|
|
31
|
+
readonly titleKey: string;
|
|
32
|
+
readonly subtitleKey: string;
|
|
33
|
+
readonly showCategoryFilter?: boolean;
|
|
34
|
+
readonly enableSearch?: boolean;
|
|
35
|
+
readonly pageSize?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ScenarioPreviewConfig {
|
|
39
|
+
readonly showTips?: boolean;
|
|
40
|
+
readonly showDetails?: boolean;
|
|
41
|
+
readonly enableCustomization?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface MagicPromptConfig {
|
|
45
|
+
readonly maxLength: number;
|
|
46
|
+
readonly minLength: number;
|
|
47
|
+
readonly headerKey: string;
|
|
48
|
+
readonly headlinePart1Key: string;
|
|
49
|
+
readonly headlinePart2Key: string;
|
|
50
|
+
readonly subtitleKey: string;
|
|
51
|
+
readonly inputLabelKey: string;
|
|
52
|
+
readonly surpriseButtonKey: string;
|
|
53
|
+
readonly placeholderKey: string;
|
|
54
|
+
readonly styleTitleKey: string;
|
|
55
|
+
readonly inspirationTitleKey: string;
|
|
56
|
+
readonly continueKey: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface VisualStyleOption {
|
|
60
|
+
readonly id: string;
|
|
61
|
+
readonly icon: string;
|
|
62
|
+
readonly labelKey: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface InspirationChipData {
|
|
66
|
+
readonly id: string;
|
|
67
|
+
readonly labelKey: string;
|
|
68
|
+
readonly promptKey: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const SCENARIO_DEFAULTS = {
|
|
72
|
+
pageSize: 10,
|
|
73
|
+
maxPromptLength: 500,
|
|
74
|
+
minPromptLength: 10,
|
|
75
|
+
} as const;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenarios Feature
|
|
3
|
+
* Config-driven scenario selection and preview screens
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Domain types
|
|
7
|
+
export type {
|
|
8
|
+
ScenarioData,
|
|
9
|
+
ScenarioCategory,
|
|
10
|
+
ScenarioSelectorConfig,
|
|
11
|
+
ScenarioPreviewConfig,
|
|
12
|
+
MagicPromptConfig,
|
|
13
|
+
VisualStyleOption,
|
|
14
|
+
InspirationChipData,
|
|
15
|
+
} from "./domain/types";
|
|
16
|
+
export { ScenarioCategory as ScenarioCategoryEnum, SCENARIO_DEFAULTS } from "./domain/types";
|
|
17
|
+
|
|
18
|
+
// Components
|
|
19
|
+
export {
|
|
20
|
+
ScenarioHeader,
|
|
21
|
+
ScenarioGrid,
|
|
22
|
+
MagicPromptHeadline,
|
|
23
|
+
InspirationChips,
|
|
24
|
+
StyleSelector,
|
|
25
|
+
} from "./presentation/components";
|
|
26
|
+
export type {
|
|
27
|
+
ScenarioHeaderProps,
|
|
28
|
+
ScenarioGridProps,
|
|
29
|
+
MagicPromptHeadlineProps,
|
|
30
|
+
InspirationChipsProps,
|
|
31
|
+
StyleSelectorProps,
|
|
32
|
+
} from "./presentation/components";
|
|
33
|
+
|
|
34
|
+
// Screens
|
|
35
|
+
export { ScenarioSelectorScreen } from "./presentation/screens/ScenarioSelectorScreen";
|
|
36
|
+
export type { ScenarioSelectorScreenProps } from "./presentation/screens/ScenarioSelectorScreen";
|
|
37
|
+
|
|
38
|
+
export { ScenarioPreviewScreen } from "./presentation/screens/ScenarioPreviewScreen";
|
|
39
|
+
export type {
|
|
40
|
+
ScenarioPreviewScreenProps,
|
|
41
|
+
ScenarioPreviewTranslations,
|
|
42
|
+
} from "./presentation/screens/ScenarioPreviewScreen";
|
|
43
|
+
|
|
44
|
+
export { MagicPromptScreen } from "./presentation/screens/MagicPromptScreen";
|
|
45
|
+
export type { MagicPromptScreenProps } from "./presentation/screens/MagicPromptScreen";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InspirationChips Component
|
|
3
|
+
* Horizontal scrollable suggestion chips for Magic Prompt
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useMemo } from "react";
|
|
7
|
+
import { View, StyleSheet, ScrollView, TouchableOpacity } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
useAppDesignTokens,
|
|
11
|
+
} from "@umituz/react-native-design-system";
|
|
12
|
+
import type { InspirationChipData } from "../../domain/types";
|
|
13
|
+
|
|
14
|
+
export interface InspirationChipsProps {
|
|
15
|
+
readonly chips: readonly InspirationChipData[];
|
|
16
|
+
readonly title: string;
|
|
17
|
+
readonly onSelect: (promptKey: string) => void;
|
|
18
|
+
readonly t: (key: string) => string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const InspirationChips: React.FC<InspirationChipsProps> = ({
|
|
22
|
+
chips,
|
|
23
|
+
title,
|
|
24
|
+
onSelect,
|
|
25
|
+
t,
|
|
26
|
+
}) => {
|
|
27
|
+
const tokens = useAppDesignTokens();
|
|
28
|
+
|
|
29
|
+
const styles = useMemo(
|
|
30
|
+
() =>
|
|
31
|
+
StyleSheet.create({
|
|
32
|
+
container: {
|
|
33
|
+
marginBottom: tokens.spacing.lg,
|
|
34
|
+
},
|
|
35
|
+
sectionTitle: {
|
|
36
|
+
fontWeight: "700",
|
|
37
|
+
marginBottom: tokens.spacing.sm,
|
|
38
|
+
},
|
|
39
|
+
chipsContainer: {
|
|
40
|
+
gap: tokens.spacing.sm,
|
|
41
|
+
paddingBottom: 4,
|
|
42
|
+
},
|
|
43
|
+
chip: {
|
|
44
|
+
paddingHorizontal: tokens.spacing.md,
|
|
45
|
+
paddingVertical: 10,
|
|
46
|
+
borderRadius: 999,
|
|
47
|
+
borderWidth: 1,
|
|
48
|
+
borderColor: tokens.colors.border,
|
|
49
|
+
backgroundColor: tokens.colors.surface,
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
[tokens],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<View style={styles.container}>
|
|
57
|
+
<AtomicText type="labelLarge" style={styles.sectionTitle}>
|
|
58
|
+
{title}
|
|
59
|
+
</AtomicText>
|
|
60
|
+
<ScrollView
|
|
61
|
+
horizontal
|
|
62
|
+
showsHorizontalScrollIndicator={false}
|
|
63
|
+
contentContainerStyle={styles.chipsContainer}
|
|
64
|
+
>
|
|
65
|
+
{chips.map((chip) => (
|
|
66
|
+
<TouchableOpacity
|
|
67
|
+
key={chip.id}
|
|
68
|
+
style={styles.chip}
|
|
69
|
+
onPress={() => onSelect(chip.promptKey)}
|
|
70
|
+
>
|
|
71
|
+
<AtomicText
|
|
72
|
+
type="bodySmall"
|
|
73
|
+
style={{ color: tokens.colors.textPrimary }}
|
|
74
|
+
>
|
|
75
|
+
{t(chip.labelKey)}
|
|
76
|
+
</AtomicText>
|
|
77
|
+
</TouchableOpacity>
|
|
78
|
+
))}
|
|
79
|
+
</ScrollView>
|
|
80
|
+
</View>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MagicPromptHeadline Component
|
|
3
|
+
* Headline section with highlighted text for Magic Prompt screen
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, StyleSheet } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
useAppDesignTokens,
|
|
11
|
+
} from "@umituz/react-native-design-system";
|
|
12
|
+
|
|
13
|
+
export interface MagicPromptHeadlineProps {
|
|
14
|
+
readonly headlinePart1: string;
|
|
15
|
+
readonly headlinePart2: string;
|
|
16
|
+
readonly subtitle: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const MagicPromptHeadline: React.FC<MagicPromptHeadlineProps> = ({
|
|
20
|
+
headlinePart1,
|
|
21
|
+
headlinePart2,
|
|
22
|
+
subtitle,
|
|
23
|
+
}) => {
|
|
24
|
+
const tokens = useAppDesignTokens();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<View style={styles.container}>
|
|
28
|
+
<View style={styles.titleRow}>
|
|
29
|
+
<AtomicText
|
|
30
|
+
type="headlineLarge"
|
|
31
|
+
style={[styles.title, { color: tokens.colors.textPrimary }]}
|
|
32
|
+
>
|
|
33
|
+
{headlinePart1}{" "}
|
|
34
|
+
<AtomicText
|
|
35
|
+
type="headlineLarge"
|
|
36
|
+
style={[
|
|
37
|
+
styles.titleHighlight,
|
|
38
|
+
{ color: tokens.colors.textPrimary },
|
|
39
|
+
]}
|
|
40
|
+
>
|
|
41
|
+
{headlinePart2}
|
|
42
|
+
</AtomicText>
|
|
43
|
+
</AtomicText>
|
|
44
|
+
</View>
|
|
45
|
+
<AtomicText
|
|
46
|
+
type="bodyLarge"
|
|
47
|
+
style={[styles.subtitle, { color: tokens.colors.textSecondary }]}
|
|
48
|
+
>
|
|
49
|
+
{subtitle}
|
|
50
|
+
</AtomicText>
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const styles = StyleSheet.create({
|
|
56
|
+
container: {
|
|
57
|
+
marginVertical: 24,
|
|
58
|
+
},
|
|
59
|
+
titleRow: {
|
|
60
|
+
marginBottom: 12,
|
|
61
|
+
},
|
|
62
|
+
title: {
|
|
63
|
+
fontWeight: "800",
|
|
64
|
+
fontSize: 32,
|
|
65
|
+
lineHeight: 40,
|
|
66
|
+
},
|
|
67
|
+
titleHighlight: {
|
|
68
|
+
fontWeight: "800",
|
|
69
|
+
fontSize: 32,
|
|
70
|
+
lineHeight: 40,
|
|
71
|
+
textDecorationLine: "underline",
|
|
72
|
+
textDecorationColor: "rgba(255, 140, 90, 0.5)",
|
|
73
|
+
textDecorationStyle: "solid",
|
|
74
|
+
},
|
|
75
|
+
subtitle: {
|
|
76
|
+
fontSize: 16,
|
|
77
|
+
lineHeight: 24,
|
|
78
|
+
},
|
|
79
|
+
});
|