remotion 4.0.508 → 4.0.509

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.
@@ -9,4 +9,5 @@ export declare const RemotionRootContexts: React.FC<{
9
9
  readonly videoEnabled: boolean;
10
10
  readonly audioEnabled: boolean;
11
11
  readonly frameState: Record<string, number> | null;
12
+ readonly _experimentalKeepAudioContextAlive: boolean;
12
13
  }>;
@@ -13,7 +13,7 @@ const SequenceManager_js_1 = require("./SequenceManager.js");
13
13
  const TimelineContext_js_1 = require("./TimelineContext.js");
14
14
  const use_media_enabled_js_1 = require("./use-media-enabled.js");
15
15
  const duration_state_js_1 = require("./video/duration-state.js");
16
- const RemotionRootContexts = ({ children, numberOfAudioTags, logLevel, audioLatencyHint, previewSampleRate, videoEnabled, audioEnabled, frameState, }) => {
16
+ const RemotionRootContexts = ({ children, numberOfAudioTags, logLevel, audioLatencyHint, previewSampleRate, videoEnabled, audioEnabled, frameState, _experimentalKeepAudioContextAlive, }) => {
17
17
  const nonceContext = (0, react_1.useMemo)(() => {
18
18
  let counter = 0;
19
19
  return {
@@ -23,6 +23,6 @@ const RemotionRootContexts = ({ children, numberOfAudioTags, logLevel, audioLate
23
23
  const logging = (0, react_1.useMemo)(() => {
24
24
  return { logLevel, mountTime: Date.now() };
25
25
  }, [logLevel]);
26
- return (jsx_runtime_1.jsx(log_level_context_js_1.LogLevelContext.Provider, { value: logging, children: jsx_runtime_1.jsx(nonce_js_1.NonceContext.Provider, { value: nonceContext, children: jsx_runtime_1.jsx(TimelineContext_js_1.TimelineContextProvider, { frameState: frameState, children: jsx_runtime_1.jsx(use_media_enabled_js_1.MediaEnabledProvider, { videoEnabled: videoEnabled, audioEnabled: audioEnabled, children: jsx_runtime_1.jsx(EditorProps_js_1.EditorPropsProvider, { children: jsx_runtime_1.jsx(prefetch_state_js_1.PrefetchProvider, { children: jsx_runtime_1.jsx(SequenceManager_js_1.SequenceManagerProvider, { children: jsx_runtime_1.jsx(duration_state_js_1.DurationsContextProvider, { children: jsx_runtime_1.jsx(buffering_js_1.BufferingProvider, { children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioContextProvider, { audioLatencyHint: audioLatencyHint, audioEnabled: audioEnabled, previewSampleRate: previewSampleRate, children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioTagsContextProvider, { numberOfAudioTags: numberOfAudioTags, children: children }) }) }) }) }) }) }) }) }) }) }));
26
+ return (jsx_runtime_1.jsx(log_level_context_js_1.LogLevelContext.Provider, { value: logging, children: jsx_runtime_1.jsx(nonce_js_1.NonceContext.Provider, { value: nonceContext, children: jsx_runtime_1.jsx(TimelineContext_js_1.TimelineContextProvider, { frameState: frameState, children: jsx_runtime_1.jsx(use_media_enabled_js_1.MediaEnabledProvider, { videoEnabled: videoEnabled, audioEnabled: audioEnabled, children: jsx_runtime_1.jsx(EditorProps_js_1.EditorPropsProvider, { children: jsx_runtime_1.jsx(prefetch_state_js_1.PrefetchProvider, { children: jsx_runtime_1.jsx(SequenceManager_js_1.SequenceManagerProvider, { children: jsx_runtime_1.jsx(duration_state_js_1.DurationsContextProvider, { children: jsx_runtime_1.jsx(buffering_js_1.BufferingProvider, { children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioContextProvider, { audioLatencyHint: audioLatencyHint, audioEnabled: audioEnabled, previewSampleRate: previewSampleRate, _experimentalKeepAudioContextAlive: _experimentalKeepAudioContextAlive, children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioTagsContextProvider, { numberOfAudioTags: numberOfAudioTags, children: children }) }) }) }) }) }) }) }) }) }) }));
27
27
  };
28
28
  exports.RemotionRootContexts = RemotionRootContexts;
@@ -61,6 +61,7 @@ type SharedAudioContextValue = {
61
61
  suspend: () => Promise<void>;
62
62
  getIsResumingAudioContext: () => Promise<AudioContextResumeResult> | null;
63
63
  unscheduleAudioNode: (node: AudioBufferSourceNode) => void;
64
+ _experimentalKeepAudioContextAlive: boolean;
64
65
  };
65
66
  type SharedAudioTagsContextValue = {
66
67
  registerAudio: (options: {
@@ -87,6 +88,7 @@ export declare const SharedAudioContextProvider: React.FC<{
87
88
  readonly audioLatencyHint: AudioContextLatencyCategory;
88
89
  readonly audioEnabled: boolean;
89
90
  readonly previewSampleRate: number | null;
91
+ readonly _experimentalKeepAudioContextAlive: boolean;
90
92
  }>;
91
93
  export declare const SharedAudioTagsContextProvider: React.FC<{
92
94
  readonly numberOfAudioTags: number;
@@ -88,7 +88,7 @@ const shouldSaveForLater = (state) => {
88
88
  }
89
89
  throw new Error(`Unexpected audio context state: ${state}`);
90
90
  };
91
- const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled, previewSampleRate }) => {
91
+ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled, previewSampleRate, _experimentalKeepAudioContextAlive, }) => {
92
92
  const logLevel = (0, log_level_context_js_1.useLogLevel)();
93
93
  const sampleRate = previewSampleRate !== null && previewSampleRate !== void 0 ? previewSampleRate : 48000;
94
94
  (0, react_1.useEffect)(() => {
@@ -104,6 +104,11 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
104
104
  sampleRate,
105
105
  });
106
106
  const audioContextIsPlayingEventually = (0, react_1.useRef)(false);
107
+ const initialExperimentalKeepAudioContextAlive = (0, react_1.useRef)(_experimentalKeepAudioContextAlive);
108
+ if (initialExperimentalKeepAudioContextAlive.current !==
109
+ _experimentalKeepAudioContextAlive) {
110
+ throw new Error('`_experimentalKeepAudioContextAlive` cannot be changed dynamically.');
111
+ }
107
112
  const isResuming = (0, react_1.useRef)(null);
108
113
  const nextResumeAttemptId = (0, react_1.useRef)(0);
109
114
  const audioSyncAnchor = (0, react_1.useMemo)(() => ({ value: 0 }), []);
@@ -141,7 +146,14 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
141
146
  reason: 'audio context is closed',
142
147
  };
143
148
  }
144
- const saveForLater = shouldSaveForLater(currentState);
149
+ // With _experimentalKeepAudioContextAlive, the native context stays
150
+ // `running` while silenced, so the state alone does not reveal that playback
151
+ // is paused. Queue the node like the suspend path does, otherwise it
152
+ // would start right away at a stale position and become audible when
153
+ // the gain ramps back up.
154
+ const saveForLater = shouldSaveForLater(currentState) ||
155
+ (_experimentalKeepAudioContextAlive &&
156
+ !audioContextIsPlayingEventually.current);
145
157
  if (duration > 0) {
146
158
  if (saveForLater) {
147
159
  nodesToResume.current.set(node, {
@@ -187,7 +199,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
187
199
  reason: 'missed ' + Math.abs(offset).toFixed(2) + 's',
188
200
  };
189
201
  };
190
- }, [ctxAndGain, logLevel]);
202
+ }, [ctxAndGain, _experimentalKeepAudioContextAlive, logLevel]);
191
203
  const resume = (0, react_1.useCallback)(() => {
192
204
  if (!ctxAndGain) {
193
205
  return Promise.resolve();
@@ -203,6 +215,13 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
203
215
  node.start(r.scheduledTime, r.offset, r.duration);
204
216
  });
205
217
  nodesToResume.current.clear();
218
+ if (_experimentalKeepAudioContextAlive &&
219
+ ctxAndGain.audioContext.state === 'running') {
220
+ // The context was never suspended, so there is nothing to wait for:
221
+ // the resume-wait machinery, its timeout and the mute-on-failure
222
+ // path are not entered.
223
+ return Promise.resolve();
224
+ }
206
225
  const resumePromise = ctxAndGain.resume();
207
226
  const abortController = new AbortController();
208
227
  const resumeAttemptId = nextResumeAttemptId.current++;
@@ -228,7 +247,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
228
247
  // Already logged above; swallow to avoid unhandled rejection
229
248
  // since callers (e.g. use-playback.ts) do not await this.
230
249
  });
