@umituz/react-native-ai-generation-content 1.37.9 → 1.37.10
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/creations/presentation/screens/CreationsGalleryScreen.tsx +5 -22
- package/src/domains/creations/presentation/screens/creations-gallery.styles.ts +11 -0
- package/src/domains/creations/presentation/screens/creations-gallery.types.ts +18 -0
- package/src/domains/generation/infrastructure/flow/scenario-configs.ts +69 -0
- package/src/domains/generation/infrastructure/flow/{step-builder.ts → step-builders.ts} +3 -86
- package/src/domains/generation/wizard/configs/image-to-video.config.ts +1 -1
- package/src/domains/generation/wizard/configs/text-to-image.config.ts +2 -1
- package/src/domains/generation/wizard/configs/text-to-video.config.ts +1 -1
- package/src/domains/generation/wizard/domain/entities/wizard-feature.types.ts +118 -0
- package/src/domains/generation/wizard/domain/entities/wizard-step.types.ts +96 -0
- package/src/domains/generation/wizard/index.ts +7 -3
- package/src/domains/generation/wizard/infrastructure/builders/dynamic-step-builder.ts +2 -5
- package/src/domains/generation/wizard/infrastructure/utils/index.ts +4 -1
- package/src/domains/generation/wizard/infrastructure/utils/primitive-extractors.ts +119 -0
- package/src/domains/generation/wizard/infrastructure/utils/wizard-field-extractors.ts +65 -0
- package/src/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +1 -1
- package/src/domains/generation/wizard/presentation/components/WizardFlowContent.tsx +1 -1
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.utils.ts +1 -1
- package/src/domains/generation/wizard/presentation/hooks/useWizardGeneration.ts +17 -50
- package/src/domains/generation/wizard/presentation/hooks/wizard-generation.types.ts +39 -0
- package/src/domains/scenarios/configs/wizard-config-resolver.ts +1 -1
- package/src/domains/scenarios/configs/wizard-input.types.ts +1 -1
- package/src/domains/scenarios/configs/wizard-step-factories.ts +1 -1
- package/src/features/image-to-video/presentation/hooks/image-to-video-feature.types.ts +59 -0
- package/src/features/image-to-video/presentation/hooks/useImageToVideoFeature.ts +15 -55
- package/src/domains/generation/wizard/domain/entities/wizard-config.types.ts +0 -230
- package/src/domains/generation/wizard/infrastructure/utils/wizard-data-extractors.ts +0 -232
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wizard Configuration Types
|
|
3
|
-
* Generic, feature-agnostic wizard configuration system
|
|
4
|
-
*
|
|
5
|
-
* All features use these same types - NO feature-specific wizards!
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { StepType } from "../../../../../domain/entities/flow-config.types";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Generic Step Configuration
|
|
12
|
-
* Base interface for all step configs
|
|
13
|
-
*/
|
|
14
|
-
export interface BaseStepConfig {
|
|
15
|
-
readonly id: string;
|
|
16
|
-
readonly type: StepType | string;
|
|
17
|
-
readonly enabled?: boolean;
|
|
18
|
-
readonly required?: boolean;
|
|
19
|
-
readonly titleKey?: string;
|
|
20
|
-
readonly subtitleKey?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Photo Upload Step Configuration
|
|
25
|
-
* Used by ANY feature that needs photo uploads (couple, face-swap, image-to-video, etc.)
|
|
26
|
-
*/
|
|
27
|
-
export interface PhotoUploadStepConfig extends BaseStepConfig {
|
|
28
|
-
readonly type: "photo_upload";
|
|
29
|
-
readonly label?: string; // "Photo 1", "Photo 2", "Your Photo", etc.
|
|
30
|
-
readonly showFaceDetection?: boolean;
|
|
31
|
-
readonly showNameInput?: boolean;
|
|
32
|
-
readonly showPhotoTips?: boolean;
|
|
33
|
-
readonly maxFileSizeMB?: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Text Input Step Configuration
|
|
38
|
-
* Used by text-to-video, prompt-based features, etc.
|
|
39
|
-
*/
|
|
40
|
-
export interface TextInputStepConfig extends BaseStepConfig {
|
|
41
|
-
readonly type: "text_input";
|
|
42
|
-
readonly placeholderKey?: string;
|
|
43
|
-
readonly minLength?: number;
|
|
44
|
-
readonly maxLength?: number;
|
|
45
|
-
readonly multiline?: boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Selection Step Configuration
|
|
50
|
-
* Generic selection (style, duration, aspect ratio, etc.)
|
|
51
|
-
*/
|
|
52
|
-
export interface SelectionStepConfig extends BaseStepConfig {
|
|
53
|
-
readonly type: "selection";
|
|
54
|
-
readonly selectionType: "style" | "duration" | "aspect_ratio" | "quality" | "resolution" | "custom";
|
|
55
|
-
readonly options: readonly {
|
|
56
|
-
readonly id: string;
|
|
57
|
-
readonly label: string;
|
|
58
|
-
readonly icon?: string;
|
|
59
|
-
readonly value: unknown;
|
|
60
|
-
}[];
|
|
61
|
-
readonly multiSelect?: boolean;
|
|
62
|
-
readonly defaultValue?: string | string[];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Preview Step Configuration
|
|
67
|
-
* Shows preview before generation (scenario preview, settings review, etc.)
|
|
68
|
-
*/
|
|
69
|
-
export interface PreviewStepConfig extends BaseStepConfig {
|
|
70
|
-
readonly type: "preview";
|
|
71
|
-
readonly previewType: "scenario" | "settings" | "custom";
|
|
72
|
-
readonly showContinueButton?: boolean;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Auth Gate Step Configuration
|
|
77
|
-
* Blocks flow if user is not authenticated
|
|
78
|
-
*/
|
|
79
|
-
export interface AuthGateStepConfig extends BaseStepConfig {
|
|
80
|
-
readonly type: "auth_gate";
|
|
81
|
-
readonly allowAnonymous?: boolean;
|
|
82
|
-
readonly messageKey?: string;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Credit Gate Step Configuration
|
|
87
|
-
* Blocks flow if user doesn't have enough credits
|
|
88
|
-
*/
|
|
89
|
-
export interface CreditGateStepConfig extends BaseStepConfig {
|
|
90
|
-
readonly type: "credit_gate";
|
|
91
|
-
readonly requiredCredits: number;
|
|
92
|
-
readonly messageKey?: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Union of all step config types
|
|
97
|
-
*/
|
|
98
|
-
export type WizardStepConfig =
|
|
99
|
-
| AuthGateStepConfig
|
|
100
|
-
| CreditGateStepConfig
|
|
101
|
-
| PhotoUploadStepConfig
|
|
102
|
-
| TextInputStepConfig
|
|
103
|
-
| SelectionStepConfig
|
|
104
|
-
| PreviewStepConfig
|
|
105
|
-
| BaseStepConfig;
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Feature Flow Configuration
|
|
109
|
-
* Each feature (image-to-video, face-swap, couple-future, etc.) provides this
|
|
110
|
-
*/
|
|
111
|
-
export interface WizardFeatureConfig {
|
|
112
|
-
readonly id: string;
|
|
113
|
-
readonly name: string;
|
|
114
|
-
readonly steps: readonly WizardStepConfig[];
|
|
115
|
-
readonly translations?: {
|
|
116
|
-
readonly [key: string]: string | Record<string, string>;
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Scenario-based Configuration Builder
|
|
122
|
-
* Shorthand for common patterns (couple scenarios, single photo, etc.)
|
|
123
|
-
*/
|
|
124
|
-
export interface ScenarioBasedConfig {
|
|
125
|
-
readonly photoCount?: number; // 0, 1, 2, etc.
|
|
126
|
-
readonly photoLabels?: readonly string[];
|
|
127
|
-
readonly requireText?: boolean;
|
|
128
|
-
readonly requireStyleSelection?: boolean;
|
|
129
|
-
readonly requireDurationSelection?: boolean;
|
|
130
|
-
readonly customSteps?: readonly WizardStepConfig[];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Build wizard config from scenario shorthand
|
|
135
|
-
*/
|
|
136
|
-
export const buildWizardConfigFromScenario = (
|
|
137
|
-
scenarioId: string,
|
|
138
|
-
config: ScenarioBasedConfig,
|
|
139
|
-
): WizardFeatureConfig => {
|
|
140
|
-
const steps: WizardStepConfig[] = [];
|
|
141
|
-
|
|
142
|
-
// Add photo upload steps (max 2 photos)
|
|
143
|
-
if (config.photoCount && config.photoCount > 0) {
|
|
144
|
-
const maxPhotos = Math.min(config.photoCount, 2); // Max 2 photos
|
|
145
|
-
for (let i = 0; i < maxPhotos; i++) {
|
|
146
|
-
steps.push({
|
|
147
|
-
id: `photo_${i + 1}`,
|
|
148
|
-
type: "photo_upload",
|
|
149
|
-
label: config.photoLabels?.[i] || `Photo ${i + 1}`,
|
|
150
|
-
showFaceDetection: true,
|
|
151
|
-
showPhotoTips: true,
|
|
152
|
-
required: true,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Add text input
|
|
158
|
-
if (config.requireText) {
|
|
159
|
-
steps.push({
|
|
160
|
-
id: "text_input",
|
|
161
|
-
type: "text_input",
|
|
162
|
-
required: true,
|
|
163
|
-
minLength: 10,
|
|
164
|
-
maxLength: 500,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Add style selection
|
|
169
|
-
if (config.requireStyleSelection) {
|
|
170
|
-
steps.push({
|
|
171
|
-
id: "style_selection",
|
|
172
|
-
type: "selection",
|
|
173
|
-
selectionType: "style",
|
|
174
|
-
options: [], // Provided by feature
|
|
175
|
-
required: true,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Add duration selection
|
|
180
|
-
if (config.requireDurationSelection) {
|
|
181
|
-
steps.push({
|
|
182
|
-
id: "duration_selection",
|
|
183
|
-
type: "selection",
|
|
184
|
-
selectionType: "duration",
|
|
185
|
-
options: [
|
|
186
|
-
{ id: "5s", label: "5 seconds", value: 5 },
|
|
187
|
-
{ id: "10s", label: "10 seconds", value: 10 },
|
|
188
|
-
{ id: "15s", label: "15 seconds", value: 15 },
|
|
189
|
-
],
|
|
190
|
-
required: true,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Add custom steps
|
|
195
|
-
if (config.customSteps) {
|
|
196
|
-
steps.push(...config.customSteps);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return {
|
|
200
|
-
id: scenarioId,
|
|
201
|
-
name: scenarioId,
|
|
202
|
-
steps,
|
|
203
|
-
};
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Pre-configured scenarios for common use cases
|
|
208
|
-
* Labels should be provided from main app via photoLabels parameter
|
|
209
|
-
*/
|
|
210
|
-
export const WIZARD_PRESETS = {
|
|
211
|
-
// Two photo features (couple, comparison, etc.)
|
|
212
|
-
TWO_PHOTOS: {
|
|
213
|
-
photoCount: 2,
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
// Single photo features
|
|
217
|
-
SINGLE_PHOTO: {
|
|
218
|
-
photoCount: 1,
|
|
219
|
-
requireStyleSelection: true,
|
|
220
|
-
requireDurationSelection: true,
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
// Text-only features
|
|
224
|
-
TEXT_ONLY: {
|
|
225
|
-
photoCount: 0,
|
|
226
|
-
requireText: true,
|
|
227
|
-
requireStyleSelection: true,
|
|
228
|
-
requireDurationSelection: true,
|
|
229
|
-
},
|
|
230
|
-
} as const;
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wizard Data Extractors
|
|
3
|
-
* Type-safe utilities for extracting values from wizard data
|
|
4
|
-
*
|
|
5
|
-
* Pattern: Type Guards + Normalizers
|
|
6
|
-
* @see https://www.typescriptlang.org/docs/handbook/2/narrowing.html
|
|
7
|
-
* @see https://betterstack.com/community/guides/scaling-nodejs/typescript-type-guards/
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// Type Guards
|
|
12
|
-
// ============================================================================
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Check if value is a non-null object
|
|
16
|
-
*/
|
|
17
|
-
function isObject(value: unknown): value is Record<string, unknown> {
|
|
18
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Check if object has a specific property
|
|
23
|
-
* Uses 'in' operator for safe property checking
|
|
24
|
-
*/
|
|
25
|
-
function hasProperty<K extends string>(
|
|
26
|
-
obj: Record<string, unknown>,
|
|
27
|
-
key: K,
|
|
28
|
-
): obj is Record<K, unknown> {
|
|
29
|
-
return key in obj;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ============================================================================
|
|
33
|
-
// String Extractors
|
|
34
|
-
// ============================================================================
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Extracts string from various input formats:
|
|
38
|
-
* - Direct string: "my text"
|
|
39
|
-
* - Object with text field: { text: "my text" }
|
|
40
|
-
* - Object with uri field (fallback): { uri: "my text" }
|
|
41
|
-
*
|
|
42
|
-
* @param value - The value to extract string from
|
|
43
|
-
* @returns The extracted string or undefined
|
|
44
|
-
*/
|
|
45
|
-
export function extractString(value: unknown): string | undefined {
|
|
46
|
-
// Direct string
|
|
47
|
-
if (typeof value === "string") {
|
|
48
|
-
return value;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Object with text or uri field
|
|
52
|
-
if (isObject(value)) {
|
|
53
|
-
if (hasProperty(value, "text") && typeof value.text === "string") {
|
|
54
|
-
return value.text;
|
|
55
|
-
}
|
|
56
|
-
if (hasProperty(value, "uri") && typeof value.uri === "string") {
|
|
57
|
-
return value.uri;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Extracts and trims string, returning undefined if empty
|
|
66
|
-
*/
|
|
67
|
-
export function extractTrimmedString(value: unknown): string | undefined {
|
|
68
|
-
const str = extractString(value);
|
|
69
|
-
const trimmed = str?.trim();
|
|
70
|
-
return trimmed && trimmed.length > 0 ? trimmed : undefined;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ============================================================================
|
|
74
|
-
// Number Extractors
|
|
75
|
-
// ============================================================================
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Extracts number from various input formats:
|
|
79
|
-
* - Direct number: 5
|
|
80
|
-
* - Object with value field: { value: 5 }
|
|
81
|
-
* - Object with selection field: { selection: 5 }
|
|
82
|
-
*
|
|
83
|
-
* @param value - The value to extract number from
|
|
84
|
-
* @returns The extracted number or undefined
|
|
85
|
-
*/
|
|
86
|
-
export function extractNumber(value: unknown): number | undefined {
|
|
87
|
-
// Direct number
|
|
88
|
-
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
89
|
-
return value;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Object with value or selection field
|
|
93
|
-
if (isObject(value)) {
|
|
94
|
-
if (hasProperty(value, "value") && typeof value.value === "number") {
|
|
95
|
-
return value.value;
|
|
96
|
-
}
|
|
97
|
-
if (hasProperty(value, "selection") && typeof value.selection === "number") {
|
|
98
|
-
return value.selection;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return undefined;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ============================================================================
|
|
106
|
-
// Selection Extractors
|
|
107
|
-
// ============================================================================
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Extracts selection value (string or string array) from wizard data
|
|
111
|
-
*
|
|
112
|
-
* @param value - The value to extract selection from
|
|
113
|
-
* @returns The extracted selection or undefined
|
|
114
|
-
*/
|
|
115
|
-
export function extractSelection(value: unknown): string | string[] | undefined {
|
|
116
|
-
// Direct string
|
|
117
|
-
if (typeof value === "string") {
|
|
118
|
-
return value;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Direct string array
|
|
122
|
-
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
123
|
-
return value as string[];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Object with selection field
|
|
127
|
-
if (isObject(value)) {
|
|
128
|
-
if (hasProperty(value, "selection")) {
|
|
129
|
-
const selection = value.selection;
|
|
130
|
-
if (typeof selection === "string") {
|
|
131
|
-
return selection;
|
|
132
|
-
}
|
|
133
|
-
if (Array.isArray(selection) && selection.every((v) => typeof v === "string")) {
|
|
134
|
-
return selection as string[];
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return undefined;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ============================================================================
|
|
143
|
-
// Prompt Extractor (Specialized)
|
|
144
|
-
// ============================================================================
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Extracts prompt from wizard data with fallback chain
|
|
148
|
-
* Checks multiple keys in order: prompt, motion_prompt, text
|
|
149
|
-
*
|
|
150
|
-
* @param wizardData - The wizard data object
|
|
151
|
-
* @param fallback - Optional fallback value (e.g., scenario.aiPrompt)
|
|
152
|
-
* @returns The extracted and trimmed prompt or undefined
|
|
153
|
-
*/
|
|
154
|
-
export function extractPrompt(
|
|
155
|
-
wizardData: Record<string, unknown>,
|
|
156
|
-
fallback?: string,
|
|
157
|
-
): string | undefined {
|
|
158
|
-
// Priority chain for prompt keys
|
|
159
|
-
const promptKeys = ["prompt", "motion_prompt", "text", "userPrompt"];
|
|
160
|
-
|
|
161
|
-
for (const key of promptKeys) {
|
|
162
|
-
if (key in wizardData) {
|
|
163
|
-
const extracted = extractTrimmedString(wizardData[key]);
|
|
164
|
-
if (extracted) {
|
|
165
|
-
return extracted;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Use fallback if provided
|
|
171
|
-
return fallback?.trim() || undefined;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ============================================================================
|
|
175
|
-
// Duration Extractor (Specialized)
|
|
176
|
-
// ============================================================================
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Extracts duration from wizard data
|
|
180
|
-
* Handles both direct number and object with value field
|
|
181
|
-
*
|
|
182
|
-
* @param wizardData - The wizard data object
|
|
183
|
-
* @returns The extracted duration in seconds or undefined
|
|
184
|
-
*/
|
|
185
|
-
export function extractDuration(
|
|
186
|
-
wizardData: Record<string, unknown>,
|
|
187
|
-
): number | undefined {
|
|
188
|
-
const durationData = wizardData.duration;
|
|
189
|
-
|
|
190
|
-
const extracted = extractNumber(durationData);
|
|
191
|
-
if (extracted !== undefined && extracted > 0) {
|
|
192
|
-
return extracted;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// ============================================================================
|
|
199
|
-
// Aspect Ratio Extractor (Specialized)
|
|
200
|
-
// ============================================================================
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Extracts aspect ratio from wizard data
|
|
204
|
-
* Common values: "16:9", "9:16", "1:1", "4:3"
|
|
205
|
-
*
|
|
206
|
-
* @param wizardData - The wizard data object
|
|
207
|
-
* @returns The extracted aspect ratio or undefined
|
|
208
|
-
*/
|
|
209
|
-
export function extractAspectRatio(
|
|
210
|
-
wizardData: Record<string, unknown>,
|
|
211
|
-
): string | undefined {
|
|
212
|
-
const aspectRatioData = wizardData.aspect_ratio;
|
|
213
|
-
return extractTrimmedString(aspectRatioData);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ============================================================================
|
|
217
|
-
// Resolution Extractor (Specialized)
|
|
218
|
-
// ============================================================================
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Extracts resolution from wizard data
|
|
222
|
-
* Common values: "720p", "1080p", "default"
|
|
223
|
-
*
|
|
224
|
-
* @param wizardData - The wizard data object
|
|
225
|
-
* @returns The extracted resolution or undefined
|
|
226
|
-
*/
|
|
227
|
-
export function extractResolution(
|
|
228
|
-
wizardData: Record<string, unknown>,
|
|
229
|
-
): string | undefined {
|
|
230
|
-
const resolutionData = wizardData.resolution;
|
|
231
|
-
return extractTrimmedString(resolutionData);
|
|
232
|
-
}
|