remotion 4.0.489 → 4.0.490

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.
@@ -83,6 +83,7 @@ export declare const htmlInCanvasSchema: {
83
83
  readonly step: 0.01;
84
84
  readonly default: 1;
85
85
  readonly description: "Scale";
86
+ readonly defaultKeyframeOutput: "perceptual-scale";
86
87
  };
87
88
  readonly 'style.rotate': {
88
89
  readonly type: "rotation-css";
package/dist/cjs/Img.d.ts CHANGED
@@ -36,6 +36,7 @@ export declare const imgSchema: {
36
36
  readonly step: 0.01;
37
37
  readonly default: 1;
38
38
  readonly description: "Scale";
39
+ readonly defaultKeyframeOutput: "perceptual-scale";
39
40
  };
40
41
  readonly 'style.rotate': {
41
42
  readonly type: "rotation-css";
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
- import type { SequenceControls } from './CompositionManager.js';
3
2
  import { type InteractivitySchema } from './interactivity-schema.js';
4
3
  import type { AbsoluteFillLayout, SequenceProps } from './Sequence.js';
4
+ import { type WithInteractivitySchemaOptions } from './with-interactivity-schema.js';
5
5
  type InteractiveHtmlTag = 'a' | 'article' | 'aside' | 'button' | 'code' | 'div' | 'em' | 'footer' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'header' | 'label' | 'li' | 'main' | 'nav' | 'ol' | 'p' | 'pre' | 'section' | 'small' | 'span' | 'strong' | 'ul';
6
6
  type InteractiveSvgTag = 'circle' | 'ellipse' | 'g' | 'line' | 'path' | 'rect' | 'svg' | 'text';
7
7
  type InteractiveTag = InteractiveHtmlTag | InteractiveSvgTag;
@@ -17,6 +17,7 @@ type InteractiveSequenceProps = InteractiveBaseProps & {
17
17
  };
18
18
  type InteractiveElementProps<Tag extends InteractiveTag> = Omit<React.ComponentPropsWithoutRef<Tag>, keyof InteractiveSequenceProps> & InteractiveSequenceProps;
19
19
  type InteractiveElementComponent<Tag extends InteractiveTag> = React.ComponentType<InteractiveElementProps<Tag> & React.RefAttributes<ElementForTag<Tag>>>;
20
+ type RemotionComponentIdentityPackage = 'remotion' | `@remotion/${string}`;
20
21
  /**
21
22
  * @description HTML and SVG elements that are registered in the Remotion Studio timeline and can be visually edited.
22
23
  */
@@ -71,6 +72,7 @@ export declare const Interactive: {
71
72
  readonly step: 0.01;
72
73
  readonly default: 1;
73
74
  readonly description: "Scale";
75
+ readonly defaultKeyframeOutput: "perceptual-scale";
74
76
  };
75
77
  readonly 'style.rotate': {
76
78
  readonly type: "rotation-css";
@@ -242,6 +244,7 @@ export declare const Interactive: {
242
244
  readonly step: 0.01;
243
245
  readonly default: 1;
244
246
  readonly description: "Scale";
247
+ readonly defaultKeyframeOutput: "perceptual-scale";
245
248
  };
246
249
  readonly 'style.rotate': {
247
250
  readonly type: "rotation-css";
@@ -284,15 +287,11 @@ export declare const Interactive: {
284
287
  };
285
288
  };
286
289
  };
287
- withSchema: <S extends InteractivitySchema, Props extends object>({ Component, componentName, componentIdentity, schema, supportsEffects, }: {
288
- Component: React.ComponentType<Props & {
289
- readonly controls: SequenceControls | undefined;
290
- }>;
291
- componentName: string;
292
- componentIdentity: string | null;
293
- schema: S;
294
- supportsEffects: boolean;
295
- }) => React.ComponentType<Props>;
290
+ withSchema: <S extends InteractivitySchema, Props extends object>(options: WithInteractivitySchemaOptions<S, Props>) => React.ComponentType<Props>;
291
+ _internalMakeRemotionComponentIdentity: ({ packageName, componentName, }: {
292
+ readonly packageName: RemotionComponentIdentityPackage;
293
+ readonly componentName: string;
294
+ }) => string;
296
295
  A: InteractiveElementComponent<"a">;
297
296
  Article: InteractiveElementComponent<"article">;
298
297
  Aside: InteractiveElementComponent<"aside">;
@@ -40,6 +40,21 @@ const enable_sequence_stack_traces_js_1 = require("./enable-sequence-stack-trace
40
40
  const interactivity_schema_js_1 = require("./interactivity-schema.js");
41
41
  const Sequence_js_1 = require("./Sequence.js");
42
42
  const with_interactivity_schema_js_1 = require("./with-interactivity-schema.js");
43
+ const sourcePathToIdentityPrefix = (packageName) => {
44
+ if (packageName === 'remotion') {
45
+ return 'dev.remotion.remotion';
46
+ }
47
+ if (packageName.startsWith('@remotion/')) {
48
+ const normalizedPackageName = packageName
49
+ .slice('@remotion/'.length)
50
+ .replace(/-([a-z])/g, (_, char) => char.toUpperCase());
51
+ return `dev.remotion.${normalizedPackageName}`;
52
+ }
53
+ throw new Error(`Unsupported Remotion package name: ${packageName}`);
54
+ };
55
+ const makeRemotionComponentIdentity = ({ packageName, componentName, }) => {
56
+ return `${sourcePathToIdentityPrefix(packageName)}.${componentName}`;
57
+ };
43
58
  const interactiveElementSchema = {
44
59
  ...interactivity_schema_js_1.baseSchema,
45
60
  ...interactivity_schema_js_1.transformSchema,
@@ -54,6 +69,11 @@ const setRef = (ref, value) => {
54
69
  ref.current = value;
55
70
  }
56
71
  };
72
+ const withSchema = (options) => {
73
+ const Wrapped = (0, with_interactivity_schema_js_1.withInteractivitySchema)(options);
74
+ (0, enable_sequence_stack_traces_js_1.addSequenceStackTraces)(Wrapped);
75
+ return Wrapped;
76
+ };
57
77
  const makeInteractiveElement = (tag, displayName) => {
58
78
  const Inner = (0, react_1.forwardRef)((propsWithControls, ref) => {
59
79
  const { durationInFrames, from, trimBefore, freeze, hidden, name, showInTimeline, stack, controls, ...props } = propsWithControls;
@@ -68,15 +88,17 @@ const makeInteractiveElement = (tag, displayName) => {
68
88
  }) }));
69
89
  });
70
90
  Inner.displayName = displayName;