231
- }, [ctxAndGain, logLevel]);
250
+ }, [ctxAndGain, _experimentalKeepAudioContextAlive, logLevel]);
232
251
  const getIsResumingAudioContext = (0, react_1.useCallback)(() => {
233
252
  var _a;
234
253
  var _b;
@@ -244,8 +263,56 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
244
263
  return Promise.resolve();
245
264
  }
246
265
  audioContextIsPlayingEventually.current = false;
266
+ if (_experimentalKeepAudioContextAlive) {
267
+ // Silence through the gain instead of suspending, so the context
268
+ // clock keeps running and the next resume() is instant. Audio that
269
+ // is already scheduled plays out silently; resume() ramps the gain
270
+ // back up.
271
+ ctxAndGain.gainNode.gain.cancelScheduledValues(ctxAndGain.audioContext.currentTime);
272
+ ctxAndGain.gainNode.gain.setValueAtTime(0, ctxAndGain.audioContext.currentTime);
273
+ return Promise.resolve();
274
+ }
247
275
  return ctxAndGain.suspend();
248
- }, [ctxAndGain]);
276
+ }, [ctxAndGain, _experimentalKeepAudioContextAlive]);
277
+ // With _experimentalKeepAudioContextAlive, start the context as early as
278
+ // possible so the first play never waits on the suspended→running transition. Where
279
+ // no autoplay restriction applies (e.g. Electron with
280
+ // `no-user-gesture-required`), the mount attempt succeeds immediately;
281
+ // in a browser the first user gesture starts it. suspend() never reaches
282
+ // the native context in this mode, so once running it stays running.
283
+ // The cleanup suspends the native context, since Player contexts are
284
+ // not close()d and must not be left running after unmount.
285
+ (0, react_1.useEffect)(() => {
286
+ if (!_experimentalKeepAudioContextAlive) {
287
+ return;
288
+ }
289
+ if (!ctxAndGain) {
290
+ return;
291
+ }
292
+ if (typeof window === 'undefined') {
293
+ return;
294
+ }
295
+ const wake = () => {
296
+ if (ctxAndGain.audioContext.state === 'running') {
297
+ return;
298
+ }
299
+ ctxAndGain.resume().catch(() => {
300
+ // A rejection here means autoplay is still blocked; a later
301
+ // gesture will retry.
302
+ });
303
+ };
304
+ wake();
305
+ window.addEventListener('pointerdown', wake, {
306
+ capture: true,
307
+ passive: true,
308
+ });
309
+ window.addEventListener('keydown', wake, { capture: true, passive: true });
310
+ return () => {
311
+ window.removeEventListener('pointerdown', wake, { capture: true });
312
+ window.removeEventListener('keydown', wake, { capture: true });
313
+ ctxAndGain.suspend().catch(() => { });
314
+ };
315
+ }, [ctxAndGain, _experimentalKeepAudioContextAlive]);
249
316
  const audioContextValue = (0, react_1.useMemo)(() => {
250
317
  var _a, _b;
251
318
  return {
@@ -262,6 +329,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
262
329
  suspend,
263
330
  getIsResumingAudioContext,
264
331
  unscheduleAudioNode,
332
+ _experimentalKeepAudioContextAlive,
265
333
  };
266
334
  }, [
267
335
  ctxAndGain,
@@ -272,6 +340,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
272
340
  suspend,
273
341
  getIsResumingAudioContext,
274
342
  unscheduleAudioNode,
343
+ _experimentalKeepAudioContextAlive,
275
344
  ]);
