@remotion/studio-server 4.0.468 → 4.0.469

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.
@@ -159,11 +159,14 @@ const addKeyframe = ({ expression, frame, value, }) => {
159
159
  if (!(0, can_update_sequence_props_1.isStaticValue)(expression)) {
160
160
  throw new Error('Cannot add keyframe to computed expression');
161
161
  }
162
- if (frame === 0) {
163
- throw new Error('Cannot add keyframe to static expression at frame 0 because interpolate requires two distinct frames');
164
- }
165
162
  const staticValue = (0, can_update_sequence_props_1.extractStaticValue)(expression);
166
163
  const staticOutput = (0, update_nested_prop_1.parseValueExpression)(staticValue);
164
+ const keyframes = frame === 0
165
+ ? [{ frame, output: newOutput, value }]
166
+ : [
167
+ { frame: 0, output: staticOutput, value: staticValue },
168
+ { frame, output: newOutput, value },
169
+ ];
167
170
  return createInterpolateExpression({
168
171
  callee: getInterpolationCalleeForValues({
169
172
  staticValue,
@@ -171,10 +174,7 @@ const addKeyframe = ({ expression, frame, value, }) => {
171
174
  }),
172
175
  input: b.identifier('frame'),
173
176
  extraArgs: [],
174
- keyframes: [
175
- { frame: 0, output: staticOutput, value: staticValue },
176
- { frame, output: newOutput, value },
177
- ],
177
+ keyframes,
178
178
  });
179
179
  };
180
180
  const removeKeyframe = ({ expression, frame, }) => {
@@ -187,9 +187,6 @@ const removeKeyframe = ({ expression, frame, }) => {
187
187
  throw new Error(`Cannot remove keyframe at frame ${frame}: not found`);
188
188
  }
189
189
  const nextKeyframes = existing.keyframes.filter((_keyframe, index) => index !== keyframeIndex);
190
- if (nextKeyframes.length === 1) {
191
- return (0, update_nested_prop_1.parseValueExpression)(nextKeyframes[0].value);
192
- }
193
190
  return createInterpolateExpression({
194
191
  callee: existing.callee,
195
192
  input: existing.input,
@@ -6,15 +6,6 @@ export declare const extractStaticValue: (node: Expression) => unknown;
6
6
  export declare const getComputedStatus: (node: Expression) => CanUpdateSequencePropStatus;
7
7
  export declare const findJsxElementAtNodePath: (ast: File, nodePath: SequenceNodePath) => JSXOpeningElement | null;
8
8
  export declare const lineColumnToNodePath: (ast: File, targetLine: number) => SequenceNodePath | null;
9
- export declare const computeSequencePropsOnlyStatus: ({ fileName, nodePath, keys, remotionRoot, }: {
10
- fileName: string;
11
- nodePath: SequenceNodePath;
12
- keys: string[];
13
- remotionRoot: string;
14
- }) => {
15
- canUpdate: true;
16
- props: Record<string, CanUpdateSequencePropStatus>;
17
- };
18
9
  export declare const computeSequencePropsStatusFromContent: ({ fileContents, nodePath, keys, effects, }: {
19
10
  fileContents: string;
20
11
  nodePath: SequenceNodePath;
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.computeSequencePropsStatusFromFilenameByLine = exports.computeSequencePropsStatus = exports.computeSequencePropsStatusFromContent = exports.computeSequencePropsOnlyStatus = exports.lineColumnToNodePath = exports.findJsxElementAtNodePath = exports.getComputedStatus = exports.extractStaticValue = exports.isStaticValue = void 0;
36
+ exports.computeSequencePropsStatusFromFilenameByLine = exports.computeSequencePropsStatus = exports.computeSequencePropsStatusFromContent = exports.lineColumnToNodePath = exports.findJsxElementAtNodePath = exports.getComputedStatus = exports.extractStaticValue = exports.isStaticValue = void 0;
37
37
  const node_fs_1 = require("node:fs");
38
38
  const renderer_1 = require("@remotion/renderer");
39
39
  const recast = __importStar(require("recast"));
@@ -128,6 +128,159 @@ const getNumericValue = (node) => {
128
128
  }
129
129
  return null;
130
130
  };
131
+ const getExtrapolateType = (node) => {
132
+ if (node.type === 'StringLiteral') {
133
+ if (node.value === 'extend' ||
134
+ node.value === 'identity' ||
135
+ node.value === 'clamp' ||
136
+ node.value === 'wrap') {
137
+ return node.value;
138
+ }
139
+ return null;
140
+ }
141
+ if (node.type === 'TSAsExpression') {
142
+ return getExtrapolateType(node.expression);
143
+ }
144
+ return null;
145
+ };
146
+ const getKeyframeEasing = (node) => {
147
+ if (node.type === 'TSAsExpression') {
148
+ return getKeyframeEasing(node.expression);
149
+ }
150
+ if (node.type === 'MemberExpression' &&
151
+ node.object.type === 'Identifier' &&
152
+ node.object.name === 'Easing' &&
153
+ node.property.type === 'Identifier' &&
154
+ node.property.name === 'linear' &&
155
+ node.computed === false) {
156
+ return 'linear';
157
+ }
158
+ if (node.type !== 'CallExpression' ||
159
+ node.callee.type !== 'MemberExpression' ||
160
+ node.callee.object.type !== 'Identifier' ||
161
+ node.callee.object.name !== 'Easing' ||
162
+ node.callee.property.type !== 'Identifier' ||
163
+ node.callee.property.name !== 'bezier' ||
164
+ node.callee.computed) {
165
+ return null;
166
+ }
167
+ if (node.arguments.length !== 4) {
168
+ return null;
169
+ }
170
+ const values = node.arguments.map((arg) => {
171
+ if (arg.type === 'ArgumentPlaceholder' ||
172
+ arg.type === 'JSXNamespacedName' ||
173
+ arg.type === 'SpreadElement') {
174
+ return null;
175
+ }
176
+ return getNumericValue(arg);
177
+ });
178
+ if (values.some((v) => v === null)) {
179
+ return null;
180
+ }
181
+ return values;
182
+ };
183
+ const getKeyframeEasingArray = ({ easingNode, segments, }) => {
184
+ if (segments === 0) {
185
+ return [];
186
+ }
187
+ if (easingNode.type === 'TSAsExpression') {
188
+ return getKeyframeEasingArray({
189
+ easingNode: easingNode.expression,
190
+ segments,
191
+ });
192
+ }
193
+ if (easingNode.type === 'ArrayExpression') {
194
+ if (easingNode.elements.length !== segments) {
195
+ return null;
196
+ }
197
+ const parsed = easingNode.elements.map((element) => {
198
+ if (!element || element.type === 'SpreadElement') {
199
+ return null;
200
+ }
201
+ return getKeyframeEasing(element);
202
+ });
203
+ if (parsed.some((value) => value === null)) {
204
+ return null;
205
+ }
206
+ return parsed;
207
+ }
208
+ const easing = getKeyframeEasing(easingNode);
209
+ if (!easing) {
210
+ return null;
211
+ }
212
+ return new Array(segments).fill(easing);
213
+ };
214
+ const getInterpolationMetadata = (callExpression, keyframeCount) => {
215
+ const segments = Math.max(0, keyframeCount - 1);
216
+ if (callExpression.callee.type !== 'Identifier') {
217
+ return null;
218
+ }
219
+ const defaultClamping = callExpression.callee.name === 'interpolateColors'
220
+ ? {
221
+ left: 'clamp',
222
+ right: 'clamp',
223
+ }
224
+ : {
225
+ left: 'extend',
226
+ right: 'extend',
227
+ };
228
+ const defaults = {
229
+ easing: new Array(segments).fill('linear'),
230
+ clamping: defaultClamping,
231
+ };
232
+ if (callExpression.callee.name === 'interpolateColors') {
233
+ return defaults;
234
+ }
235
+ if (callExpression.callee.name !== 'interpolate') {
236
+ return null;
237
+ }
238
+ const optionsArg = callExpression.arguments[3];
239
+ if (!optionsArg) {
240
+ return defaults;
241
+ }
242
+ if (optionsArg.type !== 'ObjectExpression') {
243
+ return null;
244
+ }
245
+ let { easing } = defaults;
246
+ let { clamping } = defaults;
247
+ for (const property of optionsArg.properties) {
248
+ if (property.type !== 'ObjectProperty' || property.computed) {
249
+ return null;
250
+ }
251
+ const key = property.key.type === 'Identifier'
252
+ ? property.key.name
253
+ : property.key.type === 'StringLiteral'
254
+ ? property.key.value
255
+ : null;
256
+ if (!key) {
257
+ return null;
258
+ }
259
+ const value = property.value;
260
+ if (key === 'easing') {
261
+ const parsedEasing = getKeyframeEasingArray({
262
+ easingNode: value,
263
+ segments,
264
+ });
265
+ if (!parsedEasing) {
266
+ return null;
267
+ }
268
+ easing = parsedEasing;
269
+ continue;
270
+ }
271
+ if (key === 'extrapolateLeft' || key === 'extrapolateRight') {
272
+ const extrapolateType = getExtrapolateType(value);
273
+ if (!extrapolateType) {
274
+ return null;
275
+ }
276
+ clamping =
277
+ key === 'extrapolateLeft'
278
+ ? { ...clamping, left: extrapolateType }
279
+ : { ...clamping, right: extrapolateType };
280
+ }
281
+ }
282
+ return { easing, clamping };
283
+ };
131
284
  const getInterpolationKeyframes = (node) => {
132
285
  if (node.type === 'TSAsExpression') {
133
286
  return getInterpolationKeyframes(node.expression);
@@ -171,17 +324,30 @@ const getInterpolationKeyframes = (node) => {
171
324
  value: (0, exports.extractStaticValue)(outputElement),
172
325
  });
173
326
  }
174
- return keyframes.length > 0 ? keyframes : undefined;
327
+ if (keyframes.length === 0) {
328
+ return undefined;
329
+ }
330
+ const metadata = getInterpolationMetadata(callExpression, keyframes.length);
331
+ if (!metadata) {
332
+ return undefined;
333
+ }
334
+ return {
335
+ keyframes,
336
+ easing: metadata.easing,
337
+ clamping: metadata.clamping,
338
+ };
175
339
  };
176
340
  const getComputedStatus = (node) => {
177
- const keyframes = getInterpolationKeyframes(node);
178
- if (!keyframes) {
341
+ const interpolation = getInterpolationKeyframes(node);
342
+ if (!interpolation) {
179
343
  return { canUpdate: false, reason: 'computed' };
180
344
  }
181
345
  return {
182
346
  canUpdate: false,
183
- reason: 'computed',
184
- keyframes,
347
+ reason: 'keyframed',
348
+ keyframes: interpolation.keyframes,
349
+ easing: interpolation.easing,
350
+ clamping: interpolation.clamping,
185
351
  };
186
352
  };
187
353
  exports.getComputedStatus = getComputedStatus;
@@ -345,24 +511,6 @@ const computeSequenceOnlyPropsRecord = ({ jsxElement, keys, }) => {
345
511
  }
346
512
  return filteredProps;
347
513
  };
348
- const computeSequencePropsOnlyStatus = ({ fileName, nodePath, keys, remotionRoot, }) => {
349
- const { absolutePath } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
350
- remotionRoot,
351
- fileName,
352
- action: 'read',
353
- });
354
- const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
355
- const ast = (0, parse_ast_1.parseAst)(fileContents);
356
- const jsxElement = (0, exports.findJsxElementAtNodePath)(ast, nodePath);
357
- if (!jsxElement) {
358
- throw new Error('Cannot compute sequence props: Could not find a JSX element at the specified location');
359
- }
360
- return {
361
- canUpdate: true,
362
- props: computeSequenceOnlyPropsRecord({ jsxElement, keys }),
363
- };
364
- };
365
- exports.computeSequencePropsOnlyStatus = computeSequencePropsOnlyStatus;
366
514
  const computeSequencePropsStatusFromContent = ({ fileContents, nodePath, keys, effects, }) => {
367
515
  const ast = (0, parse_ast_1.parseAst)(fileContents);
368
516
  const jsxElement = (0, exports.findJsxElementAtNodePath)(ast, nodePath);
@@ -86,12 +86,15 @@ const saveSequencePropsHandler = ({ input: { fileName, nodePath, key, value, def
86
86
  addedProps: [],
87
87
  });
88
88
  (0, undo_stack_1.printUndoHint)(logLevel);
89
- const newStatus = (0, can_update_sequence_props_1.computeSequencePropsOnlyStatus)({
90
- fileName,
89
+ const newStatus = (0, can_update_sequence_props_1.computeSequencePropsStatusFromContent)({
90
+ fileContents: output,
91
91
  keys: (0, studio_shared_1.getAllSchemaKeys)(schema),
92
92
  nodePath: nodePath.nodePath,
93
- remotionRoot,
93
+ effects: [],
94
94
  });
95
- return newStatus;
95
+ return {
96
+ canUpdate: true,
97
+ props: newStatus.props,
98
+ };
96
99
  });
97
100
  exports.saveSequencePropsHandler = saveSequencePropsHandler;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio-server"
4
4
  },
5
5
  "name": "@remotion/studio-server",
6
- "version": "4.0.468",
6
+ "version": "4.0.469",
7
7
  "description": "Run a Remotion Studio with a server backend",
8
8
  "main": "dist",
9
9
  "scripts": {
@@ -27,11 +27,11 @@
27
27
  "@babel/parser": "7.24.1",
28
28
  "semver": "7.5.3",
29
29
  "prettier": "3.8.1",
30
- "remotion": "4.0.468",
30
+ "remotion": "4.0.469",
31
31
  "recast": "0.23.11",
32
- "@remotion/bundler": "4.0.468",
33
- "@remotion/renderer": "4.0.468",
34
- "@remotion/studio-shared": "4.0.468",
32
+ "@remotion/bundler": "4.0.469",
33
+ "@remotion/renderer": "4.0.469",
34
+ "@remotion/studio-shared": "4.0.469",
35
35
  "memfs": "3.4.3",
36
36
  "open": "8.4.2"
37
37
  },
@@ -39,7 +39,7 @@
39
39
  "ast-types": "0.16.1",
40
40
  "react": "19.2.3",
41
41
  "@types/semver": "7.5.3",
42
- "@remotion/eslint-config-internal": "4.0.468",
42
+ "@remotion/eslint-config-internal": "4.0.469",
43
43
  "eslint": "9.19.0",
44
44
  "@types/node": "20.12.14",
45
45
  "@typescript/native-preview": "7.0.0-dev.20260217.1"