@remotion/web-renderer 4.0.375 → 4.0.376

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.
Files changed (46) hide show
  1. package/dist/artifact.d.ts +24 -0
  2. package/dist/artifact.js +59 -0
  3. package/dist/calculate-transforms.d.ts +9 -0
  4. package/dist/calculate-transforms.js +61 -0
  5. package/dist/composable.d.ts +3 -0
  6. package/dist/compose-canvas.d.ts +1 -0
  7. package/dist/compose-canvas.js +14 -0
  8. package/dist/compose-svg.d.ts +9 -0
  9. package/dist/compose-svg.js +29 -0
  10. package/dist/compose.js +6 -36
  11. package/dist/create-scaffold.d.ts +31 -0
  12. package/dist/create-scaffold.js +92 -0
  13. package/dist/esm/index.mjs +7844 -171
  14. package/dist/find-capturable-elements.d.ts +2 -0
  15. package/dist/find-capturable-elements.js +28 -0
  16. package/dist/frame-range.d.ts +2 -0
  17. package/dist/frame-range.js +15 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.js +1 -0
  20. package/dist/mediabunny-mappings.d.ts +9 -0
  21. package/dist/mediabunny-mappings.js +53 -0
  22. package/dist/parse-transform-origin.d.ts +4 -0
  23. package/dist/parse-transform-origin.js +7 -0
  24. package/dist/props-if-has-props.d.ts +37 -0
  25. package/dist/props-if-has-props.js +1 -0
  26. package/dist/render-media-on-web.d.ts +45 -0
  27. package/dist/render-media-on-web.js +190 -0
  28. package/dist/render-still-on-web.d.ts +17 -12
  29. package/dist/render-still-on-web.js +69 -104
  30. package/dist/take-screenshot.d.ts +12 -0
  31. package/dist/take-screenshot.js +18 -0
  32. package/dist/throttle-progress.d.ts +6 -0
  33. package/dist/throttle-progress.js +43 -0
  34. package/dist/update-time.d.ts +14 -0
  35. package/dist/update-time.js +17 -0
  36. package/dist/validate-video-frame.d.ts +16 -0
  37. package/dist/validate-video-frame.js +34 -0
  38. package/dist/wait-for-ready.d.ts +6 -1
  39. package/dist/wait-for-ready.js +18 -14
  40. package/package.json +12 -8
  41. package/dist/error-boundary.d.ts +0 -16
  42. package/dist/error-boundary.js +0 -20
  43. package/dist/find-canvas-elements.d.ts +0 -2
  44. package/dist/find-canvas-elements.js +0 -12
  45. package/dist/find-svg-elements.d.ts +0 -2
  46. package/dist/find-svg-elements.js +0 -12
