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.
Files changed (120) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +6 -1
  3. package/dist/render/Root.d.ts +12 -0
  4. package/dist/render/Root.js +508 -0
  5. package/dist/render/components/CaptionOverlay.d.ts +21 -0
  6. package/dist/render/components/CaptionOverlay.js +210 -0
  7. package/dist/render/components/ImageElement.d.ts +26 -0
  8. package/dist/render/components/ImageElement.js +88 -0
  9. package/dist/render/components/TextElement.d.ts +30 -0
  10. package/dist/render/components/TextElement.js +390 -0
  11. package/dist/render/components/VideoElement.d.ts +30 -0
  12. package/dist/render/components/VideoElement.js +108 -0
  13. package/dist/render/components/index.d.ts +7 -0
  14. package/dist/render/components/index.js +14 -0
  15. package/dist/render/compositions/AutoCaptionComposition.d.ts +42 -0
  16. package/dist/render/compositions/AutoCaptionComposition.js +29 -0
  17. package/dist/render/compositions/DmComposition/BaseDmComposition.d.ts +112 -0
  18. package/dist/render/compositions/DmComposition/BaseDmComposition.js +212 -0
  19. package/dist/render/compositions/DmComposition/DebugOverlay.d.ts +20 -0
  20. package/dist/render/compositions/DmComposition/DebugOverlay.js +258 -0
  21. package/dist/render/compositions/DmComposition/index.d.ts +6 -0
  22. package/dist/render/compositions/DmComposition/index.js +10 -0
  23. package/dist/render/compositions/IMessageDmComposition/convertPropsToElements.d.ts +27 -0
  24. package/dist/render/compositions/IMessageDmComposition/convertPropsToElements.js +629 -0
  25. package/dist/render/compositions/IMessageDmComposition/index.d.ts +20 -0
  26. package/dist/render/compositions/IMessageDmComposition/index.js +485 -0
  27. package/dist/render/compositions/IMessageDmComposition/types.d.ts +756 -0
  28. package/dist/render/compositions/IMessageDmComposition/types.js +225 -0
  29. package/dist/render/compositions/ImageEditorComposition.d.ts +74 -0
  30. package/dist/render/compositions/ImageEditorComposition.js +351 -0
  31. package/dist/render/compositions/InstagramDmComposition/convertPropsToElements.d.ts +60 -0
  32. package/dist/render/compositions/InstagramDmComposition/convertPropsToElements.js +1318 -0
  33. package/dist/render/compositions/InstagramDmComposition/convertPublicToProps.d.ts +30 -0
  34. package/dist/render/compositions/InstagramDmComposition/convertPublicToProps.js +131 -0
  35. package/dist/render/compositions/InstagramDmComposition/index.d.ts +16 -0
  36. package/dist/render/compositions/InstagramDmComposition/index.js +374 -0
  37. package/dist/render/compositions/InstagramDmComposition/theme.d.ts +42 -0
  38. package/dist/render/compositions/InstagramDmComposition/theme.js +55 -0
  39. package/dist/render/compositions/InstagramDmComposition/types.d.ts +215 -0
  40. package/dist/render/compositions/InstagramDmComposition/types.js +7 -0
  41. package/dist/render/compositions/ScreenshotAnimation.d.ts +14 -0
  42. package/dist/render/compositions/ScreenshotAnimation.js +268 -0
  43. package/dist/render/compositions/VideoEditorComposition.d.ts +45 -0
  44. package/dist/render/compositions/VideoEditorComposition.js +307 -0
  45. package/dist/render/compositions/index.d.ts +12 -0
  46. package/dist/render/compositions/index.js +43 -0
  47. package/dist/render/compositions/messaging/components/MediaBubble.d.ts +22 -0
  48. package/dist/render/compositions/messaging/components/MediaBubble.js +25 -0
  49. package/dist/render/compositions/messaging/components/MessageBubble.d.ts +35 -0
  50. package/dist/render/compositions/messaging/components/MessageBubble.js +34 -0
  51. package/dist/render/compositions/messaging/components/ProfilePic.d.ts +23 -0
  52. package/dist/render/compositions/messaging/components/ProfilePic.js +37 -0
  53. package/dist/render/compositions/messaging/components/Reaction.d.ts +23 -0
  54. package/dist/render/compositions/messaging/components/Reaction.js +19 -0
  55. package/dist/render/compositions/messaging/components/TypingIndicator.d.ts +25 -0
  56. package/dist/render/compositions/messaging/components/TypingIndicator.js +66 -0
  57. package/dist/render/compositions/messaging/index.d.ts +14 -0
  58. package/dist/render/compositions/messaging/index.js +43 -0
  59. package/dist/render/compositions/messaging/types.d.ts +148 -0
  60. package/dist/render/compositions/messaging/types.js +21 -0
  61. package/dist/render/compositions/messaging/utils/bubbleRadius.d.ts +45 -0
  62. package/dist/render/compositions/messaging/utils/bubbleRadius.js +84 -0
  63. package/dist/render/compositions/messaging/utils/groupMessages.d.ts +41 -0
  64. package/dist/render/compositions/messaging/utils/groupMessages.js +110 -0
  65. package/dist/render/data/phone-top-nav.d.ts +1 -0
  66. package/dist/render/data/phone-top-nav.js +4 -0
  67. package/dist/render/data/screenshot.d.ts +164 -0
  68. package/dist/render/data/screenshot.js +63 -0
  69. package/dist/render/hooks/index.d.ts +54 -0
  70. package/dist/render/hooks/index.js +132 -0
  71. package/dist/render/index.d.ts +12 -0
  72. package/dist/render/index.js +36 -0
  73. package/dist/render/types/base.d.ts +148 -0
  74. package/dist/render/types/base.js +5 -0
  75. package/dist/render/types/caption.d.ts +105 -0
  76. package/dist/render/types/caption.js +8 -0
  77. package/dist/render/types/crop.d.ts +60 -0
  78. package/dist/render/types/crop.js +8 -0
  79. package/dist/render/types/deduplication.d.ts +284 -0
  80. package/dist/render/types/deduplication.js +240 -0
  81. package/dist/render/types/editor.d.ts +97 -0
  82. package/dist/render/types/editor.js +10 -0
  83. package/dist/render/types/element.d.ts +139 -0
  84. package/dist/render/types/element.js +19 -0
  85. package/dist/render/types/index.d.ts +20 -0
  86. package/dist/render/types/index.js +24 -0
  87. package/dist/render/types/instagram-dm-public.d.ts +60 -0
  88. package/dist/render/types/instagram-dm-public.js +8 -0
  89. package/dist/render/types/position.d.ts +59 -0
  90. package/dist/render/types/position.js +5 -0
  91. package/dist/render/types/screenshot.d.ts +57 -0
  92. package/dist/render/types/screenshot.js +34 -0
  93. package/dist/render/types/segment.d.ts +163 -0
  94. package/dist/render/types/segment.js +8 -0
  95. package/dist/render/types/video.d.ts +192 -0
  96. package/dist/render/types/video.js +14 -0
  97. package/dist/render/utils/captionPresets.d.ts +38 -0
  98. package/dist/render/utils/captionPresets.js +168 -0
  99. package/dist/render/utils/cropBounds.d.ts +20 -0
  100. package/dist/render/utils/cropBounds.js +166 -0
  101. package/dist/render/utils/defaults.d.ts +74 -0
  102. package/dist/render/utils/defaults.js +91 -0
  103. package/dist/render/utils/emoji.d.ts +40 -0
  104. package/dist/render/utils/emoji.js +105 -0
  105. package/dist/render/utils/fit.d.ts +35 -0
  106. package/dist/render/utils/fit.js +63 -0
  107. package/dist/render/utils/fonts.d.ts +55 -0
  108. package/dist/render/utils/fonts.js +191 -0
  109. package/dist/render/utils/index.d.ts +14 -0
  110. package/dist/render/utils/index.js +73 -0
  111. package/dist/render/utils/positionResolver.d.ts +64 -0
  112. package/dist/render/utils/positionResolver.js +508 -0
  113. package/dist/render/utils/text.d.ts +50 -0
  114. package/dist/render/utils/text.js +177 -0
  115. package/dist/render/utils/timeline.d.ts +62 -0
  116. package/dist/render/utils/timeline.js +172 -0
  117. package/dist/render.d.ts +1 -1
  118. package/dist/types.d.ts +137 -18
  119. package/dist/types.js +58 -0
  120. 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;