remotion 4.0.454 → 4.0.455

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 (40) hide show
  1. package/dist/cjs/CompositionManager.d.ts +2 -0
  2. package/dist/cjs/HtmlInCanvas.d.ts +49 -22
  3. package/dist/cjs/HtmlInCanvas.js +173 -88
  4. package/dist/cjs/Sequence.d.ts +2 -0
  5. package/dist/cjs/Sequence.js +6 -1
  6. package/dist/cjs/{canvas-effects → effects}/Solid.d.ts +1 -1
  7. package/dist/cjs/{canvas-effects → effects}/Solid.js +13 -8
  8. package/dist/cjs/{canvas-effects → effects}/canvas-pool.js +2 -4
  9. package/dist/cjs/{canvas-effects → effects}/define-effect.d.ts +1 -1
  10. package/dist/cjs/{canvas-effects → effects}/define-effect.js +8 -4
  11. package/dist/cjs/{canvas-effects → effects}/effect-internals.d.ts +1 -1
  12. package/dist/cjs/{canvas-effects → effects}/effect-types.d.ts +4 -5
  13. package/dist/cjs/{canvas-effects → effects}/effect-types.js +1 -2
  14. package/dist/cjs/effects/index.d.ts +1 -0
  15. package/dist/cjs/{enable-effects.js → effects/index.js} +0 -2
  16. package/dist/cjs/{canvas-effects → effects}/run-effect-chain.d.ts +3 -4
  17. package/dist/cjs/{canvas-effects → effects}/run-effect-chain.js +20 -24
  18. package/dist/cjs/effects/use-effect-chain-state.d.ts +4 -0
  19. package/dist/cjs/effects/use-effect-chain-state.js +31 -0
  20. package/dist/cjs/index.d.ts +5 -0
  21. package/dist/cjs/index.js +6 -1
  22. package/dist/cjs/internals.d.ts +8 -0
  23. package/dist/cjs/internals.js +9 -0
  24. package/dist/cjs/use-media-in-timeline.js +2 -0
  25. package/dist/cjs/version.d.ts +1 -1
  26. package/dist/cjs/version.js +1 -1
  27. package/dist/esm/index.mjs +1416 -769
  28. package/dist/esm/version.mjs +1 -1
  29. package/package.json +2 -2
  30. package/dist/cjs/canvas-effects/index.d.ts +0 -4
  31. package/dist/cjs/canvas-effects/index.js +0 -8
  32. package/dist/cjs/canvas-effects/use-effect-chain-state.d.ts +0 -2
  33. package/dist/cjs/canvas-effects/use-effect-chain-state.js +0 -27
  34. package/dist/cjs/enable-effects.d.ts +0 -1
  35. /package/dist/cjs/{canvas-effects → effects}/canvas-pool.d.ts +0 -0
  36. /package/dist/cjs/{canvas-effects → effects}/effect-internals.js +0 -0
  37. /package/dist/cjs/{canvas-effects → effects}/gpu-device.d.ts +0 -0
  38. /package/dist/cjs/{canvas-effects → effects}/gpu-device.js +0 -0
  39. /package/dist/cjs/{canvas-effects → effects}/use-memoized-effects.d.ts +0 -0
  40. /package/dist/cjs/{canvas-effects → effects}/use-memoized-effects.js +0 -0
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import type { AnyZodObject } from './any-zod-type.js';
4
4
  import type { CalculateMetadataFunction } from './Composition.js';
5
5
  import type { DownloadBehavior } from './download-behavior.js';
6
+ import type { EffectDefinitionAndStack } from './effects/effect-types.js';
6
7
  import type { NonceHistory } from './nonce.js';
7
8
  import type { InferProps, PropsIfHasProps } from './props-if-has-props.js';
8
9
  import type { SequenceSchema } from './sequence-field-schema.js';
@@ -68,6 +69,7 @@ export type TSequence = {
68
69
  premountDisplay: number | null;
69
70
  postmountDisplay: number | null;
70
71
  controls: SequenceControls | null;
72
+ effects: EffectDefinitionAndStack<unknown>[];
71
73
  } & EnhancedTSequenceData;
