@remotion/web-renderer 4.0.485 → 4.0.487

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/LICENSE.md ADDED
@@ -0,0 +1,49 @@
1
+ # Remotion License
2
+
3
+ In Remotion 5.0, the license will slightly change. [View the changes here](https://github.com/remotion-dev/remotion/pull/3750).
4
+
5
+ ---
6
+
7
+ Depending on the type of your legal entity, you are granted permission to use Remotion for your project. Individuals and small companies are allowed to use Remotion to create videos for free (even commercial), while a company license is required for for-profit organizations of a certain size. This two-tier system was designed to ensure funding for this project while still allowing the source code to be available and the program to be free for most. Read below for the exact terms of use.
8
+
9
+ - [Free License](#free-license)
10
+ - [Company License](#company-license)
11
+
12
+ ## Free License
13
+
14
+ Copyright © 2026 [Remotion](https://www.remotion.dev)
15
+
16
+ ### Eligibility
17
+
18
+ You are eligible to use Remotion for free if you are:
19
+
20
+ - an individual
21
+ - a for-profit organization with up to 3 employees
22
+ - a non-profit or not-for-profit organization
23
+ - evaluating whether Remotion is a good fit, and are not yet using it in a commercial way
24
+
25
+ ### Allowed use cases
26
+
27
+ Permission is hereby granted, free of charge, to any person eligible for the "Free License", to use the software non-commercially or commercially for the purpose of creating videos and images and to modify the software to their own liking, for the purpose of fulfilling their custom use case or to contribute bug fixes or improvements back to Remotion.
28
+
29
+ ### Disallowed use cases
30
+
31
+ It is not allowed to copy or modify Remotion code for the purpose of selling, renting, licensing, relicensing, or sublicensing your own derivate of Remotion.
32
+
33
+ ### Warranty notice
34
+
35
+ The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. In no event shall the author or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
36
+
37
+ ### Support
38
+
39
+ Support is provided on a best-we-can-do basis via GitHub Issues and Discord.
40
+
41
+ ## Company License
42
+
43
+ You are required to obtain a Company License to use Remotion if you are not within the group of entities eligible for a Free License. This license will enable you to use Remotion for the allowed use cases specified in the Free License, and give you access to prioritized support (read the [Support Policy](https://www.remotion.dev/docs/support)).
44
+
45
+ Visit [remotion.pro](https://www.remotion.pro/license) for pricing and to buy a license.
46
+
47
+ ### FAQs
48
+
49
+ Are you not sure whether you need a Company License because of an edge case? Here are some [frequently asked questions](https://www.remotion.pro/faq).
package/dist/compose.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const compose: ({ element, context, logLevel, parentRect, internalState, onlyBackgroundClipText, scale, }: {
1
+ export declare const compose: ({ element, context, logLevel, parentRect, internalState, onlyBackgroundClipText, scale, waitForPageResponsiveness, }: {
2
2
  element: HTMLElement | SVGElement;
3
3
  context: OffscreenCanvasRenderingContext2D;
4
4
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -23,4 +23,5 @@ export declare const compose: ({ element, context, logLevel, parentRect, interna
23
23
  };
24
24
  onlyBackgroundClipText: boolean;
25
25
  scale: number;
26
+ waitForPageResponsiveness: (() => Promise<void>) | null;
26
27
  }) => Promise<void>;
@@ -5,7 +5,7 @@ export type ProcessNodeReturnValue = {
5
5
  } | {
6
6
  type: 'skip-children';
7
7
  };
8
- export declare const processNode: ({ element, context, draw, logLevel, parentRect, internalState, rootElement, scale, }: {
8
+ export declare const processNode: ({ element, context, draw, logLevel, parentRect, internalState, rootElement, scale, waitForPageResponsiveness, }: {
9
9
  element: HTMLElement | SVGElement;
10
10
  context: OffscreenCanvasRenderingContext2D;
11
11
  draw: DrawFn;
@@ -31,4 +31,5 @@ export declare const processNode: ({ element, context, draw, logLevel, parentRec
31
31
  };
32
32
  rootElement: HTMLElement | SVGElement;
33
33
  scale: number;
34
+ waitForPageResponsiveness: (() => Promise<void>) | null;
34
35
  }) => Promise<ProcessNodeReturnValue>;
@@ -1,5 +1,5 @@
1
1
  import type { ProcessNodeReturnValue } from '../process-node';
2
- export declare const handleTextNode: ({ node, context, logLevel, parentRect, internalState, rootElement, onlyBackgroundClipText, scale, }: {
2
+ export declare const handleTextNode: ({ node, context, logLevel, parentRect, internalState, rootElement, onlyBackgroundClipText, scale, waitForPageResponsiveness, }: {
3
3
  node: Text;
4
4
  context: OffscreenCanvasRenderingContext2D;
5
5
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -25,4 +25,5 @@ export declare const handleTextNode: ({ node, context, logLevel, parentRect, int
25
25
  rootElement: HTMLElement | SVGElement;
26
26
  onlyBackgroundClipText: boolean;
27
27
  scale: number;
28
+ waitForPageResponsiveness: (() => Promise<void>) | null;
28
29
  }) => Promise<ProcessNodeReturnValue>;
@@ -1,5 +1,5 @@
1
1
  type TextDecorationLine = 'underline' | 'overline' | 'line-through';
2
- export type TextDecorationStyle = 'solid' | 'double' | 'dotted' | 'dashed';
2
+ export type TextDecorationStyle = 'solid' | 'double' | 'dotted' | 'dashed' | 'wavy';
3
3
  export type TextDecoration = {
4
4
  lines: TextDecorationLine[];
5
5
  color: string;
@@ -1248,6 +1248,53 @@ var makeVideoSampleSourceCleanup = (encodingConfig) => {
1248
1248
  };
1249
1249
  };
1250
1250
 
1251
+ // src/page-responsiveness.ts
1252
+ var PRESET_INTERVALS = {
1253
+ low: 100,
1254
+ medium: 33,
1255
+ high: 16
1256
+ };
1257
+ var resolvePageResponsivenessInterval = (pageResponsiveness) => {
1258
+ if (pageResponsiveness === "disabled") {
1259
+ return null;
1260
+ }
1261
+ if (pageResponsiveness === "low" || pageResponsiveness === "medium" || pageResponsiveness === "high") {
1262
+ return PRESET_INTERVALS[pageResponsiveness];
1263
+ }
1264
+ if (typeof pageResponsiveness === "number") {
1265
+ if (Number.isNaN(pageResponsiveness)) {
1266
+ throw new Error("`pageResponsiveness` should not be NaN, but is NaN");
1267
+ }
1268
+ if (!Number.isFinite(pageResponsiveness)) {
1269
+ throw new Error(`"pageResponsiveness" must be finite, but is ${pageResponsiveness}`);
1270
+ }
1271
+ if (pageResponsiveness <= 0) {
1272
+ throw new Error(`"pageResponsiveness" must be greater than 0, but is ${pageResponsiveness}`);
1273
+ }
1274
+ return pageResponsiveness;
1275
+ }
1276
+ throw new Error(`"pageResponsiveness" must be one of "disabled", "low", "medium", "high", or a number, but got ${JSON.stringify(pageResponsiveness)}`);
1277
+ };
1278
+ var createPageResponsivenessController = ({
1279
+ intervalInMilliseconds,
1280
+ now,
1281
+ wait
1282
+ }) => {
1283
+ let lastYieldAt = now();
1284
+ return {
1285
+ waitIfNeeded: async () => {
1286
+ if (intervalInMilliseconds === null) {
1287
+ return;
1288
+ }
1289
+ if (now() - lastYieldAt < intervalInMilliseconds) {
1290
+ return;
1291
+ }
1292
+ await wait();
1293
+ lastYieldAt = now();
1294
+ }
1295
+ };
1296
+ };
1297
+
1251
1298
  // src/render-operations-queue.ts
1252
1299
  var onlyOneRenderAtATimeQueue = {
1253
1300
  ref: Promise.resolve()
@@ -2045,6 +2092,38 @@ var filterRequiresPrecompositing = (filter) => {
2045
2092
  }
2046
2093
  return null;
2047
2094
  };
2095
+ var isReplacedElement = (element) => {
2096
+ return element instanceof HTMLImageElement || element instanceof HTMLVideoElement || element instanceof HTMLCanvasElement || element instanceof HTMLIFrameElement || element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement || element instanceof HTMLObjectElement || element instanceof HTMLEmbedElement;
2097
+ };
2098
+ var canApplyCssTransforms = ({
2099
+ element,
2100
+ computedStyle
2101
+ }) => {
2102
+ if (element instanceof SVGElement) {
2103
+ return true;
2104
+ }
2105
+ if (computedStyle.display !== "inline") {
2106
+ return true;
2107
+ }
2108
+ return isReplacedElement(element);
2109
+ };
2110
+ var makeTransformResetter = (element) => {
2111
+ const { transform, scale, rotate } = element.style;
2112
+ return (hasApplicableTransformCssValue) => {
2113
+ if (hasApplicableTransformCssValue) {
2114
+ element.style.transform = "none";
2115
+ element.style.scale = "none";
2116
+ element.style.rotate = "none";
2117
+ }
2118
+ return () => {
2119
+ if (hasApplicableTransformCssValue) {
2120
+ element.style.transform = transform;
2121
+ element.style.scale = scale;
2122
+ element.style.rotate = rotate;
2123
+ }
2124
+ };
2125
+ };
2126
+ };
2048
2127
  var getInternalTransformOrigin = (transform) => {
2049
2128
  const centerX = transform.boundingClientRect.width / 2;
2050
2129
  const centerY = transform.boundingClientRect.height / 2;
@@ -2099,21 +2178,21 @@ var calculateTransforms = ({
2099
2178
  }
2100
2179
  });
2101
2180
  }
2102
- if (hasAnyTransformCssValue(computedStyle) || parent === element) {
2103
- const toParse = hasTransformCssValue(computedStyle) ? computedStyle.transform : undefined;
2181
+ const hasApplicableTransformCssValue = canApplyCssTransforms({ computedStyle, element: parent }) && hasAnyTransformCssValue(computedStyle);
2182
+ if (hasApplicableTransformCssValue || parent === element) {
2183
+ const toParse = hasApplicableTransformCssValue && hasTransformCssValue(computedStyle) ? computedStyle.transform : undefined;
2104
2184
  const matrix = new DOMMatrix(toParse);
2105
- const { transform, scale, rotate } = parent.style;
2185
+ const resetTransforms = makeTransformResetter(parent);
2186
+ const { scale, rotate } = parent.style;
2106
2187
  const additionalMatrices = [];
2107
- if (rotate !== "" && rotate !== "none") {
2188
+ if (hasApplicableTransformCssValue && rotate !== "" && rotate !== "none") {
2108
2189
  additionalMatrices.push(new DOMMatrix(`rotate(${rotate})`));
2109
2190
  }
2110
- if (scale !== "" && scale !== "none") {
2191
+ if (hasApplicableTransformCssValue && scale !== "" && scale !== "none") {
2111
2192
  additionalMatrices.push(new DOMMatrix(`scale(${scale})`));
2112
2193
  }
2113
2194
  additionalMatrices.push(matrix);
2114
- parent.style.transform = "none";
2115
- parent.style.scale = "none";
2116
- parent.style.rotate = "none";
2195
+ const cleanup = resetTransforms(hasApplicableTransformCssValue);
2117
2196
  transforms.push({
2118
2197
  element: parent,
2119
2198
  transformOrigin: computedStyle.transformOrigin,
@@ -2122,9 +2201,7 @@ var calculateTransforms = ({
2122
2201
  });
2123
2202
  const parentRef = parent;
2124
2203
  toReset.push(() => {
2125
- parentRef.style.transform = transform;
2126
- parentRef.style.scale = scale;
2127
- parentRef.style.rotate = rotate;
2204
+ cleanup();
2128
2205
  parentRef.style.transition = originalTransition;
2129
2206
  });
2130
2207
  } else {
@@ -2727,7 +2804,8 @@ var drawBackground = async ({
2727
2804
  logLevel,
2728
2805
  internalState,
2729
2806
  scale,
2730
- onlyBackgroundClipText: true
2807
+ onlyBackgroundClipText: true,
2808
+ waitForPageResponsiveness: null
2731
2809
  });
2732
2810
  onlyBackgroundClipText.setTransform(new DOMMatrix().scale(scale, scale));
2733
2811
  element.style.backgroundClip = originalBackgroundClip;
@@ -3921,7 +3999,8 @@ var processNode = async ({
3921
3999
  parentRect,
3922
4000
  internalState,
3923
4001
  rootElement,
3924
- scale
4002
+ scale,
4003
+ waitForPageResponsiveness
3925
4004
  }) => {
3926
4005
  let __stack = [];
3927
4006
  try {
@@ -3985,8 +4064,12 @@ var processNode = async ({
3985
4064
  logLevel,
3986
4065
  internalState,
3987
4066
  scale,
3988
- onlyBackgroundClipText: false
4067
+ onlyBackgroundClipText: false,
4068
+ waitForPageResponsiveness
3989
4069
  });
4070
+ if (waitForPageResponsiveness !== null) {
4071
+ await waitForPageResponsiveness();
4072
+ }
3990
4073
  let drawable = tempContext.canvas;
3991
4074
  const rectAfterTransforms = roundToExpandRect(scaleRect({
3992
4075
  scale,
@@ -4003,6 +4086,9 @@ var processNode = async ({
4003
4086
  tempContext,
4004
4087
  scale
4005
4088
  });
4089
+ if (waitForPageResponsiveness !== null) {
4090
+ await waitForPageResponsiveness();
4091
+ }
4006
4092
  }
4007
4093
  if (precompositing.needs3DTransformViaWebGL) {
4008
4094
  const t = handle3dTransform({
@@ -4016,6 +4102,9 @@ var processNode = async ({
4016
4102
  if (t) {
4017
4103
  drawable = t;
4018
4104
  }
4105
+ if (waitForPageResponsiveness !== null) {
4106
+ await waitForPageResponsiveness();
4107
+ }
4019
4108
  }
4020
4109
  const previousTransform = context.getTransform();
4021
4110
  context.setTransform(new DOMMatrix);
@@ -4032,6 +4121,9 @@ var processNode = async ({
4032
4121
  drawPrecomposedCanvas();
4033
4122
  }
4034
4123
  context.setTransform(previousTransform);
4124
+ if (waitForPageResponsiveness !== null) {
4125
+ await waitForPageResponsiveness();
4126
+ }
4035
4127
  Internals6.Log.trace({
4036
4128
  logLevel,
4037
4129
  tag: "@remotion/web-renderer"
@@ -4134,10 +4226,7 @@ var getDefaultTextDecorationThickness = (fontSizePx) => {
4134
4226
  return Math.max(1, Number.isFinite(fontSizePx) ? fontSizePx / 16 : 1);
4135
4227
  };
4136
4228
  var getTextDecorationStyle = (style) => {
4137
- if (style === "wavy") {
4138
- return null;
4139
- }
4140
- if (style === "double" || style === "dotted" || style === "dashed") {
4229
+ if (style === "double" || style === "dotted" || style === "dashed" || style === "wavy") {
4141
4230
  return style;
4142
4231
  }
4143
4232
  return "solid";
@@ -4147,9 +4236,6 @@ var parseTextDecoration = ({
4147
4236
  style
4148
4237
  }) => {
4149
4238
  const textDecorationStyle = getTextDecorationStyle(style.getPropertyValue("text-decoration-style").trim());
4150
- if (textDecorationStyle === null) {
4151
- return null;
4152
- }
4153
4239
  const textDecorationLine = style.getPropertyValue("text-decoration-line");
4154
4240
  const lineParts = textDecorationLine.split(/\s+/);
4155
4241
  const lines = textDecorationLines.filter((line) => lineParts.includes(line));
@@ -4223,6 +4309,34 @@ var getTextDecorationLineDashPattern = (style, thickness) => {
4223
4309
  }
4224
4310
  return [];
4225
4311
  };
4312
+ var drawWavyTextDecorationLine = ({
4313
+ contextToDraw,
4314
+ endX,
4315
+ lineY,
4316
+ startX,
4317
+ thickness
4318
+ }) => {
4319
+ const step = Math.max(2, thickness);
4320
+ const amplitude = step;
4321
+ const halfWavelength = step * 2;
4322
+ const wavelength = halfWavelength * 2;
4323
+ const phaseStartX = Math.floor(startX / wavelength) * wavelength;
4324
+ contextToDraw.save();
4325
+ contextToDraw.beginPath();
4326
+ contextToDraw.rect(startX, lineY - amplitude - thickness, endX - startX, (amplitude + thickness) * 2);
4327
+ contextToDraw.clip();
4328
+ contextToDraw.beginPath();
4329
+ contextToDraw.moveTo(phaseStartX, lineY);
4330
+ let x = phaseStartX;
4331
+ let direction = 1;
4332
+ while (x < endX) {
4333
+ contextToDraw.quadraticCurveTo(x + halfWavelength / 2, lineY + direction * amplitude * 2, x + halfWavelength, lineY);
4334
+ x += halfWavelength;
4335
+ direction = -direction;
4336
+ }
4337
+ contextToDraw.stroke();
4338
+ contextToDraw.restore();
4339
+ };
4226
4340
  var strokeTextDecorationLine = ({
4227
4341
  contextToDraw,
4228
4342
  endX,
@@ -4264,6 +4378,17 @@ var drawTextDecoration = ({
4264
4378
  thickness: textDecoration.thickness,
4265
4379
  fontSizePx
4266
4380
  });
4381
+ if (textDecoration.style === "wavy") {
4382
+ contextToDraw.setLineDash([]);
4383
+ drawWavyTextDecorationLine({
4384
+ contextToDraw,
4385
+ endX,
4386
+ lineY,
4387
+ startX,
4388
+ thickness: textDecoration.thickness
4389
+ });
4390
+ continue;
4391
+ }
4267
4392
  if (textDecoration.style === "double") {
4268
4393
  contextToDraw.setLineDash([]);
4269
4394
  strokeTextDecorationLine({
@@ -4426,7 +4551,8 @@ var handleTextNode = async ({
4426
4551
  internalState,
4427
4552
  rootElement,
4428
4553
  onlyBackgroundClipText,
4429
- scale
4554
+ scale,
4555
+ waitForPageResponsiveness
4430
4556
  }) => {
4431
4557
  const span = document.createElement("span");
4432
4558
  const parent = node.parentNode;
@@ -4443,7 +4569,8 @@ var handleTextNode = async ({
4443
4569
  parentRect,
4444
4570
  internalState,
4445
4571
  rootElement,
4446
- scale
4572
+ scale,
4573
+ waitForPageResponsiveness
4447
4574
  });
4448
4575
  parent.insertBefore(node, span);
4449
4576
  parent.removeChild(span);
@@ -4459,7 +4586,8 @@ var walkOverNode = ({
4459
4586
  internalState,
4460
4587
  rootElement,
4461
4588
  onlyBackgroundClipText,
4462
- scale
4589
+ scale,
4590
+ waitForPageResponsiveness
4463
4591
  }) => {
4464
4592
  if (node instanceof HTMLElement || node instanceof SVGElement) {
4465
4593
  return processNode({
@@ -4470,7 +4598,8 @@ var walkOverNode = ({
4470
4598
  parentRect,
4471
4599
  internalState,
4472
4600
  rootElement,
4473
- scale
4601
+ scale,
4602
+ waitForPageResponsiveness
4474
4603
  });
4475
4604
  }
4476
4605
  if (node instanceof Text) {
@@ -4482,7 +4611,8 @@ var walkOverNode = ({
4482
4611
  internalState,
4483
4612
  rootElement,
4484
4613
  onlyBackgroundClipText,
4485
- scale
4614
+ scale,
4615
+ waitForPageResponsiveness
4486
4616
  });
4487
4617
  }
4488
4618
  throw new Error("Unknown node type");
@@ -4509,7 +4639,8 @@ var compose = async ({
4509
4639
  parentRect,
4510
4640
  internalState,
4511
4641
  onlyBackgroundClipText,
4512
- scale
4642
+ scale,
4643
+ waitForPageResponsiveness
4513
4644
  }) => {
4514
4645
  let __stack = [];
4515
4646
  try {
@@ -4532,7 +4663,8 @@ var compose = async ({
4532
4663
  internalState,
4533
4664
  rootElement: element,
4534
4665
  onlyBackgroundClipText,
4535
- scale
4666
+ scale,
4667
+ waitForPageResponsiveness
4536
4668
  });
4537
4669
  if (val.type === "skip-children") {
4538
4670
  if (!skipToNextNonDescendant(treeWalker)) {
@@ -4546,6 +4678,9 @@ var compose = async ({
4546
4678
  break;
4547
4679
  }
4548
4680
  }
4681
+ if (waitForPageResponsiveness !== null) {
4682
+ await waitForPageResponsiveness();
4683
+ }
4549
4684
  }
4550
4685
  } catch (_catch) {
4551
4686
  var _err = _catch, _hasErr = 1;
@@ -4563,7 +4698,8 @@ var createLayer = async ({
4563
4698
  onlyBackgroundClipText,
4564
4699
  cutout,
4565
4700
  htmlInCanvasContext,
4566
- onHtmlInCanvasLayerOutcome
4701
+ onHtmlInCanvasLayerOutcome,
4702
+ waitForPageResponsiveness
4567
4703
  }) => {
4568
4704
  const scaledWidth = Math.ceil(cutout.width * scale);
4569
4705
  const scaledHeight = Math.ceil(cutout.height * scale);
@@ -4599,7 +4735,8 @@ var createLayer = async ({
4599
4735
  parentRect: cutout,
4600
4736
  internalState,
4601
4737
  onlyBackgroundClipText,
4602
- scale
4738
+ scale,
4739
+ waitForPageResponsiveness
4603
4740
  });
4604
4741
  return context;
4605
4742
  };
@@ -4827,6 +4964,7 @@ var internalRenderMediaOnWeb = async ({
4827
4964
  transparent,
4828
4965
  onArtifact,
4829
4966
  onFrame,
4967
+ pageResponsiveness,
4830
4968
  outputTarget: userDesiredOutputTarget,
4831
4969
  licenseKey,
4832
4970
  muted,
@@ -4838,6 +4976,7 @@ var internalRenderMediaOnWeb = async ({
4838
4976
  let __stack2 = [];
4839
4977
  try {
4840
4978
  validateScale(scale);
4979
+ const pageResponsivenessIntervalInMilliseconds = resolvePageResponsivenessInterval(pageResponsiveness);
4841
4980
  let htmlInCanvasLayerOutcomeReported = false;
4842
4981
  const onHtmlInCanvasLayerOutcome = (outcome) => {
4843
4982
  if (htmlInCanvasLayerOutcomeReported) {
@@ -4949,6 +5088,19 @@ var internalRenderMediaOnWeb = async ({
4949
5088
  });
4950
5089
  }
4951
5090
  const internalState = __using(__stack2, makeInternalState(), 0);
5091
+ const pageResponsivenessController = createPageResponsivenessController({
5092
+ intervalInMilliseconds: pageResponsivenessIntervalInMilliseconds,
5093
+ now: () => performance.now(),
5094
+ wait: () => new Promise((resolve) => {
5095
+ setTimeout(resolve, 0);
5096
+ })
5097
+ });
5098
+ const waitForPageResponsiveness = async () => {
5099
+ await pageResponsivenessController.waitIfNeeded();
5100
+ if (signal?.aborted) {
5101
+ throw new Error("renderMediaOnWeb() was cancelled");
5102
+ }
5103
+ };
4952
5104
  const keepalive = __using(__stack2, createBackgroundKeepalive({
4953
5105
  fps: resolved.fps,
4954
5106
  logLevel
@@ -5062,13 +5214,15 @@ var internalRenderMediaOnWeb = async ({
5062
5214
  onlyBackgroundClipText: false,
5063
5215
  cutout: new DOMRect(0, 0, resolved.width, resolved.height),
5064
5216
  htmlInCanvasContext,
5065
- onHtmlInCanvasLayerOutcome: htmlInCanvasContext ? onHtmlInCanvasLayerOutcome : undefined
5217
+ onHtmlInCanvasLayerOutcome: htmlInCanvasContext ? onHtmlInCanvasLayerOutcome : undefined,
5218
+ waitForPageResponsiveness
5066
5219
  });
5067
5220
  internalState.addCreateFrameTime(performance.now() - createFrameStart);
5068
5221
  layerCanvas = layer.canvas;
5069
5222
  if (signal?.aborted) {
5070
5223
  throw new Error("renderMediaOnWeb() was cancelled");
5071
5224
  }
5225
+ await waitForPageResponsiveness();
5072
5226
  const videoFrame = new VideoFrame(layer.canvas, {
5073
5227
  timestamp
5074
5228
  });
@@ -5085,6 +5239,7 @@ var internalRenderMediaOnWeb = async ({
5085
5239
  expectedHeight: Math.round(resolved.height * scale),
5086
5240
  expectedTimestamp: timestamp
5087
5241
  });
5242
+ await waitForPageResponsiveness();
5088
5243
  }
5089
5244
  }
5090
5245
  const now = Date.now();
@@ -5110,11 +5265,10 @@ var internalRenderMediaOnWeb = async ({
5110
5265
  onArtifact
5111
5266
  });
5112
5267
  }
5113
- if (signal?.aborted) {
5114
- throw new Error("renderMediaOnWeb() was cancelled");
5115
- }
5268
+ await waitForPageResponsiveness();
5116
5269
  const audio = muted ? null : onlyInlineAudio({ assets, fps: resolved.fps, timestamp, sampleRate });
5117
5270
  internalState.addAudioMixingTime(performance.now() - audioCombineStart);
5271
+ await waitForPageResponsiveness();
5118
5272
  const addSampleStart = performance.now();
5119
5273
  const encodingPromises = [];
5120
5274
  if (frameToEncode && videoSampleSource) {
@@ -5133,8 +5287,10 @@ var internalRenderMediaOnWeb = async ({
5133
5287
  if (signal?.aborted) {
5134
5288
  throw new Error("renderMediaOnWeb() was cancelled");
5135
5289
  }
5290
+ await waitForPageResponsiveness();
5136
5291
  }
5137
5292
  onProgress?.(getProgressPayload());
5293
+ await waitForPageResponsiveness();
5138
5294
  videoSampleSource?.videoSampleSource.close();
5139
5295
  audioSampleSource?.audioSampleSource.close();
5140
5296
  await outputWithCleanup.output.finalize();
@@ -5222,6 +5378,7 @@ var renderMediaOnWeb = (options) => {
5222
5378
  transparent: options.transparent ?? false,
5223
5379
  onArtifact: options.onArtifact ?? null,
5224
5380
  onFrame: options.onFrame ?? null,
5381
+ pageResponsiveness: options.pageResponsiveness ?? "medium",
5225
5382
  outputTarget: options.outputTarget ?? null,
5226
5383
  licenseKey: options.licenseKey ?? null,
5227
5384
  muted: options.muted ?? false,
@@ -5387,7 +5544,8 @@ async function internalRenderStillOnWeb({
5387
5544
  onlyBackgroundClipText: false,
5388
5545
  cutout: new DOMRect(0, 0, resolved.width, resolved.height),
5389
5546
  htmlInCanvasContext,
5390
- onHtmlInCanvasLayerOutcome: htmlInCanvasContext ? onHtmlInCanvasLayerOutcome : undefined
5547
+ onHtmlInCanvasLayerOutcome: htmlInCanvasContext ? onHtmlInCanvasLayerOutcome : undefined,
5548
+ waitForPageResponsiveness: null
5391
5549
  });
5392
5550
  const { canvas } = capturedFrame;
5393
5551
  const assets = collectAssets.current.collectAssets();
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export { getEncodableAudioCodecs, getEncodableVideoCodecs, type GetEncodableAudi
7
7
  export { getDefaultAudioCodecForContainer, getDefaultContainerForCodec, getDefaultVideoCodecForContainer, getSupportedAudioCodecsForContainer, getSupportedVideoCodecsForContainer, isAudioOnlyContainer, } from './mediabunny-mappings';
8
8
  export type { WebRendererAudioCodec, WebRendererContainer, WebRendererQuality, WebRendererVideoCodec, } from './mediabunny-mappings';
9
9
  export type { WebRendererOutputTarget } from './output-target';
10
+ export type { WebRendererPageResponsiveness } from './page-responsiveness';
10
11
  export { renderMediaOnWeb } from './render-media-on-web';
11
12
  export type { RenderMediaOnWebOptions, RenderMediaOnWebProgress, RenderMediaOnWebProgressCallback, RenderMediaOnWebResult, WebRendererHardwareAcceleration, } from './render-media-on-web';
12
13
  export { renderStillOnWeb } from './render-still-on-web';
@@ -0,0 +1,9 @@
1
+ export type WebRendererPageResponsiveness = 'disabled' | 'low' | 'medium' | 'high' | number;
2
+ export declare const resolvePageResponsivenessInterval: (pageResponsiveness: unknown) => number | null;
3
+ export declare const createPageResponsivenessController: ({ intervalInMilliseconds, now, wait, }: {
4
+ intervalInMilliseconds: number | null;
5
+ now: () => number;
6
+ wait: () => Promise<void>;
7
+ }) => {
8
+ waitIfNeeded: () => Promise<void>;
9
+ };
@@ -7,6 +7,7 @@ import type { InternalState } from './internal-state';
7
7
  import type { WebRendererAudioCodec, WebRendererContainer, WebRendererQuality } from './mediabunny-mappings';
8
8
  import { type WebRendererVideoCodec } from './mediabunny-mappings';
9
9
  import type { WebRendererOutputTarget } from './output-target';
10
+ import { type WebRendererPageResponsiveness } from './page-responsiveness';
10
11
  import type { CompositionCalculateMetadataOrExplicit } from './props-if-has-props';
11
12
  import { type OnFrameCallback } from './validate-video-frame';
12
13
  export type InputPropsIfHasProps<Schema extends $ZodObject, Props> = $ZodObject extends Schema ? {} extends Props ? {
@@ -60,6 +61,7 @@ type OptionalRenderMediaOnWebOptions<Schema extends $ZodObject> = {
60
61
  transparent: boolean;
61
62
  onArtifact: WebRendererOnArtifact | null;
62
63
  onFrame: OnFrameCallback | null;
64
+ pageResponsiveness: WebRendererPageResponsiveness;
63
65
  outputTarget: WebRendererOutputTarget | null;
64
66
  licenseKey: string | null;
65
67
  isProduction: boolean;
@@ -6,7 +6,7 @@ export type HtmlInCanvasLayerOutcome = {
6
6
  reason: string;
7
7
  shouldWarn: boolean;
8
8
  };
9
- export declare const createLayer: ({ element, scale, logLevel, internalState, onlyBackgroundClipText, cutout, htmlInCanvasContext, onHtmlInCanvasLayerOutcome, }: {
9
+ export declare const createLayer: ({ element, scale, logLevel, internalState, onlyBackgroundClipText, cutout, htmlInCanvasContext, onHtmlInCanvasLayerOutcome, waitForPageResponsiveness, }: {
10
10
  element: HTMLElement | SVGElement;
11
11
  scale: number;
12
12
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -32,4 +32,5 @@ export declare const createLayer: ({ element, scale, logLevel, internalState, on
32
32
  cutout: DOMRect;
33
33
  htmlInCanvasContext?: HtmlInCanvasContext | null | undefined;
34
34
  onHtmlInCanvasLayerOutcome?: ((outcome: HtmlInCanvasLayerOutcome) => void) | undefined;
35
+ waitForPageResponsiveness: (() => Promise<void>) | null;
35
36
  }) => Promise<OffscreenCanvasRenderingContext2D>;
@@ -1,5 +1,5 @@
1
1
  import type { ProcessNodeReturnValue } from './drawing/process-node';
2
- export declare const walkOverNode: ({ node, context, logLevel, parentRect, internalState, rootElement, onlyBackgroundClipText, scale, }: {
2
+ export declare const walkOverNode: ({ node, context, logLevel, parentRect, internalState, rootElement, onlyBackgroundClipText, scale, waitForPageResponsiveness, }: {
3
3
  node: Node;
4
4
  context: OffscreenCanvasRenderingContext2D;
5
5
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -25,4 +25,5 @@ export declare const walkOverNode: ({ node, context, logLevel, parentRect, inter
25
25
  rootElement: HTMLElement | SVGElement;
26
26
  onlyBackgroundClipText: boolean;
27
27
  scale: number;
28
+ waitForPageResponsiveness: (() => Promise<void>) | null;
28
29
  }) => Promise<ProcessNodeReturnValue>;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/web-renderer"
4
4
  },
5
5
  "name": "@remotion/web-renderer",
6
- "version": "4.0.485",
6
+ "version": "4.0.487",
7
7
  "main": "dist/index.js",
8
8
  "type": "module",
9
9
  "scripts": {
@@ -19,22 +19,22 @@
19
19
  "author": "Remotion <jonny@remotion.dev>",
20
20
  "license": "SEE LICENSE IN LICENSE.md",
21
21
  "dependencies": {
22
- "@mediabunny/mp3-encoder": "1.50.3",
23
- "@mediabunny/aac-encoder": "1.50.3",
24
- "@mediabunny/flac-encoder": "1.50.3",
25
- "@remotion/licensing": "4.0.485",
26
- "remotion": "4.0.485",
27
- "mediabunny": "1.50.3"
22
+ "@mediabunny/mp3-encoder": "1.50.7",
23
+ "@mediabunny/aac-encoder": "1.50.7",
24
+ "@mediabunny/flac-encoder": "1.50.7",
25
+ "@remotion/licensing": "4.0.487",
26
+ "remotion": "4.0.487",
27
+ "mediabunny": "1.50.7"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@react-three/fiber": "9.2.0",
31
- "@remotion/eslint-config-internal": "4.0.485",
32
- "@remotion/paths": "4.0.485",
33
- "@remotion/player": "4.0.485",
34
- "@remotion/media": "4.0.485",
35
- "@remotion/shapes": "4.0.485",
36
- "@remotion/three": "4.0.485",
37
- "@remotion/transitions": "4.0.485",
31
+ "@remotion/eslint-config-internal": "4.0.487",
32
+ "@remotion/paths": "4.0.487",
33
+ "@remotion/player": "4.0.487",
34
+ "@remotion/media": "4.0.487",
35
+ "@remotion/shapes": "4.0.487",
36
+ "@remotion/three": "4.0.487",
37
+ "@remotion/transitions": "4.0.487",
38
38
  "@types/three": "0.170.0",
39
39
  "@typescript/native-preview": "7.0.0-dev.20260217.1",
40
40
  "@vitejs/plugin-react": "4.3.4",