ugcinc-render 1.5.20 → 1.5.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -916,6 +916,8 @@ interface ImageElementProps {
916
916
  segment: ImageSegment;
917
917
  /** The source URL of the image */
918
918
  src: string;
919
+ /** Start frame of this segment in the composition (needed for fade-in) */
920
+ startFrame?: number;
919
921
  /** Optional scale for high-DPI rendering */
920
922
  scale?: number;
921
923
  }
@@ -925,8 +927,9 @@ interface ImageElementProps {
925
927
  * - Opacity
926
928
  * - Rotation
927
929
  * - Border radius (uniform or per-corner)
930
+ * - Fade-in effect
928
931
  */
929
- declare function ImageElement({ segment, src, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
932
+ declare function ImageElement({ segment, src, startFrame, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
930
933
 
931
934
  interface VideoElementProps {
932
935
  segment: VideoSegment;
package/dist/index.d.ts CHANGED
@@ -916,6 +916,8 @@ interface ImageElementProps {
916
916
  segment: ImageSegment;
917
917
  /** The source URL of the image */
918
918
  src: string;
919
+ /** Start frame of this segment in the composition (needed for fade-in) */
920
+ startFrame?: number;
919
921
  /** Optional scale for high-DPI rendering */
920
922
  scale?: number;
921
923
  }
@@ -925,8 +927,9 @@ interface ImageElementProps {
925
927
  * - Opacity
926
928
  * - Rotation
927
929
  * - Border radius (uniform or per-corner)
930
+ * - Fade-in effect
928
931
  */
929
- declare function ImageElement({ segment, src, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
932
+ declare function ImageElement({ segment, src, startFrame, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
930
933
 
931
934
  interface VideoElementProps {
932
935
  segment: VideoSegment;
package/dist/index.js CHANGED
@@ -717,15 +717,26 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
717
717
  function fitModeToCss(fit) {
718
718
  return fit;
719
719
  }
720
- function ImageElement({ segment, src, scale = 1 }) {
720
+ function ImageElement({ segment, src, startFrame = 0, scale = 1 }) {
721
+ const frame = (0, import_remotion.useCurrentFrame)();
722
+ const { fps } = (0, import_remotion.useVideoConfig)();
721
723
  const fit = segment.fit ?? IMAGE_DEFAULTS.fit;
722
724
  const opacity = (segment.opacity ?? VISUAL_DEFAULTS.opacity) / 100;
723
725
  const rotation = segment.rotation ?? 0;
724
726
  const borderRadius = segment.borderRadius;
727
+ const fadeIn = segment.fadeIn ?? 0;
725
728
  const x = segment.xOffset * scale;
726
729
  const y = segment.yOffset * scale;
727
730
  const width = segment.width * scale;
728
731
  const height = segment.height * scale;
732
+ const fadeOpacity = (0, import_react2.useMemo)(() => {
733
+ if (fadeIn <= 0) return 1;
734
+ const framesFromStart = frame - startFrame;
735
+ const fadeInFrames = fadeIn / 1e3 * fps;
736
+ if (framesFromStart >= fadeInFrames) return 1;
737
+ if (framesFromStart <= 0) return 0;
738
+ return framesFromStart / fadeInFrames;
739
+ }, [frame, startFrame, fadeIn, fps]);
729
740
  const borderRadiusStyle = (0, import_react2.useMemo)(() => {
730
741
  if (!borderRadius) return void 0;
731
742
  if (typeof borderRadius === "number") {
@@ -744,14 +755,14 @@ function ImageElement({ segment, src, scale = 1 }) {
744
755
  transform: rotation !== 0 ? `rotate(${rotation}deg)` : void 0,
745
756
  transformOrigin: "center center",
746
757
  overflow: "hidden",
747
- borderRadius: borderRadiusStyle
748
- }), [x, y, width, height, rotation, borderRadiusStyle]);
758
+ borderRadius: borderRadiusStyle,
759
+ opacity: opacity * fadeOpacity
760
+ }), [x, y, width, height, rotation, borderRadiusStyle, opacity, fadeOpacity]);
749
761
  const imageStyle = (0, import_react2.useMemo)(() => ({
750
762
  width: "100%",
751
763
  height: "100%",
752
- objectFit: fitModeToCss(fit),
753
- opacity
754
- }), [fit, opacity]);
764
+ objectFit: fitModeToCss(fit)
765
+ }), [fit]);
755
766
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
756
767
  import_remotion.Img,
757
768
  {
@@ -1320,17 +1331,42 @@ function ImageEditorComposition({
1320
1331
  if (typeof document !== "undefined" && document.fonts) {
1321
1332
  console.log("[Composition] Waiting for fonts to load...");
1322
1333
  await document.fonts.ready;
1323
- const fontChecks = await Promise.all([
1324
- document.fonts.load('normal 48px "SF Pro"').catch(() => []),
1325
- document.fonts.load('bold 48px "SF Pro"').catch(() => []),
1326
- document.fonts.load('normal 48px "TikTok Sans"').catch(() => []),
1327
- document.fonts.load('bold 48px "TikTok Sans"').catch(() => [])
1328
- ]);
1334
+ const fontsToLoad = [
1335
+ // SF Pro
1336
+ 'normal 48px "SF Pro"',
1337
+ 'bold 48px "SF Pro"',
1338
+ '100 48px "SF Pro"',
1339
+ '200 48px "SF Pro"',
1340
+ '300 48px "SF Pro"',
1341
+ '400 48px "SF Pro"',
1342
+ '500 48px "SF Pro"',
1343
+ '600 48px "SF Pro"',
1344
+ '700 48px "SF Pro"',
1345
+ '800 48px "SF Pro"',
1346
+ '900 48px "SF Pro"',
1347
+ // TikTok Sans
1348
+ 'normal 48px "TikTok Sans"',
1349
+ 'bold 48px "TikTok Sans"',
1350
+ '400 48px "TikTok Sans"',
1351
+ '500 48px "TikTok Sans"',
1352
+ '600 48px "TikTok Sans"',
1353
+ '700 48px "TikTok Sans"',
1354
+ '800 48px "TikTok Sans"',
1355
+ '900 48px "TikTok Sans"',
1356
+ // Apple Color Emoji
1357
+ 'normal 48px "Apple Color Emoji"'
1358
+ ];
1359
+ await Promise.all(
1360
+ fontsToLoad.map(
1361
+ (font) => document.fonts.load(font).catch(() => [])
1362
+ )
1363
+ );
1364
+ await document.fonts.ready;
1329
1365
  console.log("[Composition] Fonts loaded:", {
1330
- sfProNormal: document.fonts.check('normal 48px "SF Pro"'),
1331
- sfProBold: document.fonts.check('bold 48px "SF Pro"'),
1332
- tiktokNormal: document.fonts.check('normal 48px "TikTok Sans"'),
1333
- tiktokBold: document.fonts.check('bold 48px "TikTok Sans"')
1366
+ sfPro: document.fonts.check('normal 48px "SF Pro"'),
1367
+ tiktok: document.fonts.check('normal 48px "TikTok Sans"'),
1368
+ emoji: document.fonts.check('normal 48px "Apple Color Emoji"'),
1369
+ totalFonts: document.fonts.size
1334
1370
  });
1335
1371
  }
1336
1372
  setFontsLoaded(true);
@@ -1709,6 +1745,7 @@ function VideoEditorComposition({
1709
1745
  {
1710
1746
  segment,
1711
1747
  src,
1748
+ startFrame,
1712
1749
  scale: 1
1713
1750
  },
1714
1751
  segment.id
package/dist/index.mjs CHANGED
@@ -626,20 +626,31 @@ function TextElement({ segment, scale = 1 }) {
626
626
 
627
627
  // src/components/ImageElement.tsx
628
628
  import { useMemo as useMemo2 } from "react";
629
- import { Img } from "remotion";
629
+ import { Img, useCurrentFrame, useVideoConfig } from "remotion";
630
630
  import { jsx as jsx2 } from "react/jsx-runtime";
631
631
  function fitModeToCss(fit) {
632
632
  return fit;
633
633
  }
634
- function ImageElement({ segment, src, scale = 1 }) {
634
+ function ImageElement({ segment, src, startFrame = 0, scale = 1 }) {
635
+ const frame = useCurrentFrame();
636
+ const { fps } = useVideoConfig();
635
637
  const fit = segment.fit ?? IMAGE_DEFAULTS.fit;
636
638
  const opacity = (segment.opacity ?? VISUAL_DEFAULTS.opacity) / 100;
637
639
  const rotation = segment.rotation ?? 0;
638
640
  const borderRadius = segment.borderRadius;
641
+ const fadeIn = segment.fadeIn ?? 0;
639
642
  const x = segment.xOffset * scale;
640
643
  const y = segment.yOffset * scale;
641
644
  const width = segment.width * scale;
642
645
  const height = segment.height * scale;
646
+ const fadeOpacity = useMemo2(() => {
647
+ if (fadeIn <= 0) return 1;
648
+ const framesFromStart = frame - startFrame;
649
+ const fadeInFrames = fadeIn / 1e3 * fps;
650
+ if (framesFromStart >= fadeInFrames) return 1;
651
+ if (framesFromStart <= 0) return 0;
652
+ return framesFromStart / fadeInFrames;
653
+ }, [frame, startFrame, fadeIn, fps]);
643
654
  const borderRadiusStyle = useMemo2(() => {
644
655
  if (!borderRadius) return void 0;
645
656
  if (typeof borderRadius === "number") {
@@ -658,14 +669,14 @@ function ImageElement({ segment, src, scale = 1 }) {
658
669
  transform: rotation !== 0 ? `rotate(${rotation}deg)` : void 0,
659
670
  transformOrigin: "center center",
660
671
  overflow: "hidden",
661
- borderRadius: borderRadiusStyle
662
- }), [x, y, width, height, rotation, borderRadiusStyle]);
672
+ borderRadius: borderRadiusStyle,
673
+ opacity: opacity * fadeOpacity
674
+ }), [x, y, width, height, rotation, borderRadiusStyle, opacity, fadeOpacity]);
663
675
  const imageStyle = useMemo2(() => ({
664
676
  width: "100%",
665
677
  height: "100%",
666
- objectFit: fitModeToCss(fit),
667
- opacity
668
- }), [fit, opacity]);
678
+ objectFit: fitModeToCss(fit)
679
+ }), [fit]);
669
680
  return /* @__PURE__ */ jsx2("div", { style: containerStyle, children: /* @__PURE__ */ jsx2(
670
681
  Img,
671
682
  {
@@ -1234,17 +1245,42 @@ function ImageEditorComposition({
1234
1245
  if (typeof document !== "undefined" && document.fonts) {
1235
1246
  console.log("[Composition] Waiting for fonts to load...");
1236
1247
  await document.fonts.ready;
1237
- const fontChecks = await Promise.all([
1238
- document.fonts.load('normal 48px "SF Pro"').catch(() => []),
1239
- document.fonts.load('bold 48px "SF Pro"').catch(() => []),
1240
- document.fonts.load('normal 48px "TikTok Sans"').catch(() => []),
1241
- document.fonts.load('bold 48px "TikTok Sans"').catch(() => [])
1242
- ]);
1248
+ const fontsToLoad = [
1249
+ // SF Pro
1250
+ 'normal 48px "SF Pro"',
1251
+ 'bold 48px "SF Pro"',
1252
+ '100 48px "SF Pro"',
1253
+ '200 48px "SF Pro"',
1254
+ '300 48px "SF Pro"',
1255
+ '400 48px "SF Pro"',
1256
+ '500 48px "SF Pro"',
1257
+ '600 48px "SF Pro"',
1258
+ '700 48px "SF Pro"',
1259
+ '800 48px "SF Pro"',
1260
+ '900 48px "SF Pro"',
1261
+ // TikTok Sans
1262
+ 'normal 48px "TikTok Sans"',
1263
+ 'bold 48px "TikTok Sans"',
1264
+ '400 48px "TikTok Sans"',
1265
+ '500 48px "TikTok Sans"',
1266
+ '600 48px "TikTok Sans"',
1267
+ '700 48px "TikTok Sans"',
1268
+ '800 48px "TikTok Sans"',
1269
+ '900 48px "TikTok Sans"',
1270
+ // Apple Color Emoji
1271
+ 'normal 48px "Apple Color Emoji"'
1272
+ ];
1273
+ await Promise.all(
1274
+ fontsToLoad.map(
1275
+ (font) => document.fonts.load(font).catch(() => [])
1276
+ )
1277
+ );
1278
+ await document.fonts.ready;
1243
1279
  console.log("[Composition] Fonts loaded:", {
1244
- sfProNormal: document.fonts.check('normal 48px "SF Pro"'),
1245
- sfProBold: document.fonts.check('bold 48px "SF Pro"'),
1246
- tiktokNormal: document.fonts.check('normal 48px "TikTok Sans"'),
1247
- tiktokBold: document.fonts.check('bold 48px "TikTok Sans"')
1280
+ sfPro: document.fonts.check('normal 48px "SF Pro"'),
1281
+ tiktok: document.fonts.check('normal 48px "TikTok Sans"'),
1282
+ emoji: document.fonts.check('normal 48px "Apple Color Emoji"'),
1283
+ totalFonts: document.fonts.size
1248
1284
  });
1249
1285
  }
1250
1286
  setFontsLoaded(true);
@@ -1417,11 +1453,11 @@ function BackgroundImage({
1417
1453
 
1418
1454
  // src/compositions/VideoEditorComposition.tsx
1419
1455
  import { useMemo as useMemo5 } from "react";
1420
- import { AbsoluteFill as AbsoluteFill2, useCurrentFrame as useCurrentFrame2, useVideoConfig as useVideoConfig2, Sequence, Audio } from "remotion";
1456
+ import { AbsoluteFill as AbsoluteFill2, useCurrentFrame as useCurrentFrame3, useVideoConfig as useVideoConfig3, Sequence, Audio } from "remotion";
1421
1457
 
1422
1458
  // src/components/VideoElement.tsx
1423
1459
  import { useMemo as useMemo4 } from "react";
1424
- import { Video, useCurrentFrame, useVideoConfig } from "remotion";
1460
+ import { Video, useCurrentFrame as useCurrentFrame2, useVideoConfig as useVideoConfig2 } from "remotion";
1425
1461
  import { jsx as jsx4 } from "react/jsx-runtime";
1426
1462
  function fitModeToCss2(fit) {
1427
1463
  return fit;
@@ -1433,8 +1469,8 @@ function VideoElement({
1433
1469
  durationInFrames,
1434
1470
  scale = 1
1435
1471
  }) {
1436
- const frame = useCurrentFrame();
1437
- const { fps } = useVideoConfig();
1472
+ const frame = useCurrentFrame2();
1473
+ const { fps } = useVideoConfig2();
1438
1474
  const fit = segment.fit ?? VIDEO_DEFAULTS.fit;
1439
1475
  const speed = segment.speed ?? VIDEO_DEFAULTS.speed;
1440
1476
  const volume = (segment.volume ?? VIDEO_DEFAULTS.volume) / 100;
@@ -1554,8 +1590,8 @@ function VideoEditorComposition({
1554
1590
  sources = {},
1555
1591
  textContent = {}
1556
1592
  }) {
1557
- const frame = useCurrentFrame2();
1558
- const { fps, durationInFrames } = useVideoConfig2();
1593
+ const frame = useCurrentFrame3();
1594
+ const { fps, durationInFrames } = useVideoConfig3();
1559
1595
  const segmentTimings = useMemo5(
1560
1596
  () => calculateSegmentTimings(config, fps),
1561
1597
  [config, fps]
@@ -1623,6 +1659,7 @@ function VideoEditorComposition({
1623
1659
  {
1624
1660
  segment,
1625
1661
  src,
1662
+ startFrame,
1626
1663
  scale: 1
1627
1664
  },
1628
1665
  segment.id
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.5.20",
3
+ "version": "1.5.22",
4
4
  "description": "Unified rendering package for UGC Inc - shared types, components, and compositions for pixel-perfect client/server rendering",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",