remotion 4.0.526 → 4.0.528

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 (66) hide show
  1. package/dist/cjs/AbsoluteFill.d.ts +29 -3
  2. package/dist/cjs/AbsoluteFill.js +19 -2
  3. package/dist/cjs/CompositionManager.d.ts +2 -0
  4. package/dist/cjs/HtmlInCanvas.d.ts +28 -2
  5. package/dist/cjs/HtmlInCanvas.js +17 -3
  6. package/dist/cjs/Img.d.ts +2 -2
  7. package/dist/cjs/Img.js +1 -1
  8. package/dist/cjs/Interactive.d.ts +20 -2
  9. package/dist/cjs/Interactive.js +30 -8
  10. package/dist/cjs/Sequence.d.ts +1 -0
  11. package/dist/cjs/Sequence.js +49 -18
  12. package/dist/cjs/SequenceContext.d.ts +5 -0
  13. package/dist/cjs/TimelineContext.d.ts +2 -0
  14. package/dist/cjs/TimelineContext.js +8 -1
  15. package/dist/cjs/animated-image/AnimatedImage.d.ts +9 -10
  16. package/dist/cjs/animated-image/AnimatedImage.js +1 -12
  17. package/dist/cjs/audio/AudioForPreview.js +1 -0
  18. package/dist/cjs/audio/AudioForRendering.js +11 -7
  19. package/dist/cjs/audio/html5-audio.js +14 -7
  20. package/dist/cjs/audio/use-audio-frame.d.ts +1 -0
  21. package/dist/cjs/audio/use-audio-frame.js +14 -2
  22. package/dist/cjs/calculate-media-duration.js +1 -2
  23. package/dist/cjs/canvas-image/CanvasImage.d.ts +1 -1
  24. package/dist/cjs/canvas-image/CanvasImage.js +1 -1
  25. package/dist/cjs/canvas-image/props.d.ts +1 -1
  26. package/dist/cjs/effects/Solid.d.ts +28 -2
  27. package/dist/cjs/effects/Solid.js +16 -3
  28. package/dist/cjs/freeze.js +11 -3
  29. package/dist/cjs/get-asset-file-name.js +7 -1
  30. package/dist/cjs/get-sequence-boundary-tolerance.d.ts +7 -0
  31. package/dist/cjs/get-sequence-boundary-tolerance.js +10 -0
  32. package/dist/cjs/index.d.ts +3 -3
  33. package/dist/cjs/interactivity-schema.d.ts +75 -1
  34. package/dist/cjs/interactivity-schema.js +21 -1
  35. package/dist/cjs/internals.d.ts +23 -2
  36. package/dist/cjs/internals.js +2 -0
  37. package/dist/cjs/interpolate.d.ts +5 -0
  38. package/dist/cjs/interpolate.js +85 -5
  39. package/dist/cjs/loop/index.d.ts +7 -1
  40. package/dist/cjs/loop/index.js +35 -9
  41. package/dist/cjs/no-react.d.ts +10 -1
  42. package/dist/cjs/series/index.d.ts +1 -1
  43. package/dist/cjs/series/index.js +1 -0
  44. package/dist/cjs/spring/spring-utils.js +15 -12
  45. package/dist/cjs/timeline-position-state.d.ts +1 -0
  46. package/dist/cjs/timeline-position-state.js +8 -1
  47. package/dist/cjs/use-current-frame.js +2 -1
  48. package/dist/cjs/use-media-in-timeline.d.ts +5 -2
  49. package/dist/cjs/use-media-in-timeline.js +78 -13
  50. package/dist/cjs/use-media-playback.js +8 -3
  51. package/dist/cjs/use-premounting.js +19 -5
  52. package/dist/cjs/use-timeline-seek.d.ts +6 -0
  53. package/dist/cjs/use-timeline-seek.js +18 -0
  54. package/dist/cjs/version.d.ts +1 -1
  55. package/dist/cjs/version.js +1 -1
  56. package/dist/cjs/video/OffthreadVideo.js +7 -1
  57. package/dist/cjs/video/OffthreadVideoForRendering.js +11 -8
  58. package/dist/cjs/video/VideoForPreview.js +1 -0
  59. package/dist/cjs/video/VideoForRendering.js +12 -10
  60. package/dist/cjs/video/html5-video.js +12 -7
  61. package/dist/esm/index.mjs +927 -477
  62. package/dist/esm/no-react.mjs +103 -17
  63. package/dist/esm/version.mjs +1 -1
  64. package/package.json +2 -2
  65. package/dist/cjs/nonce.d.ts +0 -11
  66. package/dist/cjs/nonce.js +0 -42
