ugcinc-render 1.6.1 → 1.8.0

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/index.d.mts CHANGED
@@ -441,6 +441,10 @@ interface VideoEditorBaseSegment {
441
441
  startTrim?: number;
442
442
  /** Trim from end in milliseconds */
443
443
  endTrim?: number;
444
+ /** UI-only: reference to input for dynamic start trim value - resolved to startTrim before rendering */
445
+ startTrimInputRef?: string;
446
+ /** UI-only: reference to input for dynamic end trim value - resolved to endTrim before rendering */
447
+ endTrimInputRef?: string;
444
448
  /** UI-only: helper for timing mode display - stripped before rendering */
445
449
  timeMode?: TimeMode;
446
450
  /** Parent segment ID for overlays */
@@ -1067,6 +1071,112 @@ declare function isDeduplicationLevel(input: DeduplicationInput): input is Dedup
1067
1071
  */
1068
1072
  declare function resolveDeduplicationConfig(input: DeduplicationInput): DeduplicationConfig;
1069
1073
 
1074
+ /**
1075
+ * Caption types for auto-captioning functionality
1076
+ *
1077
+ * These types are used by both the CaptionOverlay component (client + server)
1078
+ * and the AutoCaptionComposition for rendering captioned videos.
1079
+ */
1080
+ /**
1081
+ * Caption word with timing information from transcription
1082
+ */
1083
+ interface CaptionWord {
1084
+ /** The word text */
1085
+ word: string;
1086
+ /** Start time in milliseconds */
1087
+ startMs: number;
1088
+ /** End time in milliseconds */
1089
+ endMs: number;
1090
+ }
1091
+ /**
1092
+ * Caption page - a group of words displayed together
1093
+ */
1094
+ interface CaptionPage {
1095
+ /** Words in this page */
1096
+ words: CaptionWord[];
1097
+ /** Start time of first word in ms */
1098
+ startMs: number;
1099
+ /** End time of last word in ms */
1100
+ endMs: number;
1101
+ }
1102
+ /**
1103
+ * Caption style preset names
1104
+ */
1105
+ type CaptionPreset = 'hormozi' | 'minimal' | 'bold-pop' | 'clean' | 'neon';
1106
+ /**
1107
+ * Caption vertical position on screen
1108
+ */
1109
+ type CaptionPosition = 'top' | 'center' | 'bottom';
1110
+ /**
1111
+ * Caption font weight options
1112
+ */
1113
+ type CaptionFontWeight = 'normal' | 'bold' | 'black';
1114
+ /**
1115
+ * Full caption style configuration
1116
+ */
1117
+ interface CaptionStyle {
1118
+ /** Preset name (if used, provides defaults for other fields) */
1119
+ preset?: CaptionPreset;
1120
+ /** Google Font name (e.g., 'Montserrat', 'Inter') */
1121
+ fontName: string;
1122
+ /** Font size in pixels (relative to 1920p height) */
1123
+ fontSize: number;
1124
+ /** Font weight */
1125
+ fontWeight: CaptionFontWeight;
1126
+ /** Text color for inactive words */
1127
+ fontColor: string;
1128
+ /** Text color for the currently active/highlighted word */
1129
+ highlightColor: string;
1130
+ /** Stroke/outline color */
1131
+ strokeColor: string;
1132
+ /** Stroke width in pixels */
1133
+ strokeWidth: number;
1134
+ /** Background color (hex) - omit for transparent */
1135
+ backgroundColor?: string;
1136
+ /** Background opacity (0-1) */
1137
+ backgroundOpacity?: number;
1138
+ /** Background border radius in pixels */
1139
+ backgroundBorderRadius?: number;
1140
+ /** Vertical position on screen */
1141
+ position: CaptionPosition;
1142
+ /** Vertical offset in pixels from position edge */
1143
+ yOffset: number;
1144
+ /** Max width as percentage of video width (0-100) */
1145
+ maxWidth: number;
1146
+ /** Enable bounce/scale animation on active word */
1147
+ enableAnimation: boolean;
1148
+ /** Maximum words per caption page/screen */
1149
+ wordsPerPage: number;
1150
+ }
1151
+ /**
1152
+ * Props for AutoCaptionComposition
1153
+ */
1154
+ interface AutoCaptionCompositionProps {
1155
+ /** Video source URL */
1156
+ videoUrl: string;
1157
+ /** Video width in pixels */
1158
+ width: number;
1159
+ /** Video height in pixels */
1160
+ height: number;
1161
+ /** Video duration in milliseconds */
1162
+ durationMs: number;
1163
+ /** Caption words with timing */
1164
+ captions: CaptionWord[];
1165
+ /** Caption styling configuration */
1166
+ style: CaptionStyle;
1167
+ }
1168
+ /**
1169
+ * Props for CaptionOverlay component
1170
+ */
1171
+ interface CaptionOverlayProps {
1172
+ /** Caption words with timing */
1173
+ captions: CaptionWord[];
1174
+ /** Caption styling configuration */
1175
+ style: CaptionStyle;
1176
+ /** Override current time for preview mode (ms) */
1177
+ previewTimeMs?: number;
1178
+ }
1179
+
1070
1180
  interface ImageEditorCompositionProps {
1071
1181
  /** The editor configuration to render (legacy format with pre-resolved segments) */
1072
1182
  config?: ImageEditorConfig;
@@ -1167,6 +1277,16 @@ interface VideoEditorCompositionProps {
1167
1277
  */
1168
1278
  declare function VideoEditorComposition({ config, sources, textContent, }: VideoEditorCompositionProps): react_jsx_runtime.JSX.Element;
1169
1279
 
1280
+ /**
1281
+ * AutoCaptionComposition renders a video with synchronized caption overlay
1282
+ */
1283
+ declare function AutoCaptionComposition({ videoUrl, captions, style, }: AutoCaptionCompositionProps): react_jsx_runtime.JSX.Element;
1284
+ /**
1285
+ * AutoCaptionComposition with standard Video component
1286
+ * Use this variant for client-side preview where OffthreadVideo may not work
1287
+ */
1288
+ declare function AutoCaptionCompositionWithVideo({ videoUrl, captions, style, }: AutoCaptionCompositionProps): react_jsx_runtime.JSX.Element;
1289
+
1170
1290
  interface TextElementProps {
1171
1291
  segment: TextSegment;
1172
1292
  /** Optional scale for high-DPI rendering */
@@ -1232,6 +1352,11 @@ interface VideoElementProps {
1232
1352
  */
1233
1353
  declare function VideoElement({ segment, src, startFrame, durationInFrames, scale }: VideoElementProps): react_jsx_runtime.JSX.Element;
1234
1354
 
1355
+ /**
1356
+ * CaptionOverlay renders animated captions synchronized with video playback
1357
+ */
1358
+ declare function CaptionOverlay({ captions, style, previewTimeMs }: CaptionOverlayProps): react_jsx_runtime.JSX.Element | null;
1359
+
1235
1360
  /**
1236
1361
  * Font utilities for the rendering system
1237
1362
  */
@@ -1588,6 +1713,45 @@ declare function generateSegmentId(): string;
1588
1713
  */
1589
1714
  declare function generateOverlayId(): string;
1590
1715
 
1716
+ /**
1717
+ * Caption style presets and resolution utilities
1718
+ *
1719
+ * Provides TikTok-style caption presets and a function to resolve
1720
+ * partial config into a complete CaptionStyle object.
1721
+ */
1722
+
1723
+ /**
1724
+ * Predefined caption style presets
1725
+ *
1726
+ * Font sizes are designed for 1920p height and scale proportionally.
1727
+ * maxWidth is a percentage of video width (0-100).
1728
+ */
1729
+ declare const CAPTION_PRESETS: Record<CaptionPreset, Omit<CaptionStyle, 'preset'>>;
1730
+ /**
1731
+ * Default caption style (used when no preset or custom values provided)
1732
+ */
1733
+ declare const DEFAULT_CAPTION_STYLE: Omit<CaptionStyle, "preset">;
1734
+ /**
1735
+ * Resolve a partial caption style config into a complete CaptionStyle object
1736
+ *
1737
+ * Priority:
1738
+ * 1. Explicit values in the input style
1739
+ * 2. Preset values (if preset is specified)
1740
+ * 3. Default values (hormozi preset)
1741
+ *
1742
+ * @param style - Partial caption style configuration
1743
+ * @returns Complete CaptionStyle object with all fields populated
1744
+ */
1745
+ declare function resolveCaptionStyle(style: Partial<CaptionStyle> | undefined): CaptionStyle;
1746
+ /**
1747
+ * Get all available preset names
1748
+ */
1749
+ declare function getCaptionPresetNames(): CaptionPreset[];
1750
+ /**
1751
+ * Check if a string is a valid caption preset name
1752
+ */
1753
+ declare function isValidCaptionPreset(name: string): name is CaptionPreset;
1754
+
1591
1755
  /**
1592
1756
  * Hook exports for ugcinc-render
1593
1757
  *
@@ -1650,4 +1814,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
1650
1814
 
1651
1815
  declare const RenderRoot: React.FC;
1652
1816
 
1653
- export { APPLE_EMOJI_FONT, type Acceleration, type AudioConfig, type AudioSegment, type BaseEditorConfig, type BaseSegment, type BlendMode, type BorderRadiusConfig, type Channel, type ColorFilterConfig, type ColorShiftConfig, type CropAxisConfig, type CropBoundary, type CropBounds, type CropConfig, type CuttingConfig, DEDUPLICATION_LEVELS, DIMENSION_PRESETS, type DeduplicationConfig, type DeduplicationInput, type DeduplicationLevel, type DiagonalFilterConfig, type DiagonalFilterType, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, type EnhanceLevel, type EnhancementConfig, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type FrameManipulationConfig, type GifOverlayConfig, type GifSource, type GradientOverlayConfig, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, LEVEL_1_CONFIG, LEVEL_2_CONFIG, LEVEL_3_CONFIG, LEVEL_4_CONFIG, LEVEL_5_CONFIG, type LensCorrectionConfig, type MobileEncodingConfig, type ParticleOverlayConfig, type PhoneConfig, type PhoneModel, type PhoneType, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type SpeedConfig, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextStyleProperties, type TextWrap, type TimeMode, type TimeValue, type TraceRemovalConfig, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoCodec, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoExtension, type VideoSegment, type VisualAdjustmentsConfig, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, debugFontStatus, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDeduplicationLevel, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveDeduplicationConfig, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
1817
+ export { APPLE_EMOJI_FONT, type Acceleration, type AudioConfig, type AudioSegment, AutoCaptionComposition, type AutoCaptionCompositionProps, AutoCaptionCompositionWithVideo, type BaseEditorConfig, type BaseSegment, type BlendMode, type BorderRadiusConfig, CAPTION_PRESETS, type CaptionFontWeight, CaptionOverlay, type CaptionOverlayProps, type CaptionPage, type CaptionPosition, type CaptionPreset, type CaptionStyle, type CaptionWord, type Channel, type ColorFilterConfig, type ColorShiftConfig, type CropAxisConfig, type CropBoundary, type CropBounds, type CropConfig, type CuttingConfig, DEDUPLICATION_LEVELS, DEFAULT_CAPTION_STYLE, DIMENSION_PRESETS, type DeduplicationConfig, type DeduplicationInput, type DeduplicationLevel, type DiagonalFilterConfig, type DiagonalFilterType, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, type EnhanceLevel, type EnhancementConfig, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type FrameManipulationConfig, type GifOverlayConfig, type GifSource, type GradientOverlayConfig, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, LEVEL_1_CONFIG, LEVEL_2_CONFIG, LEVEL_3_CONFIG, LEVEL_4_CONFIG, LEVEL_5_CONFIG, type LensCorrectionConfig, type MobileEncodingConfig, type ParticleOverlayConfig, type PhoneConfig, type PhoneModel, type PhoneType, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type SpeedConfig, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextStyleProperties, type TextWrap, type TimeMode, type TimeValue, type TraceRemovalConfig, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoCodec, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoExtension, type VideoSegment, type VisualAdjustmentsConfig, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, debugFontStatus, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getCaptionPresetNames, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDeduplicationLevel, isDynamicCropEnabled, isSegmentVisibleAtTime, isValidCaptionPreset, parseHexColor, parseTime, preloadFonts, resolveCaptionStyle, resolveDeduplicationConfig, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
package/dist/index.d.ts CHANGED
@@ -441,6 +441,10 @@ interface VideoEditorBaseSegment {
441
441
  startTrim?: number;
442
442
  /** Trim from end in milliseconds */
443
443
  endTrim?: number;
444
+ /** UI-only: reference to input for dynamic start trim value - resolved to startTrim before rendering */
445
+ startTrimInputRef?: string;
446
+ /** UI-only: reference to input for dynamic end trim value - resolved to endTrim before rendering */
447
+ endTrimInputRef?: string;
444
448
  /** UI-only: helper for timing mode display - stripped before rendering */
445
449
  timeMode?: TimeMode;
446
450
  /** Parent segment ID for overlays */
@@ -1067,6 +1071,112 @@ declare function isDeduplicationLevel(input: DeduplicationInput): input is Dedup
1067
1071
  */
1068
1072
  declare function resolveDeduplicationConfig(input: DeduplicationInput): DeduplicationConfig;
1069
1073
 
1074
+ /**
1075
+ * Caption types for auto-captioning functionality
1076
+ *
1077
+ * These types are used by both the CaptionOverlay component (client + server)
1078
+ * and the AutoCaptionComposition for rendering captioned videos.
1079
+ */
1080
+ /**
1081
+ * Caption word with timing information from transcription
1082
+ */
1083
+ interface CaptionWord {
1084
+ /** The word text */
1085
+ word: string;
1086
+ /** Start time in milliseconds */
1087
+ startMs: number;
1088
+ /** End time in milliseconds */
1089
+ endMs: number;
1090
+ }
1091
+ /**
1092
+ * Caption page - a group of words displayed together
1093
+ */
1094
+ interface CaptionPage {
1095
+ /** Words in this page */
1096
+ words: CaptionWord[];
1097
+ /** Start time of first word in ms */
1098
+ startMs: number;
1099
+ /** End time of last word in ms */
1100
+ endMs: number;
1101
+ }
1102
+ /**
1103
+ * Caption style preset names
1104
+ */
1105
+ type CaptionPreset = 'hormozi' | 'minimal' | 'bold-pop' | 'clean' | 'neon';
1106
+ /**
1107
+ * Caption vertical position on screen
1108
+ */
1109
+ type CaptionPosition = 'top' | 'center' | 'bottom';
1110
+ /**
1111
+ * Caption font weight options
1112
+ */
1113
+ type CaptionFontWeight = 'normal' | 'bold' | 'black';
1114
+ /**
1115
+ * Full caption style configuration
1116
+ */
1117
+ interface CaptionStyle {
1118
+ /** Preset name (if used, provides defaults for other fields) */
1119
+ preset?: CaptionPreset;
1120
+ /** Google Font name (e.g., 'Montserrat', 'Inter') */
1121
+ fontName: string;
1122
+ /** Font size in pixels (relative to 1920p height) */
1123
+ fontSize: number;
1124
+ /** Font weight */
1125
+ fontWeight: CaptionFontWeight;
1126
+ /** Text color for inactive words */
1127
+ fontColor: string;
1128
+ /** Text color for the currently active/highlighted word */
1129
+ highlightColor: string;
1130
+ /** Stroke/outline color */
1131
+ strokeColor: string;
1132
+ /** Stroke width in pixels */
1133
+ strokeWidth: number;
1134
+ /** Background color (hex) - omit for transparent */
1135
+ backgroundColor?: string;
1136
+ /** Background opacity (0-1) */
1137
+ backgroundOpacity?: number;
1138
+ /** Background border radius in pixels */
1139
+ backgroundBorderRadius?: number;
1140
+ /** Vertical position on screen */
1141
+ position: CaptionPosition;
1142
+ /** Vertical offset in pixels from position edge */
1143
+ yOffset: number;
1144
+ /** Max width as percentage of video width (0-100) */
1145
+ maxWidth: number;
1146
+ /** Enable bounce/scale animation on active word */
1147
+ enableAnimation: boolean;
1148
+ /** Maximum words per caption page/screen */
1149
+ wordsPerPage: number;
1150
+ }
1151
+ /**
1152
+ * Props for AutoCaptionComposition
1153
+ */
1154
+ interface AutoCaptionCompositionProps {
1155
+ /** Video source URL */
1156
+ videoUrl: string;
1157
+ /** Video width in pixels */
1158
+ width: number;
1159
+ /** Video height in pixels */
1160
+ height: number;
1161
+ /** Video duration in milliseconds */
1162
+ durationMs: number;
1163
+ /** Caption words with timing */
1164
+ captions: CaptionWord[];
1165
+ /** Caption styling configuration */
1166
+ style: CaptionStyle;
1167
+ }
1168
+ /**
1169
+ * Props for CaptionOverlay component
1170
+ */
1171
+ interface CaptionOverlayProps {
1172
+ /** Caption words with timing */
1173
+ captions: CaptionWord[];
1174
+ /** Caption styling configuration */
1175
+ style: CaptionStyle;
1176
+ /** Override current time for preview mode (ms) */
1177
+ previewTimeMs?: number;
1178
+ }
1179
+
1070
1180
  interface ImageEditorCompositionProps {
1071
1181
  /** The editor configuration to render (legacy format with pre-resolved segments) */
1072
1182
  config?: ImageEditorConfig;
@@ -1167,6 +1277,16 @@ interface VideoEditorCompositionProps {
1167
1277
  */
1168
1278
  declare function VideoEditorComposition({ config, sources, textContent, }: VideoEditorCompositionProps): react_jsx_runtime.JSX.Element;
1169
1279
 
1280
+ /**
1281
+ * AutoCaptionComposition renders a video with synchronized caption overlay
1282
+ */
1283
+ declare function AutoCaptionComposition({ videoUrl, captions, style, }: AutoCaptionCompositionProps): react_jsx_runtime.JSX.Element;
1284
+ /**
1285
+ * AutoCaptionComposition with standard Video component
1286
+ * Use this variant for client-side preview where OffthreadVideo may not work
1287
+ */
1288
+ declare function AutoCaptionCompositionWithVideo({ videoUrl, captions, style, }: AutoCaptionCompositionProps): react_jsx_runtime.JSX.Element;
1289
+
1170
1290
  interface TextElementProps {
1171
1291
  segment: TextSegment;
1172
1292
  /** Optional scale for high-DPI rendering */
@@ -1232,6 +1352,11 @@ interface VideoElementProps {
1232
1352
  */
1233
1353
  declare function VideoElement({ segment, src, startFrame, durationInFrames, scale }: VideoElementProps): react_jsx_runtime.JSX.Element;
1234
1354
 
1355
+ /**
1356
+ * CaptionOverlay renders animated captions synchronized with video playback
1357
+ */
1358
+ declare function CaptionOverlay({ captions, style, previewTimeMs }: CaptionOverlayProps): react_jsx_runtime.JSX.Element | null;
1359
+
1235
1360
  /**
1236
1361
  * Font utilities for the rendering system
1237
1362
  */
@@ -1588,6 +1713,45 @@ declare function generateSegmentId(): string;
1588
1713
  */
1589
1714
  declare function generateOverlayId(): string;
1590
1715
 
1716
+ /**
1717
+ * Caption style presets and resolution utilities
1718
+ *
1719
+ * Provides TikTok-style caption presets and a function to resolve
1720
+ * partial config into a complete CaptionStyle object.
1721
+ */
1722
+
1723
+ /**
1724
+ * Predefined caption style presets
1725
+ *
1726
+ * Font sizes are designed for 1920p height and scale proportionally.
1727
+ * maxWidth is a percentage of video width (0-100).
1728
+ */
1729
+ declare const CAPTION_PRESETS: Record<CaptionPreset, Omit<CaptionStyle, 'preset'>>;
1730
+ /**
1731
+ * Default caption style (used when no preset or custom values provided)
1732
+ */
1733
+ declare const DEFAULT_CAPTION_STYLE: Omit<CaptionStyle, "preset">;
1734
+ /**
1735
+ * Resolve a partial caption style config into a complete CaptionStyle object
1736
+ *
1737
+ * Priority:
1738
+ * 1. Explicit values in the input style
1739
+ * 2. Preset values (if preset is specified)
1740
+ * 3. Default values (hormozi preset)
1741
+ *
1742
+ * @param style - Partial caption style configuration
1743
+ * @returns Complete CaptionStyle object with all fields populated
1744
+ */
1745
+ declare function resolveCaptionStyle(style: Partial<CaptionStyle> | undefined): CaptionStyle;
1746
+ /**
1747
+ * Get all available preset names
1748
+ */
1749
+ declare function getCaptionPresetNames(): CaptionPreset[];
1750
+ /**
1751
+ * Check if a string is a valid caption preset name
1752
+ */
1753
+ declare function isValidCaptionPreset(name: string): name is CaptionPreset;
1754
+
1591
1755
  /**
1592
1756
  * Hook exports for ugcinc-render
1593
1757
  *
@@ -1650,4 +1814,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
1650
1814
 
1651
1815
  declare const RenderRoot: React.FC;
1652
1816
 
1653
- export { APPLE_EMOJI_FONT, type Acceleration, type AudioConfig, type AudioSegment, type BaseEditorConfig, type BaseSegment, type BlendMode, type BorderRadiusConfig, type Channel, type ColorFilterConfig, type ColorShiftConfig, type CropAxisConfig, type CropBoundary, type CropBounds, type CropConfig, type CuttingConfig, DEDUPLICATION_LEVELS, DIMENSION_PRESETS, type DeduplicationConfig, type DeduplicationInput, type DeduplicationLevel, type DiagonalFilterConfig, type DiagonalFilterType, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, type EnhanceLevel, type EnhancementConfig, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type FrameManipulationConfig, type GifOverlayConfig, type GifSource, type GradientOverlayConfig, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, LEVEL_1_CONFIG, LEVEL_2_CONFIG, LEVEL_3_CONFIG, LEVEL_4_CONFIG, LEVEL_5_CONFIG, type LensCorrectionConfig, type MobileEncodingConfig, type ParticleOverlayConfig, type PhoneConfig, type PhoneModel, type PhoneType, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type SpeedConfig, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextStyleProperties, type TextWrap, type TimeMode, type TimeValue, type TraceRemovalConfig, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoCodec, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoExtension, type VideoSegment, type VisualAdjustmentsConfig, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, debugFontStatus, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDeduplicationLevel, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveDeduplicationConfig, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
1817
+ export { APPLE_EMOJI_FONT, type Acceleration, type AudioConfig, type AudioSegment, AutoCaptionComposition, type AutoCaptionCompositionProps, AutoCaptionCompositionWithVideo, type BaseEditorConfig, type BaseSegment, type BlendMode, type BorderRadiusConfig, CAPTION_PRESETS, type CaptionFontWeight, CaptionOverlay, type CaptionOverlayProps, type CaptionPage, type CaptionPosition, type CaptionPreset, type CaptionStyle, type CaptionWord, type Channel, type ColorFilterConfig, type ColorShiftConfig, type CropAxisConfig, type CropBoundary, type CropBounds, type CropConfig, type CuttingConfig, DEDUPLICATION_LEVELS, DEFAULT_CAPTION_STYLE, DIMENSION_PRESETS, type DeduplicationConfig, type DeduplicationInput, type DeduplicationLevel, type DiagonalFilterConfig, type DiagonalFilterType, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, type EnhanceLevel, type EnhancementConfig, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type FrameManipulationConfig, type GifOverlayConfig, type GifSource, type GradientOverlayConfig, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, LEVEL_1_CONFIG, LEVEL_2_CONFIG, LEVEL_3_CONFIG, LEVEL_4_CONFIG, LEVEL_5_CONFIG, type LensCorrectionConfig, type MobileEncodingConfig, type ParticleOverlayConfig, type PhoneConfig, type PhoneModel, type PhoneType, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type SpeedConfig, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextStyleProperties, type TextWrap, type TimeMode, type TimeValue, type TraceRemovalConfig, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoCodec, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoExtension, type VideoSegment, type VisualAdjustmentsConfig, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, debugFontStatus, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getCaptionPresetNames, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDeduplicationLevel, isDynamicCropEnabled, isSegmentVisibleAtTime, isValidCaptionPreset, parseHexColor, parseTime, preloadFonts, resolveCaptionStyle, resolveDeduplicationConfig, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
package/dist/index.js CHANGED
@@ -31,7 +31,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  APPLE_EMOJI_FONT: () => APPLE_EMOJI_FONT,
34
+ AutoCaptionComposition: () => AutoCaptionComposition,
35
+ AutoCaptionCompositionWithVideo: () => AutoCaptionCompositionWithVideo,
36
+ CAPTION_PRESETS: () => CAPTION_PRESETS,
37
+ CaptionOverlay: () => CaptionOverlay,
34
38
  DEDUPLICATION_LEVELS: () => DEDUPLICATION_LEVELS,
39
+ DEFAULT_CAPTION_STYLE: () => DEFAULT_CAPTION_STYLE,
35
40
  DIMENSION_PRESETS: () => DIMENSION_PRESETS,
36
41
  FONT_FAMILIES: () => FONT_FAMILIES,
37
42
  FONT_URLS: () => FONT_URLS,
@@ -69,6 +74,7 @@ __export(index_exports, {
69
74
  generateSegmentId: () => generateSegmentId,
70
75
  getBaseSegments: () => getBaseSegments,
71
76
  getBorderRadii: () => getBorderRadii,
77
+ getCaptionPresetNames: () => getCaptionPresetNames,
72
78
  getDependentElements: () => getDependentElements,
73
79
  getFontFamily: () => getFontFamily,
74
80
  getOverlays: () => getOverlays,
@@ -79,9 +85,11 @@ __export(index_exports, {
79
85
  isDeduplicationLevel: () => isDeduplicationLevel,
80
86
  isDynamicCropEnabled: () => isDynamicCropEnabled,
81
87
  isSegmentVisibleAtTime: () => isSegmentVisibleAtTime,
88
+ isValidCaptionPreset: () => isValidCaptionPreset,
82
89
  parseHexColor: () => parseHexColor,
83
90
  parseTime: () => parseTime,
84
91
  preloadFonts: () => preloadFonts,
92
+ resolveCaptionStyle: () => resolveCaptionStyle,
85
93
  resolveDeduplicationConfig: () => resolveDeduplicationConfig,
86
94
  resolveElementPositions: () => resolveElementPositions,
87
95
  useFontsLoaded: () => useFontsLoaded,
@@ -2051,6 +2059,194 @@ function VideoEditorComposition({
2051
2059
  ] });
2052
2060
  }
2053
2061
 
2062
+ // src/compositions/AutoCaptionComposition.tsx
2063
+ var import_remotion6 = require("remotion");
2064
+
2065
+ // src/components/CaptionOverlay.tsx
2066
+ var import_react6 = require("react");
2067
+ var import_remotion5 = require("remotion");
2068
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2069
+ function createPages(words, wordsPerPage) {
2070
+ const pages = [];
2071
+ for (let i = 0; i < words.length; i += wordsPerPage) {
2072
+ const pageWords = words.slice(i, i + wordsPerPage);
2073
+ if (pageWords.length > 0) {
2074
+ pages.push({
2075
+ words: pageWords,
2076
+ startMs: pageWords[0].startMs,
2077
+ endMs: pageWords[pageWords.length - 1].endMs
2078
+ });
2079
+ }
2080
+ }
2081
+ return pages;
2082
+ }
2083
+ function hexToRgba2(hex, opacity) {
2084
+ const cleanHex = hex.replace("#", "");
2085
+ const r = parseInt(cleanHex.substring(0, 2), 16);
2086
+ const g = parseInt(cleanHex.substring(2, 4), 16);
2087
+ const b = parseInt(cleanHex.substring(4, 6), 16);
2088
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
2089
+ }
2090
+ function CaptionOverlay({ captions, style, previewTimeMs }) {
2091
+ const frame = (0, import_remotion5.useCurrentFrame)();
2092
+ const { fps, width, height } = (0, import_remotion5.useVideoConfig)();
2093
+ const currentMs = previewTimeMs ?? frame / fps * 1e3;
2094
+ const pages = (0, import_react6.useMemo)(
2095
+ () => createPages(captions, style.wordsPerPage),
2096
+ [captions, style.wordsPerPage]
2097
+ );
2098
+ const currentPage = pages.find(
2099
+ (page) => currentMs >= page.startMs && currentMs <= page.endMs
2100
+ );
2101
+ if (!currentPage) return null;
2102
+ const maxWidthPx = style.maxWidth / 100 * width;
2103
+ const scaleFactor = height / 1920;
2104
+ const scaledFontSize = style.fontSize * scaleFactor;
2105
+ const scaledStrokeWidth = style.strokeWidth * scaleFactor;
2106
+ const scaledYOffset = style.yOffset * scaleFactor;
2107
+ const scaledBorderRadius = (style.backgroundBorderRadius ?? 0) * scaleFactor;
2108
+ const getPositionStyles = () => {
2109
+ const base = {
2110
+ position: "absolute",
2111
+ left: "50%"
2112
+ };
2113
+ switch (style.position) {
2114
+ case "top":
2115
+ return {
2116
+ ...base,
2117
+ top: scaledYOffset,
2118
+ transform: "translateX(-50%)"
2119
+ };
2120
+ case "center":
2121
+ return {
2122
+ ...base,
2123
+ top: "50%",
2124
+ transform: "translate(-50%, -50%)"
2125
+ };
2126
+ case "bottom":
2127
+ default:
2128
+ return {
2129
+ ...base,
2130
+ bottom: scaledYOffset,
2131
+ transform: "translateX(-50%)"
2132
+ };
2133
+ }
2134
+ };
2135
+ const containerStyles = {
2136
+ ...getPositionStyles(),
2137
+ maxWidth: maxWidthPx,
2138
+ textAlign: "center",
2139
+ // Add padding if background is enabled
2140
+ padding: style.backgroundColor ? `${scaledFontSize * 0.3}px ${scaledFontSize * 0.5}px` : 0,
2141
+ // Background styling
2142
+ backgroundColor: style.backgroundColor ? hexToRgba2(style.backgroundColor, style.backgroundOpacity ?? 0.7) : "transparent",
2143
+ borderRadius: scaledBorderRadius
2144
+ };
2145
+ const numericFontWeight = style.fontWeight === "black" ? 900 : style.fontWeight === "bold" ? 700 : 400;
2146
+ const buildStrokeShadow = () => {
2147
+ if (scaledStrokeWidth <= 0) return "none";
2148
+ const sw = scaledStrokeWidth;
2149
+ const sc = style.strokeColor;
2150
+ return `
2151
+ -${sw}px -${sw}px 0 ${sc},
2152
+ ${sw}px -${sw}px 0 ${sc},
2153
+ -${sw}px ${sw}px 0 ${sc},
2154
+ ${sw}px ${sw}px 0 ${sc},
2155
+ 0 -${sw}px 0 ${sc},
2156
+ 0 ${sw}px 0 ${sc},
2157
+ -${sw}px 0 0 ${sc},
2158
+ ${sw}px 0 0 ${sc}
2159
+ `;
2160
+ };
2161
+ const textContainerStyles = {
2162
+ fontFamily: `"${style.fontName}", sans-serif`,
2163
+ fontSize: scaledFontSize,
2164
+ fontWeight: numericFontWeight,
2165
+ lineHeight: 1.2,
2166
+ wordBreak: "break-word",
2167
+ textShadow: buildStrokeShadow()
2168
+ };
2169
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: containerStyles, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: textContainerStyles, children: currentPage.words.map((word, index) => {
2170
+ const isActive = currentMs >= word.startMs && currentMs <= word.endMs;
2171
+ const color = isActive ? style.highlightColor : style.fontColor;
2172
+ let scale = 1;
2173
+ if (style.enableAnimation && isActive) {
2174
+ const wordDuration = word.endMs - word.startMs;
2175
+ if (wordDuration > 0) {
2176
+ const progress = (currentMs - word.startMs) / wordDuration;
2177
+ scale = (0, import_remotion5.interpolate)(
2178
+ progress,
2179
+ [0, 0.15, 0.3, 1],
2180
+ [1, 1.15, 1.05, 1],
2181
+ {
2182
+ extrapolateLeft: "clamp",
2183
+ extrapolateRight: "clamp"
2184
+ }
2185
+ );
2186
+ }
2187
+ }
2188
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2189
+ "span",
2190
+ {
2191
+ style: {
2192
+ color,
2193
+ display: "inline-block",
2194
+ transform: `scale(${scale})`,
2195
+ transformOrigin: "center center"
2196
+ },
2197
+ children: [
2198
+ word.word,
2199
+ index < currentPage.words.length - 1 ? " " : ""
2200
+ ]
2201
+ },
2202
+ `${word.word}-${word.startMs}-${index}`
2203
+ );
2204
+ }) }) });
2205
+ }
2206
+
2207
+ // src/compositions/AutoCaptionComposition.tsx
2208
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2209
+ function AutoCaptionComposition({
2210
+ videoUrl,
2211
+ captions,
2212
+ style
2213
+ }) {
2214
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_remotion6.AbsoluteFill, { style: { backgroundColor: "#000000" }, children: [
2215
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2216
+ import_remotion6.OffthreadVideo,
2217
+ {
2218
+ src: videoUrl,
2219
+ style: {
2220
+ width: "100%",
2221
+ height: "100%",
2222
+ objectFit: "contain"
2223
+ }
2224
+ }
2225
+ ),
2226
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CaptionOverlay, { captions, style })
2227
+ ] });
2228
+ }
2229
+ function AutoCaptionCompositionWithVideo({
2230
+ videoUrl,
2231
+ captions,
2232
+ style
2233
+ }) {
2234
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_remotion6.AbsoluteFill, { style: { backgroundColor: "#000000" }, children: [
2235
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2236
+ import_remotion6.Video,
2237
+ {
2238
+ src: videoUrl,
2239
+ style: {
2240
+ width: "100%",
2241
+ height: "100%",
2242
+ objectFit: "contain"
2243
+ }
2244
+ }
2245
+ ),
2246
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CaptionOverlay, { captions, style })
2247
+ ] });
2248
+ }
2249
+
2054
2250
  // src/utils/fit.ts
2055
2251
  function calculateFitDimensions({
2056
2252
  sourceWidth,
@@ -2205,11 +2401,140 @@ function generateOverlayId() {
2205
2401
  return `overlay-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
2206
2402
  }
2207
2403
 
2404
+ // src/utils/captionPresets.ts
2405
+ var CAPTION_PRESETS = {
2406
+ /**
2407
+ * Hormozi style - bold, attention-grabbing captions
2408
+ * Yellow highlight on white text with thick black stroke
2409
+ */
2410
+ hormozi: {
2411
+ fontName: "Montserrat",
2412
+ fontSize: 80,
2413
+ fontWeight: "black",
2414
+ fontColor: "#FFFFFF",
2415
+ highlightColor: "#FFFF00",
2416
+ strokeColor: "#000000",
2417
+ strokeWidth: 3,
2418
+ position: "bottom",
2419
+ yOffset: 100,
2420
+ maxWidth: 90,
2421
+ enableAnimation: true,
2422
+ wordsPerPage: 3
2423
+ },
2424
+ /**
2425
+ * Minimal style - clean and understated
2426
+ * White on white (no highlight distinction), smaller text
2427
+ */
2428
+ minimal: {
2429
+ fontName: "Inter",
2430
+ fontSize: 60,
2431
+ fontWeight: "bold",
2432
+ fontColor: "#FFFFFF",
2433
+ highlightColor: "#FFFFFF",
2434
+ strokeColor: "#000000",
2435
+ strokeWidth: 2,
2436
+ position: "bottom",
2437
+ yOffset: 80,
2438
+ maxWidth: 85,
2439
+ enableAnimation: false,
2440
+ wordsPerPage: 5
2441
+ },
2442
+ /**
2443
+ * Bold Pop style - maximum impact, one word at a time
2444
+ * Yellow text with red highlight, centered position
2445
+ */
2446
+ "bold-pop": {
2447
+ fontName: "Bangers",
2448
+ fontSize: 90,
2449
+ fontWeight: "bold",
2450
+ fontColor: "#FFFF00",
2451
+ highlightColor: "#FF0000",
2452
+ strokeColor: "#000000",
2453
+ strokeWidth: 4,
2454
+ position: "center",
2455
+ yOffset: 0,
2456
+ maxWidth: 80,
2457
+ enableAnimation: true,
2458
+ wordsPerPage: 2
2459
+ },
2460
+ /**
2461
+ * Clean style - professional look with background
2462
+ * White text with blue highlight, semi-transparent background
2463
+ */
2464
+ clean: {
2465
+ fontName: "Roboto",
2466
+ fontSize: 55,
2467
+ fontWeight: "normal",
2468
+ fontColor: "#FFFFFF",
2469
+ highlightColor: "#3B82F6",
2470
+ strokeColor: "#000000",
2471
+ strokeWidth: 0,
2472
+ backgroundColor: "#000000",
2473
+ backgroundOpacity: 0.7,
2474
+ backgroundBorderRadius: 8,
2475
+ position: "bottom",
2476
+ yOffset: 80,
2477
+ maxWidth: 90,
2478
+ enableAnimation: false,
2479
+ wordsPerPage: 6
2480
+ },
2481
+ /**
2482
+ * Neon style - vibrant, eye-catching colors
2483
+ * Cyan text with magenta highlight
2484
+ */
2485
+ neon: {
2486
+ fontName: "Poppins",
2487
+ fontSize: 70,
2488
+ fontWeight: "bold",
2489
+ fontColor: "#00FFFF",
2490
+ highlightColor: "#FF00FF",
2491
+ strokeColor: "#000000",
2492
+ strokeWidth: 3,
2493
+ position: "bottom",
2494
+ yOffset: 100,
2495
+ maxWidth: 85,
2496
+ enableAnimation: true,
2497
+ wordsPerPage: 3
2498
+ }
2499
+ };
2500
+ var DEFAULT_CAPTION_STYLE = CAPTION_PRESETS.hormozi;
2501
+ function resolveCaptionStyle(style) {
2502
+ if (!style) {
2503
+ return { ...DEFAULT_CAPTION_STYLE };
2504
+ }
2505
+ const presetValues = style.preset ? CAPTION_PRESETS[style.preset] : {};
2506
+ const defaults = DEFAULT_CAPTION_STYLE;
2507
+ return {
2508
+ preset: style.preset,
2509
+ fontName: style.fontName ?? presetValues.fontName ?? defaults.fontName,
2510
+ fontSize: style.fontSize ?? presetValues.fontSize ?? defaults.fontSize,
2511
+ fontWeight: style.fontWeight ?? presetValues.fontWeight ?? defaults.fontWeight,
2512
+ fontColor: style.fontColor ?? presetValues.fontColor ?? defaults.fontColor,
2513
+ highlightColor: style.highlightColor ?? presetValues.highlightColor ?? defaults.highlightColor,
2514
+ strokeColor: style.strokeColor ?? presetValues.strokeColor ?? defaults.strokeColor,
2515
+ strokeWidth: style.strokeWidth ?? presetValues.strokeWidth ?? defaults.strokeWidth,
2516
+ backgroundColor: style.backgroundColor ?? presetValues.backgroundColor,
2517
+ backgroundOpacity: style.backgroundOpacity ?? presetValues.backgroundOpacity,
2518
+ backgroundBorderRadius: style.backgroundBorderRadius ?? presetValues.backgroundBorderRadius,
2519
+ position: style.position ?? presetValues.position ?? defaults.position,
2520
+ yOffset: style.yOffset ?? presetValues.yOffset ?? defaults.yOffset,
2521
+ maxWidth: style.maxWidth ?? presetValues.maxWidth ?? defaults.maxWidth,
2522
+ enableAnimation: style.enableAnimation ?? presetValues.enableAnimation ?? defaults.enableAnimation,
2523
+ wordsPerPage: style.wordsPerPage ?? presetValues.wordsPerPage ?? defaults.wordsPerPage
2524
+ };
2525
+ }
2526
+ function getCaptionPresetNames() {
2527
+ return Object.keys(CAPTION_PRESETS);
2528
+ }
2529
+ function isValidCaptionPreset(name) {
2530
+ return name in CAPTION_PRESETS;
2531
+ }
2532
+
2208
2533
  // src/hooks/index.ts
2209
- var import_react6 = require("react");
2534
+ var import_react7 = require("react");
2210
2535
  function useFontsLoaded() {
2211
- const [loaded, setLoaded] = (0, import_react6.useState)(areFontsLoaded());
2212
- (0, import_react6.useEffect)(() => {
2536
+ const [loaded, setLoaded] = (0, import_react7.useState)(areFontsLoaded());
2537
+ (0, import_react7.useEffect)(() => {
2213
2538
  if (!loaded) {
2214
2539
  preloadFonts().then(() => setLoaded(true)).catch(console.error);
2215
2540
  }
@@ -2217,8 +2542,8 @@ function useFontsLoaded() {
2217
2542
  return loaded;
2218
2543
  }
2219
2544
  function useImageLoader(src) {
2220
- const [image, setImage] = (0, import_react6.useState)(null);
2221
- (0, import_react6.useEffect)(() => {
2545
+ const [image, setImage] = (0, import_react7.useState)(null);
2546
+ (0, import_react7.useEffect)(() => {
2222
2547
  if (!src) {
2223
2548
  setImage(null);
2224
2549
  return;
@@ -2238,9 +2563,9 @@ function useImageLoader(src) {
2238
2563
  return image;
2239
2564
  }
2240
2565
  function useImagePreloader(sources) {
2241
- const [images, setImages] = (0, import_react6.useState)({});
2242
- const [loaded, setLoaded] = (0, import_react6.useState)(false);
2243
- (0, import_react6.useEffect)(() => {
2566
+ const [images, setImages] = (0, import_react7.useState)({});
2567
+ const [loaded, setLoaded] = (0, import_react7.useState)(false);
2568
+ (0, import_react7.useEffect)(() => {
2244
2569
  const entries = Object.entries(sources);
2245
2570
  if (entries.length === 0) {
2246
2571
  setLoaded(true);
@@ -2278,7 +2603,7 @@ function useImagePreloader(sources) {
2278
2603
  return { loaded, images };
2279
2604
  }
2280
2605
  function useResolvedPositions(elements, textValues) {
2281
- return (0, import_react6.useMemo)(() => {
2606
+ return (0, import_react7.useMemo)(() => {
2282
2607
  if (elements.length === 0) {
2283
2608
  return { elements: [], errors: [] };
2284
2609
  }
@@ -2287,8 +2612,8 @@ function useResolvedPositions(elements, textValues) {
2287
2612
  }
2288
2613
 
2289
2614
  // src/Root.tsx
2290
- var import_remotion5 = require("remotion");
2291
- var import_jsx_runtime6 = require("react/jsx-runtime");
2615
+ var import_remotion7 = require("remotion");
2616
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2292
2617
  var defaultImageProps = {
2293
2618
  config: {
2294
2619
  width: 1080,
@@ -2361,9 +2686,9 @@ var calculateImageMetadata = async ({ props }) => {
2361
2686
  };
2362
2687
  };
2363
2688
  var RenderRoot = () => {
2364
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2365
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2366
- import_remotion5.Composition,
2689
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2690
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2691
+ import_remotion7.Composition,
2367
2692
  {
2368
2693
  id: "ImageEditorComposition",
2369
2694
  component: ImageComp,
@@ -2375,8 +2700,8 @@ var RenderRoot = () => {
2375
2700
  defaultProps: defaultImageProps
2376
2701
  }
2377
2702
  ),
2378
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2379
- import_remotion5.Composition,
2703
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2704
+ import_remotion7.Composition,
2380
2705
  {
2381
2706
  id: "VideoEditorComposition",
2382
2707
  component: VideoComp,
@@ -2392,7 +2717,12 @@ var RenderRoot = () => {
2392
2717
  // Annotate the CommonJS export names for ESM import in node:
2393
2718
  0 && (module.exports = {
2394
2719
  APPLE_EMOJI_FONT,
2720
+ AutoCaptionComposition,
2721
+ AutoCaptionCompositionWithVideo,
2722
+ CAPTION_PRESETS,
2723
+ CaptionOverlay,
2395
2724
  DEDUPLICATION_LEVELS,
2725
+ DEFAULT_CAPTION_STYLE,
2396
2726
  DIMENSION_PRESETS,
2397
2727
  FONT_FAMILIES,
2398
2728
  FONT_URLS,
@@ -2430,6 +2760,7 @@ var RenderRoot = () => {
2430
2760
  generateSegmentId,
2431
2761
  getBaseSegments,
2432
2762
  getBorderRadii,
2763
+ getCaptionPresetNames,
2433
2764
  getDependentElements,
2434
2765
  getFontFamily,
2435
2766
  getOverlays,
@@ -2440,9 +2771,11 @@ var RenderRoot = () => {
2440
2771
  isDeduplicationLevel,
2441
2772
  isDynamicCropEnabled,
2442
2773
  isSegmentVisibleAtTime,
2774
+ isValidCaptionPreset,
2443
2775
  parseHexColor,
2444
2776
  parseTime,
2445
2777
  preloadFonts,
2778
+ resolveCaptionStyle,
2446
2779
  resolveDeduplicationConfig,
2447
2780
  resolveElementPositions,
2448
2781
  useFontsLoaded,
package/dist/index.mjs CHANGED
@@ -1957,6 +1957,194 @@ function VideoEditorComposition({
1957
1957
  ] });
1958
1958
  }
1959
1959
 
1960
+ // src/compositions/AutoCaptionComposition.tsx
1961
+ import { AbsoluteFill as AbsoluteFill3, Video, OffthreadVideo as OffthreadVideo2 } from "remotion";
1962
+
1963
+ // src/components/CaptionOverlay.tsx
1964
+ import { useMemo as useMemo6 } from "react";
1965
+ import { useCurrentFrame as useCurrentFrame4, useVideoConfig as useVideoConfig4, interpolate } from "remotion";
1966
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1967
+ function createPages(words, wordsPerPage) {
1968
+ const pages = [];
1969
+ for (let i = 0; i < words.length; i += wordsPerPage) {
1970
+ const pageWords = words.slice(i, i + wordsPerPage);
1971
+ if (pageWords.length > 0) {
1972
+ pages.push({
1973
+ words: pageWords,
1974
+ startMs: pageWords[0].startMs,
1975
+ endMs: pageWords[pageWords.length - 1].endMs
1976
+ });
1977
+ }
1978
+ }
1979
+ return pages;
1980
+ }
1981
+ function hexToRgba2(hex, opacity) {
1982
+ const cleanHex = hex.replace("#", "");
1983
+ const r = parseInt(cleanHex.substring(0, 2), 16);
1984
+ const g = parseInt(cleanHex.substring(2, 4), 16);
1985
+ const b = parseInt(cleanHex.substring(4, 6), 16);
1986
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
1987
+ }
1988
+ function CaptionOverlay({ captions, style, previewTimeMs }) {
1989
+ const frame = useCurrentFrame4();
1990
+ const { fps, width, height } = useVideoConfig4();
1991
+ const currentMs = previewTimeMs ?? frame / fps * 1e3;
1992
+ const pages = useMemo6(
1993
+ () => createPages(captions, style.wordsPerPage),
1994
+ [captions, style.wordsPerPage]
1995
+ );
1996
+ const currentPage = pages.find(
1997
+ (page) => currentMs >= page.startMs && currentMs <= page.endMs
1998
+ );
1999
+ if (!currentPage) return null;
2000
+ const maxWidthPx = style.maxWidth / 100 * width;
2001
+ const scaleFactor = height / 1920;
2002
+ const scaledFontSize = style.fontSize * scaleFactor;
2003
+ const scaledStrokeWidth = style.strokeWidth * scaleFactor;
2004
+ const scaledYOffset = style.yOffset * scaleFactor;
2005
+ const scaledBorderRadius = (style.backgroundBorderRadius ?? 0) * scaleFactor;
2006
+ const getPositionStyles = () => {
2007
+ const base = {
2008
+ position: "absolute",
2009
+ left: "50%"
2010
+ };
2011
+ switch (style.position) {
2012
+ case "top":
2013
+ return {
2014
+ ...base,
2015
+ top: scaledYOffset,
2016
+ transform: "translateX(-50%)"
2017
+ };
2018
+ case "center":
2019
+ return {
2020
+ ...base,
2021
+ top: "50%",
2022
+ transform: "translate(-50%, -50%)"
2023
+ };
2024
+ case "bottom":
2025
+ default:
2026
+ return {
2027
+ ...base,
2028
+ bottom: scaledYOffset,
2029
+ transform: "translateX(-50%)"
2030
+ };
2031
+ }
2032
+ };
2033
+ const containerStyles = {
2034
+ ...getPositionStyles(),
2035
+ maxWidth: maxWidthPx,
2036
+ textAlign: "center",
2037
+ // Add padding if background is enabled
2038
+ padding: style.backgroundColor ? `${scaledFontSize * 0.3}px ${scaledFontSize * 0.5}px` : 0,
2039
+ // Background styling
2040
+ backgroundColor: style.backgroundColor ? hexToRgba2(style.backgroundColor, style.backgroundOpacity ?? 0.7) : "transparent",
2041
+ borderRadius: scaledBorderRadius
2042
+ };
2043
+ const numericFontWeight = style.fontWeight === "black" ? 900 : style.fontWeight === "bold" ? 700 : 400;
2044
+ const buildStrokeShadow = () => {
2045
+ if (scaledStrokeWidth <= 0) return "none";
2046
+ const sw = scaledStrokeWidth;
2047
+ const sc = style.strokeColor;
2048
+ return `
2049
+ -${sw}px -${sw}px 0 ${sc},
2050
+ ${sw}px -${sw}px 0 ${sc},
2051
+ -${sw}px ${sw}px 0 ${sc},
2052
+ ${sw}px ${sw}px 0 ${sc},
2053
+ 0 -${sw}px 0 ${sc},
2054
+ 0 ${sw}px 0 ${sc},
2055
+ -${sw}px 0 0 ${sc},
2056
+ ${sw}px 0 0 ${sc}
2057
+ `;
2058
+ };
2059
+ const textContainerStyles = {
2060
+ fontFamily: `"${style.fontName}", sans-serif`,
2061
+ fontSize: scaledFontSize,
2062
+ fontWeight: numericFontWeight,
2063
+ lineHeight: 1.2,
2064
+ wordBreak: "break-word",
2065
+ textShadow: buildStrokeShadow()
2066
+ };
2067
+ return /* @__PURE__ */ jsx6("div", { style: containerStyles, children: /* @__PURE__ */ jsx6("div", { style: textContainerStyles, children: currentPage.words.map((word, index) => {
2068
+ const isActive = currentMs >= word.startMs && currentMs <= word.endMs;
2069
+ const color = isActive ? style.highlightColor : style.fontColor;
2070
+ let scale = 1;
2071
+ if (style.enableAnimation && isActive) {
2072
+ const wordDuration = word.endMs - word.startMs;
2073
+ if (wordDuration > 0) {
2074
+ const progress = (currentMs - word.startMs) / wordDuration;
2075
+ scale = interpolate(
2076
+ progress,
2077
+ [0, 0.15, 0.3, 1],
2078
+ [1, 1.15, 1.05, 1],
2079
+ {
2080
+ extrapolateLeft: "clamp",
2081
+ extrapolateRight: "clamp"
2082
+ }
2083
+ );
2084
+ }
2085
+ }
2086
+ return /* @__PURE__ */ jsxs4(
2087
+ "span",
2088
+ {
2089
+ style: {
2090
+ color,
2091
+ display: "inline-block",
2092
+ transform: `scale(${scale})`,
2093
+ transformOrigin: "center center"
2094
+ },
2095
+ children: [
2096
+ word.word,
2097
+ index < currentPage.words.length - 1 ? " " : ""
2098
+ ]
2099
+ },
2100
+ `${word.word}-${word.startMs}-${index}`
2101
+ );
2102
+ }) }) });
2103
+ }
2104
+
2105
+ // src/compositions/AutoCaptionComposition.tsx
2106
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2107
+ function AutoCaptionComposition({
2108
+ videoUrl,
2109
+ captions,
2110
+ style
2111
+ }) {
2112
+ return /* @__PURE__ */ jsxs5(AbsoluteFill3, { style: { backgroundColor: "#000000" }, children: [
2113
+ /* @__PURE__ */ jsx7(
2114
+ OffthreadVideo2,
2115
+ {
2116
+ src: videoUrl,
2117
+ style: {
2118
+ width: "100%",
2119
+ height: "100%",
2120
+ objectFit: "contain"
2121
+ }
2122
+ }
2123
+ ),
2124
+ /* @__PURE__ */ jsx7(CaptionOverlay, { captions, style })
2125
+ ] });
2126
+ }
2127
+ function AutoCaptionCompositionWithVideo({
2128
+ videoUrl,
2129
+ captions,
2130
+ style
2131
+ }) {
2132
+ return /* @__PURE__ */ jsxs5(AbsoluteFill3, { style: { backgroundColor: "#000000" }, children: [
2133
+ /* @__PURE__ */ jsx7(
2134
+ Video,
2135
+ {
2136
+ src: videoUrl,
2137
+ style: {
2138
+ width: "100%",
2139
+ height: "100%",
2140
+ objectFit: "contain"
2141
+ }
2142
+ }
2143
+ ),
2144
+ /* @__PURE__ */ jsx7(CaptionOverlay, { captions, style })
2145
+ ] });
2146
+ }
2147
+
1960
2148
  // src/utils/fit.ts
1961
2149
  function calculateFitDimensions({
1962
2150
  sourceWidth,
@@ -2111,8 +2299,137 @@ function generateOverlayId() {
2111
2299
  return `overlay-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
2112
2300
  }
2113
2301
 
2302
+ // src/utils/captionPresets.ts
2303
+ var CAPTION_PRESETS = {
2304
+ /**
2305
+ * Hormozi style - bold, attention-grabbing captions
2306
+ * Yellow highlight on white text with thick black stroke
2307
+ */
2308
+ hormozi: {
2309
+ fontName: "Montserrat",
2310
+ fontSize: 80,
2311
+ fontWeight: "black",
2312
+ fontColor: "#FFFFFF",
2313
+ highlightColor: "#FFFF00",
2314
+ strokeColor: "#000000",
2315
+ strokeWidth: 3,
2316
+ position: "bottom",
2317
+ yOffset: 100,
2318
+ maxWidth: 90,
2319
+ enableAnimation: true,
2320
+ wordsPerPage: 3
2321
+ },
2322
+ /**
2323
+ * Minimal style - clean and understated
2324
+ * White on white (no highlight distinction), smaller text
2325
+ */
2326
+ minimal: {
2327
+ fontName: "Inter",
2328
+ fontSize: 60,
2329
+ fontWeight: "bold",
2330
+ fontColor: "#FFFFFF",
2331
+ highlightColor: "#FFFFFF",
2332
+ strokeColor: "#000000",
2333
+ strokeWidth: 2,
2334
+ position: "bottom",
2335
+ yOffset: 80,
2336
+ maxWidth: 85,
2337
+ enableAnimation: false,
2338
+ wordsPerPage: 5
2339
+ },
2340
+ /**
2341
+ * Bold Pop style - maximum impact, one word at a time
2342
+ * Yellow text with red highlight, centered position
2343
+ */
2344
+ "bold-pop": {
2345
+ fontName: "Bangers",
2346
+ fontSize: 90,
2347
+ fontWeight: "bold",
2348
+ fontColor: "#FFFF00",
2349
+ highlightColor: "#FF0000",
2350
+ strokeColor: "#000000",
2351
+ strokeWidth: 4,
2352
+ position: "center",
2353
+ yOffset: 0,
2354
+ maxWidth: 80,
2355
+ enableAnimation: true,
2356
+ wordsPerPage: 2
2357
+ },
2358
+ /**
2359
+ * Clean style - professional look with background
2360
+ * White text with blue highlight, semi-transparent background
2361
+ */
2362
+ clean: {
2363
+ fontName: "Roboto",
2364
+ fontSize: 55,
2365
+ fontWeight: "normal",
2366
+ fontColor: "#FFFFFF",
2367
+ highlightColor: "#3B82F6",
2368
+ strokeColor: "#000000",
2369
+ strokeWidth: 0,
2370
+ backgroundColor: "#000000",
2371
+ backgroundOpacity: 0.7,
2372
+ backgroundBorderRadius: 8,
2373
+ position: "bottom",
2374
+ yOffset: 80,
2375
+ maxWidth: 90,
2376
+ enableAnimation: false,
2377
+ wordsPerPage: 6
2378
+ },
2379
+ /**
2380
+ * Neon style - vibrant, eye-catching colors
2381
+ * Cyan text with magenta highlight
2382
+ */
2383
+ neon: {
2384
+ fontName: "Poppins",
2385
+ fontSize: 70,
2386
+ fontWeight: "bold",
2387
+ fontColor: "#00FFFF",
2388
+ highlightColor: "#FF00FF",
2389
+ strokeColor: "#000000",
2390
+ strokeWidth: 3,
2391
+ position: "bottom",
2392
+ yOffset: 100,
2393
+ maxWidth: 85,
2394
+ enableAnimation: true,
2395
+ wordsPerPage: 3
2396
+ }
2397
+ };
2398
+ var DEFAULT_CAPTION_STYLE = CAPTION_PRESETS.hormozi;
2399
+ function resolveCaptionStyle(style) {
2400
+ if (!style) {
2401
+ return { ...DEFAULT_CAPTION_STYLE };
2402
+ }
2403
+ const presetValues = style.preset ? CAPTION_PRESETS[style.preset] : {};
2404
+ const defaults = DEFAULT_CAPTION_STYLE;
2405
+ return {
2406
+ preset: style.preset,
2407
+ fontName: style.fontName ?? presetValues.fontName ?? defaults.fontName,
2408
+ fontSize: style.fontSize ?? presetValues.fontSize ?? defaults.fontSize,
2409
+ fontWeight: style.fontWeight ?? presetValues.fontWeight ?? defaults.fontWeight,
2410
+ fontColor: style.fontColor ?? presetValues.fontColor ?? defaults.fontColor,
2411
+ highlightColor: style.highlightColor ?? presetValues.highlightColor ?? defaults.highlightColor,
2412
+ strokeColor: style.strokeColor ?? presetValues.strokeColor ?? defaults.strokeColor,
2413
+ strokeWidth: style.strokeWidth ?? presetValues.strokeWidth ?? defaults.strokeWidth,
2414
+ backgroundColor: style.backgroundColor ?? presetValues.backgroundColor,
2415
+ backgroundOpacity: style.backgroundOpacity ?? presetValues.backgroundOpacity,
2416
+ backgroundBorderRadius: style.backgroundBorderRadius ?? presetValues.backgroundBorderRadius,
2417
+ position: style.position ?? presetValues.position ?? defaults.position,
2418
+ yOffset: style.yOffset ?? presetValues.yOffset ?? defaults.yOffset,
2419
+ maxWidth: style.maxWidth ?? presetValues.maxWidth ?? defaults.maxWidth,
2420
+ enableAnimation: style.enableAnimation ?? presetValues.enableAnimation ?? defaults.enableAnimation,
2421
+ wordsPerPage: style.wordsPerPage ?? presetValues.wordsPerPage ?? defaults.wordsPerPage
2422
+ };
2423
+ }
2424
+ function getCaptionPresetNames() {
2425
+ return Object.keys(CAPTION_PRESETS);
2426
+ }
2427
+ function isValidCaptionPreset(name) {
2428
+ return name in CAPTION_PRESETS;
2429
+ }
2430
+
2114
2431
  // src/hooks/index.ts
2115
- import { useEffect as useEffect2, useState as useState2, useMemo as useMemo6 } from "react";
2432
+ import { useEffect as useEffect2, useState as useState2, useMemo as useMemo7 } from "react";
2116
2433
  function useFontsLoaded() {
2117
2434
  const [loaded, setLoaded] = useState2(areFontsLoaded());
2118
2435
  useEffect2(() => {
@@ -2184,7 +2501,7 @@ function useImagePreloader(sources) {
2184
2501
  return { loaded, images };
2185
2502
  }
2186
2503
  function useResolvedPositions(elements, textValues) {
2187
- return useMemo6(() => {
2504
+ return useMemo7(() => {
2188
2505
  if (elements.length === 0) {
2189
2506
  return { elements: [], errors: [] };
2190
2507
  }
@@ -2194,7 +2511,7 @@ function useResolvedPositions(elements, textValues) {
2194
2511
 
2195
2512
  // src/Root.tsx
2196
2513
  import { Composition } from "remotion";
2197
- import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
2514
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2198
2515
  var defaultImageProps = {
2199
2516
  config: {
2200
2517
  width: 1080,
@@ -2267,8 +2584,8 @@ var calculateImageMetadata = async ({ props }) => {
2267
2584
  };
2268
2585
  };
2269
2586
  var RenderRoot = () => {
2270
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
2271
- /* @__PURE__ */ jsx6(
2587
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
2588
+ /* @__PURE__ */ jsx8(
2272
2589
  Composition,
2273
2590
  {
2274
2591
  id: "ImageEditorComposition",
@@ -2281,7 +2598,7 @@ var RenderRoot = () => {
2281
2598
  defaultProps: defaultImageProps
2282
2599
  }
2283
2600
  ),
2284
- /* @__PURE__ */ jsx6(
2601
+ /* @__PURE__ */ jsx8(
2285
2602
  Composition,
2286
2603
  {
2287
2604
  id: "VideoEditorComposition",
@@ -2297,7 +2614,12 @@ var RenderRoot = () => {
2297
2614
  };
2298
2615
  export {
2299
2616
  APPLE_EMOJI_FONT,
2617
+ AutoCaptionComposition,
2618
+ AutoCaptionCompositionWithVideo,
2619
+ CAPTION_PRESETS,
2620
+ CaptionOverlay,
2300
2621
  DEDUPLICATION_LEVELS,
2622
+ DEFAULT_CAPTION_STYLE,
2301
2623
  DIMENSION_PRESETS,
2302
2624
  FONT_FAMILIES,
2303
2625
  FONT_URLS,
@@ -2335,6 +2657,7 @@ export {
2335
2657
  generateSegmentId,
2336
2658
  getBaseSegments,
2337
2659
  getBorderRadii,
2660
+ getCaptionPresetNames,
2338
2661
  getDependentElements,
2339
2662
  getFontFamily,
2340
2663
  getOverlays,
@@ -2345,9 +2668,11 @@ export {
2345
2668
  isDeduplicationLevel,
2346
2669
  isDynamicCropEnabled,
2347
2670
  isSegmentVisibleAtTime,
2671
+ isValidCaptionPreset,
2348
2672
  parseHexColor,
2349
2673
  parseTime,
2350
2674
  preloadFonts,
2675
+ resolveCaptionStyle,
2351
2676
  resolveDeduplicationConfig,
2352
2677
  resolveElementPositions,
2353
2678
  useFontsLoaded,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.6.1",
3
+ "version": "1.8.0",
4
4
  "description": "Unified rendering package for UGC Inc - shared types, components, and compositions for pixel-perfect client/server rendering",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",