ugcinc 3.84.6 → 3.85.1
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.ts +2 -1
- package/dist/index.js +6 -1
- package/dist/render/Root.d.ts +12 -0
- package/dist/render/Root.js +508 -0
- package/dist/render/components/CaptionOverlay.d.ts +21 -0
- package/dist/render/components/CaptionOverlay.js +210 -0
- package/dist/render/components/ImageElement.d.ts +26 -0
- package/dist/render/components/ImageElement.js +88 -0
- package/dist/render/components/TextElement.d.ts +30 -0
- package/dist/render/components/TextElement.js +390 -0
- package/dist/render/components/VideoElement.d.ts +30 -0
- package/dist/render/components/VideoElement.js +108 -0
- package/dist/render/components/index.d.ts +7 -0
- package/dist/render/components/index.js +14 -0
- package/dist/render/compositions/AutoCaptionComposition.d.ts +42 -0
- package/dist/render/compositions/AutoCaptionComposition.js +29 -0
- package/dist/render/compositions/DmComposition/BaseDmComposition.d.ts +112 -0
- package/dist/render/compositions/DmComposition/BaseDmComposition.js +212 -0
- package/dist/render/compositions/DmComposition/DebugOverlay.d.ts +20 -0
- package/dist/render/compositions/DmComposition/DebugOverlay.js +258 -0
- package/dist/render/compositions/DmComposition/index.d.ts +6 -0
- package/dist/render/compositions/DmComposition/index.js +10 -0
- package/dist/render/compositions/IMessageDmComposition/convertPropsToElements.d.ts +27 -0
- package/dist/render/compositions/IMessageDmComposition/convertPropsToElements.js +629 -0
- package/dist/render/compositions/IMessageDmComposition/index.d.ts +20 -0
- package/dist/render/compositions/IMessageDmComposition/index.js +485 -0
- package/dist/render/compositions/IMessageDmComposition/types.d.ts +756 -0
- package/dist/render/compositions/IMessageDmComposition/types.js +225 -0
- package/dist/render/compositions/ImageEditorComposition.d.ts +74 -0
- package/dist/render/compositions/ImageEditorComposition.js +351 -0
- package/dist/render/compositions/InstagramDmComposition/convertPropsToElements.d.ts +60 -0
- package/dist/render/compositions/InstagramDmComposition/convertPropsToElements.js +1318 -0
- package/dist/render/compositions/InstagramDmComposition/convertPublicToProps.d.ts +30 -0
- package/dist/render/compositions/InstagramDmComposition/convertPublicToProps.js +131 -0
- package/dist/render/compositions/InstagramDmComposition/index.d.ts +16 -0
- package/dist/render/compositions/InstagramDmComposition/index.js +374 -0
- package/dist/render/compositions/InstagramDmComposition/theme.d.ts +42 -0
- package/dist/render/compositions/InstagramDmComposition/theme.js +55 -0
- package/dist/render/compositions/InstagramDmComposition/types.d.ts +215 -0
- package/dist/render/compositions/InstagramDmComposition/types.js +7 -0
- package/dist/render/compositions/ScreenshotAnimation.d.ts +14 -0
- package/dist/render/compositions/ScreenshotAnimation.js +268 -0
- package/dist/render/compositions/VideoEditorComposition.d.ts +45 -0
- package/dist/render/compositions/VideoEditorComposition.js +307 -0
- package/dist/render/compositions/index.d.ts +12 -0
- package/dist/render/compositions/index.js +43 -0
- package/dist/render/compositions/messaging/components/MediaBubble.d.ts +22 -0
- package/dist/render/compositions/messaging/components/MediaBubble.js +25 -0
- package/dist/render/compositions/messaging/components/MessageBubble.d.ts +35 -0
- package/dist/render/compositions/messaging/components/MessageBubble.js +34 -0
- package/dist/render/compositions/messaging/components/ProfilePic.d.ts +23 -0
- package/dist/render/compositions/messaging/components/ProfilePic.js +37 -0
- package/dist/render/compositions/messaging/components/Reaction.d.ts +23 -0
- package/dist/render/compositions/messaging/components/Reaction.js +19 -0
- package/dist/render/compositions/messaging/components/TypingIndicator.d.ts +25 -0
- package/dist/render/compositions/messaging/components/TypingIndicator.js +66 -0
- package/dist/render/compositions/messaging/index.d.ts +14 -0
- package/dist/render/compositions/messaging/index.js +43 -0
- package/dist/render/compositions/messaging/types.d.ts +148 -0
- package/dist/render/compositions/messaging/types.js +21 -0
- package/dist/render/compositions/messaging/utils/bubbleRadius.d.ts +45 -0
- package/dist/render/compositions/messaging/utils/bubbleRadius.js +84 -0
- package/dist/render/compositions/messaging/utils/groupMessages.d.ts +41 -0
- package/dist/render/compositions/messaging/utils/groupMessages.js +110 -0
- package/dist/render/data/phone-top-nav.d.ts +1 -0
- package/dist/render/data/phone-top-nav.js +4 -0
- package/dist/render/data/screenshot.d.ts +164 -0
- package/dist/render/data/screenshot.js +63 -0
- package/dist/render/hooks/index.d.ts +54 -0
- package/dist/render/hooks/index.js +132 -0
- package/dist/render/index.d.ts +12 -0
- package/dist/render/index.js +36 -0
- package/dist/render/types/base.d.ts +148 -0
- package/dist/render/types/base.js +5 -0
- package/dist/render/types/caption.d.ts +105 -0
- package/dist/render/types/caption.js +8 -0
- package/dist/render/types/crop.d.ts +60 -0
- package/dist/render/types/crop.js +8 -0
- package/dist/render/types/deduplication.d.ts +284 -0
- package/dist/render/types/deduplication.js +240 -0
- package/dist/render/types/editor.d.ts +97 -0
- package/dist/render/types/editor.js +10 -0
- package/dist/render/types/element.d.ts +139 -0
- package/dist/render/types/element.js +19 -0
- package/dist/render/types/index.d.ts +20 -0
- package/dist/render/types/index.js +24 -0
- package/dist/render/types/instagram-dm-public.d.ts +60 -0
- package/dist/render/types/instagram-dm-public.js +8 -0
- package/dist/render/types/position.d.ts +59 -0
- package/dist/render/types/position.js +5 -0
- package/dist/render/types/screenshot.d.ts +57 -0
- package/dist/render/types/screenshot.js +34 -0
- package/dist/render/types/segment.d.ts +163 -0
- package/dist/render/types/segment.js +8 -0
- package/dist/render/types/video.d.ts +192 -0
- package/dist/render/types/video.js +14 -0
- package/dist/render/utils/captionPresets.d.ts +38 -0
- package/dist/render/utils/captionPresets.js +168 -0
- package/dist/render/utils/cropBounds.d.ts +20 -0
- package/dist/render/utils/cropBounds.js +166 -0
- package/dist/render/utils/defaults.d.ts +74 -0
- package/dist/render/utils/defaults.js +91 -0
- package/dist/render/utils/emoji.d.ts +40 -0
- package/dist/render/utils/emoji.js +105 -0
- package/dist/render/utils/fit.d.ts +35 -0
- package/dist/render/utils/fit.js +63 -0
- package/dist/render/utils/fonts.d.ts +55 -0
- package/dist/render/utils/fonts.js +191 -0
- package/dist/render/utils/index.d.ts +14 -0
- package/dist/render/utils/index.js +73 -0
- package/dist/render/utils/positionResolver.d.ts +64 -0
- package/dist/render/utils/positionResolver.js +508 -0
- package/dist/render/utils/text.d.ts +50 -0
- package/dist/render/utils/text.js +177 -0
- package/dist/render/utils/timeline.d.ts +62 -0
- package/dist/render/utils/timeline.js +172 -0
- package/dist/render.d.ts +1 -1
- package/dist/types.d.ts +137 -18
- package/dist/types.js +58 -0
- package/package.json +20 -6
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TextElement = TextElement;
|
|
37
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
|
+
/**
|
|
39
|
+
* TextElement Component
|
|
40
|
+
*
|
|
41
|
+
* Renders a text segment using HTML/CSS for pixel-perfect consistency
|
|
42
|
+
* between client preview and server render.
|
|
43
|
+
*/
|
|
44
|
+
const react_1 = __importStar(require("react"));
|
|
45
|
+
const defaults_1 = require("../utils/defaults");
|
|
46
|
+
const fonts_1 = require("../utils/fonts");
|
|
47
|
+
const text_1 = require("../utils/text");
|
|
48
|
+
/**
|
|
49
|
+
* Calculate the actual width for auto-width text AND the line breaks.
|
|
50
|
+
* Uses DOM-based measurement to ensure the same fonts are used as CSS rendering.
|
|
51
|
+
*
|
|
52
|
+
* This function determines:
|
|
53
|
+
* 1. Where text would wrap at maxWidth (the line breaks)
|
|
54
|
+
* 2. The width of the widest line (for background sizing)
|
|
55
|
+
*
|
|
56
|
+
* By returning the actual lines, we can render with explicit line breaks,
|
|
57
|
+
* ensuring text wraps at maxWidth while displaying in a calculatedWidth container.
|
|
58
|
+
*/
|
|
59
|
+
function calculateAutoWidthAndLines({ text, maxWidth, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing, lineHeight, }) {
|
|
60
|
+
// Check if we're in a browser environment
|
|
61
|
+
if (typeof document === 'undefined') {
|
|
62
|
+
return { width: maxWidth, lines: [text] };
|
|
63
|
+
}
|
|
64
|
+
// Available width for text content (excluding padding)
|
|
65
|
+
const availableWidth = maxWidth - paddingLeft - paddingRight;
|
|
66
|
+
// Handle edge case of very small or zero available width
|
|
67
|
+
if (availableWidth <= 0) {
|
|
68
|
+
return { width: maxWidth, lines: [text] };
|
|
69
|
+
}
|
|
70
|
+
// Create a measurement span with exact same styling as render
|
|
71
|
+
const measureSpan = document.createElement('span');
|
|
72
|
+
measureSpan.style.cssText = `
|
|
73
|
+
position: absolute;
|
|
74
|
+
visibility: hidden;
|
|
75
|
+
pointer-events: none;
|
|
76
|
+
font-family: ${fontFamily};
|
|
77
|
+
font-size: ${fontSize}px;
|
|
78
|
+
font-weight: ${fontWeight};
|
|
79
|
+
letter-spacing: ${letterSpacing}px;
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
`;
|
|
82
|
+
document.body.appendChild(measureSpan);
|
|
83
|
+
// Get computed style to verify font
|
|
84
|
+
const computedStyle = window.getComputedStyle(measureSpan);
|
|
85
|
+
const computedFontFamily = computedStyle.fontFamily;
|
|
86
|
+
// Check if requested font is loaded
|
|
87
|
+
const requestedFontLoaded = typeof document.fonts !== 'undefined'
|
|
88
|
+
? document.fonts.check(`${fontWeight} ${fontSize}px ${fontFamily.split(',')[0]}`)
|
|
89
|
+
: 'unknown';
|
|
90
|
+
const measureText = (textToMeasure) => {
|
|
91
|
+
measureSpan.textContent = textToMeasure;
|
|
92
|
+
return measureSpan.getBoundingClientRect().width;
|
|
93
|
+
};
|
|
94
|
+
// Measure reference string for debugging
|
|
95
|
+
measureSpan.textContent = 'M';
|
|
96
|
+
const mWidth = measureSpan.getBoundingClientRect().width;
|
|
97
|
+
// Split text into words, preserving spaces for accurate measurement
|
|
98
|
+
const words = text.split(/(\s+)/);
|
|
99
|
+
const lines = [];
|
|
100
|
+
let currentLine = '';
|
|
101
|
+
for (let i = 0; i < words.length; i++) {
|
|
102
|
+
const word = words[i];
|
|
103
|
+
if (word === '\n' || word === '\r\n') {
|
|
104
|
+
if (currentLine)
|
|
105
|
+
lines.push(currentLine);
|
|
106
|
+
currentLine = '';
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (!word)
|
|
110
|
+
continue;
|
|
111
|
+
if (/^\s+$/.test(word)) {
|
|
112
|
+
if (currentLine)
|
|
113
|
+
currentLine += word;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const testLine = currentLine + word;
|
|
117
|
+
const testWidth = measureText(testLine);
|
|
118
|
+
const WRAP_TOLERANCE = 0.5;
|
|
119
|
+
if (testWidth > availableWidth + WRAP_TOLERANCE && currentLine.trim()) {
|
|
120
|
+
lines.push(currentLine.trimEnd());
|
|
121
|
+
currentLine = word;
|
|
122
|
+
const wordWidth = measureText(word);
|
|
123
|
+
if (wordWidth > availableWidth) {
|
|
124
|
+
let remainingWord = word;
|
|
125
|
+
while (remainingWord) {
|
|
126
|
+
let breakPoint = 1;
|
|
127
|
+
for (let j = 1; j <= remainingWord.length; j++) {
|
|
128
|
+
measureSpan.textContent = remainingWord.substring(0, j);
|
|
129
|
+
if (measureSpan.getBoundingClientRect().width > availableWidth) {
|
|
130
|
+
breakPoint = Math.max(1, j - 1);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
breakPoint = j;
|
|
134
|
+
}
|
|
135
|
+
if (breakPoint >= remainingWord.length) {
|
|
136
|
+
currentLine = remainingWord;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
lines.push(remainingWord.substring(0, breakPoint));
|
|
141
|
+
remainingWord = remainingWord.substring(breakPoint);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
currentLine = testLine;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (currentLine.trim())
|
|
151
|
+
lines.push(currentLine.trimEnd());
|
|
152
|
+
if (lines.length === 0)
|
|
153
|
+
lines.push('');
|
|
154
|
+
// Find the widest line
|
|
155
|
+
let widestLineWidth = 0;
|
|
156
|
+
for (const line of lines) {
|
|
157
|
+
const lineWidth = measureText(line);
|
|
158
|
+
widestLineWidth = Math.max(widestLineWidth, lineWidth);
|
|
159
|
+
}
|
|
160
|
+
document.body.removeChild(measureSpan);
|
|
161
|
+
const calculatedWidth = Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
|
|
162
|
+
// CONDENSED DEBUG LOG
|
|
163
|
+
console.log('[TextElement] WIDTH_CALC:', JSON.stringify({
|
|
164
|
+
text: text.substring(0, 30),
|
|
165
|
+
fontFamily: fontFamily.split(',')[0],
|
|
166
|
+
computedFont: computedFontFamily.split(',')[0],
|
|
167
|
+
fontLoaded: requestedFontLoaded,
|
|
168
|
+
fontSize,
|
|
169
|
+
fontWeight,
|
|
170
|
+
letterSpacing,
|
|
171
|
+
mWidth: Math.round(mWidth * 100) / 100,
|
|
172
|
+
widestLine: Math.round(widestLineWidth * 100) / 100,
|
|
173
|
+
paddingL: paddingLeft,
|
|
174
|
+
paddingR: paddingRight,
|
|
175
|
+
calcWidth: Math.round(calculatedWidth * 100) / 100,
|
|
176
|
+
maxWidth,
|
|
177
|
+
lines: lines.length,
|
|
178
|
+
}));
|
|
179
|
+
return { width: calculatedWidth, lines };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* TextElement renders text with full styling support including:
|
|
183
|
+
* - Font family, size, weight, color
|
|
184
|
+
* - Letter spacing, line height
|
|
185
|
+
* - Horizontal and vertical alignment
|
|
186
|
+
* - Text stroke/outline
|
|
187
|
+
* - Background with opacity and border radius
|
|
188
|
+
* - Padding (uniform and individual)
|
|
189
|
+
* - Auto-width with box alignment
|
|
190
|
+
* - Rotation
|
|
191
|
+
*
|
|
192
|
+
* When autoWidth is enabled:
|
|
193
|
+
* - Text wraps at maxWidth (the element's width)
|
|
194
|
+
* - Background shrinks to the widest line
|
|
195
|
+
* - boxAlign positions the box within the maxWidth container
|
|
196
|
+
*/
|
|
197
|
+
function TextElement({ segment, scale = 1 }) {
|
|
198
|
+
// Apply defaults
|
|
199
|
+
const fontType = segment.fontType ?? defaults_1.TEXT_DEFAULTS.fontType;
|
|
200
|
+
const fontSize = (segment.fontSize ?? defaults_1.TEXT_DEFAULTS.fontSize) * scale;
|
|
201
|
+
const fontWeight = segment.fontWeight ?? defaults_1.TEXT_DEFAULTS.fontWeight;
|
|
202
|
+
const color = segment.color ?? defaults_1.TEXT_DEFAULTS.color;
|
|
203
|
+
const alignment = segment.alignment ?? defaults_1.TEXT_DEFAULTS.alignment;
|
|
204
|
+
const verticalAlign = segment.verticalAlign ?? defaults_1.TEXT_DEFAULTS.verticalAlign;
|
|
205
|
+
const lineHeight = segment.lineHeight ?? defaults_1.TEXT_DEFAULTS.lineHeight;
|
|
206
|
+
const letterSpacing = (segment.letterSpacing ?? defaults_1.TEXT_DEFAULTS.letterSpacing) * scale;
|
|
207
|
+
const strokeColor = segment.strokeColor;
|
|
208
|
+
const strokeWidth = (segment.strokeWidth ?? defaults_1.TEXT_DEFAULTS.strokeWidth) * scale;
|
|
209
|
+
// Padding - base values (always used for line calculation)
|
|
210
|
+
const uniformPadding = (segment.padding ?? defaults_1.TEXT_DEFAULTS.padding) * scale;
|
|
211
|
+
const basePaddingTop = segment.paddingTop !== undefined ? segment.paddingTop * scale : uniformPadding;
|
|
212
|
+
const basePaddingRight = segment.paddingRight !== undefined ? segment.paddingRight * scale : uniformPadding;
|
|
213
|
+
const basePaddingBottom = segment.paddingBottom !== undefined ? segment.paddingBottom * scale : uniformPadding;
|
|
214
|
+
const basePaddingLeft = segment.paddingLeft !== undefined ? segment.paddingLeft * scale : uniformPadding;
|
|
215
|
+
// Multi-line EXTRA padding (scaled) - ADDED to base padding when 2+ lines
|
|
216
|
+
const extraPaddingTop = (segment.multiLineExtraPaddingTop ?? 0) * scale;
|
|
217
|
+
const extraPaddingRight = (segment.multiLineExtraPaddingRight ?? 0) * scale;
|
|
218
|
+
const extraPaddingBottom = (segment.multiLineExtraPaddingBottom ?? 0) * scale;
|
|
219
|
+
const extraPaddingLeft = (segment.multiLineExtraPaddingLeft ?? 0) * scale;
|
|
220
|
+
// Check if any extra padding is defined
|
|
221
|
+
const hasExtraPadding = extraPaddingTop > 0 || extraPaddingRight > 0 || extraPaddingBottom > 0 || extraPaddingLeft > 0;
|
|
222
|
+
// Position and size
|
|
223
|
+
const x = segment.xOffset * scale;
|
|
224
|
+
const y = segment.yOffset * scale;
|
|
225
|
+
const width = segment.width * scale;
|
|
226
|
+
const height = segment.height * scale;
|
|
227
|
+
const rotation = segment.rotation ?? 0;
|
|
228
|
+
// Auto-width
|
|
229
|
+
const autoWidth = segment.autoWidth ?? defaults_1.TEXT_DEFAULTS.autoWidth;
|
|
230
|
+
const boxAlign = segment.boxAlign ?? defaults_1.TEXT_DEFAULTS.boxAlign;
|
|
231
|
+
// Background
|
|
232
|
+
const backgroundColor = segment.backgroundColor;
|
|
233
|
+
const backgroundOpacity = segment.backgroundOpacity ?? defaults_1.TEXT_DEFAULTS.backgroundOpacity;
|
|
234
|
+
const backgroundBorderRadius = segment.backgroundBorderRadius;
|
|
235
|
+
// Get font family
|
|
236
|
+
const fontFamily = (0, fonts_1.getFontFamily)(fontType);
|
|
237
|
+
// Calculate width and line breaks
|
|
238
|
+
const { calculatedWidth, calculatedLines, paddingTop, paddingRight, paddingBottom, paddingLeft } = (0, react_1.useMemo)(() => {
|
|
239
|
+
let finalPaddingTop = basePaddingTop;
|
|
240
|
+
let finalPaddingRight = basePaddingRight;
|
|
241
|
+
let finalPaddingBottom = basePaddingBottom;
|
|
242
|
+
let finalPaddingLeft = basePaddingLeft;
|
|
243
|
+
if (!autoWidth && !hasExtraPadding) {
|
|
244
|
+
return {
|
|
245
|
+
calculatedWidth: width,
|
|
246
|
+
calculatedLines: [segment.text],
|
|
247
|
+
paddingTop: finalPaddingTop,
|
|
248
|
+
paddingRight: finalPaddingRight,
|
|
249
|
+
paddingBottom: finalPaddingBottom,
|
|
250
|
+
paddingLeft: finalPaddingLeft,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const baseResult = calculateAutoWidthAndLines({
|
|
254
|
+
text: segment.text,
|
|
255
|
+
maxWidth: width,
|
|
256
|
+
paddingLeft: basePaddingLeft,
|
|
257
|
+
paddingRight: basePaddingRight,
|
|
258
|
+
fontSize,
|
|
259
|
+
fontWeight,
|
|
260
|
+
fontFamily,
|
|
261
|
+
letterSpacing,
|
|
262
|
+
lineHeight,
|
|
263
|
+
});
|
|
264
|
+
const isMultiLine = baseResult.lines.length >= 2;
|
|
265
|
+
if (isMultiLine) {
|
|
266
|
+
finalPaddingTop = basePaddingTop + extraPaddingTop;
|
|
267
|
+
finalPaddingRight = basePaddingRight + extraPaddingRight;
|
|
268
|
+
finalPaddingBottom = basePaddingBottom + extraPaddingBottom;
|
|
269
|
+
finalPaddingLeft = basePaddingLeft + extraPaddingLeft;
|
|
270
|
+
}
|
|
271
|
+
let finalResult = baseResult;
|
|
272
|
+
if (isMultiLine && (extraPaddingLeft > 0 || extraPaddingRight > 0)) {
|
|
273
|
+
finalResult = calculateAutoWidthAndLines({
|
|
274
|
+
text: segment.text,
|
|
275
|
+
maxWidth: width,
|
|
276
|
+
paddingLeft: finalPaddingLeft,
|
|
277
|
+
paddingRight: finalPaddingRight,
|
|
278
|
+
fontSize,
|
|
279
|
+
fontWeight,
|
|
280
|
+
fontFamily,
|
|
281
|
+
letterSpacing,
|
|
282
|
+
lineHeight,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
calculatedWidth: autoWidth ? finalResult.width : width,
|
|
287
|
+
calculatedLines: autoWidth ? finalResult.lines : [segment.text],
|
|
288
|
+
paddingTop: finalPaddingTop,
|
|
289
|
+
paddingRight: finalPaddingRight,
|
|
290
|
+
paddingBottom: finalPaddingBottom,
|
|
291
|
+
paddingLeft: finalPaddingLeft,
|
|
292
|
+
};
|
|
293
|
+
}, [
|
|
294
|
+
autoWidth, segment.text, width,
|
|
295
|
+
basePaddingTop, basePaddingRight, basePaddingBottom, basePaddingLeft,
|
|
296
|
+
extraPaddingTop, extraPaddingRight, extraPaddingBottom, extraPaddingLeft,
|
|
297
|
+
hasExtraPadding,
|
|
298
|
+
fontSize, fontWeight, fontFamily, letterSpacing, lineHeight
|
|
299
|
+
]);
|
|
300
|
+
// Calculate border radius CSS
|
|
301
|
+
const borderRadiusStyle = (0, react_1.useMemo)(() => {
|
|
302
|
+
if (!backgroundBorderRadius)
|
|
303
|
+
return undefined;
|
|
304
|
+
const radii = (0, text_1.getBorderRadii)(backgroundBorderRadius);
|
|
305
|
+
if (!radii || radii.length < 4)
|
|
306
|
+
return undefined;
|
|
307
|
+
return `${radii[0] * scale}px ${radii[1] * scale}px ${radii[2] * scale}px ${radii[3] * scale}px`;
|
|
308
|
+
}, [backgroundBorderRadius, scale]);
|
|
309
|
+
// Positioning container style
|
|
310
|
+
const positioningContainerStyle = (0, react_1.useMemo)(() => ({
|
|
311
|
+
position: 'absolute',
|
|
312
|
+
left: x,
|
|
313
|
+
top: y,
|
|
314
|
+
width: width,
|
|
315
|
+
height: height,
|
|
316
|
+
transform: rotation !== 0 ? `rotate(${rotation}deg)` : undefined,
|
|
317
|
+
transformOrigin: 'center center',
|
|
318
|
+
display: 'flex',
|
|
319
|
+
flexDirection: 'column',
|
|
320
|
+
justifyContent: verticalAlign === 'top' ? 'flex-start' : verticalAlign === 'bottom' ? 'flex-end' : 'center',
|
|
321
|
+
alignItems: autoWidth
|
|
322
|
+
? (boxAlign === 'center' ? 'center' : boxAlign === 'right' ? 'flex-end' : 'flex-start')
|
|
323
|
+
: 'stretch',
|
|
324
|
+
}), [x, y, width, height, rotation, verticalAlign, autoWidth, boxAlign]);
|
|
325
|
+
// Background box style
|
|
326
|
+
const backgroundBoxStyle = (0, react_1.useMemo)(() => {
|
|
327
|
+
const baseStyle = autoWidth
|
|
328
|
+
? { width: calculatedWidth, maxWidth: width }
|
|
329
|
+
: { display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 };
|
|
330
|
+
if (!backgroundColor)
|
|
331
|
+
return baseStyle;
|
|
332
|
+
return {
|
|
333
|
+
...baseStyle,
|
|
334
|
+
backgroundColor: (0, text_1.hexToRgba)(backgroundColor, backgroundOpacity),
|
|
335
|
+
borderRadius: borderRadiusStyle,
|
|
336
|
+
};
|
|
337
|
+
}, [autoWidth, calculatedWidth, width, backgroundColor, backgroundOpacity, borderRadiusStyle]);
|
|
338
|
+
// Text style
|
|
339
|
+
const textStyle = (0, react_1.useMemo)(() => ({
|
|
340
|
+
fontFamily,
|
|
341
|
+
fontSize,
|
|
342
|
+
fontWeight,
|
|
343
|
+
color,
|
|
344
|
+
lineHeight,
|
|
345
|
+
letterSpacing,
|
|
346
|
+
textAlign: alignment === 'justify' ? 'justify' : alignment,
|
|
347
|
+
padding: `${paddingTop}px ${paddingRight}px ${paddingBottom}px ${paddingLeft}px`,
|
|
348
|
+
whiteSpace: 'pre-wrap',
|
|
349
|
+
wordBreak: 'break-word',
|
|
350
|
+
...(!autoWidth && {
|
|
351
|
+
display: 'flex',
|
|
352
|
+
flexDirection: 'column',
|
|
353
|
+
justifyContent: verticalAlign === 'top' ? 'flex-start' : verticalAlign === 'bottom' ? 'flex-end' : 'center',
|
|
354
|
+
flex: 1,
|
|
355
|
+
minHeight: 0,
|
|
356
|
+
}),
|
|
357
|
+
...(strokeWidth > 0 && strokeColor && {
|
|
358
|
+
textShadow: `
|
|
359
|
+
-${strokeWidth}px -${strokeWidth}px 0 ${strokeColor},
|
|
360
|
+
${strokeWidth}px -${strokeWidth}px 0 ${strokeColor},
|
|
361
|
+
-${strokeWidth}px ${strokeWidth}px 0 ${strokeColor},
|
|
362
|
+
${strokeWidth}px ${strokeWidth}px 0 ${strokeColor},
|
|
363
|
+
0 -${strokeWidth}px 0 ${strokeColor},
|
|
364
|
+
0 ${strokeWidth}px 0 ${strokeColor},
|
|
365
|
+
-${strokeWidth}px 0 0 ${strokeColor},
|
|
366
|
+
${strokeWidth}px 0 0 ${strokeColor}
|
|
367
|
+
`,
|
|
368
|
+
WebkitTextStroke: `${strokeWidth}px ${strokeColor}`,
|
|
369
|
+
paintOrder: 'stroke fill',
|
|
370
|
+
}),
|
|
371
|
+
}), [
|
|
372
|
+
fontFamily, fontSize, fontWeight, color, lineHeight, letterSpacing, alignment,
|
|
373
|
+
paddingTop, paddingRight, paddingBottom, paddingLeft, strokeWidth, strokeColor,
|
|
374
|
+
autoWidth, verticalAlign
|
|
375
|
+
]);
|
|
376
|
+
if (autoWidth) {
|
|
377
|
+
const textContent = calculatedLines.map((line, index) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [line, index < calculatedLines.length - 1 && (0, jsx_runtime_1.jsx)("br", {})] }, index)));
|
|
378
|
+
if (backgroundColor) {
|
|
379
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.jsx)("div", { style: {
|
|
380
|
+
width: calculatedWidth,
|
|
381
|
+
maxWidth: width,
|
|
382
|
+
backgroundColor: (0, text_1.hexToRgba)(backgroundColor, backgroundOpacity),
|
|
383
|
+
borderRadius: borderRadiusStyle,
|
|
384
|
+
}, children: (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: textContent }) }) }));
|
|
385
|
+
}
|
|
386
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.jsx)("div", { style: { width: calculatedWidth, maxWidth: width }, children: (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: textContent }) }) }));
|
|
387
|
+
}
|
|
388
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: positioningContainerStyle, children: (0, jsx_runtime_1.jsx)("div", { style: backgroundBoxStyle, children: (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: segment.text }) }) }));
|
|
389
|
+
}
|
|
390
|
+
exports.default = TextElement;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VideoElement Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a video segment with playback controls, fit modes, and visual effects.
|
|
5
|
+
* Uses the renderer's Video component for frame-accurate playback.
|
|
6
|
+
*/
|
|
7
|
+
import type { VideoSegment } from '../types';
|
|
8
|
+
export interface VideoElementProps {
|
|
9
|
+
segment: VideoSegment;
|
|
10
|
+
/** The source URL of the video */
|
|
11
|
+
src: string;
|
|
12
|
+
/** Start frame of this segment in the composition */
|
|
13
|
+
startFrame: number;
|
|
14
|
+
/** Duration of this segment in frames */
|
|
15
|
+
durationInFrames: number;
|
|
16
|
+
/** Optional scale for high-DPI rendering */
|
|
17
|
+
scale?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* VideoElement renders videos with:
|
|
21
|
+
* - Playback speed control
|
|
22
|
+
* - Volume control
|
|
23
|
+
* - Fit modes (cover, contain, fill)
|
|
24
|
+
* - Opacity
|
|
25
|
+
* - Rotation
|
|
26
|
+
* - Border radius
|
|
27
|
+
* - Fade-in effect
|
|
28
|
+
*/
|
|
29
|
+
export declare function VideoElement({ segment, src, startFrame, durationInFrames, scale }: VideoElementProps): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export default VideoElement;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VideoElement = VideoElement;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* VideoElement Component
|
|
7
|
+
*
|
|
8
|
+
* Renders a video segment with playback controls, fit modes, and visual effects.
|
|
9
|
+
* Uses the renderer's Video component for frame-accurate playback.
|
|
10
|
+
*/
|
|
11
|
+
const react_1 = require("react");
|
|
12
|
+
const remotion_1 = require("remotion");
|
|
13
|
+
const defaults_1 = require("../utils/defaults");
|
|
14
|
+
const text_1 = require("../utils/text");
|
|
15
|
+
/**
|
|
16
|
+
* Map FitMode to CSS object-fit
|
|
17
|
+
*/
|
|
18
|
+
function fitModeToCss(fit) {
|
|
19
|
+
return fit;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* VideoElement renders videos with:
|
|
23
|
+
* - Playback speed control
|
|
24
|
+
* - Volume control
|
|
25
|
+
* - Fit modes (cover, contain, fill)
|
|
26
|
+
* - Opacity
|
|
27
|
+
* - Rotation
|
|
28
|
+
* - Border radius
|
|
29
|
+
* - Fade-in effect
|
|
30
|
+
*/
|
|
31
|
+
function VideoElement({ segment, src, startFrame, durationInFrames, scale = 1 }) {
|
|
32
|
+
const frame = (0, remotion_1.useCurrentFrame)();
|
|
33
|
+
const { fps } = (0, remotion_1.useVideoConfig)();
|
|
34
|
+
// Apply defaults
|
|
35
|
+
const fit = segment.fit ?? defaults_1.VIDEO_DEFAULTS.fit;
|
|
36
|
+
const speed = segment.speed ?? defaults_1.VIDEO_DEFAULTS.speed;
|
|
37
|
+
const volume = (segment.volume ?? defaults_1.VIDEO_DEFAULTS.volume) / 100;
|
|
38
|
+
const opacity = (segment.opacity ?? defaults_1.VISUAL_DEFAULTS.opacity) / 100;
|
|
39
|
+
const rotation = segment.rotation ?? 0;
|
|
40
|
+
const borderRadius = segment.borderRadius;
|
|
41
|
+
const fadeIn = segment.fadeIn ?? 0;
|
|
42
|
+
const loop = segment.loop ?? defaults_1.VIDEO_DEFAULTS.loop;
|
|
43
|
+
// For loop timing: use explicit sourceDurationMs, or fall back to segment duration
|
|
44
|
+
const loopSourceDurationMs = segment.sourceDurationMs
|
|
45
|
+
?? (segment.duration?.type === 'absolute' ? segment.duration.value : undefined);
|
|
46
|
+
// Position and size
|
|
47
|
+
const x = segment.xOffset * scale;
|
|
48
|
+
const y = segment.yOffset * scale;
|
|
49
|
+
const width = segment.width * scale;
|
|
50
|
+
const height = segment.height * scale;
|
|
51
|
+
// Calculate start time offset for source video (in frames for OffthreadVideo)
|
|
52
|
+
const startFromMs = segment.startTrim ?? 0;
|
|
53
|
+
const startFromFrames = Math.round((startFromMs / 1000) * fps);
|
|
54
|
+
// Calculate fade-in opacity
|
|
55
|
+
const fadeOpacity = (0, react_1.useMemo)(() => {
|
|
56
|
+
if (fadeIn <= 0)
|
|
57
|
+
return 1;
|
|
58
|
+
const framesFromStart = frame - startFrame;
|
|
59
|
+
const fadeInFrames = (fadeIn / 1000) * fps;
|
|
60
|
+
if (framesFromStart >= fadeInFrames)
|
|
61
|
+
return 1;
|
|
62
|
+
if (framesFromStart <= 0)
|
|
63
|
+
return 0;
|
|
64
|
+
return framesFromStart / fadeInFrames;
|
|
65
|
+
}, [frame, startFrame, fadeIn, fps]);
|
|
66
|
+
// Calculate border radius CSS
|
|
67
|
+
const borderRadiusStyle = (0, react_1.useMemo)(() => {
|
|
68
|
+
if (!borderRadius)
|
|
69
|
+
return undefined;
|
|
70
|
+
if (typeof borderRadius === 'number') {
|
|
71
|
+
return `${borderRadius * scale}px`;
|
|
72
|
+
}
|
|
73
|
+
const radii = (0, text_1.getBorderRadii)(borderRadius);
|
|
74
|
+
if (!radii || radii.length < 4)
|
|
75
|
+
return undefined;
|
|
76
|
+
return `${radii[0] * scale}px ${radii[1] * scale}px ${radii[2] * scale}px ${radii[3] * scale}px`;
|
|
77
|
+
}, [borderRadius, scale]);
|
|
78
|
+
// Container style
|
|
79
|
+
const containerStyle = (0, react_1.useMemo)(() => ({
|
|
80
|
+
position: 'absolute',
|
|
81
|
+
left: x,
|
|
82
|
+
top: y,
|
|
83
|
+
width,
|
|
84
|
+
height,
|
|
85
|
+
transform: rotation !== 0 ? `rotate(${rotation}deg)` : undefined,
|
|
86
|
+
transformOrigin: 'center center',
|
|
87
|
+
overflow: 'hidden',
|
|
88
|
+
borderRadius: borderRadiusStyle,
|
|
89
|
+
opacity: opacity * fadeOpacity,
|
|
90
|
+
}), [x, y, width, height, rotation, borderRadiusStyle, opacity, fadeOpacity]);
|
|
91
|
+
// Video style
|
|
92
|
+
const videoStyle = (0, react_1.useMemo)(() => ({
|
|
93
|
+
width: '100%',
|
|
94
|
+
height: '100%',
|
|
95
|
+
objectFit: fitModeToCss(fit),
|
|
96
|
+
}), [fit]);
|
|
97
|
+
// Calculate loop duration in frames (accounting for speed)
|
|
98
|
+
const loopDurationInFrames = (0, react_1.useMemo)(() => {
|
|
99
|
+
if (!loop || !loopSourceDurationMs)
|
|
100
|
+
return undefined;
|
|
101
|
+
// Duration adjusted for playback speed
|
|
102
|
+
const effectiveDurationMs = loopSourceDurationMs / speed;
|
|
103
|
+
return Math.max(1, Math.round((effectiveDurationMs / 1000) * fps));
|
|
104
|
+
}, [loop, loopSourceDurationMs, speed, fps]);
|
|
105
|
+
const videoElement = ((0, jsx_runtime_1.jsx)(remotion_1.OffthreadVideo, { src: src, style: videoStyle, startFrom: startFromFrames, playbackRate: speed, volume: volume }));
|
|
106
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: containerStyle, children: loop && loopDurationInFrames ? ((0, jsx_runtime_1.jsx)(remotion_1.Loop, { durationInFrames: loopDurationInFrames, children: videoElement })) : (videoElement) }));
|
|
107
|
+
}
|
|
108
|
+
exports.default = VideoElement;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component exports for ugcinc-render
|
|
3
|
+
*/
|
|
4
|
+
export { TextElement, type TextElementProps } from './TextElement';
|
|
5
|
+
export { ImageElement, type ImageElementProps } from './ImageElement';
|
|
6
|
+
export { VideoElement, type VideoElementProps } from './VideoElement';
|
|
7
|
+
export { CaptionOverlay } from './CaptionOverlay';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Component exports for ugcinc-render
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CaptionOverlay = exports.VideoElement = exports.ImageElement = exports.TextElement = void 0;
|
|
7
|
+
var TextElement_1 = require("./TextElement");
|
|
8
|
+
Object.defineProperty(exports, "TextElement", { enumerable: true, get: function () { return TextElement_1.TextElement; } });
|
|
9
|
+
var ImageElement_1 = require("./ImageElement");
|
|
10
|
+
Object.defineProperty(exports, "ImageElement", { enumerable: true, get: function () { return ImageElement_1.ImageElement; } });
|
|
11
|
+
var VideoElement_1 = require("./VideoElement");
|
|
12
|
+
Object.defineProperty(exports, "VideoElement", { enumerable: true, get: function () { return VideoElement_1.VideoElement; } });
|
|
13
|
+
var CaptionOverlay_1 = require("./CaptionOverlay");
|
|
14
|
+
Object.defineProperty(exports, "CaptionOverlay", { enumerable: true, get: function () { return CaptionOverlay_1.CaptionOverlay; } });
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutoCaptionComposition
|
|
3
|
+
*
|
|
4
|
+
* A Remotion composition that renders a video with TikTok-style captions overlay.
|
|
5
|
+
* Used for both server-side rendering (Modal) and client-side preview.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Video playback with caption overlay
|
|
9
|
+
* - Word-by-word highlighting synchronized to audio
|
|
10
|
+
* - Customizable styling via CaptionStyle
|
|
11
|
+
* - Proper text wrapping within bounds
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { Player } from '@remotion/player';
|
|
16
|
+
* import { AutoCaptionComposition } from 'ugcinc-render/compositions';
|
|
17
|
+
*
|
|
18
|
+
* <Player
|
|
19
|
+
* component={AutoCaptionComposition}
|
|
20
|
+
* inputProps={{
|
|
21
|
+
* videoUrl: 'https://...',
|
|
22
|
+
* captions: [{ word: 'Hello', startMs: 0, endMs: 500 }, ...],
|
|
23
|
+
* style: { preset: 'hormozi' },
|
|
24
|
+
* }}
|
|
25
|
+
* durationInFrames={durationInFrames}
|
|
26
|
+
* fps={30}
|
|
27
|
+
* compositionWidth={1080}
|
|
28
|
+
* compositionHeight={1920}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
import type { AutoCaptionCompositionProps } from '../types/caption';
|
|
33
|
+
/**
|
|
34
|
+
* AutoCaptionComposition renders a video with synchronized caption overlay
|
|
35
|
+
*/
|
|
36
|
+
export declare function AutoCaptionComposition({ videoUrl, captions, style, }: AutoCaptionCompositionProps): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
/**
|
|
38
|
+
* AutoCaptionComposition with standard Video component
|
|
39
|
+
* Use this variant for client-side preview where OffthreadVideo may not work
|
|
40
|
+
*/
|
|
41
|
+
export declare function AutoCaptionCompositionWithVideo({ videoUrl, captions, style, }: AutoCaptionCompositionProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export default AutoCaptionComposition;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoCaptionComposition = AutoCaptionComposition;
|
|
4
|
+
exports.AutoCaptionCompositionWithVideo = AutoCaptionCompositionWithVideo;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const remotion_1 = require("remotion");
|
|
7
|
+
const CaptionOverlay_1 = require("../components/CaptionOverlay");
|
|
8
|
+
/**
|
|
9
|
+
* AutoCaptionComposition renders a video with synchronized caption overlay
|
|
10
|
+
*/
|
|
11
|
+
function AutoCaptionComposition({ videoUrl, captions, style, }) {
|
|
12
|
+
return ((0, jsx_runtime_1.jsxs)(remotion_1.AbsoluteFill, { style: { backgroundColor: '#000000' }, children: [(0, jsx_runtime_1.jsx)(remotion_1.OffthreadVideo, { src: videoUrl, style: {
|
|
13
|
+
width: '100%',
|
|
14
|
+
height: '100%',
|
|
15
|
+
objectFit: 'cover',
|
|
16
|
+
} }), (0, jsx_runtime_1.jsx)(CaptionOverlay_1.CaptionOverlay, { captions: captions, style: style })] }));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* AutoCaptionComposition with standard Video component
|
|
20
|
+
* Use this variant for client-side preview where OffthreadVideo may not work
|
|
21
|
+
*/
|
|
22
|
+
function AutoCaptionCompositionWithVideo({ videoUrl, captions, style, }) {
|
|
23
|
+
return ((0, jsx_runtime_1.jsxs)(remotion_1.AbsoluteFill, { style: { backgroundColor: '#000000' }, children: [(0, jsx_runtime_1.jsx)(remotion_1.Video, { src: videoUrl, style: {
|
|
24
|
+
width: '100%',
|
|
25
|
+
height: '100%',
|
|
26
|
+
objectFit: 'cover',
|
|
27
|
+
} }), (0, jsx_runtime_1.jsx)(CaptionOverlay_1.CaptionOverlay, { captions: captions, style: style })] }));
|
|
28
|
+
}
|
|
29
|
+
exports.default = AutoCaptionComposition;
|