@remotion/studio 4.0.388 → 4.0.390

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.
@@ -41694,7 +41694,7 @@ var RenderModalWithLoader = (props) => {
41694
41694
  import { getDefaultOutLocation as getDefaultOutLocation2 } from "@remotion/studio-shared";
41695
41695
 
41696
41696
  // ../web-renderer/dist/esm/index.mjs
41697
- import { Internals as Internals310 } from "remotion";
41697
+ import { Internals as Internals410 } from "remotion";
41698
41698
  import { NoReactInternals as NoReactInternals16 } from "remotion/no-react";
41699
41699
  import { createRef as createRef12 } from "react";
41700
41700
  import { flushSync as flushSync2 } from "react-dom";
@@ -41705,8 +41705,9 @@ import { flushSync } from "react-dom";
41705
41705
  import { Internals as Internals62 } from "remotion";
41706
41706
  import { jsx as jsx257 } from "react/jsx-runtime";
41707
41707
  import { jsx as jsx258 } from "react/jsx-runtime";
41708
+ import { Internals as Internals310 } from "remotion";
41708
41709
  import {
41709
- Internals as Internals410
41710
+ Internals as Internals510
41710
41711
  } from "remotion";
41711
41712
  /*!
41712
41713
  * Copyright (c) 2025-present, Vanilagy and contributors
@@ -50455,12 +50456,16 @@ var calculateTransforms = (element) => {
50455
50456
  const transforms = [];
50456
50457
  const toReset = [];
50457
50458
  let opacity = 1;
50459
+ let elementComputedStyle = null;
50458
50460
  while (parent) {
50459
50461
  const computedStyle = getComputedStyle(parent);
50460
50462
  const parentOpacity = computedStyle.opacity;
50461
50463
  if (parentOpacity && parentOpacity !== "") {
50462
50464
  opacity *= parseFloat(parentOpacity);
50463
50465
  }
50466
+ if (parent === element) {
50467
+ elementComputedStyle = computedStyle;
50468
+ }
50464
50469
  if (computedStyle.transform && computedStyle.transform !== "none" || parent === element) {
50465
50470
  const toParse = computedStyle.transform === "none" || computedStyle.transform === "" ? undefined : computedStyle.transform;
50466
50471
  const matrix = new DOMMatrix(toParse);
@@ -50493,6 +50498,9 @@ var calculateTransforms = (element) => {
50493
50498
  const transformMatrix = new DOMMatrix().translate(globalTransformOrigin.x, globalTransformOrigin.y).multiply(transform.matrix).translate(-globalTransformOrigin.x, -globalTransformOrigin.y);
50494
50499
  totalMatrix.multiplySelf(transformMatrix);
50495
50500
  }
50501
+ if (!elementComputedStyle) {
50502
+ throw new Error("Element computed style not found");
50503
+ }
50496
50504
  return {
50497
50505
  dimensions,
50498
50506
  totalMatrix,
@@ -50502,7 +50510,8 @@ var calculateTransforms = (element) => {
50502
50510
  }
50503
50511
  },
50504
50512
  nativeTransformOrigin,
50505
- opacity
50513
+ opacity,
50514
+ computedStyle: elementComputedStyle
50506
50515
  };
50507
50516
  };
50508
50517
  var drawBorder = ({
@@ -50609,43 +50618,12 @@ var setTransform = ({
50609
50618
  ctx.setTransform(new DOMMatrix);
50610
50619
  };
50611
50620
  };
50612
- var turnSvgIntoDrawable = (svg) => {
50613
- const originalTransform = svg.style.transform;
50614
- const originalTransformOrigin = svg.style.transformOrigin;
50615
- const originalMarginLeft = svg.style.marginLeft;
50616
- const originalMarginRight = svg.style.marginRight;
50617
- const originalMarginTop = svg.style.marginTop;
50618
- const originalMarginBottom = svg.style.marginBottom;
50619
- svg.style.transform = "none";
50620
- svg.style.transformOrigin = "";
50621
- svg.style.marginLeft = "0";
50622
- svg.style.marginRight = "0";
50623
- svg.style.marginTop = "0";
50624
- svg.style.marginBottom = "0";
50625
- const svgData = new XMLSerializer().serializeToString(svg);
50626
- svg.style.marginLeft = originalMarginLeft;
50627
- svg.style.marginRight = originalMarginRight;
50628
- svg.style.marginTop = originalMarginTop;
50629
- svg.style.marginBottom = originalMarginBottom;
50630
- svg.style.transform = originalTransform;
50631
- svg.style.transformOrigin = originalTransformOrigin;
50632
- return new Promise((resolve, reject) => {
50633
- const image = new Image;
50634
- const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
50635
- image.onload = function() {
50636
- resolve(image);
50637
- };
50638
- image.onerror = () => {
50639
- reject(new Error("Failed to convert SVG to image"));
50640
- };
50641
- image.src = url2;
50642
- });
50643
- };
50644
50621
  var drawElementToCanvas = async ({
50645
50622
  element,
50646
- context
50623
+ context,
50624
+ draw
50647
50625
  }) => {
50648
- const { totalMatrix, reset, dimensions, opacity } = calculateTransforms(element);
50626
+ const { totalMatrix, reset, dimensions, opacity, computedStyle } = calculateTransforms(element);
50649
50627
  if (opacity === 0) {
50650
50628
  reset();
50651
50629
  return;
@@ -50654,7 +50632,6 @@ var drawElementToCanvas = async ({
50654
50632
  reset();
50655
50633
  return;
50656
50634
  }
50657
- const computedStyle = getComputedStyle(element);
50658
50635
  const background2 = computedStyle.backgroundColor;
50659
50636
  const borderRadius = parseBorderRadius({
50660
50637
  borderRadius: computedStyle.borderRadius,
@@ -50677,16 +50654,13 @@ var drawElementToCanvas = async ({
50677
50654
  ctx: context,
50678
50655
  opacity
50679
50656
  });
50680
- const drawable = element instanceof SVGSVGElement ? await turnSvgIntoDrawable(element) : element instanceof HTMLImageElement ? element : element instanceof HTMLCanvasElement ? element : null;
50681
50657
  if (background2 && background2 !== "transparent" && !(background2.startsWith("rgba") && (background2.endsWith(", 0)") || background2.endsWith(",0")))) {
50682
50658
  const originalFillStyle = context.fillStyle;
50683
50659
  context.fillStyle = background2;
50684
50660
  context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50685
50661
  context.fillStyle = originalFillStyle;
50686
50662
  }
50687
- if (drawable) {
50688
- context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50689
- }
50663
+ await draw(dimensions, computedStyle);
50690
50664
  drawBorder({
50691
50665
  ctx: context,
50692
50666
  x: dimensions.left,
@@ -50701,9 +50675,209 @@ var drawElementToCanvas = async ({
50701
50675
  finishTransform();
50702
50676
  reset();
50703
50677
  };
50678
+ var getCollapsedText = (span) => {
50679
+ const textNode = span.firstChild;
50680
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
50681
+ throw new Error("Span must contain a single text node");
50682
+ }
50683
+ const originalText = textNode.textContent || "";
50684
+ let collapsedText = originalText;
50685
+ const measureWidth = (text3) => {
50686
+ textNode.textContent = text3;
50687
+ return span.getBoundingClientRect().width;
50688
+ };
50689
+ const originalWidth = measureWidth(originalText);
50690
+ if (/^\s/.test(collapsedText)) {
50691
+ const trimmedLeading = collapsedText.replace(/^\s+/, "");
50692
+ const newWidth = measureWidth(trimmedLeading);
50693
+ if (newWidth === originalWidth) {
50694
+ collapsedText = trimmedLeading;
50695
+ }
50696
+ }
50697
+ if (/\s$/.test(collapsedText)) {
50698
+ const currentWidth = measureWidth(collapsedText);
50699
+ const trimmedTrailing = collapsedText.replace(/\s+$/, "");
50700
+ const newWidth = measureWidth(trimmedTrailing);
50701
+ if (newWidth === currentWidth) {
50702
+ collapsedText = trimmedTrailing;
50703
+ }
50704
+ }
50705
+ if (/\s\s/.test(collapsedText)) {
50706
+ const currentWidth = measureWidth(collapsedText);
50707
+ const collapsedInternal = collapsedText.replace(/\s\s+/g, " ");
50708
+ const newWidth = measureWidth(collapsedInternal);
50709
+ if (newWidth === currentWidth) {
50710
+ collapsedText = collapsedInternal;
50711
+ }
50712
+ }
50713
+ textNode.textContent = originalText;
50714
+ return collapsedText;
50715
+ };
50716
+ function findLineBreaks(span, rtl) {
50717
+ const textNode = span.childNodes[0];
50718
+ const originalText = textNode.textContent;
50719
+ const originalRect = span.getBoundingClientRect();
50720
+ const computedStyle = getComputedStyle(span);
50721
+ const segmenter = new Intl.Segmenter("en", { granularity: "word" });
50722
+ const segments = segmenter.segment(originalText);
50723
+ const words = Array.from(segments).map((s) => s.segment);
50724
+ const lines2 = [];
50725
+ let currentLine = "";
50726
+ let testText = "";
50727
+ let previousRect = originalRect;
50728
+ textNode.textContent = "";
50729
+ for (let i = 0;i < words.length; i += 1) {
50730
+ const word = words[i];
50731
+ testText += word;
50732
+ let wordsToAdd = word;
50733
+ while (typeof words[i + 1] !== "undefined" && words[i + 1].trim() === "") {
50734
+ testText += words[i + 1];
50735
+ wordsToAdd += words[i + 1];
50736
+ i++;
50737
+ }
50738
+ previousRect = span.getBoundingClientRect();
50739
+ textNode.textContent = testText;
50740
+ const collapsedText = getCollapsedText(span);
50741
+ textNode.textContent = collapsedText;
50742
+ const rect = span.getBoundingClientRect();
50743
+ const currentHeight = rect.height;
50744
+ if (previousRect && previousRect.height !== 0 && Math.abs(currentHeight - previousRect.height) > 2) {
50745
+ const offsetHorizontal = rtl ? previousRect.right - originalRect.right : previousRect.left - originalRect.left;
50746
+ const shouldCollapse = !computedStyle.whiteSpaceCollapse.includes("preserve");
50747
+ lines2.push({
50748
+ text: shouldCollapse ? currentLine.trim() : currentLine,
50749
+ offsetTop: currentHeight - previousRect.height,
50750
+ offsetHorizontal
50751
+ });
50752
+ currentLine = wordsToAdd;
50753
+ } else {
50754
+ currentLine += wordsToAdd;
50755
+ }
50756
+ }
50757
+ if (currentLine) {
50758
+ textNode.textContent = testText;
50759
+ const rects = span.getClientRects();
50760
+ const rect = span.getBoundingClientRect();
50761
+ const lastRect = rects[rects.length - 1];
50762
+ const offsetHorizontal = rtl ? lastRect.right - originalRect.right : lastRect.left - originalRect.left;
50763
+ lines2.push({
50764
+ text: currentLine,
50765
+ offsetTop: rect.height - previousRect.height,
50766
+ offsetHorizontal
50767
+ });
50768
+ }
50769
+ textNode.textContent = originalText;
50770
+ return lines2;
50771
+ }
50772
+ var applyTextTransform = (text3, transform) => {
50773
+ if (transform === "uppercase") {
50774
+ return text3.toUpperCase();
50775
+ }
50776
+ if (transform === "lowercase") {
50777
+ return text3.toLowerCase();
50778
+ }
50779
+ if (transform === "capitalize") {
50780
+ return text3.replace(/\b\w/g, (char) => char.toUpperCase());
50781
+ }
50782
+ return text3;
50783
+ };
50784
+ var handleTextNode = async (node, context) => {
50785
+ const span = document.createElement("span");
50786
+ const parent = node.parentNode;
50787
+ if (!parent) {
50788
+ throw new Error("Text node has no parent");
50789
+ }
50790
+ parent.insertBefore(span, node);
50791
+ span.appendChild(node);
50792
+ await drawElementToCanvas({
50793
+ context,
50794
+ element: span,
50795
+ draw(rect, style12) {
50796
+ const {
50797
+ fontFamily,
50798
+ fontSize: fontSize2,
50799
+ fontWeight,
50800
+ color,
50801
+ lineHeight: lineHeight2,
50802
+ direction,
50803
+ writingMode,
50804
+ letterSpacing,
50805
+ textTransform
50806
+ } = style12;
50807
+ const isVertical = writingMode !== "horizontal-tb";
50808
+ if (isVertical) {
50809
+ Internals310.Log.warn({
50810
+ logLevel: "warn",
50811
+ tag: "@remotion/web-renderer"
50812
+ }, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
50813
+ return;
50814
+ }
50815
+ context.save();
50816
+ context.font = `${fontWeight} ${fontSize2} ${fontFamily}`;
50817
+ context.fillStyle = color;
50818
+ context.letterSpacing = letterSpacing;
50819
+ const fontSizePx = parseFloat(fontSize2);
50820
+ const lineHeightPx = lineHeight2 === "normal" ? 1.2 * fontSizePx : parseFloat(lineHeight2);
50821
+ const baselineOffset = (lineHeightPx - fontSizePx) / 2;
50822
+ const isRTL = direction === "rtl";
50823
+ context.textAlign = isRTL ? "right" : "left";
50824
+ context.textBaseline = "top";
50825
+ const originalText = span.textContent;
50826
+ const collapsedText = getCollapsedText(span);
50827
+ const transformedText = applyTextTransform(collapsedText, textTransform);
50828
+ span.textContent = transformedText;
50829
+ const xPosition = isRTL ? rect.right : rect.left;
50830
+ const lines2 = findLineBreaks(span, isRTL);
50831
+ let offsetTop = 0;
50832
+ for (const line4 of lines2) {
50833
+ context.fillText(line4.text, xPosition + line4.offsetHorizontal, rect.top + baselineOffset + offsetTop);
50834
+ offsetTop += line4.offsetTop;
50835
+ }
50836
+ span.textContent = originalText;
50837
+ context.restore();
50838
+ }
50839
+ });
50840
+ parent.insertBefore(node, span);
50841
+ parent.removeChild(span);
50842
+ };
50843
+ var turnSvgIntoDrawable = (svg) => {
50844
+ const originalTransform = svg.style.transform;
50845
+ const originalTransformOrigin = svg.style.transformOrigin;
50846
+ const originalMarginLeft = svg.style.marginLeft;
50847
+ const originalMarginRight = svg.style.marginRight;
50848
+ const originalMarginTop = svg.style.marginTop;
50849
+ const originalMarginBottom = svg.style.marginBottom;
50850
+ svg.style.transform = "none";
50851
+ svg.style.transformOrigin = "";
50852
+ svg.style.marginLeft = "0";
50853
+ svg.style.marginRight = "0";
50854
+ svg.style.marginTop = "0";
50855
+ svg.style.marginBottom = "0";
50856
+ const svgData = new XMLSerializer().serializeToString(svg);
50857
+ svg.style.marginLeft = originalMarginLeft;
50858
+ svg.style.marginRight = originalMarginRight;
50859
+ svg.style.marginTop = originalMarginTop;
50860
+ svg.style.marginBottom = originalMarginBottom;
50861
+ svg.style.transform = originalTransform;
50862
+ svg.style.transformOrigin = originalTransformOrigin;
50863
+ return new Promise((resolve, reject) => {
50864
+ const image = new Image;
50865
+ const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
50866
+ image.onload = function() {
50867
+ resolve(image);
50868
+ };
50869
+ image.onerror = () => {
50870
+ reject(new Error("Failed to convert SVG to image"));
50871
+ };
50872
+ image.src = url2;
50873
+ });
50874
+ };
50704
50875
  var compose = async (element, context) => {
50705
50876
  const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
50706
50877
  if (node instanceof Element) {
50878
+ if (node.parentElement instanceof SVGSVGElement) {
50879
+ return NodeFilter.FILTER_REJECT;
50880
+ }
50707
50881
  const computedStyle = getComputedStyle(node);
50708
50882
  return computedStyle.display === "none" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
50709
50883
  }
@@ -50712,7 +50886,18 @@ var compose = async (element, context) => {
50712
50886
  while (treeWalker.nextNode()) {
50713
50887
  const node = treeWalker.currentNode;
50714
50888
  if (node instanceof HTMLElement || node instanceof SVGElement) {
50715
- await drawElementToCanvas({ element: node, context });
50889
+ await drawElementToCanvas({
50890
+ element: node,
50891
+ context,
50892
+ draw: async (dimensions) => {
50893
+ const drawable = await (node instanceof SVGSVGElement ? turnSvgIntoDrawable(node) : node instanceof HTMLImageElement ? node : node instanceof HTMLCanvasElement ? node : null);
50894
+ if (drawable) {
50895
+ context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50896
+ }
50897
+ }
50898
+ });
50899
+ } else if (node instanceof Text) {
50900
+ await handleTextNode(node, context);
50716
50901
  }
50717
50902
  }
50718
50903
  };
@@ -50863,7 +51048,7 @@ var internalRenderMediaOnWeb = async ({
50863
51048
  if (codec && !format.getSupportedCodecs().includes(codecToMediabunnyCodec(codec))) {
50864
51049
  return Promise.reject(new Error(`Codec ${codec} is not supported for container ${container60}`));
50865
51050
  }
50866
- const resolved = await Internals310.resolveVideoConfig({
51051
+ const resolved = await Internals410.resolveVideoConfig({
50867
51052
  calculateMetadata: composition.calculateMetadata ?? null,
50868
51053
  signal: signal ?? new AbortController().signal,
50869
51054
  defaultProps: composition.defaultProps ?? {},
@@ -51062,7 +51247,7 @@ async function internalRenderStillOnWeb({
51062
51247
  signal,
51063
51248
  onArtifact
51064
51249
  }) {
51065
- const resolved = await Internals410.resolveVideoConfig({
51250
+ const resolved = await Internals510.resolveVideoConfig({
51066
51251
  calculateMetadata: composition.calculateMetadata ?? null,
51067
51252
  signal: signal ?? new AbortController().signal,
51068
51253
  defaultProps: composition.defaultProps ?? {},
@@ -41713,7 +41713,7 @@ var RenderModalWithLoader = (props) => {
41713
41713
  import { getDefaultOutLocation as getDefaultOutLocation2 } from "@remotion/studio-shared";
41714
41714
 
41715
41715
  // ../web-renderer/dist/esm/index.mjs
41716
- import { Internals as Internals310 } from "remotion";
41716
+ import { Internals as Internals410 } from "remotion";
41717
41717
  import { NoReactInternals as NoReactInternals16 } from "remotion/no-react";
41718
41718
  import { createRef as createRef12 } from "react";
41719
41719
  import { flushSync as flushSync2 } from "react-dom";
@@ -41724,8 +41724,9 @@ import { flushSync } from "react-dom";
41724
41724
  import { Internals as Internals62 } from "remotion";
41725
41725
  import { jsx as jsx257 } from "react/jsx-runtime";
41726
41726
  import { jsx as jsx258 } from "react/jsx-runtime";
41727
+ import { Internals as Internals310 } from "remotion";
41727
41728
  import {
41728
- Internals as Internals410
41729
+ Internals as Internals510
41729
41730
  } from "remotion";
41730
41731
  /*!
41731
41732
  * Copyright (c) 2025-present, Vanilagy and contributors
@@ -50474,12 +50475,16 @@ var calculateTransforms = (element) => {
50474
50475
  const transforms = [];
50475
50476
  const toReset = [];
50476
50477
  let opacity = 1;
50478
+ let elementComputedStyle = null;
50477
50479
  while (parent) {
50478
50480
  const computedStyle = getComputedStyle(parent);
50479
50481
  const parentOpacity = computedStyle.opacity;
50480
50482
  if (parentOpacity && parentOpacity !== "") {
50481
50483
  opacity *= parseFloat(parentOpacity);
50482
50484
  }
50485
+ if (parent === element) {
50486
+ elementComputedStyle = computedStyle;
50487
+ }
50483
50488
  if (computedStyle.transform && computedStyle.transform !== "none" || parent === element) {
50484
50489
  const toParse = computedStyle.transform === "none" || computedStyle.transform === "" ? undefined : computedStyle.transform;
50485
50490
  const matrix = new DOMMatrix(toParse);
@@ -50512,6 +50517,9 @@ var calculateTransforms = (element) => {
50512
50517
  const transformMatrix = new DOMMatrix().translate(globalTransformOrigin.x, globalTransformOrigin.y).multiply(transform.matrix).translate(-globalTransformOrigin.x, -globalTransformOrigin.y);
50513
50518
  totalMatrix.multiplySelf(transformMatrix);
50514
50519
  }
50520
+ if (!elementComputedStyle) {
50521
+ throw new Error("Element computed style not found");
50522
+ }
50515
50523
  return {
50516
50524
  dimensions,
50517
50525
  totalMatrix,
@@ -50521,7 +50529,8 @@ var calculateTransforms = (element) => {
50521
50529
  }
50522
50530
  },
50523
50531
  nativeTransformOrigin,
50524
- opacity
50532
+ opacity,
50533
+ computedStyle: elementComputedStyle
50525
50534
  };
50526
50535
  };
50527
50536
  var drawBorder = ({
@@ -50628,43 +50637,12 @@ var setTransform = ({
50628
50637
  ctx.setTransform(new DOMMatrix);
50629
50638
  };
50630
50639
  };
50631
- var turnSvgIntoDrawable = (svg) => {
50632
- const originalTransform = svg.style.transform;
50633
- const originalTransformOrigin = svg.style.transformOrigin;
50634
- const originalMarginLeft = svg.style.marginLeft;
50635
- const originalMarginRight = svg.style.marginRight;
50636
- const originalMarginTop = svg.style.marginTop;
50637
- const originalMarginBottom = svg.style.marginBottom;
50638
- svg.style.transform = "none";
50639
- svg.style.transformOrigin = "";
50640
- svg.style.marginLeft = "0";
50641
- svg.style.marginRight = "0";
50642
- svg.style.marginTop = "0";
50643
- svg.style.marginBottom = "0";
50644
- const svgData = new XMLSerializer().serializeToString(svg);
50645
- svg.style.marginLeft = originalMarginLeft;
50646
- svg.style.marginRight = originalMarginRight;
50647
- svg.style.marginTop = originalMarginTop;
50648
- svg.style.marginBottom = originalMarginBottom;
50649
- svg.style.transform = originalTransform;
50650
- svg.style.transformOrigin = originalTransformOrigin;
50651
- return new Promise((resolve, reject) => {
50652
- const image = new Image;
50653
- const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
50654
- image.onload = function() {
50655
- resolve(image);
50656
- };
50657
- image.onerror = () => {
50658
- reject(new Error("Failed to convert SVG to image"));
50659
- };
50660
- image.src = url2;
50661
- });
50662
- };
50663
50640
  var drawElementToCanvas = async ({
50664
50641
  element,
50665
- context
50642
+ context,
50643
+ draw
50666
50644
  }) => {
50667
- const { totalMatrix, reset, dimensions, opacity } = calculateTransforms(element);
50645
+ const { totalMatrix, reset, dimensions, opacity, computedStyle } = calculateTransforms(element);
50668
50646
  if (opacity === 0) {
50669
50647
  reset();
50670
50648
  return;
@@ -50673,7 +50651,6 @@ var drawElementToCanvas = async ({
50673
50651
  reset();
50674
50652
  return;
50675
50653
  }
50676
- const computedStyle = getComputedStyle(element);
50677
50654
  const background2 = computedStyle.backgroundColor;
50678
50655
  const borderRadius = parseBorderRadius({
50679
50656
  borderRadius: computedStyle.borderRadius,
@@ -50696,16 +50673,13 @@ var drawElementToCanvas = async ({
50696
50673
  ctx: context,
50697
50674
  opacity
50698
50675
  });
50699
- const drawable = element instanceof SVGSVGElement ? await turnSvgIntoDrawable(element) : element instanceof HTMLImageElement ? element : element instanceof HTMLCanvasElement ? element : null;
50700
50676
  if (background2 && background2 !== "transparent" && !(background2.startsWith("rgba") && (background2.endsWith(", 0)") || background2.endsWith(",0")))) {
50701
50677
  const originalFillStyle = context.fillStyle;
50702
50678
  context.fillStyle = background2;
50703
50679
  context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50704
50680
  context.fillStyle = originalFillStyle;
50705
50681
  }
50706
- if (drawable) {
50707
- context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50708
- }
50682
+ await draw(dimensions, computedStyle);
50709
50683
  drawBorder({
50710
50684
  ctx: context,
50711
50685
  x: dimensions.left,
@@ -50720,9 +50694,209 @@ var drawElementToCanvas = async ({
50720
50694
  finishTransform();
50721
50695
  reset();
50722
50696
  };
50697
+ var getCollapsedText = (span) => {
50698
+ const textNode = span.firstChild;
50699
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
50700
+ throw new Error("Span must contain a single text node");
50701
+ }
50702
+ const originalText = textNode.textContent || "";
50703
+ let collapsedText = originalText;
50704
+ const measureWidth = (text3) => {
50705
+ textNode.textContent = text3;
50706
+ return span.getBoundingClientRect().width;
50707
+ };
50708
+ const originalWidth = measureWidth(originalText);
50709
+ if (/^\s/.test(collapsedText)) {
50710
+ const trimmedLeading = collapsedText.replace(/^\s+/, "");
50711
+ const newWidth = measureWidth(trimmedLeading);
50712
+ if (newWidth === originalWidth) {
50713
+ collapsedText = trimmedLeading;
50714
+ }
50715
+ }
50716
+ if (/\s$/.test(collapsedText)) {
50717
+ const currentWidth = measureWidth(collapsedText);
50718
+ const trimmedTrailing = collapsedText.replace(/\s+$/, "");
50719
+ const newWidth = measureWidth(trimmedTrailing);
50720
+ if (newWidth === currentWidth) {
50721
+ collapsedText = trimmedTrailing;
50722
+ }
50723
+ }
50724
+ if (/\s\s/.test(collapsedText)) {
50725
+ const currentWidth = measureWidth(collapsedText);
50726
+ const collapsedInternal = collapsedText.replace(/\s\s+/g, " ");
50727
+ const newWidth = measureWidth(collapsedInternal);
50728
+ if (newWidth === currentWidth) {
50729
+ collapsedText = collapsedInternal;
50730
+ }
50731
+ }
50732
+ textNode.textContent = originalText;
50733
+ return collapsedText;
50734
+ };
50735
+ function findLineBreaks(span, rtl) {
50736
+ const textNode = span.childNodes[0];
50737
+ const originalText = textNode.textContent;
50738
+ const originalRect = span.getBoundingClientRect();
50739
+ const computedStyle = getComputedStyle(span);
50740
+ const segmenter = new Intl.Segmenter("en", { granularity: "word" });
50741
+ const segments = segmenter.segment(originalText);
50742
+ const words = Array.from(segments).map((s) => s.segment);
50743
+ const lines2 = [];
50744
+ let currentLine = "";
50745
+ let testText = "";
50746
+ let previousRect = originalRect;
50747
+ textNode.textContent = "";
50748
+ for (let i = 0;i < words.length; i += 1) {
50749
+ const word = words[i];
50750
+ testText += word;
50751
+ let wordsToAdd = word;
50752
+ while (typeof words[i + 1] !== "undefined" && words[i + 1].trim() === "") {
50753
+ testText += words[i + 1];
50754
+ wordsToAdd += words[i + 1];
50755
+ i++;
50756
+ }
50757
+ previousRect = span.getBoundingClientRect();
50758
+ textNode.textContent = testText;
50759
+ const collapsedText = getCollapsedText(span);
50760
+ textNode.textContent = collapsedText;
50761
+ const rect = span.getBoundingClientRect();
50762
+ const currentHeight = rect.height;
50763
+ if (previousRect && previousRect.height !== 0 && Math.abs(currentHeight - previousRect.height) > 2) {
50764
+ const offsetHorizontal = rtl ? previousRect.right - originalRect.right : previousRect.left - originalRect.left;
50765
+ const shouldCollapse = !computedStyle.whiteSpaceCollapse.includes("preserve");
50766
+ lines2.push({
50767
+ text: shouldCollapse ? currentLine.trim() : currentLine,
50768
+ offsetTop: currentHeight - previousRect.height,
50769
+ offsetHorizontal
50770
+ });
50771
+ currentLine = wordsToAdd;
50772
+ } else {
50773
+ currentLine += wordsToAdd;
50774
+ }
50775
+ }
50776
+ if (currentLine) {
50777
+ textNode.textContent = testText;
50778
+ const rects = span.getClientRects();
50779
+ const rect = span.getBoundingClientRect();
50780
+ const lastRect = rects[rects.length - 1];
50781
+ const offsetHorizontal = rtl ? lastRect.right - originalRect.right : lastRect.left - originalRect.left;
50782
+ lines2.push({
50783
+ text: currentLine,
50784
+ offsetTop: rect.height - previousRect.height,
50785
+ offsetHorizontal
50786
+ });
50787
+ }
50788
+ textNode.textContent = originalText;
50789
+ return lines2;
50790
+ }
50791
+ var applyTextTransform = (text3, transform) => {
50792
+ if (transform === "uppercase") {
50793
+ return text3.toUpperCase();
50794
+ }
50795
+ if (transform === "lowercase") {
50796
+ return text3.toLowerCase();
50797
+ }
50798
+ if (transform === "capitalize") {
50799
+ return text3.replace(/\b\w/g, (char) => char.toUpperCase());
50800
+ }
50801
+ return text3;
50802
+ };
50803
+ var handleTextNode = async (node, context) => {
50804
+ const span = document.createElement("span");
50805
+ const parent = node.parentNode;
50806
+ if (!parent) {
50807
+ throw new Error("Text node has no parent");
50808
+ }
50809
+ parent.insertBefore(span, node);
50810
+ span.appendChild(node);
50811
+ await drawElementToCanvas({
50812
+ context,
50813
+ element: span,
50814
+ draw(rect, style12) {
50815
+ const {
50816
+ fontFamily,
50817
+ fontSize: fontSize2,
50818
+ fontWeight,
50819
+ color,
50820
+ lineHeight: lineHeight2,
50821
+ direction,
50822
+ writingMode,
50823
+ letterSpacing,
50824
+ textTransform
50825
+ } = style12;
50826
+ const isVertical = writingMode !== "horizontal-tb";
50827
+ if (isVertical) {
50828
+ Internals310.Log.warn({
50829
+ logLevel: "warn",
50830
+ tag: "@remotion/web-renderer"
50831
+ }, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
50832
+ return;
50833
+ }
50834
+ context.save();
50835
+ context.font = `${fontWeight} ${fontSize2} ${fontFamily}`;
50836
+ context.fillStyle = color;
50837
+ context.letterSpacing = letterSpacing;
50838
+ const fontSizePx = parseFloat(fontSize2);
50839
+ const lineHeightPx = lineHeight2 === "normal" ? 1.2 * fontSizePx : parseFloat(lineHeight2);
50840
+ const baselineOffset = (lineHeightPx - fontSizePx) / 2;
50841
+ const isRTL = direction === "rtl";
50842
+ context.textAlign = isRTL ? "right" : "left";
50843
+ context.textBaseline = "top";
50844
+ const originalText = span.textContent;
50845
+ const collapsedText = getCollapsedText(span);
50846
+ const transformedText = applyTextTransform(collapsedText, textTransform);
50847
+ span.textContent = transformedText;
50848
+ const xPosition = isRTL ? rect.right : rect.left;
50849
+ const lines2 = findLineBreaks(span, isRTL);
50850
+ let offsetTop = 0;
50851
+ for (const line4 of lines2) {
50852
+ context.fillText(line4.text, xPosition + line4.offsetHorizontal, rect.top + baselineOffset + offsetTop);
50853
+ offsetTop += line4.offsetTop;
50854
+ }
50855
+ span.textContent = originalText;
50856
+ context.restore();
50857
+ }
50858
+ });
50859
+ parent.insertBefore(node, span);
50860
+ parent.removeChild(span);
50861
+ };
50862
+ var turnSvgIntoDrawable = (svg) => {
50863
+ const originalTransform = svg.style.transform;
50864
+ const originalTransformOrigin = svg.style.transformOrigin;
50865
+ const originalMarginLeft = svg.style.marginLeft;
50866
+ const originalMarginRight = svg.style.marginRight;
50867
+ const originalMarginTop = svg.style.marginTop;
50868
+ const originalMarginBottom = svg.style.marginBottom;
50869
+ svg.style.transform = "none";
50870
+ svg.style.transformOrigin = "";
50871
+ svg.style.marginLeft = "0";
50872
+ svg.style.marginRight = "0";
50873
+ svg.style.marginTop = "0";
50874
+ svg.style.marginBottom = "0";
50875
+ const svgData = new XMLSerializer().serializeToString(svg);
50876
+ svg.style.marginLeft = originalMarginLeft;
50877
+ svg.style.marginRight = originalMarginRight;
50878
+ svg.style.marginTop = originalMarginTop;
50879
+ svg.style.marginBottom = originalMarginBottom;
50880
+ svg.style.transform = originalTransform;
50881
+ svg.style.transformOrigin = originalTransformOrigin;
50882
+ return new Promise((resolve, reject) => {
50883
+ const image = new Image;
50884
+ const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
50885
+ image.onload = function() {
50886
+ resolve(image);
50887
+ };
50888
+ image.onerror = () => {
50889
+ reject(new Error("Failed to convert SVG to image"));
50890
+ };
50891
+ image.src = url2;
50892
+ });
50893
+ };
50723
50894
  var compose = async (element, context) => {
50724
50895
  const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
50725
50896
  if (node instanceof Element) {
50897
+ if (node.parentElement instanceof SVGSVGElement) {
50898
+ return NodeFilter.FILTER_REJECT;
50899
+ }
50726
50900
  const computedStyle = getComputedStyle(node);
50727
50901
  return computedStyle.display === "none" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
50728
50902
  }
@@ -50731,7 +50905,18 @@ var compose = async (element, context) => {
50731
50905
  while (treeWalker.nextNode()) {
50732
50906
  const node = treeWalker.currentNode;
50733
50907
  if (node instanceof HTMLElement || node instanceof SVGElement) {
50734
- await drawElementToCanvas({ element: node, context });
50908
+ await drawElementToCanvas({
50909
+ element: node,
50910
+ context,
50911
+ draw: async (dimensions) => {
50912
+ const drawable = await (node instanceof SVGSVGElement ? turnSvgIntoDrawable(node) : node instanceof HTMLImageElement ? node : node instanceof HTMLCanvasElement ? node : null);
50913
+ if (drawable) {
50914
+ context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50915
+ }
50916
+ }
50917
+ });
50918
+ } else if (node instanceof Text) {
50919
+ await handleTextNode(node, context);
50735
50920
  }
50736
50921
  }
50737
50922
  };
@@ -50882,7 +51067,7 @@ var internalRenderMediaOnWeb = async ({
50882
51067
  if (codec && !format.getSupportedCodecs().includes(codecToMediabunnyCodec(codec))) {
50883
51068
  return Promise.reject(new Error(`Codec ${codec} is not supported for container ${container60}`));
50884
51069
  }
50885
- const resolved = await Internals310.resolveVideoConfig({
51070
+ const resolved = await Internals410.resolveVideoConfig({
50886
51071
  calculateMetadata: composition.calculateMetadata ?? null,
50887
51072
  signal: signal ?? new AbortController().signal,
50888
51073
  defaultProps: composition.defaultProps ?? {},
@@ -51081,7 +51266,7 @@ async function internalRenderStillOnWeb({
51081
51266
  signal,
51082
51267
  onArtifact
51083
51268
  }) {
51084
- const resolved = await Internals410.resolveVideoConfig({
51269
+ const resolved = await Internals510.resolveVideoConfig({
51085
51270
  calculateMetadata: composition.calculateMetadata ?? null,
51086
51271
  signal: signal ?? new AbortController().signal,
51087
51272
  defaultProps: composition.defaultProps ?? {},
@@ -41993,7 +41993,7 @@ var RenderModalWithLoader = (props) => {
41993
41993
  import { getDefaultOutLocation as getDefaultOutLocation2 } from "@remotion/studio-shared";
41994
41994
 
41995
41995
  // ../web-renderer/dist/esm/index.mjs
41996
- import { Internals as Internals310 } from "remotion";
41996
+ import { Internals as Internals410 } from "remotion";
41997
41997
  import { NoReactInternals as NoReactInternals16 } from "remotion/no-react";
41998
41998
  import { createRef as createRef13 } from "react";
41999
41999
  import { flushSync as flushSync2 } from "react-dom";
@@ -42004,8 +42004,9 @@ import { flushSync } from "react-dom";
42004
42004
  import { Internals as Internals62 } from "remotion";
42005
42005
  import { jsx as jsx258 } from "react/jsx-runtime";
42006
42006
  import { jsx as jsx259 } from "react/jsx-runtime";
42007
+ import { Internals as Internals310 } from "remotion";
42007
42008
  import {
42008
- Internals as Internals410
42009
+ Internals as Internals510
42009
42010
  } from "remotion";
42010
42011
  /*!
42011
42012
  * Copyright (c) 2025-present, Vanilagy and contributors
@@ -50754,12 +50755,16 @@ var calculateTransforms = (element) => {
50754
50755
  const transforms = [];
50755
50756
  const toReset = [];
50756
50757
  let opacity = 1;
50758
+ let elementComputedStyle = null;
50757
50759
  while (parent) {
50758
50760
  const computedStyle = getComputedStyle(parent);
50759
50761
  const parentOpacity = computedStyle.opacity;
50760
50762
  if (parentOpacity && parentOpacity !== "") {
50761
50763
  opacity *= parseFloat(parentOpacity);
50762
50764
  }
50765
+ if (parent === element) {
50766
+ elementComputedStyle = computedStyle;
50767
+ }
50763
50768
  if (computedStyle.transform && computedStyle.transform !== "none" || parent === element) {
50764
50769
  const toParse = computedStyle.transform === "none" || computedStyle.transform === "" ? undefined : computedStyle.transform;
50765
50770
  const matrix = new DOMMatrix(toParse);
@@ -50792,6 +50797,9 @@ var calculateTransforms = (element) => {
50792
50797
  const transformMatrix = new DOMMatrix().translate(globalTransformOrigin.x, globalTransformOrigin.y).multiply(transform.matrix).translate(-globalTransformOrigin.x, -globalTransformOrigin.y);
50793
50798
  totalMatrix.multiplySelf(transformMatrix);
50794
50799
  }
50800
+ if (!elementComputedStyle) {
50801
+ throw new Error("Element computed style not found");
50802
+ }
50795
50803
  return {
50796
50804
  dimensions,
50797
50805
  totalMatrix,
@@ -50801,7 +50809,8 @@ var calculateTransforms = (element) => {
50801
50809
  }
50802
50810
  },
50803
50811
  nativeTransformOrigin,
50804
- opacity
50812
+ opacity,
50813
+ computedStyle: elementComputedStyle
50805
50814
  };
50806
50815
  };
50807
50816
  var drawBorder = ({
@@ -50908,43 +50917,12 @@ var setTransform = ({
50908
50917
  ctx.setTransform(new DOMMatrix);
50909
50918
  };
50910
50919
  };
50911
- var turnSvgIntoDrawable = (svg) => {
50912
- const originalTransform = svg.style.transform;
50913
- const originalTransformOrigin = svg.style.transformOrigin;
50914
- const originalMarginLeft = svg.style.marginLeft;
50915
- const originalMarginRight = svg.style.marginRight;
50916
- const originalMarginTop = svg.style.marginTop;
50917
- const originalMarginBottom = svg.style.marginBottom;
50918
- svg.style.transform = "none";
50919
- svg.style.transformOrigin = "";
50920
- svg.style.marginLeft = "0";
50921
- svg.style.marginRight = "0";
50922
- svg.style.marginTop = "0";
50923
- svg.style.marginBottom = "0";
50924
- const svgData = new XMLSerializer().serializeToString(svg);
50925
- svg.style.marginLeft = originalMarginLeft;
50926
- svg.style.marginRight = originalMarginRight;
50927
- svg.style.marginTop = originalMarginTop;
50928
- svg.style.marginBottom = originalMarginBottom;
50929
- svg.style.transform = originalTransform;
50930
- svg.style.transformOrigin = originalTransformOrigin;
50931
- return new Promise((resolve, reject) => {
50932
- const image = new Image;
50933
- const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
50934
- image.onload = function() {
50935
- resolve(image);
50936
- };
50937
- image.onerror = () => {
50938
- reject(new Error("Failed to convert SVG to image"));
50939
- };
50940
- image.src = url2;
50941
- });
50942
- };
50943
50920
  var drawElementToCanvas = async ({
50944
50921
  element,
50945
- context
50922
+ context,
50923
+ draw
50946
50924
  }) => {
50947
- const { totalMatrix, reset, dimensions, opacity } = calculateTransforms(element);
50925
+ const { totalMatrix, reset, dimensions, opacity, computedStyle } = calculateTransforms(element);
50948
50926
  if (opacity === 0) {
50949
50927
  reset();
50950
50928
  return;
@@ -50953,7 +50931,6 @@ var drawElementToCanvas = async ({
50953
50931
  reset();
50954
50932
  return;
50955
50933
  }
50956
- const computedStyle = getComputedStyle(element);
50957
50934
  const background2 = computedStyle.backgroundColor;
50958
50935
  const borderRadius = parseBorderRadius({
50959
50936
  borderRadius: computedStyle.borderRadius,
@@ -50976,16 +50953,13 @@ var drawElementToCanvas = async ({
50976
50953
  ctx: context,
50977
50954
  opacity
50978
50955
  });
50979
- const drawable = element instanceof SVGSVGElement ? await turnSvgIntoDrawable(element) : element instanceof HTMLImageElement ? element : element instanceof HTMLCanvasElement ? element : null;
50980
50956
  if (background2 && background2 !== "transparent" && !(background2.startsWith("rgba") && (background2.endsWith(", 0)") || background2.endsWith(",0")))) {
50981
50957
  const originalFillStyle = context.fillStyle;
50982
50958
  context.fillStyle = background2;
50983
50959
  context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50984
50960
  context.fillStyle = originalFillStyle;
50985
50961
  }
50986
- if (drawable) {
50987
- context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
50988
- }
50962
+ await draw(dimensions, computedStyle);
50989
50963
  drawBorder({
50990
50964
  ctx: context,
50991
50965
  x: dimensions.left,
@@ -51000,9 +50974,209 @@ var drawElementToCanvas = async ({
51000
50974
  finishTransform();
51001
50975
  reset();
51002
50976
  };
50977
+ var getCollapsedText = (span) => {
50978
+ const textNode = span.firstChild;
50979
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
50980
+ throw new Error("Span must contain a single text node");
50981
+ }
50982
+ const originalText = textNode.textContent || "";
50983
+ let collapsedText = originalText;
50984
+ const measureWidth = (text3) => {
50985
+ textNode.textContent = text3;
50986
+ return span.getBoundingClientRect().width;
50987
+ };
50988
+ const originalWidth = measureWidth(originalText);
50989
+ if (/^\s/.test(collapsedText)) {
50990
+ const trimmedLeading = collapsedText.replace(/^\s+/, "");
50991
+ const newWidth = measureWidth(trimmedLeading);
50992
+ if (newWidth === originalWidth) {
50993
+ collapsedText = trimmedLeading;
50994
+ }
50995
+ }
50996
+ if (/\s$/.test(collapsedText)) {
50997
+ const currentWidth = measureWidth(collapsedText);
50998
+ const trimmedTrailing = collapsedText.replace(/\s+$/, "");
50999
+ const newWidth = measureWidth(trimmedTrailing);
51000
+ if (newWidth === currentWidth) {
51001
+ collapsedText = trimmedTrailing;
51002
+ }
51003
+ }
51004
+ if (/\s\s/.test(collapsedText)) {
51005
+ const currentWidth = measureWidth(collapsedText);
51006
+ const collapsedInternal = collapsedText.replace(/\s\s+/g, " ");
51007
+ const newWidth = measureWidth(collapsedInternal);
51008
+ if (newWidth === currentWidth) {
51009
+ collapsedText = collapsedInternal;
51010
+ }
51011
+ }
51012
+ textNode.textContent = originalText;
51013
+ return collapsedText;
51014
+ };
51015
+ function findLineBreaks(span, rtl) {
51016
+ const textNode = span.childNodes[0];
51017
+ const originalText = textNode.textContent;
51018
+ const originalRect = span.getBoundingClientRect();
51019
+ const computedStyle = getComputedStyle(span);
51020
+ const segmenter = new Intl.Segmenter("en", { granularity: "word" });
51021
+ const segments = segmenter.segment(originalText);
51022
+ const words = Array.from(segments).map((s) => s.segment);
51023
+ const lines2 = [];
51024
+ let currentLine = "";
51025
+ let testText = "";
51026
+ let previousRect = originalRect;
51027
+ textNode.textContent = "";
51028
+ for (let i = 0;i < words.length; i += 1) {
51029
+ const word = words[i];
51030
+ testText += word;
51031
+ let wordsToAdd = word;
51032
+ while (typeof words[i + 1] !== "undefined" && words[i + 1].trim() === "") {
51033
+ testText += words[i + 1];
51034
+ wordsToAdd += words[i + 1];
51035
+ i++;
51036
+ }
51037
+ previousRect = span.getBoundingClientRect();
51038
+ textNode.textContent = testText;
51039
+ const collapsedText = getCollapsedText(span);
51040
+ textNode.textContent = collapsedText;
51041
+ const rect = span.getBoundingClientRect();
51042
+ const currentHeight = rect.height;
51043
+ if (previousRect && previousRect.height !== 0 && Math.abs(currentHeight - previousRect.height) > 2) {
51044
+ const offsetHorizontal = rtl ? previousRect.right - originalRect.right : previousRect.left - originalRect.left;
51045
+ const shouldCollapse = !computedStyle.whiteSpaceCollapse.includes("preserve");
51046
+ lines2.push({
51047
+ text: shouldCollapse ? currentLine.trim() : currentLine,
51048
+ offsetTop: currentHeight - previousRect.height,
51049
+ offsetHorizontal
51050
+ });
51051
+ currentLine = wordsToAdd;
51052
+ } else {
51053
+ currentLine += wordsToAdd;
51054
+ }
51055
+ }
51056
+ if (currentLine) {
51057
+ textNode.textContent = testText;
51058
+ const rects = span.getClientRects();
51059
+ const rect = span.getBoundingClientRect();
51060
+ const lastRect = rects[rects.length - 1];
51061
+ const offsetHorizontal = rtl ? lastRect.right - originalRect.right : lastRect.left - originalRect.left;
51062
+ lines2.push({
51063
+ text: currentLine,
51064
+ offsetTop: rect.height - previousRect.height,
51065
+ offsetHorizontal
51066
+ });
51067
+ }
51068
+ textNode.textContent = originalText;
51069
+ return lines2;
51070
+ }
51071
+ var applyTextTransform = (text3, transform) => {
51072
+ if (transform === "uppercase") {
51073
+ return text3.toUpperCase();
51074
+ }
51075
+ if (transform === "lowercase") {
51076
+ return text3.toLowerCase();
51077
+ }
51078
+ if (transform === "capitalize") {
51079
+ return text3.replace(/\b\w/g, (char) => char.toUpperCase());
51080
+ }
51081
+ return text3;
51082
+ };
51083
+ var handleTextNode = async (node, context) => {
51084
+ const span = document.createElement("span");
51085
+ const parent = node.parentNode;
51086
+ if (!parent) {
51087
+ throw new Error("Text node has no parent");
51088
+ }
51089
+ parent.insertBefore(span, node);
51090
+ span.appendChild(node);
51091
+ await drawElementToCanvas({
51092
+ context,
51093
+ element: span,
51094
+ draw(rect, style12) {
51095
+ const {
51096
+ fontFamily,
51097
+ fontSize: fontSize2,
51098
+ fontWeight,
51099
+ color,
51100
+ lineHeight: lineHeight2,
51101
+ direction,
51102
+ writingMode,
51103
+ letterSpacing,
51104
+ textTransform
51105
+ } = style12;
51106
+ const isVertical = writingMode !== "horizontal-tb";
51107
+ if (isVertical) {
51108
+ Internals310.Log.warn({
51109
+ logLevel: "warn",
51110
+ tag: "@remotion/web-renderer"
51111
+ }, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
51112
+ return;
51113
+ }
51114
+ context.save();
51115
+ context.font = `${fontWeight} ${fontSize2} ${fontFamily}`;
51116
+ context.fillStyle = color;
51117
+ context.letterSpacing = letterSpacing;
51118
+ const fontSizePx = parseFloat(fontSize2);
51119
+ const lineHeightPx = lineHeight2 === "normal" ? 1.2 * fontSizePx : parseFloat(lineHeight2);
51120
+ const baselineOffset = (lineHeightPx - fontSizePx) / 2;
51121
+ const isRTL = direction === "rtl";
51122
+ context.textAlign = isRTL ? "right" : "left";
51123
+ context.textBaseline = "top";
51124
+ const originalText = span.textContent;
51125
+ const collapsedText = getCollapsedText(span);
51126
+ const transformedText = applyTextTransform(collapsedText, textTransform);
51127
+ span.textContent = transformedText;
51128
+ const xPosition = isRTL ? rect.right : rect.left;
51129
+ const lines2 = findLineBreaks(span, isRTL);
51130
+ let offsetTop = 0;
51131
+ for (const line4 of lines2) {
51132
+ context.fillText(line4.text, xPosition + line4.offsetHorizontal, rect.top + baselineOffset + offsetTop);
51133
+ offsetTop += line4.offsetTop;
51134
+ }
51135
+ span.textContent = originalText;
51136
+ context.restore();
51137
+ }
51138
+ });
51139
+ parent.insertBefore(node, span);
51140
+ parent.removeChild(span);
51141
+ };
51142
+ var turnSvgIntoDrawable = (svg) => {
51143
+ const originalTransform = svg.style.transform;
51144
+ const originalTransformOrigin = svg.style.transformOrigin;
51145
+ const originalMarginLeft = svg.style.marginLeft;
51146
+ const originalMarginRight = svg.style.marginRight;
51147
+ const originalMarginTop = svg.style.marginTop;
51148
+ const originalMarginBottom = svg.style.marginBottom;
51149
+ svg.style.transform = "none";
51150
+ svg.style.transformOrigin = "";
51151
+ svg.style.marginLeft = "0";
51152
+ svg.style.marginRight = "0";
51153
+ svg.style.marginTop = "0";
51154
+ svg.style.marginBottom = "0";
51155
+ const svgData = new XMLSerializer().serializeToString(svg);
51156
+ svg.style.marginLeft = originalMarginLeft;
51157
+ svg.style.marginRight = originalMarginRight;
51158
+ svg.style.marginTop = originalMarginTop;
51159
+ svg.style.marginBottom = originalMarginBottom;
51160
+ svg.style.transform = originalTransform;
51161
+ svg.style.transformOrigin = originalTransformOrigin;
51162
+ return new Promise((resolve, reject) => {
51163
+ const image = new Image;
51164
+ const url2 = `data:image/svg+xml;base64,${btoa(svgData)}`;
51165
+ image.onload = function() {
51166
+ resolve(image);
51167
+ };
51168
+ image.onerror = () => {
51169
+ reject(new Error("Failed to convert SVG to image"));
51170
+ };
51171
+ image.src = url2;
51172
+ });
51173
+ };
51003
51174
  var compose = async (element, context) => {
51004
51175
  const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
51005
51176
  if (node instanceof Element) {
51177
+ if (node.parentElement instanceof SVGSVGElement) {
51178
+ return NodeFilter.FILTER_REJECT;
51179
+ }
51006
51180
  const computedStyle = getComputedStyle(node);
51007
51181
  return computedStyle.display === "none" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
51008
51182
  }
@@ -51011,7 +51185,18 @@ var compose = async (element, context) => {
51011
51185
  while (treeWalker.nextNode()) {
51012
51186
  const node = treeWalker.currentNode;
51013
51187
  if (node instanceof HTMLElement || node instanceof SVGElement) {
51014
- await drawElementToCanvas({ element: node, context });
51188
+ await drawElementToCanvas({
51189
+ element: node,
51190
+ context,
51191
+ draw: async (dimensions) => {
51192
+ const drawable = await (node instanceof SVGSVGElement ? turnSvgIntoDrawable(node) : node instanceof HTMLImageElement ? node : node instanceof HTMLCanvasElement ? node : null);
51193
+ if (drawable) {
51194
+ context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
51195
+ }
51196
+ }
51197
+ });
51198
+ } else if (node instanceof Text) {
51199
+ await handleTextNode(node, context);
51015
51200
  }
51016
51201
  }
51017
51202
  };
@@ -51162,7 +51347,7 @@ var internalRenderMediaOnWeb = async ({
51162
51347
  if (codec && !format.getSupportedCodecs().includes(codecToMediabunnyCodec(codec))) {
51163
51348
  return Promise.reject(new Error(`Codec ${codec} is not supported for container ${container60}`));
51164
51349
  }
51165
- const resolved = await Internals310.resolveVideoConfig({
51350
+ const resolved = await Internals410.resolveVideoConfig({
51166
51351
  calculateMetadata: composition.calculateMetadata ?? null,
51167
51352
  signal: signal ?? new AbortController().signal,
51168
51353
  defaultProps: composition.defaultProps ?? {},
@@ -51361,7 +51546,7 @@ async function internalRenderStillOnWeb({
51361
51546
  signal,
51362
51547
  onArtifact
51363
51548
  }) {
51364
- const resolved = await Internals410.resolveVideoConfig({
51549
+ const resolved = await Internals510.resolveVideoConfig({
51365
51550
  calculateMetadata: composition.calculateMetadata ?? null,
51366
51551
  signal: signal ?? new AbortController().signal,
51367
51552
  defaultProps: composition.defaultProps ?? {},
@@ -206,7 +206,7 @@ var renderContent = (Root) => {
206
206
  renderToDOM(/* @__PURE__ */ jsx("div", {
207
207
  children: /* @__PURE__ */ jsx(DelayedSpinner, {})
208
208
  }));
