@remotion/transitions 4.0.413 → 4.0.415

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.
@@ -1,8 +1,9 @@
1
1
  import type { FC, PropsWithChildren } from 'react';
2
2
  import type { AbsoluteFillLayout, LayoutAndStyle, SequencePropsWithoutDuration } from 'remotion';
3
3
  import { NoReactInternals } from 'remotion/no-react';
4
- import type { TransitionSeriesTransitionProps } from './types.js';
4
+ import type { TransitionSeriesOverlayProps, TransitionSeriesTransitionProps } from './types.js';
5
5
  declare const TransitionSeriesTransition: <PresentationProps extends Record<string, unknown>>(_props: TransitionSeriesTransitionProps<PresentationProps>) => null;
6
+ declare const SeriesOverlay: FC<TransitionSeriesOverlayProps>;
6
7
  type LayoutBasedProps = true extends typeof NoReactInternals.ENABLE_V5_BREAKING_CHANGES ? AbsoluteFillLayout : LayoutAndStyle;
7
8
  type SeriesSequenceProps = PropsWithChildren<{
8
9
  readonly durationInFrames: number;
@@ -17,5 +18,6 @@ declare const SeriesSequence: ({ children }: SeriesSequenceProps) => import("rea
17
18
  export declare const TransitionSeries: FC<SequencePropsWithoutDuration> & {
18
19
  Sequence: typeof SeriesSequence;
19
20
  Transition: typeof TransitionSeriesTransition;
21
+ Overlay: typeof SeriesOverlay;
20
22
  };
21
23
  export {};
@@ -9,9 +9,10 @@ const context_js_1 = require("./context.js");
9
9
  const flatten_children_js_1 = require("./flatten-children.js");
10
10
  const slide_js_1 = require("./presentations/slide.js");
11
11
  const validate_js_1 = require("./validate.js");
12
- const TransitionSeriesTransition = function (
13
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
14
- _props) {
12
+ const TransitionSeriesTransition = function (_props) {
13
+ return null;
14
+ };
15
+ const SeriesOverlay = () => {
15
16
  return null;
16
17
  };
17
18
  const SeriesSequence = ({ children }) => {
@@ -25,8 +26,13 @@ const TransitionSeriesChildren = ({ children, }) => {
25
26
  let transitionOffsets = 0;
26
27
  let startFrame = 0;
27
28
  const flattedChildren = (0, flatten_children_js_1.flattenChildren)(children);
28
- return react_1.Children.map(flattedChildren, (child, i) => {
29
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
29
+ // Collect overlay render info to emit after the main loop
30
+ const overlayRenders = [];
31
+ // Track sequence durations for overlay validation
32
+ const sequenceDurations = [];
33
+ let pendingOverlayValidation = false;
34
+ const mainChildren = react_1.Children.map(flattedChildren, (child, i) => {
35
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
30
36
  const current = child;
31
37
  if (typeof current === 'string') {
32
38
  // Don't throw if it's just some accidential whitespace
@@ -37,28 +43,94 @@ const TransitionSeriesChildren = ({ children, }) => {
37
43
  }
38
44
  const hasPrev = flattedChildren[i - 1];
39
45
  const nextPrev = flattedChildren[i + 1];
40
- const prev = typeof hasPrev === 'string' || typeof hasPrev === 'undefined'
41
- ? null
42
- : hasPrev.type === TransitionSeriesTransition
43
- ? hasPrev
44
- : null;
45
- const next = typeof nextPrev === 'string' || typeof nextPrev === 'undefined'
46
- ? null
47
- : nextPrev.type === TransitionSeriesTransition
48
- ? nextPrev
49
- : null;
50
46
  const prevIsTransition = typeof hasPrev === 'string' || typeof hasPrev === 'undefined'
51
47
  ? false
52
48
  : hasPrev.type === TransitionSeriesTransition;
49
+ const prevIsOverlay = typeof hasPrev === 'string' || typeof hasPrev === 'undefined'
50
+ ? false
51
+ : hasPrev.type === SeriesOverlay;
52
+ // Handle overlay
53
+ if (current.type === SeriesOverlay) {
54
+ // Validate: two overlays in a row
55
+ if (prevIsOverlay) {
56
+ throw new TypeError(`A <TransitionSeries.Overlay /> component must not be followed by another <TransitionSeries.Overlay /> component (nth children = ${i - 1} and ${i})`);
57
+ }
58
+ // Validate: overlay next to transition
59
+ if (prevIsTransition) {
60
+ throw new TypeError(`A <TransitionSeries.Transition /> component must not be followed by a <TransitionSeries.Overlay /> component (nth children = ${i - 1} and ${i})`);
61
+ }
62
+ const nextIsTransition = typeof nextPrev === 'string' || typeof nextPrev === 'undefined'
63
+ ? false
64
+ : nextPrev.type === TransitionSeriesTransition;
65
+ if (nextIsTransition) {
66
+ throw new TypeError(`A <TransitionSeries.Overlay /> component must not be followed by a <TransitionSeries.Transition /> component (nth children = ${i} and ${i + 1})`);
67
+ }
68
+ const overlayProps = current.props;
69
+ (0, validate_js_1.validateDurationInFrames)(overlayProps.durationInFrames, {
70
+ component: `of a <TransitionSeries.Overlay /> component`,
71
+ allowFloats: false,
72
+ });
73
+ const overlayOffset = (_a = overlayProps.offset) !== null && _a !== void 0 ? _a : 0;
74
+ if (Number.isNaN(overlayOffset)) {
75
+ throw new TypeError(`The "offset" property of a <TransitionSeries.Overlay /> must not be NaN, but got NaN.`);
76
+ }
77
+ if (!Number.isFinite(overlayOffset)) {
78
+ throw new TypeError(`The "offset" property of a <TransitionSeries.Overlay /> must be finite, but got ${overlayOffset}.`);
79
+ }
80
+ if (overlayOffset % 1 !== 0) {
81
+ throw new TypeError(`The "offset" property of a <TransitionSeries.Overlay /> must be an integer, but got ${overlayOffset}.`);
82
+ }
83
+ // Find the previous sequence (the cut point is at startFrame + transitionOffsets)
84
+ const cutPoint = startFrame + transitionOffsets;
85
+ const halfDuration = overlayProps.durationInFrames / 2;
86
+ const overlayFrom = cutPoint - halfDuration + overlayOffset;
87
+ if (overlayFrom < 0) {
88
+ throw new TypeError(`A <TransitionSeries.Overlay /> extends before frame 0. The overlay starts at frame ${overlayFrom}. Reduce the duration or adjust the offset.`);
89
+ }
90
+ // Validate: overlay must not exceed the previous sequence duration
91
+ const prevSeqIdx = sequenceDurations.length - 1;
92
+ if (prevSeqIdx >= 0) {
93
+ const overlayStartInPrev = halfDuration - overlayOffset;
94
+ if (overlayStartInPrev > sequenceDurations[prevSeqIdx]) {
95
+ throw new TypeError(`A <TransitionSeries.Overlay /> extends beyond the previous sequence. The overlay needs ${overlayStartInPrev} frames before the cut, but the previous sequence is only ${sequenceDurations[prevSeqIdx]} frames long.`);
96
+ }
97
+ }
98
+ // We'll validate the next sequence side after we process it
99
+ pendingOverlayValidation = true;
100
+ // Store overlay info for deferred rendering
101
+ overlayRenders.push({
102
+ cutPoint,
103
+ overlayFrom,
104
+ durationInFrames: overlayProps.durationInFrames,
105
+ overlayOffset,
106
+ halfDuration,
107
+ children: overlayProps.children,
108
+ index: i,
109
+ });
110
+ return null;
111
+ }
53
112
  if (current.type === TransitionSeriesTransition) {
54
113
  if (prevIsTransition) {
55
114
  throw new TypeError(`A <TransitionSeries.Transition /> component must not be followed by another <TransitionSeries.Transition /> component (nth children = ${i - 1} and ${i})`);
56
115
  }
116
+ if (prevIsOverlay) {
117
+ throw new TypeError(`A <TransitionSeries.Overlay /> component must not be followed by a <TransitionSeries.Transition /> component (nth children = ${i - 1} and ${i})`);
118
+ }
57
119
  return null;
58
120
  }
59
121
  if (current.type !== SeriesSequence) {
60
- throw new TypeError(`The <TransitionSeries /> component only accepts a list of <TransitionSeries.Sequence /> and <TransitionSeries.Transition /> components as its children, but got ${current} instead`);
122
+ throw new TypeError(`The <TransitionSeries /> component only accepts a list of <TransitionSeries.Sequence />, <TransitionSeries.Transition />, and <TransitionSeries.Overlay /> components as its children, but got ${current} instead`);
61
123
  }
124
+ const prev = typeof hasPrev === 'string' || typeof hasPrev === 'undefined'
125
+ ? null
126
+ : hasPrev.type === TransitionSeriesTransition
127
+ ? hasPrev
128
+ : null;
129
+ const next = typeof nextPrev === 'string' || typeof nextPrev === 'undefined'
130
+ ? null
131
+ : nextPrev.type === TransitionSeriesTransition
132
+ ? nextPrev
133
+ : null;
62
134
  const castedChildAgain = current;
63
135
  const debugInfo = `index = ${i}, duration = ${castedChildAgain.props.durationInFrames}`;
64
136
  if (!(castedChildAgain === null || castedChildAgain === void 0 ? void 0 : castedChildAgain.props.children)) {
@@ -70,7 +142,7 @@ const TransitionSeriesChildren = ({ children, }) => {
70
142
  component: `of a <TransitionSeries.Sequence /> component`,
71
143
  allowFloats: true,
72
144
  });
73
- const offset = (_a = castedChildAgain.props.offset) !== null && _a !== void 0 ? _a : 0;
145
+ const offset = (_b = castedChildAgain.props.offset) !== null && _b !== void 0 ? _b : 0;
74
146
  if (Number.isNaN(offset)) {
75
147
  throw new TypeError(`The "offset" property of a <TransitionSeries.Sequence /> must not be NaN, but got NaN (${debugInfo}).`);
76
148
  }
@@ -95,6 +167,17 @@ const TransitionSeriesChildren = ({ children, }) => {
95
167
  startFrame -= actualStartFrame;
96
168
  actualStartFrame = 0;
97
169
  }
170
+ // Track sequence durations for overlay validation
171
+ sequenceDurations.push(durationInFramesProp);
172
+ // Validate: check if a preceding overlay extends beyond this sequence
173
+ if (pendingOverlayValidation) {
174
+ pendingOverlayValidation = false;
175
+ const lastOverlay = overlayRenders[overlayRenders.length - 1];
176
+ const framesAfterCut = lastOverlay.halfDuration + lastOverlay.overlayOffset;
177
+ if (framesAfterCut > durationInFramesProp) {
178
+ throw new TypeError(`A <TransitionSeries.Overlay /> extends beyond the next sequence. The overlay needs ${framesAfterCut} frames after the cut, but the next sequence is only ${durationInFramesProp} frames long.`);
179
+ }
180
+ }
98
181
  const nextProgress = next
99
182
  ? next.props.timing.getProgress({
100
183
  frame: frame -
@@ -119,32 +202,38 @@ const TransitionSeriesChildren = ({ children, }) => {
119
202
  throw new Error(`The duration of a <TransitionSeries.Sequence /> must not be shorter than the duration of the previous <TransitionSeries.Transition />. The transition is ${prev.props.timing.getDurationInFrames({ fps })} frames long, but the sequence is only ${durationInFramesProp} frames long (${debugInfo})`);
120
203
  }
121
204
  if (next && prev && nextProgress !== null && prevProgress !== null) {
122
- const nextPresentation = (_b = next.props.presentation) !== null && _b !== void 0 ? _b : (0, slide_js_1.slide)();
123
- const prevPresentation = (_c = prev.props.presentation) !== null && _c !== void 0 ? _c : (0, slide_js_1.slide)();
205
+ const nextPresentation = (_c = next.props.presentation) !== null && _c !== void 0 ? _c : (0, slide_js_1.slide)();
206
+ const prevPresentation = (_d = prev.props.presentation) !== null && _d !== void 0 ? _d : (0, slide_js_1.slide)();
124
207
  const UppercaseNextPresentation = nextPresentation.component;
125
208
  const UppercasePrevPresentation = prevPresentation.component;
126
209
  return ((0, jsx_runtime_1.jsx)(remotion_1.Sequence
127
210
  // eslint-disable-next-line react/no-array-index-key
128
- , { from: actualStartFrame, durationInFrames: durationInFramesProp, ...passedProps, name: passedProps.name || '<TS.Sequence>', children: (0, jsx_runtime_1.jsx)(UppercaseNextPresentation, { passedProps: (_d = nextPresentation.props) !== null && _d !== void 0 ? _d : {}, presentationDirection: "exiting", presentationProgress: nextProgress, presentationDurationInFrames: next.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInExitingProgressContext, { presentationProgress: nextProgress, children: (0, jsx_runtime_1.jsx)(UppercasePrevPresentation, { passedProps: (_e = prevPresentation.props) !== null && _e !== void 0 ? _e : {}, presentationDirection: "entering", presentationProgress: prevProgress, presentationDurationInFrames: prev.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInEnteringProgressContext, { presentationProgress: prevProgress, children: child }) }) }) }) }, i));
211
+ , { from: actualStartFrame, durationInFrames: durationInFramesProp, ...passedProps, name: passedProps.name || '<TS.Sequence>', children: (0, jsx_runtime_1.jsx)(UppercaseNextPresentation, { passedProps: (_e = nextPresentation.props) !== null && _e !== void 0 ? _e : {}, presentationDirection: "exiting", presentationProgress: nextProgress, presentationDurationInFrames: next.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInExitingProgressContext, { presentationProgress: nextProgress, children: (0, jsx_runtime_1.jsx)(UppercasePrevPresentation, { passedProps: (_f = prevPresentation.props) !== null && _f !== void 0 ? _f : {}, presentationDirection: "entering", presentationProgress: prevProgress, presentationDurationInFrames: prev.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInEnteringProgressContext, { presentationProgress: prevProgress, children: child }) }) }) }) }, i));
129
212
  }
130
213
  if (prevProgress !== null && prev) {
131
- const prevPresentation = (_f = prev.props.presentation) !== null && _f !== void 0 ? _f : (0, slide_js_1.slide)();
214
+ const prevPresentation = (_g = prev.props.presentation) !== null && _g !== void 0 ? _g : (0, slide_js_1.slide)();
132
215
  const UppercasePrevPresentation = prevPresentation.component;
133
216
  return ((0, jsx_runtime_1.jsx)(remotion_1.Sequence
134
217
  // eslint-disable-next-line react/no-array-index-key
135
- , { from: actualStartFrame, durationInFrames: durationInFramesProp, ...passedProps, name: passedProps.name || '<TS.Sequence>', children: (0, jsx_runtime_1.jsx)(UppercasePrevPresentation, { passedProps: (_g = prevPresentation.props) !== null && _g !== void 0 ? _g : {}, presentationDirection: "entering", presentationProgress: prevProgress, presentationDurationInFrames: prev.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInEnteringProgressContext, { presentationProgress: prevProgress, children: child }) }) }, i));
218
+ , { from: actualStartFrame, durationInFrames: durationInFramesProp, ...passedProps, name: passedProps.name || '<TS.Sequence>', children: (0, jsx_runtime_1.jsx)(UppercasePrevPresentation, { passedProps: (_h = prevPresentation.props) !== null && _h !== void 0 ? _h : {}, presentationDirection: "entering", presentationProgress: prevProgress, presentationDurationInFrames: prev.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInEnteringProgressContext, { presentationProgress: prevProgress, children: child }) }) }, i));
136
219
  }
137
220
  if (nextProgress !== null && next) {
138
- const nextPresentation = (_h = next.props.presentation) !== null && _h !== void 0 ? _h : (0, slide_js_1.slide)();
221
+ const nextPresentation = (_j = next.props.presentation) !== null && _j !== void 0 ? _j : (0, slide_js_1.slide)();
139
222
  const UppercaseNextPresentation = nextPresentation.component;
140
223
  return ((0, jsx_runtime_1.jsx)(remotion_1.Sequence
141
224
  // eslint-disable-next-line react/no-array-index-key
142
- , { from: actualStartFrame, durationInFrames: durationInFramesProp, ...passedProps, name: passedProps.name || '<TS.Sequence>', children: (0, jsx_runtime_1.jsx)(UppercaseNextPresentation, { passedProps: (_j = nextPresentation.props) !== null && _j !== void 0 ? _j : {}, presentationDirection: "exiting", presentationProgress: nextProgress, presentationDurationInFrames: next.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInExitingProgressContext, { presentationProgress: nextProgress, children: child }) }) }, i));
225
+ , { from: actualStartFrame, durationInFrames: durationInFramesProp, ...passedProps, name: passedProps.name || '<TS.Sequence>', children: (0, jsx_runtime_1.jsx)(UppercaseNextPresentation, { passedProps: (_k = nextPresentation.props) !== null && _k !== void 0 ? _k : {}, presentationDirection: "exiting", presentationProgress: nextProgress, presentationDurationInFrames: next.props.timing.getDurationInFrames({ fps }), children: (0, jsx_runtime_1.jsx)(context_js_1.WrapInExitingProgressContext, { presentationProgress: nextProgress, children: child }) }) }, i));
143
226
  }
