remotion 4.0.533 → 4.0.534

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 (38) hide show
  1. package/dist/cjs/CompositionManager.d.ts +4 -0
  2. package/dist/cjs/CompositionManagerProvider.js +6 -9
  3. package/dist/cjs/HtmlInCanvas.js +2 -1
  4. package/dist/cjs/Sequence.d.ts +5 -0
  5. package/dist/cjs/Sequence.js +44 -11
  6. package/dist/cjs/SequenceManager.d.ts +9 -7
  7. package/dist/cjs/SequenceManager.js +24 -4
  8. package/dist/cjs/Track.d.ts +6 -0
  9. package/dist/cjs/Track.js +32 -0
  10. package/dist/cjs/animated-image/AnimatedImage.js +2 -1
  11. package/dist/cjs/canvas-image/CanvasImage.js +2 -1
  12. package/dist/cjs/effects/Solid.js +4 -3
  13. package/dist/cjs/effects/use-memoized-effects.d.ts +3 -1
  14. package/dist/cjs/effects/use-memoized-effects.js +8 -4
  15. package/dist/cjs/evaluate-source-expressions.d.ts +5 -0
  16. package/dist/cjs/evaluate-source-expressions.js +98 -0
  17. package/dist/cjs/freeze.js +6 -8
  18. package/dist/cjs/index.d.ts +2 -0
  19. package/dist/cjs/index.js +3 -1
  20. package/dist/cjs/internals.d.ts +10 -3
  21. package/dist/cjs/internals.js +8 -0
  22. package/dist/cjs/no-react.d.ts +2 -0
  23. package/dist/cjs/no-react.js +3 -0
  24. package/dist/cjs/series/index.js +3 -1
  25. package/dist/cjs/timeline-track-context.d.ts +9 -0
  26. package/dist/cjs/timeline-track-context.js +6 -0
  27. package/dist/cjs/use-media-in-timeline.js +14 -2
  28. package/dist/cjs/use-schema.d.ts +32 -18
  29. package/dist/cjs/version.d.ts +1 -1
  30. package/dist/cjs/version.js +1 -1
  31. package/dist/cjs/video/html5-video.js +1 -1
  32. package/dist/cjs/with-interactivity-schema.js +8 -2
  33. package/dist/cjs/wrap-remotion-context.d.ts +2 -0
  34. package/dist/cjs/wrap-remotion-context.js +8 -1
  35. package/dist/esm/index.mjs +404 -205
  36. package/dist/esm/no-react.mjs +78 -0
  37. package/dist/esm/version.mjs +1 -1
  38. package/package.json +2 -2
@@ -7,6 +7,7 @@ import type { EffectDefinition } from './effects/effect-types.js';
7
7
  import type { InteractivitySchema } from './interactivity-schema.js';
8
8
  import type { InferProps, PropsIfHasProps } from './props-if-has-props.js';
9
9
  import type { RuntimeValueSnapshot, RuntimeValueStore } from './runtime-value-store.js';
10
+ import type { TimelineTrackItem } from './timeline-track-context.js';
10
11
  import type { VideoConfigValues } from './video-config.js';