@@ -0,0 +1,24 @@
1
+ import type { DownloadBehavior, TRenderAsset } from 'remotion';
2
+ export type EmittedArtifact = {
3
+ filename: string;
4
+ content: string | Uint8Array;
5
+ frame: number;
6
+ downloadBehavior: DownloadBehavior | null;
7
+ };
8
+ export declare const onlyArtifact: ({ assets, frameBuffer, }: {
9
+ assets: TRenderAsset[];
10
+ frameBuffer: Blob | OffscreenCanvas | null;
11
+ }) => Promise<EmittedArtifact[]>;
12
+ export type OnArtifact = (asset: EmittedArtifact) => void;
13
+ export type ArtifactsRef = React.RefObject<{
14
+ collectAssets: () => TRenderAsset[];
15
+ } | null>;
16
+ export declare const handleArtifacts: ({ ref, onArtifact, }: {
17
+ ref: ArtifactsRef;
18
+ onArtifact: OnArtifact | null;
19
+ }) => {
20
+ handle: ({ imageData, frame, }: {
21
+ imageData: Blob | OffscreenCanvas | null;
22
+ frame: number;
23
+ }) => Promise<void>;
24
+ };
@@ -0,0 +1,59 @@
1
+ import { NoReactInternals } from 'remotion/no-react';
2
+ export const onlyArtifact = async ({ assets, frameBuffer, }) => {
3
+ const artifacts = assets.filter((asset) => asset.type === 'artifact');
4
+ let frameBufferUint8 = null;
5
+ const result = [];
6
+ for (const artifact of artifacts) {
7
+ if (artifact.contentType === 'binary' || artifact.contentType === 'text') {
8
+ result.push({
9
+ frame: artifact.frame,
10
+ content: artifact.content,
11
+ filename: artifact.filename,
12
+ downloadBehavior: artifact.downloadBehavior,
13
+ });
14
+ continue;
15
+ }
16
+ if (artifact.contentType === 'thumbnail') {
17
+ if (frameBuffer === null) {
18
+ // A thumbnail artifact was defined to be emitted, but the output was not a video.
19
+ // Also, in Lambda, there are extra frames which are not video frames.
20
+ // This could happen if a thumbnail is unconditionally emitted.
21
+ continue;
22
+ }
23
+ const ab = frameBuffer instanceof Blob
24
+ ? await frameBuffer.arrayBuffer()
25
+ : new Uint8Array(await (await frameBuffer.convertToBlob({ type: 'image/png' })).arrayBuffer());
26
+ frameBufferUint8 = new Uint8Array(ab);
27
+ result.push({
28
+ frame: artifact.frame,
29
+ content: frameBufferUint8,
30
+ filename: artifact.filename,
31
+ downloadBehavior: artifact.downloadBehavior,
32
+ });
33
+ continue;
34
+ }
35
+ throw new Error('Unknown artifact type: ' + artifact);
36
+ }
37
+ return result.filter(NoReactInternals.truthy);
38
+ };
39
+ export const handleArtifacts = ({ ref, onArtifact, }) => {
40
+ const previousArtifacts = [];
41
+ const handle = async ({ imageData, frame, }) => {
42
+ const artifactAssets = ref.current.collectAssets();
43
+ if (onArtifact) {
44
+ const artifacts = await onlyArtifact({
45
+ assets: artifactAssets,
46
+ frameBuffer: imageData,
47
+ });
48
+ for (const artifact of artifacts) {
49
+ const previousArtifact = previousArtifacts.find((a) => a.filename === artifact.filename);
50
+ if (previousArtifact) {
51
+ throw new Error(`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`);
52
+ }
53
+ onArtifact(artifact);
54
+ previousArtifacts.push({ frame, filename: artifact.filename });
55
+ }
56
+ }
57
+ };
58
+ return { handle };
59
+ };
@@ -0,0 +1,9 @@
1
+ export declare const calculateTransforms: (element: HTMLElement | SVGSVGElement) => {
2
+ dimensions: DOMRect;
3
+ totalMatrix: DOMMatrix;
4
+ reset: () => void;
5
+ nativeTransformOrigin: {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ };
@@ -0,0 +1,61 @@
1
+ import { parseTransformOrigin } from './parse-transform-origin';
2
+ export const calculateTransforms = (element) => {
3
+ var _a, _b;
4
+ // Compute the cumulative transform by traversing parent nodes
5
+ let parent = element;
6
+ const transforms = [];
7
+ const toReset = [];
8
+ while (parent) {
9
+ const computedStyle = getComputedStyle(parent);
10
+ if ((computedStyle.transform && computedStyle.transform !== 'none') ||
11
+ parent === element) {
12
+ const toParse = computedStyle.transform === 'none' || computedStyle.transform === ''
13
+ ? undefined
14
+ : computedStyle.transform;
15
+ const matrix = new DOMMatrix(toParse);
16
+ const { transform } = parent.style;
17
+ parent.style.transform = 'none';
18
+ transforms.push({
19
+ matrix,
20
+ rect: parent,
21
+ transformOrigin: computedStyle.transformOrigin,
22
+ boundingClientRect: null,
23
+ });
24
+ const parentRef = parent;
25
+ toReset.push(() => {
26
+ parentRef.style.transform = transform;
27
+ });
28
+ }
29
+ parent = parent.parentElement;
30
+ }
31
+ for (const transform of transforms) {
32
+ transform.boundingClientRect = transform.rect.getBoundingClientRect();
33
+ }
34
+ const dimensions = transforms[0].boundingClientRect;
35
+ const nativeTransformOrigin = (_a = parseTransformOrigin(transforms[0].transformOrigin)) !== null && _a !== void 0 ? _a : { x: dimensions.width / 2, y: dimensions.height / 2 };
36
+ let totalMatrix = new DOMMatrix();
37
+ for (const transform of transforms.slice().reverse()) {
38
+ if (!transform.boundingClientRect) {
39
+ throw new Error('Bounding client rect not found');
40
+ }
41
+ const centerX = transform.boundingClientRect.width / 2;
42
+ const centerY = transform.boundingClientRect.height / 2;
43
+ const { x: originX, y: originY } = (_b = parseTransformOrigin(transform.transformOrigin)) !== null && _b !== void 0 ? _b : { x: centerX, y: centerY };
44
+ const deviationFromX = centerX - originX;
45
+ const deviationFromY = centerY - originY;
46
+ totalMatrix = totalMatrix
47
+ .translate(-deviationFromX, -deviationFromY)
48
+ .multiply(transform.matrix)
49
+ .translate(deviationFromX, deviationFromY);
50
+ }
51
+ return {
52
+ dimensions,
53
+ totalMatrix,
54
+ reset: () => {
55
+ for (const reset of toReset) {
56
+ reset();
57
+ }
58
+ },
59
+ nativeTransformOrigin,
60
+ };
61
+ };
@@ -4,4 +4,7 @@ export type Composable = {
4
4
  } | {
5
5
  type: 'svg';
6
6
  element: SVGSVGElement;
7
+ } | {
8
+ type: 'img';
9
+ element: HTMLImageElement;
7
10
  };
@@ -0,0 +1 @@
1
+ export declare const composeCanvas: (canvas: HTMLCanvasElement | HTMLImageElement, context: OffscreenCanvasRenderingContext2D) => void;
@@ -0,0 +1,14 @@
1
+ import { calculateTransforms } from './calculate-transforms';
2
+ export const composeCanvas = (canvas, context) => {
3
+ const { totalMatrix, reset, dimensions, nativeTransformOrigin } = calculateTransforms(canvas);
4
+ const translateX = nativeTransformOrigin.x + dimensions.left;
5
+ const translateY = nativeTransformOrigin.y + dimensions.top;
6
+ const matrix = new DOMMatrix()
7
+ .translate(translateX, translateY)
8
+ .multiply(totalMatrix)
9
+ .translate(-translateX, -translateY);
10
+ context.setTransform(matrix);
11
+ context.drawImage(canvas, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
12
+ context.setTransform(new DOMMatrix());
13
+ reset();
14
+ };
@@ -0,0 +1,9 @@
1
+ type ImgDrawable = {
2
+ image: HTMLImageElement;
3
+ width: number;
4
+ height: number;
5
+ left: number;
6
+ top: number;
7
+ };
8
+ export declare const svgToImageBitmap: (svg: SVGSVGElement) => Promise<ImgDrawable | null>;
9
+ export {};
@@ -0,0 +1,29 @@
1
+ import { calculateTransforms } from './calculate-transforms';
2
+ export const svgToImageBitmap = (svg) => {
3
+ const { dimensions: svgDimensions, totalMatrix, reset, } = calculateTransforms(svg);
4
+ const originalTransform = svg.style.transform;
5
+ const originalTransformOrigin = svg.style.transformOrigin;
6
+ svg.style.transform = totalMatrix.toString();
7
+ svg.style.transformOrigin = '50% 50%';
8
+ const svgData = new XMLSerializer().serializeToString(svg);
9
+ svg.style.transform = originalTransform;
10
+ svg.style.transformOrigin = originalTransformOrigin;
11
+ reset();
12
+ return new Promise((resolve, reject) => {
13
+ const image = new Image(svgDimensions.width, svgDimensions.height);
14
+ const url = `data:image/svg+xml;base64,${btoa(svgData)}`;
15
+ image.onload = function () {
16
+ resolve({
17
+ image,
18
+ width: svgDimensions.width,
19
+ height: svgDimensions.height,
20
+ left: svgDimensions.left,
21
+ top: svgDimensions.top,
22
+ });
23
+ };
24
+ image.onerror = () => {
25
+ reject(new Error('Failed to convert SVG to image'));
26
+ };
27
+ image.src = url;
28
+ });
29
+ };
package/dist/compose.js CHANGED
@@ -1,31 +1,5 @@
1
- const svgToImageBitmap = (svg) => {
2
- const computedStyle = getComputedStyle(svg);
3
- const { transform: originalTransform } = computedStyle;
4
- svg.style.transform = 'none';
5
- const svgDimensions = svg.getBoundingClientRect();
6
- svg.style.transform = originalTransform;
7
- if (svgDimensions.width === 0 || svgDimensions.height === 0) {
8
- return Promise.resolve(null);
9
- }
10
- const svgData = new XMLSerializer().serializeToString(svg);
11
- return new Promise((resolve, reject) => {
12
- const image = new Image(svgDimensions.width, svgDimensions.height);
13
- const url = 'data:image/svg+xml;base64,' + window.btoa(svgData);
14
- image.onload = function () {
15
- resolve({
16
- image,
17
- width: svgDimensions.width,
18
- height: svgDimensions.height,
19
- left: svgDimensions.left,
20
- top: svgDimensions.top,
21
- });
22
- };
23
- image.onerror = () => {
24
- reject(new Error('Failed to convert SVG to image'));
25
- };
26
- image.src = url;
27
- });
28
- };
1
+ import { composeCanvas } from './compose-canvas';
2
+ import { svgToImageBitmap } from './compose-svg';
29
3
  export const compose = async ({ composables, width, height, }) => {
30
4
  const canvas = new OffscreenCanvas(width, height);
31
5
  const context = canvas.getContext('2d');
@@ -34,18 +8,14 @@ export const compose = async ({ composables, width, height, }) => {
34
8
  }
35
9
  // TODO: Consider z-index
36
10
  for (const composable of composables) {
37
- if (composable.type === 'canvas') {
38
- const boundingClientRect = composable.element.getBoundingClientRect();
39
- context.drawImage(composable.element, boundingClientRect.left, boundingClientRect.top);
11
+ if (composable.type === 'canvas' || composable.type === 'img') {
12
+ composeCanvas(composable.element, context);
40
13
  }
41
14
  else if (composable.type === 'svg') {
42
- // This already takes care of the "transform" of the SVG
43
- // but not of the transforms of the parent
15
+ // This already accumulates the transforms of the parent
44
16
  const imageBitmap = await svgToImageBitmap(composable.element);
45
17
  if (imageBitmap) {
46
- // transform origin
47
- // Don't need to get transform from SVG
48
- context.drawImage(imageBitmap.image, imageBitmap.left, imageBitmap.top);
18
+ context.drawImage(imageBitmap.image, imageBitmap.left, imageBitmap.top, imageBitmap.width, imageBitmap.height);
49
19
  }
50
20
  }
51
21
  }
@@ -0,0 +1,31 @@
1
+ import { type ComponentType } from 'react';
2
+ import type { Codec, LogLevel, TRenderAsset } from 'remotion';
3
+ import { type _InternalTypes } from 'remotion';
4
+ import type { AnyZodObject } from 'zod';
5
+ import type { TimeUpdaterRef } from './update-time';
6
+ export declare function createScaffold<Props extends Record<string, unknown>>({ width, height, delayRenderTimeoutInMilliseconds, logLevel, resolvedProps, id, mediaCacheSizeInBytes, durationInFrames, fps, initialFrame, schema, Component, audioEnabled, videoEnabled, defaultCodec, defaultOutName, }: {
7
+ width: number;
8
+ height: number;
9
+ delayRenderTimeoutInMilliseconds: number;
10
+ logLevel: LogLevel;
11
+ resolvedProps: Record<string, unknown>;
12
+ id: string;
13
+ mediaCacheSizeInBytes: number | null;
14
+ initialFrame: number;
15
+ durationInFrames: number;
16
+ fps: number;
17
+ schema: AnyZodObject | null;
18
+ Component: ComponentType<Props>;
19
+ audioEnabled: boolean;
20
+ videoEnabled: boolean;
21
+ defaultCodec: Codec | null;
22
+ defaultOutName: string | null;
23
+ }): Promise<{
24
+ delayRenderScope: _InternalTypes['DelayRenderScope'];
25
+ div: HTMLDivElement;
26
+ cleanupScaffold: () => void;
27
+ timeUpdater: React.RefObject<TimeUpdaterRef | null>;
28
+ collectAssets: React.RefObject<{
29
+ collectAssets: () => TRenderAsset[];
30
+ } | null>;
31
+ }>;
@@ -0,0 +1,92 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createRef } from 'react';
3
+ import { flushSync } from 'react-dom';
4
+ import ReactDOM from 'react-dom/client';
5
+ import { Internals } from 'remotion';
6
+ import { UpdateTime } from './update-time';
7
+ import { withResolvers } from './with-resolvers';
8
+ export async function createScaffold({ width, height, delayRenderTimeoutInMilliseconds, logLevel, resolvedProps, id, mediaCacheSizeInBytes, durationInFrames, fps, initialFrame, schema, Component, audioEnabled, videoEnabled, defaultCodec, defaultOutName, }) {
9
+ if (!ReactDOM.createRoot) {
10
+ throw new Error('@remotion/web-renderer requires React 18 or higher');
11
+ }
12
+ const div = document.createElement('div');
13
+ // Match same behavior as renderEntry.tsx
14
+ div.style.display = 'flex';
15
+ div.style.backgroundColor = 'transparent';
16
+ div.style.position = 'fixed';
17
+ div.style.width = `${width}px`;
18
+ div.style.height = `${height}px`;
19
+ div.style.zIndex = '-9999';
20
+ document.body.appendChild(div);
21
+ const { promise, resolve, reject } = withResolvers();
22
+ // TODO: This might not work in React 18
23
+ const root = ReactDOM.createRoot(div, {
24
+ onUncaughtError: (err) => {
25
+ reject(err);
26
+ },
27
+ });
28
+ const delayRenderScope = {
29
+ remotion_renderReady: true,
30
+ remotion_delayRenderTimeouts: {},
31
+ remotion_puppeteerTimeout: delayRenderTimeoutInMilliseconds,
32
+ remotion_attempt: 0,
33
+ };
34
+ const timeUpdater = createRef();
35
+ const collectAssets = createRef();
36
+ flushSync(() => {
37
+ root.render(_jsx(Internals.MaxMediaCacheSizeContext.Provider, { value: mediaCacheSizeInBytes, children: _jsx(Internals.RemotionEnvironmentContext, { value: {
38
+ isStudio: false,
39
+ isRendering: true,
40
+ isPlayer: false,
41
+ isReadOnlyStudio: false,
42
+ isClientSideRendering: true,
43
+ }, children: _jsx(Internals.DelayRenderContextType.Provider, { value: delayRenderScope, children: _jsx(Internals.CompositionManager.Provider, { value: {
44
+ compositions: [
45
+ {
46
+ id,
47
+ // @ts-expect-error
48
+ component: Component,
49
+ nonce: 0,
50
+ defaultProps: {},
51
+ folderName: null,
52
+ parentFolderName: null,
53
+ schema: schema !== null && schema !== void 0 ? schema : null,
54
+ calculateMetadata: null,
55
+ durationInFrames,
56
+ fps,
57
+ height,
58
+ width,
59
+ },
60
+ ],
61
+ canvasContent: {
62
+ type: 'composition',
63
+ compositionId: id,
64
+ },
65
+ currentCompositionMetadata: {
66
+ props: resolvedProps,
67
+ durationInFrames,
68
+ fps,
69
+ height,
70
+ width,
71
+ defaultCodec: defaultCodec !== null && defaultCodec !== void 0 ? defaultCodec : null,
72
+ defaultOutName: defaultOutName !== null && defaultOutName !== void 0 ? defaultOutName : null,
73
+ defaultVideoImageFormat: null,
74
+ defaultPixelFormat: null,
75
+ defaultProResProfile: null,
76
+ },
77
+ folders: [],
78
+ }, children: _jsx(Internals.RenderAssetManagerProvider, { collectAssets: collectAssets, children: _jsx(UpdateTime, { audioEnabled: audioEnabled, videoEnabled: videoEnabled, logLevel: logLevel, compId: id, initialFrame: initialFrame, timeUpdater: timeUpdater, children: _jsx(Internals.CanUseRemotionHooks, { value: true, children: _jsx(Component, { ...resolvedProps }) }) }) }) }) }) }) }));
79
+ });
80
+ resolve();
81
+ await promise;
82
+ return {
83
+ delayRenderScope,
84
+ div,
85
+ cleanupScaffold: () => {
86
+ root.unmount();
87
+ div.remove();
88
+ },
89
+ timeUpdater,
90
+ collectAssets,
91
+ };
92
+ }