71
- const Wrapped = (0, with_interactivity_schema_js_1.withInteractivitySchema)({
91
+ const Wrapped = withSchema({
72
92
  Component: Inner,
73
93
  componentName: displayName,
74
- componentIdentity: `dev.remotion.remotion.${displayName.slice(1, -1)}`,
94
+ componentIdentity: makeRemotionComponentIdentity({
95
+ packageName: 'remotion',
96
+ componentName: displayName.slice(1, -1),
97
+ }),
75
98
  schema: interactiveElementSchema,
76
99
  supportsEffects: false,
77
100
  });
78
101
  Wrapped.displayName = displayName;
79
- (0, enable_sequence_stack_traces_js_1.addSequenceStackTraces)(Wrapped);
80
102
  return Wrapped;
81
103
  };
82
104
  /**
@@ -88,7 +110,8 @@ exports.Interactive = {
88
110
  textSchema: interactivity_schema_js_1.textSchema,
89
111
  premountSchema: interactivity_schema_js_1.premountSchema,
90
112
  sequenceSchema: interactivity_schema_js_1.sequenceSchema,
91
- withSchema: with_interactivity_schema_js_1.withInteractivitySchema,
113
+ withSchema,
114
+ _internalMakeRemotionComponentIdentity: makeRemotionComponentIdentity,
92
115
  A: makeInteractiveElement('a', '<Interactive.A>'),
93
116
  Article: makeInteractiveElement('article', '<Interactive.Article>'),
94
117
  Aside: makeInteractiveElement('aside', '<Interactive.Aside>'),
@@ -20,6 +20,7 @@ export declare const canvasImageSchema: {
20
20
  readonly step: 0.01;
21
21
  readonly default: 1;
22
22
  readonly description: "Scale";
23
+ readonly defaultKeyframeOutput: "perceptual-scale";
23
24
  };
24
25
  readonly 'style.rotate': {
25
26
  readonly type: "rotation-css";
@@ -1,3 +1,4 @@
1
+ import type { LogLevel } from './log.js';
1
2
  import type { RemotionEnvironment } from './remotion-environment-context.js';
2
3
  export type DelayRenderScope = {
3
4
  remotion_renderReady: boolean;
@@ -26,21 +27,24 @@ export type DelayRenderOptions = {
26
27
  * This allows useDelayRender to control its own environment source.
27
28
  * @private
28
29
  */
29
- export declare const delayRenderInternal: ({ scope, environment, label, options, }: {
30
+ type DelayRenderInternalOptions = {
30
31
  scope: DelayRenderScope;
31
32
  environment: RemotionEnvironment;
32
33
  label: string | null;
33
34
  options: DelayRenderOptions;
34
- }) => number;
35
+ };
36
+ export declare const delayRenderInternal: ({ scope, environment, label, options, }: DelayRenderInternalOptions) => number;
35
37
  export declare const delayRender: (label?: string | undefined, options?: DelayRenderOptions | undefined) => number;
36
38
  /**
37
39
  * Internal function that accepts environment as parameter.
38
40
  * @private
39
41
  */
40
- export declare const continueRenderInternal: ({ scope, handle, environment, logLevel, }: {
42
+ type ContinueRenderInternalOptions = {
41
43
  scope: DelayRenderScope;
42
44
  handle: number;
43
45
  environment: RemotionEnvironment;
44
- logLevel: "error" | "info" | "trace" | "verbose" | "warn";
45
- }) => void;
46
+ logLevel: LogLevel;
47
+ };
48
+ export declare const continueRenderInternal: ({ scope, handle, environment, logLevel, }: ContinueRenderInternalOptions) => void;
46
49
  export declare const continueRender: (handle: number) => void;
50
+ export {};
@@ -17,11 +17,6 @@ exports.DELAY_RENDER_RETRIES_LEFT = 'Retries left: ';
17
17
  exports.DELAY_RENDER_RETRY_TOKEN = '- Rendering the frame will be retried.';
18
18
  exports.DELAY_RENDER_CLEAR_TOKEN = 'handle was cleared after';
19
19
  const defaultTimeout = 30000;
20
- /**
21
- * Internal function that accepts environment as parameter.
22
- * This allows useDelayRender to control its own environment source.
23
- * @private
24
- */
25
20
  const delayRenderInternal = ({ scope, environment, label, options, }) => {
26
21
  var _a;
27
22
  var _b, _c, _d, _e;
@@ -80,10 +75,6 @@ const delayRender = (label, options) => {
80
75
  });
81
76
  };
82
77
  exports.delayRender = delayRender;