144
227
  return ((0, jsx_runtime_1.jsx)(remotion_1.Sequence
145
228
  // eslint-disable-next-line react/no-array-index-key
146
229
  , { from: actualStartFrame, durationInFrames: durationInFramesProp, ...passedProps, name: passedProps.name || '<TS.Sequence>', children: child }, i));
147
230
  });
231
+ // Now render overlay sequences
232
+ const overlayElements = overlayRenders.map((overlayInfo) => {
233
+ const info = overlayInfo;
234
+ return ((0, jsx_runtime_1.jsx)(remotion_1.Sequence, { from: Math.round(info.overlayFrom), durationInFrames: info.durationInFrames, name: "<TS.Overlay>", layout: "absolute-fill", children: info.children }, `overlay-${info.index}`));
235
+ });
236
+ return [...(mainChildren || []), ...overlayElements];
148
237
  }, [children, fps, frame]);
149
238
  // eslint-disable-next-line react/jsx-no-useless-fragment
150
239
  return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: childrenValue });
@@ -165,5 +254,7 @@ const TransitionSeries = ({ children, name, layout: passedLayout, ...otherProps
165
254
  exports.TransitionSeries = TransitionSeries;
166
255
  exports.TransitionSeries.Sequence = SeriesSequence;
167
256
  exports.TransitionSeries.Transition = TransitionSeriesTransition;
257
+ exports.TransitionSeries.Overlay = SeriesOverlay;
168
258
  remotion_1.Internals.addSequenceStackTraces(exports.TransitionSeries);
169
259
  remotion_1.Internals.addSequenceStackTraces(SeriesSequence);
260
+ remotion_1.Internals.addSequenceStackTraces(SeriesOverlay);
@@ -177,6 +177,9 @@ import { jsx as jsx3, Fragment } from "react/jsx-runtime";
177
177
  var TransitionSeriesTransition = function(_props) {
178
178
  return null;
179
179
  };
180
+ var SeriesOverlay = () => {
181
+ return null;
182
+ };
180
183
  var SeriesSequence = ({ children }) => {
181
184
  return /* @__PURE__ */ jsx3(Fragment, {
182
185
  children
@@ -191,7 +194,10 @@ var TransitionSeriesChildren = ({
191
194
  let transitionOffsets = 0;
192
195
  let startFrame = 0;
193
196
  const flattedChildren = flattenChildren(children);
194
- return Children.map(flattedChildren, (child, i) => {
197
+ const overlayRenders = [];
198
+ const sequenceDurations = [];
199
+ let pendingOverlayValidation = false;
200
+ const mainChildren = Children.map(flattedChildren, (child, i) => {
195
201
  const current = child;
196
202
  if (typeof current === "string") {
197
203
  if (current.trim() === "") {
@@ -201,18 +207,73 @@ var TransitionSeriesChildren = ({
201
207
  }
202
208
  const hasPrev = flattedChildren[i - 1];
203
209
  const nextPrev = flattedChildren[i + 1];
204
- const prev = typeof hasPrev === "string" || typeof hasPrev === "undefined" ? null : hasPrev.type === TransitionSeriesTransition ? hasPrev : null;
205
- const next = typeof nextPrev === "string" || typeof nextPrev === "undefined" ? null : nextPrev.type === TransitionSeriesTransition ? nextPrev : null;
206
210
  const prevIsTransition = typeof hasPrev === "string" || typeof hasPrev === "undefined" ? false : hasPrev.type === TransitionSeriesTransition;
211
+ const prevIsOverlay = typeof hasPrev === "string" || typeof hasPrev === "undefined" ? false : hasPrev.type === SeriesOverlay;
212
+ if (current.type === SeriesOverlay) {
213
+ if (prevIsOverlay) {
214
+ throw new TypeError(`A <TransitionSeries.Overlay /> component must not be followed by another <TransitionSeries.Overlay /> component (nth children = ${i - 1} and ${i})`);
215
+ }
216
+ if (prevIsTransition) {
217
+ throw new TypeError(`A <TransitionSeries.Transition /> component must not be followed by a <TransitionSeries.Overlay /> component (nth children = ${i - 1} and ${i})`);
218
+ }
219
+ const nextIsTransition = typeof nextPrev === "string" || typeof nextPrev === "undefined" ? false : nextPrev.type === TransitionSeriesTransition;
220
+ if (nextIsTransition) {
221
+ throw new TypeError(`A <TransitionSeries.Overlay /> component must not be followed by a <TransitionSeries.Transition /> component (nth children = ${i} and ${i + 1})`);
222
+ }
223
+ const overlayProps = current.props;
224
+ validateDurationInFrames(overlayProps.durationInFrames, {
225
+ component: `of a <TransitionSeries.Overlay /> component`,
226
+ allowFloats: false
227
+ });
228
+ const overlayOffset = overlayProps.offset ?? 0;
229
+ if (Number.isNaN(overlayOffset)) {
230
+ throw new TypeError(`The "offset" property of a <TransitionSeries.Overlay /> must not be NaN, but got NaN.`);
231
+ }
232
+ if (!Number.isFinite(overlayOffset)) {
233
+ throw new TypeError(`The "offset" property of a <TransitionSeries.Overlay /> must be finite, but got ${overlayOffset}.`);
234
+ }
235
+ if (overlayOffset % 1 !== 0) {
236
+ throw new TypeError(`The "offset" property of a <TransitionSeries.Overlay /> must be an integer, but got ${overlayOffset}.`);
237
+ }
238
+ const cutPoint = startFrame + transitionOffsets;
239
+ const halfDuration = overlayProps.durationInFrames / 2;
240
+ const overlayFrom = cutPoint - halfDuration + overlayOffset;
241
+ if (overlayFrom < 0) {
242
+ throw new TypeError(`A <TransitionSeries.Overlay /> extends before frame 0. The overlay starts at frame ${overlayFrom}. Reduce the duration or adjust the offset.`);
243
+ }
244
+ const prevSeqIdx = sequenceDurations.length - 1;
245
+ if (prevSeqIdx >= 0) {
246
+ const overlayStartInPrev = halfDuration - overlayOffset;
247
+ if (overlayStartInPrev > sequenceDurations[prevSeqIdx]) {
248
+ throw new TypeError(`A <TransitionSeries.Overlay /> extends beyond the previous sequence. The overlay needs ${overlayStartInPrev} frames before the cut, but the previous sequence is only ${sequenceDurations[prevSeqIdx]} frames long.`);
249
+ }
250
+ }
251
+ pendingOverlayValidation = true;
252
+ overlayRenders.push({
253
+ cutPoint,
254
+ overlayFrom,
255
+ durationInFrames: overlayProps.durationInFrames,
256
+ overlayOffset,
257
+ halfDuration,
258
+ children: overlayProps.children,
259
+ index: i
260
+ });
261
+ return null;
262
+ }
207
263
  if (current.type === TransitionSeriesTransition) {
208
264
  if (prevIsTransition) {
209
265
  throw new TypeError(`A <TransitionSeries.Transition /> component must not be followed by another <TransitionSeries.Transition /> component (nth children = ${i - 1} and ${i})`);
210
266
  }
267
+ if (prevIsOverlay) {
268
+ throw new TypeError(`A <TransitionSeries.Overlay /> component must not be followed by a <TransitionSeries.Transition /> component (nth children = ${i - 1} and ${i})`);
269
+ }
211
270
  return null;
212
271
  }
213
272
  if (current.type !== SeriesSequence) {
214
- throw new TypeError(`The <TransitionSeries /> component only accepts a list of <TransitionSeries.Sequence /> and <TransitionSeries.Transition /> components as its children, but got ${current} instead`);
273
+ throw new TypeError(`The <TransitionSeries /> component only accepts a list of <TransitionSeries.Sequence />, <TransitionSeries.Transition />, and <TransitionSeries.Overlay /> components as its children, but got ${current} instead`);
215
274
  }
275
+ const prev = typeof hasPrev === "string" || typeof hasPrev === "undefined" ? null : hasPrev.type === TransitionSeriesTransition ? hasPrev : null;
276
+ const next = typeof nextPrev === "string" || typeof nextPrev === "undefined" ? null : nextPrev.type === TransitionSeriesTransition ? nextPrev : null;
216
277
  const castedChildAgain = current;
217
278
  const debugInfo = `index = ${i}, duration = ${castedChildAgain.props.durationInFrames}`;
218
279
  if (!castedChildAgain?.props.children) {
@@ -252,6 +313,15 @@ var TransitionSeriesChildren = ({
252
313
  startFrame -= actualStartFrame;
253
314
  actualStartFrame = 0;
254
315
  }
316
+ sequenceDurations.push(durationInFramesProp);
317
+ if (pendingOverlayValidation) {
318
+ pendingOverlayValidation = false;
319
+ const lastOverlay = overlayRenders[overlayRenders.length - 1];
320
+ const framesAfterCut = lastOverlay.halfDuration + lastOverlay.overlayOffset;
321
+ if (framesAfterCut > durationInFramesProp) {
322
+ throw new TypeError(`A <TransitionSeries.Overlay /> extends beyond the next sequence. The overlay needs ${framesAfterCut} frames after the cut, but the next sequence is only ${durationInFramesProp} frames long.`);
323
+ }
324
+ }
255
325
  const nextProgress = next ? next.props.timing.getProgress({
256
326
  frame: frame - actualStartFrame - durationInFrames + next.props.timing.getDurationInFrames({ fps }),
257
327
  fps
@@ -345,6 +415,17 @@ var TransitionSeriesChildren = ({
345
415
  children: child
346
416
  }, i);
347
417
  });
418
+ const overlayElements = overlayRenders.map((overlayInfo) => {
419
+ const info = overlayInfo;
420
+ return /* @__PURE__ */ jsx3(Sequence, {
421
+ from: Math.round(info.overlayFrom),
422
+ durationInFrames: info.durationInFrames,
423
+ name: "<TS.Overlay>",
424
+ layout: "absolute-fill",
425
+ children: info.children
426
+ }, `overlay-${info.index}`);
427
+ });
428
+ return [...mainChildren || [], ...overlayElements];
348
429
  }, [children, fps, frame]);
349
430
  return /* @__PURE__ */ jsx3(Fragment, {
350
431
  children: childrenValue
@@ -367,8 +448,10 @@ var TransitionSeries = ({ children, name, layout: passedLayout, ...otherProps })
367
448
  };
368
449
  TransitionSeries.Sequence = SeriesSequence;
369
450
  TransitionSeries.Transition = TransitionSeriesTransition;
451
+ TransitionSeries.Overlay = SeriesOverlay;
370
452
  Internals.addSequenceStackTraces(TransitionSeries);
371
453
  Internals.addSequenceStackTraces(SeriesSequence);
454
+ Internals.addSequenceStackTraces(SeriesOverlay);
372
455
  // src/use-transition-progress.ts
373
456
  import React4 from "react";
374
457
  var useTransitionProgress = () => {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { linearTiming } from './timings/linear-timing.js';
2
2
  export { springTiming } from './timings/spring-timing.js';
3
3
  export { TransitionSeries } from './TransitionSeries.js';
4
- export { TransitionPresentation, TransitionPresentationComponentProps, TransitionTiming, } from './types.js';
4
+ export { TransitionPresentation, TransitionPresentationComponentProps, TransitionSeriesOverlayProps, TransitionTiming, } from './types.js';
5
5
  export { TransitionState, useTransitionProgress, } from './use-transition-progress.js';
package/dist/types.d.ts CHANGED
@@ -25,4 +25,9 @@ export type TransitionPresentationComponentProps<PresentationProps extends Recor
25
25
  passedProps: PresentationProps;
26
26
  presentationDurationInFrames: number;
27
27
  };
28
+ export type TransitionSeriesOverlayProps = {
29
+ readonly durationInFrames: number;
30
+ readonly offset?: number;
31
+ readonly children: React.ReactNode;
32
+ };
28
33
  export {};
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/transitions"
4
4
  },
5
5
  "name": "@remotion/transitions",
6
- "version": "4.0.413",
6
+ "version": "4.0.415",
7
7
  "description": "Library for creating transitions in Remotion",
8
8
  "sideEffects": false,
9
9
  "main": "dist/esm/index.mjs",
@@ -22,18 +22,18 @@
22
22
  "url": "https://github.com/remotion-dev/remotion/issues"
23
23
  },
24
24
  "dependencies": {
25
- "remotion": "4.0.413",
26
- "@remotion/shapes": "4.0.413",
27
- "@remotion/paths": "4.0.413"
25
+ "remotion": "4.0.415",
26
+ "@remotion/shapes": "4.0.415",
27
+ "@remotion/paths": "4.0.415"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@happy-dom/global-registrator": "14.5.1",
31
- "remotion": "4.0.413",
31
+ "remotion": "4.0.415",
32
32
  "react": "19.2.3",
33
33
  "react-dom": "19.2.3",
34
- "@remotion/test-utils": "4.0.413",
35
- "@remotion/player": "4.0.413",
36
- "@remotion/eslint-config-internal": "4.0.413",
34
+ "@remotion/test-utils": "4.0.415",
35
+ "@remotion/player": "4.0.415",
36
+ "@remotion/eslint-config-internal": "4.0.415",
37
37
  "eslint": "9.19.0"
38
38
  },
39
39
  "peerDependencies": {