ugcinc-render 1.4.0 → 1.5.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.mts CHANGED
@@ -337,27 +337,47 @@ interface ImageEditorNodeConfig {
337
337
  }
338
338
 
339
339
  /**
340
- * Video editor types for rendering
340
+ * Video editor types for the WEBAPP UI
341
+ *
342
+ * These types include UI-only fields (marked with comments).
343
+ * The renderer uses types from editor.ts and segment.ts instead.
344
+ *
345
+ * UI-only fields are stripped before sending to renderer:
346
+ * - timeMode: UI helper for displaying timing mode
347
+ * - inputRef/textInputRef: Resolved to source/text before rendering
348
+ * - dimensionPreset: Renderer uses width/height directly
349
+ * - previewUrls/previewTextValues: Only for UI preview
341
350
  */
342
351
 
343
352
  /**
344
- * UI helper for timing mode
353
+ * UI helper for timing mode - not sent to renderer
345
354
  */
346
355
  type TimeMode = 'fixed' | 'flexible';
347
356
  /**
348
- * Base properties for all video editor segments
357
+ * Base properties for all video editor segments (UI)
349
358
  */
350
359
  interface VideoEditorBaseSegment {
360
+ /** Unique segment identifier */
351
361
  id: string;
362
+ /** Order in the channel (0-based) */
352
363
  order: number;
364
+ /** Time offset from previous segment or absolute position */
353
365
  offset: TimeValue;
366
+ /** Segment duration - required for sequential playback */
354
367
  duration?: TimeValue;
368
+ /** Trim from start in milliseconds */
355
369
  startTrim?: number;
370
+ /** Trim from end in milliseconds */
356
371
  endTrim?: number;
372
+ /** UI-only: helper for timing mode display - stripped before rendering */
357
373
  timeMode?: TimeMode;
374
+ /** Parent segment ID for overlays */
358
375
  parentId?: string;
376
+ /** Relative start (0-1) within parent for overlays */
359
377
  relativeStart?: number;
378
+ /** Relative end (0-1) within parent for overlays */
360
379
  relativeEnd?: number;
380
+ /** Fade-in duration in milliseconds */
361
381
  fadeIn?: number;
362
382
  }
363
383
  /**
@@ -374,11 +394,13 @@ interface VideoEditorVisualSegment extends VideoEditorBaseSegment {
374
394
  opacity?: number;
375
395
  }
376
396
  /**
377
- * Video segment
397
+ * Video segment (UI)
378
398
  */