83
- /**
84
- * Internal function that accepts environment as parameter.
85
- * @private
86
- */
87
78
  const continueRenderInternal = ({ scope, handle, environment, logLevel, }) => {
88
79
  if (typeof handle === 'undefined') {
89
80
  throw new TypeError('The continueRender() method must be called with a parameter that is the return value of delayRender(). No value was passed.');
@@ -92,28 +83,22 @@ const continueRenderInternal = ({ scope, handle, environment, logLevel, }) => {
92
83
  throw new TypeError('The parameter passed into continueRender() must be the return value of delayRender() which is a number. Got: ' +
93
84
  JSON.stringify(handle));
94
85
  }
95
- scope.remotion_delayRenderHandles = scope.remotion_delayRenderHandles.filter((h) => {
96
- if (h === handle) {
97
- if (environment.isRendering && scope !== undefined) {
98
- if (!scope.remotion_delayRenderTimeouts[handle]) {
99
- return false;
100
- }
101
- const { label, startTime, timeout } = scope.remotion_delayRenderTimeouts[handle];
102
- clearTimeout(timeout);
103
- const message = [
104
- label ? `"${label}"` : 'A handle',
105
- exports.DELAY_RENDER_CLEAR_TOKEN,
106
- `${Date.now() - startTime}ms`,
107
- ]
108
- .filter(truthy_js_1.truthy)
109
- .join(' ');
110
- log_js_1.Log.verbose({ logLevel, tag: 'delayRender()' }, message);
111
- delete scope.remotion_delayRenderTimeouts[handle];
112
- }
113
- return false;
114
- }
115
- return true;
116
- });
86
+ const handleExists = scope.remotion_delayRenderHandles.includes(handle);
87
+ const timeoutEntry = scope.remotion_delayRenderTimeouts[handle];
88
+ if (handleExists && environment.isRendering && timeoutEntry) {
89
+ const { label, startTime, timeout } = timeoutEntry;
90
+ clearTimeout(timeout);
91
+ const message = [
92
+ label ? `"${label}"` : 'A handle',
93
+ exports.DELAY_RENDER_CLEAR_TOKEN,
94
+ `${Date.now() - startTime}ms`,
95
+ ]
96
+ .filter(truthy_js_1.truthy)
97
+ .join(' ');
98
+ log_js_1.Log.verbose({ logLevel, tag: 'delayRender()' }, message);
99
+ delete scope.remotion_delayRenderTimeouts[handle];
100
+ }
101
+ scope.remotion_delayRenderHandles = scope.remotion_delayRenderHandles.filter((h) => h !== handle);
117
102
  if (scope.remotion_delayRenderHandles.length === 0) {
118
103
  scope.remotion_renderReady = true;
119
104
  }
@@ -32,6 +32,7 @@ export declare const solidSchema: {
32
32
  readonly step: 0.01;
33
33
  readonly default: 1;
34
34
  readonly description: "Scale";
35
+ readonly defaultKeyframeOutput: "perceptual-scale";
35
36
  };
36
37
  readonly 'style.rotate': {
37
38
  readonly type: "rotation-css";
@@ -28,7 +28,7 @@ const getFlatSchemaWithAllKeys = (schema) => {
28
28
  const out = {};
29
29
  const addKey = (key, field) => {
30
30
  if (key in out) {
31
- throw new Error(`Duplicate key "${key}" in schema: discriminated union variants must not share keys`);
31
+ return;
32
32
  }
33
33
  out[key] = field;
34
34
  };
@@ -132,7 +132,7 @@ export * from './internals.js';
132
132
  export { interpolateColors, type InterpolateColorsOptions, } from './interpolate-colors.js';
133
133
  export { LogLevel } from './log.js';
134
134
  export { Loop } from './loop/index.js';
135
- export { assertValidInterpolateEasingOption, assertValidInterpolatePosterizeOption, EasingFunction, ExtrapolateType, interpolate, InterpolateOptions, random, RandomSeed, } from './no-react';
135
+ export { assertValidInterpolateEasingOption, assertValidInterpolatePosterizeOption, EasingFunction, ExtrapolateType, interpolate, InterpolateOutputOption, InterpolateOptions, random, RandomSeed, } from './no-react';
136
136
  export type { NonceHistory } from './nonce.js';
137
137
  export { prefetch, PrefetchOnProgress } from './prefetch.js';
138
138
  export { registerRoot } from './register-root.js';
@@ -1,3 +1,4 @@
1
+ import type { InterpolateOutputOption } from './interpolate.js';
1
2
  export type HiddenFieldSchema = {
2
3
  type: 'hidden';
3
4
  keyframable?: boolean;
@@ -11,6 +12,7 @@ export type NumberFieldSchema = {
11
12
  description?: string;
12
13
  hiddenFromList: boolean;
13
14
  keyframable?: boolean;
15
+ defaultKeyframeOutput?: InterpolateOutputOption;
14
16
  };
15
17
  export type BooleanFieldSchema = {
16
18
  type: 'boolean';
@@ -56,6 +58,7 @@ export type ScaleFieldSchema = {
56
58
  default: number | string | undefined;
57
59
  description?: string;
58
60
  keyframable?: boolean;
61
+ defaultKeyframeOutput?: InterpolateOutputOption;
59
62
  };
60
63
  export type UvCoordinateFieldSchema = {
61
64
  type: 'uv-coordinate';
@@ -101,7 +104,7 @@ export type EnumFieldSchema = {
101
104
  variants: Record<string, InteractivitySchema>;
102
105
  keyframable?: boolean;
103
106
  };
104
- export type NumberArrayItemSchema = Omit<NumberFieldSchema, 'default' | 'description' | 'hiddenFromList' | 'keyframable'>;
107
+ export type NumberArrayItemSchema = Omit<NumberFieldSchema, 'default' | 'description' | 'hiddenFromList' | 'keyframable' | 'defaultKeyframeOutput'>;
105
108
  export type BooleanArrayItemSchema = Omit<BooleanFieldSchema, 'default' | 'description' | 'keyframable'>;
106
109
  export type RotationCssArrayItemSchema = Omit<RotationCssFieldSchema, 'default' | 'description' | 'keyframable'>;
107
110
  export type RotationDegreesArrayItemSchema = Omit<RotationDegreesFieldSchema, 'default' | 'description' | 'keyframable'>;
@@ -148,6 +151,7 @@ export declare const transformSchema: {
148
151
  readonly step: 0.01;
149
152
  readonly default: 1;
150
153
  readonly description: "Scale";
154
+ readonly defaultKeyframeOutput: "perceptual-scale";
151
155
  };
152
156
  readonly 'style.rotate': {
153
157
  readonly type: "rotation-css";
@@ -184,6 +188,7 @@ export declare const sequenceVisualStyleSchema: {
184
188
  readonly step: 0.01;
185
189
  readonly default: 1;
186
190
  readonly description: "Scale";
191
+ readonly defaultKeyframeOutput: "perceptual-scale";
187
192
  };
188
193
  readonly 'style.rotate': {
189
194
  readonly type: "rotation-css";
@@ -351,6 +356,7 @@ export declare const sequenceStyleSchema: {
351
356
  readonly step: 0.01;
352
357
  readonly default: 1;
353
358
  readonly description: "Scale";
359
+ readonly defaultKeyframeOutput: "perceptual-scale";
354
360
  };
355
361
  readonly 'style.rotate': {
356
362
  readonly type: "rotation-css";
@@ -505,6 +511,7 @@ export declare const sequenceSchema: {
505
511
  readonly step: 0.01;
506
512
  readonly default: 1;
507
513
  readonly description: "Scale";
514
+ readonly defaultKeyframeOutput: "perceptual-scale";
508
515
  };
509
516
  readonly 'style.rotate': {
510
517
  readonly type: "rotation-css";
@@ -620,6 +627,7 @@ export declare const sequenceSchemaWithoutFrom: {
620
627
  readonly step: 0.01;
621
628
  readonly default: 1;
622
629
  readonly description: "Scale";
630
+ readonly defaultKeyframeOutput: "perceptual-scale";
623
631
  };
624
632
  readonly 'style.rotate': {
625
633
  readonly type: "rotation-css";
@@ -20,6 +20,7 @@ exports.transformSchema = {
20
20
  step: 0.01,
21
21
  default: 1,
22
22
  description: 'Scale',
23
+ defaultKeyframeOutput: 'perceptual-scale',
23
24
  },
24
25
  'style.rotate': {
25
26
  type: 'rotation-css',
@@ -162,6 +162,7 @@ export declare const Internals: {
162
162
  readonly step: 0.01;
163
163
  readonly default: 1;
164
164
  readonly description: "Scale";
165
+ readonly defaultKeyframeOutput: "perceptual-scale";
165
166
  };
166
167
  readonly 'style.rotate': {
167
168
  readonly type: "rotation-css";
@@ -224,6 +225,7 @@ export declare const Internals: {
224
225
  readonly step: 0.01;
225
226
  readonly default: 1;
226
227
  readonly description: "Scale";
228
+ readonly defaultKeyframeOutput: "perceptual-scale";
227
229
  };
228
230
  readonly 'style.rotate': {
229
231
  readonly type: "rotation-css";
@@ -281,6 +283,7 @@ export declare const Internals: {
281
283
  readonly step: 0.01;
282
284
  readonly default: 1;
283
285
  readonly description: "Scale";
286
+ readonly defaultKeyframeOutput: "perceptual-scale";
284
287
  };
285
288
  readonly 'style.rotate': {
286
289
  readonly type: "rotation-css";
@@ -417,6 +420,7 @@ export declare const Internals: {
417
420
  readonly step: 0.01;
418
421
  readonly default: 1;
419
422
  readonly description: "Scale";
423
+ readonly defaultKeyframeOutput: "perceptual-scale";
420
424
  };
421
425
  readonly 'style.rotate': {
422
426
  readonly type: "rotation-css";
@@ -58,6 +58,7 @@ const interpolateKeyframedStatus = ({ frame, forceSpringAllowTail, status, }) =>
58
58
  easing: easing.map((e) => easingToFn({ easing: e, forceSpringAllowTail })),
59
59
  extrapolateLeft: clamping.left,
60
60
  extrapolateRight: clamping.right,
61
+ output: status.output,
61
62
  posterize: status.posterize,
62
63
  });
63
64
  }
@@ -1,4 +1,5 @@
1
1
  export type ExtrapolateType = 'extend' | 'identity' | 'clamp' | 'wrap';
2
+ export type InterpolateOutputOption = 'linear' | 'perceptual-scale';
2
3
  /**
3
4
  * @description This function allows you to map a range of values to another with a concise syntax
4
5
  * @see [Documentation](https://www.remotion.dev/docs/interpolate)
@@ -10,6 +11,7 @@ export type InterpolateOptions = Partial<{
10
11
  easing: EasingFunction | readonly EasingFunction[];
11
12
  extrapolateLeft: ExtrapolateType;
12
13
  extrapolateRight: ExtrapolateType;
14
+ output: InterpolateOutputOption;
13
15
  posterize: number;
14
16
  }>;
15
17
  type NumericTuple = readonly [number, ...number[]];
@@ -269,8 +269,20 @@ const serializeStringInterpolationValue = ({ kind, values, units, dimensions, })
269
269
  .map((value, index) => `${stringifyNumber(value)}${units[index]}`)
270
270
  .join(' ');
271
271
  };
272
+ const toSignedArea = (scale) => {
273
+ if (scale === 0) {
274
+ return 0;
275
+ }
276
+ return Math.sign(scale) * scale * scale;
277
+ };
278
+ const fromSignedArea = (area) => {
279
+ if (area === 0) {
280
+ return 0;
281
+ }
282
+ return Math.sign(area) * Math.sqrt(Math.abs(area));
283
+ };
272
284
  function interpolateFunction(input, inputRange, outputRange, options) {
273
- const { extrapolateLeft, extrapolateRight, easing } = options;
285
+ const { extrapolateLeft, extrapolateRight, easing, output } = options;
274
286
  let result = input;
275
287
  const [inputMin, inputMax] = inputRange;
276
288
  const [outputMin, outputMax] = outputRange;
@@ -312,7 +324,14 @@ function interpolateFunction(input, inputRange, outputRange, options) {
312
324
  // Easing
313
325
  result = easing(result);
314
326
  // Output Range
315
- result = result * (outputMax - outputMin) + outputMin;
327
+ if (output === 'perceptual-scale') {
328
+ const signedAreaMin = toSignedArea(outputMin);
329
+ const signedAreaMax = toSignedArea(outputMax);
330
+ result = fromSignedArea(result * (signedAreaMax - signedAreaMin) + signedAreaMin);
331
+ }
332
+ else {
333
+ result = result * (outputMax - outputMin) + outputMin;
334
+ }
316
335
  return result;
317
336
  }
318
337
  function findRange(input, inputRange) {
@@ -325,6 +344,9 @@ function findRange(input, inputRange) {
325
344
  return i - 1;
326
345
  }
327
346
  const defaultEasing = (num) => num;
347
+ const resolveOutputOption = (output) => {
348
+ return output !== null && output !== void 0 ? output : 'linear';
349
+ };
328
350
  const shouldExtendRightForEasing = (easing) => {
329
351
  return easing.remotionShouldExtendRight === true;
330
352
  };
@@ -338,7 +360,7 @@ const resolveEasingForSegment = ({ easing, segmentIndex, }) => {
338
360
  // `segmentIndex` is in [0, inputRange.length - 2]; array length was validated above.
339
361
  return easing[segmentIndex];
340
362
  };
341
- const interpolateSegment = ({ input, inputRange, outputRange, easing, extrapolateLeft, extrapolateRight, }) => {
363
+ const interpolateSegment = ({ input, inputRange, outputRange, easing, extrapolateLeft, extrapolateRight, output, }) => {
342
364
  return interpolateFunction(input, inputRange, outputRange, {
343
365
  easing,
344
366
  extrapolateLeft,
@@ -347,9 +369,11 @@ const interpolateSegment = ({ input, inputRange, outputRange, easing, extrapolat
347
369
  shouldExtendRightForEasing(easing)
348
370
  ? 'extend'
349
371
  : extrapolateRight,
372
+ output,
350
373
  });
351
374
  };
352
375
  const interpolateNumber = ({ input, inputRange, outputRange, options, }) => {
376
+ const output = resolveOutputOption(options === null || options === void 0 ? void 0 : options.output);
353
377
  if (inputRange.length === 1) {
354
378
  return outputRange[0];
355
379
  }
@@ -377,6 +401,7 @@ const interpolateNumber = ({ input, inputRange, outputRange, options, }) => {
377
401
  easing,
378
402
  extrapolateLeft,
379
403
  extrapolateRight,
404
+ output,
380
405
  });
381
406
  for (let segmentIndex = 0; segmentIndex < range; segmentIndex++) {
382
407
  const previousEasing = resolveEasingForSegment({
@@ -397,6 +422,7 @@ const interpolateNumber = ({ input, inputRange, outputRange, options, }) => {
397
422
  easing: previousEasing,
398
423
  extrapolateLeft,
399
424
  extrapolateRight: 'extend',
425
+ output,
400
426
  });
401
427
  result += continuedSegmentValue - outputRange[segmentIndex + 1];
402
428
  }
@@ -440,14 +466,18 @@ const interpolateString = ({ input, inputRange, outputRange, options, }) => {
440
466
  }
441
467
  }
442
468
  }
443
- return serializeStringInterpolationValue({
444
- kind,
445
- values: [0, 0, 0].map((_, axis) => interpolateNumber({
469
+ const values = [0, 0, 0];
470
+ for (let axis = 0; axis < dimensions; axis++) {
471
+ values[axis] = interpolateNumber({
446
472
  input,
447
473
  inputRange,
448
474
  outputRange: parsedOutputRange.map((parsed) => parsed.values[axis]),
449
475
  options,
450
- })),
476
+ });
477
+ }
478
+ return serializeStringInterpolationValue({
479
+ kind,
480
+ values,
451
481
  units,
452
482
  dimensions,
453
483
  });
@@ -529,6 +559,14 @@ function assertValidInterpolatePosterizeOption(posterize) {
529
559
  throw new Error(`posterize must be a positive finite number, but got ${posterize}`);
530
560
  }
531
561
  }
562
+ function assertValidInterpolateOutputOption(output) {
563
+ if (output === undefined ||
564
+ output === 'linear' ||
565
+ output === 'perceptual-scale') {
566
+ return;
567
+ }
568
+ throw new Error(`output must be "linear" or "perceptual-scale", but got ${String(output)}`);
569
+ }
532
570
  function interpolate(input, inputRange, outputRange, options) {
533
571
  if (typeof input === 'undefined') {
534
572
  throw new Error('input can not be undefined');
@@ -550,6 +588,7 @@ function interpolate(input, inputRange, outputRange, options) {
550
588
  checkValidInputRange(inputRange);
551
589
  assertValidInterpolateEasingOption(options === null || options === void 0 ? void 0 : options.easing, inputRange.length);
552
590
  assertValidInterpolatePosterizeOption(options === null || options === void 0 ? void 0 : options.posterize);
591
+ assertValidInterpolateOutputOption(options === null || options === void 0 ? void 0 : options.output);
553
592
  if (typeof input !== 'number') {
554
593
  throw new TypeError('Cannot interpolate an input which is not a number');
555
594
  }
@@ -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, InterpolateOptions, } from './interpolate';
4
+ export type { EasingFunction, ExtrapolateType, InterpolateOutputOption, InterpolateOptions, } 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';
@@ -115,6 +115,7 @@ export declare const NoReactInternals: {
115
115
  readonly step: 0.01;
116
116
  readonly default: 1;
117
117
  readonly description: "Scale";
118
+ readonly defaultKeyframeOutput: "perceptual-scale";
118
119
  };
119
120
  readonly 'style.rotate': {
120
121
  readonly type: "rotation-css";
@@ -1,5 +1,5 @@
1
1
  import type { InteractivitySchema } from './interactivity-schema.js';
2
- import type { ExtrapolateType } from './interpolate.js';
2
+ import type { ExtrapolateType, InterpolateOutputOption } from './interpolate.js';
3
3
  import type { CanUpdateSequencePropsResponse, SequencePropsSubscriptionKey } from './SequenceManager.js';
4
4
  export type CanUpdateSequencePropStatusStatic = {
5
5
  status: 'static';
@@ -45,6 +45,7 @@ export type CanUpdateSequencePropStatusKeyframed = {
45
45
  easing: CanUpdateSequencePropStatusEasing[];
46
46
  clamping: CanUpdateSequencePropStatusClamping;
47
47
  posterize: number | undefined;
48
+ output: InterpolateOutputOption | undefined;
48
49
  };
49
50
  export type CanUpdateSequencePropStatusFalse = CanUpdateSequencePropStatusComputed;
50
51
  export type CanUpdateSequencePropStatus = CanUpdateSequencePropStatusStatic | CanUpdateSequencePropStatusKeyframed | CanUpdateSequencePropStatusFalse;
@@ -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.489";
6
+ export declare const VERSION = "4.0.490";
@@ -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.489';
10
+ exports.VERSION = '4.0.490';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { SequenceControls } from './CompositionManager.js';
2
+ import type { JsxComponentIdentity, SequenceControls } from './CompositionManager.js';
3
3
  import { type InteractivitySchema } from './interactivity-schema.js';
4
4
  export declare const getNestedValue: (obj: Record<string, unknown>, key: string) => unknown;
5
5
  export declare const getRuntimeValueForSchemaKey: ({ flatSchema, key, props, }: {
@@ -16,12 +16,13 @@ export declare const mergeValues: ({ flatSchema, props, valuesDotNotation, schem
16
16
  schemaKeys: string[];
17
17
  propsToDelete: Set<string>;
18
18
  }) => Record<string, unknown>;
19
- export declare const withInteractivitySchema: <S extends InteractivitySchema, Props extends object>({ Component, componentName, componentIdentity, schema, supportsEffects, }: {
19
+ export type WithInteractivitySchemaOptions<S extends InteractivitySchema, Props extends object> = {
20
20
  Component: React.ComponentType<Props & {
21
21
  readonly controls: SequenceControls | undefined;
22
22
  }>;
23
23
  componentName: string;
24
- componentIdentity: string | null;
24
+ componentIdentity: JsxComponentIdentity | null;
25
25
  schema: S;
26
26
  supportsEffects: boolean;
27
- }) => React.ComponentType<Props>;
27
+ };
28
+ export declare const withInteractivitySchema: <S extends InteractivitySchema, Props extends object>({ Component, componentName, componentIdentity, schema, supportsEffects, }: WithInteractivitySchemaOptions<S, Props>) => React.ComponentType<Props>;
@@ -1,16 +1,16 @@
1
1
  import React from 'react';
2
2
  import type { SequenceControls } from './CompositionManager.js';
3
- import { type SequenceSchema } from './sequence-field-schema.js';
3
+ import { type InteractivitySchema } from './interactivity-schema.js';
4
4
  export declare const getNestedValue: (obj: Record<string, unknown>, key: string) => unknown;
5
5
  export declare const readValuesFromProps: (props: Record<string, unknown>, keys: string[]) => Record<string, unknown>;
6
- export declare const selectActiveKeys: (schema: SequenceSchema, values: Record<string, unknown>) => string[];
6
+ export declare const selectActiveKeys: (schema: InteractivitySchema, values: Record<string, unknown>) => string[];
7
7
  export declare const mergeValues: ({ props, valuesDotNotation, schemaKeys, propsToDelete, }: {
8
8
  props: Record<string, unknown>;
9
9
  valuesDotNotation: Record<string, unknown>;
10
10
  schemaKeys: string[];
11
11
  propsToDelete: Set<string>;
12
12
  }) => Record<string, unknown>;
13
- export declare const wrapInSchema: <S extends SequenceSchema, Props extends object>({ Component, componentIdentity, schema, supportsEffects, }: {
13
+ export declare const wrapInSchema: <S extends InteractivitySchema, Props extends object>({ Component, componentIdentity, schema, supportsEffects, }: {
14
14
  Component: React.ComponentType<Props & {
15
15
  readonly _experimentalControls: SequenceControls | undefined;
16
16
  }>;
@@ -38,7 +38,7 @@ const react_1 = __importStar(require("react"));
38
38
  const delete_nested_key_js_1 = require("./delete-nested-key.js");
39
39
  const use_memoized_effects_js_1 = require("./effects/use-memoized-effects.js");
40
40
  const flatten_schema_js_1 = require("./flatten-schema.js");
41
- const sequence_field_schema_js_1 = require("./sequence-field-schema.js");
41
+ const interactivity_schema_js_1 = require("./interactivity-schema.js");
42
42
  const sequence_node_path_js_1 = require("./sequence-node-path.js");
43
43
  const SequenceManager_js_1 = require("./SequenceManager.js");
44
44
  const use_current_frame_js_1 = require("./use-current-frame.js");
@@ -100,7 +100,7 @@ exports.mergeValues = mergeValues;
100
100
  const stackToOverrideMap = {};
101
101
  const wrapInSchema = ({ Component, componentIdentity, schema, supportsEffects, }) => {
102
102
  // Schema is static for a component, so we move this outside
103
- const schemaWithSequenceName = (0, sequence_field_schema_js_1.extendSchemaWithSequenceName)(schema);
103
+ const schemaWithSequenceName = (0, interactivity_schema_js_1.extendSchemaWithSequenceName)(schema);
104
104
  const flatSchema = (0, flatten_schema_js_1.getFlatSchemaWithAllKeys)(schemaWithSequenceName);
105
105
  const flatKeys = Object.keys(flatSchema);
106
106
  const Wrapped = (0, react_1.forwardRef)((props, ref) => {
@@ -965,26 +965,20 @@ var continueRenderInternal = ({
965
965
  if (typeof handle !== "number") {
966
966
  throw new TypeError("The parameter passed into continueRender() must be the return value of delayRender() which is a number. Got: " + JSON.stringify(handle));
967
967
  }
968
- scope.remotion_delayRenderHandles = scope.remotion_delayRenderHandles.filter((h) => {
969
- if (h === handle) {
970
- if (environment.isRendering && scope !== undefined) {
971
- if (!scope.remotion_delayRenderTimeouts[handle]) {
972
- return false;
973
- }
974
- const { label: label2, startTime, timeout } = scope.remotion_delayRenderTimeouts[handle];
975
- clearTimeout(timeout);
976
- const message = [
977
- label2 ? `"${label2}"` : "A handle",
978
- DELAY_RENDER_CLEAR_TOKEN,
979
- `${Date.now() - startTime}ms`
980
- ].filter(truthy).join(" ");
981
- Log.verbose({ logLevel, tag: "delayRender()" }, message);
982
- delete scope.remotion_delayRenderTimeouts[handle];
983
- }
984
- return false;
985
- }
986
- return true;
987
- });
968
+ const handleExists = scope.remotion_delayRenderHandles.includes(handle);
969
+ const timeoutEntry = scope.remotion_delayRenderTimeouts[handle];
970
+ if (handleExists && environment.isRendering && timeoutEntry) {
971
+ const { label: label2, startTime, timeout } = timeoutEntry;
972
+ clearTimeout(timeout);
973
+ const message = [
974
+ label2 ? `"${label2}"` : "A handle",
975
+ DELAY_RENDER_CLEAR_TOKEN,
976
+ `${Date.now() - startTime}ms`
977
+ ].filter(truthy).join(" ");
978
+ Log.verbose({ logLevel, tag: "delayRender()" }, message);
979
+ delete scope.remotion_delayRenderTimeouts[handle];
980
+ }
981
+ scope.remotion_delayRenderHandles = scope.remotion_delayRenderHandles.filter((h) => h !== handle);
988
982
  if (scope.remotion_delayRenderHandles.length === 0) {
989
983
  scope.remotion_renderReady = true;
990
984
  }
@@ -1291,7 +1285,7 @@ var addSequenceStackTraces = (component) => {
1291
1285
  };
1292
1286
 
1293
1287
  // src/version.ts
1294
- var VERSION = "4.0.489";
1288
+ var VERSION = "4.0.490";
1295
1289
 
1296
1290
  // src/multiple-versions-warning.ts
1297
1291
  var checkMultipleRemotionVersions = () => {
@@ -1731,7 +1725,8 @@ var transformSchema = {
1731
1725
  max: 100,
1732
1726
  step: 0.01,
1733
1727
  default: 1,
1734
- description: "Scale"
1728
+ description: "Scale",
1729
+ defaultKeyframeOutput: "perceptual-scale"
1735
1730
  },
1736
1731
  "style.rotate": {
1737
1732
  type: "rotation-css",
@@ -2562,8 +2557,20 @@ var serializeStringInterpolationValue = ({
2562
2557
  }
2563
2558
  return values.slice(0, dimensions).map((value, index) => `${stringifyNumber(value)}${units[index]}`).join(" ");
2564
2559
  };
2560
+ var toSignedArea = (scale) => {
2561
+ if (scale === 0) {
2562
+ return 0;
2563
+ }
2564
+ return Math.sign(scale) * scale * scale;
2565
+ };
2566
+ var fromSignedArea = (area) => {
2567
+ if (area === 0) {
2568
+ return 0;
2569
+ }
2570
+ return Math.sign(area) * Math.sqrt(Math.abs(area));
2571
+ };
2565
2572
  function interpolateFunction(input, inputRange, outputRange, options) {
2566
- const { extrapolateLeft, extrapolateRight, easing } = options;
2573
+ const { extrapolateLeft, extrapolateRight, easing, output } = options;
2567
2574
  let result = input;
2568
2575
  const [inputMin, inputMax] = inputRange;
2569
2576
  const [outputMin, outputMax] = outputRange;
@@ -2594,7 +2601,13 @@ function interpolateFunction(input, inputRange, outputRange, options) {
2594
2601
  }
2595
2602
  result = (result - inputMin) / (inputMax - inputMin);
2596
2603
  result = easing(result);
2597
- result = result * (outputMax - outputMin) + outputMin;
2604
+ if (output === "perceptual-scale") {
2605
+ const signedAreaMin = toSignedArea(outputMin);
2606
+ const signedAreaMax = toSignedArea(outputMax);
2607
+ result = fromSignedArea(result * (signedAreaMax - signedAreaMin) + signedAreaMin);
2608
+ } else {
2609
+ result = result * (outputMax - outputMin) + outputMin;
2610
+ }
2598
2611
  return result;
2599
2612
  }
2600
2613
  function findRange(input, inputRange) {
@@ -2607,6 +2620,9 @@ function findRange(input, inputRange) {
2607
2620
  return i - 1;
2608
2621
  }
2609
2622
  var defaultEasing = (num) => num;
2623
+ var resolveOutputOption = (output) => {
2624
+ return output ?? "linear";
2625
+ };
2610
2626
  var shouldExtendRightForEasing = (easing) => {
2611
2627
  return easing.remotionShouldExtendRight === true;
2612
2628
  };
@@ -2628,12 +2644,14 @@ var interpolateSegment = ({
2628
2644
  outputRange,
2629
2645
  easing,
2630
2646
  extrapolateLeft,
2631
- extrapolateRight
2647
+ extrapolateRight,
2648
+ output
2632
2649
  }) => {
2633
2650
  return interpolateFunction(input, inputRange, outputRange, {
2634
2651
  easing,
2635
2652
  extrapolateLeft,
2636
- extrapolateRight: input > inputRange[1] && extrapolateRight === "clamp" && shouldExtendRightForEasing(easing) ? "extend" : extrapolateRight
2653
+ extrapolateRight: input > inputRange[1] && extrapolateRight === "clamp" && shouldExtendRightForEasing(easing) ? "extend" : extrapolateRight,
2654
+ output
2637
2655
  });
2638
2656
  };
2639
2657
  var interpolateNumber = ({
@@ -2642,6 +2660,7 @@ var interpolateNumber = ({
2642
2660
  outputRange,
2643
2661
  options
2644
2662
  }) => {
2663
+ const output = resolveOutputOption(options?.output);
2645
2664
  if (inputRange.length === 1) {
2646
2665
  return outputRange[0];
2647
2666
  }
@@ -2666,7 +2685,8 @@ var interpolateNumber = ({
2666
2685
  outputRange: [outputRange[range], outputRange[range + 1]],
2667
2686
  easing,
2668
2687
  extrapolateLeft,
2669
- extrapolateRight
2688
+ extrapolateRight,
2689
+ output
2670
2690
  });
2671
2691
  for (let segmentIndex = 0;segmentIndex < range; segmentIndex++) {
2672
2692
  const previousEasing = resolveEasingForSegment({
@@ -2686,7 +2706,8 @@ var interpolateNumber = ({
2686
2706
  outputRange: [outputRange[segmentIndex], outputRange[segmentIndex + 1]],
2687
2707
  easing: previousEasing,
2688
2708
  extrapolateLeft,
2689
- extrapolateRight: "extend"
2709
+ extrapolateRight: "extend",
2710
+ output
2690
2711
  });
2691
2712
  result += continuedSegmentValue - outputRange[segmentIndex + 1];
2692
2713
  }
@@ -2734,14 +2755,18 @@ var interpolateString = ({
2734
2755
  }
2735
2756
  }
2736
2757
  }
2737
- return serializeStringInterpolationValue({
2738
- kind,
2739
- values: [0, 0, 0].map((_, axis) => interpolateNumber({
2758
+ const values = [0, 0, 0];
2759
+ for (let axis = 0;axis < dimensions; axis++) {
2760
+ values[axis] = interpolateNumber({
2740
2761
  input,
2741
2762
  inputRange,
2742
2763
  outputRange: parsedOutputRange.map((parsed) => parsed.values[axis]),
2743
2764
  options
2744
- })),
2765
+ });
2766
+ }
2767
+ return serializeStringInterpolationValue({
2768
+ kind,
2769
+ values,
2745
2770
  units,
2746
2771
  dimensions
2747
2772
  });
@@ -2825,6 +2850,12 @@ function assertValidInterpolatePosterizeOption(posterize) {
2825
2850
  throw new Error(`posterize must be a positive finite number, but got ${posterize}`);
2826
2851
  }
2827
2852
  }
2853
+ function assertValidInterpolateOutputOption(output) {
2854
+ if (output === undefined || output === "linear" || output === "perceptual-scale") {
2855
+ return;
2856
+ }
2857
+ throw new Error(`output must be "linear" or "perceptual-scale", but got ${String(output)}`);
2858
+ }
2828
2859
  function interpolate(input, inputRange, outputRange, options) {
2829
2860
  if (typeof input === "undefined") {
2830
2861
  throw new Error("input can not be undefined");
@@ -2842,6 +2873,7 @@ function interpolate(input, inputRange, outputRange, options) {
2842
2873
  checkValidInputRange(inputRange);
2843
2874
  assertValidInterpolateEasingOption(options?.easing, inputRange.length);
2844
2875
  assertValidInterpolatePosterizeOption(options?.posterize);
2876
+ assertValidInterpolateOutputOption(options?.output);
2845
2877
  if (typeof input !== "number") {
2846
2878
  throw new TypeError("Cannot interpolate an input which is not a number");
2847
2879
  }
@@ -3802,6 +3834,7 @@ var interpolateKeyframedStatus = ({
3802
3834
  easing: easing.map((e) => easingToFn({ easing: e, forceSpringAllowTail })),
3803
3835
  extrapolateLeft: clamping.left,
3804
3836
  extrapolateRight: clamping.right,
3837
+ output: status.output,
3805
3838
  posterize: status.posterize
3806
3839
  });
3807
3840
  } catch {
@@ -4051,7 +4084,7 @@ var getFlatSchemaWithAllKeys = (schema) => {
4051
4084
  const out = {};
4052
4085
  const addKey = (key, field) => {
4053
4086
  if (key in out) {
4054
- throw new Error(`Duplicate key "${key}" in schema: discriminated union variants must not share keys`);
4087
+ return;
4055
4088
  }
4056
4089
  out[key] = field;
4057
4090
  };
@@ -10249,6 +10282,22 @@ addSequenceStackTraces(Img);
10249
10282
  // src/Interactive.tsx
10250
10283
  import React29, { forwardRef as forwardRef12, useCallback as useCallback20, useRef as useRef25 } from "react";
10251
10284
  import { jsx as jsx29 } from "react/jsx-runtime";
10285
+ var sourcePathToIdentityPrefix = (packageName) => {
10286
+ if (packageName === "remotion") {
10287
+ return "dev.remotion.remotion";
10288
+ }
10289
+ if (packageName.startsWith("@remotion/")) {
10290
+ const normalizedPackageName = packageName.slice("@remotion/".length).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
10291
+ return `dev.remotion.${normalizedPackageName}`;
10292
+ }
10293
+ throw new Error(`Unsupported Remotion package name: ${packageName}`);
10294
+ };
10295
+ var makeRemotionComponentIdentity = ({
10296
+ packageName,
10297
+ componentName
10298
+ }) => {
10299
+ return `${sourcePathToIdentityPrefix(packageName)}.${componentName}`;
10300
+ };
10252
10301
  var interactiveElementSchema = {
10253
10302
  ...baseSchema,
10254
10303
  ...transformSchema,
@@ -10262,6 +10311,11 @@ var setRef = (ref, value) => {
10262
10311
  ref.current = value;
10263
10312
  }
10264
10313
  };
10314
+ var withSchema = (options) => {
10315
+ const Wrapped = withInteractivitySchema(options);
10316
+ addSequenceStackTraces(Wrapped);
10317
+ return Wrapped;
10318
+ };
10265
10319
  var makeInteractiveElement = (tag, displayName) => {
10266
10320
  const Inner = forwardRef12((propsWithControls, ref) => {
10267
10321
  const {
@@ -10301,15 +10355,17 @@ var makeInteractiveElement = (tag, displayName) => {
10301
10355
  });
10302
10356
  });
10303
10357
  Inner.displayName = displayName;
10304
- const Wrapped = withInteractivitySchema({
10358
+ const Wrapped = withSchema({
10305
10359
  Component: Inner,
10306
10360
  componentName: displayName,
10307
- componentIdentity: `dev.remotion.remotion.${displayName.slice(1, -1)}`,
10361
+ componentIdentity: makeRemotionComponentIdentity({
10362
+ packageName: "remotion",
10363
+ componentName: displayName.slice(1, -1)
10364
+ }),
10308
10365
  schema: interactiveElementSchema,
10309
10366
  supportsEffects: false
10310
10367
  });
10311
10368
  Wrapped.displayName = displayName;
10312
- addSequenceStackTraces(Wrapped);
10313
10369
  return Wrapped;
10314
10370
  };
10315
10371
  var Interactive = {
@@ -10318,7 +10374,8 @@ var Interactive = {
10318
10374
  textSchema,
10319
10375
  premountSchema,
10320
10376
  sequenceSchema,
10321
- withSchema: withInteractivitySchema,
10377
+ withSchema,
10378
+ _internalMakeRemotionComponentIdentity: makeRemotionComponentIdentity,
10322
10379
  A: makeInteractiveElement("a", "<Interactive.A>"),
10323
10380
  Article: makeInteractiveElement("article", "<Interactive.Article>"),
10324
10381
  Aside: makeInteractiveElement("aside", "<Interactive.Aside>"),
@@ -251,8 +251,20 @@ var serializeStringInterpolationValue = ({
251
251
  }
252
252
  return values.slice(0, dimensions).map((value, index) => `${stringifyNumber(value)}${units[index]}`).join(" ");
253
253
  };
254
+ var toSignedArea = (scale) => {
255
+ if (scale === 0) {
256
+ return 0;
257
+ }
258
+ return Math.sign(scale) * scale * scale;
259
+ };
260
+ var fromSignedArea = (area) => {
261
+ if (area === 0) {
262
+ return 0;
263
+ }
264
+ return Math.sign(area) * Math.sqrt(Math.abs(area));
265
+ };
254
266
  function interpolateFunction(input, inputRange, outputRange, options) {
255
- const { extrapolateLeft, extrapolateRight, easing } = options;
267
+ const { extrapolateLeft, extrapolateRight, easing, output } = options;
256
268
  let result = input;
257
269
  const [inputMin, inputMax] = inputRange;
258
270
  const [outputMin, outputMax] = outputRange;
@@ -283,7 +295,13 @@ function interpolateFunction(input, inputRange, outputRange, options) {
283
295
  }
284
296
  result = (result - inputMin) / (inputMax - inputMin);
285
297
  result = easing(result);
286
- result = result * (outputMax - outputMin) + outputMin;
298
+ if (output === "perceptual-scale") {
299
+ const signedAreaMin = toSignedArea(outputMin);
300
+ const signedAreaMax = toSignedArea(outputMax);
301
+ result = fromSignedArea(result * (signedAreaMax - signedAreaMin) + signedAreaMin);
302
+ } else {
303
+ result = result * (outputMax - outputMin) + outputMin;
304
+ }
287
305
  return result;
288
306
  }
289
307
  function findRange(input, inputRange) {
@@ -296,6 +314,9 @@ function findRange(input, inputRange) {
296
314
  return i - 1;
297
315
  }
298
316
  var defaultEasing = (num) => num;
317
+ var resolveOutputOption = (output) => {
318
+ return output ?? "linear";
319
+ };
299
320
  var shouldExtendRightForEasing = (easing) => {
300
321
  return easing.remotionShouldExtendRight === true;
301
322
  };
@@ -317,12 +338,14 @@ var interpolateSegment = ({
317
338
  outputRange,
318
339
  easing,
319
340
  extrapolateLeft,
320
- extrapolateRight
341
+ extrapolateRight,
342
+ output
321
343
  }) => {
322
344
  return interpolateFunction(input, inputRange, outputRange, {
323
345
  easing,
324
346
  extrapolateLeft,
325
- extrapolateRight: input > inputRange[1] && extrapolateRight === "clamp" && shouldExtendRightForEasing(easing) ? "extend" : extrapolateRight
347
+ extrapolateRight: input > inputRange[1] && extrapolateRight === "clamp" && shouldExtendRightForEasing(easing) ? "extend" : extrapolateRight,
348
+ output
326
349
  });
327
350
  };
328
351
  var interpolateNumber = ({
@@ -331,6 +354,7 @@ var interpolateNumber = ({
331
354
  outputRange,
332
355
  options
333
356
  }) => {
357
+ const output = resolveOutputOption(options?.output);
334
358
  if (inputRange.length === 1) {
335
359
  return outputRange[0];
336
360
  }
@@ -355,7 +379,8 @@ var interpolateNumber = ({
355
379
  outputRange: [outputRange[range], outputRange[range + 1]],
356
380
  easing,
357
381
  extrapolateLeft,
358
- extrapolateRight
382
+ extrapolateRight,
383
+ output
359
384
  });
360
385
  for (let segmentIndex = 0;segmentIndex < range; segmentIndex++) {
361
386
  const previousEasing = resolveEasingForSegment({
@@ -375,7 +400,8 @@ var interpolateNumber = ({
375
400
  outputRange: [outputRange[segmentIndex], outputRange[segmentIndex + 1]],
376
401
  easing: previousEasing,
377
402
  extrapolateLeft,
378
- extrapolateRight: "extend"
403
+ extrapolateRight: "extend",
404
+ output
379
405
  });
380
406
  result += continuedSegmentValue - outputRange[segmentIndex + 1];
381
407
  }
@@ -423,14 +449,18 @@ var interpolateString = ({
423
449
  }
424
450
  }
425
451
  }
426
- return serializeStringInterpolationValue({
427
- kind,
428
- values: [0, 0, 0].map((_, axis) => interpolateNumber({
452
+ const values = [0, 0, 0];
453
+ for (let axis = 0;axis < dimensions; axis++) {
454
+ values[axis] = interpolateNumber({
429
455
  input,
430
456
  inputRange,
431
457
  outputRange: parsedOutputRange.map((parsed) => parsed.values[axis]),
432
458
  options
433
- })),
459
+ });
460
+ }
461
+ return serializeStringInterpolationValue({
462
+ kind,
463
+ values,
434
464
  units,
435
465
  dimensions
436
466
  });
@@ -514,6 +544,12 @@ function assertValidInterpolatePosterizeOption(posterize) {
514
544
  throw new Error(`posterize must be a positive finite number, but got ${posterize}`);
515
545
  }
516
546
  }
547
+ function assertValidInterpolateOutputOption(output) {
548
+ if (output === undefined || output === "linear" || output === "perceptual-scale") {
549
+ return;
550
+ }
551
+ throw new Error(`output must be "linear" or "perceptual-scale", but got ${String(output)}`);
552
+ }
517
553
  function interpolate(input, inputRange, outputRange, options) {
518
554
  if (typeof input === "undefined") {
519
555
  throw new Error("input can not be undefined");
@@ -531,6 +567,7 @@ function interpolate(input, inputRange, outputRange, options) {
531
567
  checkValidInputRange(inputRange);
532
568
  assertValidInterpolateEasingOption(options?.easing, inputRange.length);
533
569
  assertValidInterpolatePosterizeOption(options?.posterize);
570
+ assertValidInterpolateOutputOption(options?.output);
534
571
  if (typeof input !== "number") {
535
572
  throw new TypeError("Cannot interpolate an input which is not a number");
536
573
  }
@@ -705,7 +742,8 @@ var transformSchema = {
705
742
  max: 100,
706
743
  step: 0.01,
707
744
  default: 1,
708
- description: "Scale"
745
+ description: "Scale",
746
+ defaultKeyframeOutput: "perceptual-scale"
709
747
  },
710
748
  "style.rotate": {
711
749
  type: "rotation-css",
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "4.0.489";
2
+ var VERSION = "4.0.490";
3
3
  export {
4
4
  VERSION
5
5
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/core"
4
4
  },
5
5
  "name": "remotion",
6
- "version": "4.0.489",
6
+ "version": "4.0.490",
7
7
  "description": "Make videos programmatically",
8
8
  "main": "dist/cjs/index.js",
9
9
  "types": "dist/cjs/index.d.ts",
@@ -35,7 +35,7 @@
35
35
  "react-dom": "19.2.3",
36
36
  "webpack": "5.105.0",
37
37
  "zod": "4.3.6",
38
- "@remotion/eslint-config-internal": "4.0.489",
38
+ "@remotion/eslint-config-internal": "4.0.490",
39
39
  "eslint": "9.19.0",
40
40
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
41
41
  },
@@ -1,8 +0,0 @@
1
- import { type InterpolateOptions } from './interpolate.js';
2
- export type InterpolateTranslateOptions = InterpolateOptions;
3
- export declare const interpolateTranslate: (input: number, inputRange: readonly number[], outputRange: readonly string[], options?: Partial<{
4
- easing: import("./interpolate.js").EasingFunction | readonly import("./interpolate.js").EasingFunction[];
5
- extrapolateLeft: import("./interpolate.js").ExtrapolateType;
6
- extrapolateRight: import("./interpolate.js").ExtrapolateType;
7
- posterize: number;
8
- }> | undefined) => string;
@@ -1,70 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.interpolateTranslate = void 0;
4
- const interpolate_js_1 = require("./interpolate.js");
5
- const pixelValueRegex = /^([+-]?(?:\d+\.?\d*|\.\d+))px$/;
6
- const parseTranslate = (value) => {
7
- if (typeof value !== 'string') {
8
- throw new TypeError(`outputRange must contain only strings, but got ${typeof value}`);
9
- }
10
- const parts = value.trim().split(/\s+/);
11
- if (parts.length < 1 || parts.length > 3 || parts[0] === '') {
12
- throw new TypeError(`translate values must contain 1 to 3 pixel values, but got "${value}"`);
13
- }
14
- return parts.map((part) => {
15
- const match = pixelValueRegex.exec(part);
16
- if (match === null) {
17
- throw new TypeError(`interpolateTranslate() only supports px values, but got "${part}" in "${value}"`);
18
- }
19
- return Number(match[1]);
20
- });
21
- };
22
- /*
23
- * @description Allows you to map a range of values to CSS translate values using pixel units.
24
- * @see [Documentation](https://remotion.dev/docs/interpolate-translate)
25
- */
26
- const interpolateTranslate = (input, inputRange, outputRange, options) => {
27
- var _a;
28
- if (typeof input === 'undefined') {
29
- throw new TypeError('input can not be undefined');
30
- }
31
- if (typeof inputRange === 'undefined') {
32
- throw new TypeError('inputRange can not be undefined');
33
- }
34
- if (typeof outputRange === 'undefined') {
35
- throw new TypeError('outputRange can not be undefined');
36
- }
37
- if (inputRange.length !== outputRange.length) {
38
- throw new TypeError('inputRange (' +
39
- inputRange.length +
40
- ' values provided) and outputRange (' +
41
- outputRange.length +
42
- ' values provided) must have the same length');
43
- }
44
- const parsedOutputRange = outputRange.map((translateValue) => parseTranslate(translateValue));
45
- const firstValueLength = (_a = parsedOutputRange[0]) === null || _a === void 0 ? void 0 : _a.length;
46
- if (firstValueLength === undefined) {
47
- throw new TypeError('outputRange must have at least 1 element');
48
- }
49
- for (const parsedTranslate of parsedOutputRange) {
50
- if (parsedTranslate.length !== firstValueLength) {
51
- throw new TypeError(`All translate values must have the same number of pixel values, but got ${firstValueLength} and ${parsedTranslate.length}`);
52
- }
53
- }
54
- return new Array(firstValueLength)
55
- .fill(true)
56
- .map((_, index) => {
57
- const outputValues = [];
58
- for (const translateValue of parsedOutputRange) {
59
- const value = translateValue[index];
60
- if (value === undefined) {
61
- throw new TypeError(`All translate values must have the same number of pixel values, but got ${firstValueLength} and ${translateValue.length}`);
62
- }
63
- outputValues.push(value);
64
- }
65
- const interpolatedValue = (0, interpolate_js_1.interpolate)(input, inputRange, outputValues, options);
66
- return `${interpolatedValue}px`;
67
- })
68
- .join(' ');
69
- };
70
- exports.interpolateTranslate = interpolateTranslate;