@remotion/web-renderer 4.0.371 → 4.0.373

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.
@@ -0,0 +1,7 @@
1
+ export type Composable = {
2
+ type: 'canvas';
3
+ element: HTMLCanvasElement;
4
+ } | {
5
+ type: 'svg';
6
+ element: SVGSVGElement;
7
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/compose.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export type Composable = HTMLCanvasElement;
1
+ import type { Composable } from './composable';
2
2
  export declare const compose: ({ composables, width, height, }: {
3
3
  composables: Composable[];
4
4
  width: number;
5
5
  height: number;
6
- }) => OffscreenCanvas;
6
+ }) => Promise<OffscreenCanvas>;
package/dist/compose.js CHANGED
@@ -1,7 +1,32 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compose = void 0;
4
- const compose = ({ composables, width, height, }) => {
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
+ };
29
+ export const compose = async ({ composables, width, height, }) => {
5
30
  const canvas = new OffscreenCanvas(width, height);
6
31
  const context = canvas.getContext('2d');
7
32
  if (!context) {
@@ -9,8 +34,20 @@ const compose = ({ composables, width, height, }) => {
9
34
  }
10
35
  // TODO: Consider z-index
11
36
  for (const composable of composables) {
12
- context.drawImage(composable, 0, 0);
37
+ if (composable.type === 'canvas') {
38
+ const boundingClientRect = composable.element.getBoundingClientRect();
39
+ context.drawImage(composable.element, boundingClientRect.left, boundingClientRect.top);
40
+ }
41
+ 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
44
+ const imageBitmap = await svgToImageBitmap(composable.element);
45
+ if (imageBitmap) {
46
+ // transform origin
47
+ // Don't need to get transform from SVG
48
+ context.drawImage(imageBitmap.image, imageBitmap.left, imageBitmap.top);
49
+ }
50
+ }
13
51
  }
14
52
  return canvas;
15
53
  };
16
- exports.compose = compose;
@@ -3,7 +3,35 @@ import ReactDOM from "react-dom/client";
3
3
  import { Internals } from "remotion";
4
4
 
5
5
  // src/compose.ts