209
- import("./chunk-fea6d7v0.js").then(({ StudioInternals }) => {
209
+ import("./chunk-w9aq75yh.js").then(({ StudioInternals }) => {
210
210
  window.remotion_isStudio = true;
211
211
  window.remotion_isReadOnlyStudio = true;
212
212
  window.remotion_inputProps = "{}";
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio"
4
4
  },
5
5
  "name": "@remotion/studio",
6
- "version": "4.0.388",
6
+ "version": "4.0.390",
7
7
  "description": "APIs for interacting with the Remotion Studio",
8
8
  "main": "dist",
9
9
  "sideEffects": false,
@@ -25,13 +25,13 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "semver": "7.5.3",
28
- "remotion": "4.0.388",
29
- "@remotion/player": "4.0.388",
30
- "@remotion/media-utils": "4.0.388",
31
- "@remotion/renderer": "4.0.388",
32
- "@remotion/web-renderer": "4.0.388",
33
- "@remotion/studio-shared": "4.0.388",
34
- "@remotion/zod-types": "4.0.388",
28
+ "remotion": "4.0.390",
29
+ "@remotion/player": "4.0.390",
30
+ "@remotion/media-utils": "4.0.390",
31
+ "@remotion/renderer": "4.0.390",
32
+ "@remotion/web-renderer": "4.0.390",
33
+ "@remotion/studio-shared": "4.0.390",
34
+ "@remotion/zod-types": "4.0.390",
35
35
  "mediabunny": "1.25.8",
36
36
  "memfs": "3.4.3",
37
37
  "source-map": "0.7.3",
@@ -42,7 +42,7 @@
42
42
  "react": "19.2.3",
43
43
  "react-dom": "19.2.3",
44
44
  "@types/semver": "^7.3.4",
45
- "@remotion/eslint-config-internal": "4.0.388",
45
+ "@remotion/eslint-config-internal": "4.0.390",
46
46
  "eslint": "9.19.0"
47
47
  },
48
48
  "publishConfig": {