379
399
  interface VideoEditorVideoSegment extends VideoEditorVisualSegment {
380
400
  type: 'video';
401
+ /** UI-only: reference to input node - resolved to source before rendering */
381
402
  inputRef?: string;
403
+ /** Actual source URL (set after inputRef is resolved) */
382
404
  source?: string;
383
405
  fit?: FitMode;
384
406
  speed?: number;
@@ -386,31 +408,37 @@ interface VideoEditorVideoSegment extends VideoEditorVisualSegment {
386
408
  borderRadius?: number | BorderRadiusConfig;
387
409
  }
388
410
  /**
389
- * Audio segment
411
+ * Audio segment (UI)
390
412
  */
391
413
  interface VideoEditorAudioSegment extends VideoEditorBaseSegment {
392
414
  type: 'audio';
415
+ /** UI-only: reference to input node - resolved to source before rendering */
393
416
  inputRef?: string;
417
+ /** Actual source URL (set after inputRef is resolved) */
394
418
  source?: string;
395
419
  volume?: number;
396
420
  }
397
421
  /**
398
- * Image segment
422
+ * Image segment (UI)
399
423
  */
400
424
  interface VideoEditorImageSegment extends VideoEditorVisualSegment {
401
425
  type: 'image';
426
+ /** UI-only: reference to input node - resolved to source before rendering */
402
427
  inputRef?: string;
428
+ /** Actual source URL (set after inputRef is resolved) */
403
429
  source?: string;
404
430
  fit?: FitMode;
405
431
  loop?: boolean;
406
432
  borderRadius?: number | BorderRadiusConfig;
407
433
  }
408
434
  /**
409
- * Text segment
435
+ * Text segment (UI)
410
436
  */
411
437
  interface VideoEditorTextSegment extends VideoEditorVisualSegment {
412
438
  type: 'text';
439
+ /** UI-only: reference to text input - resolved to text before rendering */
413
440
  textInputRef?: string;
441
+ /** Actual text content (set after textInputRef is resolved) */
414
442
  text?: string;
415
443
  alignment?: 'left' | 'center' | 'right' | 'justify';
416
444
  verticalAlign?: 'top' | 'middle' | 'bottom';
@@ -445,16 +473,25 @@ interface VideoEditorChannel {
445
473
  segments: VideoEditorSegment[];
446
474
  }
447
475
  /**
448
- * Video editor node configuration
476
+ * Video editor node configuration for WEBAPP UI
477
+ *
478
+ * Duration is NOT stored - it's calculated from segment durations.
479
+ * dimensionPreset, previewUrls, previewTextValues are UI-only.
449
480
  */
450
481
  interface VideoEditorNodeConfig {
482
+ /** Canvas width in pixels */
451
483
  width: number;
484
+ /** Canvas height in pixels */
452
485
  height: number;
486
+ /** Frames per second */
453
487
  fps: number;
454
- duration?: number;
488
+ /** UI-only: dimension preset selector */
455
489
  dimensionPreset: DimensionPresetKey;
490
+ /** Channels containing segments */
456
491
  channels: VideoEditorChannel[];
492
+ /** UI-only: preview URLs for displaying in editor */
457
493
  previewUrls?: Record<string, string>;
494
+ /** UI-only: preview text values for displaying in editor */
458
495
  previewTextValues?: Record<string, string>;
459
496
  }
460
497
  /**
@@ -644,15 +681,16 @@ type VisualSegmentUnion = VideoSegment | ImageSegment | TextSegment;
644
681
  type StaticSegment = ImageSegment | TextSegment;
645
682
 
646
683
  /**
647
- * Editor configuration types
684
+ * Editor configuration types for RENDERING
685
+ *
686
+ * These types define what the renderer receives.
687
+ * Duration is always calculated from segments - there is no duration field.
648
688
  *
649
- * Editors are the top-level containers that define the canvas dimensions,
650
- * timing, and channels containing segments.
689
+ * Note: UI-specific types (with previewUrls, dimensionPreset, etc.) are in video.ts
651
690
  */
652
691
 
653
692
  /**
654
- * A channel (track) containing segments
655
- * Channels allow for parallel playback of segments
693
+ * A channel (track) containing segments for rendering
656
694
  */
657
695
  interface Channel<T extends Segment = Segment> {
658
696
  /** Unique channel identifier */
@@ -661,17 +699,16 @@ interface Channel<T extends Segment = Segment> {
661
699
  segments: T[];
662
700
  }
663
701
  /**
664
- * Base editor configuration shared by all editor types
702
+ * Base editor configuration for rendering
703
+ * Duration is calculated from segments, not stored.
665
704
  */
666
705
  interface BaseEditorConfig {
667
706
  /** Canvas width in pixels */
668
707
  width: number;
669
708
  /** Canvas height in pixels */
670
709
  height: number;
671
- /** Frames per second (video only) */
710
+ /** Frames per second */
672
711
  fps: number;
673
- /** Total duration in milliseconds (auto-calculated if 0 or undefined) */
674
- duration?: number;
675
712
  }
676
713
  /**
677
714
  * Full editor configuration with all segment types
package/dist/index.d.ts CHANGED
@@ -337,27 +337,47 @@ interface ImageEditorNodeConfig {
337
337
  }
338
338
 
339
339
  /**
340
- * Video editor types for rendering
340
+ * Video editor types for the WEBAPP UI
341
+ *
342
+ * These types include UI-only fields (marked with comments).
343
+ * The renderer uses types from editor.ts and segment.ts instead.
344
+ *
345
+ * UI-only fields are stripped before sending to renderer:
346
+ * - timeMode: UI helper for displaying timing mode
347
+ * - inputRef/textInputRef: Resolved to source/text before rendering
348
+ * - dimensionPreset: Renderer uses width/height directly
349
+ * - previewUrls/previewTextValues: Only for UI preview
341
350
  */
342
351
 
343
352
  /**
344
- * UI helper for timing mode
353
+ * UI helper for timing mode - not sent to renderer
345
354
  */
346
355
  type TimeMode = 'fixed' | 'flexible';
347
356
  /**
348
- * Base properties for all video editor segments
357
+ * Base properties for all video editor segments (UI)
349
358
  */
350
359
  interface VideoEditorBaseSegment {
360
+ /** Unique segment identifier */
351
361
  id: string;
362
+ /** Order in the channel (0-based) */
352
363
  order: number;
364
+ /** Time offset from previous segment or absolute position */
353
365
  offset: TimeValue;
366
+ /** Segment duration - required for sequential playback */
354
367
  duration?: TimeValue;
368
+ /** Trim from start in milliseconds */
355
369
  startTrim?: number;
370
+ /** Trim from end in milliseconds */
356
371
  endTrim?: number;
372
+ /** UI-only: helper for timing mode display - stripped before rendering */
357
373
  timeMode?: TimeMode;
374
+ /** Parent segment ID for overlays */
358
375
  parentId?: string;
376
+ /** Relative start (0-1) within parent for overlays */
359
377
  relativeStart?: number;
378
+ /** Relative end (0-1) within parent for overlays */
360
379
  relativeEnd?: number;
380
+ /** Fade-in duration in milliseconds */
361
381
  fadeIn?: number;
362
382
  }
363
383
  /**
@@ -374,11 +394,13 @@ interface VideoEditorVisualSegment extends VideoEditorBaseSegment {
374
394
  opacity?: number;
375
395
  }
376
396
  /**
377
- * Video segment
397
+ * Video segment (UI)
378
398
  */
379
399
  interface VideoEditorVideoSegment extends VideoEditorVisualSegment {
380
400
  type: 'video';
401
+ /** UI-only: reference to input node - resolved to source before rendering */
381
402
  inputRef?: string;
403
+ /** Actual source URL (set after inputRef is resolved) */
382
404
  source?: string;
383
405
  fit?: FitMode;
384
406
  speed?: number;
@@ -386,31 +408,37 @@ interface VideoEditorVideoSegment extends VideoEditorVisualSegment {
386
408
  borderRadius?: number | BorderRadiusConfig;
387
409
  }
388
410
  /**
389
- * Audio segment
411
+ * Audio segment (UI)
390
412
  */
391
413
  interface VideoEditorAudioSegment extends VideoEditorBaseSegment {
392
414
  type: 'audio';
415
+ /** UI-only: reference to input node - resolved to source before rendering */
393
416
  inputRef?: string;
417
+ /** Actual source URL (set after inputRef is resolved) */
394
418
  source?: string;
395
419
  volume?: number;
396
420
  }
397
421
  /**
398
- * Image segment
422
+ * Image segment (UI)
399
423
  */
400
424
  interface VideoEditorImageSegment extends VideoEditorVisualSegment {
401
425
  type: 'image';
426
+ /** UI-only: reference to input node - resolved to source before rendering */
402
427
  inputRef?: string;
428
+ /** Actual source URL (set after inputRef is resolved) */
403
429
  source?: string;
404
430
  fit?: FitMode;
405
431
  loop?: boolean;
406
432
  borderRadius?: number | BorderRadiusConfig;
407
433
  }
408
434
  /**
409
- * Text segment
435
+ * Text segment (UI)
410
436
  */
411
437
  interface VideoEditorTextSegment extends VideoEditorVisualSegment {
412
438
  type: 'text';
439
+ /** UI-only: reference to text input - resolved to text before rendering */
413
440
  textInputRef?: string;
441
+ /** Actual text content (set after textInputRef is resolved) */
414
442
  text?: string;
415
443
  alignment?: 'left' | 'center' | 'right' | 'justify';
416
444
  verticalAlign?: 'top' | 'middle' | 'bottom';
@@ -445,16 +473,25 @@ interface VideoEditorChannel {
445
473
  segments: VideoEditorSegment[];
446
474
  }
447
475
  /**
448
- * Video editor node configuration
476
+ * Video editor node configuration for WEBAPP UI
477
+ *
478
+ * Duration is NOT stored - it's calculated from segment durations.
479
+ * dimensionPreset, previewUrls, previewTextValues are UI-only.
449
480
  */
450
481
  interface VideoEditorNodeConfig {
482
+ /** Canvas width in pixels */
451
483
  width: number;
484
+ /** Canvas height in pixels */
452
485
  height: number;
486
+ /** Frames per second */
453
487
  fps: number;
454
- duration?: number;
488
+ /** UI-only: dimension preset selector */
455
489
  dimensionPreset: DimensionPresetKey;
490
+ /** Channels containing segments */
456
491
  channels: VideoEditorChannel[];
492
+ /** UI-only: preview URLs for displaying in editor */
457
493
  previewUrls?: Record<string, string>;
494
+ /** UI-only: preview text values for displaying in editor */
458
495
  previewTextValues?: Record<string, string>;
459
496
  }
460
497
  /**
@@ -644,15 +681,16 @@ type VisualSegmentUnion = VideoSegment | ImageSegment | TextSegment;
644
681
  type StaticSegment = ImageSegment | TextSegment;
645
682
 
646
683
  /**
647
- * Editor configuration types
684
+ * Editor configuration types for RENDERING
685
+ *
686
+ * These types define what the renderer receives.
687
+ * Duration is always calculated from segments - there is no duration field.
648
688
  *
649
- * Editors are the top-level containers that define the canvas dimensions,
650
- * timing, and channels containing segments.
689
+ * Note: UI-specific types (with previewUrls, dimensionPreset, etc.) are in video.ts
651
690
  */
652
691
 
653
692
  /**
654
- * A channel (track) containing segments
655
- * Channels allow for parallel playback of segments
693
+ * A channel (track) containing segments for rendering
656
694
  */
657
695
  interface Channel<T extends Segment = Segment> {
658
696
  /** Unique channel identifier */
@@ -661,17 +699,16 @@ interface Channel<T extends Segment = Segment> {
661
699
  segments: T[];
662
700
  }
663
701
  /**
664
- * Base editor configuration shared by all editor types
702
+ * Base editor configuration for rendering
703
+ * Duration is calculated from segments, not stored.
665
704
  */
666
705
  interface BaseEditorConfig {
667
706
  /** Canvas width in pixels */
668
707
  width: number;
669
708
  /** Canvas height in pixels */
670
709
  height: number;
671
- /** Frames per second (video only) */
710
+ /** Frames per second */
672
711
  fps: number;
673
- /** Total duration in milliseconds (auto-calculated if 0 or undefined) */
674
- duration?: number;
675
712
  }
676
713
  /**
677
714
  * Full editor configuration with all segment types
package/dist/index.js CHANGED
@@ -294,6 +294,55 @@ function hexToRgba(hex, opacity = 100) {
294
294
 
295
295
  // src/components/TextElement.tsx
296
296
  var import_jsx_runtime = require("react/jsx-runtime");
297
+ function calculateAutoWidth({
298
+ text,
299
+ maxWidth,
300
+ paddingLeft,
301
+ paddingRight,
302
+ fontSize,
303
+ fontWeight,
304
+ fontFamily,
305
+ letterSpacing
306
+ }) {
307
+ if (typeof document === "undefined") {
308
+ throw new Error("calculateAutoWidth requires a browser environment with document available");
309
+ }
310
+ const canvas = document.createElement("canvas");
311
+ const ctx = canvas.getContext("2d");
312
+ if (!ctx) {
313
+ throw new Error("Failed to get 2D canvas context for text measurement");
314
+ }
315
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
316
+ const availableWidth = maxWidth - paddingLeft - paddingRight;
317
+ const words = text.split(" ");
318
+ const lines = [];
319
+ let currentLine = "";
320
+ for (const word of words) {
321
+ if (!word) continue;
322
+ const testLine = currentLine + (currentLine ? " " : "") + word;
323
+ const charCount = [...testLine].length;
324
+ const extraSpacing = charCount > 1 ? (charCount - 1) * letterSpacing : 0;
325
+ const totalWidth = ctx.measureText(testLine).width + extraSpacing;
326
+ if (totalWidth > availableWidth && currentLine) {
327
+ lines.push(currentLine);
328
+ currentLine = word;
329
+ } else {
330
+ currentLine = testLine;
331
+ }
332
+ }
333
+ if (currentLine) lines.push(currentLine);
334
+ let widestLineWidth = 0;
335
+ for (const line of lines) {
336
+ const chars = [...line];
337
+ let lineWidth = 0;
338
+ for (const char of chars) {
339
+ lineWidth += ctx.measureText(char).width + letterSpacing;
340
+ }
341
+ if (chars.length > 0) lineWidth -= letterSpacing;
342
+ widestLineWidth = Math.max(widestLineWidth, lineWidth);
343
+ }
344
+ return Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
345
+ }
297
346
  function TextElement({ segment, scale = 1 }) {
298
347
  const fontType = segment.fontType ?? TEXT_DEFAULTS.fontType;
299
348
  const fontSize = (segment.fontSize ?? TEXT_DEFAULTS.fontSize) * scale;
@@ -321,6 +370,19 @@ function TextElement({ segment, scale = 1 }) {
321
370
  const backgroundOpacity = segment.backgroundOpacity ?? TEXT_DEFAULTS.backgroundOpacity;
322
371
  const backgroundBorderRadius = segment.backgroundBorderRadius;
323
372
  const fontFamily = getFontFamily(fontType);
373
+ const calculatedWidth = (0, import_react.useMemo)(() => {
374
+ if (!autoWidth) return width;
375
+ return calculateAutoWidth({
376
+ text: segment.text,
377
+ maxWidth: width,
378
+ paddingLeft,
379
+ paddingRight,
380
+ fontSize,
381
+ fontWeight,
382
+ fontFamily,
383
+ letterSpacing
384
+ });
385
+ }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing]);
324
386
  const borderRadiusStyle = (0, import_react.useMemo)(() => {
325
387
  if (!backgroundBorderRadius) return void 0;
326
388
  const radii = getBorderRadii(backgroundBorderRadius);
@@ -344,8 +406,10 @@ function TextElement({ segment, scale = 1 }) {
344
406
  }), [x, y, width, height, rotation, verticalAlign, autoWidth, boxAlign]);
345
407
  const backgroundBoxStyle = (0, import_react.useMemo)(() => {
346
408
  const baseStyle = autoWidth ? {
347
- width: "fit-content",
348
- maxWidth: "100%"
409
+ // Use explicit calculated width instead of fit-content
410
+ // This fixes the issue where multi-line text doesn't shrink to widest line
411
+ width: calculatedWidth,
412
+ maxWidth: width
349
413
  } : {
350
414
  // When not autoWidth, let the text div handle everything
351
415
  display: "flex",
@@ -359,7 +423,7 @@ function TextElement({ segment, scale = 1 }) {
359
423
  backgroundColor: hexToRgba(backgroundColor, backgroundOpacity),
360
424
  borderRadius: borderRadiusStyle
361
425
  };
362
- }, [autoWidth, backgroundColor, backgroundOpacity, borderRadiusStyle]);
426
+ }, [autoWidth, calculatedWidth, width, backgroundColor, backgroundOpacity, borderRadiusStyle]);
363
427
  const textStyle = (0, import_react.useMemo)(() => ({
364
428
  fontFamily,
365
429
  fontSize,
@@ -1245,9 +1309,9 @@ function calculateSegmentTimings(config, fps) {
1245
1309
  const startFrame = currentFrame + offsetFrames;
1246
1310
  let durationMs;
1247
1311
  if (segment.duration) {
1248
- durationMs = segment.duration.type === "absolute" ? segment.duration.value : segment.duration.value * (config.duration ?? 1e4);
1312
+ durationMs = segment.duration.type === "absolute" ? segment.duration.value : 5e3;
1249
1313
  } else {
1250
- durationMs = (config.duration ?? 1e4) - startFrame / fps * 1e3;
1314
+ durationMs = 5e3;
1251
1315
  }
1252
1316
  const durationInFrames = Math.max(1, Math.round(durationMs / 1e3 * fps));
1253
1317
  const endFrame = startFrame + durationInFrames;
@@ -1666,7 +1730,6 @@ var defaultVideoProps = {
1666
1730
  width: 1080,
1667
1731
  height: 1920,
1668
1732
  fps: 30,
1669
- duration: 5e3,
1670
1733
  channels: []
1671
1734
  },
1672
1735
  sources: {},
package/dist/index.mjs CHANGED
@@ -220,6 +220,55 @@ function hexToRgba(hex, opacity = 100) {
220
220
 
221
221
  // src/components/TextElement.tsx
222
222
  import { jsx } from "react/jsx-runtime";
223
+ function calculateAutoWidth({
224
+ text,
225
+ maxWidth,
226
+ paddingLeft,
227
+ paddingRight,
228
+ fontSize,
229
+ fontWeight,
230
+ fontFamily,
231
+ letterSpacing
232
+ }) {
233
+ if (typeof document === "undefined") {
234
+ throw new Error("calculateAutoWidth requires a browser environment with document available");
235
+ }
236
+ const canvas = document.createElement("canvas");
237
+ const ctx = canvas.getContext("2d");
238
+ if (!ctx) {
239
+ throw new Error("Failed to get 2D canvas context for text measurement");
240
+ }
241
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
242
+ const availableWidth = maxWidth - paddingLeft - paddingRight;
243
+ const words = text.split(" ");
244
+ const lines = [];
245
+ let currentLine = "";
246
+ for (const word of words) {
247
+ if (!word) continue;
248
+ const testLine = currentLine + (currentLine ? " " : "") + word;
249
+ const charCount = [...testLine].length;
250
+ const extraSpacing = charCount > 1 ? (charCount - 1) * letterSpacing : 0;
251
+ const totalWidth = ctx.measureText(testLine).width + extraSpacing;
252
+ if (totalWidth > availableWidth && currentLine) {
253
+ lines.push(currentLine);
254
+ currentLine = word;
255
+ } else {
256
+ currentLine = testLine;
257
+ }
258
+ }
259
+ if (currentLine) lines.push(currentLine);
260
+ let widestLineWidth = 0;
261
+ for (const line of lines) {
262
+ const chars = [...line];
263
+ let lineWidth = 0;
264
+ for (const char of chars) {
265
+ lineWidth += ctx.measureText(char).width + letterSpacing;
266
+ }
267
+ if (chars.length > 0) lineWidth -= letterSpacing;
268
+ widestLineWidth = Math.max(widestLineWidth, lineWidth);
269
+ }
270
+ return Math.min(widestLineWidth + paddingLeft + paddingRight, maxWidth);
271
+ }
223
272
  function TextElement({ segment, scale = 1 }) {
224
273
  const fontType = segment.fontType ?? TEXT_DEFAULTS.fontType;
225
274
  const fontSize = (segment.fontSize ?? TEXT_DEFAULTS.fontSize) * scale;
@@ -247,6 +296,19 @@ function TextElement({ segment, scale = 1 }) {
247
296
  const backgroundOpacity = segment.backgroundOpacity ?? TEXT_DEFAULTS.backgroundOpacity;
248
297
  const backgroundBorderRadius = segment.backgroundBorderRadius;
249
298
  const fontFamily = getFontFamily(fontType);
299
+ const calculatedWidth = useMemo(() => {
300
+ if (!autoWidth) return width;
301
+ return calculateAutoWidth({
302
+ text: segment.text,
303
+ maxWidth: width,
304
+ paddingLeft,
305
+ paddingRight,
306
+ fontSize,
307
+ fontWeight,
308
+ fontFamily,
309
+ letterSpacing
310
+ });
311
+ }, [autoWidth, segment.text, width, paddingLeft, paddingRight, fontSize, fontWeight, fontFamily, letterSpacing]);
250
312
  const borderRadiusStyle = useMemo(() => {
251
313
  if (!backgroundBorderRadius) return void 0;
252
314
  const radii = getBorderRadii(backgroundBorderRadius);
@@ -270,8 +332,10 @@ function TextElement({ segment, scale = 1 }) {
270
332
  }), [x, y, width, height, rotation, verticalAlign, autoWidth, boxAlign]);
271
333
  const backgroundBoxStyle = useMemo(() => {
272
334
  const baseStyle = autoWidth ? {
273
- width: "fit-content",
274
- maxWidth: "100%"
335
+ // Use explicit calculated width instead of fit-content
336
+ // This fixes the issue where multi-line text doesn't shrink to widest line
337
+ width: calculatedWidth,
338
+ maxWidth: width
275
339
  } : {
276
340
  // When not autoWidth, let the text div handle everything
277
341
  display: "flex",
@@ -285,7 +349,7 @@ function TextElement({ segment, scale = 1 }) {
285
349
  backgroundColor: hexToRgba(backgroundColor, backgroundOpacity),
286
350
  borderRadius: borderRadiusStyle
287
351
  };
288
- }, [autoWidth, backgroundColor, backgroundOpacity, borderRadiusStyle]);
352
+ }, [autoWidth, calculatedWidth, width, backgroundColor, backgroundOpacity, borderRadiusStyle]);
289
353
  const textStyle = useMemo(() => ({
290
354
  fontFamily,
291
355
  fontSize,
@@ -1171,9 +1235,9 @@ function calculateSegmentTimings(config, fps) {
1171
1235
  const startFrame = currentFrame + offsetFrames;
1172
1236
  let durationMs;
1173
1237
  if (segment.duration) {
1174
- durationMs = segment.duration.type === "absolute" ? segment.duration.value : segment.duration.value * (config.duration ?? 1e4);
1238
+ durationMs = segment.duration.type === "absolute" ? segment.duration.value : 5e3;
1175
1239
  } else {
1176
- durationMs = (config.duration ?? 1e4) - startFrame / fps * 1e3;
1240
+ durationMs = 5e3;
1177
1241
  }
1178
1242
  const durationInFrames = Math.max(1, Math.round(durationMs / 1e3 * fps));
1179
1243
  const endFrame = startFrame + durationInFrames;
@@ -1592,7 +1656,6 @@ var defaultVideoProps = {
1592
1656
  width: 1080,
1593
1657
  height: 1920,
1594
1658
  fps: 30,
1595
- duration: 5e3,
1596
1659
  channels: []
1597
1660
  },
1598
1661
  sources: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc-render",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
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",