11
12
  export type TComposition<Schema extends AnyZodObject, Props extends Record<string, unknown>> = {
12
13
  width: number | undefined;
@@ -89,8 +90,11 @@ export type TSequence = {
89
90
  parent: string | null;
90
91
  showInTimeline: boolean;
91
92
  timelineOrder: number | null;
93
+ timelineTrack?: TimelineTrackItem;
92
94
  loopDisplay: LoopDisplay | undefined;
93
95
  getStack: () => string | null;
96
+ /** Read the committed child clock without updating timeline metadata each frame. */
97
+ getCurrentFrame?: (() => number) | null;
94
98
  premountDisplay: number | null;
95
99
  postmountDisplay: number | null;
96
100
  controls: SequenceRegistrationControls | null;
@@ -28,11 +28,10 @@ const CompositionManagerProvider = ({ children, onlyRenderComposition, currentCo
28
28
  // CompositionManagerProvider state
29
29
  const currentcompositionsRef = (0, react_1.useRef)(compositions);
30
30
  const updateCompositions = (0, react_1.useCallback)((updateComps) => {
31
- setCompositions((comps) => {
32
- const updated = updateComps(comps);
33
- currentcompositionsRef.current = updated;
34
- return updated;
35
- });
31
+ // The renderer may read registrations before React commits the state.
32
+ const updated = updateComps(currentcompositionsRef.current);
33
+ currentcompositionsRef.current = updated;
34
+ setCompositions(updated);
36
35
  }, []);
37
36
  const registerComposition = (0, react_1.useCallback)((comp) => {
38
37
  const orderKey = (0, sequence_order_marker_js_1.getCompositionAndFolderOrderKey)({
@@ -58,10 +57,8 @@ const CompositionManagerProvider = ({ children, onlyRenderComposition, currentCo
58
57
  }, [updateCompositions]);
59
58
  const unregisterComposition = (0, react_1.useCallback)((id) => {
60
59
  internalOrderRef.current.delete((0, sequence_order_marker_js_1.getCompositionAndFolderOrderKey)({ type: 'composition', id }));
61
- setCompositions((comps) => {
62
- return comps.filter((c) => c.id !== id);
63
- });
64
- }, []);
60
+ updateCompositions((comps) => comps.filter((c) => c.id !== id));
61
+ }, [updateCompositions]);
65
62
  const registerFolder = (0, react_1.useCallback)((name, parent, stack) => {
66
63
  const orderKey = (0, sequence_order_marker_js_1.getCompositionAndFolderOrderKey)({
67
64
  type: 'folder',
@@ -147,7 +147,7 @@ const defaultOnPaint = ({ canvas, elementImage, }) => {
147
147
  /* eslint-enable react/require-default-props */
148
148
  const HtmlInCanvasAncestorContext = (0, react_1.createContext)(false);
149
149
  const HtmlInCanvasContent = (0, react_1.forwardRef)(({ width, height, effects, children, canvasSiblings, onPaint, onInit, pixelDensity, controls, style, }, ref) => {
150
- var _a;
150
+ var _a, _b;
151
151
  const isInsideAncestorHtmlInCanvas = (0, react_1.useContext)(HtmlInCanvasAncestorContext);
152
152
  assertHtmlInCanvasDimensions(width, height);
153
153
  if (isInsideAncestorHtmlInCanvas && !(0, exports.isHtmlInCanvasNestingSupported)()) {
@@ -180,6 +180,7 @@ const HtmlInCanvasContent = (0, react_1.forwardRef)(({ width, height, effects, c
180
180
  const memoizedEffects = (0, use_memoized_effects_js_1.useMemoizedEffects)({
181
181
  effects,
182
182
  overrideId: (_a = controls === null || controls === void 0 ? void 0 : controls.overrideId) !== null && _a !== void 0 ? _a : null,
183
+ videoConfigValues: (_b = controls === null || controls === void 0 ? void 0 : controls.videoConfigValues) !== null && _b !== void 0 ? _b : null,
183
184
  });
184
185
  // Refs so the paint handler always reads fresh values.
185
186
  const effectsRef = (0, react_1.useRef)(memoizedEffects);
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { LoopDisplay, SequenceControls } from './CompositionManager.js';
3
3
  import type { EffectDefinition } from './effects/effect-types.js';
4
+ import { type TimelineTrackItem } from './timeline-track-context.js';
4
5
  import type { BasicMediaInTimelineReturnType } from './use-media-in-timeline.js';
5
6
  export type AbsoluteFillLayout = {
6
7
  layout?: 'absolute-fill';
@@ -32,6 +33,10 @@ export type SequencePropsWithoutDuration = {
32
33
  readonly hidden?: boolean;
33
34
  readonly controls?: SequenceControls;
34
35
  readonly _remotionInternalEffects?: readonly EffectDefinition<unknown>[];
36
+ /**
37
+ * @deprecated For internal use only.
38
+ */
39
+ readonly _remotionInternalTimelineTrack?: Pick<TimelineTrackItem, 'role'>;
35
40
  /**
36
41
  * @deprecated For internal use only.
37
42
  */
@@ -18,6 +18,7 @@ const SequenceContext_js_1 = require("./SequenceContext.js");
18
18
  const SequenceManager_js_1 = require("./SequenceManager.js");
19
19
  const is_inside_series_js_1 = require("./series/is-inside-series.js");
20
20
  const timeline_position_state_js_1 = require("./timeline-position-state.js");
21
+ const timeline_track_context_js_1 = require("./timeline-track-context.js");
21
22
  const use_premounting_js_1 = require("./use-premounting.js");
22
23
  const use_remotion_environment_js_1 = require("./use-remotion-environment.js");
23
24
  const use_sequence_registration_js_1 = require("./use-sequence-registration.js");
@@ -26,12 +27,14 @@ const v5_flag_js_1 = require("./v5-flag.js");
26
27
  const with_interactivity_schema_js_1 = require("./with-interactivity-schema.js");
27
28
  const SeriesContentVisibilityContext = (0, react_1.createContext)(true);
28
29
  const EMPTY_EFFECTS = [];
29
- const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playbackRate = 1, loop = false, freeze, durationInFrames, children, name, height, width, showInTimeline = true, hidden = false, controls, _remotionInternalEffects, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalDocumentationLink: documentationLink, _remotionInternalSingleChildComponent: singleChildComponent, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, _remotionInternalIsMedia: isMedia, outlineRef: passedRefForOutline, cropLeft, cropRight, cropTop, cropBottom, ...other }, ref) => {
30
- var _a, _b, _c, _d, _e;
30
+ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playbackRate = 1, loop = false, freeze, durationInFrames, children, name, height, width, showInTimeline = true, hidden = false, controls, _remotionInternalEffects, _remotionInternalTimelineTrack: timelineTrackItem, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalDocumentationLink: documentationLink, _remotionInternalSingleChildComponent: singleChildComponent, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, _remotionInternalIsMedia: isMedia, outlineRef: passedRefForOutline, cropLeft, cropRight, cropTop, cropBottom, ...other }, ref) => {
31
+ var _a, _b, _c, _d, _e, _f;
31
32
  const { layout = 'absolute-fill' } = other;
32
33
  const [id] = (0, react_1.useState)(() => String(Math.random()));
33
34
  const parentSequence = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
34
- const parentPlaybackRate = (_a = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.playbackRate) !== null && _a !== void 0 ? _a : 1;
35
+ const timelineTrack = (0, react_1.useContext)(timeline_track_context_js_1.TimelineTrackContext);
36
+ const timelineTrackRole = (_a = timelineTrackItem === null || timelineTrackItem === void 0 ? void 0 : timelineTrackItem.role) !== null && _a !== void 0 ? _a : 'clip';
37
+ const parentPlaybackRate = (_b = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.playbackRate) !== null && _b !== void 0 ? _b : 1;
35
38
  const cumulativePlaybackRate = parentPlaybackRate * playbackRate;
36
39
  const cumulatedFrom = parentSequence
37
40
  ? parentSequence.cumulatedFrom + parentSequence.relativeFrom
@@ -141,7 +144,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
141
144
  const clockFrom = from + loopOffset;
142
145
  const effectiveRelativeFrom = clockFrom - trimBefore / playbackRate;
143
146
  const relativeFrom = effectiveRelativeFrom / parentPlaybackRate;
144
- const absoluteFrom = ((_b = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.absoluteFrom) !== null && _b !== void 0 ? _b : 0) + relativeFrom;
147
+ const absoluteFrom = ((_c = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.absoluteFrom) !== null && _c !== void 0 ? _c : 0) + relativeFrom;
145
148
  // Inside a loop, the local clock ends after `durationInFrames`, except in a final
146
149
  // iteration that the parent cuts short.
147
150
  const localClockEnd = loopPeriod === null
@@ -255,11 +258,18 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
255
258
  // and if it changes, it would lead to-remounting of the sequence.
256
259
  const stackRef = (0, react_1.useRef)(null);
257
260
  stackRef.current = controls
258
- ? ((_d = (_c = (0, enable_sequence_stack_traces_js_1.getStackForControls)(controls)) !== null && _c !== void 0 ? _c : stack) !== null && _d !== void 0 ? _d : null)
261
+ ? ((_e = (_d = (0, enable_sequence_stack_traces_js_1.getStackForControls)(controls)) !== null && _d !== void 0 ? _d : stack) !== null && _e !== void 0 ? _e : null)
259
262
  : (stack !== null && stack !== void 0 ? stack : null);
263
+ const getStack = (0, react_1.useCallback)(() => stackRef.current, []);
260
264
  const registeredFrozenFrame = typeof freeze === 'number' ? freeze : null;
265
+ const currentFrame = registeredFrozenFrame !== null && registeredFrozenFrame !== void 0 ? registeredFrozenFrame : (absoluteFrame - currentSequenceStart) * cumulativePlaybackRate;
266
+ const currentFrameRef = (0, react_1.useRef)(currentFrame);
267
+ (0, react_1.useLayoutEffect)(() => {
268
+ currentFrameRef.current = currentFrame;
269
+ }, [currentFrame]);
270
+ const getCurrentFrame = (0, react_1.useCallback)(() => currentFrameRef.current, []);
261
271
  const registeredTrimBefore = trimBefore === 0 ? null : trimBefore;
262
- const parentCumulatedNegativeFrom = (_e = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.cumulatedNegativeFrom) !== null && _e !== void 0 ? _e : 0;
272
+ const parentCumulatedNegativeFrom = (_f = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.cumulatedNegativeFrom) !== null && _f !== void 0 ? _f : 0;
263
273
  const startMediaFrom = isMedia && isMedia.type !== 'image'
264
274
  ? isMedia.data.startMediaFrom +
265
275
  parentCumulatedNegativeFrom -
@@ -335,9 +345,18 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
335
345
  ]);
336
346
  const getSequenceForRegistration = (0, react_1.useCallback)(() => {
337
347
  var _a, _b, _c;
348
+ const trackRegistration = timelineTrack
349
+ ? {
350
+ timelineTrack: {
351
+ ...timelineTrack,
352
+ role: timelineTrackRole,
353
+ },
354
+ }
355
+ : {};
338
356
  if (isMedia) {
339
357
  if (isMedia.type === 'image') {
340
358
  return {
359
+ ...trackRegistration,
341
360
  sequencePlaybackRate: playbackRate,
342
361
  type: 'image',
343
362
  controls: registrationControls,
@@ -356,7 +375,8 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
356
375
  showInTimeline,
357
376
  timelineOrder: null,
358
377
  src: isMedia.src,
359
- getStack: () => stackRef.current,
378
+ getStack,
379
+ getCurrentFrame,
360
380
  refForOutline: refForOutline !== null && refForOutline !== void 0 ? refForOutline : null,
361
381
  isInsideSeries,
362
382
  frozenFrame: registeredFrozenFrame,
@@ -364,6 +384,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
364
384
  };
365
385
  }
366
386
  return {
387
+ ...trackRegistration,
367
388
  type: isMedia.type,
368
389
  sequencePlaybackRate: playbackRate,
369
390
  controls: registrationControls,
@@ -384,7 +405,8 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
384
405
  showInTimeline,
385
406
  timelineOrder: null,
386
407
  src: isMedia.data.src,
387
- getStack: () => stackRef.current,
408
+ getStack,
409
+ getCurrentFrame,
388
410
  startMediaFrom: startMediaFrom !== null && startMediaFrom !== void 0 ? startMediaFrom : isMedia.data.startMediaFrom,
389
411
  mediaFrameAtSequenceZero,
390
412
  volume: isMedia.data.volumes,
@@ -397,6 +419,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
397
419
  };
398
420
  }
399
421
  return {
422
+ ...trackRegistration,
400
423
  from,
401
424
  sequencePlaybackRate: playbackRate,
402
425
  trimBefore: registeredTrimBefore,
@@ -412,7 +435,8 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
412
435
  showInTimeline,
413
436
  timelineOrder: null,
414
437
  loopDisplay: resolvedLoopDisplay,
415
- getStack: () => stackRef.current,
438
+ getStack,
439
+ getCurrentFrame,
416
440
  premountDisplay: premountDisplay !== null && premountDisplay !== void 0 ? premountDisplay : null,
417
441
  postmountDisplay: postmountDisplay !== null && postmountDisplay !== void 0 ? postmountDisplay : null,
418
442
  controls: registrationControls,
@@ -425,6 +449,10 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
425
449
  };
426
450
  }, [
427
451
  id,
452
+ timelineTrack,
453
+ timelineTrackRole,
454
+ getCurrentFrame,
455
+ getStack,
428
456
  timelineClipName,
429
457
  playbackRate,
430
458
  parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.id,
@@ -513,8 +541,13 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, playba
513
541
  // Retain resolved scene registrations so Studio can recover the natural end
514
542
  // even when seeking past an explicit wrapper trim. Scene contents stay hidden.
515
543
  const retainSeriesRegistration = env.isStudio && canInferDuration;
516
- const sequenceContent = loopedContent === null ? (retainSeriesRegistration ? (children) : null) : other.layout === 'none' ? (loopedContent) : (jsx_runtime_1.jsx(AbsoluteFillElement_js_1.AbsoluteFillElement, { ref: sequenceRef, style: defaultStyle, className: other.className, children: loopedContent }));
517
- const sequence = (jsx_runtime_1.jsx(SequenceContext_js_1.SequenceContext.Provider, { value: contextValue, children: retainSeriesRegistration ? (jsx_runtime_1.jsx(SeriesContentVisibilityContext.Provider, { value: content !== null, children: sequenceContent })) : (sequenceContent) }));
544
+ const renderedContent = loopedContent === null ? (retainSeriesRegistration ? (children) : null) : other.layout === 'none' ? (loopedContent) : (jsx_runtime_1.jsx(AbsoluteFillElement_js_1.AbsoluteFillElement, { ref: sequenceRef, style: defaultStyle, className: other.className, children: loopedContent }));
545
+ const sequenceContent = (jsx_runtime_1.jsx(SequenceContext_js_1.SequenceContext.Provider, { value: contextValue, children: retainSeriesRegistration ? (jsx_runtime_1.jsx(SeriesContentVisibilityContext.Provider, { value: content !== null, children: renderedContent })) : (renderedContent) }));
546
+ // Keep the provider mounted when track grouping changes so clip state survives.
547
+ const sequence = (jsx_runtime_1.jsx(timeline_track_context_js_1.TimelineTrackContext.Provider, { value: timelineTrackRole === 'track' ||
548
+ (timelineTrackRole === 'clip' && !showInTimeline)
549
+ ? timelineTrack
550
+ : null, children: sequenceContent }));
518
551
  return shouldDiscoverOutline ? (jsx_runtime_1.jsx(sequence_order_marker_js_1.SequenceOrderMarker, { sequenceId: id, outlineChildrenRef: automaticOutlineRef, children: sequence })) : (sequence);
519
552
  };
520
553
  const RegularSequence = (0, react_1.forwardRef)(RegularSequenceRefForwardingFunction);
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { TSequence } from './CompositionManager.js';
3
3
  import type { CanUpdateSequencePropStatus, DragOverrideValue, GetDragOverrides, GetEffectDragOverrides, PropStatuses } from './use-schema.js';
4
+ import type { SourceNumericValue } from './use-schema.js';
4
5
  import type { VideoConfigValues } from './video-config.js';
5
6
  export type SequenceManagerContext = {
6
7
  registerSequence: (seq: TSequence) => void;
@@ -66,12 +67,12 @@ export type VisualModeBatchSetters = {
66
67
  readonly value: DragOverrideValue;
67
68
  }[]) => void;
68
69
  };
69
- export type CanUpdateEffectPropsResponseTrue = {
70
+ export type CanUpdateEffectPropsResponseTrue<Numeric = SourceNumericValue> = {
70
71
  canUpdate: true;
71
72
  callee: string;
72
73
  importPath: string | null;
73
74
  effectIndex: number;
74
- props: Record<string, CanUpdateSequencePropStatus>;
75
+ props: Record<string, CanUpdateSequencePropStatus<Numeric>>;
75
76
  };
76
77
  export type CannotUpdateEffectReason = 'not-found' | 'computed' | 'not-call-expression';
77
78
  export type CannotUpdateSequenceReason = 'not-found' | 'error';
@@ -80,17 +81,17 @@ export type CanUpdateEffectPropsResponseFalse = {
80
81
  effectIndex: number;
81
82
  reason: CannotUpdateEffectReason;
82
83
  };
83
- export type CanUpdateEffectPropsResponse = CanUpdateEffectPropsResponseTrue | CanUpdateEffectPropsResponseFalse;
84
- export type CanUpdateSequencePropsResponseTrue = {
84
+ export type CanUpdateEffectPropsResponse<Numeric = SourceNumericValue> = CanUpdateEffectPropsResponseTrue<Numeric> | CanUpdateEffectPropsResponseFalse;
85
+ export type CanUpdateSequencePropsResponseTrue<Numeric = SourceNumericValue> = {
85
86
  canUpdate: true;
86
- props: Record<string, CanUpdateSequencePropStatus>;
87
- effects: CanUpdateEffectPropsResponse[];
87
+ props: Record<string, CanUpdateSequencePropStatus<Numeric>>;
88
+ effects: CanUpdateEffectPropsResponse<Numeric>[];
88
89
  };
89
90
  export type CanUpdateSequencePropsResponseFalse = {
90
91
  canUpdate: false;
91
92
  reason: CannotUpdateSequenceReason;
92
93
  };
93
- export type CanUpdateSequencePropsResponse = CanUpdateSequencePropsResponseTrue | CanUpdateSequencePropsResponseFalse;
94
+ export type CanUpdateSequencePropsResponse<Numeric = SourceNumericValue> = CanUpdateSequencePropsResponseTrue<Numeric> | CanUpdateSequencePropsResponseFalse;
94
95
  export declare const makeSequencePropsSubscriptionKey: (key: SequencePropsSubscriptionKey) => string;
95
96
  export declare const VisualModePropStatusesContext: React.Context<VisualModePropStatuses>;
96
97
  export declare const VisualModePropStatusesRefContext: React.Context<VisualModePropStatusesRef>;
@@ -105,6 +106,7 @@ export type SequencePropsSubscriptionKey = {
105
106
  nodePath: SequenceNodePath;
106
107
  sequenceKeys: string[];
107
108
  effectKeys: string[][];
109
+ /** Evaluation context of a rendered instance. Never part of source identity. */
108
110
  videoConfigValues: VideoConfigValues | null;
109
111
  };
110
112
  export type { VideoConfigValues } from './video-config.js';
@@ -505,11 +505,31 @@ const SequenceManagerProvider = ({ children }) => {
505
505
  if (index === -1) {
506
506
  return seqs;
507
507
  }
508
+ const previousSequence = seqs[index];
509
+ const timelineOrder = (_b = (_a = committedOrderRef.current) === null || _a === void 0 ? void 0 : _a.get(seq.id)) !== null && _b !== void 0 ? _b : null;
510
+ // A new registration getter does not necessarily change the metadata.
511
+ let unchanged = Object.is(previousSequence.timelineOrder, timelineOrder);
512
+ // The manager adds timelineOrder even if the incoming object omits it.
513
+ let keyCount = 1;
514
+ if (unchanged) {
515
+ for (const key in seq) {
516
+ if (key === 'timelineOrder' ||
517
+ !Object.prototype.hasOwnProperty.call(seq, key)) {
518
+ continue;
519
+ }
520
+ keyCount++;
521
+ if (!Object.prototype.hasOwnProperty.call(previousSequence, key) ||
522
+ !Object.is(previousSequence[key], seq[key])) {
523
+ unchanged = false;
524
+ break;
525
+ }
526
+ }
527
+ }
528
+ if (unchanged && Object.keys(previousSequence).length === keyCount) {
529
+ return seqs;
530
+ }
508
531
  const next = [...seqs];
509
- next[index] = {
510
- ...seq,
511
- timelineOrder: (_b = (_a = committedOrderRef.current) === null || _a === void 0 ? void 0 : _a.get(seq.id)) !== null && _b !== void 0 ? _b : null,
512
- };
532
+ next[index] = { ...seq, timelineOrder };
513
533
  return next;
514
534
  });
515
535
  }, []);
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import { type SequenceProps } from './Sequence.js';
3
+ export type TrackProps = Pick<SequenceProps, 'children' | 'name' | 'from' | 'durationInFrames' | 'hidden' | 'showInTimeline'>;
4
+ export declare const TrackWithoutSchema: React.FC<SequenceProps>;
5
+ /** Groups clips on one Studio timeline row and applies Sequence timing. */
6
+ export declare const Track: React.ComponentType<TrackProps>;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Track = exports.TrackWithoutSchema = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const enable_sequence_stack_traces_js_1 = require("./enable-sequence-stack-traces.js");
7
+ const interactivity_schema_js_1 = require("./interactivity-schema.js");
8
+ const Sequence_js_1 = require("./Sequence.js");
9
+ const timeline_track_context_js_1 = require("./timeline-track-context.js");
10
+ const with_interactivity_schema_js_1 = require("./with-interactivity-schema.js");
11
+ const trackSchema = {
12
+ name: interactivity_schema_js_1.sequenceSchema.name,
13
+ hidden: interactivity_schema_js_1.sequenceSchema.hidden,
14
+ };
15
+ // Series variants share the Track foundation while retaining their own
16
+ // Sequence props, layout defaults, and interactivity schemas.
17
+ const TrackWithoutSchema = ({ name = 'Track', children, layout = 'none', ...props }) => {
18
+ const [id] = (0, react_1.useState)(() => String(Math.random()));
19
+ const tracksEnabled = (0, react_1.useContext)(timeline_track_context_js_1.ExperimentalTracksEnabledContext);
20
+ const value = (0, react_1.useMemo)(() => (tracksEnabled ? { id, name } : null), [id, name, tracksEnabled]);
21
+ return (jsx_runtime_1.jsx(timeline_track_context_js_1.TimelineTrackContext.Provider, { value: value, children: jsx_runtime_1.jsx(Sequence_js_1.SequenceWithoutSchema, { ...props, name: name, layout: layout, _remotionInternalTimelineTrack: { role: 'track' }, children: children }) }));
22
+ };
23
+ exports.TrackWithoutSchema = TrackWithoutSchema;
24
+ /** Groups clips on one Studio timeline row and applies Sequence timing. */
25
+ exports.Track = (0, with_interactivity_schema_js_1.withInteractivitySchema)({
26
+ Component: exports.TrackWithoutSchema,
27
+ componentName: '<Track>',
28
+ componentIdentity: 'dev.remotion.remotion.Track',
29
+ schema: trackSchema,
30
+ supportsEffects: false,
31
+ });
32
+ (0, enable_sequence_stack_traces_js_1.addSequenceStackTraces)(exports.Track);
@@ -52,7 +52,7 @@ const getCanvasPropsFromSequenceProps = (props) => {
52
52
  return canvasProps;
53
53
  };
54
54
  const AnimatedImageContent = (0, react_2.forwardRef)(({ src, width, height, onError, loopBehavior = 'loop', playbackRate = 1, fit = 'fill', requestInit, effects, controls, ...props }, canvasRef) => {
55
- var _a;
55
+ var _a, _b;
56
56
  const resolvedSrc = (0, resolve_image_source_1.resolveAnimatedImageSource)(src);
57
57
  const [imageDecoder, setImageDecoder] = (0, react_2.useState)(null);
58
58
  const { delayRender, continueRender } = (0, use_delay_render_js_1.useDelayRender)();
@@ -69,6 +69,7 @@ const AnimatedImageContent = (0, react_2.forwardRef)(({ src, width, height, onEr
69
69
  const memoizedEffects = (0, use_memoized_effects_js_1.useMemoizedEffects)({
70
70
  effects,
71
71
  overrideId: (_a = controls === null || controls === void 0 ? void 0 : controls.overrideId) !== null && _a !== void 0 ? _a : null,
72
+ videoConfigValues: (_b = controls === null || controls === void 0 ? void 0 : controls.videoConfigValues) !== null && _b !== void 0 ? _b : null,
72
73
  });
73
74
  (0, react_2.useImperativeHandle)(canvasRef, () => {
74
75
  var _a;
@@ -111,7 +111,7 @@ const waitForNextFrame = ({ onFrame, }) => {
111
111
  return () => cancelAnimationFrame(frame);
112
112
  };
113
113
  const CanvasImageContent = (0, react_1.forwardRef)(({ src, crossOrigin, width, height, fit = 'fill', effects, controls, onError, className, style, id, pauseWhenLoading, maxRetries = 2, delayRenderRetries, delayRenderTimeoutInMilliseconds, ...canvasProps }, ref) => {
114
- var _a;
114
+ var _a, _b;
115
115
  const { delayRender, continueRender, cancelRender } = (0, use_delay_render_js_1.useDelayRender)();
116
116
  const { delayPlayback } = (0, use_buffer_state_js_1.useBufferState)();
117
117
  const [outputCanvas, setOutputCanvas] = (0, react_1.useState)(null);
@@ -121,6 +121,7 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, crossOrigin, width, h
121
121
  const memoizedEffects = (0, use_memoized_effects_js_1.useMemoizedEffects)({
122
122
  effects,
123
123
  overrideId: (_a = controls === null || controls === void 0 ? void 0 : controls.overrideId) !== null && _a !== void 0 ? _a : null,
124
+ videoConfigValues: (_b = controls === null || controls === void 0 ? void 0 : controls.videoConfigValues) !== null && _b !== void 0 ? _b : null,
124
125
  });
125
126
  const sequenceContext = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
126
127
  const pendingLoadDelayRef = (0, react_1.useRef)(null);
@@ -65,7 +65,7 @@ exports.solidSchema = {
65
65
  ...interactivity_schema_js_1.borderRadiusSchema,
66
66
  ...interactivity_schema_js_1.cropSchema,
67
67
  };
68
- const SolidInner = ({ color, width, height, effects = [], className, style, pixelDensity, overrideId, reference, }) => {
68
+ const SolidInner = ({ color, width, height, effects = [], className, style, pixelDensity, overrideId, videoConfigValues, reference, }) => {
69
69
  const { delayRender, continueRender, cancelRender } = (0, use_delay_render_js_1.useDelayRender)();
70
70
  const resolvedPixelDensity = resolveSolidPixelDensity(pixelDensity);
71
71
  const canvasWidth = Math.ceil(width * resolvedPixelDensity);
@@ -74,6 +74,7 @@ const SolidInner = ({ color, width, height, effects = [], className, style, pixe
74
74
  const memoizedEffects = (0, use_memoized_effects_js_1.useMemoizedEffects)({
75
75
  effects,
76
76
  overrideId: overrideId !== null && overrideId !== void 0 ? overrideId : null,
77
+ videoConfigValues,
77
78
  });
78
79
  const sourceCanvas = (0, react_1.useMemo)(() => {
79
80
  if (typeof document === 'undefined') {
@@ -157,7 +158,7 @@ const SolidInner = ({ color, width, height, effects = [], className, style, pixe
157
158
  return (jsx_runtime_1.jsx("canvas", { ref: canvasRef, width: canvasWidth, height: canvasHeight, className: className, style: canvasStyle }));
158
159
  };
159
160
  const SolidOuter = (0, react_1.forwardRef)(({ effects = [], controls, color, height, width, className, durationInFrames, style, name, from, premountFor, postmountFor, styleWhilePremounted, styleWhilePostmounted, trimBefore, playbackRate, loop, freeze, hidden, showInTimeline, pixelDensity, cropLeft, cropRight, cropTop, cropBottom, ...props }, ref) => {
160
- var _a;
161
+ var _a, _b;
161
162
  props;
162
163
  const memoizedEffectDefinitions = (0, use_memoized_effects_js_1.useMemoizedEffectDefinitions)(effects);
163
164
  const actualRef = (0, react_1.useRef)(null);
@@ -186,7 +187,7 @@ const SolidOuter = (0, react_1.forwardRef)(({ effects = [], controls, color, hei
186
187
  style: premountingStyle,
187
188
  componentName: '<Solid />',
188
189
  });
189
- return (jsx_runtime_1.jsx(freeze_js_1.Freeze, { frame: freezeFrame, active: isPremountingOrPostmounting, _remotionInternalIsPremounting: premountingActive, children: jsx_runtime_1.jsx(Sequence_js_1.Sequence, { layout: "none", from: from, trimBefore: trimBefore, playbackRate: playbackRate, loop: loop, freeze: freeze, hidden: hidden, showInTimeline: showInTimeline, controls: controls, _remotionInternalEffects: memoizedEffectDefinitions, durationInFrames: durationInFrames, name: name !== null && name !== void 0 ? name : '<Solid>', _remotionInternalDocumentationLink: "https://www.remotion.dev/docs/solid", ...props, _remotionInternalPremountDisplay: effectivePremountFor || null, _remotionInternalPostmountDisplay: effectivePostmountFor || null, _remotionInternalIsPremounting: premountingActive, _remotionInternalIsPostmounting: postmountingActive, children: jsx_runtime_1.jsx(SolidInner, { reference: actualRef, overrideId: (_a = controls === null || controls === void 0 ? void 0 : controls.overrideId) !== null && _a !== void 0 ? _a : null, color: color, height: height, width: width, className: className, style: croppedStyle !== null && croppedStyle !== void 0 ? croppedStyle : undefined, effects: effects, pixelDensity: pixelDensity }) }) }));
190
+ return (jsx_runtime_1.jsx(freeze_js_1.Freeze, { frame: freezeFrame, active: isPremountingOrPostmounting, _remotionInternalIsPremounting: premountingActive, children: jsx_runtime_1.jsx(Sequence_js_1.Sequence, { layout: "none", from: from, trimBefore: trimBefore, playbackRate: playbackRate, loop: loop, freeze: freeze, hidden: hidden, showInTimeline: showInTimeline, controls: controls, _remotionInternalEffects: memoizedEffectDefinitions, durationInFrames: durationInFrames, name: name !== null && name !== void 0 ? name : '<Solid>', _remotionInternalDocumentationLink: "https://www.remotion.dev/docs/solid", ...props, _remotionInternalPremountDisplay: effectivePremountFor || null, _remotionInternalPostmountDisplay: effectivePostmountFor || null, _remotionInternalIsPremounting: premountingActive, _remotionInternalIsPostmounting: postmountingActive, children: jsx_runtime_1.jsx(SolidInner, { reference: actualRef, videoConfigValues: (_a = controls === null || controls === void 0 ? void 0 : controls.videoConfigValues) !== null && _a !== void 0 ? _a : null, overrideId: (_b = controls === null || controls === void 0 ? void 0 : controls.overrideId) !== null && _b !== void 0 ? _b : null, color: color, height: height, width: width, className: className, style: croppedStyle !== null && croppedStyle !== void 0 ? croppedStyle : undefined, effects: effects, pixelDensity: pixelDensity }) }) }));
190
191
  });
191
192
  exports.Solid = (0, with_interactivity_schema_js_1.withInteractivitySchema)({
192
193
  Component: SolidOuter,
@@ -2,6 +2,7 @@ import type { RuntimeValueStore } from '../runtime-value-store.js';
2
2
  import type { CannotUpdateEffectReason, CannotUpdateSequenceReason } from '../SequenceManager.js';
3
3
  import { type SequencePropsSubscriptionKey } from '../SequenceManager.js';
4
4
  import { type CanUpdateSequencePropStatus, type PropStatuses } from '../use-schema.js';
5
+ import type { VideoConfigValues } from '../video-config.js';
5
6
  import type { EffectDefinitionAndStack, EffectDescriptor, EffectDefinition } from './effect-types.js';
6
7
  export declare const useMemoizedEffectDefinitions: (effects: readonly EffectDescriptor<unknown>[]) => readonly EffectDefinition<unknown>[] & {
7
8
  readonly runtimeValues: readonly RuntimeValueStore[];
@@ -23,8 +24,9 @@ export declare const getEffectPropStatusesCtx: ({ propStatuses, nodePath, effect
23
24
  }) => EffectStatus;
24
25
  export declare const getPropStatusesCtx: (propStatuses: PropStatuses, nodePath: SequencePropsSubscriptionKey) => Record<string, CanUpdateSequencePropStatus> | undefined;
25
26
  export type GetPropStatusesType = typeof getPropStatusesCtx;
26
- export declare const useMemoizedEffects: ({ effects, overrideId, }: {
27
+ export declare const useMemoizedEffects: ({ effects, overrideId, videoConfigValues, }: {
27
28
  effects: readonly EffectDescriptor<unknown>[];
28
29
  readonly overrideId: string | null;
30
+ videoConfigValues: VideoConfigValues | null;
29
31
  }) => EffectDefinitionAndStack<unknown>[];
30
32
  export {};
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useMemoizedEffects = exports.getPropStatusesCtx = exports.getEffectPropStatusesCtx = exports.useMemoizedEffectDefinitions = void 0;
4
4
  const react_1 = require("react");
5
+ const evaluate_source_expressions_js_1 = require("../evaluate-source-expressions.js");
5
6
  const get_effective_visual_mode_value_js_1 = require("../get-effective-visual-mode-value.js");
6
7
  const interpolate_keyframed_status_js_1 = require("../interpolate-keyframed-status.js");
7
8
  const runtime_value_store_js_1 = require("../runtime-value-store.js");
@@ -106,7 +107,10 @@ const getEffectPropStatusesCtx = ({ propStatuses, nodePath, effectIndex, }) => {
106
107
  if (!effect.canUpdate) {
107
108
  return { type: 'cannot-update-effect', reason: effect.reason };
108
109
  }
109
- return { type: 'can-update-effect', props: effect.props };
110
+ return {
111
+ type: 'can-update-effect',
112
+ props: (0, evaluate_source_expressions_js_1.evaluateSourcePropStatuses)(effect.props, nodePath.videoConfigValues),
113
+ };
110
114
  };
111
115
  exports.getEffectPropStatusesCtx = getEffectPropStatusesCtx;
112
116
  const getPropStatusesCtx = (propStatuses, nodePath) => {
@@ -117,10 +121,10 @@ const getPropStatusesCtx = (propStatuses, nodePath) => {
117
121
  if (!status.canUpdate) {
118
122
  return undefined;
119
123
  }
120
- return status.props;
124
+ return (0, evaluate_source_expressions_js_1.evaluateSourcePropStatuses)(status.props, nodePath.videoConfigValues);
121
125
  };
122
126
  exports.getPropStatusesCtx = getPropStatusesCtx;
123
- const useMemoizedEffects = ({ effects, overrideId, }) => {
127
+ const useMemoizedEffects = ({ effects, overrideId, videoConfigValues, }) => {
124
128
  var _a;
125
129
  const previousRef = (0, react_1.useRef)(null);
126
130
  const { propStatuses } = (0, react_1.useContext)(SequenceManager_js_1.VisualModePropStatusesContext);
@@ -142,7 +146,7 @@ const useMemoizedEffects = ({ effects, overrideId, }) => {
142
146
  }
143
147
  const effectStatus = (0, exports.getEffectPropStatusesCtx)({
144
148
  propStatuses,
145
- nodePath,
149
+ nodePath: { ...nodePath, videoConfigValues },
146
150
  effectIndex: index,
147
151
  });
148
152
  const propStatusOverrides = effectStatus.type === 'can-update-effect'
@@ -0,0 +1,5 @@
1
+ import type { CanUpdateSequencePropSource, CanUpdateSequencePropStatus, SourceNumericValue } from './use-schema.js';
2
+ import type { VideoConfigValues } from './video-config.js';
3
+ export declare const evaluateSourceNumericValue: (expression: SourceNumericValue, config: VideoConfigValues | null) => number;
4
+ /** Resolve at the consumer, never in the shared source subscription cache. */
5
+ export declare const evaluateSourcePropStatuses: (props: Record<string, CanUpdateSequencePropSource>, config: VideoConfigValues | null) => Record<string, CanUpdateSequencePropStatus>;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evaluateSourcePropStatuses = exports.evaluateSourceNumericValue = void 0;
4
+ const evaluateSourceNumericValue = (expression, config) => {
5
+ var _a;
6
+ if (typeof expression === 'number') {
7
+ return expression;
8
+ }
9
+ if (expression.type === 'literal') {
10
+ return expression.value;
11
+ }
12
+ if (expression.type === 'binary') {
13
+ const left = (0, exports.evaluateSourceNumericValue)(expression.left, config);
14
+ const right = (0, exports.evaluateSourceNumericValue)(expression.right, config);
15
+ switch (expression.operator) {
16
+ case '+':
17
+ return left + right;
18
+ case '-':
19
+ return left - right;
20
+ case '*':
21
+ return left * right;
22
+ case '/':
23
+ return left / right;
24
+ default:
25
+ throw new Error('Unknown source expression operator');
26
+ }
27
+ }
28
+ const value = expression.binding.type === 'constant'
29
+ ? expression.binding.value
30
+ : ((_a = config === null || config === void 0 ? void 0 : config[expression.binding.field]) !== null && _a !== void 0 ? _a : NaN);
31
+ if (expression.type === 'video-config-multiplication') {
32
+ return value * expression.multiplier;
33
+ }
34
+ if (expression.type === 'video-config-subtraction') {
35
+ return value - expression.subtrahend;
36
+ }
37
+ return value;
38
+ };
39
+ exports.evaluateSourceNumericValue = evaluateSourceNumericValue;
40
+ const evaluatedProps = new WeakMap();
41
+ /** Resolve at the consumer, never in the shared source subscription cache. */
42
+ const evaluateSourcePropStatuses = (props, config) => {
43
+ var _a;
44
+ const configKey = config === null
45
+ ? ''
46
+ : `${config.durationInFrames}:${config.fps}:${config.width}:${config.height}`;
47
+ const cached = (_a = evaluatedProps.get(props)) !== null && _a !== void 0 ? _a : new Map();
48
+ const existing = cached.get(configKey);
49
+ if (existing)
50
+ return existing;
51
+ const result = Object.fromEntries(Object.entries(props).map(([key, status]) => {
52
+ var _a;
53
+ if (status.status === 'computed') {
54
+ return [key, status];
55
+ }
56
+ const adjustment = status.keyframeDisplayOffsetAdjustment === null
57
+ ? null
58
+ : (0, exports.evaluateSourceNumericValue)(status.keyframeDisplayOffsetAdjustment, config);
59
+ const rate = (0, exports.evaluateSourceNumericValue)((_a = status.keyframePlaybackRateAdjustment) !== null && _a !== void 0 ? _a : 1, config);
60
+ if ((adjustment !== null && !Number.isFinite(adjustment)) ||
61
+ !Number.isFinite(rate) ||
62
+ rate <= 0) {
63
+ return [key, { status: 'computed' }];
64
+ }
65
+ const timing = {
66
+ keyframeDisplayOffsetAdjustment: adjustment,
67
+ ...(status.keyframePlaybackRateAdjustment === undefined
68
+ ? {}
69
+ : { keyframePlaybackRateAdjustment: rate }),
70
+ };
71
+ if (status.status === 'static') {
72
+ const value = status.numericExpression === undefined
73
+ ? status.codeValue
74
+ : (0, exports.evaluateSourceNumericValue)(status.numericExpression, config);
75
+ return [
76
+ key,
77
+ status.numericExpression !== undefined && !Number.isFinite(value)
78
+ ? { status: 'computed' }
79
+ : { ...status, ...timing, codeValue: value },
80
+ ];
81
+ }
82
+ const keyframes = status.keyframes.map((keyframe) => ({
83
+ ...keyframe,
84
+ frame: (0, exports.evaluateSourceNumericValue)(keyframe.frame, config),
85
+ }));
86
+ if (keyframes.some((keyframe, index) => !Number.isFinite(keyframe.frame) ||
87
+ (index > 0 && keyframe.frame <= keyframes[index - 1].frame))) {
88
+ return [key, { status: 'computed' }];
89
+ }
90
+ return [key, { ...status, ...timing, keyframes }];
91
+ }));
92
+ cached.set(configKey, result);
93
+ if (cached.size > 8)
94
+ cached.delete(cached.keys().next().value);
95
+ evaluatedProps.set(props, cached);
96
+ return result;
97
+ };
98
+ exports.evaluateSourcePropStatuses = evaluateSourcePropStatuses;