visualfries 0.1.0 → 0.1.3
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/dist/SceneBuilder.svelte.d.ts +1627 -4795
- package/dist/animations/AnimationPresetsRegister.d.ts +49 -225
- package/dist/builders/_ComponentState.svelte.d.ts +110 -462
- package/dist/builders/html/processors/TextEffectsStyleProcessor.js +1 -1
- package/dist/components/hooks/SubtitlesHook.d.ts +25 -38
- package/dist/layers/Layer.svelte.d.ts +1627 -4795
- package/dist/managers/LayersManager.svelte.d.ts +1627 -4795
- package/dist/managers/StateManager.svelte.d.ts +1659 -4853
- package/dist/managers/SubtitlesManager.svelte.d.ts +76 -115
- package/dist/schemas/scene/animations.d.ts +1389 -89357
- package/dist/schemas/scene/animations.js +18 -17
- package/dist/schemas/scene/components.d.ts +23513 -0
- package/dist/schemas/scene/components.js +223 -145
- package/dist/schemas/scene/core.d.ts +21255 -0
- package/dist/schemas/scene/core.js +25 -23
- package/dist/schemas/scene/index.d.ts +5 -3
- package/dist/schemas/scene/index.js +1 -1
- package/dist/schemas/scene/properties.d.ts +456 -746
- package/dist/schemas/scene/properties.js +83 -70
- package/dist/schemas/scene/subtitles.d.ts +204 -1040
- package/dist/schemas/scene/subtitles.js +21 -12
- package/dist/schemas/scene/utils.d.ts +38 -0
- package/dist/schemas/scene/utils.js +137 -0
- package/package.json +9 -5
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { isValidColor } from './utils.js';
|
|
3
3
|
import { EffectBaseShape, EffectShape as BaseEffectShape, BlurEffectShape, ShadowShape as StructuredShadowShape, OutlineShape as StructuredOutlineShape, ColorTypeShape, GradientDefinitionShape } from './properties.js';
|
|
4
|
-
import {
|
|
4
|
+
import { AnimationReferenceShape } from './animations.js';
|
|
5
|
+
import { coerceValidNumber, coerceNumber, coercePositiveNumber, coerceNormalizedNumber, coerceNonNegativeNumber, coerceInteger } from './utils.js';
|
|
5
6
|
// Utility functions
|
|
6
7
|
const toFixed3 = (val) => parseFloat(val.toFixed(3));
|
|
7
8
|
const toFixed3Optional = (val) => {
|
|
@@ -19,14 +20,14 @@ export const StructuredFontSizeShape = z.union([
|
|
|
19
20
|
.transform((val) => ({ value: val, unit: 'px' })),
|
|
20
21
|
z.object({
|
|
21
22
|
value: z.number().positive(),
|
|
22
|
-
unit: z.enum(['px', 'em', 'rem', '%']).
|
|
23
|
+
unit: z.enum(['px', 'em', 'rem', '%']).prefault('px')
|
|
23
24
|
})
|
|
24
25
|
]);
|
|
25
26
|
export const StructuredEmSizeShape = z.union([
|
|
26
27
|
z.number().transform((val) => ({ value: val, unit: 'em' })),
|
|
27
28
|
z.object({
|
|
28
29
|
value: z.number(),
|
|
29
|
-
unit: z.enum(['px', 'em', 'rem', '%']).
|
|
30
|
+
unit: z.enum(['px', 'em', 'rem', '%']).prefault('em')
|
|
30
31
|
})
|
|
31
32
|
]);
|
|
32
33
|
const FontWeightShape = z.enum([
|
|
@@ -74,10 +75,10 @@ export const TextAppearanceShape = z.object({
|
|
|
74
75
|
color: ColorTypeShape.nullable().optional(),
|
|
75
76
|
backgroundColor: ColorTypeShape.nullable().optional(),
|
|
76
77
|
fontWeight: FontWeightShape.optional(),
|
|
77
|
-
scale:
|
|
78
|
-
backgroundPaddingX:
|
|
79
|
-
backgroundPaddingY:
|
|
80
|
-
backgroundBorderRadius:
|
|
78
|
+
scale: coercePositiveNumber().optional(),
|
|
79
|
+
backgroundPaddingX: coerceNonNegativeNumber().optional(),
|
|
80
|
+
backgroundPaddingY: coerceNonNegativeNumber().optional(),
|
|
81
|
+
backgroundBorderRadius: coerceNonNegativeNumber().optional()
|
|
81
82
|
})
|
|
82
83
|
.nullable()
|
|
83
84
|
.optional(),
|
|
@@ -87,20 +88,20 @@ export const TextAppearanceShape = z.object({
|
|
|
87
88
|
color: ColorTypeShape.nullable().optional(),
|
|
88
89
|
backgroundColor: ColorTypeShape.nullable().optional(),
|
|
89
90
|
fontWeight: FontWeightShape.optional(),
|
|
90
|
-
scale:
|
|
91
|
-
backgroundPaddingX:
|
|
92
|
-
backgroundPaddingY:
|
|
93
|
-
backgroundBorderRadius:
|
|
91
|
+
scale: coercePositiveNumber().optional(),
|
|
92
|
+
backgroundPaddingX: coerceNonNegativeNumber().optional(),
|
|
93
|
+
backgroundPaddingY: coerceNonNegativeNumber().optional(),
|
|
94
|
+
backgroundBorderRadius: coerceNonNegativeNumber().optional()
|
|
94
95
|
})
|
|
95
96
|
.nullable()
|
|
96
97
|
.optional(),
|
|
97
98
|
highlightColors: z.array(ColorTypeShape).nullable().optional()
|
|
98
99
|
});
|
|
99
100
|
const BgShape = z.object({
|
|
100
|
-
enabled: z.boolean().
|
|
101
|
+
enabled: z.boolean().prefault(false),
|
|
101
102
|
color: ColorTypeShape,
|
|
102
|
-
target: z.enum(['wrapper', 'element']).
|
|
103
|
-
radius: z.number().min(0).
|
|
103
|
+
target: z.enum(['wrapper', 'element']).prefault('wrapper').optional(),
|
|
104
|
+
radius: z.number().min(0).prefault(0).optional()
|
|
104
105
|
});
|
|
105
106
|
export const BackgroundShape = z.union([BgShape, ColorTypeShape]).transform((value) => {
|
|
106
107
|
if (typeof value === 'object' && value !== null && 'enabled' in value) {
|
|
@@ -117,31 +118,96 @@ export const BackgroundShape = z.union([BgShape, ColorTypeShape]).transform((val
|
|
|
117
118
|
* General appearance schema for all components
|
|
118
119
|
*/
|
|
119
120
|
export const AppearanceShape = z.object({
|
|
120
|
-
x:
|
|
121
|
-
y:
|
|
122
|
-
width:
|
|
123
|
-
height:
|
|
124
|
-
offsetX:
|
|
125
|
-
offsetY:
|
|
126
|
-
opacity:
|
|
127
|
-
rotation:
|
|
128
|
-
scaleX:
|
|
129
|
-
scaleY:
|
|
130
|
-
background: BackgroundShape.nullable().
|
|
121
|
+
x: coerceValidNumber(),
|
|
122
|
+
y: coerceValidNumber(),
|
|
123
|
+
width: coercePositiveNumber(),
|
|
124
|
+
height: coercePositiveNumber(),
|
|
125
|
+
offsetX: coerceValidNumber().optional(), // 0 = left, 132 = 132px from left
|
|
126
|
+
offsetY: coerceValidNumber().optional(), // 0 = top, 132 = 132px from top
|
|
127
|
+
opacity: coerceNormalizedNumber().prefault(1).optional(),
|
|
128
|
+
rotation: coerceValidNumber().prefault(0).optional(),
|
|
129
|
+
scaleX: coerceValidNumber().prefault(1).optional(),
|
|
130
|
+
scaleY: coerceValidNumber().prefault(1).optional(),
|
|
131
|
+
background: BackgroundShape.nullable().prefault(null).optional(), // ColorTypeShape
|
|
131
132
|
// Text-specific appearance properties
|
|
132
133
|
text: TextAppearanceShape.optional(),
|
|
133
134
|
// Optional alignment properties
|
|
134
135
|
verticalAlign: z.enum(['top', 'center', 'bottom']).optional(),
|
|
135
136
|
horizontalAlign: z.enum(['left', 'center', 'right']).optional(),
|
|
136
137
|
// not optimal but to prevent linter errors. This is for subtitles only
|
|
137
|
-
backgroundAlwaysVisible: z.boolean().
|
|
138
|
+
backgroundAlwaysVisible: z.boolean().prefault(false).optional()
|
|
139
|
+
});
|
|
140
|
+
/**
|
|
141
|
+
* Specialized appearance shapes for different component types
|
|
142
|
+
* These are NOT exported to reduce .d.ts file size - they're only used internally
|
|
143
|
+
*/
|
|
144
|
+
/**
|
|
145
|
+
* Text-focused appearance shape for TEXT components
|
|
146
|
+
*/
|
|
147
|
+
const TextFocusedAppearanceShape = AppearanceShape.extend({
|
|
148
|
+
text: TextAppearanceShape,
|
|
149
|
+
verticalAlign: z.enum(['top', 'center', 'bottom']).optional(),
|
|
150
|
+
horizontalAlign: z.enum(['left', 'center', 'right']).optional()
|
|
151
|
+
});
|
|
152
|
+
/**
|
|
153
|
+
* Shape appearance for SHAPE components
|
|
154
|
+
*/
|
|
155
|
+
const ShapeAppearanceShape = AppearanceShape.extend({
|
|
156
|
+
color: ColorTypeShape.optional() // fill color
|
|
157
|
+
});
|
|
158
|
+
/**
|
|
159
|
+
* Color appearance for COLOR components
|
|
160
|
+
*/
|
|
161
|
+
const ColorAppearanceShape = AppearanceShape.extend({
|
|
162
|
+
background: z.string().refine(isValidColor, {
|
|
163
|
+
error: 'Invalid color format for ColorComponent background'
|
|
164
|
+
})
|
|
165
|
+
});
|
|
166
|
+
/**
|
|
167
|
+
* Gradient appearance for GRADIENT components
|
|
168
|
+
*/
|
|
169
|
+
const GradientAppearanceShape = AppearanceShape.extend({
|
|
170
|
+
background: GradientDefinitionShape // Requires a gradient type in background
|
|
171
|
+
});
|
|
172
|
+
/**
|
|
173
|
+
* AI Emoji shape for subtitle components
|
|
174
|
+
*/
|
|
175
|
+
export const AIEmojiShape = z.object({
|
|
176
|
+
text: z.string(),
|
|
177
|
+
emoji: z.string(),
|
|
178
|
+
startAt: coerceValidNumber(),
|
|
179
|
+
endAt: coerceValidNumber(),
|
|
180
|
+
componentId: z.string().optional()
|
|
181
|
+
}).refine((data) => data.startAt <= data.endAt, {
|
|
182
|
+
error: 'endAt must be greater than or equal to startAt',
|
|
183
|
+
path: ['endAt']
|
|
184
|
+
});
|
|
185
|
+
/**
|
|
186
|
+
* Subtitle appearance for SUBTITLES components
|
|
187
|
+
*/
|
|
188
|
+
const SubtitleAppearanceShape = AppearanceShape.extend({
|
|
189
|
+
text: TextAppearanceShape,
|
|
190
|
+
verticalAlign: z.enum(['top', 'center', 'bottom']).optional(),
|
|
191
|
+
horizontalAlign: z.enum(['left', 'center', 'right']).optional(),
|
|
192
|
+
hasAIEmojis: z.boolean().prefault(false).optional(),
|
|
193
|
+
aiEmojisPlacement: z.enum(['top', 'bottom']).prefault('top').optional(),
|
|
194
|
+
aiEmojisPlacementOffset: coerceValidNumber().prefault(30).optional(),
|
|
195
|
+
aiEmojis: z.array(AIEmojiShape).optional(),
|
|
196
|
+
highlighterColor1: ColorTypeShape.optional(),
|
|
197
|
+
highlighterColor2: ColorTypeShape.optional(),
|
|
198
|
+
highlighterColor3: ColorTypeShape.optional()
|
|
138
199
|
});
|
|
139
200
|
/**
|
|
140
201
|
* Timeline structure for components
|
|
141
202
|
*/
|
|
142
|
-
export const ComponentTimelineShape = z
|
|
143
|
-
|
|
144
|
-
|
|
203
|
+
export const ComponentTimelineShape = z
|
|
204
|
+
.object({
|
|
205
|
+
startAt: coerceNonNegativeNumber().transform(toFixed3),
|
|
206
|
+
endAt: coerceNonNegativeNumber().transform(toFixed3)
|
|
207
|
+
})
|
|
208
|
+
.refine((t) => t.startAt <= t.endAt, {
|
|
209
|
+
message: 'timeline endAt must be ≥ startAt',
|
|
210
|
+
path: ['endAt']
|
|
145
211
|
});
|
|
146
212
|
/**
|
|
147
213
|
* Animation structure
|
|
@@ -149,35 +215,50 @@ export const ComponentTimelineShape = z.object({
|
|
|
149
215
|
export const AnimationShape = z.object({
|
|
150
216
|
id: z.string(),
|
|
151
217
|
name: z.string(),
|
|
152
|
-
startAt:
|
|
218
|
+
startAt: coerceNonNegativeNumber().optional(),
|
|
153
219
|
animation: AnimationReferenceShape,
|
|
154
|
-
enabled: z.boolean().
|
|
220
|
+
enabled: z.boolean().prefault(true).optional()
|
|
155
221
|
});
|
|
156
222
|
/**
|
|
157
223
|
* Source metadata for media components
|
|
158
224
|
*/
|
|
159
225
|
export const SourceMetadataShape = z.object({
|
|
160
|
-
width:
|
|
161
|
-
height:
|
|
162
|
-
duration:
|
|
226
|
+
width: coercePositiveNumber().optional(),
|
|
227
|
+
height: coercePositiveNumber().optional(),
|
|
228
|
+
duration: coerceNonNegativeNumber().optional(),
|
|
163
229
|
format: z.string().optional(),
|
|
164
230
|
codec: z.string().optional(),
|
|
165
|
-
bitrate:
|
|
166
|
-
fps:
|
|
231
|
+
bitrate: coercePositiveNumber().optional(),
|
|
232
|
+
fps: coercePositiveNumber().optional(),
|
|
167
233
|
hasAudio: z.boolean().optional()
|
|
168
234
|
});
|
|
169
235
|
/**
|
|
170
236
|
* Component source definition
|
|
171
237
|
*/
|
|
172
|
-
export const ComponentSourceShape = z
|
|
173
|
-
|
|
174
|
-
|
|
238
|
+
export const ComponentSourceShape = z
|
|
239
|
+
.object({
|
|
240
|
+
url: z.url().optional(), // might have assetId. However should be required for video and other components
|
|
241
|
+
streamUrl: z.url().optional(),
|
|
175
242
|
assetId: z.string().optional(),
|
|
176
243
|
languageCode: z.string().optional(),
|
|
177
|
-
startAt:
|
|
178
|
-
endAt:
|
|
244
|
+
startAt: coerceNonNegativeNumber().transform(toFixed3Optional).optional(),
|
|
245
|
+
endAt: coerceNonNegativeNumber().transform(toFixed3Optional).optional(),
|
|
179
246
|
metadata: SourceMetadataShape.optional(),
|
|
180
247
|
transcriptFormat: z.string().optional()
|
|
248
|
+
})
|
|
249
|
+
.refine((data) => {
|
|
250
|
+
// Only validate if both startAt and endAt are present and not null
|
|
251
|
+
if (data.startAt !== undefined &&
|
|
252
|
+
data.startAt !== null &&
|
|
253
|
+
data.endAt !== undefined &&
|
|
254
|
+
data.endAt !== null) {
|
|
255
|
+
return data.startAt <= data.endAt;
|
|
256
|
+
}
|
|
257
|
+
// If either is undefined or null, validation passes
|
|
258
|
+
return true;
|
|
259
|
+
}, {
|
|
260
|
+
message: 'endAt must be greater than or equal to startAt',
|
|
261
|
+
path: ['endAt'] // Error targets the endAt field
|
|
181
262
|
});
|
|
182
263
|
/**
|
|
183
264
|
* Timing anchor for components that need synchronization (like subtitles)
|
|
@@ -187,32 +268,32 @@ export const TimingAnchorShape = z.object({
|
|
|
187
268
|
assetId: z.string().optional(),
|
|
188
269
|
layerId: z.string().optional(),
|
|
189
270
|
componentId: z.string().optional(),
|
|
190
|
-
offset:
|
|
271
|
+
offset: coerceValidNumber().prefault(0)
|
|
191
272
|
});
|
|
192
273
|
/**
|
|
193
274
|
* Layout split effect schema
|
|
194
275
|
*/
|
|
195
276
|
export const LayoutSplitEffectShape = EffectBaseShape.extend({
|
|
196
277
|
type: z.literal('layoutSplit'),
|
|
197
|
-
pieces:
|
|
198
|
-
sceneWidth:
|
|
199
|
-
sceneHeight:
|
|
200
|
-
chunks: z.array(z.record(z.any())).optional()
|
|
278
|
+
pieces: coerceInteger(1).optional(),
|
|
279
|
+
sceneWidth: coercePositiveNumber().optional(),
|
|
280
|
+
sceneHeight: coercePositiveNumber().optional(),
|
|
281
|
+
chunks: z.array(z.record(z.string(), z.any())).optional()
|
|
201
282
|
});
|
|
202
283
|
/**
|
|
203
284
|
* Rotation randomizer effect schema
|
|
204
285
|
*/
|
|
205
286
|
export const RotationRandomizerEffectShape = EffectBaseShape.extend({
|
|
206
287
|
type: z.literal('rotationRandomizer'),
|
|
207
|
-
maxRotation:
|
|
208
|
-
animate: z.boolean().
|
|
209
|
-
seed:
|
|
288
|
+
maxRotation: coerceValidNumber().prefault(2),
|
|
289
|
+
animate: z.boolean().prefault(true),
|
|
290
|
+
seed: coerceInteger().optional()
|
|
210
291
|
});
|
|
211
292
|
export const FillBackgroundBlurEffectShape = z.object({
|
|
212
293
|
type: z.literal('fillBackgroundBlur'),
|
|
213
|
-
enabled: z.boolean().
|
|
294
|
+
enabled: z.boolean().prefault(true),
|
|
214
295
|
// Using the hardcoded value from PixiSplitScreenDisplayObjectHook.ts as default
|
|
215
|
-
blurAmount:
|
|
296
|
+
blurAmount: coerceNonNegativeNumber().prefault(50)
|
|
216
297
|
// Note: intensity and blendMode might not be applicable here, keeping it simple.
|
|
217
298
|
});
|
|
218
299
|
/**
|
|
@@ -244,7 +325,7 @@ export const ComponentEffectShape = z.union([
|
|
|
244
325
|
TextOutlineEffectShape
|
|
245
326
|
]);
|
|
246
327
|
const ComponentEffectsShape = z.object({
|
|
247
|
-
enabled: z.boolean().optional().
|
|
328
|
+
enabled: z.boolean().optional().prefault(true), // Globally enable/disable all effects?
|
|
248
329
|
map: z
|
|
249
330
|
.union([
|
|
250
331
|
z.record(z.string().min(1), // Key: Effect name/ID (e.g., "mainBlur", "rotator")
|
|
@@ -260,17 +341,18 @@ const ComponentEffectsShape = z.object({
|
|
|
260
341
|
// If it's already an object, return as is
|
|
261
342
|
return val;
|
|
262
343
|
})
|
|
263
|
-
.
|
|
344
|
+
.prefault({}) // Default to an empty object if no effects
|
|
264
345
|
});
|
|
265
346
|
const ComponentAnimationsShape = z.object({
|
|
266
|
-
enabled: z.boolean().optional().
|
|
267
|
-
list: z.array(AnimationShape).
|
|
268
|
-
subtitlesSeed: z.
|
|
347
|
+
enabled: z.boolean().optional().prefault(true), // Globally enable/disable all animations?
|
|
348
|
+
list: z.array(AnimationShape).prefault([]),
|
|
349
|
+
subtitlesSeed: z.int().optional()
|
|
269
350
|
});
|
|
270
351
|
/**
|
|
271
|
-
* Base component schema
|
|
352
|
+
* Base component schema without appearance (to reduce type duplication)
|
|
353
|
+
* Each component type will add its own specialized appearance
|
|
272
354
|
*/
|
|
273
|
-
|
|
355
|
+
const ComponentBaseWithoutAppearanceShape = z.object({
|
|
274
356
|
id: z.string(),
|
|
275
357
|
name: z.string().optional(),
|
|
276
358
|
type: z.enum([
|
|
@@ -285,77 +367,96 @@ export const ComponentBaseShape = z.object({
|
|
|
285
367
|
'SUBTITLES'
|
|
286
368
|
]),
|
|
287
369
|
timeline: ComponentTimelineShape,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
order: z.number().default(0),
|
|
370
|
+
animations: ComponentAnimationsShape.prefault({}),
|
|
371
|
+
effects: ComponentEffectsShape.prefault({}),
|
|
372
|
+
visible: z.boolean().prefault(true),
|
|
373
|
+
order: coerceValidNumber().prefault(0),
|
|
293
374
|
checksum: z.string().optional()
|
|
294
375
|
});
|
|
376
|
+
/**
|
|
377
|
+
* Base component schema that all component types will extend
|
|
378
|
+
* @deprecated Use ComponentBaseWithoutAppearanceShape and add appearance per component
|
|
379
|
+
*/
|
|
380
|
+
export const ComponentBaseShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
381
|
+
appearance: AppearanceShape
|
|
382
|
+
});
|
|
295
383
|
/**
|
|
296
384
|
* Text component schema
|
|
297
385
|
*/
|
|
298
|
-
export const TextComponentShape =
|
|
386
|
+
export const TextComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
299
387
|
type: z.literal('TEXT'),
|
|
300
388
|
text: z.string(),
|
|
301
|
-
isAIEmoji: z.boolean().
|
|
302
|
-
appearance:
|
|
303
|
-
text: TextAppearanceShape,
|
|
304
|
-
verticalAlign: z.enum(['top', 'center', 'bottom']).optional(),
|
|
305
|
-
horizontalAlign: z.enum(['left', 'center', 'right']).optional()
|
|
306
|
-
})
|
|
389
|
+
isAIEmoji: z.boolean().prefault(false).optional(),
|
|
390
|
+
appearance: TextFocusedAppearanceShape
|
|
307
391
|
}).strict();
|
|
308
392
|
/**
|
|
309
393
|
* Image component schema
|
|
310
394
|
*/
|
|
311
|
-
export const ImageComponentShape =
|
|
395
|
+
export const ImageComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
312
396
|
type: z.literal('IMAGE'),
|
|
313
397
|
source: ComponentSourceShape,
|
|
398
|
+
appearance: AppearanceShape,
|
|
314
399
|
crop: z
|
|
315
400
|
.object({
|
|
316
|
-
xPercent:
|
|
317
|
-
yPercent:
|
|
318
|
-
widthPercent:
|
|
319
|
-
heightPercent:
|
|
401
|
+
xPercent: coerceNormalizedNumber().prefault(0),
|
|
402
|
+
yPercent: coerceNormalizedNumber().prefault(0),
|
|
403
|
+
widthPercent: coerceNormalizedNumber().prefault(1),
|
|
404
|
+
heightPercent: coerceNormalizedNumber().prefault(1)
|
|
320
405
|
})
|
|
321
406
|
.optional()
|
|
322
407
|
}).strict();
|
|
323
408
|
/**
|
|
324
409
|
* GIF component schema
|
|
325
410
|
*/
|
|
326
|
-
export const GifComponentShape =
|
|
411
|
+
export const GifComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
327
412
|
type: z.literal('GIF'),
|
|
328
413
|
source: ComponentSourceShape,
|
|
414
|
+
appearance: AppearanceShape,
|
|
329
415
|
playback: z
|
|
330
416
|
.object({
|
|
331
|
-
loop: z.boolean().
|
|
332
|
-
speed:
|
|
417
|
+
loop: z.boolean().prefault(true),
|
|
418
|
+
speed: coercePositiveNumber().prefault(1)
|
|
333
419
|
})
|
|
334
420
|
.optional()
|
|
335
421
|
}).strict();
|
|
336
422
|
/**
|
|
337
423
|
* Video component schema
|
|
338
424
|
*/
|
|
339
|
-
export const VideoComponentShape =
|
|
425
|
+
export const VideoComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
340
426
|
type: z.literal('VIDEO'),
|
|
341
427
|
source: ComponentSourceShape,
|
|
342
|
-
|
|
343
|
-
|
|
428
|
+
appearance: AppearanceShape,
|
|
429
|
+
volume: coerceNormalizedNumber().prefault(1),
|
|
430
|
+
muted: z.boolean().prefault(false),
|
|
344
431
|
playback: z
|
|
345
432
|
.object({
|
|
346
|
-
autoplay: z.boolean().
|
|
347
|
-
loop: z.boolean().
|
|
348
|
-
playbackRate:
|
|
349
|
-
startAt:
|
|
350
|
-
endAt:
|
|
433
|
+
autoplay: z.boolean().prefault(true),
|
|
434
|
+
loop: z.boolean().prefault(false),
|
|
435
|
+
playbackRate: coercePositiveNumber().prefault(1),
|
|
436
|
+
startAt: coerceNonNegativeNumber().prefault(0),
|
|
437
|
+
endAt: coerceValidNumber().optional()
|
|
438
|
+
})
|
|
439
|
+
.refine((data) => {
|
|
440
|
+
// Only validate if both startAt and endAt are present and not null
|
|
441
|
+
if (data.startAt !== undefined &&
|
|
442
|
+
data.startAt !== null &&
|
|
443
|
+
data.endAt !== undefined &&
|
|
444
|
+
data.endAt !== null) {
|
|
445
|
+
return data.startAt <= data.endAt;
|
|
446
|
+
}
|
|
447
|
+
// If either is undefined or null, validation passes
|
|
448
|
+
return true;
|
|
449
|
+
}, {
|
|
450
|
+
error: 'endAt must be greater than or equal to startAt',
|
|
451
|
+
path: ['endAt']
|
|
351
452
|
})
|
|
352
453
|
.optional(),
|
|
353
454
|
crop: z
|
|
354
455
|
.object({
|
|
355
|
-
x:
|
|
356
|
-
y:
|
|
357
|
-
width:
|
|
358
|
-
height:
|
|
456
|
+
x: coerceValidNumber().prefault(0),
|
|
457
|
+
y: coerceValidNumber().prefault(0),
|
|
458
|
+
width: coerceNormalizedNumber().prefault(1),
|
|
459
|
+
height: coerceNormalizedNumber().prefault(1)
|
|
359
460
|
})
|
|
360
461
|
.optional()
|
|
361
462
|
}).strict();
|
|
@@ -364,23 +465,25 @@ export const VideoComponentShape = ComponentBaseShape.extend({
|
|
|
364
465
|
*/
|
|
365
466
|
export const LinearProgressConfigShape = z.object({
|
|
366
467
|
type: z.literal('linear'),
|
|
367
|
-
direction: z.enum(['horizontal', 'vertical']).
|
|
368
|
-
reverse: z.boolean().
|
|
369
|
-
anchor: z.enum(['start', 'center', 'end']).
|
|
468
|
+
direction: z.enum(['horizontal', 'vertical']).prefault('horizontal'),
|
|
469
|
+
reverse: z.boolean().prefault(false).optional(),
|
|
470
|
+
anchor: z.enum(['start', 'center', 'end']).prefault('start').optional()
|
|
370
471
|
});
|
|
371
472
|
export const PerimeterProgressConfigShape = z.object({
|
|
372
473
|
type: z.literal('perimeter'),
|
|
373
|
-
startCorner: z
|
|
374
|
-
|
|
375
|
-
|
|
474
|
+
startCorner: z
|
|
475
|
+
.enum(['top-left', 'top-right', 'bottom-right', 'bottom-left'])
|
|
476
|
+
.prefault('top-left'),
|
|
477
|
+
clockwise: z.boolean().prefault(true).optional(),
|
|
478
|
+
strokeWidth: coercePositiveNumber().prefault(4).optional()
|
|
376
479
|
});
|
|
377
480
|
export const RadialProgressConfigShape = z.object({
|
|
378
481
|
type: z.literal('radial'),
|
|
379
|
-
startAngle:
|
|
380
|
-
clockwise: z.boolean().
|
|
381
|
-
innerRadius:
|
|
382
|
-
strokeWidth:
|
|
383
|
-
capStyle: z.enum(['butt', 'round', 'square']).
|
|
482
|
+
startAngle: coerceValidNumber().prefault(-90).optional(), // -90 = top (12 o'clock), 0 = right (3 o'clock)
|
|
483
|
+
clockwise: z.boolean().prefault(true).optional(),
|
|
484
|
+
innerRadius: coerceNormalizedNumber().prefault(0).optional(), // 0 = filled circle, >0 = ring/donut
|
|
485
|
+
strokeWidth: coercePositiveNumber().optional(), // For ring style
|
|
486
|
+
capStyle: z.enum(['butt', 'round', 'square']).prefault('round').optional()
|
|
384
487
|
});
|
|
385
488
|
export const DoubleProgressConfigShape = z.object({
|
|
386
489
|
type: z.literal('double'),
|
|
@@ -388,8 +491,8 @@ export const DoubleProgressConfigShape = z.object({
|
|
|
388
491
|
.array(z.object({
|
|
389
492
|
direction: z.enum(['horizontal', 'vertical']),
|
|
390
493
|
position: z.enum(['top', 'bottom', 'left', 'right']),
|
|
391
|
-
reverse: z.boolean().
|
|
392
|
-
offset:
|
|
494
|
+
reverse: z.boolean().prefault(false).optional(),
|
|
495
|
+
offset: coerceValidNumber().prefault(0).optional() // Offset from edge in pixels
|
|
393
496
|
}))
|
|
394
497
|
.min(2)
|
|
395
498
|
.max(4) // At least 2 paths, max 4 for performance
|
|
@@ -397,8 +500,8 @@ export const DoubleProgressConfigShape = z.object({
|
|
|
397
500
|
export const CustomProgressConfigShape = z.object({
|
|
398
501
|
type: z.literal('custom'),
|
|
399
502
|
pathData: z.string(), // SVG path data for custom progress shapes
|
|
400
|
-
strokeWidth:
|
|
401
|
-
capStyle: z.enum(['butt', 'round', 'square']).
|
|
503
|
+
strokeWidth: coercePositiveNumber().prefault(4).optional(),
|
|
504
|
+
capStyle: z.enum(['butt', 'round', 'square']).prefault('round').optional()
|
|
402
505
|
});
|
|
403
506
|
/**
|
|
404
507
|
* Union of all progress configuration types
|
|
@@ -413,13 +516,13 @@ export const ProgressConfigShape = z.discriminatedUnion('type', [
|
|
|
413
516
|
/**
|
|
414
517
|
* Shape component schema for basic geometric shapes
|
|
415
518
|
*/
|
|
416
|
-
export const ShapeComponentShape =
|
|
519
|
+
export const ShapeComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
417
520
|
type: z.literal('SHAPE'),
|
|
418
521
|
shape: z.union([
|
|
419
522
|
// Progress shape with specialized configuration
|
|
420
523
|
z.object({
|
|
421
524
|
type: z.literal('progress'),
|
|
422
|
-
progressConfig: ProgressConfigShape.optional().
|
|
525
|
+
progressConfig: ProgressConfigShape.optional().prefault({
|
|
423
526
|
type: 'linear',
|
|
424
527
|
direction: 'horizontal',
|
|
425
528
|
reverse: false,
|
|
@@ -435,69 +538,44 @@ export const ShapeComponentShape = ComponentBaseShape.extend({
|
|
|
435
538
|
cornerRadius: z.number().min(0).optional() // For rectangle
|
|
436
539
|
})
|
|
437
540
|
]),
|
|
438
|
-
appearance:
|
|
439
|
-
color: ColorTypeShape.optional() // fill color
|
|
440
|
-
})
|
|
541
|
+
appearance: ShapeAppearanceShape
|
|
441
542
|
}).strict();
|
|
442
543
|
/**
|
|
443
544
|
* Audio component schema
|
|
444
545
|
*/
|
|
445
|
-
export const AudioComponentShape =
|
|
546
|
+
export const AudioComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
446
547
|
type: z.literal('AUDIO'),
|
|
447
548
|
source: ComponentSourceShape,
|
|
448
|
-
|
|
449
|
-
|
|
549
|
+
appearance: AppearanceShape,
|
|
550
|
+
volume: coerceNormalizedNumber().prefault(1),
|
|
551
|
+
muted: z.boolean().prefault(false)
|
|
450
552
|
}).strict();
|
|
451
553
|
/**
|
|
452
554
|
* Color component schema
|
|
453
555
|
*/
|
|
454
|
-
export const ColorComponentShape =
|
|
556
|
+
export const ColorComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
455
557
|
type: z.literal('COLOR'),
|
|
456
|
-
appearance:
|
|
457
|
-
background: z
|
|
458
|
-
.string()
|
|
459
|
-
.refine(isValidColor, { message: 'Invalid color format for ColorComponent background' })
|
|
460
|
-
})
|
|
558
|
+
appearance: ColorAppearanceShape
|
|
461
559
|
}).strict();
|
|
462
560
|
/**
|
|
463
561
|
* Gradient component schema
|
|
464
562
|
*/
|
|
465
|
-
export const GradientComponentShape =
|
|
563
|
+
export const GradientComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
466
564
|
type: z.literal('GRADIENT'),
|
|
467
|
-
appearance:
|
|
468
|
-
background: GradientDefinitionShape // Requires a gradient type in background
|
|
469
|
-
})
|
|
565
|
+
appearance: GradientAppearanceShape
|
|
470
566
|
}).strict();
|
|
471
567
|
/**
|
|
472
568
|
* Subtitles component schema
|
|
473
569
|
*/
|
|
474
|
-
export const
|
|
475
|
-
text: z.string(),
|
|
476
|
-
emoji: z.string(),
|
|
477
|
-
startAt: z.number(),
|
|
478
|
-
endAt: z.number(),
|
|
479
|
-
componentId: z.string().optional()
|
|
480
|
-
});
|
|
481
|
-
export const SubtitleComponentShape = ComponentBaseShape.extend({
|
|
570
|
+
export const SubtitleComponentShape = ComponentBaseWithoutAppearanceShape.extend({
|
|
482
571
|
type: z.literal('SUBTITLES'),
|
|
483
|
-
source: ComponentSourceShape.
|
|
484
|
-
url: z.
|
|
572
|
+
source: ComponentSourceShape.safeExtend({
|
|
573
|
+
url: z.url().optional()
|
|
485
574
|
// Subtitles might need specific source fields, e.g., format
|
|
486
575
|
}).optional(),
|
|
487
576
|
timingAnchor: TimingAnchorShape,
|
|
488
577
|
text: z.string().optional(), // Optional: if text is directly embedded
|
|
489
|
-
appearance:
|
|
490
|
-
text: TextAppearanceShape,
|
|
491
|
-
verticalAlign: z.enum(['top', 'center', 'bottom']).optional(),
|
|
492
|
-
horizontalAlign: z.enum(['left', 'center', 'right']).optional(),
|
|
493
|
-
hasAIEmojis: z.boolean().default(false).optional(),
|
|
494
|
-
aiEmojisPlacement: z.enum(['top', 'bottom']).default('top').optional(),
|
|
495
|
-
aiEmojisPlacementOffset: z.number().default(30).optional(),
|
|
496
|
-
aiEmojis: z.array(AIEmojiShape).optional(),
|
|
497
|
-
highlighterColor1: ColorTypeShape.optional(),
|
|
498
|
-
highlighterColor2: ColorTypeShape.optional(),
|
|
499
|
-
highlighterColor3: ColorTypeShape.optional()
|
|
500
|
-
})
|
|
578
|
+
appearance: SubtitleAppearanceShape
|
|
501
579
|
}).strict();
|
|
502
580
|
/**
|
|
503
581
|
* Union of all component types for polymorphic handling
|