ugcinc-render 1.8.0 → 1.8.2

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.
@@ -0,0 +1,1065 @@
1
+ /**
2
+ * Base types used throughout the rendering system
3
+ */
4
+ /**
5
+ * Time value - can be absolute (milliseconds) or relative (0-1 percentage)
6
+ *
7
+ * @example
8
+ * // Absolute: 500 milliseconds
9
+ * { type: 'absolute', value: 500 }
10
+ *
11
+ * @example
12
+ * // Relative: 50% of total duration
13
+ * { type: 'relative', value: 0.5 }
14
+ */
15
+ interface TimeValue {
16
+ type: 'absolute' | 'relative';
17
+ value: number;
18
+ }
19
+ /**
20
+ * Border radius configuration for individual corners.
21
+ * All values in pixels, default 0 (no rounding).
22
+ */
23
+ interface BorderRadiusConfig {
24
+ /** Top-left corner radius in pixels */
25
+ topLeft?: number;
26
+ /** Top-right corner radius in pixels */
27
+ topRight?: number;
28
+ /** Bottom-right corner radius in pixels */
29
+ bottomRight?: number;
30
+ /** Bottom-left corner radius in pixels */
31
+ bottomLeft?: number;
32
+ }
33
+ /**
34
+ * Fit mode for media (images/videos)
35
+ * - cover: Fill the area, cropping if necessary
36
+ * - contain: Fit within the area, letterboxing if necessary
37
+ * - fill: Stretch to fill the area exactly
38
+ */
39
+ type FitMode = 'cover' | 'contain' | 'fill';
40
+ /**
41
+ * Segment type identifiers
42
+ */
43
+ type SegmentType = 'video' | 'image' | 'text' | 'audio' | 'editor';
44
+ /**
45
+ * Font type identifiers
46
+ */
47
+ type FontType = 'arial' | 'tiktok' | 'apple';
48
+ /**
49
+ * Font weight options
50
+ */
51
+ type FontWeight = 'normal' | 'bold';
52
+ /**
53
+ * Text alignment options
54
+ */
55
+ type TextAlignment = 'left' | 'center' | 'right' | 'justify';
56
+ /**
57
+ * Vertical alignment options
58
+ */
59
+ type VerticalAlignment = 'top' | 'middle' | 'bottom';
60
+ /**
61
+ * Text direction
62
+ */
63
+ type TextDirection = 'ltr' | 'rtl';
64
+ /**
65
+ * Text wrapping mode
66
+ */
67
+ type TextWrap = 'word' | 'char' | 'none';
68
+ /**
69
+ * Word break behavior
70
+ */
71
+ type WordBreak = 'normal' | 'break-all' | 'break-word';
72
+ /**
73
+ * Hyphenation mode
74
+ */
75
+ type Hyphenation = 'none' | 'auto';
76
+ /**
77
+ * Text overflow behavior
78
+ */
79
+ type TextOverflow = 'clip' | 'ellipsis';
80
+ /**
81
+ * Shared text styling properties used by both render and UI text segments.
82
+ * This ensures feature parity between image editor and video editor text elements.
83
+ */
84
+ interface TextStyleProperties {
85
+ /** Horizontal alignment (default: 'left') */
86
+ alignment?: TextAlignment;
87
+ /** Vertical alignment within bounds (default: 'top') */
88
+ verticalAlign?: VerticalAlignment;
89
+ /** Text direction (default: 'ltr') */
90
+ direction?: TextDirection;
91
+ /** Inner padding in pixels - uniform for all sides (default: 0) */
92
+ padding?: number;
93
+ /** Top padding in pixels */
94
+ paddingTop?: number;
95
+ /** Right padding in pixels */
96
+ paddingRight?: number;
97
+ /** Bottom padding in pixels */
98
+ paddingBottom?: number;
99
+ /** Left padding in pixels */
100
+ paddingLeft?: number;
101
+ /** Extra top padding added when text has 2+ lines */
102
+ multiLineExtraPaddingTop?: number;
103
+ /** Extra right padding added when text has 2+ lines */
104
+ multiLineExtraPaddingRight?: number;
105
+ /** Extra bottom padding added when text has 2+ lines */
106
+ multiLineExtraPaddingBottom?: number;
107
+ /** Extra left padding added when text has 2+ lines */
108
+ multiLineExtraPaddingLeft?: number;
109
+ /** Font family (default: 'arial') */
110
+ fontType?: FontType;
111
+ /** Font size in pixels (default: 40) */
112
+ fontSize?: number;
113
+ /** Font weight (default: 'normal') */
114
+ fontWeight?: FontWeight;
115
+ /** Line height multiplier (default: 1.2) */
116
+ lineHeight?: number;
117
+ /** Letter spacing in pixels (default: 0) */
118
+ letterSpacing?: number;
119
+ /** How text wraps to new lines (default: 'word') */
120
+ textWrap?: TextWrap;
121
+ /** How words break across lines (default: 'normal') */
122
+ wordBreak?: WordBreak;
123
+ /** Automatic hyphenation (default: 'none') */
124
+ hyphenation?: Hyphenation;
125
+ /** Maximum lines (0 = unlimited) (default: 0) */
126
+ maxLines?: number;
127
+ /** How overflow is handled (default: 'clip') */
128
+ textOverflow?: TextOverflow;
129
+ /** Ellipsis string for overflow (default: '...') */
130
+ ellipsis?: string;
131
+ /** Text color (default: '#000000') */
132
+ color?: string;
133
+ /** Background color in hex format #RRGGBB (default: transparent) */
134
+ backgroundColor?: string;
135
+ /** Background opacity 0-100 (default: 100) */
136
+ backgroundOpacity?: number;
137
+ /** Border radius for background box - individual corners (default: 0) */
138
+ backgroundBorderRadius?: BorderRadiusConfig;
139
+ /** Text outline color (default: none) */
140
+ strokeColor?: string;
141
+ /** Outline width in pixels (default: 0) */
142
+ strokeWidth?: number;
143
+ /** When true, box shrinks to fit content (width becomes max width) */
144
+ autoWidth?: boolean;
145
+ /** Which side the box anchors to when autoWidth is true (default: 'left') */
146
+ boxAlign?: 'left' | 'center' | 'right';
147
+ }
148
+
149
+ /**
150
+ * Position and anchor types for relative element positioning
151
+ */
152
+ /**
153
+ * Vertical anchor for Y positioning (parent element)
154
+ * Specifies which vertical edge of the reference element to anchor to
155
+ */
156
+ type VerticalAnchor = 'top' | 'bottom';
157
+ /**
158
+ * Horizontal anchor for X positioning (parent element)
159
+ * Specifies which horizontal edge of the reference element to anchor to
160
+ */
161
+ type HorizontalAnchor = 'left' | 'right';
162
+ /**
163
+ * Self anchor for X positioning
164
+ * Specifies which horizontal edge of THIS element to use for positioning
165
+ */
166
+ type HorizontalSelfAnchor = 'left' | 'center' | 'right';
167
+ /**
168
+ * Self anchor for Y positioning
169
+ * Specifies which vertical edge of THIS element to use for positioning
170
+ */
171
+ type VerticalSelfAnchor = 'top' | 'middle' | 'bottom';
172
+ /**
173
+ * Relative X position configuration
174
+ * When set, the segment's X position is calculated relative to another segment
175
+ *
176
+ * @example
177
+ * // Position this segment to the right of another segment with 10px gap
178
+ * { elementId: 'other-element', anchor: 'right', selfAnchor: 'left', offset: 10 }
179
+ */
180
+ interface RelativePositionConfigX {
181
+ /** ID of the segment to position relative to */
182
+ elementId: string;
183
+ /** Which horizontal edge of the reference segment to anchor to */
184
+ anchor: HorizontalAnchor;
185
+ /** Which horizontal edge of THIS segment to use (default: opposite of anchor) */
186
+ selfAnchor?: HorizontalSelfAnchor;
187
+ /** Offset in pixels from the anchor point */
188
+ offset: number;
189
+ }
190
+ /**
191
+ * Relative Y position configuration
192
+ * When set, the segment's Y position is calculated relative to another segment
193
+ *
194
+ * @example
195
+ * // Position this segment below another segment with a 10px gap
196
+ * { elementId: 'message-1', anchor: 'bottom', selfAnchor: 'top', offset: 10 }
197
+ */
198
+ interface RelativePositionConfigY {
199
+ /** ID of the segment to position relative to */
200
+ elementId: string;
201
+ /** Which vertical edge of the reference segment to anchor to */
202
+ anchor: VerticalAnchor;
203
+ /** Which vertical edge of THIS segment to use (default: opposite of anchor) */
204
+ selfAnchor?: VerticalSelfAnchor;
205
+ /** Offset in pixels from the anchor point */
206
+ offset: number;
207
+ }
208
+
209
+ /**
210
+ * Dynamic cropping configuration types
211
+ *
212
+ * Dynamic cropping allows the output dimensions to be calculated
213
+ * based on element positions at render time.
214
+ */
215
+ /**
216
+ * Crop boundary - can be static (element ID) or dynamic (input reference)
217
+ */
218
+ interface CropBoundary {
219
+ /** Static: reference a specific element by ID */
220
+ elementId?: string;
221
+ /** Variable: get element ID from input (creates an input port) */
222
+ inputRef?: string;
223
+ }
224
+ /**
225
+ * Configuration for cropping along one axis (vertical = height, horizontal = width)
226
+ */
227
+ interface CropAxisConfig {
228
+ /** Whether cropping is enabled for this axis */
229
+ enabled: boolean;
230
+ /**
231
+ * Crop mode:
232
+ * - 'all-elements': Crop to bounding box of all visible elements
233
+ * - 'between-elements': Crop between specific boundary elements
234
+ */
235
+ mode: 'all-elements' | 'between-elements';
236
+ /** Top/left element boundary (between-elements mode only) */
237
+ startBoundary?: CropBoundary;
238
+ /** Bottom/right element boundary (between-elements mode only) */
239
+ endBoundary?: CropBoundary;
240
+ /** Padding before (top/left) in pixels */
241
+ paddingStart?: number;
242
+ /** Padding after (bottom/right) in pixels */
243
+ paddingEnd?: number;
244
+ /** Minimum dimension (prevents cropping too small) */
245
+ minSize?: number;
246
+ }
247
+ /**
248
+ * Dynamic crop configuration for image/video editor
249
+ */
250
+ interface DynamicCropConfig {
251
+ /** Crop height based on elements */
252
+ vertical?: CropAxisConfig;
253
+ /** Crop width based on elements */
254
+ horizontal?: CropAxisConfig;
255
+ }
256
+ /**
257
+ * Result of crop bounds calculation
258
+ */
259
+ interface CropBounds {
260
+ /** Left edge of crop area */
261
+ x: number;
262
+ /** Top edge of crop area */
263
+ y: number;
264
+ /** Width of cropped output */
265
+ width: number;
266
+ /** Height of cropped output */
267
+ height: number;
268
+ }
269
+
270
+ /**
271
+ * Raw element types for editor input
272
+ *
273
+ * These types represent the raw configuration that users define in the editor.
274
+ * They include relative positioning which gets resolved to absolute positions
275
+ * before rendering.
276
+ */
277
+
278
+ /**
279
+ * Dimension preset keys for image editor
280
+ */
281
+ type DimensionPresetKey = 'tiktok' | 'instagram-square' | 'instagram-post' | 'youtube' | 'twitter' | 'custom';
282
+ /**
283
+ * Dimension preset configuration
284
+ */
285
+ interface DimensionPreset {
286
+ width: number;
287
+ height: number;
288
+ label: string;
289
+ ratio: string;
290
+ }
291
+ /**
292
+ * Standard dimension presets
293
+ */
294
+ declare const DIMENSION_PRESETS: Record<Exclude<DimensionPresetKey, 'custom'>, DimensionPreset>;
295
+ /**
296
+ * Image editor element - a raw image or text layer in the editor
297
+ *
298
+ * This is the input format that includes relative positioning.
299
+ * It gets resolved to absolute positions before being converted to segments.
300
+ */
301
+ interface ImageEditorElement {
302
+ /** Unique element identifier */
303
+ id: string;
304
+ /** Element type */
305
+ type: 'image' | 'text';
306
+ /** X position in pixels (may be overridden by relativePositionX) */
307
+ x: number;
308
+ /** Y position in pixels (may be overridden by relativePositionY) */
309
+ y: number;
310
+ /** Width in pixels */
311
+ width: number;
312
+ /** Height in pixels */
313
+ height: number;
314
+ /** Z-index for layering */
315
+ zIndex: number;
316
+ /** Rotation in degrees clockwise */
317
+ rotation?: number;
318
+ /** Whether the element is locked from editing */
319
+ locked?: boolean;
320
+ /** Relative X positioning config - when set, x is calculated from the reference element */
321
+ relativePositionX?: RelativePositionConfigX;
322
+ /** Relative Y positioning config - when set, y is calculated from the reference element */
323
+ relativePositionY?: RelativePositionConfigY;
324
+ /** Text content */
325
+ text?: string;
326
+ /** Maps to text input port for variable text (e.g., "text_1") */
327
+ textInputId?: string;
328
+ /** Text color (hex) */
329
+ color?: string;
330
+ /** Outline width in pixels (0 = no outline) */
331
+ outlineWidth?: number;
332
+ /** Outline color */
333
+ strokeColor?: string;
334
+ /** Font family */
335
+ font?: FontType;
336
+ /** Font size in pixels */
337
+ fontSize?: number;
338
+ /** Font weight */
339
+ fontWeight?: FontWeight;
340
+ /** Line height multiplier */
341
+ lineHeight?: number;
342
+ /** Letter spacing in pixels */
343
+ letterSpacing?: number;
344
+ /** Horizontal text alignment */
345
+ textAlign?: TextAlignment;
346
+ /** Vertical text alignment within bounds */
347
+ verticalAlign?: VerticalAlignment;
348
+ /** Background color for text box (hex) */
349
+ backgroundColor?: string;
350
+ /** Background opacity 0-100 */
351
+ backgroundOpacity?: number;
352
+ /** Corner radius for background box */
353
+ backgroundBorderRadius?: BorderRadiusConfig;
354
+ paddingTop?: number;
355
+ paddingRight?: number;
356
+ paddingBottom?: number;
357
+ paddingLeft?: number;
358
+ multiLineExtraPaddingTop?: number;
359
+ multiLineExtraPaddingRight?: number;
360
+ multiLineExtraPaddingBottom?: number;
361
+ multiLineExtraPaddingLeft?: number;
362
+ /** When true, box shrinks to fit content (width becomes max width) */
363
+ autoWidth?: boolean;
364
+ /** Which side the box anchors to when autoWidth is true */
365
+ boxAlign?: 'left' | 'center' | 'right';
366
+ /** Maps to input port (e.g., "image_1", "image_2") */
367
+ inputId?: string;
368
+ /** How the image fits its container */
369
+ fit?: FitMode;
370
+ /** Opacity percentage 0-100 */
371
+ opacity?: number;
372
+ /** Corner radius - single number for all corners, or object for individual */
373
+ borderRadius?: number | BorderRadiusConfig;
374
+ }
375
+ /**
376
+ * Image editor node configuration
377
+ *
378
+ * This is the complete configuration for an image editor node,
379
+ * including canvas dimensions, elements, and crop settings.
380
+ */
381
+ interface ImageEditorNodeConfig {
382
+ /** Canvas width in pixels */
383
+ width: number;
384
+ /** Canvas height in pixels */
385
+ height: number;
386
+ /** Aspect ratio string (e.g., "9:16") */
387
+ aspectRatio: string;
388
+ /** Dimension preset key */
389
+ dimensionPreset: DimensionPresetKey;
390
+ /** Elements to render */
391
+ elements: ImageEditorElement[];
392
+ /** Background type: 'image' for image input, 'color' for solid color */
393
+ backgroundType?: 'image' | 'color';
394
+ /** Background color (hex) when backgroundType is 'color' */
395
+ backgroundColor?: string;
396
+ /** How the background image fits the canvas */
397
+ backgroundFit?: FitMode;
398
+ /** Cached background image URL for consistent preview */
399
+ previewBackgroundUrl?: string;
400
+ /** Cached image URLs for image elements (keyed by inputId) */
401
+ previewImageUrls?: Record<string, string>;
402
+ /** Cached text values for text elements (keyed by textInputId) */
403
+ previewTextValues?: Record<string, string>;
404
+ /** Dynamic cropping configuration */
405
+ dynamicCrop?: DynamicCropConfig;
406
+ }
407
+
408
+ /**
409
+ * Video editor types for the WEBAPP UI
410
+ *
411
+ * These types include UI-only fields (marked with comments).
412
+ * The renderer uses types from editor.ts and segment.ts instead.
413
+ *
414
+ * UI-only fields are stripped before sending to renderer:
415
+ * - timeMode: UI helper for displaying timing mode
416
+ * - inputRef/textInputRef: Resolved to source/text before rendering
417
+ * - dimensionPreset: Renderer uses width/height directly
418
+ * - previewUrls/previewTextValues: Only for UI preview
419
+ */
420
+
421
+ /**
422
+ * UI helper for timing mode - not sent to renderer
423
+ */
424
+ type TimeMode = 'fixed' | 'flexible';
425
+ /**
426
+ * Base properties for all video editor segments (UI)
427
+ */
428
+ interface VideoEditorBaseSegment {
429
+ /** Unique segment identifier */
430
+ id: string;
431
+ /** Order in the channel (0-based) */
432
+ order: number;
433
+ /** Time offset from previous segment or absolute position */
434
+ offset: TimeValue;
435
+ /** Segment duration - required for sequential playback */
436
+ duration?: TimeValue;
437
+ /** Trim from start in milliseconds */
438
+ startTrim?: number;
439
+ /** Trim from end in milliseconds */
440
+ endTrim?: number;
441
+ /** UI-only: reference to input for dynamic start trim value - resolved to startTrim before rendering */
442
+ startTrimInputRef?: string;
443
+ /** UI-only: reference to input for dynamic end trim value - resolved to endTrim before rendering */
444
+ endTrimInputRef?: string;
445
+ /** UI-only: helper for timing mode display - stripped before rendering */
446
+ timeMode?: TimeMode;
447
+ /** Parent segment ID for overlays */
448
+ parentId?: string;
449
+ /** Relative start (0-1) within parent for overlays */
450
+ relativeStart?: number;
451
+ /** Relative end (0-1) within parent for overlays */
452
+ relativeEnd?: number;
453
+ /** Fade-in duration in milliseconds */
454
+ fadeIn?: number;
455
+ }
456
+ /**
457
+ * Base properties for visual segments
458
+ */
459
+ interface VideoEditorVisualSegment extends VideoEditorBaseSegment {
460
+ xOffset: number;
461
+ yOffset: number;
462
+ width: number;
463
+ height: number;
464
+ zIndex?: number;
465
+ scale?: number;
466
+ rotation?: number;
467
+ opacity?: number;
468
+ }
469
+ /**
470
+ * Video segment (UI)
471
+ */
472
+ interface VideoEditorVideoSegment extends VideoEditorVisualSegment {
473
+ type: 'video';
474
+ /** UI-only: reference to input node - resolved to source before rendering */
475
+ inputRef?: string;
476
+ /** Actual source URL (set after inputRef is resolved) */
477
+ source?: string;
478
+ fit?: FitMode;
479
+ speed?: number;
480
+ volume?: number;
481
+ borderRadius?: number | BorderRadiusConfig;
482
+ }
483
+ /**
484
+ * Audio segment (UI)
485
+ */
486
+ interface VideoEditorAudioSegment extends VideoEditorBaseSegment {
487
+ type: 'audio';
488
+ /** UI-only: reference to input node - resolved to source before rendering */
489
+ inputRef?: string;
490
+ /** Actual source URL (set after inputRef is resolved) */
491
+ source?: string;
492
+ volume?: number;
493
+ }
494
+ /**
495
+ * Image segment (UI)
496
+ */
497
+ interface VideoEditorImageSegment extends VideoEditorVisualSegment {
498
+ type: 'image';
499
+ /** UI-only: reference to input node - resolved to source before rendering */
500
+ inputRef?: string;
501
+ /** Actual source URL (set after inputRef is resolved) */
502
+ source?: string;
503
+ fit?: FitMode;
504
+ loop?: boolean;
505
+ borderRadius?: number | BorderRadiusConfig;
506
+ }
507
+ /**
508
+ * Text segment (UI)
509
+ *
510
+ * Extends TextStyleProperties which contains all shared text styling options.
511
+ * This ensures feature parity between image editor and video editor text elements.
512
+ */
513
+ interface VideoEditorTextSegment extends VideoEditorVisualSegment, TextStyleProperties {
514
+ type: 'text';
515
+ /** UI-only: reference to text input - resolved to text before rendering */
516
+ textInputRef?: string;
517
+ /** Actual text content (set after textInputRef is resolved) */
518
+ text?: string;
519
+ }
520
+ /**
521
+ * Union of all video editor segment types
522
+ */
523
+ type VideoEditorSegment = VideoEditorVideoSegment | VideoEditorAudioSegment | VideoEditorImageSegment | VideoEditorTextSegment;
524
+ /**
525
+ * Video editor channel
526
+ */
527
+ interface VideoEditorChannel {
528
+ id: string;
529
+ name: string;
530
+ segments: VideoEditorSegment[];
531
+ }
532
+ /**
533
+ * Video editor node configuration for WEBAPP UI
534
+ *
535
+ * Duration is NOT stored - it's calculated from segment durations.
536
+ * dimensionPreset, previewUrls, previewTextValues are UI-only.
537
+ */
538
+ interface VideoEditorNodeConfig {
539
+ /** Canvas width in pixels */
540
+ width: number;
541
+ /** Canvas height in pixels */
542
+ height: number;
543
+ /** Frames per second */
544
+ fps: number;
545
+ /** UI-only: dimension preset selector */
546
+ dimensionPreset: DimensionPresetKey;
547
+ /** Channels containing segments */
548
+ channels: VideoEditorChannel[];
549
+ /** UI-only: preview URLs for displaying in editor */
550
+ previewUrls?: Record<string, string>;
551
+ /** UI-only: preview text values for displaying in editor */
552
+ previewTextValues?: Record<string, string>;
553
+ }
554
+ /**
555
+ * Segment position on timeline (calculated from segment timing properties)
556
+ */
557
+ interface SegmentTimelinePosition {
558
+ /** Start time in milliseconds */
559
+ startMs: number;
560
+ /** Duration in milliseconds */
561
+ durationMs: number;
562
+ }
563
+
564
+ /**
565
+ * Caption types for auto-captioning functionality
566
+ *
567
+ * These types are used by both the CaptionOverlay component (client + server)
568
+ * and the AutoCaptionComposition for rendering captioned videos.
569
+ */
570
+ /**
571
+ * Caption word with timing information from transcription
572
+ */
573
+ interface CaptionWord {
574
+ /** The word text */
575
+ word: string;
576
+ /** Start time in milliseconds */
577
+ startMs: number;
578
+ /** End time in milliseconds */
579
+ endMs: number;
580
+ }
581
+ /**
582
+ * Caption page - a group of words displayed together
583
+ */
584
+ interface CaptionPage {
585
+ /** Words in this page */
586
+ words: CaptionWord[];
587
+ /** Start time of first word in ms */
588
+ startMs: number;
589
+ /** End time of last word in ms */
590
+ endMs: number;
591
+ }
592
+ /**
593
+ * Caption style preset names
594
+ */
595
+ type CaptionPreset = 'hormozi' | 'minimal' | 'bold-pop' | 'clean' | 'neon';
596
+ /**
597
+ * Caption vertical position on screen
598
+ */
599
+ type CaptionPosition = 'top' | 'center' | 'bottom';
600
+ /**
601
+ * Caption font weight options
602
+ */
603
+ type CaptionFontWeight = 'normal' | 'bold' | 'black';
604
+ /**
605
+ * Full caption style configuration
606
+ */
607
+ interface CaptionStyle {
608
+ /** Preset name (if used, provides defaults for other fields) */
609
+ preset?: CaptionPreset;
610
+ /** Google Font name (e.g., 'Montserrat', 'Inter') */
611
+ fontName: string;
612
+ /** Font size in pixels (relative to 1920p height) */
613
+ fontSize: number;
614
+ /** Font weight */
615
+ fontWeight: CaptionFontWeight;
616
+ /** Text color for inactive words */
617
+ fontColor: string;
618
+ /** Text color for the currently active/highlighted word */
619
+ highlightColor: string;
620
+ /** Stroke/outline color */
621
+ strokeColor: string;
622
+ /** Stroke width in pixels */
623
+ strokeWidth: number;
624
+ /** Background color (hex) - omit for transparent */
625
+ backgroundColor?: string;
626
+ /** Background opacity (0-1) */
627
+ backgroundOpacity?: number;
628
+ /** Background border radius in pixels */
629
+ backgroundBorderRadius?: number;
630
+ /** Vertical position on screen */
631
+ position: CaptionPosition;
632
+ /** Vertical offset in pixels from position edge */
633
+ yOffset: number;
634
+ /** Max width as percentage of video width (0-100) */
635
+ maxWidth: number;
636
+ /** Enable bounce/scale animation on active word */
637
+ enableAnimation: boolean;
638
+ /** Maximum words per caption page/screen */
639
+ wordsPerPage: number;
640
+ }
641
+ /**
642
+ * Props for AutoCaptionComposition
643
+ */
644
+ interface AutoCaptionCompositionProps {
645
+ /** Video source URL */
646
+ videoUrl: string;
647
+ /** Video width in pixels */
648
+ width: number;
649
+ /** Video height in pixels */
650
+ height: number;
651
+ /** Video duration in milliseconds */
652
+ durationMs: number;
653
+ /** Caption words with timing */
654
+ captions: CaptionWord[];
655
+ /** Caption styling configuration */
656
+ style: CaptionStyle;
657
+ }
658
+ /**
659
+ * Props for CaptionOverlay component
660
+ */
661
+ interface CaptionOverlayProps {
662
+ /** Caption words with timing */
663
+ captions: CaptionWord[];
664
+ /** Caption styling configuration */
665
+ style: CaptionStyle;
666
+ /** Override current time for preview mode (ms) */
667
+ previewTimeMs?: number;
668
+ }
669
+
670
+ /**
671
+ * Font utilities for the rendering system
672
+ */
673
+
674
+ /**
675
+ * Apple Color Emoji font name - used as fallback for emoji characters
676
+ */
677
+ declare const APPLE_EMOJI_FONT = "\"Apple Color Emoji\"";
678
+ /**
679
+ * Font family CSS strings for each font type
680
+ * These match the fonts registered in the rendering system
681
+ * Apple Color Emoji is added as a fallback for consistent emoji rendering
682
+ */
683
+ declare const FONT_FAMILIES: Record<FontType, string>;
684
+ /**
685
+ * Get the CSS font family string for a font type
686
+ */
687
+ declare function getFontFamily(fontType: FontType): string;
688
+ /**
689
+ * Build a CSS font string
690
+ */
691
+ declare function buildFontString({ fontType, fontSize, fontWeight, }: {
692
+ fontType: FontType;
693
+ fontSize: number;
694
+ fontWeight: 'normal' | 'bold';
695
+ }): string;
696
+ /**
697
+ * Font URLs for loading web fonts
698
+ * These are the public URLs to the font files
699
+ */
700
+ declare const FONT_URLS: {
701
+ tiktok: {
702
+ regular: string;
703
+ bold: string;
704
+ };
705
+ apple: {
706
+ regular: string;
707
+ };
708
+ emoji: {
709
+ apple: string;
710
+ };
711
+ };
712
+ /**
713
+ * Preload fonts for rendering
714
+ * Call this in your Root component or composition
715
+ */
716
+ declare function preloadFonts(): Promise<void>;
717
+ /**
718
+ * Check if fonts are loaded
719
+ */
720
+ declare function areFontsLoaded(): boolean;
721
+ /**
722
+ * Debug utility to log current font status
723
+ */
724
+ declare function debugFontStatus(): void;
725
+
726
+ /**
727
+ * Fit calculation utilities for media positioning
728
+ */
729
+
730
+ /**
731
+ * Result of fit dimension calculation
732
+ */
733
+ interface FitDimensions {
734
+ /** X offset within target area */
735
+ x: number;
736
+ /** Y offset within target area */
737
+ y: number;
738
+ /** Rendered width */
739
+ width: number;
740
+ /** Rendered height */
741
+ height: number;
742
+ /** Source X offset (for cropping) */
743
+ sourceX: number;
744
+ /** Source Y offset (for cropping) */
745
+ sourceY: number;
746
+ /** Source width to use */
747
+ sourceWidth: number;
748
+ /** Source height to use */
749
+ sourceHeight: number;
750
+ }
751
+ /**
752
+ * Calculate dimensions for fit modes (cover, contain, fill)
753
+ */
754
+ declare function calculateFitDimensions({ sourceWidth, sourceHeight, targetWidth, targetHeight, fit, }: {
755
+ sourceWidth: number;
756
+ sourceHeight: number;
757
+ targetWidth: number;
758
+ targetHeight: number;
759
+ fit: FitMode;
760
+ }): FitDimensions;
761
+
762
+ /**
763
+ * Default values for segment properties
764
+ *
765
+ * These defaults match the server-side rendering defaults exactly
766
+ * to ensure consistent behavior between client preview and server render.
767
+ */
768
+
769
+ /**
770
+ * Default values for text segments
771
+ */
772
+ declare const TEXT_DEFAULTS: {
773
+ fontType: FontType;
774
+ fontSize: number;
775
+ fontWeight: FontWeight;
776
+ color: string;
777
+ alignment: TextAlignment;
778
+ verticalAlign: VerticalAlignment;
779
+ direction: TextDirection;
780
+ lineHeight: number;
781
+ letterSpacing: number;
782
+ padding: number;
783
+ textWrap: TextWrap;
784
+ wordBreak: WordBreak;
785
+ hyphenation: Hyphenation;
786
+ maxLines: number;
787
+ textOverflow: TextOverflow;
788
+ ellipsis: string;
789
+ strokeWidth: number;
790
+ backgroundOpacity: number;
791
+ autoWidth: boolean;
792
+ boxAlign: "left";
793
+ };
794
+ /**
795
+ * Default values for image segments
796
+ */
797
+ declare const IMAGE_DEFAULTS: {
798
+ fit: FitMode;
799
+ opacity: number;
800
+ loop: boolean;
801
+ speed: number;
802
+ };
803
+ /**
804
+ * Default values for video segments
805
+ */
806
+ declare const VIDEO_DEFAULTS: {
807
+ volume: number;
808
+ fit: FitMode;
809
+ opacity: number;
810
+ loop: boolean;
811
+ speed: number;
812
+ };
813
+ /**
814
+ * Default values for visual segment positioning
815
+ */
816
+ declare const VISUAL_DEFAULTS: {
817
+ xOffset: number;
818
+ yOffset: number;
819
+ zIndex: number;
820
+ scale: number;
821
+ rotation: number;
822
+ opacity: number;
823
+ };
824
+ /**
825
+ * Apply default values to a text segment
826
+ */
827
+ declare function applyTextDefaults<T extends Record<string, unknown>>(segment: T): T & typeof TEXT_DEFAULTS;
828
+ /**
829
+ * Apply default values to an image segment
830
+ */
831
+ declare function applyImageDefaults<T extends Record<string, unknown>>(segment: T): T & typeof IMAGE_DEFAULTS;
832
+ /**
833
+ * Apply default values to a video segment
834
+ */
835
+ declare function applyVideoDefaults<T extends Record<string, unknown>>(segment: T): T & typeof VIDEO_DEFAULTS;
836
+
837
+ /**
838
+ * Text utilities for measuring and wrapping text
839
+ */
840
+
841
+ /**
842
+ * Wrap text to fit within a specific width
843
+ * Accounts for letter spacing when measuring text width
844
+ */
845
+ declare function wrapText({ text, maxWidth, letterSpacing, textWrap, maxLines, measureText, }: {
846
+ text: string;
847
+ maxWidth: number;
848
+ letterSpacing?: number;
849
+ textWrap?: 'word' | 'char' | 'none';
850
+ maxLines?: number;
851
+ measureText: (text: string) => number;
852
+ }): string[];
853
+ /**
854
+ * Calculate line width with letter spacing
855
+ */
856
+ declare function calculateLineWidth({ line, letterSpacing, measureText, }: {
857
+ line: string;
858
+ letterSpacing: number;
859
+ measureText: (text: string) => number;
860
+ }): number;
861
+ /**
862
+ * Convert borderRadius value to array format for roundRect()
863
+ * Returns [topLeft, topRight, bottomRight, bottomLeft] or null if no rounding needed
864
+ */
865
+ declare function getBorderRadii(borderRadius: number | BorderRadiusConfig | undefined): number[] | null;
866
+ /**
867
+ * Parse hex color to RGB values
868
+ */
869
+ declare function parseHexColor(hex: string): {
870
+ r: number;
871
+ g: number;
872
+ b: number;
873
+ };
874
+ /**
875
+ * Convert hex color and opacity to rgba string
876
+ */
877
+ declare function hexToRgba(hex: string, opacity?: number): string;
878
+
879
+ /**
880
+ * Position Resolution Utility
881
+ *
882
+ * Resolves relative element positions to absolute coordinates.
883
+ * X and Y positions can be independently set to relative or absolute.
884
+ * Handles dependency resolution, circular dependency detection, and cascading updates.
885
+ */
886
+
887
+ /**
888
+ * Calculate the actual dimensions of a text element when autoWidth is enabled
889
+ * Returns { actualWidth, actualX, actualHeight } where:
890
+ * - actualX is adjusted based on boxAlign
891
+ * - actualHeight is calculated based on the number of wrapped lines
892
+ */
893
+ declare function calculateAutoWidthDimensions(elem: ImageEditorElement, textContent: string, ctx?: CanvasRenderingContext2D | null): {
894
+ actualWidth: number;
895
+ actualX: number;
896
+ actualHeight: number;
897
+ lineCount: number;
898
+ } | null;
899
+ interface PositionResolutionError {
900
+ elementId: string;
901
+ type: 'circular_dependency' | 'missing_reference' | 'invalid_config';
902
+ message: string;
903
+ /** Which axis the error is for */
904
+ axis?: 'x' | 'y';
905
+ /** For circular dependencies, the cycle path */
906
+ cyclePath?: string[];
907
+ }
908
+ interface PositionResolutionResult {
909
+ /** Elements with resolved absolute positions */
910
+ elements: ImageEditorElement[];
911
+ /** Any errors encountered during resolution */
912
+ errors: PositionResolutionError[];
913
+ }
914
+ /**
915
+ * Resolve all relative positions to absolute coordinates
916
+ *
917
+ * X and Y are resolved independently, allowing:
918
+ * - X absolute, Y relative (common for vertical stacking)
919
+ * - X relative, Y absolute (common for horizontal stacking)
920
+ * - Both relative (can be to different elements)
921
+ * - Both absolute
922
+ *
923
+ * @param elements - Array of elements to resolve
924
+ * @param textValues - Optional map of textInputId -> text content for autoWidth calculation
925
+ */
926
+ declare function resolveElementPositions(elements: ImageEditorElement[], textValues?: Record<string, string>): PositionResolutionResult;
927
+ /**
928
+ * Check if an element can be set as a reference for another element on a specific axis
929
+ */
930
+ declare function canSetAsReference(elements: ImageEditorElement[], elementId: string, proposedReferenceId: string, axis: 'x' | 'y'): boolean;
931
+ /**
932
+ * Get all elements that would be affected if a given element's position/size changes
933
+ */
934
+ declare function getDependentElements(elements: ImageEditorElement[], elementId: string): string[];
935
+ /**
936
+ * Get the element that a given element references for X position (if any)
937
+ */
938
+ declare function getReferenceElementX(elements: ImageEditorElement[], elementId: string): ImageEditorElement | null;
939
+ /**
940
+ * Get the element that a given element references for Y position (if any)
941
+ */
942
+ declare function getReferenceElementY(elements: ImageEditorElement[], elementId: string): ImageEditorElement | null;
943
+
944
+ /**
945
+ * Utility functions for calculating dynamic crop bounds
946
+ */
947
+
948
+ /**
949
+ * Calculate dynamic crop bounds based on element positions
950
+ *
951
+ * @param elements - Array of resolved elements with absolute positions
952
+ * @param dynamicCrop - Crop configuration
953
+ * @param canvasWidth - Original canvas width
954
+ * @param canvasHeight - Original canvas height
955
+ * @param textValues - Optional map of textInputId -> text content for height calculation
956
+ * @returns CropBounds with x, y, width, height
957
+ */
958
+ declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop: DynamicCropConfig | undefined, canvasWidth: number, canvasHeight: number, textValues?: Record<string, string>): CropBounds;
959
+ /**
960
+ * Check if dynamic crop is enabled (either vertical or horizontal)
961
+ */
962
+ declare function isDynamicCropEnabled(dynamicCrop: DynamicCropConfig | undefined): boolean;
963
+
964
+ /**
965
+ * Timeline utility functions for video editor
966
+ *
967
+ * These functions calculate segment positions on the timeline based on
968
+ * the segment/overlay timing model. Used by both the webapp UI and
969
+ * the VideoEditorComposition for rendering.
970
+ */
971
+
972
+ /**
973
+ * Create a default TimeValue for segment offsets
974
+ */
975
+ declare function defaultOffset(mode?: TimeMode): TimeValue;
976
+ /**
977
+ * Get base segments (no parentId) for a channel
978
+ * Base segments are the primary timeline elements that overlays attach to
979
+ */
980
+ declare function getBaseSegments(channel: VideoEditorChannel): VideoEditorSegment[];
981
+ /**
982
+ * Get overlays for a specific parent segment (or global overlays if parentId is null)
983
+ */
984
+ declare function getOverlays(channel: VideoEditorChannel, parentId: string | null): VideoEditorSegment[];
985
+ /**
986
+ * Calculate segment position on timeline
987
+ *
988
+ * For base segments:
989
+ * - Position is calculated from offset and previous segments
990
+ * - Duration is from the duration property or default (5 seconds)
991
+ *
992
+ * For overlay segments:
993
+ * - Position is calculated relative to parent using relativeStart/relativeEnd
994
+ * - relativeStart/relativeEnd are fractions (0-1) of parent duration
995
+ */
996
+ declare function getSegmentTimelinePosition(segment: VideoEditorSegment, baseSegments: VideoEditorSegment[], channel: VideoEditorChannel): SegmentTimelinePosition;
997
+ /**
998
+ * Check if a segment is visible at a given time
999
+ */
1000
+ declare function isSegmentVisibleAtTime(segment: VideoEditorSegment, time: number, channel: VideoEditorChannel): boolean;
1001
+ /**
1002
+ * Calculate estimated total duration based on segments
1003
+ */
1004
+ declare function calculateEstimatedDuration(channels: VideoEditorChannel[]): number;
1005
+ /**
1006
+ * Calculate the timeline content end time (used for both ruler and scroll width)
1007
+ */
1008
+ declare function calculateTimelineContentEnd(channel: VideoEditorChannel): number;
1009
+ /**
1010
+ * Format time in mm:ss.ms
1011
+ */
1012
+ declare function formatTime(ms: number): string;
1013
+ /**
1014
+ * Parse time string to milliseconds
1015
+ */
1016
+ declare function parseTime(timeStr: string): number;
1017
+ /**
1018
+ * Generate a unique segment ID
1019
+ */
1020
+ declare function generateSegmentId(): string;
1021
+ /**
1022
+ * Generate a unique overlay ID
1023
+ */
1024
+ declare function generateOverlayId(): string;
1025
+
1026
+ /**
1027
+ * Caption style presets and resolution utilities
1028
+ *
1029
+ * Provides TikTok-style caption presets and a function to resolve
1030
+ * partial config into a complete CaptionStyle object.
1031
+ */
1032
+
1033
+ /**
1034
+ * Predefined caption style presets
1035
+ *
1036
+ * Font sizes are designed for 1920p height and scale proportionally.
1037
+ * maxWidth is a percentage of video width (0-100).
1038
+ */
1039
+ declare const CAPTION_PRESETS: Record<CaptionPreset, Omit<CaptionStyle, 'preset'>>;
1040
+ /**
1041
+ * Default caption style (used when no preset or custom values provided)
1042
+ */
1043
+ declare const DEFAULT_CAPTION_STYLE: Omit<CaptionStyle, "preset">;
1044
+ /**
1045
+ * Resolve a partial caption style config into a complete CaptionStyle object
1046
+ *
1047
+ * Priority:
1048
+ * 1. Explicit values in the input style
1049
+ * 2. Preset values (if preset is specified)
1050
+ * 3. Default values (hormozi preset)
1051
+ *
1052
+ * @param style - Partial caption style configuration
1053
+ * @returns Complete CaptionStyle object with all fields populated
1054
+ */
1055
+ declare function resolveCaptionStyle(style: Partial<CaptionStyle> | undefined): CaptionStyle;
1056
+ /**
1057
+ * Get all available preset names
1058
+ */
1059
+ declare function getCaptionPresetNames(): CaptionPreset[];
1060
+ /**
1061
+ * Check if a string is a valid caption preset name
1062
+ */
1063
+ declare function isValidCaptionPreset(name: string): name is CaptionPreset;
1064
+
1065
+ export { FONT_URLS as $, type AutoCaptionCompositionProps as A, type BorderRadiusConfig as B, type CaptionOverlayProps as C, type DynamicCropConfig as D, type SegmentTimelinePosition as E, type FitMode as F, type CropBoundary as G, type Hyphenation as H, type ImageEditorElement as I, type CropAxisConfig as J, type CropBounds as K, type CaptionWord as L, type CaptionPage as M, type CaptionPreset as N, type CaptionPosition as O, type PositionResolutionResult as P, type CaptionFontWeight as Q, type RelativePositionConfigX as R, type SegmentType as S, type TimeValue as T, type CaptionStyle as U, type VerticalAlignment as V, type WordBreak as W, FONT_FAMILIES as X, APPLE_EMOJI_FONT as Y, getFontFamily as Z, buildFontString as _, type RelativePositionConfigY as a, preloadFonts as a0, areFontsLoaded as a1, debugFontStatus as a2, calculateFitDimensions as a3, type FitDimensions as a4, TEXT_DEFAULTS as a5, IMAGE_DEFAULTS as a6, VIDEO_DEFAULTS as a7, VISUAL_DEFAULTS as a8, applyTextDefaults as a9, generateOverlayId as aA, CAPTION_PRESETS as aB, DEFAULT_CAPTION_STYLE as aC, resolveCaptionStyle as aD, getCaptionPresetNames as aE, isValidCaptionPreset as aF, applyImageDefaults as aa, applyVideoDefaults as ab, wrapText as ac, calculateLineWidth as ad, getBorderRadii as ae, parseHexColor as af, hexToRgba as ag, resolveElementPositions as ah, calculateAutoWidthDimensions as ai, canSetAsReference as aj, getDependentElements as ak, getReferenceElementX as al, getReferenceElementY as am, type PositionResolutionError as an, calculateCropBounds as ao, isDynamicCropEnabled as ap, defaultOffset as aq, getBaseSegments as ar, getOverlays as as, getSegmentTimelinePosition as at, isSegmentVisibleAtTime as au, calculateEstimatedDuration as av, calculateTimelineContentEnd as aw, formatTime as ax, parseTime as ay, generateSegmentId as az, type TextStyleProperties as b, type FontType as c, type FontWeight as d, type TextAlignment as e, type TextDirection as f, type TextWrap as g, type TextOverflow as h, type VerticalAnchor as i, type HorizontalAnchor as j, type HorizontalSelfAnchor as k, type VerticalSelfAnchor as l, type ImageEditorNodeConfig as m, type DimensionPresetKey as n, type DimensionPreset as o, DIMENSION_PRESETS as p, type TimeMode as q, type VideoEditorBaseSegment as r, type VideoEditorVisualSegment as s, type VideoEditorVideoSegment as t, type VideoEditorAudioSegment as u, type VideoEditorImageSegment as v, type VideoEditorTextSegment as w, type VideoEditorSegment as x, type VideoEditorChannel as y, type VideoEditorNodeConfig as z };