276
345
  return (jsx_runtime_1.jsx(exports.SharedAudioContext.Provider, { value: audioContextValue, children: children }));
277
346
  };
@@ -28,19 +28,23 @@ const waitUntilActuallyResumed = (audioContext, logLevel, signal) => {
28
28
  resolve(result);
29
29
  };
30
30
  onAbort = () => finish('cancelled');
31
+ const hasAudiblyStarted = (startPerformanceTime) => {
32
+ const outputTimestamp = audioContext.getOutputTimestamp();
33
+ return (startPerformanceTime !== undefined &&
34
+ outputTimestamp.performanceTime !== undefined &&
35
+ outputTimestamp.performanceTime > startPerformanceTime &&
36
+ outputTimestamp.contextTime !== undefined &&
37
+ outputTimestamp.contextTime > startCurrentTime);
38
+ };
31
39
  const check = () => {
32
- var _a;
33
- var _b;
40
+ var _a, _b;
41
+ var _c;
34
42
  animationFrame = null;
35
43
  const { currentTime } = audioContext;
36
44
  const outputTimestamp = audioContext.getOutputTimestamp();
37
45
  const elapsedWallClock = performance.now() - startWallClock;
38
- if (startOutputPerformanceTime !== undefined &&
39
- outputTimestamp.performanceTime !== undefined &&
40
- outputTimestamp.performanceTime > startOutputPerformanceTime &&
41
- outputTimestamp.contextTime !== undefined &&
42
- outputTimestamp.contextTime > startCurrentTime) {
43
- log_js_1.Log.verbose({ logLevel, tag: 'audio' }, `waitUntilActuallyResumed: getOutputTimestamp.performanceTime advanced from ${startOutputPerformanceTime.toFixed(6)} to ${outputTimestamp.performanceTime.toFixed(6)} after ${elapsedWallClock.toFixed(1)}ms. currentTime=${currentTime.toFixed(6)} (advanced by ${(currentTime - startCurrentTime).toFixed(6)}), getOutputTimestamp.performanceTime=${(_b = (_a = outputTimestamp.performanceTime) === null || _a === void 0 ? void 0 : _a.toFixed(1)) !== null && _b !== void 0 ? _b : 'undefined'}`);
46
+ if (hasAudiblyStarted(startOutputPerformanceTime)) {
47
+ log_js_1.Log.verbose({ logLevel, tag: 'audio' }, `waitUntilActuallyResumed: getOutputTimestamp.performanceTime advanced from ${startOutputPerformanceTime.toFixed(6)} to ${(_a = outputTimestamp.performanceTime) === null || _a === void 0 ? void 0 : _a.toFixed(6)} after ${elapsedWallClock.toFixed(1)}ms. currentTime=${currentTime.toFixed(6)} (advanced by ${(currentTime - startCurrentTime).toFixed(6)}), getOutputTimestamp.performanceTime=${(_c = (_b = outputTimestamp.performanceTime) === null || _b === void 0 ? void 0 : _b.toFixed(1)) !== null && _c !== void 0 ? _c : 'undefined'}`);
44
48
  finish('resumed');
45
49
  return;
46
50
  }
@@ -52,6 +56,15 @@ const waitUntilActuallyResumed = (audioContext, logLevel, signal) => {
52
56
  }
53
57
  signal.addEventListener('abort', onAbort, { once: true });
54
58
  timeout = setTimeout(() => {
59
+ // The success check is scheduled with requestAnimationFrame, which can
60
+ // be starved while the main thread is busy. Re-check once before
61
+ // declaring failure so a resume that actually completed is not
62
+ // reported as failed - the Player would otherwise mute itself even
63
+ // though playback was started by a user gesture and audio is running.
64
+ if (hasAudiblyStarted(startOutputPerformanceTime)) {
65
+ finish('resumed');
66
+ return;
67
+ }
55
68
  log_js_1.Log.warn({ logLevel, tag: 'audio' }, 'WARNING: You enabled autoPlay on an unmuted <Player /> and the browser did not allow the video to be started. Remotion muted the <Player /> so it can play. To properly handle this, either set the `muted` prop or remove the `autoPlay` prop');
56
69
  finish('failed');
57
70
  }, RESUME_WAIT_TIMEOUT);
@@ -738,6 +738,7 @@ export declare const Internals: {
738
738
  readonly videoEnabled: boolean;
739
739
  readonly audioEnabled: boolean;
740
740
  readonly frameState: Record<string, number> | null;
741
+ readonly _experimentalKeepAudioContextAlive: boolean;
741
742
  }>;
742
743
  readonly CompositionManagerProvider: ({ children, onlyRenderComposition, currentCompositionMetadata, initialCompositions, initialCanvasContent, }: {
743
744
  readonly children: import("react").ReactNode;
@@ -816,12 +817,14 @@ export declare const Internals: {
816
817
  suspend: () => Promise<void>;
817
818
  getIsResumingAudioContext: () => Promise<import("./audio/wait-until-actually-resumed.js").AudioContextResumeResult> | null;
818
819
  unscheduleAudioNode: (node: AudioBufferSourceNode) => void;
820
+ _experimentalKeepAudioContextAlive: boolean;
819
821
  } | null>;
820
822
  readonly SharedAudioContextProvider: import("react").FC<{
821
823
  readonly children: import("react").ReactNode;
822
824
  readonly audioLatencyHint: AudioContextLatencyCategory;
823
825
  readonly audioEnabled: boolean;
824
826
  readonly previewSampleRate: number | null;
827
+ readonly _experimentalKeepAudioContextAlive: boolean;
825
828
  }>;
826
829
  readonly SharedAudioTagsContext: import("react").Context<{
827
830
  registerAudio: (options: {
@@ -1214,10 +1217,11 @@ export declare const Internals: {
1214
1217
  status: CanUpdateSequencePropStatusKeyframed;
1215
1218
  }) => string | number | readonly number[] | null;
1216
1219
  readonly makeStaticDragOverride: (value: unknown) => DragOverrideValue;
1217
- readonly makeKeyframedDragOverride: ({ status, frame, value, }: {
1220
+ readonly makeKeyframedDragOverride: ({ status, frame, value, defaultEasing, }: {
1218
1221
  status: CanUpdateSequencePropStatusKeyframed;
1219
1222
  frame: number;
1220
1223
  value: unknown;
1224
+ defaultEasing?: CanUpdateSequencePropStatusEasing | undefined;
1221
1225
  }) => DragOverrideValue;
1222
1226
  readonly resolveDragOverrideValue: ({ dragOverrideValue, frame, }: {
1223
1227
  dragOverrideValue: DragOverrideValue | undefined;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assertValidInterpolateEasingOption = assertValidInterpolateEasingOption;
4
4
  exports.assertValidInterpolatePosterizeOption = assertValidInterpolatePosterizeOption;
5
5
  exports.interpolate = interpolate;
6
+ const easing_js_1 = require("./easing.js");
6
7
  const normalize_number_js_1 = require("./normalize-number.js");
7
8
  const angleUnits = new Set(['deg', 'rad', 'grad', 'turn']);
8
9
  const lengthUnits = new Set([
@@ -71,11 +72,13 @@ const transformOriginCenter = { value: 50, unit: '%' };
71
72
  const stringifyNumber = (value) => {
72
73
  return String((0, normalize_number_js_1.normalizeNumber)(value));
73
74
  };
75
+ class UnsupportedStringInterpolationValueError extends TypeError {
76
+ }
74
77
  const parseStringInterpolationComponent = (component, value) => {
75
78
  var _a;
76
79
  const match = cssNumberRegex.exec(component);
77
80
  if (match === null) {
78
- throw new TypeError(`Cannot interpolate "${value}" because "${component}" is not a supported scale, translate, or rotate value`);
81
+ throw new UnsupportedStringInterpolationValueError(`Cannot interpolate "${value}" because "${component}" is not a supported scale, translate, or rotate value`);
79
82
  }
80
83
  const unit = (_a = match[2]) !== null && _a !== void 0 ? _a : null;
81
84
  const numberValue = Number(match[1]);
@@ -563,6 +566,57 @@ const interpolateString = ({ input, inputRange, outputRange, options, }) => {
563
566
  axisRotation: hasAxisRotation,
564
567
  });
565
568
  };
569
+ const interpolateDiscreteString = ({ input, inputRange, outputRange, options, }) => {
570
+ if (inputRange.length === 1) {
571
+ return outputRange[0];
572
+ }
573
+ for (let segmentIndex = 0; segmentIndex < inputRange.length - 1; segmentIndex++) {
574
+ if (resolveEasingForSegment({
575
+ easing: options === null || options === void 0 ? void 0 : options.easing,
576
+ segmentIndex,
577
+ }) !== easing_js_1.Easing.step1) {
578
+ throw new TypeError('Non-numeric strings can only be interpolated using Easing.step1');
579
+ }
580
+ }
581
+ const posterizedInput = (options === null || options === void 0 ? void 0 : options.posterize) === undefined
582
+ ? input
583
+ : Math.floor(input / options.posterize) * options.posterize;
584
+ const inputMin = inputRange[0];
585
+ const inputMax = inputRange[inputRange.length - 1];
586
+ let resolvedInput = posterizedInput;
587
+ if (resolvedInput < inputMin) {
588
+ if ((options === null || options === void 0 ? void 0 : options.extrapolateLeft) === 'identity') {
589
+ throw new TypeError('extrapolateLeft: "identity" is not supported for non-numeric strings');
590
+ }
591
+ if ((options === null || options === void 0 ? void 0 : options.extrapolateLeft) === 'wrap') {
592
+ const wrapRange = inputMax - inputMin;
593
+ resolvedInput =
594
+ ((((resolvedInput - inputMin) % wrapRange) + wrapRange) % wrapRange) +
595
+ inputMin;
596
+ }
597
+ else {
598
+ return outputRange[0];
599
+ }
600
+ }
601
+ if (resolvedInput > inputMax) {
602
+ if ((options === null || options === void 0 ? void 0 : options.extrapolateRight) === 'identity') {
603
+ throw new TypeError('extrapolateRight: "identity" is not supported for non-numeric strings');
604
+ }
605
+ if ((options === null || options === void 0 ? void 0 : options.extrapolateRight) === 'wrap') {
606
+ const wrapRange = inputMax - inputMin;
607
+ resolvedInput =
608
+ ((((resolvedInput - inputMin) % wrapRange) + wrapRange) % wrapRange) +
609
+ inputMin;
610
+ }
611
+ else {
612
+ return outputRange[outputRange.length - 1];
613
+ }
614
+ }
615
+ const range = findRange(resolvedInput, inputRange);
616
+ return resolvedInput >= inputRange[range + 1]
617
+ ? outputRange[range + 1]
618
+ : outputRange[range];
619
+ };
566
620
  const validateTupleOutputRange = (outputRange) => {
567
621
  var _a;
568
622
  const dimensions = (_a = outputRange[0]) === null || _a === void 0 ? void 0 : _a.length;
@@ -681,7 +735,32 @@ function interpolate(input, inputRange, outputRange, options) {
681
735
  if (!outputRange.every((output) => typeof output === 'string' || typeof output === 'number')) {
682
736
  throw new TypeError('outputRange must contain only numbers, or supported scale, translate, and rotate strings');
683
737
  }
684
- return interpolateString({ input, inputRange, outputRange, options });
738
+ try {
739
+ return interpolateString({ input, inputRange, outputRange, options });
740
+ }
741
+ catch (error) {
742
+ if (!outputRange.every((output) => typeof output === 'string')) {
743
+ throw error;
744
+ }
745
+ const hasNonNumericString = outputRange.some((output) => {
746
+ try {
747
+ parseStringInterpolationValue(output);
748
+ return false;
749
+ }
750
+ catch (parseError) {
751
+ return parseError instanceof UnsupportedStringInterpolationValueError;
752
+ }
753
+ });
754
+ if (!hasNonNumericString) {
755
+ throw error;
756
+ }
757
+ return interpolateDiscreteString({
758
+ input,
759
+ inputRange,
760
+ outputRange,
761
+ options,
762
+ });
763
+ }
685
764
  }
686
765
  if (outputRange.every((output) => Array.isArray(output))) {
687
766
  return interpolateTuple({ input, inputRange, outputRange, options });
@@ -90,10 +90,11 @@ export type GetEffectPropStatuses = (nodePath: SequencePropsSubscriptionKey, eff
90
90
  export type GetDragOverrides = (nodePath: SequencePropsSubscriptionKey) => DragOverrides[string];
91
91
  export type GetEffectDragOverrides = (nodePath: SequencePropsSubscriptionKey, effectIndex: number) => Record<string, DragOverrideValue>;
92
92
  export declare const makeStaticDragOverride: (value: unknown) => DragOverrideValue;
93
- export declare const makeKeyframedDragOverride: ({ status, frame, value, }: {
93
+ export declare const makeKeyframedDragOverride: ({ status, frame, value, defaultEasing, }: {
94
94
  status: CanUpdateSequencePropStatusKeyframed;
95
95
  frame: number;
96
96
  value: unknown;
97
+ defaultEasing?: CanUpdateSequencePropStatusEasing | undefined;
97
98
  }) => DragOverrideValue;
98
99
  export declare const getStaticDragOverrideValue: (dragOverrideValue: DragOverrideValue | undefined) => unknown;
99
100
  export declare const isKeyframedStatus: (status: CanUpdateSequencePropStatus | null) => status is CanUpdateSequencePropStatusKeyframed;
@@ -19,7 +19,7 @@ const makeStaticDragOverride = (value) => {
19
19
  return { type: 'static', value };
20
20
  };
21
21
  exports.makeStaticDragOverride = makeStaticDragOverride;
22
- const makeKeyframedDragOverride = ({ status, frame, value, }) => {
22
+ const makeKeyframedDragOverride = ({ status, frame, value, defaultEasing = exports.DEFAULT_LINEAR_EASING, }) => {
23
23
  const existingIndex = status.keyframes.findIndex((keyframe) => keyframe.frame === frame);
24
24
  const keyframes = existingIndex === -1
25
25
  ? [...status.keyframes, { frame, value }].sort((first, second) => first.frame - second.frame)
@@ -33,12 +33,12 @@ const makeKeyframedDragOverride = ({ status, frame, value, }) => {
33
33
  keyframeCount: keyframes.length,
34
34
  });
35
35
  const easingToDuplicate = easingIndexToDuplicate === null
36
- ? exports.DEFAULT_LINEAR_EASING
36
+ ? defaultEasing
37
37
  : easing[easingIndexToDuplicate];
38
38
  easing.splice(insertedKeyframeIndex, 0, easingToDuplicate);
39
39
  }
40
40
  while (easing.length < keyframes.length - 1) {
41
- easing.push(exports.DEFAULT_LINEAR_EASING);
41
+ easing.push(defaultEasing);
42
42
  }
43
43
  if (easing.length > keyframes.length - 1) {
44
44
  easing.length = keyframes.length - 1;
@@ -3,4 +3,4 @@
3
3
  * @see [Documentation](https://remotion.dev/docs/version)
4
4
  * @returns {string} The current version of the remotion package
5
5
  */
6
- export declare const VERSION = "4.0.508";
6
+ export declare const VERSION = "4.0.509";
@@ -7,4 +7,4 @@ exports.VERSION = void 0;
7
7
  * @see [Documentation](https://remotion.dev/docs/version)
8
8
  * @returns {string} The current version of the remotion package
9
9
  */
10
- exports.VERSION = '4.0.508';
10
+ exports.VERSION = '4.0.509';