@remotion/studio-server 4.0.477 → 4.0.479
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.
- package/dist/codemods/duplicate-effect.d.ts +26 -0
- package/dist/codemods/duplicate-effect.js +92 -0
- package/dist/codemods/effect-param-expression.js +17 -4
- package/dist/codemods/update-effect-props/update-effect-props.d.ts +3 -3
- package/dist/codemods/update-keyframes/update-keyframes.d.ts +5 -5
- package/dist/codemods/update-keyframes/update-keyframes.js +89 -26
- package/dist/codemods/update-sequence-props/update-sequence-props.d.ts +4 -4
- package/dist/helpers/resolve-composition-component.js +18 -1
- package/dist/preview-server/api-routes.js +2 -0
- package/dist/preview-server/routes/can-update-effect-props.d.ts +5 -5
- package/dist/preview-server/routes/can-update-sequence-props.js +17 -5
- package/dist/preview-server/routes/download-remote-asset.js +7 -2
- package/dist/preview-server/routes/duplicate-effect.d.ts +3 -0
- package/dist/preview-server/routes/duplicate-effect.js +98 -0
- package/dist/preview-server/routes/insert-jsx-element.js +3 -0
- package/dist/preview-server/undo-stack.d.ts +3 -1
- package/package.json +6 -6
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SequenceNodePath } from 'remotion';
|
|
2
|
+
export type EffectDuplicationTarget = {
|
|
3
|
+
sequenceNodePath: SequenceNodePath;
|
|
4
|
+
effectIndex: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const duplicateEffects: ({ input, effects, prettierConfigOverride, }: {
|
|
7
|
+
input: string;
|
|
8
|
+
effects: EffectDuplicationTarget[];
|
|
9
|
+
prettierConfigOverride?: Record<string, unknown> | null | undefined;
|
|
10
|
+
}) => Promise<{
|
|
11
|
+
output: string;
|
|
12
|
+
formatted: boolean;
|
|
13
|
+
effectLabels: string[];
|
|
14
|
+
logLines: number[];
|
|
15
|
+
}>;
|
|
16
|
+
export declare const duplicateEffect: ({ input, sequenceNodePath, effectIndex, prettierConfigOverride, }: {
|
|
17
|
+
input: string;
|
|
18
|
+
sequenceNodePath: SequenceNodePath;
|
|
19
|
+
effectIndex: number;
|
|
20
|
+
prettierConfigOverride?: Record<string, unknown> | null | undefined;
|
|
21
|
+
}) => Promise<{
|
|
22
|
+
output: string;
|
|
23
|
+
formatted: boolean;
|
|
24
|
+
effectLabel: string;
|
|
25
|
+
logLine: number;
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.duplicateEffect = exports.duplicateEffects = void 0;
|
|
4
|
+
const types_1 = require("@babel/types");
|
|
5
|
+
const can_update_sequence_props_1 = require("../preview-server/routes/can-update-sequence-props");
|
|
6
|
+
const format_file_content_1 = require("./format-file-content");
|
|
7
|
+
const parse_ast_1 = require("./parse-ast");
|
|
8
|
+
const update_effect_props_1 = require("./update-effect-props/update-effect-props");
|
|
9
|
+
const getEffectsArray = (attr) => {
|
|
10
|
+
if (!attr.value || attr.value.type !== 'JSXExpressionContainer') {
|
|
11
|
+
throw new Error('Cannot duplicate effect: effects prop is not an array');
|
|
12
|
+
}
|
|
13
|
+
const expr = attr.value.expression;
|
|
14
|
+
if (expr.type !== 'ArrayExpression') {
|
|
15
|
+
throw new Error('Cannot duplicate effect: effects prop is not an array');
|
|
16
|
+
}
|
|
17
|
+
return expr;
|
|
18
|
+
};
|
|
19
|
+
const duplicateEffects = async ({ input, effects, prettierConfigOverride, }) => {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
var _c, _d, _e;
|
|
22
|
+
if (effects.length === 0) {
|
|
23
|
+
throw new Error('No effects were specified for duplication');
|
|
24
|
+
}
|
|
25
|
+
const ast = (0, parse_ast_1.parseAst)(input);
|
|
26
|
+
const duplicationsByAttr = new Map();
|
|
27
|
+
for (const effect of effects) {
|
|
28
|
+
const { sequenceNodePath, effectIndex } = effect;
|
|
29
|
+
const jsx = (0, can_update_sequence_props_1.findJsxElementAtNodePath)(ast, sequenceNodePath);
|
|
30
|
+
if (!jsx) {
|
|
31
|
+
throw new Error('Could not find a JSX element at the specified location to duplicate effect');
|
|
32
|
+
}
|
|
33
|
+
const attr = (0, update_effect_props_1.findEffectsAttr)((_c = jsx.attributes) !== null && _c !== void 0 ? _c : []);
|
|
34
|
+
if (!attr) {
|
|
35
|
+
throw new Error('Could not find effects on the target JSX element');
|
|
36
|
+
}
|
|
37
|
+
const effectsArray = getEffectsArray(attr);
|
|
38
|
+
const existingDuplication = duplicationsByAttr.get(attr);
|
|
39
|
+
const duplication = existingDuplication !== null && existingDuplication !== void 0 ? existingDuplication : {
|
|
40
|
+
effectsArray,
|
|
41
|
+
effectIndices: new Set(),
|
|
42
|
+
effectLabels: [],
|
|
43
|
+
logLines: [],
|
|
44
|
+
};
|
|
45
|
+
duplicationsByAttr.set(attr, duplication);
|
|
46
|
+
if (duplication.effectIndices.has(effectIndex)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const found = (0, update_effect_props_1.findEffectCallExpression)({ attr, effectIndex });
|
|
50
|
+
if (found.kind === 'error') {
|
|
51
|
+
throw new Error(`Cannot duplicate effect: ${found.reason}`);
|
|
52
|
+
}
|
|
53
|
+
duplication.effectIndices.add(effectIndex);
|
|
54
|
+
duplication.effectLabels.push(`${found.callee}()`);
|
|
55
|
+
duplication.logLines.push((_e = (_d = (_a = found.call.loc) === null || _a === void 0 ? void 0 : _a.start.line) !== null && _d !== void 0 ? _d : (_b = jsx.loc) === null || _b === void 0 ? void 0 : _b.start.line) !== null && _e !== void 0 ? _e : 1);
|
|
56
|
+
}
|
|
57
|
+
for (const duplication of duplicationsByAttr.values()) {
|
|
58
|
+
for (const effectIndex of [...duplication.effectIndices].sort((a, b) => b - a)) {
|
|
59
|
+
const effect = duplication.effectsArray.elements[effectIndex];
|
|
60
|
+
if (!effect || effect.type !== 'CallExpression') {
|
|
61
|
+
throw new Error('Cannot duplicate effect: not-call-expression');
|
|
62
|
+
}
|
|
63
|
+
duplication.effectsArray.elements.splice(effectIndex + 1, 0, (0, types_1.cloneNode)(effect, true));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const finalFile = (0, parse_ast_1.serializeAst)(ast);
|
|
67
|
+
const { output, formatted } = await (0, format_file_content_1.formatFileContent)({
|
|
68
|
+
input: finalFile,
|
|
69
|
+
prettierConfigOverride,
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
output,
|
|
73
|
+
formatted,
|
|
74
|
+
effectLabels: [...duplicationsByAttr.values()].flatMap((duplication) => duplication.effectLabels),
|
|
75
|
+
logLines: [...duplicationsByAttr.values()].flatMap((duplication) => duplication.logLines),
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
exports.duplicateEffects = duplicateEffects;
|
|
79
|
+
const duplicateEffect = async ({ input, sequenceNodePath, effectIndex, prettierConfigOverride, }) => {
|
|
80
|
+
const { output, formatted, effectLabels, logLines } = await (0, exports.duplicateEffects)({
|
|
81
|
+
input,
|
|
82
|
+
effects: [{ sequenceNodePath, effectIndex }],
|
|
83
|
+
prettierConfigOverride,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
output,
|
|
87
|
+
formatted,
|
|
88
|
+
effectLabel: effectLabels[0],
|
|
89
|
+
logLine: logLines[0],
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
exports.duplicateEffect = duplicateEffect;
|
|
@@ -38,7 +38,7 @@ const recast = __importStar(require("recast"));
|
|
|
38
38
|
const imports_1 = require("../helpers/imports");
|
|
39
39
|
const update_nested_prop_1 = require("./update-nested-prop");
|
|
40
40
|
const b = recast.types.builders;
|
|
41
|
-
const isNonLinearEasing = (easing) => easing !== 'linear';
|
|
41
|
+
const isNonLinearEasing = (easing) => easing.type !== 'linear';
|
|
42
42
|
const keyframedParamNeedsEasingImport = (param) => param.easing.some(isNonLinearEasing);
|
|
43
43
|
const getRequiredRemotionImportsForEffectParams = (params) => {
|
|
44
44
|
const requiredImports = new Set();
|
|
@@ -73,10 +73,23 @@ const ensureRemotionImportLocalNames = ({ ast, requiredImports, }) => {
|
|
|
73
73
|
};
|
|
74
74
|
exports.ensureRemotionImportLocalNames = ensureRemotionImportLocalNames;
|
|
75
75
|
const makeEasingExpression = ({ easing, easingLocalName, }) => {
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
switch (easing.type) {
|
|
77
|
+
case 'linear':
|
|
78
|
+
return b.memberExpression(b.identifier(easingLocalName), b.identifier('linear'));
|
|
79
|
+
case 'spring':
|
|
80
|
+
return b.callExpression(b.memberExpression(b.identifier(easingLocalName), b.identifier('spring')), [
|
|
81
|
+
b.objectExpression([
|
|
82
|
+
b.objectProperty(b.identifier('damping'), (0, update_nested_prop_1.parseValueExpression)(easing.damping)),
|
|
83
|
+
b.objectProperty(b.identifier('mass'), (0, update_nested_prop_1.parseValueExpression)(easing.mass)),
|
|
84
|
+
b.objectProperty(b.identifier('stiffness'), (0, update_nested_prop_1.parseValueExpression)(easing.stiffness)),
|
|
85
|
+
b.objectProperty(b.identifier('overshootClamping'), b.booleanLiteral(easing.overshootClamping)),
|
|
86
|
+
]),
|
|
87
|
+
]);
|
|
88
|
+
case 'bezier':
|
|
89
|
+
return b.callExpression(b.memberExpression(b.identifier(easingLocalName), b.identifier('bezier')), [easing.x1, easing.y1, easing.x2, easing.y2].map((value) => (0, update_nested_prop_1.parseValueExpression)(value)));
|
|
90
|
+
default:
|
|
91
|
+
throw new Error(`Unsupported easing: ${JSON.stringify(easing)}`);
|
|
78
92
|
}
|
|
79
|
-
return b.callExpression(b.memberExpression(b.identifier(easingLocalName), b.identifier('bezier')), easing.map((value) => (0, update_nested_prop_1.parseValueExpression)(value)));
|
|
80
93
|
};
|
|
81
94
|
const makeKeyframedOptions = ({ param, remotionLocalNames, }) => {
|
|
82
95
|
var _a;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ArrayExpression, CallExpression, JSXAttribute } from '@babel/types';
|
|
2
2
|
import type { EffectClipboardParam } from '@remotion/studio-shared';
|
|
3
|
-
import type { SequenceNodePath,
|
|
3
|
+
import type { SequenceNodePath, InteractivitySchema } from 'remotion';
|
|
4
4
|
export type EffectPropUpdate = {
|
|
5
5
|
key: string;
|
|
6
6
|
value: unknown;
|
|
@@ -49,7 +49,7 @@ export declare const updateEffectPropsAst: ({ input, sequenceNodePath, effectInd
|
|
|
49
49
|
sequenceNodePath: SequenceNodePath;
|
|
50
50
|
effectIndex: number;
|
|
51
51
|
update: EffectPropUpdate;
|
|
52
|
-
schema:
|
|
52
|
+
schema: InteractivitySchema;
|
|
53
53
|
}) => {
|
|
54
54
|
serialized: string;
|
|
55
55
|
oldValueString: string;
|
|
@@ -63,6 +63,6 @@ export declare const updateEffectProps: ({ input, sequenceNodePath, effectIndex,
|
|
|
63
63
|
sequenceNodePath: SequenceNodePath;
|
|
64
64
|
effectIndex: number;
|
|
65
65
|
update: EffectPropUpdate;
|
|
66
|
-
schema:
|
|
66
|
+
schema: InteractivitySchema;
|
|
67
67
|
prettierConfigOverride?: Record<string, unknown> | null | undefined;
|
|
68
68
|
}) => Promise<UpdateEffectPropsResult>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type KeyframeInterpolationFunction } from '@remotion/studio-shared';
|
|
2
|
-
import type { CanUpdateSequencePropStatus, ExtrapolateType, SequenceNodePath,
|
|
2
|
+
import type { CanUpdateSequencePropStatus, ExtrapolateType, SequenceNodePath, InteractivitySchema } from 'remotion';
|
|
3
3
|
type KeyframeEasing = Extract<CanUpdateSequencePropStatus, {
|
|
4
4
|
status: 'keyframed';
|
|
5
5
|
}>['easing'][number];
|
|
@@ -45,7 +45,7 @@ export declare const updateSequenceKeyframesAst: ({ input, nodePath, updates, sc
|
|
|
45
45
|
input: string;
|
|
46
46
|
nodePath: SequenceNodePath;
|
|
47
47
|
updates: SequenceKeyframeUpdate[];
|
|
48
|
-
schema?:
|
|
48
|
+
schema?: InteractivitySchema | undefined;
|
|
49
49
|
}) => {
|
|
50
50
|
serialized: string;
|
|
51
51
|
oldValueStrings: string[];
|
|
@@ -57,7 +57,7 @@ export declare const updateSequenceKeyframes: ({ input, nodePath, updates, schem
|
|
|
57
57
|
input: string;
|
|
58
58
|
nodePath: SequenceNodePath;
|
|
59
59
|
updates: SequenceKeyframeUpdate[];
|
|
60
|
-
schema?:
|
|
60
|
+
schema?: InteractivitySchema | undefined;
|
|
61
61
|
prettierConfigOverride?: Record<string, unknown> | null | undefined;
|
|
62
62
|
}) => Promise<{
|
|
63
63
|
output: string;
|
|
@@ -72,7 +72,7 @@ export declare const updateEffectKeyframesAst: ({ input, sequenceNodePath, effec
|
|
|
72
72
|
sequenceNodePath: SequenceNodePath;
|
|
73
73
|
effectIndex: number;
|
|
74
74
|
updates: EffectKeyframeUpdate[];
|
|
75
|
-
schema?:
|
|
75
|
+
schema?: InteractivitySchema | undefined;
|
|
76
76
|
}) => {
|
|
77
77
|
serialized: string;
|
|
78
78
|
oldValueStrings: string[];
|
|
@@ -86,7 +86,7 @@ export declare const updateEffectKeyframes: ({ input, sequenceNodePath, effectIn
|
|
|
86
86
|
sequenceNodePath: SequenceNodePath;
|
|
87
87
|
effectIndex: number;
|
|
88
88
|
updates: EffectKeyframeUpdate[];
|
|
89
|
-
schema?:
|
|
89
|
+
schema?: InteractivitySchema | undefined;
|
|
90
90
|
prettierConfigOverride?: Record<string, unknown> | null | undefined;
|
|
91
91
|
}) => Promise<{
|
|
92
92
|
output: string;
|
|
@@ -224,7 +224,7 @@ const setOptionsProperty = ({ options, propertyName, value, }) => {
|
|
|
224
224
|
}
|
|
225
225
|
options.properties.push(b.objectProperty(b.identifier(propertyName), value));
|
|
226
226
|
};
|
|
227
|
-
const isLinearEasing = (easing) => easing === 'linear';
|
|
227
|
+
const isLinearEasing = (easing) => easing.type === 'linear';
|
|
228
228
|
const getKeyframeEasing = (node) => {
|
|
229
229
|
if (node.type === 'TSAsExpression') {
|
|
230
230
|
return getKeyframeEasing(node.expression);
|
|
@@ -235,16 +235,29 @@ const getKeyframeEasing = (node) => {
|
|
|
235
235
|
node.property.type === 'Identifier' &&
|
|
236
236
|
node.property.name === 'linear' &&
|
|
237
237
|
node.computed === false) {
|
|
238
|
-
return 'linear';
|
|
238
|
+
return { type: 'linear' };
|
|
239
239
|
}
|
|
240
240
|
if (node.type !== 'CallExpression' ||
|
|
241
241
|
node.callee.type !== 'MemberExpression' ||
|
|
242
242
|
node.callee.object.type !== 'Identifier' ||
|
|
243
243
|
node.callee.object.name !== 'Easing' ||
|
|
244
244
|
node.callee.property.type !== 'Identifier' ||
|
|
245
|
-
node.callee.
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
node.callee.computed) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (node.callee.property.name === 'spring') {
|
|
249
|
+
if (node.arguments.length > 1) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const springConfig = node.arguments[0];
|
|
253
|
+
if ((springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'ArgumentPlaceholder' ||
|
|
254
|
+
(springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'JSXNamespacedName' ||
|
|
255
|
+
(springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'SpreadElement') {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
return (0, studio_shared_1.parseSpringEasingConfig)(springConfig);
|
|
259
|
+
}
|
|
260
|
+
if (node.callee.property.name !== 'bezier' || node.arguments.length !== 4) {
|
|
248
261
|
return null;
|
|
249
262
|
}
|
|
250
263
|
const values = node.arguments.map((arg) => {
|
|
@@ -258,7 +271,8 @@ const getKeyframeEasing = (node) => {
|
|
|
258
271
|
if (values.some((value) => value === null)) {
|
|
259
272
|
return null;
|
|
260
273
|
}
|
|
261
|
-
|
|
274
|
+
const [x1, y1, x2, y2] = values;
|
|
275
|
+
return { type: 'bezier', x1, y1, x2, y2 };
|
|
262
276
|
};
|
|
263
277
|
const getKeyframeEasingArray = ({ easingNode, segmentCount, }) => {
|
|
264
278
|
if (segmentCount === 0) {
|
|
@@ -285,7 +299,7 @@ const getKeyframeEasingArray = ({ easingNode, segmentCount, }) => {
|
|
|
285
299
|
}
|
|
286
300
|
const easingArray = parsed;
|
|
287
301
|
while (easingArray.length < segmentCount) {
|
|
288
|
-
easingArray.push(
|
|
302
|
+
easingArray.push(studio_shared_1.LINEAR_KEYFRAME_EASING);
|
|
289
303
|
}
|
|
290
304
|
return easingArray;
|
|
291
305
|
}
|
|
@@ -298,7 +312,7 @@ const getKeyframeEasingArray = ({ easingNode, segmentCount, }) => {
|
|
|
298
312
|
const getExistingEasingArray = ({ options, segmentCount, }) => {
|
|
299
313
|
const { prop } = findObjectOptionProperty(options, 'easing');
|
|
300
314
|
if (!prop) {
|
|
301
|
-
return
|
|
315
|
+
return Array.from({ length: segmentCount }, () => ({ type: 'linear' }));
|
|
302
316
|
}
|
|
303
317
|
const easing = getKeyframeEasingArray({
|
|
304
318
|
easingNode: prop.value,
|
|
@@ -317,10 +331,23 @@ const getExistingEasingArrayOrNull = ({ options, segmentCount, }) => {
|
|
|
317
331
|
return getExistingEasingArray({ options, segmentCount });
|
|
318
332
|
};
|
|
319
333
|
const createEasingExpression = (easing) => {
|
|
320
|
-
|
|
321
|
-
|
|
334
|
+
switch (easing.type) {
|
|
335
|
+
case 'linear':
|
|
336
|
+
return b.memberExpression(b.identifier('Easing'), b.identifier('linear'));
|
|
337
|
+
case 'spring':
|
|
338
|
+
return b.callExpression(b.memberExpression(b.identifier('Easing'), b.identifier('spring')), [
|
|
339
|
+
b.objectExpression([
|
|
340
|
+
b.objectProperty(b.identifier('damping'), (0, update_nested_prop_1.parseValueExpression)(easing.damping)),
|
|
341
|
+
b.objectProperty(b.identifier('mass'), (0, update_nested_prop_1.parseValueExpression)(easing.mass)),
|
|
342
|
+
b.objectProperty(b.identifier('stiffness'), (0, update_nested_prop_1.parseValueExpression)(easing.stiffness)),
|
|
343
|
+
b.objectProperty(b.identifier('overshootClamping'), b.booleanLiteral(easing.overshootClamping)),
|
|
344
|
+
]),
|
|
345
|
+
]);
|
|
346
|
+
case 'bezier':
|
|
347
|
+
return b.callExpression(b.memberExpression(b.identifier('Easing'), b.identifier('bezier')), [easing.x1, easing.y1, easing.x2, easing.y2].map((value) => (0, update_nested_prop_1.parseValueExpression)(value)));
|
|
348
|
+
default:
|
|
349
|
+
throw new Error(`Unsupported easing: ${JSON.stringify(easing)}`);
|
|
322
350
|
}
|
|
323
|
-
return b.callExpression(b.memberExpression(b.identifier('Easing'), b.identifier('bezier')), easing.map((value) => (0, update_nested_prop_1.parseValueExpression)(value)));
|
|
324
351
|
};
|
|
325
352
|
const createEasingArrayExpression = (easing) => b.arrayExpression(easing.map((easingValue) => createEasingExpression(easingValue)));
|
|
326
353
|
const setEasingOption = ({ options, easing, }) => {
|
|
@@ -357,7 +384,7 @@ const normalizeEasingAfterAddingKeyframe = ({ extraArgs, previousSegmentCount, n
|
|
|
357
384
|
return { extraArgs, needsEasingImport: false };
|
|
358
385
|
}
|
|
359
386
|
while (easing.length < nextSegmentCount) {
|
|
360
|
-
easing.push(
|
|
387
|
+
easing.push(studio_shared_1.LINEAR_KEYFRAME_EASING);
|
|
361
388
|
}
|
|
362
389
|
return {
|
|
363
390
|
extraArgs: getExtraArgsWithOptions({ extraArgs, options }),
|
|
@@ -384,7 +411,7 @@ const normalizeEasingAfterRemovingKeyframe = ({ extraArgs, previousSegmentCount,
|
|
|
384
411
|
easing.pop();
|
|
385
412
|
}
|
|
386
413
|
while (easing.length < nextSegmentCount) {
|
|
387
|
-
easing.push(
|
|
414
|
+
easing.push(studio_shared_1.LINEAR_KEYFRAME_EASING);
|
|
388
415
|
}
|
|
389
416
|
return {
|
|
390
417
|
extraArgs: getExtraArgsWithOptions({ extraArgs, options }),
|
|
@@ -414,7 +441,7 @@ const normalizeEasingAfterRemovingKeyframes = ({ extraArgs, previousSegmentCount
|
|
|
414
441
|
easing.pop();
|
|
415
442
|
}
|
|
416
443
|
while (easing.length < nextSegmentCount) {
|
|
417
|
-
easing.push(
|
|
444
|
+
easing.push(studio_shared_1.LINEAR_KEYFRAME_EASING);
|
|
418
445
|
}
|
|
419
446
|
return {
|
|
420
447
|
extraArgs: getExtraArgsWithOptions({ extraArgs, options }),
|
|
@@ -901,6 +928,37 @@ const getSequenceWritableProp = ({ attributes, key, missingPropInitialValue, })
|
|
|
901
928
|
},
|
|
902
929
|
};
|
|
903
930
|
};
|
|
931
|
+
const getEffectWritableProp = ({ objExpr, key, missingPropInitialValue, }) => {
|
|
932
|
+
const { prop } = findObjectProperty(objExpr, key);
|
|
933
|
+
if (!prop) {
|
|
934
|
+
if (missingPropInitialValue) {
|
|
935
|
+
return {
|
|
936
|
+
expression: createMissingPropExpression(missingPropInitialValue),
|
|
937
|
+
setExpression: (nextExpression) => {
|
|
938
|
+
objExpr.properties.push(createObjectProperty(key, nextExpression));
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
throw new Error(`Cannot update keyframes: "${key}" is not set`);
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
expression: prop.value,
|
|
946
|
+
setExpression: (nextExpression) => {
|
|
947
|
+
prop.value = nextExpression;
|
|
948
|
+
},
|
|
949
|
+
};
|
|
950
|
+
};
|
|
951
|
+
const getEffectPropsObjectExpression = (call) => {
|
|
952
|
+
if (call.arguments.length === 0) {
|
|
953
|
+
const objExpr = b.objectExpression([]);
|
|
954
|
+
call.arguments.push(objExpr);
|
|
955
|
+
return objExpr;
|
|
956
|
+
}
|
|
957
|
+
if (call.arguments[0].type !== 'ObjectExpression') {
|
|
958
|
+
throw new Error('Cannot update effect keyframe: computed');
|
|
959
|
+
}
|
|
960
|
+
return call.arguments[0];
|
|
961
|
+
};
|
|
904
962
|
const updateSequenceKeyframesAst = ({ input, nodePath, updates, schema, }) => {
|
|
905
963
|
var _a;
|
|
906
964
|
var _b;
|
|
@@ -1014,29 +1072,34 @@ const updateEffectKeyframesAst = ({ input, sequenceNodePath, effectIndex, update
|
|
|
1014
1072
|
throw new Error(`Cannot update effect keyframe: ${found.reason}`);
|
|
1015
1073
|
}
|
|
1016
1074
|
const { call, callee: effectCallee } = found;
|
|
1017
|
-
|
|
1018
|
-
call.arguments[0].type !== 'ObjectExpression') {
|
|
1019
|
-
throw new Error('Cannot update effect keyframe: computed');
|
|
1020
|
-
}
|
|
1021
|
-
const objExpr = call.arguments[0];
|
|
1075
|
+
const objExpr = getEffectPropsObjectExpression(call);
|
|
1022
1076
|
const oldValueStrings = [];
|
|
1023
1077
|
const newValueStrings = [];
|
|
1024
1078
|
const requiredImports = new Set();
|
|
1025
1079
|
let needsFrameHook = false;
|
|
1026
1080
|
for (const update of updates) {
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1081
|
+
const prop = getEffectWritableProp({
|
|
1082
|
+
objExpr,
|
|
1083
|
+
key: update.key,
|
|
1084
|
+
missingPropInitialValue: update.operation.type === 'add'
|
|
1085
|
+
? {
|
|
1086
|
+
value: getInitialValueForMissingProp({
|
|
1087
|
+
schema: schema !== null && schema !== void 0 ? schema : null,
|
|
1088
|
+
key: update.key,
|
|
1089
|
+
newValue: update.operation.value,
|
|
1090
|
+
}),
|
|
1091
|
+
}
|
|
1092
|
+
: null,
|
|
1093
|
+
});
|
|
1094
|
+
oldValueStrings.push(recast.print(prop.expression).code);
|
|
1032
1095
|
const { expression: nextExpression, introduced } = applyKeyframeOperation({
|
|
1033
|
-
expression: prop.
|
|
1096
|
+
expression: prop.expression,
|
|
1034
1097
|
key: update.key,
|
|
1035
1098
|
operation: update.operation,
|
|
1036
1099
|
schema: schema !== null && schema !== void 0 ? schema : null,
|
|
1037
1100
|
});
|
|
1038
1101
|
newValueStrings.push(recast.print(nextExpression).code);
|
|
1039
|
-
prop.
|
|
1102
|
+
prop.setExpression(nextExpression);
|
|
1040
1103
|
if (introduced.calleeName) {
|
|
1041
1104
|
requiredImports.add(introduced.calleeName);
|
|
1042
1105
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SequenceNodePath,
|
|
1
|
+
import type { SequenceNodePath, InteractivitySchema } from 'remotion';
|
|
2
2
|
export type SequencePropUpdate = {
|
|
3
3
|
key: string;
|
|
4
4
|
value: unknown;
|
|
@@ -11,7 +11,7 @@ export type RemovedProp = {
|
|
|
11
11
|
export type SequencePropsNodeUpdate = {
|
|
12
12
|
nodePath: SequenceNodePath;
|
|
13
13
|
updates: SequencePropUpdate[];
|
|
14
|
-
schema:
|
|
14
|
+
schema: InteractivitySchema;
|
|
15
15
|
};
|
|
16
16
|
export type SequencePropsNodeUpdateResult = {
|
|
17
17
|
oldValueStrings: string[];
|
|
@@ -35,7 +35,7 @@ export declare const updateSequencePropsAst: ({ input, nodePath, updates, schema
|
|
|
35
35
|
input: string;
|
|
36
36
|
nodePath: SequenceNodePath;
|
|
37
37
|
updates: SequencePropUpdate[];
|
|
38
|
-
schema:
|
|
38
|
+
schema: InteractivitySchema;
|
|
39
39
|
}) => {
|
|
40
40
|
serialized: string;
|
|
41
41
|
oldValueStrings: string[];
|
|
@@ -51,7 +51,7 @@ export declare const updateSequenceProps: ({ input, nodePath, updates, schema, p
|
|
|
51
51
|
input: string;
|
|
52
52
|
nodePath: SequenceNodePath;
|
|
53
53
|
updates: SequencePropUpdate[];
|
|
54
|
-
schema:
|
|
54
|
+
schema: InteractivitySchema;
|
|
55
55
|
prettierConfigOverride: PrettierConfigOverride;
|
|
56
56
|
}) => Promise<UpdateSequencePropsResult>;
|
|
57
57
|
export {};
|
|
@@ -515,7 +515,13 @@ const createStringAttribute = (name, value) => {
|
|
|
515
515
|
const createBooleanAttribute = (name, value) => {
|
|
516
516
|
return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(name), recast.types.builders.jsxExpressionContainer(recast.types.builders.booleanLiteral(value)));
|
|
517
517
|
};
|
|
518
|
-
const
|
|
518
|
+
const translateDecimalPlaces = 1;
|
|
519
|
+
const roundTranslateCoordinate = (value) => {
|
|
520
|
+
const factor = 10 ** translateDecimalPlaces;
|
|
521
|
+
const rounded = Math.round(value * factor) / factor;
|
|
522
|
+
return Object.is(rounded, -0) ? 0 : rounded;
|
|
523
|
+
};
|
|
524
|
+
const formatTranslateValue = ({ x, y }) => `${roundTranslateCoordinate(x)}px ${roundTranslateCoordinate(y)}px`;
|
|
519
525
|
const createPositionAbsoluteStyleAttribute = (position) => {
|
|
520
526
|
const properties = [
|
|
521
527
|
recast.types.builders.objectProperty(recast.types.builders.identifier('position'), recast.types.builders.stringLiteral('absolute')),
|
|
@@ -735,6 +741,14 @@ const ensureImgImport = (ast) => {
|
|
|
735
741
|
label: '<Img>',
|
|
736
742
|
});
|
|
737
743
|
};
|
|
744
|
+
const ensureAnimatedImageImport = (ast) => {
|
|
745
|
+
return ensureOfficialNamedImport({
|
|
746
|
+
ast,
|
|
747
|
+
importedName: 'AnimatedImage',
|
|
748
|
+
sourcePath: 'remotion',
|
|
749
|
+
label: '<AnimatedImage>',
|
|
750
|
+
});
|
|
751
|
+
};
|
|
738
752
|
const ensureVideoImport = (ast) => {
|
|
739
753
|
return ensureOfficialNamedImport({
|
|
740
754
|
ast,
|
|
@@ -1045,6 +1059,9 @@ const createInsertableJsxElement = ({ ast, element, }) => {
|
|
|
1045
1059
|
else if (element.assetType === 'gif') {
|
|
1046
1060
|
localName = ensureGifImport(ast);
|
|
1047
1061
|
}
|
|
1062
|
+
else if (element.assetType === 'animated-image') {
|
|
1063
|
+
localName = ensureAnimatedImageImport(ast);
|
|
1064
|
+
}
|
|
1048
1065
|
else if (element.assetType === 'audio') {
|
|
1049
1066
|
localName = ensureAudioImport(ast);
|
|
1050
1067
|
}
|
|
@@ -15,6 +15,7 @@ const delete_jsx_node_1 = require("./routes/delete-jsx-node");
|
|
|
15
15
|
const delete_keyframes_1 = require("./routes/delete-keyframes");
|
|
16
16
|
const delete_static_file_1 = require("./routes/delete-static-file");
|
|
17
17
|
const download_remote_asset_1 = require("./routes/download-remote-asset");
|
|
18
|
+
const duplicate_effect_1 = require("./routes/duplicate-effect");
|
|
18
19
|
const duplicate_jsx_node_1 = require("./routes/duplicate-jsx-node");
|
|
19
20
|
const insert_jsx_element_1 = require("./routes/insert-jsx-element");
|
|
20
21
|
const install_dependency_1 = require("./routes/install-dependency");
|
|
@@ -67,6 +68,7 @@ exports.allApiRoutes = {
|
|
|
67
68
|
'/api/save-effect-props': save_effect_props_1.saveEffectPropsHandler,
|
|
68
69
|
'/api/add-effect': add_effect_1.addEffectHandler,
|
|
69
70
|
'/api/reorder-effect': reorder_effect_1.reorderEffectHandler,
|
|
71
|
+
'/api/duplicate-effect': duplicate_effect_1.duplicateEffectHandler,
|
|
70
72
|
'/api/reorder-sequence': reorder_sequence_1.reorderSequenceHandler,
|
|
71
73
|
'/api/delete-keyframes': delete_keyframes_1.deleteKeyframesHandler,
|
|
72
74
|
'/api/move-keyframes': move_keyframes_1.moveKeyframesHandler,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { File, JSXOpeningElement } from '@babel/types';
|
|
2
|
-
import type { CanUpdateEffectPropsResponse, SequenceNodePath,
|
|
2
|
+
import type { CanUpdateEffectPropsResponse, SequenceNodePath, InteractivitySchema } from 'remotion';
|
|
3
3
|
export declare const computeEffectPropStatus: ({ ast, jsx, effectIndex, keys, }: {
|
|
4
4
|
ast: File;
|
|
5
5
|
jsx: JSXOpeningElement;
|
|
@@ -9,13 +9,13 @@ export declare const computeEffectPropStatus: ({ ast, jsx, effectIndex, keys, }:
|
|
|
9
9
|
export declare const computeEffectPropsStatusesFromContent: ({ fileContents, sequenceNodePath, effects, keysFor, }: {
|
|
10
10
|
fileContents: string;
|
|
11
11
|
sequenceNodePath: SequenceNodePath;
|
|
12
|
-
effects:
|
|
13
|
-
keysFor: (effect:
|
|
12
|
+
effects: InteractivitySchema[];
|
|
13
|
+
keysFor: (effect: InteractivitySchema) => string[];
|
|
14
14
|
}) => CanUpdateEffectPropsResponse[];
|
|
15
15
|
export declare const computeEffectPropsStatusesFromFile: ({ fileName, sequenceNodePath, effects, keysFor, remotionRoot, }: {
|
|
16
16
|
fileName: string;
|
|
17
17
|
sequenceNodePath: SequenceNodePath;
|
|
18
|
-
effects:
|
|
19
|
-
keysFor: (effect:
|
|
18
|
+
effects: InteractivitySchema[];
|
|
19
|
+
keysFor: (effect: InteractivitySchema) => string[];
|
|
20
20
|
remotionRoot: string;
|
|
21
21
|
}) => CanUpdateEffectPropsResponse[];
|
|
@@ -210,18 +210,29 @@ const getKeyframeEasing = (node) => {
|
|
|
210
210
|
node.property.type === 'Identifier' &&
|
|
211
211
|
node.property.name === 'linear' &&
|
|
212
212
|
node.computed === false) {
|
|
213
|
-
return 'linear';
|
|
213
|
+
return { type: 'linear' };
|
|
214
214
|
}
|
|
215
215
|
if (node.type !== 'CallExpression' ||
|
|
216
216
|
node.callee.type !== 'MemberExpression' ||
|
|
217
217
|
node.callee.object.type !== 'Identifier' ||
|
|
218
218
|
node.callee.object.name !== 'Easing' ||
|
|
219
219
|
node.callee.property.type !== 'Identifier' ||
|
|
220
|
-
node.callee.property.name !== 'bezier' ||
|
|
221
220
|
node.callee.computed) {
|
|
222
221
|
return null;
|
|
223
222
|
}
|
|
224
|
-
if (node.
|
|
223
|
+
if (node.callee.property.name === 'spring') {
|
|
224
|
+
if (node.arguments.length > 1) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const springConfig = node.arguments[0];
|
|
228
|
+
if ((springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'ArgumentPlaceholder' ||
|
|
229
|
+
(springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'JSXNamespacedName' ||
|
|
230
|
+
(springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'SpreadElement') {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return (0, studio_shared_1.parseSpringEasingConfig)(springConfig);
|
|
234
|
+
}
|
|
235
|
+
if (node.callee.property.name !== 'bezier' || node.arguments.length !== 4) {
|
|
225
236
|
return null;
|
|
226
237
|
}
|
|
227
238
|
const values = node.arguments.map((arg) => {
|
|
@@ -235,7 +246,8 @@ const getKeyframeEasing = (node) => {
|
|
|
235
246
|
if (values.some((v) => v === null)) {
|
|
236
247
|
return null;
|
|
237
248
|
}
|
|
238
|
-
|
|
249
|
+
const [x1, y1, x2, y2] = values;
|
|
250
|
+
return { type: 'bezier', x1, y1, x2, y2 };
|
|
239
251
|
};
|
|
240
252
|
const getKeyframeEasingArray = ({ easingNode, segments, }) => {
|
|
241
253
|
if (segments === 0) {
|
|
@@ -280,7 +292,7 @@ const getInterpolationMetadata = (interpolationFunction, callExpression, keyfram
|
|
|
280
292
|
right: 'extend',
|
|
281
293
|
};
|
|
282
294
|
const defaults = {
|
|
283
|
-
easing:
|
|
295
|
+
easing: Array.from({ length: segments }, () => studio_shared_1.LINEAR_KEYFRAME_EASING),
|
|
284
296
|
clamping: defaultClamping,
|
|
285
297
|
posterize: undefined,
|
|
286
298
|
};
|
|
@@ -13,9 +13,10 @@ const validate_same_origin_1 = require("../validate-same-origin");
|
|
|
13
13
|
const maxRemoteAssetSize = 50 * 1024 * 1024;
|
|
14
14
|
const remoteAssetDownloadTimeout = 15000;
|
|
15
15
|
const maxRemoteAssetRedirects = 5;
|
|
16
|
-
const remoteAssetAcceptHeader = 'image/png,image/jpeg,image/webp,image/bmp,image/gif';
|
|
16
|
+
const remoteAssetAcceptHeader = 'image/png,image/apng,image/jpeg,image/webp,image/bmp,image/gif';
|
|
17
17
|
const extensionsForFileType = {
|
|
18
18
|
png: ['png'],
|
|
19
|
+
apng: ['png', 'apng'],
|
|
19
20
|
jpeg: ['jpg', 'jpeg'],
|
|
20
21
|
webp: ['webp'],
|
|
21
22
|
bmp: ['bmp'],
|
|
@@ -253,7 +254,11 @@ const downloadRemoteAssetHandler = async ({ input, publicDir, request }) => {
|
|
|
253
254
|
}
|
|
254
255
|
const element = {
|
|
255
256
|
type: 'asset',
|
|
256
|
-
assetType: fileType.type === 'gif'
|
|
257
|
+
assetType: fileType.type === 'gif'
|
|
258
|
+
? 'gif'
|
|
259
|
+
: fileType.type === 'apng'
|
|
260
|
+
? 'animated-image'
|
|
261
|
+
: 'image',
|
|
257
262
|
src: assetPath,
|
|
258
263
|
srcType: 'static',
|
|
259
264
|
dimensions: fileType.dimensions,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.duplicateEffectHandler = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const renderer_1 = require("@remotion/renderer");
|
|
6
|
+
const duplicate_effect_1 = require("../../codemods/duplicate-effect");
|
|
7
|
+
const file_watcher_1 = require("../../file-watcher");
|
|
8
|
+
const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
|
|
9
|
+
const format_log_file_location_1 = require("../format-log-file-location");
|
|
10
|
+
const undo_stack_1 = require("../undo-stack");
|
|
11
|
+
const formatting_1 = require("./log-updates/formatting");
|
|
12
|
+
const log_update_1 = require("./log-updates/log-update");
|
|
13
|
+
const getDuplicatedEffectDescription = (effectLabels) => {
|
|
14
|
+
if (effectLabels.length === 1) {
|
|
15
|
+
return effectLabels[0];
|
|
16
|
+
}
|
|
17
|
+
return `${effectLabels.length} effects`;
|
|
18
|
+
};
|
|
19
|
+
const duplicateEffectHandler = async ({ input: effects, remotionRoot, logLevel }) => {
|
|
20
|
+
var _a;
|
|
21
|
+
try {
|
|
22
|
+
if (effects.length === 0) {
|
|
23
|
+
throw new Error('No effects were specified for duplication');
|
|
24
|
+
}
|
|
25
|
+
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[duplicate-effect] Received request to duplicate ${effects.length} effect target${effects.length === 1 ? '' : 's'}`);
|
|
26
|
+
const itemsByFileName = new Map();
|
|
27
|
+
for (const item of effects) {
|
|
28
|
+
const fileItems = (_a = itemsByFileName.get(item.fileName)) !== null && _a !== void 0 ? _a : [];
|
|
29
|
+
fileItems.push(item);
|
|
30
|
+
itemsByFileName.set(item.fileName, fileItems);
|
|
31
|
+
}
|
|
32
|
+
const updates = await Promise.all([...itemsByFileName.entries()].map(async ([fileName, fileItems]) => {
|
|
33
|
+
const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
|
|
34
|
+
remotionRoot,
|
|
35
|
+
fileName,
|
|
36
|
+
action: 'modify',
|
|
37
|
+
});
|
|
38
|
+
const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
39
|
+
const { output, formatted, effectLabels, logLines } = await (0, duplicate_effect_1.duplicateEffects)({
|
|
40
|
+
input: fileContents,
|
|
41
|
+
effects: fileItems.map((item) => ({
|
|
42
|
+
sequenceNodePath: item.sequenceNodePath.nodePath,
|
|
43
|
+
effectIndex: item.effectIndex,
|
|
44
|
+
})),
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
absolutePath,
|
|
48
|
+
fileRelativeToRoot,
|
|
49
|
+
fileContents,
|
|
50
|
+
output,
|
|
51
|
+
formatted,
|
|
52
|
+
effectLabels,
|
|
53
|
+
logLine: Math.min(...logLines),
|
|
54
|
+
};
|
|
55
|
+
}));
|
|
56
|
+
for (const update of updates) {
|
|
57
|
+
const duplicatedEffectDescription = getDuplicatedEffectDescription(update.effectLabels);
|
|
58
|
+
(0, undo_stack_1.pushToUndoStack)({
|
|
59
|
+
filePath: update.absolutePath,
|
|
60
|
+
oldContents: update.fileContents,
|
|
61
|
+
newContents: null,
|
|
62
|
+
logLevel,
|
|
63
|
+
remotionRoot,
|
|
64
|
+
logLine: update.logLine,
|
|
65
|
+
description: {
|
|
66
|
+
undoMessage: `↩️ Duplication of ${duplicatedEffectDescription}`,
|
|
67
|
+
redoMessage: `↪️ Duplication of ${duplicatedEffectDescription}`,
|
|
68
|
+
},
|
|
69
|
+
entryType: 'duplicate-effect',
|
|
70
|
+
suppressHmrOnFileRestore: false,
|
|
71
|
+
});
|
|
72
|
+
(0, undo_stack_1.suppressUndoStackInvalidation)(update.absolutePath);
|
|
73
|
+
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(update.absolutePath, update.output, undefined);
|
|
74
|
+
const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
|
|
75
|
+
remotionRoot,
|
|
76
|
+
absolutePath: update.absolutePath,
|
|
77
|
+
line: update.logLine,
|
|
78
|
+
});
|
|
79
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} Duplicated ${(0, formatting_1.attrName)(duplicatedEffectDescription)}`);
|
|
80
|
+
if (!update.formatted) {
|
|
81
|
+
(0, log_update_1.warnAboutPrettierOnce)(logLevel);
|
|
82
|
+
}
|
|
83
|
+
renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[duplicate-effect] Wrote ${update.fileRelativeToRoot}${update.formatted ? ' (formatted)' : ''}`);
|
|
84
|
+
}
|
|
85
|
+
(0, undo_stack_1.printUndoHint)(logLevel);
|
|
86
|
+
return {
|
|
87
|
+
success: true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
reason: err.message,
|
|
94
|
+
stack: err.stack,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
exports.duplicateEffectHandler = duplicateEffectHandler;
|
|
@@ -3,7 +3,7 @@ export interface UndoEntryDescription {
|
|
|
3
3
|
undoMessage: string;
|
|
4
4
|
redoMessage: string;
|
|
5
5
|
}
|
|
6
|
-
type UndoEntryType = 'visual-control' | 'default-props' | 'sequence-props' | 'effect-props' | 'keyframe-add' | 'keyframe-delete' | 'add-effect' | 'delete-effect' | 'paste-effects' | 'reorder-effect' | 'reorder-sequence' | 'delete-jsx-node' | 'duplicate-jsx-node' | 'insert-jsx-element' | 'delete-composition' | 'rename-composition' | 'duplicate-composition' | 'delete-folder' | 'rename-folder';
|
|
6
|
+
type UndoEntryType = 'visual-control' | 'default-props' | 'sequence-props' | 'effect-props' | 'keyframe-add' | 'keyframe-delete' | 'add-effect' | 'delete-effect' | 'duplicate-effect' | 'paste-effects' | 'reorder-effect' | 'reorder-sequence' | 'delete-jsx-node' | 'duplicate-jsx-node' | 'insert-jsx-element' | 'delete-composition' | 'rename-composition' | 'duplicate-composition' | 'delete-folder' | 'rename-folder';
|
|
7
7
|
type UndoEntrySnapshot = {
|
|
8
8
|
filePath: string;
|
|
9
9
|
oldContents: string;
|
|
@@ -35,6 +35,8 @@ type UndoEntry = {
|
|
|
35
35
|
entryType: 'add-effect';
|
|
36
36
|
} | {
|
|
37
37
|
entryType: 'delete-effect';
|
|
38
|
+
} | {
|
|
39
|
+
entryType: 'duplicate-effect';
|
|
38
40
|
} | {
|
|
39
41
|
entryType: 'paste-effects';
|
|
40
42
|
} | {
|
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.
|
|
6
|
+
"version": "4.0.479",
|
|
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.
|
|
30
|
+
"remotion": "4.0.479",
|
|
31
31
|
"recast": "0.23.11",
|
|
32
|
-
"@remotion/bundler": "4.0.
|
|
33
|
-
"@remotion/renderer": "4.0.
|
|
34
|
-
"@remotion/studio-shared": "4.0.
|
|
32
|
+
"@remotion/bundler": "4.0.479",
|
|
33
|
+
"@remotion/renderer": "4.0.479",
|
|
34
|
+
"@remotion/studio-shared": "4.0.479",
|
|
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.
|
|
42
|
+
"@remotion/eslint-config-internal": "4.0.479",
|
|
43
43
|
"eslint": "9.19.0",
|
|
44
44
|
"@types/node": "20.12.14",
|
|
45
45
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|