6
- var compose = ({
6
+ var svgToImageBitmap = (svg) => {
7
+ const computedStyle = getComputedStyle(svg);
8
+ const { transform: originalTransform } = computedStyle;
9
+ svg.style.transform = "none";
10
+ const svgDimensions = svg.getBoundingClientRect();
11
+ svg.style.transform = originalTransform;
12
+ if (svgDimensions.width === 0 || svgDimensions.height === 0) {
13
+ return Promise.resolve(null);
14
+ }
15
+ const svgData = new XMLSerializer().serializeToString(svg);
16
+ return new Promise((resolve, reject) => {
17
+ const image = new Image(svgDimensions.width, svgDimensions.height);
18
+ const url = "data:image/svg+xml;base64," + window.btoa(svgData);
19
+ image.onload = function() {
20
+ resolve({
21
+ image,
22
+ width: svgDimensions.width,
23
+ height: svgDimensions.height,
24
+ left: svgDimensions.left,
25
+ top: svgDimensions.top
26
+ });
27
+ };
28
+ image.onerror = () => {
29
+ reject(new Error("Failed to convert SVG to image"));
30
+ };
31
+ image.src = url;
32
+ });
33
+ };
34
+ var compose = async ({
7
35
  composables,
8
36
  width,
9
37
  height
@@ -14,7 +42,15 @@ var compose = ({
14
42
  throw new Error("Could not get context");
15
43
  }
16
44
  for (const composable of composables) {
17
- context.drawImage(composable, 0, 0);
45
+ if (composable.type === "canvas") {
46
+ const boundingClientRect = composable.element.getBoundingClientRect();
47
+ context.drawImage(composable.element, boundingClientRect.left, boundingClientRect.top);
48
+ } else if (composable.type === "svg") {
49
+ const imageBitmap = await svgToImageBitmap(composable.element);
50
+ if (imageBitmap) {
51
+ context.drawImage(imageBitmap.image, imageBitmap.left, imageBitmap.top);
52
+ }
53
+ }
18
54
  }
19
55
  return canvas;
20
56
  };
@@ -25,17 +61,34 @@ var findCanvasElements = (element) => {
25
61
  const composables = [];
26
62
  Array.from(canvasElements).forEach((canvasElement) => {
27
63
  const canvas = canvasElement;
28
- composables.push(canvas);
64
+ composables.push({
65
+ type: "canvas",
66
+ element: canvas
67
+ });
68
+ });
69
+ return composables;
70
+ };
71
+
72
+ // src/find-svg-elements.ts
73
+ var findSvgElements = (element) => {
74
+ const svgElements = element.querySelectorAll("svg");
75
+ const composables = [];
76
+ Array.from(svgElements).forEach((svgElement) => {
77
+ const svg = svgElement;
78
+ composables.push({
79
+ type: "svg",
80
+ element: svg
81
+ });
29
82
  });
30
83
  return composables;
31
84
  };
32
85
 
33
86
  // src/wait-for-ready.ts
34
- var waitForReady = (timeoutInMilliseconds) => {
87
+ var waitForReady = (timeoutInMilliseconds, scope) => {
35
88
  const { promise, resolve, reject } = Promise.withResolvers();
36
89
  const start = Date.now();
37
90
  const interval = setInterval(() => {
38
- if (window.remotion_renderReady === true) {
91
+ if (scope.remotion_renderReady === true) {
39
92
  resolve(true);
40
93
  clearInterval(interval);
41
94
  return;
@@ -46,7 +99,7 @@ var waitForReady = (timeoutInMilliseconds) => {
46
99
  return;
47
100
  }
48
101
  if (Date.now() - start > timeoutInMilliseconds + 3000) {
49
- reject(new Error(Object.values(window.remotion_delayRenderTimeouts).map((d) => d.label).join(", ")));
102
+ reject(new Error(Object.values(scope.remotion_delayRenderTimeouts).map((d) => d.label).join(", ")));
50
103
  clearInterval(interval);
51
104
  }
52
105
  }, 50);
@@ -55,61 +108,35 @@ var waitForReady = (timeoutInMilliseconds) => {
55
108
 
56
109
  // src/render-still-on-web.tsx
57
110
  import { jsx } from "react/jsx-runtime";
58
- var renderStillOnWeb = async ({
111
+ var COMP_ID = "markup";
112
+ var internalRenderStillOnWeb = async ({
59
113
  Component,
60
114
  width,
61
115
  height,
62
116
  fps,
63
117
  durationInFrames,
64
- frame
118
+ frame,
119
+ delayRenderTimeoutInMilliseconds,
120
+ logLevel
65
121
  }) => {
66
122
  const div = document.createElement("div");
67
123
  div.style.display = "flex";
68
124
  div.style.backgroundColor = "transparent";
69
- div.style.position = "absolute";
125
+ div.style.position = "fixed";
70
126
  div.style.width = `${width}px`;
71
127
  div.style.height = `${height}px`;
128
+ div.style.zIndex = "-9999";
72
129
  document.body.appendChild(div);
73
- const delayRenderTimeoutInMilliseconds = 1e4;
74
130
  if (!ReactDOM.createRoot) {
75
131
  throw new Error("@remotion/web-renderer requires React 18 or higher");
76
132
  }
77
- const compositionManagerContext = {
78
- currentCompositionMetadata: {
79
- durationInFrames,
80
- fps,
81
- height,
82
- width,
83
- props: {},
84
- defaultCodec: null,
85
- defaultOutName: null,
86
- defaultVideoImageFormat: null,
87
- defaultPixelFormat: null,
88
- defaultProResProfile: null
89
- },
90
- folders: [],
91
- compositions: [
92
- {
93
- id: "markup",
94
- component: Component,
95
- nonce: 0,
96
- defaultProps: undefined,
97
- folderName: null,
98
- parentFolderName: null,
99
- schema: null,
100
- calculateMetadata: null,
101
- durationInFrames,
102
- fps,
103
- height,
104
- width
105
- }
106
- ],
107
- canvasContent: {
108
- type: "composition",
109
- compositionId: "markup"
110
- }
111
- };
112
133
  const root = ReactDOM.createRoot(div);
134
+ const delayRenderScope = {
135
+ remotion_renderReady: true,
136
+ remotion_delayRenderTimeouts: {},
137
+ remotion_puppeteerTimeout: delayRenderTimeoutInMilliseconds,
138
+ remotion_attempt: 0
139
+ };
113
140
  root.render(/* @__PURE__ */ jsx(Internals.RemotionEnvironmentContext, {
114
141
  value: {
115
142
  isStudio: false,
@@ -118,54 +145,80 @@ var renderStillOnWeb = async ({
118
145
  isReadOnlyStudio: false,
119
146
  isClientSideRendering: true
120
147
  },
121
- children: /* @__PURE__ */ jsx(Internals.RemotionRoot, {
122
- audioEnabled: true,
123
- videoEnabled: true,
124
- logLevel: "info",
125
- numberOfAudioTags: 0,
126
- onlyRenderComposition: null,
127
- currentCompositionMetadata: {
128
- props: {},
129
- durationInFrames,
130
- fps,
131
- height,
132
- width,
133
- defaultCodec: null,
134
- defaultOutName: null,
135
- defaultVideoImageFormat: null,
136
- defaultPixelFormat: null,
137
- defaultProResProfile: null
138
- },
139
- audioLatencyHint: "interactive",
140
- children: /* @__PURE__ */ jsx(Internals.CanUseRemotionHooks, {
141
- value: true,
142
- children: /* @__PURE__ */ jsx(Internals.CompositionManager.Provider, {
143
- value: compositionManagerContext,
144
- children: /* @__PURE__ */ jsx(Component, {})
148
+ children: /* @__PURE__ */ jsx(Internals.DelayRenderContextType.Provider, {
149
+ value: delayRenderScope,
150
+ children: /* @__PURE__ */ jsx(Internals.CompositionManagerProvider, {
151
+ initialCanvasContent: {
152
+ type: "composition",
153
+ compositionId: COMP_ID
154
+ },
155
+ onlyRenderComposition: null,
156
+ currentCompositionMetadata: {
157
+ props: {},
158
+ durationInFrames,
159
+ fps,
160
+ height,
161
+ width,
162
+ defaultCodec: null,
163
+ defaultOutName: null,
164
+ defaultVideoImageFormat: null,
165
+ defaultPixelFormat: null,
166
+ defaultProResProfile: null
167
+ },
168
+ initialCompositions: [
169
+ {
170
+ id: COMP_ID,
171
+ component: Component,
172
+ nonce: 0,
173
+ defaultProps: undefined,
174
+ folderName: null,
175
+ parentFolderName: null,
176
+ schema: null,
177
+ calculateMetadata: null,
178
+ durationInFrames,
179
+ fps,
180
+ height,
181
+ width
182
+ }
183
+ ],
184
+ children: /* @__PURE__ */ jsx(Internals.RemotionRoot, {
185
+ audioEnabled: false,
186
+ videoEnabled: true,
187
+ logLevel,
188
+ numberOfAudioTags: 0,
189
+ audioLatencyHint: "interactive",
190
+ frameState: {
191
+ [COMP_ID]: frame
192
+ },
193
+ children: /* @__PURE__ */ jsx(Internals.CanUseRemotionHooks, {
194
+ value: true,
195
+ children: /* @__PURE__ */ jsx(Component, {})
196
+ })
145
197
  })
146
198
  })
147
199
  })
148
200
  }));
149
- window.remotion_setFrame(frame, "markup", frame);
150
- await waitForReady(delayRenderTimeoutInMilliseconds);
201
+ await waitForReady(delayRenderTimeoutInMilliseconds, delayRenderScope);
151
202
  const canvasElements = findCanvasElements(div);
152
- const composed = compose({
153
- composables: canvasElements,
203
+ const svgElements = findSvgElements(div);
204
+ const composed = await compose({
205
+ composables: [...canvasElements, ...svgElements],
154
206
  width,
155
207
  height
156
208
  });
157
209
  const imageData = await composed.convertToBlob({
158
210
  type: "image/png"
159
211
  });
160
- const blob = new Blob([imageData], { type: "image/png" });
161
- const url = URL.createObjectURL(blob);
162
- const a = document.createElement("a");
163
- a.href = url;
164
- a.download = "composed.png";
165
- a.click();
166
- URL.revokeObjectURL(url);
167
212
  root.unmount();
168
213
  div.remove();
214
+ return imageData;
215
+ };
216
+ var renderStillOnWeb = (options) => {
217
+ return internalRenderStillOnWeb({
218
+ ...options,
219
+ delayRenderTimeoutInMilliseconds: options.delayRenderTimeoutInMilliseconds ?? 30000,
220
+ logLevel: options.logLevel ?? "info"
221
+ });
169
222
  };
170
223
  export {
171
224
  renderStillOnWeb
@@ -1 +1,2 @@
1
- export declare const findCanvasElements: (element: HTMLDivElement) => HTMLCanvasElement[];
1
+ import type { Composable } from './composable';
2
+ export declare const findCanvasElements: (element: HTMLDivElement) => Composable[];
@@ -1,13 +1,12 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.findCanvasElements = void 0;
4
- const findCanvasElements = (element) => {
1
+ export const findCanvasElements = (element) => {
5
2
  const canvasElements = element.querySelectorAll('canvas');
6
3
  const composables = [];
7
4
  Array.from(canvasElements).forEach((canvasElement) => {
8
5
  const canvas = canvasElement;
9
- composables.push(canvas);
6
+ composables.push({
7
+ type: 'canvas',
8
+ element: canvas,
9
+ });
10
10
  });
11
11
  return composables;
12
12
  };
13
- exports.findCanvasElements = findCanvasElements;
@@ -0,0 +1,2 @@
1
+ import type { Composable } from './composable';
2
+ export declare const findSvgElements: (element: HTMLDivElement) => Composable[];
@@ -0,0 +1,12 @@
1
+ export const findSvgElements = (element) => {
2
+ const svgElements = element.querySelectorAll('svg');
3
+ const composables = [];
4
+ Array.from(svgElements).forEach((svgElement) => {
5
+ const svg = svgElement;
6
+ composables.push({
7
+ type: 'svg',
8
+ element: svg,
9
+ });
10
+ });
11
+ return composables;
12
+ };
package/dist/index.js CHANGED
@@ -1,5 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.renderStillOnWeb = void 0;
4
- var render_still_on_web_1 = require("./render-still-on-web");
5
- Object.defineProperty(exports, "renderStillOnWeb", { enumerable: true, get: function () { return render_still_on_web_1.renderStillOnWeb; } });
1
+ export { renderStillOnWeb } from './render-still-on-web';
@@ -1,9 +1,17 @@
1
1
  import { type ComponentType, type LazyExoticComponent } from 'react';
2
- export declare const renderStillOnWeb: ({ Component, width, height, fps, durationInFrames, frame, }: {
2
+ import type { LogLevel } from 'remotion';
3
+ type MandatoryRenderStillOnWebOptions = {
3
4
  Component: LazyExoticComponent<ComponentType<Record<string, unknown>>> | ComponentType<Record<string, unknown>>;
4
5
  width: number;
5
6
  height: number;
6
7
  fps: number;
7
8
  durationInFrames: number;
8
9
  frame: number;
9
- }) => Promise<void>;
10
+ };
11
+ type OptionalRenderStillOnWebOptions = {
12
+ delayRenderTimeoutInMilliseconds: number;
13
+ logLevel: LogLevel;
14
+ };
15
+ type RenderStillOnWebOptions = MandatoryRenderStillOnWebOptions & Partial<OptionalRenderStillOnWebOptions>;
16
+ export declare const renderStillOnWeb: (options: RenderStillOnWebOptions) => Promise<Blob>;
17
+ export {};
@@ -1,27 +1,22 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.renderStillOnWeb = void 0;
7
- const jsx_runtime_1 = require("react/jsx-runtime");
8
- const client_1 = __importDefault(require("react-dom/client"));
9
- const remotion_1 = require("remotion");
10
- const compose_1 = require("./compose");
11
- const find_canvas_elements_1 = require("./find-canvas-elements");
12
- const wait_for_ready_1 = require("./wait-for-ready");
13
- const renderStillOnWeb = async ({ Component, width, height, fps, durationInFrames, frame, }) => {
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import ReactDOM from 'react-dom/client';
3
+ import { Internals } from 'remotion';
4
+ import { compose } from './compose';
5
+ import { findCanvasElements } from './find-canvas-elements';
6
+ import { findSvgElements } from './find-svg-elements';
7
+ import { waitForReady } from './wait-for-ready';
8
+ const COMP_ID = 'markup';
9
+ const internalRenderStillOnWeb = async ({ Component, width, height, fps, durationInFrames, frame, delayRenderTimeoutInMilliseconds, logLevel, }) => {
14
10
  const div = document.createElement('div');
15
11
  // Match same behavior as renderEntry.tsx
16
12
  div.style.display = 'flex';
17
13
  div.style.backgroundColor = 'transparent';
18
- div.style.position = 'absolute';
14
+ div.style.position = 'fixed';
19
15
  div.style.width = `${width}px`;
20
16
  div.style.height = `${height}px`;
17
+ div.style.zIndex = '-9999';
21
18
  document.body.appendChild(div);
22
- // TODO: Hardcoded
23
- const delayRenderTimeoutInMilliseconds = 10000;
24
- if (!client_1.default.createRoot) {
19
+ if (!ReactDOM.createRoot) {
25
20
  throw new Error('@remotion/web-renderer requires React 18 or higher');
26
21
  }
27
22
  // TODO: Env variables
@@ -32,113 +27,84 @@ const renderStillOnWeb = async ({ Component, width, height, fps, durationInFrame
32
27
  // TODO: delayRender()
33
28
  // TODO: Video config
34
29
  // TODO: window.remotion_isPlayer
35
- // TODO: Log Level
36
- const compositionManagerContext = {
37
- currentCompositionMetadata: {
38
- durationInFrames,
39
- fps,
40
- height,
41
- width,
42
- props: {},
43
- defaultCodec: null,
44
- defaultOutName: null,
45
- defaultVideoImageFormat: null,
46
- defaultPixelFormat: null,
47
- defaultProResProfile: null,
48
- },
49
- folders: [],
50
- compositions: [
51
- {
52
- // TODO: Make dynamic
53
- id: 'markup',
54
- component: Component,
55
- nonce: 0,
56
- defaultProps: undefined,
57
- folderName: null,
58
- parentFolderName: null,
59
- schema: null,
60
- calculateMetadata: null,
61
- durationInFrames,
62
- fps,
63
- height,
64
- width,
65
- },
66
- ],
67
- canvasContent: {
68
- type: 'composition',
69
- compositionId: 'markup',
70
- },
30
+ const root = ReactDOM.createRoot(div);
31
+ const delayRenderScope = {
32
+ remotion_renderReady: true,
33
+ remotion_delayRenderTimeouts: {},
34
+ remotion_puppeteerTimeout: delayRenderTimeoutInMilliseconds,
35
+ remotion_attempt: 0,
71
36
  };
72
- const root = client_1.default.createRoot(div);
73
- root.render((0, jsx_runtime_1.jsx)(remotion_1.Internals.RemotionEnvironmentContext, { value: {
37
+ root.render(_jsx(Internals.RemotionEnvironmentContext, { value: {
74
38
  isStudio: false,
75
39
  isRendering: true,
76
40
  isPlayer: false,
77
41
  isReadOnlyStudio: false,
78
42
  isClientSideRendering: true,
79
- }, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.RemotionRoot
80
- // TODO: Hardcoded
81
- , {
82
- // TODO: Hardcoded
83
- audioEnabled: true,
84
- // TODO: Hardcoded
85
- videoEnabled: true,
86
- // TODO: Hardcoded
87
- logLevel: "info",
88
- // TODO: Hardcoded
89
- numberOfAudioTags: 0,
90
- // TODO: Hardcoded
91
- onlyRenderComposition: null,
92
- // TODO: Hardcoded
93
- currentCompositionMetadata: {
94
- // TODO: Empty
95
- props: {},
96
- // TODO: Hardcoded
97
- durationInFrames,
98
- // TODO: Hardcoded
99
- fps,
100
- // TODO: Hardcoded
101
- height,
102
- // TODO: Hardcoded
103
- width,
43
+ }, children: _jsx(Internals.DelayRenderContextType.Provider, { value: delayRenderScope, children: _jsx(Internals.CompositionManagerProvider, { initialCanvasContent: {
44
+ type: 'composition',
45
+ compositionId: COMP_ID,
46
+ }, onlyRenderComposition: null,
104
47
  // TODO: Hardcoded
105
- defaultCodec: null,
48
+ currentCompositionMetadata: {
49
+ // TODO: Empty
50
+ props: {},
51
+ durationInFrames,
52
+ fps,
53
+ height,
54
+ width,
55
+ defaultCodec: null,
56
+ defaultOutName: null,
57
+ defaultVideoImageFormat: null,
58
+ defaultPixelFormat: null,
59
+ defaultProResProfile: null,
60
+ }, initialCompositions: [
61
+ {
62
+ id: COMP_ID,
63
+ component: Component,
64
+ nonce: 0,
65
+ // TODO: Do we need to allow to set this?
66
+ defaultProps: undefined,
67
+ folderName: null,
68
+ parentFolderName: null,
69
+ schema: null,
70
+ calculateMetadata: null,
71
+ durationInFrames,
72
+ fps,
73
+ height,
74
+ width,
75
+ },
76
+ ], children: _jsx(Internals.RemotionRoot
106
77
  // TODO: Hardcoded
107
- defaultOutName: null,
108
- // TODO: Hardcoded
109
- defaultVideoImageFormat: null,
110
- // TODO: Hardcoded
111
- defaultPixelFormat: null,
112
- // TODO: Hardcoded
113
- defaultProResProfile: null,
114
- },
115
- // TODO: Hardcoded
116
- audioLatencyHint: "interactive", children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CanUseRemotionHooks, { value: true, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CompositionManager.Provider
117
- // TODO: This context is double-wrapped
118
78
  , {
119
- // TODO: This context is double-wrapped
120
- value: compositionManagerContext, children: (0, jsx_runtime_1.jsx)(Component, {}) }) }) }) }));
121
- // TODO: Doesn't work at all 🙈
122
- window.remotion_setFrame(frame, 'markup', frame);
123
- await (0, wait_for_ready_1.waitForReady)(delayRenderTimeoutInMilliseconds);
124
- const canvasElements = (0, find_canvas_elements_1.findCanvasElements)(div);
125
- const composed = (0, compose_1.compose)({
126
- composables: canvasElements,
79
+ // TODO: Hardcoded
80
+ audioEnabled: false,
81
+ // TODO: Hardcoded
82
+ videoEnabled: true, logLevel: logLevel, numberOfAudioTags: 0,
83
+ // TODO: Hardcoded
84
+ audioLatencyHint: "interactive", frameState: {
85
+ [COMP_ID]: frame,
86
+ }, children: _jsx(Internals.CanUseRemotionHooks, { value: true, children: _jsx(Component, {}) }) }) }) }) }));
87
+ // TODO: Scope cancelRender()
88
+ await waitForReady(delayRenderTimeoutInMilliseconds, delayRenderScope);
89
+ const canvasElements = findCanvasElements(div);
90
+ const svgElements = findSvgElements(div);
91
+ const composed = await compose({
92
+ composables: [...canvasElements, ...svgElements],
127
93
  width,
128
94
  height,
129
95
  });
130
96
  const imageData = await composed.convertToBlob({
131
97
  type: 'image/png',
132
98
  });
133
- // download image
134
- const blob = new Blob([imageData], { type: 'image/png' });
135
- const url = URL.createObjectURL(blob);
136
- const a = document.createElement('a');
137
- a.href = url;
138
- a.download = 'composed.png';
139
- a.click();
140
- URL.revokeObjectURL(url);
141
99
  root.unmount();
142
100
  div.remove();
101
+ return imageData;
102
+ };
103
+ export const renderStillOnWeb = (options) => {
104
+ var _a, _b;
105
+ return internalRenderStillOnWeb({
106
+ ...options,
107
+ delayRenderTimeoutInMilliseconds: (_a = options.delayRenderTimeoutInMilliseconds) !== null && _a !== void 0 ? _a : 30000,
108
+ logLevel: (_b = options.logLevel) !== null && _b !== void 0 ? _b : 'info',
109
+ });
143
110
  };
144
- exports.renderStillOnWeb = renderStillOnWeb;
@@ -1 +1,2 @@
1
- export declare const waitForReady: (timeoutInMilliseconds: number) => Promise<unknown>;
1
+ import type { _InternalTypes } from 'remotion';
2
+ export declare const waitForReady: (timeoutInMilliseconds: number, scope: _InternalTypes["DelayRenderScope"]) => Promise<unknown>;
@@ -1,11 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.waitForReady = void 0;
4
- const waitForReady = (timeoutInMilliseconds) => {
1
+ export const waitForReady = (timeoutInMilliseconds, scope) => {
5
2
  const { promise, resolve, reject } = Promise.withResolvers();
6
3
  const start = Date.now();
7
4
  const interval = setInterval(() => {
8
- if (window.remotion_renderReady === true) {
5
+ if (scope.remotion_renderReady === true) {
9
6
  resolve(true);
10
7
  clearInterval(interval);
11
8
  return;
@@ -17,7 +14,7 @@ const waitForReady = (timeoutInMilliseconds) => {
17
14
  }
18
15
  if (Date.now() - start > timeoutInMilliseconds + 3000) {
19
16
  // TODO: Error message should be just as good
20
- reject(new Error(Object.values(window.remotion_delayRenderTimeouts)
17
+ reject(new Error(Object.values(scope.remotion_delayRenderTimeouts)
21
18
  .map((d) => d.label)
22
19
  .join(', ')));
23
20
  clearInterval(interval);
@@ -25,4 +22,3 @@ const waitForReady = (timeoutInMilliseconds) => {
25
22
  }, 50);
26
23
  return promise;
27
24
  };
28
- exports.waitForReady = waitForReady;
package/package.json CHANGED
@@ -3,24 +3,29 @@
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.371",
6
+ "version": "4.0.373",
7
7
  "main": "dist/index.js",
8
8
  "sideEffects": false,
9
9
  "scripts": {
10
10
  "formatting": "prettier --experimental-cli src --check",
11
11
  "lint": "eslint src",
12
- "make": "tsc -d && bun --env-file=../.env.bundle bundle.ts"
12
+ "make": "tsc -d && bun --env-file=../.env.bundle bundle.ts",
13
+ "test": "node src/test/execute.mjs"
13
14
  },
14
15
  "author": "Remotion <jonny@remotion.dev>",
15
16
  "license": "UNLICENSED",
16
17
  "dependencies": {
17
- "remotion": "4.0.371"
18
+ "remotion": "4.0.373"
18
19
  },
19
20
  "devDependencies": {
20
- "@remotion/eslint-config-internal": "4.0.371",
21
+ "@remotion/eslint-config-internal": "4.0.373",
22
+ "@vitejs/plugin-react": "^5.1.0",
23
+ "@vitest/browser-webdriverio": "4.0.7",
21
24
  "eslint": "9.19.0",
22
25
  "react": "19.0.0",
23
- "react-dom": "19.0.0"
26
+ "react-dom": "19.0.0",
27
+ "vitest": "4.0.7",
28
+ "vitest-browser-react": "^2.0.2"
24
29
  },
25
30
  "peerDependencies": {
26
31
  "react": ">=18.0.0",