@@ -8,6 +8,7 @@ export type NumberFieldSchema = {
8
8
  min?: number;
9
9
  max?: number;
10
10
  step?: number;
11
+ integer?: boolean;
11
12
  default: number | null | undefined;
12
13
  description?: string;
13
14
  hiddenFromList: boolean;
@@ -91,6 +92,12 @@ export type TextContentFieldSchema = {
91
92
  description?: string;
92
93
  keyframable?: false;
93
94
  };
95
+ export type SvgPathFieldSchema = {
96
+ type: 'svg-path';
97
+ default: string | undefined;
98
+ description?: string;
99
+ keyframable?: false;
100
+ };
94
101
  export type FontFamilyFieldSchema = {
95
102
  type: 'font-family';
96
103
  default: string | undefined;
@@ -145,7 +152,7 @@ export type CaptionsFieldSchema = {
145
152
  description?: string;
146
153
  keyframable: false;
147
154
  };
148
- export type VisibleFieldSchema = NumberFieldSchema | BooleanFieldSchema | RotationCssFieldSchema | RotationDegreesFieldSchema | TranslateFieldSchema | TransformOriginFieldSchema | ScaleFieldSchema | UvCoordinateFieldSchema | ColorFieldSchema | TextContentFieldSchema | FontFamilyFieldSchema | FontWeightFieldSchema | AssetFieldSchema | ArrayFieldSchema | CaptionsFieldSchema | EnumFieldSchema;
155
+ export type VisibleFieldSchema = NumberFieldSchema | BooleanFieldSchema | RotationCssFieldSchema | RotationDegreesFieldSchema | TranslateFieldSchema | TransformOriginFieldSchema | ScaleFieldSchema | UvCoordinateFieldSchema | ColorFieldSchema | TextContentFieldSchema | SvgPathFieldSchema | FontFamilyFieldSchema | FontWeightFieldSchema | AssetFieldSchema | ArrayFieldSchema | CaptionsFieldSchema | EnumFieldSchema;
149
156
  export type InteractivitySchemaField = VisibleFieldSchema | HiddenFieldSchema;
150
157
  export type InteractivitySchema = {
151
158
  [key: string]: InteractivitySchemaField;
@@ -756,6 +763,46 @@ export declare const freezeField: {
756
763
  readonly hiddenFromList: true;
757
764
  };
758
765
  export declare const baseSchema: {
766
+ readonly durationInFrames: {
767
+ readonly type: "number";
768
+ readonly default: undefined;
769
+ readonly min: 1;
770
+ readonly step: 1;
771
+ readonly hiddenFromList: true;
772
+ };
773
+ readonly from: {
774
+ readonly type: "number";
775
+ readonly default: 0;
776
+ readonly step: 1;
777
+ readonly hiddenFromList: true;
778
+ };
779
+ readonly trimBefore: {
780
+ readonly type: "number";
781
+ readonly default: 0;
782
+ readonly min: 0;
783
+ readonly step: 1;
784
+ readonly hiddenFromList: true;
785
+ };
786
+ readonly playbackRate: {
787
+ readonly type: "number";
788
+ readonly default: 1;
789
+ readonly min: 0.01;
790
+ readonly step: 0.1;
791
+ readonly description: "Playback rate";
792
+ readonly hiddenFromList: false;
793
+ readonly keyframable: false;
794
+ };
795
+ readonly freeze: {
796
+ readonly type: "number";
797
+ readonly default: null;
798
+ readonly step: 1;
799
+ readonly hiddenFromList: true;
800
+ };
801
+ readonly hidden: BooleanFieldSchema;
802
+ readonly name: HiddenFieldSchema;
803
+ readonly showInTimeline: HiddenFieldSchema;
804
+ };
805
+ export declare const baseSchemaWithoutPlaybackRate: {
759
806
  readonly durationInFrames: {
760
807
  readonly type: "number";
761
808
  readonly default: undefined;
@@ -807,6 +854,15 @@ export declare const sequenceSchema: {
807
854
  readonly step: 1;
808
855
  readonly hiddenFromList: true;
809
856
  };
857
+ readonly playbackRate: {
858
+ readonly type: "number";
859
+ readonly default: 1;
860
+ readonly min: 0.01;
861
+ readonly step: 0.1;
862
+ readonly description: "Playback rate";
863
+ readonly hiddenFromList: false;
864
+ readonly keyframable: false;
865
+ };
810
866
  readonly freeze: {
811
867
  readonly type: "number";
812
868
  readonly default: null;
@@ -1010,6 +1066,15 @@ export declare const baseSchemaWithoutFrom: {
1010
1066
  readonly step: 1;
1011
1067
  readonly hiddenFromList: true;
1012
1068
  };
1069
+ readonly playbackRate: {
1070
+ readonly type: "number";
1071
+ readonly default: 1;
1072
+ readonly min: 0.01;
1073
+ readonly step: 0.1;
1074
+ readonly description: "Playback rate";
1075
+ readonly hiddenFromList: false;
1076
+ readonly keyframable: false;
1077
+ };
1013
1078
  readonly freeze: {
1014
1079
  readonly type: "number";
1015
1080
  readonly default: null;
@@ -1035,6 +1100,15 @@ export declare const sequenceSchemaWithoutFrom: {
1035
1100
  readonly step: 1;
1036
1101
  readonly hiddenFromList: true;
1037
1102
  };
1103
+ readonly playbackRate: {
1104
+ readonly type: "number";
1105
+ readonly default: 1;
1106
+ readonly min: 0.01;
1107
+ readonly step: 0.1;
1108
+ readonly description: "Playback rate";
1109
+ readonly hiddenFromList: false;
1110
+ readonly keyframable: false;
1111
+ };
1038
1112
  readonly freeze: {
1039
1113
  readonly type: "number";
1040
1114
  readonly default: null;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sequenceSchemaDefaultLayoutNone = exports.sequenceSchemaWithoutFrom = exports.baseSchemaWithoutFrom = exports.sequenceSchema = exports.baseSchema = exports.freezeField = exports.trimBeforeField = exports.fromField = exports.durationInFramesField = exports.extendSchemaWithSequenceName = exports.sequenceNameField = exports.hiddenField = exports.sequenceStyleSchema = exports.sequenceCropSchema = exports.cropSchema = exports.sequencePremountSchema = exports.premountSchema = exports.textContentSchema = exports.svgPaintSchema = exports.svgStrokeSchema = exports.backgroundSchema = exports.borderRadiusSchema = exports.borderSchema = exports.textSchema = exports.sequenceVisualStyleSchema = exports.transformSchema = exports.captionsSchema = void 0;
3
+ exports.sequenceSchemaDefaultLayoutNone = exports.sequenceSchemaWithoutFrom = exports.baseSchemaWithoutFrom = exports.sequenceSchema = exports.baseSchemaWithoutPlaybackRate = exports.baseSchema = exports.freezeField = exports.trimBeforeField = exports.fromField = exports.durationInFramesField = exports.extendSchemaWithSequenceName = exports.sequenceNameField = exports.hiddenField = exports.sequenceStyleSchema = exports.sequenceCropSchema = exports.cropSchema = exports.sequencePremountSchema = exports.premountSchema = exports.textContentSchema = exports.svgPaintSchema = exports.svgStrokeSchema = exports.backgroundSchema = exports.borderRadiusSchema = exports.borderSchema = exports.textSchema = exports.sequenceVisualStyleSchema = exports.transformSchema = exports.captionsSchema = void 0;
4
4
  exports.captionsSchema = {
5
5
  captions: {
6
6
  type: 'remotion-captions',
@@ -353,7 +353,26 @@ exports.freezeField = {
353
353
  step: 1,
354
354
  hiddenFromList: true,
355
355
  };
356
+ const playbackRateField = {
357
+ type: 'number',
358
+ default: 1,
359
+ min: 0.01,
360
+ step: 0.1,
361
+ description: 'Playback rate',
362
+ hiddenFromList: false,
363
+ keyframable: false,
364
+ };
356
365
  exports.baseSchema = {
366
+ durationInFrames: exports.durationInFramesField,
367
+ from: exports.fromField,
368
+ trimBefore: exports.trimBeforeField,
369
+ playbackRate: playbackRateField,
370
+ freeze: exports.freezeField,
371
+ hidden: exports.hiddenField,
372
+ name: exports.sequenceNameField,
373
+ showInTimeline: showInTimelineField,
374
+ };
375
+ exports.baseSchemaWithoutPlaybackRate = {
357
376
  durationInFrames: exports.durationInFramesField,
358
377
  from: exports.fromField,
359
378
  trimBefore: exports.trimBeforeField,
@@ -377,6 +396,7 @@ exports.sequenceSchema = {
377
396
  exports.baseSchemaWithoutFrom = {
378
397
  durationInFrames: exports.durationInFramesField,
379
398
  trimBefore: exports.trimBeforeField,
399
+ playbackRate: playbackRateField,
380
400
  freeze: exports.freezeField,
381
401
  hidden: exports.hiddenField,
382
402
  name: exports.sequenceNameField,
@@ -134,6 +134,15 @@ export declare const Internals: {
134
134
  readonly step: 1;
135
135
  readonly hiddenFromList: true;
136
136
  };
137
+ readonly playbackRate: {
138
+ readonly type: "number";
139
+ readonly default: 1;
140
+ readonly min: 0.01;
141
+ readonly step: 0.1;
142
+ readonly description: "Playback rate";
143
+ readonly hiddenFromList: false;
144
+ readonly keyframable: false;
145
+ };
137
146
  readonly freeze: {
138
147
  readonly type: "number";
139
148
  readonly default: null;
@@ -165,6 +174,15 @@ export declare const Internals: {
165
174
  readonly step: 1;
166
175
  readonly hiddenFromList: true;
167
176
  };
177
+ readonly playbackRate: {
178
+ readonly type: "number";
179
+ readonly default: 1;
180
+ readonly min: 0.01;
181
+ readonly step: 0.1;
182
+ readonly description: "Playback rate";
183
+ readonly hiddenFromList: false;
184
+ readonly keyframable: false;
185
+ };
168
186
  readonly freeze: {
169
187
  readonly type: "number";
170
188
  readonly default: null;
@@ -768,7 +786,7 @@ export declare const Internals: {
768
786
  readonly getRoot: () => import("react").FC<{}> | null;
769
787
  readonly useMediaVolumeState: () => readonly [number, (u: number) => void];
770
788
  readonly usePlayerMutedState: () => readonly [boolean, (u: import("react").SetStateAction<boolean>) => void];
771
- readonly useMediaInTimeline: ({ volume, mediaVolume, src, mediaType, playbackRate, displayName, id, getStack, showInTimeline, premountDisplay, postmountDisplay, loopDisplay, documentationLink, refForOutline, muted, }: {
789
+ readonly useMediaInTimeline: ({ volume, mediaVolume, src, mediaType, playbackRate, displayName, id, getStack, showInTimeline, premountDisplay, postmountDisplay, loopDisplay, loopVolumeCurveBehavior, documentationLink, refForOutline, muted, }: {
772
790
  volume: import("./volume-prop.js").VolumeProp | undefined;
773
791
  mediaVolume: number;
774
792
  src: string | undefined;
@@ -781,6 +799,7 @@ export declare const Internals: {
781
799
  premountDisplay: number | null;
782
800
  postmountDisplay: number | null;
783
801
  loopDisplay: import("./CompositionManager.js").LoopDisplay | undefined;
802
+ loopVolumeCurveBehavior: import("./index.js").LoopVolumeCurveBehavior;
784
803
  documentationLink: string | null;
785
804
  refForOutline: import("react").RefObject<Element | null> | null;
786
805
  muted: boolean;
@@ -1141,7 +1160,7 @@ export declare const Internals: {
1141
1160
  }, "ref"> & import("react").RefAttributes<HTMLAudioElement>>;
1142
1161
  readonly OBJECTFIT_CONTAIN_CLASS_NAME: "__remotion_objectfitcontain";
1143
1162
  readonly InnerOffthreadVideo: import("react").FC<import("./video/props.js").AllOffthreadVideoProps>;
1144
- readonly useBasicMediaInTimeline: ({ volume, mediaVolume, mediaType, src, displayName, trimBefore, trimAfter, playbackRate, sequenceDurationInFrames, mediaStartsAt, loop, muted, }: {
1163
+ readonly useBasicMediaInTimeline: ({ volume, mediaVolume, mediaType, src, displayName, trimBefore, trimAfter, playbackRate, sequenceDurationInFrames, mediaStartsAt, mediaFrom, loop, muted, }: {
1145
1164
  volume: import("./volume-prop.js").VolumeProp | undefined;
1146
1165
  mediaVolume: number;
1147
1166
  mediaType: "audio" | "image" | "video";
@@ -1152,6 +1171,7 @@ export declare const Internals: {
1152
1171
  playbackRate: number;
1153
1172
  sequenceDurationInFrames: number;
1154
1173
  mediaStartsAt: number;
1174
+ mediaFrom: number;
1155
1175
  loop: boolean;
1156
1176
  muted: boolean;
1157
1177
  }) => {
@@ -1170,6 +1190,7 @@ export declare const Internals: {
1170
1190
  readonly useAudioEnabled: () => boolean;
1171
1191
  readonly useBuffering: () => boolean;
1172
1192
  readonly TimelinePosition: typeof TimelinePosition;
1193
+ readonly useTimelineSeek: (setFrame: (frame: import("react").SetStateAction<Record<string, number>>) => void) => import("./use-timeline-seek.js").TimelineSeek;
1173
1194
  readonly DelayRenderContextType: import("react").Context<import("./delay-render.js").DelayRenderScope | null>;
1174
1195
  readonly TimelineContext: import("react").Context<TimelineContextValue | null>;
1175
1196
  readonly usePlaying: () => boolean;
@@ -109,6 +109,7 @@ const use_playing_js_1 = require("./use-playing.js");
109
109
  const use_premounting_js_1 = require("./use-premounting.js");
110
110
  const use_schema_js_1 = require("./use-schema.js");
111
111
  const use_sync_external_store_js_1 = require("./use-sync-external-store.js");
112
+ const use_timeline_seek_js_1 = require("./use-timeline-seek.js");
112
113
  const use_unsafe_video_config_js_1 = require("./use-unsafe-video-config.js");
113
114
  const use_video_js_1 = require("./use-video.js");
114
115
  const validate_media_props_js_1 = require("./validate-media-props.js");
@@ -270,6 +271,7 @@ exports.Internals = {
270
271
  useAudioEnabled: use_media_enabled_js_1.useAudioEnabled,
271
272
  useBuffering: use_buffering_js_1.useBuffering,
272
273
  TimelinePosition,
274
+ useTimelineSeek: use_timeline_seek_js_1.useTimelineSeek,
273
275
  DelayRenderContextType: use_delay_render_js_1.DelayRenderContextType,
274
276
  TimelineContext: TimelineContext_js_1.TimelineContext,
275
277
  usePlaying: use_playing_js_1.usePlaying,
@@ -1,5 +1,6 @@
1
1
  export type ExtrapolateType = 'extend' | 'identity' | 'clamp' | 'wrap';
2
2
  export type InterpolateOutputOption = 'linear' | 'perceptual-scale';
3
+ export type InterpolateOutputType = 'font-weight' | 'scale' | 'translate' | 'rotate' | 'transform-origin';
3
4
  /**
4
5
  * @description This function allows you to map a range of values to another with a concise syntax
5
6
  * @see [Documentation](https://www.remotion.dev/docs/interpolate)
@@ -12,6 +13,7 @@ export type InterpolateOptions = Partial<{
12
13
  extrapolateLeft: ExtrapolateType;
13
14
  extrapolateRight: ExtrapolateType;
14
15
  output: InterpolateOutputOption;
16
+ outputType: InterpolateOutputType;
15
17
  posterize: number;
16
18
  }>;
17
19
  type NumericTuple = readonly [number, ...number[]];
@@ -21,6 +23,9 @@ type WidenNumericTuple<T extends readonly number[]> = {
21
23
  export declare function assertValidInterpolateEasingOption(easing: EasingFunction | readonly EasingFunction[] | undefined, inputRangeLength: number): void;
22
24
  export declare function assertValidInterpolatePosterizeOption(posterize: number | undefined): void;
23
25
  export declare function interpolate(input: number, inputRange: readonly number[], outputRange: readonly number[], options?: InterpolateOptions): number;
26
+ export declare function interpolate(input: number, inputRange: readonly number[], outputRange: readonly (number | string)[], options: InterpolateOptions & {
27
+ outputType: 'font-weight';
28
+ }): number;
24
29
  export declare function interpolate(input: number, inputRange: readonly number[], outputRange: readonly string[], options?: InterpolateOptions): string;
25
30
  export declare function interpolate<const Tuple extends NumericTuple>(input: number, inputRange: readonly number[], outputRange: readonly Tuple[], options?: InterpolateOptions): WidenNumericTuple<Tuple>;
26
31
  export declare function interpolate(input: number, inputRange: readonly number[], outputRange: readonly (readonly number[])[], options?: InterpolateOptions): number[];
@@ -254,7 +254,7 @@ const parseAxisRotationValue = (output) => {
254
254
  axisRotation: true,
255
255
  };
256
256
  };
257
- const parseStringInterpolationValue = (output) => {
257
+ const parseStringInterpolationValue = (output, outputType) => {
258
258
  var _a, _b, _c, _d, _e, _f;
259
259
  var _g, _h, _j, _k, _l, _m;
260
260
  if (typeof output === 'number') {
@@ -269,8 +269,20 @@ const parseStringInterpolationValue = (output) => {
269
269
  axisRotation: false,
270
270
  };
271
271
  }
272
+ if (outputType === 'transform-origin') {
273
+ const transformOriginParts = output.trim().split(/\s+/);
274
+ if (transformOriginParts.length < 1 ||
275
+ transformOriginParts.length > 3 ||
276
+ transformOriginParts[0] === '') {
277
+ throw new TypeError(`String outputRange values must contain 1 to 3 components, but got "${output}"`);
278
+ }
279
+ return parseTransformOriginValue(output, transformOriginParts);
280
+ }
272
281
  const axisRotation = parseAxisRotationValue(output);
273
282
  if (axisRotation !== null) {
283
+ if (outputType !== undefined && outputType !== 'rotate') {
284
+ throw new TypeError(`Cannot interpolate "${output}" as ${outputType} because it is a rotate value`);
285
+ }
274
286
  return axisRotation;
275
287
  }
276
288
  const parts = output.trim().split(/\s+/);
@@ -278,6 +290,9 @@ const parseStringInterpolationValue = (output) => {
278
290
  throw new TypeError(`String outputRange values must contain 1 to 3 components, but got "${output}"`);
279
291
  }
280
292
  if (parts.some((part) => transformOriginKeywords.has(part.toLowerCase()))) {
293
+ if (outputType !== undefined) {
294
+ throw new TypeError(`Cannot interpolate "${output}" as ${outputType} because it is a transform-origin value`);
295
+ }
281
296
  return parseTransformOriginValue(output, parts);
282
297
  }
283
298
  const parsed = parts.map((part) => parseStringInterpolationComponent(part, output));
@@ -287,6 +302,9 @@ const parseStringInterpolationValue = (output) => {
287
302
  throw new TypeError(`Cannot interpolate "${output}" because it mixes ${kind} and ${part.kind} values`);
288
303
  }
289
304
  }
305
+ if (outputType !== undefined && outputType !== kind) {
306
+ throw new TypeError(`Cannot interpolate "${output}" as ${outputType} because it is a ${kind} value`);
307
+ }
290
308
  if (kind === 'scale') {
291
309
  const x = parsed[0].value;
292
310
  const y = (_g = (_a = parsed[1]) === null || _a === void 0 ? void 0 : _a.value) !== null && _g !== void 0 ? _g : x;
@@ -486,9 +504,9 @@ const interpolateNumber = ({ input, inputRange, outputRange, options, }) => {
486
504
  }
487
505
  return result;
488
506
  };
489
- const interpolateString = ({ input, inputRange, outputRange, options, }) => {
507
+ const interpolateString = ({ input, inputRange, outputRange, options, outputType, }) => {
490
508
  var _a;
491
- const initiallyParsedOutputRange = outputRange.map(parseStringInterpolationValue);
509
+ const initiallyParsedOutputRange = outputRange.map((output) => parseStringInterpolationValue(output, outputType));
492
510
  const hasAxisRotation = initiallyParsedOutputRange.some((parsed) => parsed.axisRotation);
493
511
  const posterizedInput = (options === null || options === void 0 ? void 0 : options.posterize) === undefined
494
512
  ? input
@@ -589,6 +607,35 @@ const interpolateString = ({ input, inputRange, outputRange, options, }) => {
589
607
  axisRotation: hasAxisRotation,
590
608
  });
591
609
  };
610
+ const interpolateFontWeight = ({ input, inputRange, outputRange, options, }) => {
611
+ const normalizedOutputRange = outputRange.map((output) => {
612
+ if (typeof output === 'string') {
613
+ const lowerCaseOutput = output.toLowerCase();
614
+ if (lowerCaseOutput === 'normal') {
615
+ return 400;
616
+ }
617
+ if (lowerCaseOutput === 'bold') {
618
+ return 700;
619
+ }
620
+ }
621
+ const match = typeof output === 'string' ? cssNumberRegex.exec(output) : null;
622
+ const value = typeof output === 'number'
623
+ ? output
624
+ : match !== null && match[2] === undefined
625
+ ? Number(match[1])
626
+ : Number.NaN;
627
+ if (!Number.isFinite(value) || value < 1 || value > 1000) {
628
+ throw new TypeError(`Cannot interpolate font weight "${output}". Expected "normal", "bold", or a number between 1 and 1000`);
629
+ }
630
+ return value;
631
+ });
632
+ return interpolateNumber({
633
+ input,
634
+ inputRange,
635
+ outputRange: normalizedOutputRange,
636
+ options,
637
+ });
638
+ };
592
639
  const interpolateDiscreteString = ({ input, inputRange, outputRange, options, }) => {
593
640
  if (inputRange.length === 1) {
594
641
  return outputRange[0];
@@ -725,6 +772,17 @@ function assertValidInterpolateOutputOption(output) {
725
772
  }
726
773
  throw new Error(`output must be "linear" or "perceptual-scale", but got ${String(output)}`);
727
774
  }
775
+ function assertValidInterpolateOutputType(outputType) {
776
+ if (outputType === undefined ||
777
+ outputType === 'font-weight' ||
778
+ outputType === 'scale' ||
779
+ outputType === 'translate' ||
780
+ outputType === 'rotate' ||
781
+ outputType === 'transform-origin') {
782
+ return;
783
+ }
784
+ throw new Error(`outputType must be "font-weight", "scale", "translate", "rotate", or "transform-origin", but got ${String(outputType)}`);
785
+ }
728
786
  function interpolate(input, inputRange, outputRange, options) {
729
787
  if (typeof input === 'undefined') {
730
788
  throw new Error('input can not be undefined');
@@ -747,19 +805,41 @@ function interpolate(input, inputRange, outputRange, options) {
747
805
  assertValidInterpolateEasingOption(options === null || options === void 0 ? void 0 : options.easing, inputRange.length);
748
806
  assertValidInterpolatePosterizeOption(options === null || options === void 0 ? void 0 : options.posterize);
749
807
  assertValidInterpolateOutputOption(options === null || options === void 0 ? void 0 : options.output);
808
+ assertValidInterpolateOutputType(options === null || options === void 0 ? void 0 : options.outputType);
750
809
  if (typeof input !== 'number') {
751
810
  throw new TypeError('Cannot interpolate an input which is not a number');
752
811
  }
753
812
  if (!Array.isArray(outputRange)) {
754
813
  throw new Error('outputRange must contain only numbers');
755
814
  }
815
+ const outputType = options === null || options === void 0 ? void 0 : options.outputType;
816
+ if (outputType === 'font-weight') {
817
+ if (!outputRange.every((output) => typeof output === 'number' || typeof output === 'string')) {
818
+ throw new TypeError('Font weight outputRange must contain only numbers or strings');
819
+ }
820
+ return interpolateFontWeight({
821
+ input,
822
+ inputRange,
823
+ outputRange,
824
+ options,
825
+ });
826
+ }
756
827
  const hasStringOutput = outputRange.some((output) => typeof output === 'string');
828
+ if (outputType !== undefined && outputType !== 'scale' && !hasStringOutput) {
829
+ throw new TypeError(`${outputType} outputRange must contain strings with the appropriate CSS units`);
830
+ }
757
831
  if (hasStringOutput) {
758
832
  if (!outputRange.every((output) => typeof output === 'string' || typeof output === 'number')) {
759
833
  throw new TypeError('outputRange must contain only numbers, or supported scale, translate, and rotate strings');
760
834
  }
761
835
  try {
762
- return interpolateString({ input, inputRange, outputRange, options });
836
+ return interpolateString({
837
+ input,
838
+ inputRange,
839
+ outputRange,
840
+ options,
841
+ outputType,
842
+ });
763
843
  }
764
844
  catch (error) {
765
845
  if (!outputRange.every((output) => typeof output === 'string')) {
@@ -767,7 +847,7 @@ function interpolate(input, inputRange, outputRange, options) {
767
847
  }
768
848
  const hasNonNumericString = outputRange.some((output) => {
769
849
  try {
770
- parseStringInterpolationValue(output);
850
+ parseStringInterpolationValue(output, outputType);
771
851
  return false;
772
852
  }
773
853
  catch (parseError) {
@@ -5,11 +5,17 @@ export type LoopProps = {
5
5
  readonly times?: number;
6
6
  readonly name?: string;
7
7
  readonly children: React.ReactNode;
8
- } & LayoutAndStyle & Pick<SequenceProps, 'showInTimeline'>;
8
+ } & LayoutAndStyle & Pick<SequenceProps, 'showInTimeline' | 'playbackRate'>;
9
9
  type LoopContextType = {
10
10
  iteration: number;
11
11
  durationInFrames: number;
12
12
  };
13
+ export declare const LoopTimelineContext: React.Context<{
14
+ startFrame: number;
15
+ firstVisibleFrame: number;
16
+ endFrame: number;
17
+ playbackRate: number;
18
+ } | null>;
13
19
  declare const useLoop: () => LoopContextType | null;
14
20
  export declare const Loop: React.FC<LoopProps> & {
15
21
  useLoop: typeof useLoop;
@@ -33,14 +33,16 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.Loop = void 0;
36
+ exports.Loop = exports.LoopTimelineContext = void 0;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const react_1 = __importStar(require("react"));
39
39
  const Sequence_js_1 = require("../Sequence.js");
40
+ const SequenceContext_js_1 = require("../SequenceContext.js");
40
41
  const use_current_frame_js_1 = require("../use-current-frame.js");
41
42
  const use_video_config_js_1 = require("../use-video-config.js");
42
43
  const validate_duration_in_frames_js_1 = require("../validation/validate-duration-in-frames.js");
43
44
  const LoopContext = (0, react_1.createContext)(null);
45
+ exports.LoopTimelineContext = (0, react_1.createContext)(null);
44
46
  const useLoop = () => {
45
47
  return react_1.default.useContext(LoopContext);
46
48
  };
@@ -48,9 +50,19 @@ const useLoop = () => {
48
50
  * @description This component allows you to quickly lay out an animation so it repeats itself.
49
51
  * @see [Documentation](https://remotion.dev/docs/loop)
50
52
  */
51
- const Loop = ({ durationInFrames, times = Infinity, children, name, showInTimeline, ...props }) => {
53
+ const Loop = ({ durationInFrames, times = Infinity, children, name, showInTimeline, playbackRate, ...props }) => {
54
+ var _a, _b;
52
55
  const currentFrame = (0, use_current_frame_js_1.useCurrentFrame)();
53
56
  const { durationInFrames: compDuration } = (0, use_video_config_js_1.useVideoConfig)();
57
+ const parentSequence = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
58
+ const parentPlaybackRate = (_a = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.playbackRate) !== null && _a !== void 0 ? _a : 1;
59
+ const loopStartFrame = parentSequence
60
+ ? parentSequence.cumulatedFrom + parentSequence.relativeFrom
61
+ : 0;
62
+ const firstVisibleFrame = loopStartFrame -
63
+ ((_b = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.cumulatedNegativeFrom) !== null && _b !== void 0 ? _b : 0) / parentPlaybackRate;
64
+ const endFrame = loopStartFrame +
65
+ Math.min(compDuration, durationInFrames * times) / parentPlaybackRate;
54
66
  (0, validate_duration_in_frames_js_1.validateDurationInFrames)(durationInFrames, {
55
67
  component: 'of the <Loop /> component',
56
68
  allowFloats: true,
@@ -66,11 +78,16 @@ const Loop = ({ durationInFrames, times = Infinity, children, name, showInTimeli
66
78
  }
67
79
  const maxTimes = Math.ceil(compDuration / durationInFrames);
68
80
  const actualTimes = Math.min(maxTimes, times);
69
- const style = props.layout === 'none' ? undefined : props.style;
70
81
  const maxFrame = durationInFrames * (actualTimes - 1);
71
- const iteration = Math.floor(currentFrame / durationInFrames);
72
- const start = iteration * durationInFrames;
73
- const from = Math.min(start, maxFrame);
82
+ const loopsElapsed = currentFrame / durationInFrames;
83
+ const nearestIteration = Math.round(loopsElapsed);
84
+ // Fractional durations and nested playback rates can put an exact loop
85
+ // boundary a few floating-point units before the next iteration.
86
+ const isAtBoundary = Math.abs(loopsElapsed - nearestIteration) <=
87
+ Number.EPSILON * Math.max(1, Math.abs(loopsElapsed)) * 4;
88
+ const iteration = Math.max(0, Math.min(actualTimes - 1, isAtBoundary ? nearestIteration : Math.floor(loopsElapsed)));
89
+ const start = isAtBoundary ? currentFrame : iteration * durationInFrames;
90
+ const from = Math.max(0, Math.min(start, maxFrame));
74
91
  const loopDisplay = (0, react_1.useMemo)(() => {
75
92
  return {
76
93
  numberOfTimes: Math.min(compDuration / durationInFrames, times),
@@ -80,11 +97,20 @@ const Loop = ({ durationInFrames, times = Infinity, children, name, showInTimeli
80
97
  }, [compDuration, durationInFrames, from, times]);
81
98
  const loopContext = (0, react_1.useMemo)(() => {
82
99
  return {
83
- iteration: Math.floor(currentFrame / durationInFrames),
100
+ iteration,
84
101
  durationInFrames,
85
102
  };
86
- }, [currentFrame, durationInFrames]);
87
- return (jsx_runtime_1.jsx(LoopContext.Provider, { value: loopContext, children: jsx_runtime_1.jsx(Sequence_js_1.Sequence, { durationInFrames: durationInFrames, from: from, name: name !== null && name !== void 0 ? name : '<Loop>', _remotionInternalDocumentationLink: "https://www.remotion.dev/docs/loop", _remotionInternalLoopDisplay: loopDisplay, layout: props.layout, style: style, showInTimeline: showInTimeline, children: children }) }));
103
+ }, [iteration, durationInFrames]);
104
+ const timelineContext = (0, react_1.useMemo)(() => ({
105
+ startFrame: loopStartFrame,
106
+ firstVisibleFrame,
107
+ endFrame,
108
+ playbackRate: parentPlaybackRate,
109
+ }), [loopStartFrame, firstVisibleFrame, endFrame, parentPlaybackRate]);
110
+ if (actualTimes === 0) {
111
+ return null;
112
+ }
113
+ return (jsx_runtime_1.jsx(exports.LoopTimelineContext.Provider, { value: timelineContext, children: jsx_runtime_1.jsx(LoopContext.Provider, { value: loopContext, children: jsx_runtime_1.jsx(Sequence_js_1.Sequence, { durationInFrames: durationInFrames, from: from, name: name !== null && name !== void 0 ? name : '<Loop>', _remotionInternalDocumentationLink: "https://www.remotion.dev/docs/loop", _remotionInternalLoopDisplay: loopDisplay, ...props, showInTimeline: showInTimeline, playbackRate: playbackRate, children: children }) }) }));
88
114
  };
89
115
  exports.Loop = Loop;
90
116
  exports.Loop.useLoop = useLoop;
@@ -1,7 +1,7 @@
1
1
  export type { ArtifactAsset, AudioOrVideoAsset, InlineAudioAsset, TRenderAsset, } from './CompositionManager';
2
2
  export type { DownloadBehavior } from './download-behavior';
3
3
  export { assertValidInterpolateEasingOption, assertValidInterpolatePosterizeOption, interpolate, } from './interpolate';
4
- export type { EasingFunction, ExtrapolateType, InterpolateOutputOption, InterpolateOptions, } from './interpolate';
4
+ export type { EasingFunction, ExtrapolateType, InterpolateOptions, InterpolateOutputOption, InterpolateOutputType, } from './interpolate';
5
5
  export { random } from './random.js';
6
6
  export type { RandomSeed } from './random.js';
7
7
  export type { VideoConfig } from './video-config';
@@ -83,6 +83,15 @@ export declare const NoReactInternals: {
83
83
  readonly step: 1;
84
84
  readonly hiddenFromList: true;
85
85
  };
86
+ readonly playbackRate: {
87
+ readonly type: "number";
88
+ readonly default: 1;
89
+ readonly min: 0.01;
90
+ readonly step: 0.1;
91
+ readonly description: "Playback rate";
92
+ readonly hiddenFromList: false;
93
+ readonly keyframable: false;
94
+ };
86
95
  readonly freeze: {
87
96
  readonly type: "number";
88
97
  readonly default: null;
@@ -4,7 +4,7 @@ type SeriesSequenceProps = PropsWithChildren<{
4
4
  readonly durationInFrames: number;
5
5
  readonly offset?: number;
6
6
  readonly className?: string;
7
- } & Pick<SequenceProps, 'layout' | 'name' | 'hidden' | 'showInTimeline' | 'freeze' | 'trimBefore'> & LayoutAndStyle>;
7
+ } & Pick<SequenceProps, 'layout' | 'name' | 'hidden' | 'showInTimeline' | 'freeze' | 'trimBefore' | 'playbackRate'> & LayoutAndStyle>;
8
8
  declare const SeriesSequence: React.ForwardRefExoticComponent<SeriesSequenceProps & React.RefAttributes<HTMLDivElement>>;
9
9
  type SeriesProps = SequenceProps;
10
10
  /**
@@ -51,6 +51,7 @@ const seriesSequenceSchema = {
51
51
  showInTimeline: Interactive_js_1.Interactive.sequenceSchema.showInTimeline,
52
52
  freeze: Interactive_js_1.Interactive.baseSchema.freeze,
53
53
  trimBefore: Interactive_js_1.Interactive.sequenceSchema.trimBefore,
54
+ playbackRate: Interactive_js_1.Interactive.sequenceSchema.playbackRate,
54
55
  layout: Interactive_js_1.Interactive.sequenceSchema.layout,
55
56
  };
56
57
  const SeriesSequenceInner = (0, react_1.forwardRef)(({ offset = 0, className = '', _remotionInternalRender = null, ...props }, ref) => {
@@ -64,15 +64,24 @@ function advance({ animation, now, config, }) {
64
64
  }
65
65
  const calculationCache = {};
66
66
  function springCalculation({ frame, fps, config = {}, }) {
67
+ var _a, _b, _c, _d;
67
68
  const from = 0;
68
69
  const to = 1;
70
+ // An explicit `undefined` (e.g. a forwarded optional prop) must not
71
+ // override the defaults, otherwise the physics turn into NaN
72
+ const resolvedConfig = {
73
+ damping: (_a = config.damping) !== null && _a !== void 0 ? _a : defaultSpringConfig.damping,
74
+ mass: (_b = config.mass) !== null && _b !== void 0 ? _b : defaultSpringConfig.mass,
75
+ stiffness: (_c = config.stiffness) !== null && _c !== void 0 ? _c : defaultSpringConfig.stiffness,
76
+ overshootClamping: (_d = config.overshootClamping) !== null && _d !== void 0 ? _d : defaultSpringConfig.overshootClamping,
77
+ };
69
78
  const cacheKey = [
70
79
  frame,
71
80
  fps,
72
- config.damping,
73
- config.mass,
74
- config.overshootClamping,
75
- config.stiffness,
81
+ resolvedConfig.damping,
82
+ resolvedConfig.mass,
83
+ resolvedConfig.overshootClamping,
84
+ resolvedConfig.stiffness,
76
85
  ].join('-');
77
86
  if (calculationCache[cacheKey]) {
78
87
  return calculationCache[cacheKey];
@@ -91,20 +100,14 @@ function springCalculation({ frame, fps, config = {}, }) {
91
100
  animation = advance({
92
101
  animation,
93
102
  now: time,
94
- config: {
95
- ...defaultSpringConfig,
96
- ...config,
97
- },
103
+ config: resolvedConfig,
98
104
  });
99
105
  }
100
106
  if (unevenRest > 0) {
101
107
  animation = advance({
102
108
  animation,
103
109
  now: (frameClamped / fps) * 1000,
104
- config: {
105
- ...defaultSpringConfig,
106
- ...config,
107
- },
110
+ config: resolvedConfig,
108
111
  });
109
112
  }
110
113
  calculationCache[cacheKey] = animation;
@@ -14,5 +14,6 @@ export declare const useTimelinePosition: () => number;
14
14
  export declare const useIsInsideFreeze: () => boolean;
15
15
  export declare const useAbsoluteTimelinePosition: () => number;
16
16
  export declare const useTimelineSetFrame: () => (u: import("react").SetStateAction<Record<string, number>>) => void;
17
+ export declare const useTimelineSeekFrame: () => (frame: import("react").SetStateAction<Record<string, number>>) => void;
17
18
  export { usePlaying } from './use-playing.js';
18
19
  export { useBuffering } from './use-buffering.js';