72
74
  export type AudioOrVideoAsset = {
73
75
  type: 'audio' | 'video';
@@ -1,32 +1,59 @@
1
1
  import React from 'react';
2
- import type { EffectsProp } from './canvas-effects/effect-types.js';
2
+ import type { EffectsProp } from './effects/effect-types.js';
3
3
  import type { AbsoluteFillLayout, LayoutAndStyle, SequenceProps } from './Sequence.js';
4
- export type HtmlInCanvasComposeParams = {
5
- readonly source: CanvasImageSource;
6
- readonly target: HTMLCanvasElement;
7
- readonly frame: number;
8
- readonly width: number;
9
- readonly height: number;
10
- readonly pixelRatio: number;
4
+ declare global {
5
+ interface ElementImage {
6
+ readonly width: number;
7
+ readonly height: number;
8
+ close(): void;
9
+ }
10
+ interface CanvasRenderingContext2D {
11
+ drawElementImage(element: Element | ElementImage, dx: number, dy: number): DOMMatrix;
12
+ drawElementImage(element: Element | ElementImage, dx: number, dy: number, dwidth: number, dheight: number): DOMMatrix;
13
+ drawElementImage(element: Element | ElementImage, sx: number, sy: number, swidth: number, sheight: number, dx: number, dy: number): DOMMatrix;
14
+ drawElementImage(element: Element | ElementImage, sx: number, sy: number, swidth: number, sheight: number, dx: number, dy: number, dwidth: number, dheight: number): DOMMatrix;
15
+ }
16
+ interface OffscreenCanvasRenderingContext2D {
17
+ drawElementImage(element: ElementImage, dx: number, dy: number): DOMMatrix;
18
+ drawElementImage(element: ElementImage, dx: number, dy: number, dwidth: number, dheight: number): DOMMatrix;
19
+ drawElementImage(element: ElementImage, sx: number, sy: number, swidth: number, sheight: number, dx: number, dy: number): DOMMatrix;
20
+ drawElementImage(element: ElementImage, sx: number, sy: number, swidth: number, sheight: number, dx: number, dy: number, dwidth: number, dheight: number): DOMMatrix;
21
+ }
22
+ interface WebGLRenderingContextBase {
23
+ texElementImage2D(target: GLenum, level: GLint, internalformat: GLint, format: GLenum, type: GLenum, element: Element | ElementImage): void;
24
+ texElementImage2D(target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, height: GLsizei, format: GLenum, type: GLenum, element: Element | ElementImage): void;
25
+ texElementImage2D(target: GLenum, level: GLint, internalformat: GLint, sx: GLfloat, sy: GLfloat, swidth: GLfloat, sheight: GLfloat, format: GLenum, type: GLenum, element: Element | ElementImage): void;
26
+ texElementImage2D(target: GLenum, level: GLint, internalformat: GLint, sx: GLfloat, sy: GLfloat, swidth: GLfloat, sheight: GLfloat, width: GLsizei, height: GLsizei, format: GLenum, type: GLenum, element: Element | ElementImage): void;
27
+ }
28
+ interface HTMLCanvasElementEventMap {
29
+ paint: Event;
30
+ }
31
+ interface HTMLCanvasElement {
32
+ layoutSubtree?: boolean;
33
+ onpaint: ((this: HTMLCanvasElement, ev: Event) => unknown) | null;
34
+ requestPaint?(): void;
35
+ captureElementImage(element: Element): ElementImage;
36
+ getElementTransform(element: Element | ElementImage, drawTransform: DOMMatrix): DOMMatrix;
37
+ }
38
+ }
39
+ export type HtmlInCanvasOnPaintParams = {
40
+ readonly canvas: OffscreenCanvas;
41
+ readonly element: HTMLDivElement;
42
+ readonly elementImage: ElementImage;
11
43
  };
12
44
  export declare const isHtmlInCanvasSupported: () => boolean;
13
- export type HtmlInCanvasProps = Omit<SequenceProps, 'children' | 'durationInFrames' | keyof LayoutAndStyle> & Omit<AbsoluteFillLayout, 'layout'> & {
45
+ export type HtmlInCanvasOnPaint = (params: HtmlInCanvasOnPaintParams) => void | Promise<void>;
46
+ export type HtmlInCanvasOnInitCleanup = () => void;
47
+ export type HtmlInCanvasOnInit = (params: HtmlInCanvasOnPaintParams) => HtmlInCanvasOnInitCleanup | Promise<HtmlInCanvasOnInitCleanup>;
48
+ export type HtmlInCanvasProps = Omit<SequenceProps, 'children' | 'durationInFrames' | keyof LayoutAndStyle> & Omit<AbsoluteFillLayout, 'layout' | 'styleWhilePostmounted' | 'postmountFor' | 'premountFor' | 'styleWhilePremounted'> & {
14
49
  readonly durationInFrames?: number;
15
50
  readonly width: number;
16
51
  readonly height: number;
17
- readonly effects?: EffectsProp;
52
+ readonly _experimentalEffects?: EffectsProp;
18
53
  readonly children: React.ReactNode;
19
- readonly pixelRatio?: number;
20
- readonly onCompose?: (params: HtmlInCanvasComposeParams) => void | Promise<void>;
54
+ readonly onPaint?: HtmlInCanvasOnPaint;
55
+ readonly onInit?: HtmlInCanvasOnInit;
21
56
  };
22
- export declare const HtmlInCanvas: React.ComponentType<Omit<SequenceProps, "children" | "durationInFrames" | "layout"> & Omit<AbsoluteFillLayout, "layout"> & {
23
- readonly durationInFrames?: number | undefined;
24
- readonly width: number;
25
- readonly height: number;
26
- readonly effects?: EffectsProp | undefined;
27
- readonly children: React.ReactNode;
28
- readonly pixelRatio?: number | undefined;
29
- readonly onCompose?: ((params: HtmlInCanvasComposeParams) => void | Promise<void>) | undefined;
30
- }> & {
31
- readonly isHtmlInCanvasSupported: () => boolean;
57
+ export declare const HtmlInCanvas: React.ForwardRefExoticComponent<HtmlInCanvasProps & React.RefAttributes<HTMLCanvasElement>> & {
58
+ readonly isHtmlInCanvasSupported: typeof isHtmlInCanvasSupported;
32
59
  };
@@ -3,24 +3,56 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HtmlInCanvas = exports.isHtmlInCanvasSupported = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
- const run_effect_chain_js_1 = require("./canvas-effects/run-effect-chain.js");
7
- const use_effect_chain_state_js_1 = require("./canvas-effects/use-effect-chain-state.js");
6
+ const delay_render_js_1 = require("./delay-render.js");
7
+ const run_effect_chain_js_1 = require("./effects/run-effect-chain.js");
8
+ const use_effect_chain_state_js_1 = require("./effects/use-effect-chain-state.js");
8
9
  const enable_sequence_stack_traces_js_1 = require("./enable-sequence-stack-traces.js");
9
10
  const Sequence_js_1 = require("./Sequence.js");
10
11
  const use_current_frame_js_1 = require("./use-current-frame.js");
11
12
  const use_delay_render_js_1 = require("./use-delay-render.js");
12
13
  const use_video_config_js_1 = require("./use-video-config.js");
13
14
  const wrap_in_schema_js_1 = require("./wrap-in-schema.js");
15
+ // Memoize the support check across the session — neither the platform
16
+ // capability nor the chrome://flags toggle can change between calls.
17
+ // SSR results are not cached so the check runs again once `document` exists.
18
+ let cachedSupport = null;
14
19
  const isHtmlInCanvasSupported = () => {
20
+ if (cachedSupport !== null) {
21
+ return cachedSupport;
22
+ }
15
23
  if (typeof document === 'undefined') {
16
24
  return false;
17
25
  }
18
26
  const canvas = document.createElement('canvas');
19
27
  const ctx = canvas.getContext('2d');
20
- return (typeof (ctx === null || ctx === void 0 ? void 0 : ctx.drawElementImage) === 'function' &&
21
- typeof canvas.requestPaint === 'function');
28
+ cachedSupport =
29
+ typeof (ctx === null || ctx === void 0 ? void 0 : ctx.drawElementImage) === 'function' &&
30
+ typeof canvas.requestPaint === 'function' &&
31
+ typeof canvas.captureElementImage === 'function';
32
+ return cachedSupport;
22
33
  };
23
34
  exports.isHtmlInCanvasSupported = isHtmlInCanvasSupported;
35
+ function assertHtmlInCanvasDimensions(width, height) {
36
+ if (typeof width !== 'number' || typeof height !== 'number') {
37
+ throw new Error(`HtmlInCanvas: \`width\` and \`height\` must be numbers. Received width=${String(width)}, height=${String(height)}.`);
38
+ }
39
+ if (!Number.isInteger(width) || width <= 0) {
40
+ throw new Error(`HtmlInCanvas: \`width\` must be a positive integer. Received: ${String(width)}.`);
41
+ }
42
+ if (!Number.isInteger(height) || height <= 0) {
43
+ throw new Error(`HtmlInCanvas: \`height\` must be a positive integer. Received: ${String(height)}.`);
44
+ }
45
+ }
46
+ const defaultOnPaint = ({ canvas, element, elementImage, }) => {
47
+ const ctx = canvas.getContext('2d');
48
+ if (!ctx) {
49
+ throw new Error('Failed to acquire 2D context for <HtmlInCanvas> canvas');
50
+ }
51
+ ctx.reset();
52
+ const transform = ctx.drawElementImage(elementImage, 0, 0);
53
+ element.style.transform = transform.toString();
54
+ };
55
+ /* eslint-enable react/require-default-props */
24
56
  const htmlInCanvasSchema = {
25
57
  'style.translate': {
26
58
  type: 'translate',
@@ -51,119 +83,172 @@ const htmlInCanvasSchema = {
51
83
  description: 'Opacity',
52
84
  },
53
85
  };
54
- const HtmlInCanvasInner = ({ width, height, effects = [], children, style, pixelRatio = 1, onCompose, controls, durationInFrames, ...sequenceProps }) => {
86
+ const HtmlInCanvasInner = (0, react_1.forwardRef)(({ width, height, _experimentalEffects: experimentalEffects = [], children, onPaint, onInit, controls, style, durationInFrames, ...sequenceProps }, ref) => {
87
+ assertHtmlInCanvasDimensions(width, height);
88
+ const { continueRender, cancelRender } = (0, use_delay_render_js_1.useDelayRender)();
89
+ if (!(0, exports.isHtmlInCanvasSupported)()) {
90
+ cancelRender(new Error('HTML in Canvas is not supported. Open this page in Chrome Canary with chrome://flags/#canvas-draw-element enabled.'));
91
+ }
55
92
  const { durationInFrames: videoDuration } = (0, use_video_config_js_1.useVideoConfig)();
56
93
  const resolvedDuration = durationInFrames !== null && durationInFrames !== void 0 ? durationInFrames : videoDuration;
57
94
  const frame = (0, use_current_frame_js_1.useCurrentFrame)();
58
- const { delayRender, continueRender, cancelRender } = (0, use_delay_render_js_1.useDelayRender)();
59
- const canvasRef = (0, react_1.useRef)(null);
60
- const sceneRef = (0, react_1.useRef)(null);
61
- const chainState = (0, use_effect_chain_state_js_1.useEffectChainState)(width, height);
95
+ const canvas2dRef = (0, react_1.useRef)(null);
96
+ const divRef = (0, react_1.useRef)(null);
97
+ const setLayoutCanvasRef = (0, react_1.useCallback)((node) => {
98
+ canvas2dRef.current = node;
99
+ if (typeof ref === 'function') {
100
+ ref(node);
101
+ }
102
+ else if (ref) {
103
+ ref.current =
104
+ node;
105
+ }
106
+ }, [ref]);
107
+ const [offscreenCanvas] = (0, react_1.useState)(() => new OffscreenCanvas(1, 1));
108
+ const chainState = (0, use_effect_chain_state_js_1.useEffectChainState)();
62
109
  // Refs so the paint handler always reads fresh values.
63
- const effectsRef = (0, react_1.useRef)(effects);
64
- effectsRef.current = effects;
110
+ const effectsRef = (0, react_1.useRef)(experimentalEffects);
111
+ effectsRef.current = experimentalEffects;
65
112
  const frameRef = (0, react_1.useRef)(frame);
66
113
  frameRef.current = frame;
67
- const pixelRatioRef = (0, react_1.useRef)(pixelRatio);
68
- pixelRatioRef.current = pixelRatio;
69
- const widthRef = (0, react_1.useRef)(width);
70
- widthRef.current = width;
71
- const heightRef = (0, react_1.useRef)(height);
72
- heightRef.current = height;
73
- // Track the current delayRender handle so the paint handler can settle it.
74
- const pendingHandleRef = (0, react_1.useRef)(null);
75
- const onPaint = (0, react_1.useCallback)(() => {
76
- const canvas = canvasRef.current;
77
- const sceneEl = sceneRef.current;
78
- const handle = pendingHandleRef.current;
79
- if (!canvas || !sceneEl || !chainState || handle === null) {
80
- return;
114
+ const onPaintRef = (0, react_1.useRef)(onPaint);
115
+ onPaintRef.current = onPaint;
116
+ const onInitRef = (0, react_1.useRef)(onInit);
117
+ onInitRef.current = onInit;
118
+ const initializedRef = (0, react_1.useRef)(false);
119
+ const onInitCleanupRef = (0, react_1.useRef)(null);
120
+ const unmountedRef = (0, react_1.useRef)(false);
121
+ const onPaintCb = (0, react_1.useCallback)(async () => {
122
+ var _a;
123
+ const element = divRef.current;
124
+ if (!element) {
125
+ throw new Error('Canvas or scene element not found');
81
126
  }
127
+ offscreenCanvas.width = width;
128
+ offscreenCanvas.height = height;
82
129
  try {
83
- const ctx = canvas.getContext('2d');
84
- if (!ctx) {
85
- throw new Error('Failed to acquire 2D context for <HtmlInCanvas> canvas');
130
+ const layoutCanvas = canvas2dRef.current;
131
+ if (!layoutCanvas) {
132
+ throw new Error('Canvas not found');
133
+ }
134
+ // `GPUQueue.copyElementImageToTexture` / related paths validate the
135
+ // layout canvas has a rendering context. `runEffectChain` only runs
136
+ // after `onPaint`, so acquire `2d` here before any capture or handler.
137
+ const layout2d = layoutCanvas.getContext('2d');
138
+ if (!layout2d) {
139
+ throw new Error('Failed to acquire 2D context for <HtmlInCanvas> layout canvas');
86
140
  }
87
- const w = widthRef.current;
88
- const h = heightRef.current;
89
- // Layout-subtree children are not shown on-screen until rasterized here;
90
- // effects then read from and write back to this same surface (via pool).
91
- ctx.reset();
92
- ctx.drawElementImage(sceneEl, 0, 0, w, h);
93
- const capturedHandle = handle;
94
- pendingHandleRef.current = null;
95
- (0, run_effect_chain_js_1.runEffectChain)({
96
- state: chainState,
97
- source: canvas,
141
+ const handle = (0, delay_render_js_1.delayRender)('onPaint');
142
+ if (!initializedRef.current) {
143
+ initializedRef.current = true;
144
+ // `onInit` may be async (e.g. WebGPU `requestAdapter`/`requestDevice`).
145
+ // Capture an `ElementImage` here only for `onInit` consumers — do NOT
146
+ // reuse it for the paint handler below, because awaiting `onInit`
147
+ // can invalidate the capture's paint context, leaving subsequent
148
+ // uploads (e.g. `copyElementImageToTexture`) failing with
149
+ // "No context found for ElementImage" on the very first paint.
150
+ const initImage = layoutCanvas.captureElementImage(element);
151
+ const currentOnInit = onInitRef.current;
152
+ if (currentOnInit) {
153
+ const cleanup = await currentOnInit({
154
+ canvas: offscreenCanvas,
155
+ element,
156
+ elementImage: initImage,
157
+ });
158
+ if (typeof cleanup !== 'function') {
159
+ throw new Error('HtmlInCanvas: when `onInit` is provided, it must return a cleanup function, or a Promise that resolves to one.');
160
+ }
161
+ if (unmountedRef.current) {
162
+ cleanup();
163
+ }
164
+ else {
165
+ onInitCleanupRef.current = cleanup;
166
+ }
167
+ }
168
+ }
169
+ const handler = (_a = onPaintRef.current) !== null && _a !== void 0 ? _a : defaultOnPaint;
170
+ const elImage = layoutCanvas.captureElementImage(element);
171
+ await handler({
172
+ canvas: offscreenCanvas,
173
+ element,
174
+ elementImage: elImage,
175
+ });
176
+ await (0, run_effect_chain_js_1.runEffectChain)({
177
+ state: chainState.get(width, height),
178
+ source: offscreenCanvas,
98
179
  effects: effectsRef.current,
99
- output: canvas,
180
+ output: canvas2dRef.current,
100
181
  frame: frameRef.current,
101
- width: w,
102
- height: h,
103
- pixelRatio: pixelRatioRef.current,
104
- })
105
- .then((completed) => {
106
- if (completed) {
107
- continueRender(capturedHandle);
108
- }
109
- })
110
- .catch((err) => {
111
- cancelRender(err);
182
+ width,
183
+ height,
112
184
  });
185
+ continueRender(handle);
113
186
  }
114
187
  catch (error) {
115
188
  cancelRender(error);
116
189
  }
117
- }, [chainState, continueRender, cancelRender]);
118
- // Set up layoutSubtree and persistent paint listener.
119
- (0, react_1.useEffect)(() => {
120
- if (!(0, exports.isHtmlInCanvasSupported)()) {
121
- cancelRender(new Error('HTML in Canvas is not supported. Open this page in Chrome Canary with chrome://flags/#canvas-draw-element enabled.'));
122
- return;
123
- }
124
- const canvas = canvasRef.current;
190
+ }, [
191
+ chainState,
192
+ continueRender,
193
+ cancelRender,
194
+ width,
195
+ height,
196
+ offscreenCanvas,
197
+ ]);
198
+ // Set up layoutSubtree and persistent paint listener. Runs as a
199
+ // layout effect so the listener is attached before the resize effect
200
+ // below dispatches its first synthetic paint.
201
+ (0, react_1.useLayoutEffect)(() => {
202
+ const canvas = canvas2dRef.current;
125
203
  if (!canvas) {
126
- return;
204
+ throw new Error('Canvas not found');
127
205
  }
128
206
  canvas.layoutSubtree = true;
129
- canvas.addEventListener('paint', onPaint);
207
+ canvas.addEventListener('paint', onPaintCb);
130
208
  return () => {
131
- canvas.removeEventListener('paint', onPaint);
209
+ var _a;
210
+ canvas.removeEventListener('paint', onPaintCb);
211
+ unmountedRef.current = true;
212
+ (_a = onInitCleanupRef.current) === null || _a === void 0 ? void 0 : _a.call(onInitCleanupRef);
213
+ onInitCleanupRef.current = null;
132
214
  };
133
- }, [onPaint, cancelRender]);
134
- // On each frame change: block the renderer and request a paint.
135
- (0, react_1.useEffect)(() => {
136
- const handle = delayRender(`HtmlInCanvas (frame ${frame})`);
137
- // Continue a stale handle from a previous frame that never got a paint.
138
- if (pendingHandleRef.current !== null) {
139
- continueRender(pendingHandleRef.current);
215
+ }, [onPaintCb, cancelRender]);
216
+ const onPaintChangedRef = (0, react_1.useRef)(false);
217
+ (0, react_1.useLayoutEffect)(() => {
218
+ var _a;
219
+ if (!onPaintChangedRef.current) {
220
+ onPaintChangedRef.current = true;
221
+ return;
140
222
  }
141
- pendingHandleRef.current = handle;
142
- const canvas = canvasRef.current;
143
- canvas === null || canvas === void 0 ? void 0 : canvas.requestPaint();
223
+ const canvas = canvas2dRef.current;
224
+ if (!canvas) {
225
+ return;
226
+ }
227
+ (_a = canvas.requestPaint) === null || _a === void 0 ? void 0 : _a.call(canvas);
228
+ }, [onPaint]);
229
+ (0, react_1.useLayoutEffect)(() => {
230
+ const canvas = canvas2dRef.current;
231
+ if (!canvas) {
232
+ return;
233
+ }
234
+ const handle = (0, delay_render_js_1.delayRender)('waiting for first paint after canvas resize');
235
+ canvas.addEventListener('paint', () => {
236
+ continueRender(handle);
237
+ }, { once: true });
144
238
  return () => {
145
- if (pendingHandleRef.current === handle) {
146
- continueRender(handle);
147
- pendingHandleRef.current = null;
148
- }
239
+ continueRender(handle);
149
240
  };
150
- }, [frame, delayRender, continueRender]);
151
- const outerStyle = (0, react_1.useMemo)(() => {
152
- return {
153
- position: 'absolute',
154
- inset: 0,
155
- width: width + 'px',
156
- height: height + 'px',
157
- };
158
- }, [width, height]);
241
+ }, [width, height, continueRender]);
159
242
  const innerStyle = (0, react_1.useMemo)(() => {
160
243
  return {
161
244
  width,
162
245
  height,
246
+ ...style,
163
247
  };
164
- }, [width, height]);
165
- return (jsx_runtime_1.jsx(Sequence_js_1.Sequence, { durationInFrames: resolvedDuration, name: "<HtmlInCanvas>", controls: controls, ...sequenceProps, style: style, children: jsx_runtime_1.jsx("canvas", { ref: canvasRef, width: width, height: height, style: outerStyle, children: jsx_runtime_1.jsx("div", { ref: sceneRef, style: innerStyle, children: children }) }) }));
166
- };
248
+ }, [width, height, style]);
249
+ return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { durationInFrames: resolvedDuration, name: "<HtmlInCanvas>", controls: controls, layout: "none", ...sequenceProps, children: (0, jsx_runtime_1.jsx)("canvas", { ref: setLayoutCanvasRef, width: width, height: height, children: (0, jsx_runtime_1.jsx)("div", { ref: divRef, style: innerStyle, children: children }) }) }));
250
+ });
251
+ HtmlInCanvasInner.displayName = 'HtmlInCanvas';
167
252
  const HtmlInCanvasWrapped = (0, wrap_in_schema_js_1.wrapInSchema)(HtmlInCanvasInner, htmlInCanvasSchema);
168
253
  exports.HtmlInCanvas = Object.assign(HtmlInCanvasWrapped, {
169
254
  isHtmlInCanvasSupported: exports.isHtmlInCanvasSupported,
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { LoopDisplay, SequenceControls } from './CompositionManager.js';
3
+ import type { EffectsProp } from './effects/effect-types.js';
3
4
  export type AbsoluteFillLayout = {
4
5
  layout?: 'absolute-fill';
5
6
  premountFor?: number;
@@ -20,6 +21,7 @@ export type SequencePropsWithoutDuration = {
20
21
  readonly name?: string;
21
22
  readonly showInTimeline?: boolean;
22
23
  readonly controls?: SequenceControls;
24
+ readonly _experimentalEffects?: EffectsProp;
23
25
  /**
24
26
  * @deprecated For internal use only.
25
27
  */
@@ -5,6 +5,8 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  /* eslint-disable @typescript-eslint/no-use-before-define */
6
6
  const react_1 = require("react");
7
7
  const AbsoluteFill_js_1 = require("./AbsoluteFill.js");
8
+ const effect_internals_js_1 = require("./effects/effect-internals.js");
9
+ const use_memoized_effects_js_1 = require("./effects/use-memoized-effects.js");
8
10
  const freeze_js_1 = require("./freeze.js");
9
11
  const nonce_js_1 = require("./nonce.js");
10
12
  const PremountContext_js_1 = require("./PremountContext.js");
@@ -15,7 +17,7 @@ const use_current_frame_1 = require("./use-current-frame");
15
17
  const use_remotion_environment_js_1 = require("./use-remotion-environment.js");
16
18
  const use_video_config_js_1 = require("./use-video-config.js");
17
19
  const v5_flag_js_1 = require("./v5-flag.js");
18
- const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Infinity, children, name, height, width, showInTimeline = true, controls, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, ...other }, ref) => {
20
+ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Infinity, children, name, height, width, showInTimeline = true, controls, _experimentalEffects, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, ...other }, ref) => {
19
21
  var _a, _b;
20
22
  const { layout = 'absolute-fill' } = other;
21
23
  const [id] = (0, react_1.useState)(() => String(Math.random()));
@@ -95,6 +97,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
95
97
  }, [name]);
96
98
  const env = (0, use_remotion_environment_js_1.useRemotionEnvironment)();
97
99
  const inheritedStack = (_a = other === null || other === void 0 ? void 0 : other.stack) !== null && _a !== void 0 ? _a : null;
100
+ const memoizedEffects = (0, use_memoized_effects_js_1.useMemoizedEffects)((0, effect_internals_js_1.flattenEffects)(_experimentalEffects !== null && _experimentalEffects !== void 0 ? _experimentalEffects : []));
98
101
  (0, react_1.useEffect)(() => {
99
102
  var _a;
100
103
  if (!env.isStudio) {
@@ -115,6 +118,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
115
118
  premountDisplay: premountDisplay !== null && premountDisplay !== void 0 ? premountDisplay : null,
116
119
  postmountDisplay: postmountDisplay !== null && postmountDisplay !== void 0 ? postmountDisplay : null,
117
120
  controls: controls !== null && controls !== void 0 ? controls : null,
121
+ effects: memoizedEffects,
118
122
  });
119
123
  return () => {
120
124
  unregisterSequence(id);
@@ -139,6 +143,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
139
143
  env.isStudio,
140
144
  inheritedStack,
141
145
  controls,
146
+ memoizedEffects,
142
147
  ]);
143
148
  // Ceil to support floats
144
149
  // https://github.com/remotion-dev/remotion/issues/2958
@@ -4,7 +4,7 @@ export type SolidProps = {
4
4
  readonly color: string;
5
5
  readonly width: number;
6
6
  readonly height: number;
7
- readonly effects?: EffectsProp;
7
+ readonly _experimentalEffects?: EffectsProp;
8
8
  readonly className?: string;
9
9
  readonly style?: React.CSSProperties;
10
10
  readonly pixelRatio?: number;
@@ -7,7 +7,7 @@ const use_current_frame_js_1 = require("../use-current-frame.js");
7
7
  const use_delay_render_js_1 = require("../use-delay-render.js");
8
8
  const run_effect_chain_js_1 = require("./run-effect-chain.js");
9
9
  const use_effect_chain_state_js_1 = require("./use-effect-chain-state.js");
10
- const Solid = ({ color, width, height, effects = [], className, style, pixelRatio = 1, }) => {
10
+ const Solid = ({ color, width, height, _experimentalEffects: experimentalEffects = [], className, style, pixelRatio = 1, }) => {
11
11
  const frame = (0, use_current_frame_js_1.useCurrentFrame)();
12
12
  const { delayRender, continueRender, cancelRender } = (0, use_delay_render_js_1.useDelayRender)();
13
13
  const [outputCanvas, setOutputCanvas] = (0, react_1.useState)(null);
@@ -20,13 +20,19 @@ const Solid = ({ color, width, height, effects = [], className, style, pixelRati
20
20
  canvas.height = 1;
21
21
  return canvas;
22
22
  }, []);
23
- const chainState = (0, use_effect_chain_state_js_1.useEffectChainState)(width, height);
23
+ const chainState = (0, use_effect_chain_state_js_1.useEffectChainState)();
24
24
  // Fill source and run effect chain on every frame / color change.
25
25
  (0, react_1.useEffect)(() => {
26
- if (!outputCanvas || !sourceCanvas || !chainState) {
26
+ if (!outputCanvas || !sourceCanvas) {
27
27
  return;
28
28
  }
29
29
  const handle = delayRender(`Solid effect chain (frame ${frame})`);
30
+ if (!chainState) {
31
+ continueRender(handle);
32
+ return () => {
33
+ continueRender(handle);
34
+ };
35
+ }
30
36
  const ctx = sourceCanvas.getContext('2d', { colorSpace: 'srgb' });
31
37
  if (!ctx) {
32
38
  cancelRender(new Error('Failed to acquire 2D context for <Solid> source'));
@@ -35,14 +41,13 @@ const Solid = ({ color, width, height, effects = [], className, style, pixelRati
35
41
  ctx.fillStyle = color;
36
42
  ctx.fillRect(0, 0, 1, 1);
37
43
  (0, run_effect_chain_js_1.runEffectChain)({
38
- state: chainState,
44
+ state: chainState.get(width, height),
39
45
  source: sourceCanvas,
40
- effects,
46
+ effects: experimentalEffects,
41
47
  output: outputCanvas,
42
48
  frame,
43
49
  width,
44
50
  height,
45
- pixelRatio,
46
51
  })
47
52
  .then((completed) => {
48
53
  if (completed) {
@@ -58,7 +63,7 @@ const Solid = ({ color, width, height, effects = [], className, style, pixelRati
58
63
  }, [
59
64
  frame,
60
65
  color,
61
- effects,
66
+ experimentalEffects,
62
67
  outputCanvas,
63
68
  sourceCanvas,
64
69
  chainState,
@@ -69,6 +74,6 @@ const Solid = ({ color, width, height, effects = [], className, style, pixelRati
69
74
  continueRender,
70
75
  cancelRender,
71
76
  ]);
72
- return (jsx_runtime_1.jsx("canvas", { ref: setOutputCanvas, width: width, height: height, className: className, style: style }));
77
+ return ((0, jsx_runtime_1.jsx)("canvas", { ref: setOutputCanvas, width: width, height: height, className: className, style: style }));
73
78
  };
74
79
  exports.Solid = Solid;
@@ -9,11 +9,9 @@ exports.CanvasPool = void 0;
9
9
  // are created with the cross-backend alpha/sRGB contract enforced (see
10
10
  // `effect-types.ts`).
11
11
  class CanvasPool {
12
- width;
13
- height;
14
- pairs = new Map();
15
- lostContexts = new Set();
16
12
  constructor(width, height) {
13
+ this.pairs = new Map();
14
+ this.lostContexts = new Set();
17
15
  this.width = width;
18
16
  this.height = height;
19
17
  }
@@ -1,3 +1,3 @@
1
1
  import type { EffectDefinition, EffectDescriptor } from './effect-types.js';
2
2
  export declare const defineEffect: <P, S>(definition: EffectDefinition<P, S>) => EffectDefinition<P, S>;
3
- export declare const createDescriptor: <P, S>(definition: EffectDefinition<P, S>, params: P) => EffectDescriptor<P>;
3
+ export declare const createDescriptor: <P, S>(definition: EffectDefinition<P, S>, params: P) => EffectDescriptor<unknown>;
@@ -10,10 +10,14 @@ exports.defineEffect = defineEffect;
10
10
  // Factory helper for constructing per-frame descriptors from a definition.
11
11
  // Effect authors typically expose a small wrapper (e.g.
12
12
  // `export const blur = (params) => createDescriptor(blurDef, params)`) so users
13
- // don't reach into the internal definition object. The state type `S` is
14
- // erased to `unknown` in the descriptor because consumers (the chain runtime,
15
- // other effects in the chain) treat state as opaque; only the definition's
16
- // own `setup`, `apply`, and `cleanup` functions ever see the real shape.
13
+ // don't reach into the internal definition object.
14
+ //
15
+ // `params` is type-checked against `P` at the call site, but the returned
16
+ // descriptor erases both `P` and `S` to `unknown` so it can be freely
17
+ // composed in `EffectsProp` arrays alongside descriptors of other effects.
18
+ // Without this erasure the descriptor would be contravariant in `P` (via
19
+ // `apply`'s argument), which would prevent assigning a concrete
20
+ // `EffectDescriptor<MyParams>` into an `EffectDescriptor<unknown>` slot.
17
21
  const createDescriptor = (definition, params) => {
18
22
  return {
19
23
  definition: definition,
@@ -4,4 +4,4 @@ export type Run = {
4
4
  readonly backend: Backend;
5
5
  readonly effects: ReadonlyArray<EffectDescriptor<unknown>>;
6
6
  };
7
- export declare const groupByBackend: (effects: readonly EffectDescriptor<unknown>[]) => Run[];
7
+ export declare const groupByBackend: (effects: ReadonlyArray<EffectDescriptor<unknown>>) => Run[];
@@ -1,4 +1,4 @@
1
- import type { SequenceSchema } from '../internals';
1
+ import type { SequenceSchema } from '../sequence-field-schema.js';
2
2
  export type Backend = '2d' | 'webgl2' | 'webgpu';
3
3
  type AnyGpuDevice = unknown;
4
4
  export type EffectApplyParams<P, S> = {
@@ -9,15 +9,14 @@ export type EffectApplyParams<P, S> = {
9
9
  readonly frame: number;
10
10
  readonly width: number;
11
11
  readonly height: number;
12
- readonly pixelRatio: number;
13
12
  readonly gpuDevice: AnyGpuDevice | null;
14
13
  };
15
14
  export type EffectDefinition<P, S = unknown> = {
16
15
  readonly type: string;
17
16
  readonly label: string;
18
17
  readonly backend: Backend;
19
- readonly setup: (target: HTMLCanvasElement) => S | Promise<S>;
20
- readonly apply: (params: EffectApplyParams<P, S>) => void | Promise<void>;
18
+ readonly setup: (target: HTMLCanvasElement) => S;
19
+ readonly apply: (params: EffectApplyParams<P, S>) => void;
21
20
  readonly cleanup: (state: S) => void;
22
21
  readonly schema: SequenceSchema | null;
23
22
  };
@@ -28,5 +27,5 @@ export type EffectDefinitionAndStack<P = unknown> = {
28
27
  export type EffectDescriptor<P = unknown> = EffectDefinitionAndStack<P> & {
29
28
  readonly params: P;
30
29
  };
31
- export type EffectsProp = ReadonlyArray<EffectDescriptor<any> | ReadonlyArray<EffectDescriptor<any>>>;
30
+ export type EffectsProp = ReadonlyArray<EffectDescriptor<unknown> | ReadonlyArray<EffectDescriptor<unknown>>>;
32
31
  export {};
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // Public types for the canvas-effects system.
2
+ // Internal types for the effects system (exported via `remotion` → `./internals.js`).
3
3
  //
4
4
  // An effect is a description of how to transform an input image into an output
5
5
  // image, executed inside a per-frame chain runtime owned by a source component
@@ -10,4 +10,3 @@
10
10
  // sRGB-encoded. Effects that perform color math in linear space are responsible
11
11
  // for converting to/from sRGB themselves.
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- /* eslint-enable @typescript-eslint/no-explicit-any */