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,210 @@
|
|
|
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.CaptionOverlay = CaptionOverlay;
|
|
37
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
|
+
/**
|
|
39
|
+
* CaptionOverlay Component
|
|
40
|
+
*
|
|
41
|
+
* Renders TikTok-style animated captions over video content.
|
|
42
|
+
* Features:
|
|
43
|
+
* - Word-by-word highlighting based on current playback time
|
|
44
|
+
* - Proper text wrapping within configurable maxWidth bounds
|
|
45
|
+
* - Vertical positioning (top/center/bottom) with offset
|
|
46
|
+
* - Optional background with opacity and border radius
|
|
47
|
+
* - Optional bounce animation on active word
|
|
48
|
+
*
|
|
49
|
+
* This component is designed for use in both:
|
|
50
|
+
* - Remotion compositions (server-side rendering)
|
|
51
|
+
* - Client-side preview (with previewTimeMs override)
|
|
52
|
+
*/
|
|
53
|
+
const react_1 = __importStar(require("react"));
|
|
54
|
+
const remotion_1 = require("remotion");
|
|
55
|
+
/**
|
|
56
|
+
* Group words into pages based on wordsPerPage
|
|
57
|
+
*
|
|
58
|
+
* Each page contains up to wordsPerPage words and spans
|
|
59
|
+
* from the first word's start time to the last word's end time.
|
|
60
|
+
*/
|
|
61
|
+
function createPages(words, wordsPerPage) {
|
|
62
|
+
const pages = [];
|
|
63
|
+
for (let i = 0; i < words.length; i += wordsPerPage) {
|
|
64
|
+
const pageWords = words.slice(i, i + wordsPerPage);
|
|
65
|
+
const firstWord = pageWords[0];
|
|
66
|
+
const lastWord = pageWords[pageWords.length - 1];
|
|
67
|
+
if (firstWord && lastWord) {
|
|
68
|
+
pages.push({
|
|
69
|
+
words: pageWords,
|
|
70
|
+
startMs: firstWord.startMs,
|
|
71
|
+
endMs: lastWord.endMs,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return pages;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Convert hex color to rgba with opacity
|
|
79
|
+
*/
|
|
80
|
+
function hexToRgba(hex, opacity) {
|
|
81
|
+
const cleanHex = hex.replace('#', '');
|
|
82
|
+
const r = parseInt(cleanHex.substring(0, 2), 16);
|
|
83
|
+
const g = parseInt(cleanHex.substring(2, 4), 16);
|
|
84
|
+
const b = parseInt(cleanHex.substring(4, 6), 16);
|
|
85
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* CaptionOverlay renders animated captions synchronized with video playback
|
|
89
|
+
*/
|
|
90
|
+
function CaptionOverlay({ captions, style, previewTimeMs }) {
|
|
91
|
+
const frame = (0, remotion_1.useCurrentFrame)();
|
|
92
|
+
const { fps, width, height } = (0, remotion_1.useVideoConfig)();
|
|
93
|
+
// Calculate current time in milliseconds
|
|
94
|
+
// Use previewTimeMs if provided (for client preview), otherwise derive from frame
|
|
95
|
+
const currentMs = previewTimeMs ?? (frame / fps) * 1000;
|
|
96
|
+
// Create pages from words based on wordsPerPage setting
|
|
97
|
+
const pages = (0, react_1.useMemo)(() => createPages(captions, style.wordsPerPage), [captions, style.wordsPerPage]);
|
|
98
|
+
// Find the page that should be displayed at the current time
|
|
99
|
+
const currentPage = pages.find(page => currentMs >= page.startMs && currentMs <= page.endMs);
|
|
100
|
+
// Don't render anything if no page is active
|
|
101
|
+
if (!currentPage)
|
|
102
|
+
return null;
|
|
103
|
+
// Calculate max width in pixels from percentage
|
|
104
|
+
const maxWidthPx = (style.maxWidth / 100) * width;
|
|
105
|
+
const horizontalMargin = (width - maxWidthPx) / 2;
|
|
106
|
+
console.log(`[CaptionOverlay] style.maxWidth: ${style.maxWidth}%, video width: ${width}px, calculated maxWidthPx: ${maxWidthPx}px`);
|
|
107
|
+
// Scale values based on video height (designed for 1920p)
|
|
108
|
+
const scaleFactor = height / 1920;
|
|
109
|
+
const scaledFontSize = style.fontSize * scaleFactor;
|
|
110
|
+
const scaledStrokeWidth = style.strokeWidth * scaleFactor;
|
|
111
|
+
const scaledYOffset = style.yOffset * scaleFactor;
|
|
112
|
+
const scaledBorderRadius = (style.backgroundBorderRadius ?? 0) * scaleFactor;
|
|
113
|
+
// Build position styles based on vertical position setting
|
|
114
|
+
// Uses left/right margins instead of transform for accurate edge alignment
|
|
115
|
+
const getPositionStyles = () => {
|
|
116
|
+
const base = {
|
|
117
|
+
position: 'absolute',
|
|
118
|
+
left: horizontalMargin,
|
|
119
|
+
right: horizontalMargin,
|
|
120
|
+
};
|
|
121
|
+
switch (style.position) {
|
|
122
|
+
case 'top':
|
|
123
|
+
return {
|
|
124
|
+
...base,
|
|
125
|
+
top: scaledYOffset,
|
|
126
|
+
};
|
|
127
|
+
case 'center':
|
|
128
|
+
return {
|
|
129
|
+
...base,
|
|
130
|
+
top: '50%',
|
|
131
|
+
transform: 'translateY(-50%)',
|
|
132
|
+
};
|
|
133
|
+
case 'bottom':
|
|
134
|
+
default:
|
|
135
|
+
return {
|
|
136
|
+
...base,
|
|
137
|
+
bottom: scaledYOffset,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
// Container styles
|
|
142
|
+
const containerStyles = {
|
|
143
|
+
...getPositionStyles(),
|
|
144
|
+
textAlign: 'center',
|
|
145
|
+
// Add padding if background is enabled
|
|
146
|
+
padding: style.backgroundColor
|
|
147
|
+
? `${scaledFontSize * 0.3}px ${scaledFontSize * 0.5}px`
|
|
148
|
+
: 0,
|
|
149
|
+
// Background styling
|
|
150
|
+
backgroundColor: style.backgroundColor
|
|
151
|
+
? hexToRgba(style.backgroundColor, style.backgroundOpacity ?? 0.7)
|
|
152
|
+
: 'transparent',
|
|
153
|
+
borderRadius: scaledBorderRadius,
|
|
154
|
+
};
|
|
155
|
+
// Convert font weight to numeric value
|
|
156
|
+
const numericFontWeight = style.fontWeight === 'black' ? 900 : style.fontWeight === 'bold' ? 700 : 400;
|
|
157
|
+
// Build text stroke shadow
|
|
158
|
+
const buildStrokeShadow = () => {
|
|
159
|
+
if (scaledStrokeWidth <= 0)
|
|
160
|
+
return 'none';
|
|
161
|
+
const sw = scaledStrokeWidth;
|
|
162
|
+
const sc = style.strokeColor;
|
|
163
|
+
// Create a multi-layer shadow for stroke effect
|
|
164
|
+
return `
|
|
165
|
+
-${sw}px -${sw}px 0 ${sc},
|
|
166
|
+
${sw}px -${sw}px 0 ${sc},
|
|
167
|
+
-${sw}px ${sw}px 0 ${sc},
|
|
168
|
+
${sw}px ${sw}px 0 ${sc},
|
|
169
|
+
0 -${sw}px 0 ${sc},
|
|
170
|
+
0 ${sw}px 0 ${sc},
|
|
171
|
+
-${sw}px 0 0 ${sc},
|
|
172
|
+
${sw}px 0 0 ${sc}
|
|
173
|
+
`;
|
|
174
|
+
};
|
|
175
|
+
// Text container styles
|
|
176
|
+
const textContainerStyles = {
|
|
177
|
+
fontFamily: `"${style.fontName}", sans-serif`,
|
|
178
|
+
fontSize: scaledFontSize,
|
|
179
|
+
fontWeight: numericFontWeight,
|
|
180
|
+
lineHeight: 1.2,
|
|
181
|
+
wordBreak: 'break-word',
|
|
182
|
+
textShadow: buildStrokeShadow(),
|
|
183
|
+
};
|
|
184
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: containerStyles, children: (0, jsx_runtime_1.jsx)("div", { style: textContainerStyles, children: currentPage.words.map((word, index) => {
|
|
185
|
+
// Determine if this word is currently being spoken
|
|
186
|
+
const isActive = currentMs >= word.startMs && currentMs <= word.endMs;
|
|
187
|
+
// Select color based on active state
|
|
188
|
+
const color = isActive ? style.highlightColor : style.fontColor;
|
|
189
|
+
// Calculate animation scale for active word
|
|
190
|
+
let scale = 1;
|
|
191
|
+
if (style.enableAnimation && isActive) {
|
|
192
|
+
const wordDuration = word.endMs - word.startMs;
|
|
193
|
+
if (wordDuration > 0) {
|
|
194
|
+
const progress = (currentMs - word.startMs) / wordDuration;
|
|
195
|
+
// Bounce effect: scale up quickly then settle back
|
|
196
|
+
scale = (0, remotion_1.interpolate)(progress, [0, 0.15, 0.3, 1], [1, 1.15, 1.05, 1], {
|
|
197
|
+
extrapolateLeft: 'clamp',
|
|
198
|
+
extrapolateRight: 'clamp',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { style: {
|
|
203
|
+
color,
|
|
204
|
+
display: 'inline-block',
|
|
205
|
+
transform: `scale(${scale})`,
|
|
206
|
+
transformOrigin: 'center center',
|
|
207
|
+
}, children: word.word }), index < currentPage.words.length - 1 && ' '] }, `${word.word}-${word.startMs}-${index}`));
|
|
208
|
+
}) }) }));
|
|
209
|
+
}
|
|
210
|
+
exports.default = CaptionOverlay;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImageElement Component
|
|
3
|
+
*
|
|
4
|
+
* Renders an image segment with fit modes, opacity, rotation, border radius, and fade-in.
|
|
5
|
+
* Uses CSS object-fit for consistent behavior between client and server.
|
|
6
|
+
*/
|
|
7
|
+
import type { ImageSegment } from '../types';
|
|
8
|
+
export interface ImageElementProps {
|
|
9
|
+
segment: ImageSegment;
|
|
10
|
+
/** The source URL of the image */
|
|
11
|
+
src: string;
|
|
12
|
+
/** Start frame of this segment in the composition (needed for fade-in) */
|
|
13
|
+
startFrame?: number;
|
|
14
|
+
/** Optional scale for high-DPI rendering */
|
|
15
|
+
scale?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* ImageElement renders images with:
|
|
19
|
+
* - Fit modes (cover, contain, fill)
|
|
20
|
+
* - Opacity
|
|
21
|
+
* - Rotation
|
|
22
|
+
* - Border radius (uniform or per-corner)
|
|
23
|
+
* - Fade-in effect
|
|
24
|
+
*/
|
|
25
|
+
export declare function ImageElement({ segment, src, startFrame, scale }: ImageElementProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export default ImageElement;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImageElement = ImageElement;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* ImageElement Component
|
|
7
|
+
*
|
|
8
|
+
* Renders an image segment with fit modes, opacity, rotation, border radius, and fade-in.
|
|
9
|
+
* Uses CSS object-fit for consistent behavior between client and server.
|
|
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
|
+
* ImageElement renders images with:
|
|
23
|
+
* - Fit modes (cover, contain, fill)
|
|
24
|
+
* - Opacity
|
|
25
|
+
* - Rotation
|
|
26
|
+
* - Border radius (uniform or per-corner)
|
|
27
|
+
* - Fade-in effect
|
|
28
|
+
*/
|
|
29
|
+
function ImageElement({ segment, src, startFrame = 0, scale = 1 }) {
|
|
30
|
+
const frame = (0, remotion_1.useCurrentFrame)();
|
|
31
|
+
const { fps } = (0, remotion_1.useVideoConfig)();
|
|
32
|
+
// Apply defaults
|
|
33
|
+
const fit = segment.fit ?? defaults_1.IMAGE_DEFAULTS.fit;
|
|
34
|
+
const opacity = (segment.opacity ?? defaults_1.VISUAL_DEFAULTS.opacity) / 100;
|
|
35
|
+
const rotation = segment.rotation ?? 0;
|
|
36
|
+
const borderRadius = segment.borderRadius;
|
|
37
|
+
const fadeIn = segment.fadeIn ?? 0;
|
|
38
|
+
// Position and size
|
|
39
|
+
const x = segment.xOffset * scale;
|
|
40
|
+
const y = segment.yOffset * scale;
|
|
41
|
+
const width = segment.width * scale;
|
|
42
|
+
const height = segment.height * scale;
|
|
43
|
+
// Calculate fade-in opacity
|
|
44
|
+
const fadeOpacity = (0, react_1.useMemo)(() => {
|
|
45
|
+
if (fadeIn <= 0)
|
|
46
|
+
return 1;
|
|
47
|
+
const framesFromStart = frame - startFrame;
|
|
48
|
+
const fadeInFrames = (fadeIn / 1000) * fps;
|
|
49
|
+
if (framesFromStart >= fadeInFrames)
|
|
50
|
+
return 1;
|
|
51
|
+
if (framesFromStart <= 0)
|
|
52
|
+
return 0;
|
|
53
|
+
return framesFromStart / fadeInFrames;
|
|
54
|
+
}, [frame, startFrame, fadeIn, fps]);
|
|
55
|
+
// Calculate border radius CSS
|
|
56
|
+
const borderRadiusStyle = (0, react_1.useMemo)(() => {
|
|
57
|
+
if (!borderRadius)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (typeof borderRadius === 'number') {
|
|
60
|
+
return `${borderRadius * scale}px`;
|
|
61
|
+
}
|
|
62
|
+
const radii = (0, text_1.getBorderRadii)(borderRadius);
|
|
63
|
+
if (!radii || radii.length < 4)
|
|
64
|
+
return undefined;
|
|
65
|
+
return `${radii[0] * scale}px ${radii[1] * scale}px ${radii[2] * scale}px ${radii[3] * scale}px`;
|
|
66
|
+
}, [borderRadius, scale]);
|
|
67
|
+
// Container style
|
|
68
|
+
const containerStyle = (0, react_1.useMemo)(() => ({
|
|
69
|
+
position: 'absolute',
|
|
70
|
+
left: x,
|
|
71
|
+
top: y,
|
|
72
|
+
width,
|
|
73
|
+
height,
|
|
74
|
+
transform: rotation !== 0 ? `rotate(${rotation}deg)` : undefined,
|
|
75
|
+
transformOrigin: 'center center',
|
|
76
|
+
overflow: 'hidden',
|
|
77
|
+
borderRadius: borderRadiusStyle,
|
|
78
|
+
opacity: opacity * fadeOpacity,
|
|
79
|
+
}), [x, y, width, height, rotation, borderRadiusStyle, opacity, fadeOpacity]);
|
|
80
|
+
// Image style
|
|
81
|
+
const imageStyle = (0, react_1.useMemo)(() => ({
|
|
82
|
+
width: '100%',
|
|
83
|
+
height: '100%',
|
|
84
|
+
objectFit: fitModeToCss(fit),
|
|
85
|
+
}), [fit]);
|
|
86
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: containerStyle, children: (0, jsx_runtime_1.jsx)(remotion_1.Img, { src: src, style: imageStyle }) }));
|
|
87
|
+
}
|
|
88
|
+
exports.default = ImageElement;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextElement Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a text segment using HTML/CSS for pixel-perfect consistency
|
|
5
|
+
* between client preview and server render.
|
|
6
|
+
*/
|
|
7
|
+
import type { TextSegment } from '../types';
|
|
8
|
+
export interface TextElementProps {
|
|
9
|
+
segment: TextSegment;
|
|
10
|
+
/** Optional scale for high-DPI rendering */
|
|
11
|
+
scale?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* TextElement renders text with full styling support including:
|
|
15
|
+
* - Font family, size, weight, color
|
|
16
|
+
* - Letter spacing, line height
|
|
17
|
+
* - Horizontal and vertical alignment
|
|
18
|
+
* - Text stroke/outline
|
|
19
|
+
* - Background with opacity and border radius
|
|
20
|
+
* - Padding (uniform and individual)
|
|
21
|
+
* - Auto-width with box alignment
|
|
22
|
+
* - Rotation
|
|
23
|
+
*
|
|
24
|
+
* When autoWidth is enabled:
|
|
25
|
+
* - Text wraps at maxWidth (the element's width)
|
|
26
|
+
* - Background shrinks to the widest line
|
|
27
|
+
* - boxAlign positions the box within the maxWidth container
|
|
28
|
+
*/
|
|
29
|
+
export declare function TextElement({ segment, scale }: TextElementProps): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export default TextElement;
|